{
  "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.297",
  "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.eval.retrieval",
        "title": "Astra: 검색 평가 실행 (recall@k / MRR)"
      },
      {
        "command": "g1nation.eval.tasks",
        "title": "Astra: 업무 평가 실행 (회의록 골든셋)"
      },
      {
        "command": "g1nation.growth.report",
        "title": "Astra: 성장 리포트 (Reflection 추이)"
      },
      {
        "command": "g1nation.growth.learningQueue",
        "title": "Astra: 학습 큐 갱신 (Need Engine)"
      },
      {
        "command": "g1nation.knowledge.decayAudit",
        "title": "Astra: 지식 노후 점검 (Knowledge Decay)"
      },
      {
        "command": "g1nation.research.runQueue",
        "title": "Astra: 학습 실행 (Research Agent — 승인된 큐 항목)"
      },
      {
        "command": "g1nation.growthCycle.runNow",
        "title": "Astra: 주간 성장 사이클 지금 실행 (평가→학습큐→노후점검→승인분 실행)"
      },
      {
        "command": "g1nation.embeddings.backfill",
        "title": "Astra: 두뇌 임베딩 전체 색인"
      },
      {
        "command": "g1nation.sleepDigest.runNow",
        "title": "Astra: 지식 사전 소화 지금 실행 (Sleep-time Digest)"
      },
      {
        "command": "g1nation.conflictScan.runNow",
        "title": "Astra: 지식 충돌 스캔 지금 실행 (신규 문서 ↔ 기존 지식)"
      },
      {
        "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.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.datacollectBridgeTarget": {
          "type": "string",
          "enum": [
            "local",
            "nas"
          ],
          "default": "nas",
          "markdownDescription": "Datacollect 백엔드(Bridge)를 어디로 보낼지 선택. **`nas`**(기본) = `g1nation.datacollectBridgeNasUrl`(NAS 상시 구동 Bridge, 예: `https://dc.koritips.com`). **`local`** = `g1nation.datacollectBridgeUrl`(로컬 `npm run bridge` — 더 이상 권장 안 함). `nas`인데 URL이 비어 있으면 안전하게 로컬로 폴백합니다. NAS 사용 시 `datacollectBridgeNasToken`(= Bridge `BRIDGE_AUTH_TOKEN`)도 설정 필요."
        },
        "g1nation.datacollectBridgeUrl": {
          "type": "string",
          "default": "http://127.0.0.1:3002",
          "description": "[local 타깃] Wiki/Datacollect MCP Bridge URL. /benchmark, /youtube, /wikify chat slash commands route here. The Bridge must be running (`npm run bridge` in the Datacollect project). 백엔드를 NAS로 옮긴 경우 보통 사용하지 않음 — `datacollectBridgeTarget=nas` 권장."
        },
        "g1nation.datacollectBridgeNasUrl": {
          "type": "string",
          "default": "https://dc.koritips.com",
          "markdownDescription": "[nas 타깃] NAS에서 상시 구동하는 경량 Bridge URL. 기본 `https://dc.koritips.com`(DSM 리버스 프록시 → 컨테이너 :3002). `datacollectBridgeTarget`을 `nas`로 두면 여기로 호출합니다. 비워두면 로컬로 폴백."
        },
        "g1nation.datacollectBridgeNasToken": {
          "type": "string",
          "default": "",
          "markdownDescription": "[nas 타깃] NAS Bridge가 요구하는 `x-bridge-token` 값(Bridge의 `BRIDGE_AUTH_TOKEN`과 일치). `nas` 타깃일 때만 요청 헤더에 실립니다. 로컬 타깃에는 영향 없음."
        },
        "g1nation.domainKnowledge.generalFolder": {
          "type": "string",
          "default": "",
          "markdownDescription": "[단계별 지식] 모든 단계가 공통으로 참조하는 General Knowledge 폴더 (활성 두뇌 기준 상대 경로, 예: `General`). 질문이 수학/코딩/팩트로 분류될 때 해당 Specialty 폴더와 함께 검색됩니다."
        },
        "g1nation.domainKnowledge.mathFolder": {
          "type": "string",
          "default": "",
          "markdownDescription": "[단계별 지식] 수학·논리 질문 전용 Specialty 폴더 (두뇌 상대 경로, 예: `Specialty_Math`). 비워두면 수학 질문도 전체 두뇌를 검색합니다."
        },
        "g1nation.domainKnowledge.codingFolder": {
          "type": "string",
          "default": "",
          "markdownDescription": "[단계별 지식] 코딩·디버깅 질문 전용 Specialty 폴더 (두뇌 상대 경로, 예: `10_Wiki/Topic_Programming`). 비워두면 코딩 질문도 전체 두뇌를 검색합니다."
        },
        "g1nation.domainKnowledge.factsFolder": {
          "type": "string",
          "default": "",
          "markdownDescription": "[단계별 지식] 일반 상식·팩트 질문 전용 Specialty 폴더 (두뇌 상대 경로, 예: `Specialty_Facts`). 비워두면 팩트 질문도 전체 두뇌를 검색합니다."
        },
        "g1nation.datacollectLocalProjectPath": {
          "type": "string",
          "default": "",
          "markdownDescription": "로컬 Datacollector 프로젝트 폴더 경로 (도구 메뉴의 'NotebookLM 백엔드 실행' 버튼이 여기서 `npm run bridge` 를 실행). 비워두면 워크스페이스 주변에서 자동 탐색하고, 못 찾으면 버튼 클릭 시 폴더 선택 창이 떠서 자동 저장됩니다."
        },
        "g1nation.datacollectSavePath": {
          "type": "string",
          "default": "",
          "markdownDescription": "`/wikify` · `/meet` · `/benchmark` · `/youtube` · `/review` · `/email wikify` 가 생성하는 **지식 문서(markdown)의 저장 폴더** (v2.2.273부터 **PC 로컬에 직접 저장** — 이전에는 Bridge 서버에 저장돼 NAS 이전 후 파일이 안 보이던 문제 해소). **비워두면** 활성 두뇌 폴더의 `Datacollect_Wiki/` 에 저장돼 brain 검색에 자동 포함됩니다. 다른 위치를 쓰려면 절대경로 입력. Astra Settings 패널의 **지식·기억 탭**에서도 편집 가능."
        },
        "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.meetVerifyPass": {
          "type": "boolean",
          "default": true,
          "markdownDescription": "`/meet` 회의록 생성 후 **검수 루프** 실행 여부(기본 ON). 검수자가 완성된 회의록을 녹취록(또는 추출 노트)과 LLM 으로 대조해 ①근거 미확인 ②\"참석자 N\" 화자번호 잔존 ③결정 과확정(테스트후결정·검토필요) ④폐기 가설 격상 ⑤액션 중복 을 지적하면, **지적된 부분만** 수정하고 `검증 통과` 또는 최대 라운드(`meetVerifyMaxRounds`)까지 재검수한다(전체 재작성 X). 통과 후 편집자(`meetEditorPass`)가 보고서 형태로 정리. LLM 호출이 여러 번 추가되어 느려짐 — 끄려면 false."
        },
        "g1nation.meetVerifyMaxRounds": {
          "type": "number",
          "default": 2,
          "minimum": 1,
          "maximum": 4,
          "markdownDescription": "`/meet` 검수 루프의 **최대 반복 횟수**(기본 `2`). 검수자가 지적 → 지적분만 수정 → 재검수를 이 횟수까지 반복하며, 도중에 `검증 통과`가 나오면 즉시 종료한다. 끝까지 지적이 남으면 회의록 끝에 `⚠️ 검수 미해결 지적` 섹션으로 남긴다. 라운드마다 LLM 호출 2회(검수+수정)가 추가되므로 로컬 모델에서는 2~3 권장."
        },
        "g1nation.meetEditorPass": {
          "type": "boolean",
          "default": true,
          "markdownDescription": "`/meet` 검수 통과 후 **편집(Editor) 패스** 실행 여부(기본 ON). 편집자가 회의록 전체를 **보고서 형태**로 구조·가독성만 다듬는다(섹션 정돈·표 정렬·개조식 통일). 근거 소스에 없는 사실을 새로 만들지 않고 기존 항목을 삭제·축약하지 않으며, 편집 직후 1회 재검증으로 환각이 없는지 확인해 문제가 있으면 편집 전 회의록을 유지한다. LLM 호출 2회 추가 — 끄려면 false."
        },
        "g1nation.meetTaskDateFallback": {
          "type": "string",
          "default": "today",
          "enum": ["today", "hold"],
          "markdownDescription": "`/meet` 액션 아이템 중 **기한이 없는(파싱 불가) 확정·기한미정** 항목의 처리. `today`(기본): 회의록 **작성일(오늘)** 을 기한으로 잡아 자동 등록 — 매번 '자동 등록 0건'이 되던 문제를 해소. `hold`: 이전처럼 등록하지 않고 보류→`/meet confirm` 으로 수동 등록. (진행미정·조건부 항목은 이 설정과 무관하게 항상 보류.)"
        },
        "g1nation.meetTaskDetailExpand": {
          "type": "boolean",
          "default": true,
          "markdownDescription": "`/meet` 등록 시 각 액션의 **작업 상세를 자동으로 풀어 씀**(기본 ON). 슬림 액션 표에는 상세 칸이 없어 Google Task 노트가 액션 한 줄만 담겨 앙상했던 문제를 해소한다 — 등록 직전 배치 LLM 1회로 각 액션을 '회의에 참석하지 않은 담당자도 무엇을·왜·어떤 결과물을 목표로 하는지 이해'할 2~3문장 설명으로 풀어 task 노트의 `■ 작업 상세`에 넣는다(근거 소스에 있는 내용만, 환각 금지). LLM 호출 1회 추가 — 끄려면 false(그러면 액션 한 줄만 등록)."
        },
        "g1nation.meetTeamRoster": {
          "type": "string",
          "default": "",
          "markdownDescription": "`/meet` 화자 정규화용 **회사 표준 팀/역할 분류**(쉼표 구분). STT 화자번호(\"참석자 N\")를 실명 추측 대신 이 팀들 중 하나로 귀속해 오귀속을 막는다. 비워두면 기본값(`PD, 기획, 사업, 클라이언트, 넥서스개발팀(서버), UI, 배경팀, 캐릭터팀, QA, 사운드, 개발PM`) 사용. 회사 구조에 맞게 수정하세요."
        },
        "g1nation.meetMaxOutputTokens": {
          "type": "number",
          "default": 8000,
          "minimum": 2048,
          "markdownDescription": "`/meet` 최종 회의록(단일샷·병합) 생성 시 **출력 토큰 상한**(max_tokens). 기본 `8000`. 이 값을 명시하지 않으면 LM 서버 기본 상한에 잘려 **회의록 후반부·긴 액션표가 통째로 누락**되던 문제를 막는다. 잘림이 감지되면(finish=length) 끊긴 지점부터 자동 이어쓰기까지 수행하므로 이 값은 '한 번에 뽑는 양'이고 최종 길이는 이보다 길어질 수 있다. 아주 긴 회의(2시간+)에서 여전히 뒷부분이 짧다면 12000~16000 으로 올려보세요. (모델 컨텍스트 창을 넘지 않는 선에서)"
        },
        "g1nation.email.watcherEnabled": {
          "type": "boolean",
          "default": true,
          "markdownDescription": "**이메일 워처** (v2.2.271) — 주기적으로 Outlook(+Gmail 연결 시)을 자동 수집해 스레드 상태를 `.astra/email_state.json` 에 누적 추적. `/email` 을 안 쳐도 미회신이 계속 추적되고, 임계 초과 시 텔레그램으로 알린다. false 면 `/email` 실행 시에만 갱신."
        },
        "g1nation.email.watchIntervalMinutes": {
          "type": "number",
          "default": 60,
          "minimum": 10,
          "markdownDescription": "이메일 워처 수집 주기(분). 기본 `60`. Outlook COM + (연결 시) Gmail API 를 이 주기로 호출한다 — 너무 짧으면 PowerShell 스폰이 잦아지므로 30 이상 권장."
        },
        "g1nation.email.overdueAlertHours": {
          "type": "number",
          "default": 0,
          "minimum": 0,
          "markdownDescription": "**미회신 즉시 알림 임계(시간)** — 기본 `0`(끔, v2.2.283): 미회신 정리는 **09:30 KST 데일리 브리핑의 '답할 이메일' 섹션**이 하루 1회 정돈된 형식으로 담당합니다. 즉시 알림을 추가로 원하면 시간(예: `24`)을 설정 — 사람 발신 메일이 그 시간 넘게 회신 대기면 텔레그램 1회 알림 (같은 대기 기간 중복 없음)."
        },
        "g1nation.email.outlookEnabled": {
          "type": "boolean",
          "default": true,
          "markdownDescription": "`/email` 의 **Outlook(클래식) 프로바이더** 사용 여부 (v2.2.276). 끄면 수집 시도 자체를 하지 않아 채팅에 Outlook 관련 실패 문구가 뜨지 않습니다. Settings 패널 → 연동 → 이메일에서도 토글 가능."
        },
        "g1nation.email.gmailEnabled": {
          "type": "boolean",
          "default": true,
          "markdownDescription": "`/email` 의 **Gmail 프로바이더** 사용 여부 (v2.2.276). Gmail 은 Google OAuth 재연결(gmail.readonly 동의)이 필요합니다 — 연결 전이거나 안 쓰면 꺼두세요. 끄면 수집 시도 자체를 하지 않아 채팅에 \"Gmail 제외\" 문구가 뜨지 않습니다. Settings 패널 → 연동 → 이메일에서도 토글 가능."
        },
        "g1nation.email.includeSubfolders": {
          "type": "boolean",
          "default": true,
          "markdownDescription": "Outlook 수집 시 **받은편지함의 하위 폴더(중첩 포함)도 순회** (v2.2.281, 기본 ON). 규칙(필터)으로 폴더에 자동 분류된 메일도 `/email` 브리핑·`scan`·워처의 대상이 됩니다. 폴더 수는 120개까지. 끄면 받은편지함 루트만."
        },
        "g1nation.email.wikifySavePath": {
          "type": "string",
          "default": "",
          "markdownDescription": "`/email wikify` 로 이메일 스레드를 지식화한 문서의 **전용 저장 폴더** (v2.2.274) — 다른 wikify 산출물(`datacollectSavePath`)과 **별도로 관리**됩니다. **비워두면** 활성 두뇌 폴더의 `Email_Wiki/` 에 저장돼 brain 검색에 자동 포함. 다른 위치를 쓰려면 절대경로 입력. Astra Settings 패널 **지식·기억 탭**에서도 편집 가능."
        },
        "g1nation.email.syncDays": {
          "type": "number",
          "default": 7,
          "minimum": 1,
          "maximum": 30,
          "markdownDescription": "워처·`/email` 이 한 번에 수집하는 최근 일수. 기본 `7`. 수집 창 밖으로 벗어난 미회신 스레드도 상태 저장소에 이미 있으면 계속 추적된다 (무활동 60일 후 자동 정리)."
        },
        "g1nation.messenger.stocksReportTimes": {
          "type": "string",
          "default": "09:00,15:00",
          "markdownDescription": "**주식 정기 보고서 발송 시각** (KST, `HH:MM` CSV — v2.2.284, 기존 하드코딩 09:00/15:00 대체). 이 시각마다 가격 갱신 → 매수시그널 감지 → Sheets 동기화 → 텔레그램 보고서. **비우면 정기 실행 없음** (수동 `/stocks run` 만). Settings 패널 → 메신저 탭에서도 편집 가능."
        },
        "g1nation.messenger.buySignalAlert": {
          "type": "boolean",
          "default": true,
          "markdownDescription": "**매수 시그널 진입 알림** — 관망 종목이 매수사정권에 진입하면 텔레그램 1회 알림 (주식 워처 실행 시각에 감지). 끄면 감지는 계속하되 발송만 생략."
        },
        "g1nation.messenger.discoverTopFive": {
          "type": "boolean",
          "default": true,
          "markdownDescription": "`/stocks discover` 완료 시 **발굴 Top 5 텔레그램 발송** 여부. 끄면 채팅에만 표시."
        },
        "g1nation.dailyBriefing.enabled": {
          "type": "boolean",
          "default": true,
          "markdownDescription": "**데일리 브리핑** — 평일(월~금) 지정 시각에 오늘의 캘린더 일정 + Google Tasks(오늘 마감·기한 경과·조건부 대기)를 텔레그램으로 발송. 텔레그램 봇 토큰·allowedChatIds·캘린더 연결이 돼 있어야 실제 발송되며, 미연결이면 조용히 건너뜀."
        },
        "g1nation.dailyBriefing.time": {
          "type": "string",
          "default": "09:30",
          "markdownDescription": "데일리 브리핑 발송 시각 (KST, `HH:MM`). 기본 `09:30`."
        },
        "g1nation.sleepDigest.enabled": {
          "type": "boolean",
          "default": true,
          "markdownDescription": "**Sleep-time 지식 사전 소화** — 매일 지정 시각(유휴 시간)에 최근 7일 내 변경된 두뇌 지식을 폴더별 '소화 노트'(`<두뇌>/Digests/`)로 변환합니다 (런당 최대 5건, 소스 미변경 시 skip). 소화 노트는 예상 질의 Q&A·핵심 사실·문서 간 연결을 담아 RAG 검색 품질을 높입니다. 원문이 항상 우선 — 노트는 삭제해도 안전 (자동 재생성). 근거: sleep-time compute (arXiv 2504.13171)."
        },
        "g1nation.sleepDigest.time": {
          "type": "string",
          "default": "03:00",
          "markdownDescription": "사전 소화 실행 시각 (KST, `HH:MM`). 기본 `03:00` — VS Code 가 켜져 있는 유휴 시간 권장."
        },
        "g1nation.growthCycle.enabled": {
          "type": "boolean",
          "default": true,
          "markdownDescription": "**주간 성장 사이클** — 매주 지정 요일·시각에 검색 평가(골든셋)→학습 큐 갱신(Need Engine)→지식 노후 점검→성장 리포트→승인된 학습 자동 실행(Research Agent, 사이클당 최대 3건)을 자동 수행하고 요약을 알림(+텔레그램). 승인(approved) 자체는 여전히 사람이 — 자동화되는 것은 '승인된 항목의 실행'뿐."
        },
        "g1nation.growthCycle.day": {
          "type": "number",
          "default": 0,
          "minimum": 0,
          "maximum": 6,
          "markdownDescription": "주간 성장 사이클 실행 요일 (0=일 … 6=토). 기본 0(일요일)."
        },
        "g1nation.growthCycle.time": {
          "type": "string",
          "default": "20:00",
          "markdownDescription": "주간 성장 사이클 실행 시각 (KST, `HH:MM`). 기본 `20:00`."
        },
        "g1nation.growthCycle.autoRunApproved": {
          "type": "boolean",
          "default": true,
          "markdownDescription": "사이클에서 **approved 상태의 학습 큐 항목**을 Research Agent 로 자동 실행할지 (사이클당 최대 3건). 끄면 사이클은 측정·식별만 하고 실행은 수동 명령으로."
        },
        "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. 8192–16384: 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.largeInputMapReduce": {
          "type": "boolean",
          "default": true,
          "description": "When a single message is too large to fit the model's context window, split it into chunks, extract only the request-relevant facts from each (no hallucination/summary), integrate them, and answer from the condensed result. Turn off to send oversized input in one shot (the server may then truncate it). Default: true"
        },
        "g1nation.mapReduceTriggerRatio": {
          "type": "number",
          "default": 0.6,
          "minimum": 0.3,
          "maximum": 0.95,
          "description": "Map-reduce kicks in when a single message exceeds (effective context window × this ratio). Lower = engages sooner (safer for big inputs, more LLM calls). Default: 0.6"
        },
        "g1nation.mapReduceConcurrency": {
          "type": "number",
          "default": 2,
          "minimum": 1,
          "maximum": 8,
          "description": "How many chunk extractions run in parallel. Keep low on a single local GPU (one model serves them sequentially anyway). Default: 2"
        },
        "g1nation.mapReduceMaxDepth": {
          "type": "number",
          "default": 3,
          "minimum": 1,
          "maximum": 6,
          "description": "Maximum hierarchical-integration depth when the combined extractions still overflow the window. Default: 3"
        },
        "g1nation.mapReduceShowProvenance": {
          "type": "boolean",
          "default": false,
          "description": "Tag each extracted block with its source chunk ([조각 k]) so the final answer can be traced back to the part of the input it came from. Default: false"
        },
        "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. 5–6) 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.8–0.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 40–80 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.05–1.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.5–3x 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 0–1 (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 (512–1024) 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-nomic-embed-text-v1.5', 'multilingual-e5-small'). When empty, Astra auto-detects one from the engine's model list on startup (any model whose name contains 'embed') and enables hybrid retrieval; clear the value after auto-detection to keep it off. 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 — measured on the golden set (24 queries, nomic-embed v1.5): recall@3 83.3%→87.5%, MRR 0.802→0.806, no regression at recall@1; higher values trade recall@1 for recall@5."
        },
        "g1nation.chunkLevelRetrieval": {
          "type": "boolean",
          "default": true,
          "description": "섹션 청크 단위 검색 (Phase 1-가). 두뇌 검색이 파일이 아니라 '##' 헤딩 기준 섹션 청크 단위로 색인·스코어링하고 매치된 섹션을 주입합니다. 골든셋 측정에서 파일 단위 대비 recall@1 12.5%→75.0% · MRR 0.217→0.802 로 검증되어 기본 켜짐(v2.2.218). 문제 시 끄고 '검색 평가 실행'으로 비교 가능."
        },
        "g1nation.chunkTargetChars": {
          "type": "number",
          "default": 1200,
          "minimum": 400,
          "maximum": 4000,
          "description": "섹션 청크의 목표 길이(문자). 섹션이 이보다 길면 문단 경계로 더 쪼갭니다. chunkLevelRetrieval 가 켜져 있을 때만 사용. 값을 바꾸면 다음 검색 때 청크 색인이 재생성됩니다. 기본 1200."
        },
        "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.requirementGraphEnabled": {
          "type": "boolean",
          "default": true,
          "description": "Requirement Graph — 업무 유형(회의록/시장조사/업무조사/일정) 감지 시 필수 요소 체크리스트를 시스템 프롬프트에 주입. 필수 요소 누락 방지. 기본 켜짐."
        },
        "g1nation.requirementCoverageEnabled": {
          "type": "boolean",
          "default": true,
          "description": "Requirement Coverage Check — 답변 완료 후 업무 필수 요소 커버리지를 결정론적(정규식)으로 검사, 누락 가능 요소를 footer 한 줄로 표시. LLM 호출 없음. 기본 켜짐."
        },
        "g1nation.epistemicGuardEnabled": {
          "type": "boolean",
          "default": true,
          "description": "Epistemic Guard — 모름/추정/확실 3분류를 강제하는 시스템 프롬프트 블록. 검색 근거 없는 turn 에서 단정 금지 + 원자료 역질문 우선. 환각 방지. 기본 켜짐."
        },
        "g1nation.confidenceEngineEnabled": {
          "type": "boolean",
          "default": true,
          "description": "Confidence Engine — 답변 확신도 0~100 을 검색 그라운딩·출처 인용·충돌·커버리지 신호로 결정론적 산출, 업무 답변 아래 footer 표시. LLM 호출 없음. 기본 켜짐."
        },
        "g1nation.escalationEnabled": {
          "type": "boolean",
          "default": true,
          "description": "Escalation Engine — 확신도 낮음/출처 충돌/조사 출처 누락 시 footer 로 사람 검토를 명시적으로 요청. confidenceEngine 에 종속. 기본 켜짐."
        },
        "g1nation.criticLoopEnabled": {
          "type": "boolean",
          "default": true,
          "description": "Critic Loop — 커버리지 누락 또는 확신도<70 인 업무 답변에만 LLM 검수 1회 실행, 발견 이슈와 보완 제안을 footer 카드로 표시. 깨끗한 답변에는 안 돌아 latency 영향 최소. 기본 켜짐."
        },
        "g1nation.reflectionEnabled": {
          "type": "boolean",
          "default": true,
          "description": "Reflection — 업무 turn 회고(확신도·누락 요소·에스컬레이션)를 두뇌 .astra/growth/reflections.jsonl 에 기록. 반복 누락 요소는 다음 turn 의 필수 요소 체크리스트에 강조된다 (같은 실수 반복 방지). 기본 켜짐."
        },
        "g1nation.orgMemoryEnabled": {
          "type": "boolean",
          "default": true,
          "description": "Organizational Memory — 두뇌 .astra/organization.md 의 조직 규칙·업무 방식·선호를 시스템 프롬프트에 항상 주입. 파일을 직접 편집하면 다음 turn 부터 반영. 파일 없으면 동작 안 함. 기본 켜짐."
        },
        "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 (0–100): 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": "markdown",
          "markdownDescription": "최종 답변 표시 방식.\n\n- `markdown` (기본): 모델 출력을 그대로 렌더러에 넘김 — 채팅 UI 가 마크다운을 렌더하므로 `##` 제목이 크게, `**` 강조가 굵게 표시되어 가독성이 좋음.\n- `plain`: 마크다운 마커(`##`, `**`, `__`, `> `, `* ` 등)를 후처리로 모두 제거. 제목/본문이 같은 크기의 평문이 됨 — 답변을 평문으로 복사해 쓰는 워크플로 전용. (작은 로컬 모델의 마커 누수 차단 용도였으나 채팅 가독성을 해쳐 기본에서 제외)"
        },
        "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.web.autoFetchUrls": {
          "type": "boolean",
          "default": true,
          "description": "URL 자동 수집 — 메시지에 http(s) 링크가 있으면 답변 생성 전에 페이지 본문을 가져와 모델에게 전달합니다 (일반 챗·기업 모드·Intent Alignment 공통). Datacollect Bridge가 켜져 있으면 고품질 추출을 우선 사용하고, 꺼져 있으면 확장이 직접 페이지를 가져옵니다. 끄면 모델이 링크 내용을 알 수 없습니다."
        },
        "g1nation.company.alignmentSelfResearch": {
          "type": "boolean",
          "default": true,
          "description": "Alignment 자가 조사 — 분석기가 만든 질문을 사용자에게 보여주기 전에 두뇌(지식 폴더)를 먼저 검색해 스스로 답할 수 있는 질문을 걸러냅니다. 두뇌에서 답을 찾은 항목은 '(자가 조사로 두뇌에서 확인)' 표시와 함께 contract에 반영되고, 정말 모르는 질문만 카드에 노출됩니다. 질문이 있을 때만 LLM 1회가 추가됩니다."
        },
        "g1nation.company.alignmentKnowledgeSave": {
          "type": "boolean",
          "default": true,
          "description": "Alignment 학습 루프 — 사용자가 alignment 라운드에서 직접 답해준 Q/A를 두뇌의 'Alignment Knowledge' 폴더에 노트로 저장합니다. 다음 turn의 자가 조사가 이 노트를 발견해 같은 질문을 두 번 묻지 않게 됩니다. 20자 미만의 짧은 답과 자가 조사 항목은 저장하지 않습니다."
        },
        "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.hollowCheck.enabled": {
          "type": "boolean",
          "default": true,
          "description": "Hollow Code Check — `<create_file>` 등 action-tag 로 만든 파일이 *빈 깡통* (empty class, stub-only function, imports-only) 인지 정규식 스캔. LLM 호출 0 — 휴리스틱만. v2.2.203 부터 Phase A 와 분리, 기본 ON. 검출 시 actionReport 에 ⚠️ 라인 추가 + (autoRetry ON 이면) 자동 재작업."
        },
        "g1nation.hollowCheck.autoRetry": {
          "type": "boolean",
          "default": true,
          "description": "Hollow 감지 시 1회 자동 재작업 — Phase B (externalVerification) 와 분리. dev-impl 같은 코드 작성 stage 가 빈 깡통을 뱉으면 같은 모델·context 에 빈 깡통 지적을 prepend 해서 1회 재호출. 재작업 결과도 hollow 재검사. 기본 ON. OFF 시 hollow 검출 후 경고만 표시."
        },
        "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.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 현재가 갱신 → **매수사정권 진입 전이 감지 시 텔레그램 즉시 알림**(v2.2.268) → Google Sheets 동기화 → 텔레그램 정기 보고서. false 면 슬래시 명령으로만 수동 실행."
        },
        "g1nation.stocks.discoverAutoAdd": {
          "type": "boolean",
          "default": true,
          "markdownDescription": "`/stocks discover` 발굴 후보를 **워치리스트(stocks.json)에 자동 편입**(기본 ON, v2.2.268). 이미 있는 심볼은 건너뛰어 수동으로 다듬은 값(매수권장가·특이사항)을 보존한다. 편입 시 **매수권장가를 224일선 기반으로 자동 산출**(현재가>MA224 → MA224 / 그 아래면 현재가×0.97 / 시세 부족 시 현재가×0.90)해 워처가 매수사정권 진입을 감시·알림할 수 있는 상태로 넣는다. 끄면 이전처럼 `/stocks add` 수동 추가."
        },
        "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.autoBackup": {
          "type": "boolean",
          "default": true,
          "markdownDescription": "**stocks.json 자동 클라우드 백업** (v2.2.297) — 목록이 바뀔 때마다(추가/제거/가격갱신/발굴 편입) 30초 디바운스 후 `g1nation.stocks.spreadsheetId` spreadsheet 의 `_backup` 탭에 원본 JSON 그대로 백업. 컴퓨터를 바꿔도 `/stocks restore` 한 번으로 복원. spreadsheetId 미설정이면 조용히 skip. 수동 백업은 `/stocks backup`."
        },
        "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"
  }
}
