diff --git a/.astra/project-context/architecture.md b/.astra/project-context/architecture.md index 9d43c10..8015dcc 100644 --- a/.astra/project-context/architecture.md +++ b/.astra/project-context/architecture.md @@ -3,15 +3,15 @@ ## Snapshot -- **Workspace**: `ConnectAI` `v2.1.0` _(absolute path varies by environment; resolved from the active VS Code workspace)_ +- **Workspace**: `ConnectAI` `v2.1.2` _(absolute path varies by environment; resolved from the active VS Code workspace)_ - **Description**: The personal intelligence layer for Antigravity and VS Code. A private cognitive partner for deep project context, memory, and proactive strategic decision-making. - **Stack**: TypeScript, Node.js, VS Code Extension, LM Studio SDK, Test runner -- **Stats**: 202 source files, ~35,762 lines across 5 top-level modules. +- **Stats**: 203 source files, ~35,950 lines across 5 top-level modules. ## Last Refresh -- **Time**: 2026-05-13T16:54:00.117Z -- **Files newly analysed**: 5 -- **Files reused from cache**: 197 +- **Time**: 2026-05-13T17:37:28.091Z +- **Files newly analysed**: 3 +- **Files reused from cache**: 200 ## Directory Map ```mermaid @@ -41,7 +41,7 @@ flowchart LR media["media/
6 files"] tests["tests/
27 files"] core_py["core_py/
6 files"] - docs["docs/
63 files"] + docs["docs/
64 files"] tests --> src ``` @@ -64,7 +64,7 @@ flowchart LR ## Modules -### `src/` — 100 files, ~23,711 lines +### `src/` — 100 files, ~23,874 lines **Sub-directories** - `src/features/` (28) — The 9-agent roster for 1인 기업 모드. Each entry is a static description — persona, role, specialty — used to build the speci @@ -85,7 +85,7 @@ flowchart LR - `src/config.ts` (216 lines) - `src/features/company/types.ts` (150 lines) — Type definitions for the 1인 기업 (One-Person Company) mode. The mode turns the user into a virtual CEO that dispatches work to a roster of specialist agents. Each turn produces a session directory conta - `src/lib/paths.ts` (151 lines) -- `src/sidebarProvider.ts` (2934 lines) +- `src/sidebarProvider.ts` (3026 lines) - `src/features/company/agents.ts` (136 lines) — The 9-agent roster for 1인 기업 모드. Each entry is a static description — persona, role, specialty — used to build the specialist's system prompt at dispatch time. The set was adopted from Connectorigin's - `src/memory/types.ts` (126 lines) — Memory Type Definitions (메모리 타입 정의) Astra의 5-Layer Cognitive Memory System의 모든 타입을 정의합니다. ① Short-Term ② Long-Term ③ Project ④ Procedural ⑤ Episodic - `src/retrieval/scoring.ts` (518 lines) — Scoring Engine — TF-IDF + Bilingual Tokenizer 단순 includes() 키워드 매칭을 넘어서, TF-IDF 가중치 기반의 문서 스코어링을 제공합니다. 한국어/영어 양국어 토크나이저를 포함합니다. @@ -100,7 +100,7 @@ flowchart LR - `src/features/projectArchitecture/scanner.ts` (644 lines) — Deep static analyser for the Project Architecture Context generator. Walks the project tree (skipping the usual nodemodules / out / dist noise), pulls the role of each interesting file from its leadin - `src/lib/contextManager.ts` (275 lines) — Context Manager (컨텍스트 한계 관리) "context length = 132k" 는 "답변을 132k 토큰까지 생성해도 된다" 가 아닙니다. 시스템 프롬프트 + 대화 기록 + 입력 문서 + 생성될 답변 + 여유분 ≤ context length 이 모듈은 요청을 보내기 전에 입력 토큰을 추정하고, - 동적으로 출력 상한(maxTokens)을 계 - `src/core/astraPath.ts` (50 lines) — Astra Path Resolver (경로 해결기) Astra의 모든 데이터 파일(.astra 디렉토리)의 경로를 중앙에서 관리합니다. 확장 프로그램의 설치 경로(extensionUri) 기반으로 .astra 디렉토리를 해결하여, 사용자 프로젝트 루트가 아닌 ConnectAI 패키지 내부에 데이터를 저장합니다. 이 모듈은 AAL(Astra Autonomou -- `src/extension.ts` (945 lines) +- `src/extension.ts` (966 lines) - `src/features/projectChronicle/types.ts` (118 lines) - `src/lmstudio/client.ts` (147 lines) - `src/retrieval/brainIndex.ts` (325 lines) — Brain Index — persistent, mtime-keyed tokenized cache of the Second Brain RAG 검색은 매 질의마다 브레인의 모든 .md 파일을 읽고 토크나이즈해서 TF-IDF 점수를 계산했습니다 — 파일 수가 많아지면 그게 병목입니다. 이 모듈은 /.astra/brain-index.json 에 @@ -160,10 +160,10 @@ flowchart LR - `core_py/optimizer.py` (55 lines) - `core_py/queue_worker.py` (82 lines) -### `docs/` — 63 files, ~2,605 lines +### `docs/` — 64 files, ~2,630 lines **Sub-directories** -- `docs/records/` (51) — Astra Project Chronicle Records +- `docs/records/` (52) — Astra Project Chronicle Records - `docs/docs/` (5) — docs Chronicle Records **Key files** @@ -172,7 +172,7 @@ flowchart LR - `docs/EXPERIENCE_MEMORY_PLAN.md` (122 lines) — Experience Memory (Mistake / Lesson Loop) — Implementation Plan - `docs/records/ConnectAI/development/2026-05-02_connectai_project_knowledge_overview.md` (121 lines) — Astra Project Knowledge Overview - `docs/records/ConnectAI/development/2026-05-03_connectai_project_knowledge_overview.md` (121 lines) — Astra Project Knowledge Overview -- `docs/records/ConnectAI/timeline.md` (125 lines) — Project Timeline +- `docs/records/ConnectAI/timeline.md` (128 lines) — Project Timeline - `docs/Advanced_Features_Implementation_Guide.md` (40 lines) — Advanced Features Implementation Guide - `docs/PROJECT_CHRONICLE_GUARD_ROADMAP.md` (43 lines) — Project Chronicle Guard: Search Engine Roadmap - `docs/UX_UI_Consistency_Guidelines.md` (44 lines) — UX/UI Consistency Guidelines @@ -307,7 +307,7 @@ Astra는 대표님의 명시적인 승인 하에 로컬 시스템의 강력한 **Designed for High-Performance Decision Making.** Copyright (C) **g1nation**. All rights reserved. -_Last auto-scan: 2026-05-13T16:54:00.117Z · signature `2c245387`_ +_Last auto-scan: 2026-05-13T17:37:28.091Z · signature `b3e670ea`_ ## Purpose diff --git a/.astra/project-context/scan-cache.json b/.astra/project-context/scan-cache.json index 6aaf2f4..89d43ce 100644 --- a/.astra/project-context/scan-cache.json +++ b/.astra/project-context/scan-cache.json @@ -1,6 +1,6 @@ { "version": 1, - "generatedAt": "2026-05-13T16:54:00.128Z", + "generatedAt": "2026-05-13T17:37:28.099Z", "files": { "src/agent.ts": { "mtimeMs": 1778683690000, @@ -249,9 +249,9 @@ "imports": [] }, "src/extension.ts": { - "mtimeMs": 1778687229000, - "size": 49361, - "lines": 945, + "mtimeMs": 1778693606000, + "size": 50539, + "lines": 966, "role": "", "imports": [ "src/utils", @@ -462,9 +462,9 @@ "imports": [] }, "src/features/projectArchitecture/index.ts": { - "mtimeMs": 1778684164000, - "size": 23147, - "lines": 523, + "mtimeMs": 1778691699000, + "size": 25373, + "lines": 573, "role": "Project Architecture Context (Feature 2) Builds a markdown document that captures the durable facts about a project — its purpose, modules, key files, constraints, decisions — so Astra can attach it t", "imports": [ "src/utils", @@ -923,9 +923,9 @@ ] }, "src/sidebarProvider.ts": { - "mtimeMs": 1778690954000, - "size": 127595, - "lines": 2934, + "mtimeMs": 1778693581000, + "size": 132228, + "lines": 3026, "role": "", "imports": [ "src/utils", @@ -1508,7 +1508,7 @@ "imports": [] }, "docs/records/ConnectAI/chronicle.config.json": { - "mtimeMs": 1778690673000, + "mtimeMs": 1778693841000, "size": 416, "lines": 11, "role": "JSON configuration", @@ -1710,6 +1710,13 @@ "role": "Development Log: /Volumes/Data/project/Antigravity/ConnectAI 분석하고 부족한 부분이나 개선이 필요한 부분을 알려주면 좋겠어.", "imports": [] }, + "docs/records/ConnectAI/development/2026-05-13_volumes-data-project-antigravity-connectai-이-프로젝트-작업할거야_implementation.md": { + "mtimeMs": 1778691256000, + "size": 990, + "lines": 22, + "role": "Development Log: /Volumes/Data/project/Antigravity/ConnectAI 이 프로젝트 작업할거야", + "imports": [] + }, "docs/records/ConnectAI/development/2026-05-13_너는-분석-요청하거나-내가-작업-요청을-할때-connectai-architecture-md-문서를-참고하고-_implementation.md": { "mtimeMs": 1778678912000, "size": 1738, @@ -1774,9 +1781,9 @@ "imports": [] }, "docs/records/ConnectAI/timeline.md": { - "mtimeMs": 1778690673000, - "size": 8318, - "lines": 125, + "mtimeMs": 1778691256000, + "size": 8485, + "lines": 128, "role": "Project Timeline", "imports": [] }, diff --git a/.astra/tests/stress/.astra/cache/259a37934ead3910a8722b82054d46d2ca2057b05c488be1dcf439166ac5a9a1.json b/.astra/tests/stress/.astra/cache/259a37934ead3910a8722b82054d46d2ca2057b05c488be1dcf439166ac5a9a1.json index c8adbda..944f9f5 100644 --- a/.astra/tests/stress/.astra/cache/259a37934ead3910a8722b82054d46d2ca2057b05c488be1dcf439166ac5a9a1.json +++ b/.astra/tests/stress/.astra/cache/259a37934ead3910a8722b82054d46d2ca2057b05c488be1dcf439166ac5a9a1.json @@ -1,5 +1,5 @@ { "result": "Final report with inconsistencies. This should be long enough to pass validation.", - "createdAt": 1778691140353, + "createdAt": 1778693761992, "modelVersion": "unknown" } \ No newline at end of file diff --git a/.astra/tests/stress/.astra/cache/65775be352df43297b63c7af59c9f4f39d2bc368f77456c37b5eef9a94a66b5c.json b/.astra/tests/stress/.astra/cache/65775be352df43297b63c7af59c9f4f39d2bc368f77456c37b5eef9a94a66b5c.json index 4d1351a..44314eb 100644 --- a/.astra/tests/stress/.astra/cache/65775be352df43297b63c7af59c9f4f39d2bc368f77456c37b5eef9a94a66b5c.json +++ b/.astra/tests/stress/.astra/cache/65775be352df43297b63c7af59c9f4f39d2bc368f77456c37b5eef9a94a66b5c.json @@ -1,5 +1,5 @@ { "result": "[CONFLICT WARNING] 성능이 200% 증가했습니다. vs 그러나 동시에 50% 감소했습니다. 최적화와 성능 저하가 동시에 발견됨.", - "createdAt": 1778691140352, + "createdAt": 1778693761991, "modelVersion": "unknown" } \ No newline at end of file diff --git a/.astra/tests/stress/.astra/cache/6894d26c5b0a55d25d756a473225c7a44d7661af673b24e3f49551a7a2e50280.json b/.astra/tests/stress/.astra/cache/6894d26c5b0a55d25d756a473225c7a44d7661af673b24e3f49551a7a2e50280.json index d20067f..036c384 100644 --- a/.astra/tests/stress/.astra/cache/6894d26c5b0a55d25d756a473225c7a44d7661af673b24e3f49551a7a2e50280.json +++ b/.astra/tests/stress/.astra/cache/6894d26c5b0a55d25d756a473225c7a44d7661af673b24e3f49551a7a2e50280.json @@ -1,5 +1,5 @@ { "result": "Detailed Execution Plan: 1. Research 2. Analyze 3. Write report with high quality.", - "createdAt": 1778691140352, + "createdAt": 1778693761991, "modelVersion": "unknown" } \ No newline at end of file diff --git a/.astra/tests/stress/.astra/cache/88cb61499f88ed38165b64bd3e8adc543795e4b427b64540a49c9ab27c7fe213.json b/.astra/tests/stress/.astra/cache/88cb61499f88ed38165b64bd3e8adc543795e4b427b64540a49c9ab27c7fe213.json index 18e0757..cc782d1 100644 --- a/.astra/tests/stress/.astra/cache/88cb61499f88ed38165b64bd3e8adc543795e4b427b64540a49c9ab27c7fe213.json +++ b/.astra/tests/stress/.astra/cache/88cb61499f88ed38165b64bd3e8adc543795e4b427b64540a49c9ab27c7fe213.json @@ -1,5 +1,5 @@ { - "result": "---\nid: stress_conflict_1778691140340\ndate: 2026-05-13T16:52:20.353Z\ntype: knowledge_artifact\nstandard: P-Reinforce v3.0\ntags: [automated, connect_ai, brain_sync]\n---\n\n## 📌 Brief Summary\nFinal report with inconsistencies. This should be long enough to pass validation.\n\nFinal report with inconsistencies. This should be long enough to pass validation.\n\n---\n## 💡 Astra의 선제적 제안 (Proactive Next Actions)\nFinal report with inconsistencies. This should be long enough to pass validation.\n---\n## 🛡️ Reliability & Audit Summary\n> [!NOTE]\n> 이 문서는 ConnectAI의 **Intelligent Resilience** 엔진에 의해 검증 및 정제되었습니다.\n\n| Metric | Value | Status |\n| :--- | :--- | :--- |\n| **Conflict Risk** | `60/100` | ⚠️ Medium |\n| **Fallbacks Used** | `0` | ✅ None |\n| **Auto Retries** | `0` | ✅ Stable |\n| **Deduplication** | `0` | Standard |\n| **Processing Time** | `0.0s` | ✅ Fast |\n\n### 🔍 Decision Audit Trail\n- **[PLANNER]** 전략 수립 중... (12ms)\n- **[RESEARCHER]** 핵심 정보 수집 및 분석 중... (0ms)\n- **[WRITER]** 최종 리포트 작성 및 편집 중... (0ms)\n", - "createdAt": 1778691140353, + "result": "---\nid: stress_conflict_1778693761978\ndate: 2026-05-13T17:36:01.992Z\ntype: knowledge_artifact\nstandard: P-Reinforce v3.0\ntags: [automated, connect_ai, brain_sync]\n---\n\n## 📌 Brief Summary\nFinal report with inconsistencies. This should be long enough to pass validation.\n\nFinal report with inconsistencies. This should be long enough to pass validation.\n\n---\n## 💡 Astra의 선제적 제안 (Proactive Next Actions)\nFinal report with inconsistencies. This should be long enough to pass validation.\n---\n## 🛡️ Reliability & Audit Summary\n> [!NOTE]\n> 이 문서는 ConnectAI의 **Intelligent Resilience** 엔진에 의해 검증 및 정제되었습니다.\n\n| Metric | Value | Status |\n| :--- | :--- | :--- |\n| **Conflict Risk** | `60/100` | ⚠️ Medium |\n| **Fallbacks Used** | `0` | ✅ None |\n| **Auto Retries** | `0` | ✅ Stable |\n| **Deduplication** | `0` | Standard |\n| **Processing Time** | `0.0s` | ✅ Fast |\n\n### 🔍 Decision Audit Trail\n- **[PLANNER]** 전략 수립 중... (12ms)\n- **[RESEARCHER]** 핵심 정보 수집 및 분석 중... (1ms)\n- **[WRITER]** 최종 리포트 작성 및 편집 중... (1ms)\n", + "createdAt": 1778693761992, "modelVersion": "unknown" } \ No newline at end of file diff --git a/.astra/tests/stress/.astra/missions/stress_conflict_1778691140340.json b/.astra/tests/stress/.astra/missions/stress_conflict_1778693761978.json similarity index 82% rename from .astra/tests/stress/.astra/missions/stress_conflict_1778691140340.json rename to .astra/tests/stress/.astra/missions/stress_conflict_1778693761978.json index 63cf002..c8e4767 100644 --- a/.astra/tests/stress/.astra/missions/stress_conflict_1778691140340.json +++ b/.astra/tests/stress/.astra/missions/stress_conflict_1778693761978.json @@ -1,8 +1,8 @@ { - "missionId": "stress_conflict_1778691140340", + "missionId": "stress_conflict_1778693761978", "status": "completed", - "startTime": "2026-05-13T16:52:20.340Z", - "totalElapsedMs": 13, + "startTime": "2026-05-13T17:36:01.978Z", + "totalElapsedMs": 14, "results": { "planner": "Detailed Execution Plan: 1. Research 2. Analyze 3. Write report with high quality.", "researcher": "[CONFLICT WARNING] 성능이 200% 증가했습니다. vs 그러나 동시에 50% 감소했습니다. 최적화와 성능 저하가 동시에 발견됨.", @@ -18,28 +18,28 @@ "to": "planner", "durationMs": 12, "message": "전략 수립 중...", - "ts": "2026-05-13T16:52:20.352Z" + "ts": "2026-05-13T17:36:01.990Z" }, { "from": "planner", "to": "researcher", - "durationMs": 0, + "durationMs": 1, "message": "핵심 정보 수집 및 분석 중...", - "ts": "2026-05-13T16:52:20.352Z" + "ts": "2026-05-13T17:36:01.991Z" }, { "from": "researcher", "to": "writer", - "durationMs": 0, + "durationMs": 1, "message": "최종 리포트 작성 및 편집 중...", - "ts": "2026-05-13T16:52:20.352Z" + "ts": "2026-05-13T17:36:01.992Z" }, { "from": "writer", "to": "completed", - "durationMs": 1, + "durationMs": 0, "message": "미션 완료", - "ts": "2026-05-13T16:52:20.353Z" + "ts": "2026-05-13T17:36:01.992Z" } ], "resilienceMetrics": { diff --git a/PATCHNOTES.md b/PATCHNOTES.md index 76dfa37..ba66016 100644 --- a/PATCHNOTES.md +++ b/PATCHNOTES.md @@ -1,4 +1,14 @@ # Astra Patch Notes + + ## v2.1.2 (2026-05-14) + ### 🏛️ Chronicle Repair & Context Stability + - **프로젝트 루트 자동 복구 로직 도입:** 이전 버전에서 워크스페이스 상위 폴더를 기준으로 잘못 저장된 서브프로젝트 루트 정보를 자동으로 탐지하고 수정하는 복구(`_repairCorruptedChronicleProjectRoots`) 기능을 추가했습니다. + - **컨텍스트 전환 정교화:** 에디터 포커스 기반의 자동 프로젝트 전환 로직을 개선하여, 사용자가 수동으로 지정한 프로젝트 컨텍스트가 의도치 않게 초기화되지 않도록 안정성을 강화했습니다. + - **아키텍처 인지 고도화:** 서브프로젝트 인식 실패 시 워크스페이스 루트로 폴백하는 동작을 보완하여 다중 프로젝트 환경에서의 일관성을 확보했습니다. + - **신규 패키징:** `astra-2.1.2.vsix` 패키지를 통해 더욱 안정화된 프로젝트 관리 환경을 배포합니다. + + --- + ## v2.1.1 (2026-05-14) ### 🏛️ Multi-Subproject Awareness & Context Precision diff --git a/docs/records/ConnectAI/chronicle.config.json b/docs/records/ConnectAI/chronicle.config.json index 2cb00c4..7acc3db 100644 --- a/docs/records/ConnectAI/chronicle.config.json +++ b/docs/records/ConnectAI/chronicle.config.json @@ -7,5 +7,5 @@ "corePurpose": "", "detailLevel": "standard", "createdAt": "2026-05-13T13:09:33.788Z", - "updatedAt": "2026-05-13T16:54:16.995Z" + "updatedAt": "2026-05-13T17:37:21.922Z" } diff --git a/package-lock.json b/package-lock.json index d4a65ef..520d71a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "astra", - "version": "2.80.43", + "version": "2.1.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "astra", - "version": "2.80.43", + "version": "2.1.2", "license": "MIT", "dependencies": { "@lmstudio/sdk": "^1.5.0", @@ -57,7 +57,6 @@ "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -1956,7 +1955,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.10.12", "caniuse-lite": "^1.0.30001782", @@ -2850,7 +2848,6 @@ "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/core": "^29.7.0", "@jest/types": "^29.6.3", @@ -4403,7 +4400,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -4636,7 +4632,6 @@ "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/package.json b/package.json index 71b6f81..48b9108 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "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.1", + "version": "2.1.2", "publisher": "g1nation", "license": "MIT", "icon": "assets/icon.png", diff --git a/src/extension.ts b/src/extension.ts index e486184..7fdf66e 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -150,6 +150,13 @@ export async function activate(context: vscode.ExtensionContext) { activity: activityTracker, loadedModels: () => lmStudioClient.listLoadedCached(), }); + // One-time repair: rewrite any chronicle projects that were saved with the + // workspace parent as their `projectRoot` (a side-effect of the old + // pre-multi-subproject activation code). Idempotent and silent when there's + // nothing to fix. + void provider._repairCorruptedChronicleProjectRoots().catch((e: any) => { + logError('architecture: chronicle repair failed.', { error: e?.message ?? String(e) }); + }); context.subscriptions.push( vscode.commands.registerCommand('g1nation.openChat', () => { provider!.openAsPanel(vscode.ViewColumn.Three); diff --git a/src/sidebarProvider.ts b/src/sidebarProvider.ts index 7a9e21d..f2f7aac 100644 --- a/src/sidebarProvider.ts +++ b/src/sidebarProvider.ts @@ -982,6 +982,64 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn await this._context.globalState.update(SidebarChatProvider.chronicleProjectsStateKey, projects); } + /** + * One-time repair for chronicle projects that were stored before the + * multi-subproject fix landed. + * + * Old activation code always treated `workspaceFolders[0]` (= the open + * parent folder, e.g. `/.../Antigravity`) as the project root, so every + * subproject the user activated (`ConnectAI`, `Datacollector_MAC`, + * `Skybound`, …) ended up with `projectRoot === ` in globalState. + * That breaks reload: `_ensureActiveProjectForWorkspace` case 2 matches + * the corrupted entry on every boot and force-switches the active + * project to whichever bad row it found first. + * + * The repair: for each chronicle project whose `projectName` does NOT + * match the basename of its stored `projectRoot` (i.e. it can't actually + * be the project root), and where a same-name subfolder exists under + * the current workspace, retarget the entry to that subfolder. Idempotent + * — re-running it after the first pass changes nothing. + */ + async _repairCorruptedChronicleProjectRoots(): Promise { + const wsRoot = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath; + if (!wsRoot) return; + const projects = this._getChronicleProjects(); + if (projects.length === 0) return; + const norm = (p: string | undefined) => (p || '').replace(/[\\/]+$/, '').toLowerCase(); + const wsRootNorm = norm(wsRoot); + let changed = false; + const repaired = projects.map((p) => { + if (!p.projectName || !p.projectRoot) return p; + const rootBase = path.basename(p.projectRoot).toLowerCase(); + const nameMatches = rootBase === p.projectName.toLowerCase(); + if (nameMatches) return p; // root basename agrees with name → trust it + // Only repair entries that look like they were silently captured at + // the workspace parent. Anything else (different machine, custom + // path) we leave alone. + if (norm(p.projectRoot) !== wsRootNorm) return p; + const candidate = path.join(wsRoot, p.projectName); + try { + if (!fs.existsSync(candidate) || !fs.statSync(candidate).isDirectory()) return p; + } catch { + return p; + } + const newDocPath = path.join(candidate, '.astra', 'project-context', 'architecture.md'); + changed = true; + return { + ...p, + projectRoot: candidate, + recordRoot: path.join(candidate, 'docs', 'records', p.projectName), + architectureDocPath: fs.existsSync(newDocPath) ? newDocPath : p.architectureDocPath, + }; + }); + if (!changed) return; + await this._putChronicleProjects(repaired); + const fixedCount = repaired.filter((p, i) => p !== projects[i]).length; + logInfo('architecture: repaired chronicle projects with wrong projectRoot.', { + wsRoot, count: fixedCount, + }); + } + // ─── Project Architecture Context (Feature 2) ────────────────────────────── // // Activation flow: @@ -1225,10 +1283,21 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn // always reporting the parent. const hint = vscode.window.activeTextEditor?.document.uri.fsPath ?? vscode.window.visibleTextEditors[0]?.document.uri.fsPath; - const workspaceRoot = resolveActiveSubprojectRoot(wsRoot, hint); - const projects = this._getChronicleProjects(); - const active = this._getActiveChronicleProject(); + const resolved = resolveActiveSubprojectRoot(wsRoot, hint); const norm = (p: string | undefined) => (p || '').replace(/[\\/]+$/, '').toLowerCase(); + const active = this._getActiveChronicleProject(); + const projects = this._getChronicleProjects(); + // Did the editor hint actually point at a nested subproject? If the + // resolver fell back to the workspace root (no nested marker, or the + // Astra sidebar itself was focused so no editor existed), we must NOT + // clobber the user's current active project — they may have set it by + // hand via the project picker, or by typing a project intent. Auto- + // switching is only safe when there is a clear editor-driven signal. + const isNestedHit = !!resolved && norm(resolved) !== norm(wsRoot); + if (!isNestedHit && active) { + return active; + } + const workspaceRoot = isNestedHit ? resolved : wsRoot; if (active && active.projectRoot && norm(active.projectRoot) === norm(workspaceRoot)) { return active; }