Files
connectai/package.json
T
koriweb c27cd823a9 fix: v2.2.202 — 기업모드 Intent Alignment 가 일반 채팅 컨텍스트 무시하던 버그
증상: 일반 채팅에서 프로젝트·요구사항을 충분히 논의한 뒤 기업모드 전환 후
후속 작업을 요청하면 "추가 정보 필요 — 맥락/목표/기준/형식" 화면이 떠
사용자에게 *방금 말한 내용을 다시 묻는* 느낌을 줌.

원인:
- Intent Classifier 는 prior chat 컨텍스트(previousBrief/Tail) 받음 → follow-up
  분기 정확
- Intent Alignment (clarification 화면 만드는 분석기) 는 IntentAnalysisInput
  인터페이스에 chat history 필드가 없음 → 오직 현재 사용자 메시지만 봄
- 결과: 모드 전환 직후 첫 라운드 분석기는 사용자가 이전에 일반 채팅에서 한
  모든 설명을 못 봄 → context 빈칸 → openQuestions 에 "맥락은?" 추가

Fix:
- IntentAnalysisInput 에 priorChatSummary?: string 필드 추가
- 시스템 프롬프트에 *모드 전환 시 context 우선 추출* 규칙 추가 — 일반 채팅에서
  명시된 항목은 추측이 아니라 명시된 사실로 취급
- _buildUserMessage() 가 [모드 전환 직전 일반 채팅 요약] 블록을 user message
  상단에 주입
- sidebarProvider.ts 호출 지점에서 this._agent.getHistory() → 최근 10 turn
  (!internal) 추출 → "role: content" 한 줄씩, content 200자 cap
- 후속 라운드 (previousContract 있음) 면 history 중복 첨부 안 함 — 이미 contract
  에 흡수됨

효과: 일반 채팅 → 기업모드 전환 시 분석기가 prior chat 의 context/goal/criteria
를 직접 추출. redundant "맥락/목표/기준/형식 다시 말해 주세요" 질문 사라짐.
첫 라운드부터 confidence=high 가능 → 바로 본 작업 진행.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-02 11:24:34 +09:00

953 lines
51 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"name": "astra",
"displayName": "Astra",
"description": "The personal intelligence layer for Antigravity and VS Code. A private cognitive partner for deep project context, memory, and proactive strategic decision-making.",
"version": "2.2.202",
"publisher": "g1nation",
"license": "MIT",
"icon": "assets/icon.png",
"repository": {
"type": "git",
"url": "https://github.com/g1nations/locallm"
},
"engines": {
"vscode": "^1.80.0"
},
"categories": [
"Machine Learning",
"Programming Languages",
"Chat"
],
"keywords": [
"ai",
"local",
"ollama",
"gemma",
"llama",
"deepseek",
"offline",
"agent",
"code-generation",
"astra",
"copilot"
],
"activationEvents": [
"onStartupFinished"
],
"main": "./out/extension.js",
"contributes": {
"commands": [
{
"command": "g1nation.newChat",
"title": "Astra: New Chat",
"icon": "$(add)"
},
{
"command": "g1nation.exportChat",
"title": "Astra: Export Chat as Markdown"
},
{
"command": "g1nation.explainSelection",
"title": "Astra: Explain Selected Code"
},
{
"command": "g1nation.focusChat",
"title": "Astra: Focus Chat Input"
},
{
"command": "g1nation.showBrainNetwork",
"title": "Astra: Show Brain Topology"
},
{
"command": "g1nation.approval.focus",
"title": "Astra: Focus Approval Panel"
},
{
"command": "g1nation.scaffoldProject",
"title": "Astra: Scaffold New Project"
},
{
"command": "g1nation.telegram.setBotToken",
"title": "Astra: Set Telegram Bot Token"
},
{
"command": "g1nation.telegram.clearBotToken",
"title": "Astra: Clear Telegram Bot Token"
},
{
"command": "g1nation.telegram.testConnection",
"title": "Astra: Test Telegram Connection"
},
{
"command": "g1nation.settings.focus",
"title": "Astra: Open Settings Panel"
},
{
"command": "g1nation.skills.editKnowledgeMap",
"title": "Astra: Edit Agent ↔ Knowledge Map"
},
{
"command": "g1nation.openChat",
"title": "Astra: Open Chat (Editor Column)",
"icon": "$(comment-discussion)"
},
{
"command": "g1nation.setupDatacollect",
"title": "Astra: Setup Datacollect Dependencies (yt-dlp, youtube-transcript-api)",
"icon": "$(cloud-download)"
},
{
"command": "g1nation.lesson.create",
"title": "Astra: New Lesson (Experience Memory)"
},
{
"command": "g1nation.lesson.fromConversation",
"title": "Astra: New Lesson from Current Conversation"
},
{
"command": "g1nation.lesson.manage",
"title": "Astra: Browse / Manage Lessons"
},
{
"command": "g1nation.architecture.refresh",
"title": "Astra: Refresh Project Architecture Context"
},
{
"command": "g1nation.architecture.detach",
"title": "Astra: Detach Project Architecture Context"
},
{
"command": "g1nation.architecture.attach",
"title": "Astra: Attach Project Architecture Context"
},
{
"command": "g1nation.architecture.open",
"title": "Astra: Open Project Architecture Doc"
},
{
"command": "g1nation.company.toggle",
"title": "Astra: Toggle 1인 기업 Mode"
},
{
"command": "g1nation.company.manage",
"title": "Astra: Manage 1인 기업 Agents"
},
{
"command": "g1nation.company.openSessions",
"title": "Astra: Open 1인 기업 Sessions Folder"
},
{
"command": "g1nation.company.pixelOffice.open",
"title": "Astra: Open Pixel Office (Full Screen)"
},
{
"command": "g1nation.calendar.connect",
"title": "Astra: Google Calendar (iCal) 연결 📅"
},
{
"command": "g1nation.calendar.refresh",
"title": "Astra: Google Calendar 새로고침 📅"
},
{
"command": "g1nation.calendar.connectOAuth",
"title": "Astra: Google Calendar OAuth 연결 (쓰기) 🔐"
},
{
"command": "g1nation.devilAgent.toggle",
"title": "Astra: Toggle Devil Agent 🎭"
}
],
"keybindings": [
{
"command": "g1nation.focusChat",
"key": "cmd+l",
"mac": "cmd+l"
}
],
"menus": {
"editor/context": [
{
"command": "g1nation.explainSelection",
"when": "editorHasSelection",
"group": "1_modification"
}
]
},
"viewsContainers": {
"activitybar": [
{
"id": "g1nation-astra-activity",
"title": "Astra (g1nation)",
"icon": "assets/icon-activitybar.svg"
}
]
},
"views": {
"g1nation-astra-activity": [
{
"id": "g1nation-astra-launcher",
"name": "Astra (g1nation) Launcher"
}
]
},
"viewsWelcome": [
{
"view": "g1nation-astra-launcher",
"contents": "✦ **Astra** — 로컬 AI 인텔리전스 레이어\n\nChat 탭을 닫았을 때 여기서 다시 열 수 있습니다.\n\n[$(comment-discussion) Open Chat](command:g1nation.openChat)\n[$(add) New Chat](command:g1nation.newChat)\n[$(gear) Settings](command:g1nation.settings.focus)\n\n---\n\n**1인 기업 모드**\n\n[$(organization) Manage Agents](command:g1nation.company.manage)\n[$(folder-opened) Open Sessions Folder](command:g1nation.company.openSessions)\n\n---\n\n**Project Architecture**\n\n[$(file-text) Open Architecture Doc](command:g1nation.architecture.open)\n[$(refresh) Refresh Architecture](command:g1nation.architecture.refresh)\n\n---\n\n**Lessons / Knowledge**\n\n[$(lightbulb) Manage Lessons](command:g1nation.lesson.manage)\n[$(edit) Edit Agent ↔ Knowledge Map](command:g1nation.skills.editKnowledgeMap)"
}
],
"configuration": {
"title": "Astra",
"properties": {
"g1nation.multiAgentEnabled": {
"type": "boolean",
"default": false,
"description": "Enable Multi-Agent Workflow (Planner -> Researcher -> Writer) for complex tasks."
},
"g1nation.datacollectBridgeUrl": {
"type": "string",
"default": "http://127.0.0.1:3002",
"description": "Wiki/Datacollect MCP Bridge URL. /research, /benchmark, /youtube chat slash commands route here. The Bridge must be running (`npm run bridge` in the Datacollect project)."
},
"g1nation.datacollectSavePath": {
"type": "string",
"default": "",
"markdownDescription": "`/benchmark` 등 Datacollect slash 명령 결과물(markdown)을 저장할 폴더. **비워두면** Bridge 기본 위치(Bridge의 `WIKI_RAW_PATH` 환경변수)에 저장됩니다 — 코드/설정 어디에도 절대경로가 박히지 않습니다. 특정 폴더로 저장하려면 절대경로를 입력하세요. Astra Settings 패널의 'Datacollect' 섹션에서도 편집 가능."
},
"g1nation.datacollectCrawlDepth": {
"type": "number",
"default": 1,
"minimum": 0,
"maximum": 3,
"markdownDescription": "`/benchmark` 사이트맵 크롤 깊이 기본값. 0=루트만, 1=직속 자식, 2=손자, 3=깊은 크롤. 명령어에서 `depth=N`으로 그때그때 덮어쓸 수 있습니다. (단일 페이지 SPA는 깊이를 올려도 1페이지)"
},
"g1nation.datacollectMaxPages": {
"type": "number",
"default": 8,
"minimum": 1,
"maximum": 20,
"markdownDescription": "`/benchmark` 스캔 최대 페이지 수 기본값. 명령어에서 `pages=N`으로 덮어쓸 수 있습니다. Bridge에서 20으로 상한이 적용됩니다."
},
"g1nation.datacollectSynthesisTemperature": {
"type": "number",
"default": 0.1,
"minimum": 0,
"maximum": 2,
"markdownDescription": "`/benchmark` LLM 4-렌즈 합성의 temperature. 낮을수록(0.1) 환각·깨진 문자가 줄고 결과가 결정적이며, 높을수록 표현이 다양해집니다. 기본 0.1 권장."
},
"g1nation.chatTemperature": {
"type": "number",
"default": 0.3,
"minimum": 0,
"maximum": 2,
"markdownDescription": "채팅 응답 생성의 temperature. 낮을수록(0.2~0.3) 한국어 오타·깨진 토큰·환각이 줄고 결과가 안정적이며, 높을수록 표현이 다양해집니다. 분석·업무용은 0.3 권장."
},
"g1nation.meetUsesTasks": {
"type": "boolean",
"default": true,
"markdownDescription": "`/meet` 액션 아이템을 **Google Tasks** 에도 등록할지 여부. 시간 없이 마감일만 있는 \"할 일\" 모델. `meetUsesCalendar` 와 독립적으로 토글 가능 — 둘 다 true 면 양쪽 모두 등록. true 로 두려면 OAuth 재연결로 Tasks 스코프 동의 + Google Cloud Console 에서 Tasks API 활성화 필요(처음 1회)."
},
"g1nation.meetUsesCalendar": {
"type": "boolean",
"default": false,
"markdownDescription": "`/meet` 액션 아이템을 **Google Calendar** 일정(all-day)으로도 등록할지 여부. **기본 `false`** — Tasks 단독 등록으로 중복 방지 (Tasks 도 캘린더 사이드바에 같이 보이므로 둘 다 켜면 중복). true 로 켜면 Tasks + Calendar 양쪽 모두 등록."
},
"g1nation.teamVoiceGuide": {
"type": "string",
"default": "",
"editPresentation": "multilineText",
"markdownDescription": "`/draft` 외부 커뮤니케이션 초안 작성 시 모든 생성에 적용되는 **팀 보이스 가이드**. 말투/금기 표현/자주 쓰는 표현/회사 약어 정의 등을 자유 형식으로. 예: '회사명은 항상 \"Astra\" 로 표기. 존댓말 기본. 이모지는 슬랙에서만. 약어 ASAP/FYI 사용 금지...' 비워두면 가이드 없이 일반 초안 생성."
},
"g1nation.memoryEnabled": {
"type": "boolean",
"default": true,
"description": "Enable layered memory injection before each model response."
},
"g1nation.memoryShortTermMessages": {
"type": "number",
"default": 8,
"minimum": 0,
"description": "Number of recent conversation messages included as short-term memory."
},
"g1nation.memoryMediumTermSessions": {
"type": "number",
"default": 5,
"minimum": 0,
"description": "Number of recent saved chat sessions included as medium-term memory."
},
"g1nation.memoryLongTermFiles": {
"type": "number",
"default": 6,
"minimum": 0,
"description": "Number of relevant Second Brain markdown files included as long-term memory."
},
"g1nation.ollamaUrl": {
"type": "string",
"default": "http://127.0.0.1:11434",
"description": "Base URL for Ollama or LM Studio. Default: http://127.0.0.1:11434"
},
"g1nation.defaultModel": {
"type": "string",
"default": "gemma4:e2b",
"description": "Default model name to use for chat requests."
},
"g1nation.requestTimeout": {
"type": "number",
"default": 300,
"description": "Request timeout in seconds. Default: 300"
},
"g1nation.contextLength": {
"type": "number",
"default": 32768,
"minimum": 2048,
"description": "Model context window in tokens (prompt + generation combined). Set this to the value your loaded model is actually running with in LM Studio / Ollama. Astra budgets prompt and output against this so it never overflows. Default: 32768"
},
"g1nation.maxOutputTokens": {
"type": "number",
"default": 4096,
"minimum": 256,
"description": "Upper bound on tokens generated per response. The effective limit is reduced automatically when the prompt is large so input + output stays within g1nation.contextLength. Default: 4096"
},
"g1nation.contextSafetyMargin": {
"type": "number",
"default": 2048,
"minimum": 0,
"description": "Tokens kept free as a safety buffer for token-count estimation error. Default: 2048"
},
"g1nation.contextOverflowPolicy": {
"type": "string",
"enum": [
"stopAtLimit",
"truncateMiddle",
"rollingWindow"
],
"default": "stopAtLimit",
"description": "Fallback behavior (LM Studio) if the prompt still exceeds the context window after Astra's own budgeting. 'stopAtLimit' fails clearly so you notice; 'truncateMiddle'/'rollingWindow' drop content silently. Default: stopAtLimit"
},
"g1nation.autoCompactHistory": {
"type": "boolean",
"default": true,
"description": "Automatically drop the oldest conversation messages from the request when the prompt would exceed the context budget (the on-screen chat history is unaffected). Default: true"
},
"g1nation.smallModelContextCap": {
"type": "number",
"default": 0,
"minimum": 0,
"description": "Optional safety knob, OFF by default (0). Some very small models (≤3B) emit an empty/EOS response when given a prompt near their context window even though it nominally fits. If you observe that with a tiny model, set this to e.g. 819216384: for ≤3B models only, Astra then budgets the prompt against this smaller effective window instead of g1nation.contextLength. Never applies to 4B+ models. Leave 0 unless you actually hit the issue — it reduces the output-token budget. Default: 0 (disabled)"
},
"g1nation.autoContinueOnOutputLimit": {
"type": "boolean",
"default": true,
"description": "When a reply is cut off because it hit the output-token limit, Astra continues it internally (compressed request — original question + the answer so far, not the whole context again) and shows one merged answer, instead of asking you to say \"이어서 작성해줘\". Default: true"
},
"g1nation.maxAutoContinuations": {
"type": "number",
"default": 4,
"minimum": 0,
"maximum": 10,
"description": "Maximum number of automatic continuation rounds per reply (prevents runaway loops). Raise it (e.g. 56) for long-form answers on slow local models; set 0 to disable auto-continuation. Default: 4"
},
"g1nation.finalOnlyRetryOnThoughtLeak": {
"type": "boolean",
"default": true,
"description": "If the model emits only hidden reasoning (<think>, <|channel|>thought, \"Thinking Process:\" …) and no user-visible answer, Astra silently re-asks it for the final answer only. Hidden reasoning is never shown either way. Default: true"
},
"g1nation.lmStudio.idleTimeoutMs": {
"type": "number",
"default": 300000,
"minimum": 0,
"description": "Auto-eject the loaded LM Studio model after this many milliseconds of inactivity. Set to 0 to disable. Default: 300000 (5 minutes)."
},
"g1nation.lmStudio.autoLoadOnSelect": {
"type": "boolean",
"default": true,
"description": "Automatically load LM Studio models into memory when selected from the Astra sidebar."
},
"g1nation.lmStudio.sampling.topP": {
"type": "number",
"default": 0.9,
"minimum": 0,
"maximum": 1,
"description": "Nucleus sampling cutoff. Small / quantized models often spew wrong-neighbour tokens (한글 깨짐: 붕괴→붕점) when the tail is wide. Lower (0.80.9) tightens; 1.0 disables. Applied to both SDK and REST paths."
},
"g1nation.lmStudio.sampling.topK": {
"type": "number",
"default": 20,
"minimum": 0,
"description": "Top-K sampling cutoff. 0 disables. Default 20 — tighter for small models, raise to 4080 for large models that already sample well."
},
"g1nation.lmStudio.sampling.minP": {
"type": "number",
"default": 0.05,
"minimum": 0,
"maximum": 1,
"description": "Min-P floor — discards tokens with probability below this fraction of the top token. Good defence against rare-token glitches. 0 disables."
},
"g1nation.lmStudio.sampling.repeatPenalty": {
"type": "number",
"default": 1.1,
"minimum": 1,
"maximum": 2,
"description": "Repeat / frequency penalty to curb stutter (것입니다서입니다…). 1.0 disables. Values 1.051.2 are typical."
},
"g1nation.lmStudio.statsInBudget": {
"type": "boolean",
"default": true,
"description": "Show token/s and time-to-first-token from LM Studio prediction stats in the context-budget badge after each turn (SDK path only)."
},
"g1nation.lmStudio.draftModel": {
"type": "string",
"default": "",
"description": "[Speculative decoding] LM Studio model key of a small draft model (e.g. 'gemma-2b-it') used to accelerate the main model. Empty disables. 1.53x throughput on large models. The draft must be downloaded in LM Studio (load is automatic on first use)."
},
"g1nation.lmStudio.load.flashAttention": {
"type": "boolean",
"default": true,
"description": "[Load option] Enable Flash Attention when loading models. Faster generation + lower memory on compatible hardware, especially helpful for long contexts. Default: true."
},
"g1nation.lmStudio.load.gpuOffloadRatio": {
"type": "string",
"default": "max",
"description": "[Load option] How much of the model to offload to GPU. 'max' = all (default), 'off' = CPU only, or a number 01 (e.g. '0.5' = half). Numeric strings are parsed."
},
"g1nation.lmStudio.load.offloadKVCacheToGpu": {
"type": "boolean",
"default": true,
"description": "[Load option] Keep KV cache on GPU memory. Faster but requires VRAM headroom. Default: true."
},
"g1nation.lmStudio.load.keepModelInMemory": {
"type": "boolean",
"default": true,
"description": "[Load option] Prevent the model from being swapped out of system memory. Improves interactive responsiveness; raises RAM use. Default: true."
},
"g1nation.lmStudio.load.useFp16ForKVCache": {
"type": "boolean",
"default": false,
"description": "[Load option] Store KV cache in FP16 (halves cache memory). Tiny quality impact for most models — try if you run out of VRAM at long contexts. Default: false."
},
"g1nation.lmStudio.load.evalBatchSize": {
"type": "number",
"default": 0,
"minimum": 0,
"description": "[Load option] Token batch size during evaluation. 0 = engine default. Higher (5121024) improves prefill speed on GPU at the cost of memory."
},
"g1nation.localBrainPath": {
"type": "string",
"default": "",
"description": "Folder path for your local Second Brain knowledge base. Leave empty to use the default folder."
},
"g1nation.brainProfiles": {
"type": "array",
"default": [],
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Stable brain profile id."
},
"name": {
"type": "string",
"description": "Display name shown in the Astra brain selector."
},
"localBrainPath": {
"type": "string",
"description": "Local folder path used as this brain's markdown knowledge base."
},
"secondBrainRepo": {
"type": "string",
"description": "Optional Git repository URL for this brain."
},
"description": {
"type": "string",
"description": "Short note shown under the active brain status."
}
}
},
"description": "Multiple brain profiles. Each item supports id, name, localBrainPath, secondBrainRepo, and description."
},
"g1nation.activeBrainId": {
"type": "string",
"default": "",
"description": "Active brain profile id used for the current chat context."
},
"g1nation.secondBrainRepo": {
"type": "string",
"default": "",
"description": "Optional GitHub repository URL used for Second Brain sync."
},
"g1nation.autoPushBrain": {
"type": "boolean",
"default": false,
"description": "Automatically commit and push Second Brain changes after updates."
},
"g1nation.maxContextSize": {
"type": "number",
"default": 32000,
"description": "Maximum character count for active file context. Default: 32000"
},
"g1nation.maxAutoSteps": {
"type": "number",
"default": 50,
"description": "Maximum autonomous steps the agent can take per request. Default: 50"
},
"g1nation.dryRun": {
"type": "boolean",
"default": false,
"description": "If enabled, the agent will ask for approval before committing any file changes."
},
"g1nation.telegram.enabled": {
"type": "boolean",
"default": false,
"description": "Enable the Telegram bot integration. When on, Astra polls a bot you configure and replies to incoming messages. Off by default — Astra remains 100% local until you opt in."
},
"g1nation.telegram.allowedChatIds": {
"type": "array",
"default": [],
"items": {
"type": "number"
},
"description": "Optional allowlist of Telegram chat IDs that may message the bot. When empty, every chat that messages the bot is accepted (use with caution)."
},
"g1nation.telegram.defaultAgent": {
"type": "string",
"default": "",
"description": "Agent name (matches an entry in the Agent ↔ Knowledge map) used to scope Second Brain retrieval for Telegram replies. Empty falls back to the map's defaultAgent, then to whole-brain search."
},
"g1nation.telegram.agentByChatId": {
"type": "object",
"default": {},
"additionalProperties": {
"type": "string"
},
"description": "Per-chat override of the Telegram agent. Keys are stringified chat IDs, values are agent names from the knowledge map. Overrides telegram.defaultAgent for the listed chats."
},
"g1nation.telegram.contextChunks": {
"type": "number",
"default": 6,
"minimum": 0,
"maximum": 20,
"description": "How many Second Brain excerpts to inject into Telegram replies. Set 0 to disable RAG (plain prompt only)."
},
"g1nation.skillKnowledgeMapPath": {
"type": "string",
"default": "",
"description": "Absolute path to the agent ↔ knowledge mapping JSON. When empty, defaults to '<workspace>/.astra/agent-knowledge-map.json'."
},
"g1nation.skillKnowledgeMap": {
"type": "object",
"default": {},
"description": "Inline fallback for the agent ↔ knowledge mapping. Used only when the JSON file is missing. Shape: { defaultAgent?, agents: [{ name, knowledgeFolders, model?, description? }] }. Folder paths can be absolute, ~-prefixed, or relative to the active brain root."
},
"g1nation.agentSkillsPath": {
"type": "string",
"default": "",
"description": "Absolute path to the agent skills folder (`.agent/skills/*.md`). When empty, defaults to '<workspace>/.agent/skills'. Use this on Windows or when your skills live outside the workspace."
},
"g1nation.embeddingModel": {
"type": "string",
"default": "",
"description": "Embedding model registered in LM Studio / Ollama (e.g. 'text-embedding-bge-small-en-v1.5', 'nomic-embed-text', 'multilingual-e5-small'). When empty, Astra uses TF-IDF only. When set, the brain is embedded lazily in the background and retrieval blends TF-IDF + cosine similarity for synonym / paraphrase matching. Multilingual models are recommended for Korean content."
},
"g1nation.embeddingBlendAlpha": {
"type": "number",
"default": 0.5,
"minimum": 0,
"maximum": 1,
"description": "Hybrid score blend: 0 = pure TF-IDF (sparse / keyword), 1 = pure embedding cosine (dense / semantic), 0.5 = balanced. Only used when g1nation.embeddingModel is set. Default 0.5."
},
"g1nation.conflictHighlightingEnabled": {
"type": "boolean",
"default": true,
"description": "Conflict Surface — 검색된 출처에서 충돌/논란 신호 감지 시 [CONFLICT WARNINGS] 블록을 시스템 프롬프트에 주입. LLM 이 상충되는 관점을 명시하고 사용자 판단에 위임하도록. 기본 켜짐."
},
"g1nation.conflictSeverityThreshold": {
"type": "string",
"enum": ["low", "medium", "high"],
"default": "medium",
"description": "Conflict 자기-신호 surface 시 최소 severity 임계. low=가장 민감(노이즈 가능), medium=균형(기본), high=강한 충돌만."
},
"g1nation.conflictCrossDocEnabled": {
"type": "boolean",
"default": true,
"description": "교차-문서 발산 감지 — 같은 주제 ≥2 chunks 의 본문 Jaccard < 0.30 인 잠재 모순 쌍을 [CONFLICT WARNINGS] 에 함께 표시. 기본 켜짐."
},
"g1nation.coveEnabled": {
"type": "boolean",
"default": true,
"description": "Chain-of-Verification (CoVe) — 답변 *작성 전* 그라운딩 체크리스트를 시스템 프롬프트에 주입해 모델이 self-verify 하도록. 할루시네이션 방지 + 출처 명확화. 기본 켜짐."
},
"g1nation.coveTopSourcesCount": {
"type": "number",
"default": 5,
"minimum": 1,
"maximum": 15,
"description": "CoVe 체크리스트에 나열할 상위 출처 개수. 너무 많으면 프롬프트 비대, 너무 적으면 그라운딩 부족. 기본 5."
},
"g1nation.coveStrictMode": {
"type": "boolean",
"default": false,
"description": "CoVe Strict 모드 — 모든 사실 주장 뒤에 출처 ID [S1] 형식으로 inline 인용 강제. 답변이 학술적·verbose 해질 수 있어 기본 off."
},
"g1nation.actionabilityEnabled": {
"type": "boolean",
"default": true,
"description": "Actionability Scoring — '현재 작업 상태' 신호(최근 슬래시 명령 + 열린 파일) 로 검색 결과를 재가중. 지금 작업 중인 컨텍스트와 직접 연결된 문서를 우선. 기본 켜짐."
},
"g1nation.distillationEnabled": {
"type": "boolean",
"default": true,
"description": "Distillation Loop — stale Episodic Memory 를 LongTerm 'episode-digest' 로 승급해 검색 노이즈 방지. /memory distill 수동 + 세션 종료 시 자동 트리거. 기본 켜짐."
},
"g1nation.distillationAgeThresholdDays": {
"type": "number",
"default": 30,
"minimum": 1,
"maximum": 365,
"description": "며칠 이상 지난 episode 를 distill 대상으로 할지. 기본 30일."
},
"g1nation.distillationIntervalDays": {
"type": "number",
"default": 7,
"minimum": 1,
"maximum": 90,
"description": "자동 distillation 의 최소 간격 (일). 마지막 실행 후 이 일수가 지나야 재실행. 기본 7일."
},
"g1nation.distillationArchiveMode": {
"type": "string",
"enum": ["mark-promoted", "archive-file"],
"default": "mark-promoted",
"description": "Distillation 후 원본 episode 처리: 'mark-promoted'=플래그만 (파일 보존, 기본), 'archive-file'=memory/episodes/archive/ 로 파일 이동."
},
"g1nation.hierarchicalReweightEnabled": {
"type": "boolean",
"default": true,
"description": "Hierarchical Context Window — 질의·문서 추상도(concrete/operational/strategic) 매칭으로 검색 결과 재가중. 같은 레벨 +15%, 양 끝 mismatch -30%. LLM 호출 없음, 결정적. 기본 켜짐."
},
"g1nation.semanticRerankEnabled": {
"type": "boolean",
"default": false,
"description": "Semantic Re-ranking — 검색된 selectedChunks 의 순서를 LLM 한 번 호출로 의도-부합도 순 재정렬. 매 turn 1회 추가 LLM 호출 (latency 비용). 기본 OFF — 명시적 on 해야 함."
},
"g1nation.semanticRerankModel": {
"type": "string",
"default": "",
"description": "Semantic Re-ranking 전용 모델 ID. 비면 defaultModel 사용. 빠른 작은 모델(예: gemma2:2b) 권장 — latency 줄임."
},
"g1nation.semanticRerankCandidateK": {
"type": "number",
"default": 15,
"minimum": 2,
"maximum": 30,
"description": "Re-rank 대상 상위 후보 개수. 많을수록 quality↑ latency↑ token↑. 기본 15."
},
"g1nation.semanticRerankTimeoutSec": {
"type": "number",
"default": 8,
"minimum": 1,
"maximum": 60,
"description": "Re-rank LLM 호출 타임아웃 (초). 초과 시 원순서 그대로 — 검색 실패 안 됨. 기본 8."
},
"g1nation.intentClarificationEnabled": {
"type": "boolean",
"default": true,
"description": "Intent Clarification — 모호 질의(환경/대상/범위/포맷/마감 누락) 감지 시 LLM 에게 추측 답변보다 *역질문 우선* 지시. 기본 켜짐."
},
"g1nation.intentClarificationStrictness": {
"type": "string",
"enum": ["low", "medium", "high"],
"default": "medium",
"description": "모호 판정 임계. low=가장 덜 묻기(2개+ missing), medium=균형(1개+), high=가장 자주 묻기 (1개+ OR 짧은 질의+trigger)."
},
"g1nation.citationTraceEnabled": {
"type": "boolean",
"default": true,
"description": "Citation Trace — 답변 끝에 사용된 출처를 *출처:* 한 줄로 정리 지시. CoVe Strict 의 가벼운 형제, 항상 ON 권장. 기본 켜짐."
},
"g1nation.selfCheckEnabled": {
"type": "boolean",
"default": false,
"description": "Post-hoc Self-Check — 답변 완료 후 별도 LLM 호출 1회로 검증 (답변 직접도/그라운딩/논리 모순). 결과를 답변 아래 footer 한 줄로 표시. 매 turn 추가 LLM 호출 (latency 비용). 기본 OFF — 명시적 opt-in."
},
"g1nation.selfCheckModel": {
"type": "string",
"default": "",
"description": "Self-check 전용 모델 ID. 비면 defaultModel 사용. 빠른 작은 모델 (예: gemma2:2b) 권장."
},
"g1nation.selfCheckTimeoutSec": {
"type": "number",
"default": 6,
"minimum": 1,
"maximum": 60,
"description": "Self-check LLM 호출 타임아웃 (초). 초과 시 흐릿한 한 줄 footer 로 fallback. 기본 6."
},
"g1nation.glossaryEnabled": {
"type": "boolean",
"default": true,
"description": "Terminology Dictionary — 사용자 편집 글로서리(.astra/glossary.md) 를 시스템 프롬프트에 주입. 표준 표기 강제 + 답변 직전 Term Check. 파일 없으면 자동 no-op. 기본 켜짐."
},
"g1nation.glossaryPath": {
"type": "string",
"default": ".astra/glossary.md",
"description": "Glossary 파일 상대 경로 (workspace root 기준). 기본 '.astra/glossary.md'."
},
"g1nation.glossaryMaxBodyLength": {
"type": "number",
"default": 4000,
"minimum": 500,
"maximum": 20000,
"description": "Glossary 본문 시스템 프롬프트 cap (chars). 너무 크면 토큰 비용↑. 초과분은 잘림. 기본 4000."
},
"g1nation.termValidatorEnabled": {
"type": "boolean",
"default": true,
"description": "Post-gen Term Validator — 답변 완료 후 글로서리 forbidden 단어 결정론적 정규식 스캔. Terminology Dictionary 의 instructional 지시를 deterministic 검증으로 보완. LLM 호출 없음, 매 turn 안전. footer 한 줄 표시. 기본 켜짐."
},
"g1nation.knowledgeMix.secondBrainWeight": {
"type": "number",
"default": 50,
"minimum": 0,
"maximum": 100,
"description": "Knowledge Mix (0100): how heavily the assistant should lean on Second Brain evidence vs. its own general knowledge. 0 = Second Brain disabled (model knowledge only). 50 = balanced (legacy default). 100 = Second Brain is the primary evidence; model knowledge only fills harmless background. Per-agent overrides in the Agent Mapping panel win over this global value."
},
"g1nation.workflow.multiAgentMode": {
"type": "string",
"enum": [
"auto",
"always",
"off"
],
"default": "auto",
"markdownDescription": "Multi-Agent(5단계) 파이프라인 발동 모드.\n\n- `auto` (기본): 작은 모델(≤4B) 감지, 큰 prompt(컨텍스트의 30%+), 명시적 키워드(보고서/리뷰/심층 분석…), 또는 사용자가 `multiAgentEnabled`를 켰을 때 자동으로 발동. 짧은 인사·잡담은 제외.\n- `always`: 인사·잡담을 제외한 모든 요청에 5단계 파이프라인 사용. 작은 모델로도 답변이 한 번에 끝나지 않는다면 이 모드가 안정적.\n- `off`: 기존 키워드/길이 휴리스틱 + 수동 `multiAgentEnabled` 토글만 사용 (legacy 동작)."
},
"g1nation.workflow.autoCtxFractionThreshold": {
"type": "number",
"default": 0.3,
"minimum": 0.05,
"maximum": 0.95,
"markdownDescription": "`workflow.multiAgentMode = auto` 일 때, prompt 토큰이 효과적 context window 의 이 비율(0~1)을 넘으면 5단계 파이프라인을 강제 발동. 기본 0.30 — 작은 모델이 input으로 컨텍스트의 30% 이상을 먹기 시작하면 한 번에 답하려다 EOS/잘림이 잘 발생한다."
},
"g1nation.chunkedSwitchTokens": {
"type": "number",
"default": 50000,
"minimum": 1000,
"markdownDescription": "**입력 prompt 가 이 토큰 수 *미만* 이면 Multi-Agent(chunked) 파이프라인 발동 안 함** — 모델이 단일 호출로 처리.\n\n키워드(\"요약\", \"리뷰\", \"보고서\" 등) 나 길이(>240자) 같은 트리거가 있어도 입력이 이 임계값 미만이면 무시되고 한 번에 답변 → 답변 속도 크게 향상.\n\n기본 **50000** — 대부분의 사용 환경에 적합. 매우 작은 컨텍스트 모델로 큰 입력을 자주 다룬다면 OOM 방지 차원에서 이 값을 낮출 수 있음."
},
"g1nation.chunkedMaxSections": {
"type": "number",
"default": 3,
"minimum": 1,
"maximum": 10,
"markdownDescription": "**Chunked 파이프라인이 답변을 쪼갤 수 있는 최대 섹션 수.**\n\n실제 LLM 호출 횟수 = `1 (outline) + N (sections) + 1 (polish)` = **2 + N 회**.\n- `1` → 총 3회 (가장 빠름, 답변이 단순할 때만 적합)\n- `3` (기본) → 총 5회\n- `5` → 총 7회 (세분화 필요할 때만)\n\n작을수록 답변 속도 빠름, 클수록 답변이 더 세분화돼서 복잡한 보고서·기획서에 유리. 기본 3 — 일반 채팅에 적합."
},
"g1nation.polishPersonaOverride": {
"type": "string",
"default": "",
"editPresentation": "multilineText",
"markdownDescription": "**ChunkedWriter 의 polish 단계 system prompt 를 직접 정의** — 답변 톤·구조를 도메인에 맞게 커스텀.\n\n비워두면 기본 polish persona (한 줄 요약 + subheading + 5-check self-review) 사용. 텍스트를 입력하면 그 내용이 그대로 polish 단계의 system prompt 로 들어갑니다.\n\n사용 예: 격식체 답변, 반말 답변, 법률·마케팅·의료 등 도메인 특화 톤. 사용자가 Astra Settings 패널 textarea 로 편집 권장."
},
"g1nation.liveStreamTokens": {
"type": "boolean",
"default": true,
"markdownDescription": "모델 토큰을 받는 즉시 채팅 버블에 흘려보낼지 여부.\n\n- `true` (기본): 토큰을 받는 즉시 표시 → TTFT 체감 속도 향상. 생성이 끝나면 sanitize 된 최종 답변으로 `streamReplace` 가 한 번에 교체하므로 control token 노출은 잠깐만 가능.\n- `false`: 토큰을 내부에서만 누적, sanitize(`<|channel|>thought` / `<think>` / `Thinking Process:` 등 제거) 끝난 **최종 답변만 한 번에** 표시. 모델의 control token 이 잠깐이라도 화면에 노출되는 누설을 원천 차단."
},
"g1nation.outputFormat": {
"type": "string",
"enum": [
"plain",
"markdown"
],
"default": "plain",
"markdownDescription": "최종 답변 표시 방식.\n\n- `plain` (기본): 모델이 무심코 내보낸 마크다운 마커(`##`, `**`, `__`, `> `, `* ` 등)를 후처리로 모두 제거. 섹션 라벨 텍스트(예: `핵심 요약`)는 유지되지만 헤더 마커는 사라져 깔끔한 plain text 로 보임. 작은 로컬 모델이 학습된 습관으로 `## 다음 한 수` 같은 마커를 흘리는 문제 차단.\n- `markdown`: legacy 동작. 모델 출력을 그대로 렌더러에 넘김."
},
"g1nation.chronicleAutoRecord": {
"type": "boolean",
"default": true,
"markdownDescription": "자동 기록 (Project Chronicle Auto-Record).\n\n- `true` (기본): 매 chat turn 후 의미 있는 대화(planning / decision / bug / development / discussion 유형 자동 판별)를 활성 프로젝트의 Chronicle 폴더에 자동 저장.\n- `false`: 자동 저장 OFF. 수동 기록 (도구 ▾ 의 기록 항목, `/wiki` 등) 은 계속 가능.\n\n사이드바 **도구 ▾** 메뉴의 `자동 기록` 토글로 즉시 전환 가능 — 설정 패널까지 들어갈 필요 없음."
},
"g1nation.company.intentClassifierModel": {
"type": "string",
"default": "",
"description": "Model used to classify whether an incoming chat message in 1인 기업 모드 is a (a) casual chat / question, (b) follow-up on the previous round, or (c) a brand-new task that should trigger the full work pipeline. Empty → uses g1nation.defaultModel. Pick a fast small model (e.g. gemma 4 e2b) so the classifier doesn't add latency before every chat send. The classifier runs once per user message and returns a one-token-ish JSON verdict, so even slow hardware sees minimal overhead."
},
"g1nation.company.disableIntentClassifier": {
"type": "boolean",
"default": false,
"description": "Bypass the intent classifier and always run the full work pipeline on every chat message in 1인 기업 모드 (legacy behaviour). Enable this only if you want every input — including 'thanks', 'show me X again' — to dispatch all agents. Off by default because most chat messages aren't new work and shouldn't burn a full pipeline."
},
"g1nation.company.autoSelectPipeline": {
"type": "boolean",
"default": true,
"description": "Let the intent classifier *automatically switch* to the pipeline it recommends for this turn (e.g. short '기획서까지만' for a planning ask, full '풀 프로덕트' for an end-to-end product). Your explicitly-activated pipeline is bypassed for the round but the activation itself isn't changed. On by default — the classifier's read of the user's intent (especially explicit signals like '기획만'·'디자인만') should be honoured. Set to false if you'd rather always run the pipeline you activated yourself."
},
"g1nation.company.intentAlignmentMode": {
"type": "string",
"enum": [
"off",
"smart",
"strict"
],
"default": "smart",
"description": "Intent Alignment — turn user prompts into an explicit Requirement Contract (C-G-C-F-Q) before dispatching a pipeline. 'off' = legacy, pipeline runs immediately. 'smart' (default) = run when confidence is high, else show a confirmation card; ask up to N rounds of clarifying questions if information is missing. 'strict' = always show the contract card and require user confirmation, regardless of confidence. Goal: stop agents from silently guessing at the user's mental model."
},
"g1nation.company.intentAlignmentMaxRounds": {
"type": "number",
"minimum": 1,
"maximum": 5,
"default": 3,
"description": "Maximum back-and-forth rounds the Intent Alignment analyzer is allowed to ask before forcing a 'confirm or cancel' card (it stops asking new questions and shows the current contract for user approval). Each round = one LLM call. Default 3."
},
"g1nation.selfReflector.enabled": {
"type": "boolean",
"default": false,
"description": "Self-Reflector Phase A — append a [Self-Reflector Check] block at the end of every substantive LLM answer (Consistency / Completeness / Accuracy, plus References / Paths for code answers). Zero extra LLM calls — the rule lives in the system prompt and the model self-imposes the checklist. OFF by default: the check block is an internal verification log that leaks into the user-facing answer and reads as unpolished. Enable only if you want that transparency block visible."
},
"g1nation.selfReflector.externalVerification": {
"type": "boolean",
"default": false,
"description": "Self-Reflector Phase B — after every 1인 기업 specialist response, run a *separate* LLM call to verify the output from an outside-context perspective (catches the 'same model self-validates' blind spot). Failed checks trigger one auto-revise round. Off by default — adds +1 LLM call per dispatched stage."
},
"g1nation.selfReflector.executionVerification": {
"type": "boolean",
"default": false,
"description": "Self-Reflector Phase C — after a code file is created via <create_file>, automatically run the language's syntax check (Python: py_compile, JS: node --check, TS: project tsc --noEmit). Failures are surfaced in the action report so the user (and the agent on a follow-up) can see exactly what broke. Requires the language toolchain installed on the user's machine. Off by default."
},
"g1nation.company.pixelOffice.enabled": {
"type": "boolean",
"default": true,
"description": "Show the Pixel Office visualisation panel above the chat — a small pixel-office-style display that mirrors the agent's current pipeline status (analyzing, need_clarification, executing, reviewing, waiting_approval, done, etc.) and the current task / contract / open questions. UI layer only; turning it off does not change any agent behaviour."
},
"g1nation.company.pixelOffice.bubbles": {
"type": "boolean",
"default": true,
"description": "Show short comic-style speech bubbles above the Pixel Office character on status changes / key events (e.g. '코드 들어간다', '잠깐, 이건 다시 보자', '좋아, 끝났다!'). Bubbles are purely narrative — they never influence the agent's decisions. Disable for a quieter UI."
},
"g1nation.google.clientId": {
"type": "string",
"default": "",
"scope": "machine",
"markdownDescription": "Google OAuth Client ID — `console.cloud.google.com/apis/credentials` → OAuth 2.0 Client ID (Desktop app) 생성 후 복사. **`Astra: Google Calendar OAuth 연결 (쓰기) 🔐`** 명령을 한 번 실행하면 이 값이 자동으로 채워집니다. Calendar + Sheets 둘 다 이 자격증명을 공유.\n\n_scope: machine — Settings Sync 로 다른 기기에 공유되지 않음._"
},
"g1nation.google.clientSecret": {
"type": "string",
"default": "",
"scope": "machine",
"markdownDescription": "Google OAuth Client Secret — Client ID 와 같은 페이지에서 발급. Desktop app OAuth 의 secret 은 Google 가이드상 *진짜 비밀이 아닌 식별자* 지만, settings.json 에 그대로 들어가므로 git 커밋 / 화면 공유 시 주의.\n\n_scope: machine — Settings Sync 안 됨._"
},
"g1nation.google.calendarId": {
"type": "string",
"default": "primary",
"markdownDescription": "일정을 등록할 Google Calendar 식별자. 기본 `primary` (본인 메인 캘린더). 특정 캘린더 쓰려면 Calendar 설정 → 캘린더 통합 → 'Calendar ID' 복사 (예: `xxxxxxx@group.calendar.google.com`)."
},
"g1nation.google.defaultEventDurationMinutes": {
"type": "number",
"default": 60,
"minimum": 5,
"maximum": 1440,
"description": "end / duration 둘 다 없는 일정의 기본 길이 (분). agent 가 회의록에서 시각만 추출하고 종료 시각은 명시 안 했을 때 적용."
},
"g1nation.google.icalUrl": {
"type": "string",
"default": "",
"scope": "machine",
"markdownDescription": "Google Calendar **비공개 iCal URL** — 읽기 전용 모드용. `calendar.google.com/calendar/u/0/r/settings` → 본인 캘린더 → '캘린더 통합' → '비공개 주소(iCal 형식)' 복사. **이 URL 을 가진 사람은 본인 캘린더 모든 일정을 볼 수 있으니 절대 공개 금지.**\n\n_scope: machine — Settings Sync 안 됨. OAuth 와는 별개 — 둘 다 셋업해도 되고 한 쪽만 해도 됨._"
},
"g1nation.google.icalDaysAhead": {
"type": "number",
"default": 14,
"minimum": 1,
"maximum": 90,
"description": "iCal 캐시에 포함할 다가오는 일정 기간 (일). default 14 = 2주치."
},
"g1nation.providers.openrouter.enabled": {
"type": "boolean",
"default": false,
"description": "OpenRouter cloud provider 활성화 — Claude/Gemini/GPT 등 100+ 모델을 OpenAI 호환 API 로 사용. API key 는 Astra Settings 패널에서 등록 (Secret Storage 사용, settings.json 비저장)."
},
"g1nation.providers.openrouter.defaultModel": {
"type": "string",
"default": "",
"description": "OpenRouter 의 기본 모델 (예: 'anthropic/claude-3.5-sonnet'). 모델 선택 시 'openrouter:<model>' 형식으로 사이드바 dropdown 에 표시."
},
"g1nation.providers.anthropic.enabled": {
"type": "boolean",
"default": false,
"description": "Anthropic Claude 직접 API 활성화. OpenRouter 보다 마진 적고 prompt caching 등 native 기능 사용 가능. API key 는 Secret Storage."
},
"g1nation.providers.anthropic.defaultModel": {
"type": "string",
"default": "claude-3-5-sonnet-20241022",
"description": "Anthropic 의 기본 모델. 예: 'claude-3-5-sonnet-20241022', 'claude-3-5-haiku-20241022'."
},
"g1nation.providers.gemini.enabled": {
"type": "boolean",
"default": false,
"description": "Google Gemini 직접 API 활성화. 1M context (gemini-1.5-pro), 무료 tier 등 native 기능 사용. API key 는 Secret Storage."
},
"g1nation.providers.gemini.defaultModel": {
"type": "string",
"default": "gemini-2.0-flash-exp",
"description": "Gemini 의 기본 모델. 예: 'gemini-2.0-flash-exp', 'gemini-1.5-pro', 'gemini-1.5-flash'."
},
"g1nation.devilAgent.enabled": {
"type": "boolean",
"default": false,
"markdownDescription": "**Devil's Advocate (도현) 활성화** — 매 답변 직후 별도 LLM 호출로 *비판적 sparring partner* 가 한 문단 반박. 사용자의 사고를 *수동적 수용 → 능동적 방어* 로 전환시키는 게 목표. Local LLM 동일 모델 재사용, ~10-15% 추가 비용."
},
"g1nation.stocks.watcherEnabled": {
"type": "boolean",
"default": true,
"markdownDescription": "**주식 자동 모니터링 활성화** — VS Code 시작 시 watcher 가동. KST 09:00 / 15:00 자동 트리거: Yahoo 현재가 갱신 → Google Sheets 동기화 → 텔레그램 보고서. false 면 슬래시 명령으로만 수동 실행."
},
"g1nation.stocks.spreadsheetId": {
"type": "string",
"default": "",
"markdownDescription": "**Stocks Google Sheets ID** — `https://docs.google.com/spreadsheets/d/<여기>/...` URL 의 ID 부분. 미설정이면 watcher / `/stocks sync` 가 silent skip. OAuth scope 은 calendar 와 공유 (sheets scope 이미 포함)."
},
"g1nation.stocks.sheetSwing": {
"type": "string",
"default": "Sheet1",
"markdownDescription": "**스윙/중기 종목 시트 탭 이름.** 기본 'Sheet1'. spreadsheet 안에 이 이름의 탭이 미리 존재해야 함."
},
"g1nation.stocks.sheetLong": {
"type": "string",
"default": "Sheet2",
"markdownDescription": "**장기투자 종목 시트 탭 이름.** 기본 'Sheet2'."
},
"g1nation.stocks.sheetUltraLow": {
"type": "string",
"default": "Sheet3",
"markdownDescription": "**저평가우량주 시트 탭 이름.** 기본 'Sheet3'."
},
"g1nation.stocks.telegramChatId": {
"type": "number",
"default": 0,
"markdownDescription": "**Stocks 보고서 전용 텔레그램 chatId** — fallback. `g1nation.telegram.allowedChatIds` 가 비어있을 때만 사용. 0 = 미설정."
}
}
}
},
"scripts": {
"vscode:prepublish": "npm run test && npm run compile",
"compile": "esbuild src/extension.ts --bundle --platform=node --external:vscode --outfile=out/extension.js",
"watch": "tsc -watch -p ./",
"test": "jest --no-cache --forceExit",
"test:engine": "jest tests/agentEngine.test.ts --verbose --no-cache",
"pretest": "npm run compile"
},
"devDependencies": {
"@types/jest": "^29.5.14",
"@types/node": "18.x",
"@types/vscode": "^1.80.0",
"@vercel/ncc": "^0.38.4",
"esbuild": "^0.28.0",
"jest": "^29.7.0",
"ts-jest": "^29.4.9",
"typescript": "^5.1.3"
},
"dependencies": {
"@lmstudio/sdk": "^1.5.0",
"pdf-parse": "^2.4.5"
}
}