{
  "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.1.6",
  "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.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"
      }
    ],
    "keybindings": [
      {
        "command": "g1nation.focusChat",
        "key": "cmd+l",
        "mac": "cmd+l"
      }
    ],
    "menus": {
      "editor/context": [
        {
          "command": "g1nation.explainSelection",
          "when": "editorHasSelection",
          "group": "1_modification"
        }
      ]
    },
    "viewsContainers": {
      "activitybar": [
        {
          "id": "astra-activity",
          "title": "Astra",
          "icon": "assets/icon-activitybar.svg"
        }
      ]
    },
    "views": {
      "astra-activity": [
        {
          "id": "astra-launcher",
          "name": "Astra Launcher"
        }
      ]
    },
    "viewsWelcome": [
      {
        "view": "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.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.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.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.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.enableReflection": {
          "type": "boolean",
          "default": true,
          "description": "Insert a Self-Reflection (Reflector) stage between Researcher and Writer in the multi-agent workflow. The Reflector critically reviews the plan and research output (gaps, contradictions, unsupported claims, drift from the original objective) and feeds a structured critique to the Writer, which must address it before producing the final report. Reflection failures are non-fatal (the Writer still runs with empty critique). Disable to save one LLM call per mission if you prioritize latency or are running on a very small model."
        }
      }
    }
  },
  "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"
  }
}
