gsd-pi 2.18.0 → 2.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -1
- package/dist/cli.js +3 -3
- package/dist/onboarding.d.ts +3 -1
- package/dist/onboarding.js +77 -3
- package/dist/remote-questions-config.d.ts +1 -1
- package/dist/resources/extensions/google-search/index.ts +164 -47
- package/dist/resources/extensions/gsd/auto-dashboard.ts +14 -2
- package/dist/resources/extensions/gsd/auto-prompts.ts +148 -39
- package/dist/resources/extensions/gsd/auto-worktree.ts +93 -9
- package/dist/resources/extensions/gsd/auto.ts +690 -39
- package/dist/resources/extensions/gsd/captures.ts +384 -0
- package/dist/resources/extensions/gsd/commands.ts +654 -36
- package/dist/resources/extensions/gsd/complexity-classifier.ts +322 -0
- package/dist/resources/extensions/gsd/context-budget.ts +243 -0
- package/dist/resources/extensions/gsd/context-store.ts +195 -0
- package/dist/resources/extensions/gsd/dashboard-overlay.ts +51 -3
- package/dist/resources/extensions/gsd/db-writer.ts +341 -0
- package/dist/resources/extensions/gsd/debug-logger.ts +178 -0
- package/dist/resources/extensions/gsd/dispatch-guard.ts +0 -1
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +54 -0
- package/dist/resources/extensions/gsd/doctor-proactive.ts +286 -0
- package/dist/resources/extensions/gsd/doctor.ts +283 -2
- package/dist/resources/extensions/gsd/export.ts +81 -2
- package/dist/resources/extensions/gsd/files.ts +39 -9
- package/dist/resources/extensions/gsd/git-service.ts +6 -0
- package/dist/resources/extensions/gsd/gsd-db.ts +752 -0
- package/dist/resources/extensions/gsd/guided-flow.ts +26 -1
- package/dist/resources/extensions/gsd/history.ts +0 -1
- package/dist/resources/extensions/gsd/index.ts +277 -1
- package/dist/resources/extensions/gsd/md-importer.ts +526 -0
- package/dist/resources/extensions/gsd/metrics.ts +84 -0
- package/dist/resources/extensions/gsd/model-cost-table.ts +65 -0
- package/dist/resources/extensions/gsd/model-router.ts +256 -0
- package/dist/resources/extensions/gsd/notifications.ts +0 -1
- package/dist/resources/extensions/gsd/post-unit-hooks.ts +72 -2
- package/dist/resources/extensions/gsd/preferences.ts +198 -150
- package/dist/resources/extensions/gsd/prompt-loader.ts +45 -9
- package/dist/resources/extensions/gsd/prompts/execute-task.md +3 -5
- package/dist/resources/extensions/gsd/prompts/heal-skill.md +45 -0
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +5 -1
- package/dist/resources/extensions/gsd/prompts/quick-task.md +48 -0
- package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -0
- package/dist/resources/extensions/gsd/prompts/replan-slice.md +8 -0
- package/dist/resources/extensions/gsd/prompts/system.md +2 -1
- package/dist/resources/extensions/gsd/prompts/triage-captures.md +62 -0
- package/dist/resources/extensions/gsd/quick.ts +156 -0
- package/dist/resources/extensions/gsd/skill-discovery.ts +5 -3
- package/dist/resources/extensions/gsd/skill-health.ts +417 -0
- package/dist/resources/extensions/gsd/skill-telemetry.ts +127 -0
- package/dist/resources/extensions/gsd/state.ts +30 -0
- package/dist/resources/extensions/gsd/templates/preferences.md +1 -0
- package/dist/resources/extensions/gsd/tests/captures.test.ts +438 -0
- package/dist/resources/extensions/gsd/tests/complexity-classifier.test.ts +181 -0
- package/dist/resources/extensions/gsd/tests/context-budget.test.ts +283 -0
- package/dist/resources/extensions/gsd/tests/context-compression.test.ts +1 -1
- package/dist/resources/extensions/gsd/tests/context-store.test.ts +462 -0
- package/dist/resources/extensions/gsd/tests/continue-here.test.ts +204 -0
- package/dist/resources/extensions/gsd/tests/dashboard-budget.test.ts +346 -0
- package/dist/resources/extensions/gsd/tests/db-writer.test.ts +602 -0
- package/dist/resources/extensions/gsd/tests/debug-logger.test.ts +185 -0
- package/dist/resources/extensions/gsd/tests/derive-state-db.test.ts +406 -0
- package/dist/resources/extensions/gsd/tests/dispatch-guard.test.ts +0 -1
- package/dist/resources/extensions/gsd/tests/dist-redirect.mjs +22 -0
- package/dist/resources/extensions/gsd/tests/doctor-proactive.test.ts +244 -0
- package/dist/resources/extensions/gsd/tests/doctor-runtime.test.ts +303 -0
- package/dist/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +434 -0
- package/dist/resources/extensions/gsd/tests/gsd-db.test.ts +353 -0
- package/dist/resources/extensions/gsd/tests/gsd-inspect.test.ts +125 -0
- package/dist/resources/extensions/gsd/tests/gsd-tools.test.ts +326 -0
- package/dist/resources/extensions/gsd/tests/integration-edge.test.ts +228 -0
- package/dist/resources/extensions/gsd/tests/integration-lifecycle.test.ts +277 -0
- package/dist/resources/extensions/gsd/tests/md-importer.test.ts +411 -0
- package/dist/resources/extensions/gsd/tests/metrics.test.ts +197 -0
- package/dist/resources/extensions/gsd/tests/milestone-transition-worktree.test.ts +144 -0
- package/dist/resources/extensions/gsd/tests/model-cost-table.test.ts +69 -0
- package/dist/resources/extensions/gsd/tests/model-isolation.test.ts +99 -0
- package/dist/resources/extensions/gsd/tests/model-router.test.ts +167 -0
- package/dist/resources/extensions/gsd/tests/parsers.test.ts +40 -0
- package/dist/resources/extensions/gsd/tests/post-unit-hooks.test.ts +41 -1
- package/dist/resources/extensions/gsd/tests/preferences-git.test.ts +0 -1
- package/dist/resources/extensions/gsd/tests/preferences-hooks.test.ts +0 -1
- package/dist/resources/extensions/gsd/tests/preferences-mode.test.ts +110 -0
- package/dist/resources/extensions/gsd/tests/preferences-models.test.ts +0 -1
- package/dist/resources/extensions/gsd/tests/prompt-budget-enforcement.test.ts +464 -0
- package/dist/resources/extensions/gsd/tests/prompt-db.test.ts +385 -0
- package/dist/resources/extensions/gsd/tests/remote-questions.test.ts +488 -1
- package/dist/resources/extensions/gsd/tests/resolve-ts-hooks.mjs +17 -29
- package/dist/resources/extensions/gsd/tests/resolve-ts.mjs +2 -8
- package/dist/resources/extensions/gsd/tests/routing-history.test.ts +215 -62
- package/dist/resources/extensions/gsd/tests/skill-lifecycle.test.ts +126 -0
- package/dist/resources/extensions/gsd/tests/stop-auto-remote.test.ts +31 -8
- package/dist/resources/extensions/gsd/tests/token-savings.test.ts +366 -0
- package/dist/resources/extensions/gsd/tests/triage-dispatch.test.ts +224 -0
- package/dist/resources/extensions/gsd/tests/triage-resolution.test.ts +215 -0
- package/dist/resources/extensions/gsd/tests/unit-runtime.test.ts +25 -1
- package/dist/resources/extensions/gsd/tests/visualizer-critical-path.test.ts +145 -0
- package/dist/resources/extensions/gsd/tests/visualizer-data.test.ts +290 -0
- package/dist/resources/extensions/gsd/tests/visualizer-overlay.test.ts +120 -0
- package/dist/resources/extensions/gsd/tests/visualizer-views.test.ts +478 -0
- package/dist/resources/extensions/gsd/tests/worktree-db-integration.test.ts +205 -0
- package/dist/resources/extensions/gsd/tests/worktree-db.test.ts +442 -0
- package/dist/resources/extensions/gsd/tests/worktree-post-create-hook.test.ts +165 -0
- package/dist/resources/extensions/gsd/triage-resolution.ts +200 -0
- package/dist/resources/extensions/gsd/triage-ui.ts +175 -0
- package/dist/resources/extensions/gsd/types.ts +29 -0
- package/dist/resources/extensions/gsd/undo.ts +0 -1
- package/dist/resources/extensions/gsd/unit-runtime.ts +5 -1
- package/dist/resources/extensions/gsd/visualizer-data.ts +505 -0
- package/dist/resources/extensions/gsd/visualizer-overlay.ts +337 -0
- package/dist/resources/extensions/gsd/visualizer-views.ts +755 -0
- package/dist/resources/extensions/gsd/worktree-command.ts +18 -0
- package/dist/resources/extensions/gsd/worktree-manager.ts +11 -4
- package/dist/resources/extensions/remote-questions/config.ts +4 -2
- package/dist/resources/extensions/remote-questions/discord-adapter.ts +35 -4
- package/dist/resources/extensions/remote-questions/format.ts +166 -14
- package/dist/resources/extensions/remote-questions/manager.ts +14 -4
- package/dist/resources/extensions/remote-questions/remote-command.ts +100 -4
- package/dist/resources/extensions/remote-questions/slack-adapter.ts +58 -2
- package/dist/resources/extensions/remote-questions/telegram-adapter.ts +161 -0
- package/dist/resources/extensions/remote-questions/types.ts +2 -1
- package/dist/resources/extensions/ttsr/ttsr-manager.ts +26 -0
- package/dist/resources/extensions/voice/index.ts +4 -3
- package/package.json +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js +12 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.js +5 -0
- package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/client.d.ts +6 -0
- package/packages/pi-coding-agent/dist/core/lsp/client.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/client.js +25 -0
- package/packages/pi-coding-agent/dist/core/lsp/client.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/index.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/lsp/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/index.js +106 -3
- package/packages/pi-coding-agent/dist/core/lsp/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/lsp.md +6 -0
- package/packages/pi-coding-agent/dist/core/lsp/types.d.ts +35 -0
- package/packages/pi-coding-agent/dist/core/lsp/types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/types.js +6 -0
- package/packages/pi-coding-agent/dist/core/lsp/types.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/utils.d.ts +3 -1
- package/packages/pi-coding-agent/dist/core/lsp/utils.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/utils.js +45 -0
- package/packages/pi-coding-agent/dist/core/lsp/utils.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +6 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.js +43 -11
- package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/system-prompt.js +7 -1
- package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/edit.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/edit.js +5 -0
- package/packages/pi-coding-agent/dist/core/tools/edit.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/index.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/tools/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/write.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/write.js +5 -0
- package/packages/pi-coding-agent/dist/core/tools/write.js.map +1 -1
- package/packages/pi-coding-agent/src/core/agent-session.ts +13 -1
- package/packages/pi-coding-agent/src/core/extensions/loader.ts +6 -0
- package/packages/pi-coding-agent/src/core/lsp/client.ts +26 -0
- package/packages/pi-coding-agent/src/core/lsp/index.ts +157 -2
- package/packages/pi-coding-agent/src/core/lsp/lsp.md +6 -0
- package/packages/pi-coding-agent/src/core/lsp/types.ts +53 -0
- package/packages/pi-coding-agent/src/core/lsp/utils.ts +56 -0
- package/packages/pi-coding-agent/src/core/settings-manager.ts +41 -11
- package/packages/pi-coding-agent/src/core/system-prompt.ts +7 -1
- package/packages/pi-coding-agent/src/core/tools/edit.ts +3 -0
- package/packages/pi-coding-agent/src/core/tools/write.ts +3 -0
- package/src/resources/extensions/google-search/index.ts +164 -47
- package/src/resources/extensions/gsd/auto-dashboard.ts +14 -2
- package/src/resources/extensions/gsd/auto-prompts.ts +148 -39
- package/src/resources/extensions/gsd/auto-worktree.ts +93 -9
- package/src/resources/extensions/gsd/auto.ts +690 -39
- package/src/resources/extensions/gsd/captures.ts +384 -0
- package/src/resources/extensions/gsd/commands.ts +654 -36
- package/src/resources/extensions/gsd/complexity-classifier.ts +322 -0
- package/src/resources/extensions/gsd/context-budget.ts +243 -0
- package/src/resources/extensions/gsd/context-store.ts +195 -0
- package/src/resources/extensions/gsd/dashboard-overlay.ts +51 -3
- package/src/resources/extensions/gsd/db-writer.ts +341 -0
- package/src/resources/extensions/gsd/debug-logger.ts +178 -0
- package/src/resources/extensions/gsd/dispatch-guard.ts +0 -1
- package/src/resources/extensions/gsd/docs/preferences-reference.md +54 -0
- package/src/resources/extensions/gsd/doctor-proactive.ts +286 -0
- package/src/resources/extensions/gsd/doctor.ts +283 -2
- package/src/resources/extensions/gsd/export.ts +81 -2
- package/src/resources/extensions/gsd/files.ts +39 -9
- package/src/resources/extensions/gsd/git-service.ts +6 -0
- package/src/resources/extensions/gsd/gsd-db.ts +752 -0
- package/src/resources/extensions/gsd/guided-flow.ts +26 -1
- package/src/resources/extensions/gsd/history.ts +0 -1
- package/src/resources/extensions/gsd/index.ts +277 -1
- package/src/resources/extensions/gsd/md-importer.ts +526 -0
- package/src/resources/extensions/gsd/metrics.ts +84 -0
- package/src/resources/extensions/gsd/model-cost-table.ts +65 -0
- package/src/resources/extensions/gsd/model-router.ts +256 -0
- package/src/resources/extensions/gsd/notifications.ts +0 -1
- package/src/resources/extensions/gsd/post-unit-hooks.ts +72 -2
- package/src/resources/extensions/gsd/preferences.ts +198 -150
- package/src/resources/extensions/gsd/prompt-loader.ts +45 -9
- package/src/resources/extensions/gsd/prompts/execute-task.md +3 -5
- package/src/resources/extensions/gsd/prompts/heal-skill.md +45 -0
- package/src/resources/extensions/gsd/prompts/plan-slice.md +5 -1
- package/src/resources/extensions/gsd/prompts/quick-task.md +48 -0
- package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -0
- package/src/resources/extensions/gsd/prompts/replan-slice.md +8 -0
- package/src/resources/extensions/gsd/prompts/system.md +2 -1
- package/src/resources/extensions/gsd/prompts/triage-captures.md +62 -0
- package/src/resources/extensions/gsd/quick.ts +156 -0
- package/src/resources/extensions/gsd/skill-discovery.ts +5 -3
- package/src/resources/extensions/gsd/skill-health.ts +417 -0
- package/src/resources/extensions/gsd/skill-telemetry.ts +127 -0
- package/src/resources/extensions/gsd/state.ts +30 -0
- package/src/resources/extensions/gsd/templates/preferences.md +1 -0
- package/src/resources/extensions/gsd/tests/captures.test.ts +438 -0
- package/src/resources/extensions/gsd/tests/complexity-classifier.test.ts +181 -0
- package/src/resources/extensions/gsd/tests/context-budget.test.ts +283 -0
- package/src/resources/extensions/gsd/tests/context-compression.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/context-store.test.ts +462 -0
- package/src/resources/extensions/gsd/tests/continue-here.test.ts +204 -0
- package/src/resources/extensions/gsd/tests/dashboard-budget.test.ts +346 -0
- package/src/resources/extensions/gsd/tests/db-writer.test.ts +602 -0
- package/src/resources/extensions/gsd/tests/debug-logger.test.ts +185 -0
- package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +406 -0
- package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +0 -1
- package/src/resources/extensions/gsd/tests/dist-redirect.mjs +22 -0
- package/src/resources/extensions/gsd/tests/doctor-proactive.test.ts +244 -0
- package/src/resources/extensions/gsd/tests/doctor-runtime.test.ts +303 -0
- package/src/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +434 -0
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +353 -0
- package/src/resources/extensions/gsd/tests/gsd-inspect.test.ts +125 -0
- package/src/resources/extensions/gsd/tests/gsd-tools.test.ts +326 -0
- package/src/resources/extensions/gsd/tests/integration-edge.test.ts +228 -0
- package/src/resources/extensions/gsd/tests/integration-lifecycle.test.ts +277 -0
- package/src/resources/extensions/gsd/tests/md-importer.test.ts +411 -0
- package/src/resources/extensions/gsd/tests/metrics.test.ts +197 -0
- package/src/resources/extensions/gsd/tests/milestone-transition-worktree.test.ts +144 -0
- package/src/resources/extensions/gsd/tests/model-cost-table.test.ts +69 -0
- package/src/resources/extensions/gsd/tests/model-isolation.test.ts +99 -0
- package/src/resources/extensions/gsd/tests/model-router.test.ts +167 -0
- package/src/resources/extensions/gsd/tests/parsers.test.ts +40 -0
- package/src/resources/extensions/gsd/tests/post-unit-hooks.test.ts +41 -1
- package/src/resources/extensions/gsd/tests/preferences-git.test.ts +0 -1
- package/src/resources/extensions/gsd/tests/preferences-hooks.test.ts +0 -1
- package/src/resources/extensions/gsd/tests/preferences-mode.test.ts +110 -0
- package/src/resources/extensions/gsd/tests/preferences-models.test.ts +0 -1
- package/src/resources/extensions/gsd/tests/prompt-budget-enforcement.test.ts +464 -0
- package/src/resources/extensions/gsd/tests/prompt-db.test.ts +385 -0
- package/src/resources/extensions/gsd/tests/remote-questions.test.ts +488 -1
- package/src/resources/extensions/gsd/tests/resolve-ts-hooks.mjs +17 -29
- package/src/resources/extensions/gsd/tests/resolve-ts.mjs +2 -8
- package/src/resources/extensions/gsd/tests/routing-history.test.ts +215 -62
- package/src/resources/extensions/gsd/tests/skill-lifecycle.test.ts +126 -0
- package/src/resources/extensions/gsd/tests/stop-auto-remote.test.ts +31 -8
- package/src/resources/extensions/gsd/tests/token-savings.test.ts +366 -0
- package/src/resources/extensions/gsd/tests/triage-dispatch.test.ts +224 -0
- package/src/resources/extensions/gsd/tests/triage-resolution.test.ts +215 -0
- package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +25 -1
- package/src/resources/extensions/gsd/tests/visualizer-critical-path.test.ts +145 -0
- package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +290 -0
- package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +120 -0
- package/src/resources/extensions/gsd/tests/visualizer-views.test.ts +478 -0
- package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +205 -0
- package/src/resources/extensions/gsd/tests/worktree-db.test.ts +442 -0
- package/src/resources/extensions/gsd/tests/worktree-post-create-hook.test.ts +165 -0
- package/src/resources/extensions/gsd/triage-resolution.ts +200 -0
- package/src/resources/extensions/gsd/triage-ui.ts +175 -0
- package/src/resources/extensions/gsd/types.ts +29 -0
- package/src/resources/extensions/gsd/undo.ts +0 -1
- package/src/resources/extensions/gsd/unit-runtime.ts +5 -1
- package/src/resources/extensions/gsd/visualizer-data.ts +505 -0
- package/src/resources/extensions/gsd/visualizer-overlay.ts +337 -0
- package/src/resources/extensions/gsd/visualizer-views.ts +755 -0
- package/src/resources/extensions/gsd/worktree-command.ts +18 -0
- package/src/resources/extensions/gsd/worktree-manager.ts +11 -4
- package/src/resources/extensions/remote-questions/config.ts +4 -2
- package/src/resources/extensions/remote-questions/discord-adapter.ts +35 -4
- package/src/resources/extensions/remote-questions/format.ts +166 -14
- package/src/resources/extensions/remote-questions/manager.ts +14 -4
- package/src/resources/extensions/remote-questions/remote-command.ts +100 -4
- package/src/resources/extensions/remote-questions/slack-adapter.ts +58 -2
- package/src/resources/extensions/remote-questions/telegram-adapter.ts +161 -0
- package/src/resources/extensions/remote-questions/types.ts +2 -1
- package/src/resources/extensions/ttsr/ttsr-manager.ts +26 -0
- package/src/resources/extensions/voice/index.ts +4 -3
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { existsSync, mkdirSync } from "node:fs";
|
|
1
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, unlinkSync } from "node:fs";
|
|
2
2
|
import { join, sep } from "node:path";
|
|
3
3
|
|
|
4
4
|
import { loadFile, parsePlan, parseRoadmap, parseSummary, saveFile, parseTaskPlanMustHaves, countMustHavesMentionedInSummary } from "./files.js";
|
|
@@ -9,6 +9,8 @@ import { listWorktrees } from "./worktree-manager.js";
|
|
|
9
9
|
import { abortAndReset } from "./git-self-heal.js";
|
|
10
10
|
import { RUNTIME_EXCLUSION_PATHS } from "./git-service.js";
|
|
11
11
|
import { nativeIsRepo, nativeWorktreeRemove, nativeBranchList, nativeBranchDelete, nativeLsFiles, nativeRmCached } from "./native-git-bridge.js";
|
|
12
|
+
import { readCrashLock, isLockProcessAlive, clearLock } from "./crash-recovery.js";
|
|
13
|
+
import { ensureGitignore } from "./gitignore.js";
|
|
12
14
|
|
|
13
15
|
export type DoctorSeverity = "info" | "warning" | "error";
|
|
14
16
|
export type DoctorIssueCode =
|
|
@@ -32,7 +34,14 @@ export type DoctorIssueCode =
|
|
|
32
34
|
| "stale_milestone_branch"
|
|
33
35
|
| "corrupt_merge_state"
|
|
34
36
|
| "tracked_runtime_files"
|
|
35
|
-
| "legacy_slice_branches"
|
|
37
|
+
| "legacy_slice_branches"
|
|
38
|
+
| "stale_crash_lock"
|
|
39
|
+
| "orphaned_completed_units"
|
|
40
|
+
| "stale_hook_state"
|
|
41
|
+
| "activity_log_bloat"
|
|
42
|
+
| "state_file_stale"
|
|
43
|
+
| "state_file_missing"
|
|
44
|
+
| "gitignore_missing_patterns";
|
|
36
45
|
|
|
37
46
|
export interface DoctorIssue {
|
|
38
47
|
severity: DoctorSeverity;
|
|
@@ -657,6 +666,275 @@ async function checkGitHealth(
|
|
|
657
666
|
}
|
|
658
667
|
}
|
|
659
668
|
|
|
669
|
+
// ── Runtime Health Checks ──────────────────────────────────────────────────
|
|
670
|
+
// Checks for stale crash locks, orphaned completed-units, stale hook state,
|
|
671
|
+
// activity log bloat, STATE.md drift, and gitignore drift.
|
|
672
|
+
|
|
673
|
+
async function checkRuntimeHealth(
|
|
674
|
+
basePath: string,
|
|
675
|
+
issues: DoctorIssue[],
|
|
676
|
+
fixesApplied: string[],
|
|
677
|
+
shouldFix: (code: DoctorIssueCode) => boolean,
|
|
678
|
+
): Promise<void> {
|
|
679
|
+
const root = gsdRoot(basePath);
|
|
680
|
+
|
|
681
|
+
// ── Stale crash lock ──────────────────────────────────────────────────
|
|
682
|
+
try {
|
|
683
|
+
const lock = readCrashLock(basePath);
|
|
684
|
+
if (lock) {
|
|
685
|
+
const alive = isLockProcessAlive(lock);
|
|
686
|
+
if (!alive) {
|
|
687
|
+
issues.push({
|
|
688
|
+
severity: "error",
|
|
689
|
+
code: "stale_crash_lock",
|
|
690
|
+
scope: "project",
|
|
691
|
+
unitId: "project",
|
|
692
|
+
message: `Stale auto.lock from PID ${lock.pid} (started ${lock.startedAt}, was executing ${lock.unitType} ${lock.unitId}) — process is no longer running`,
|
|
693
|
+
file: ".gsd/auto.lock",
|
|
694
|
+
fixable: true,
|
|
695
|
+
});
|
|
696
|
+
|
|
697
|
+
if (shouldFix("stale_crash_lock")) {
|
|
698
|
+
clearLock(basePath);
|
|
699
|
+
fixesApplied.push("cleared stale auto.lock");
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
} catch {
|
|
704
|
+
// Non-fatal — crash lock check failed
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
// ── Orphaned completed-units keys ─────────────────────────────────────
|
|
708
|
+
try {
|
|
709
|
+
const completedKeysFile = join(root, "completed-units.json");
|
|
710
|
+
if (existsSync(completedKeysFile)) {
|
|
711
|
+
const raw = readFileSync(completedKeysFile, "utf-8");
|
|
712
|
+
const keys: string[] = JSON.parse(raw);
|
|
713
|
+
const orphaned: string[] = [];
|
|
714
|
+
|
|
715
|
+
for (const key of keys) {
|
|
716
|
+
// Key format: "unitType/unitId" e.g. "execute-task/M001/S01/T01"
|
|
717
|
+
const slashIdx = key.indexOf("/");
|
|
718
|
+
if (slashIdx === -1) continue;
|
|
719
|
+
const unitType = key.slice(0, slashIdx);
|
|
720
|
+
const unitId = key.slice(slashIdx + 1);
|
|
721
|
+
|
|
722
|
+
// Only validate artifact-producing unit types
|
|
723
|
+
const { verifyExpectedArtifact } = await import("./auto-recovery.js");
|
|
724
|
+
if (!verifyExpectedArtifact(unitType, unitId, basePath)) {
|
|
725
|
+
orphaned.push(key);
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
if (orphaned.length > 0) {
|
|
730
|
+
issues.push({
|
|
731
|
+
severity: "warning",
|
|
732
|
+
code: "orphaned_completed_units",
|
|
733
|
+
scope: "project",
|
|
734
|
+
unitId: "project",
|
|
735
|
+
message: `${orphaned.length} completed-unit key(s) reference missing artifacts: ${orphaned.slice(0, 3).join(", ")}${orphaned.length > 3 ? "..." : ""}`,
|
|
736
|
+
file: ".gsd/completed-units.json",
|
|
737
|
+
fixable: true,
|
|
738
|
+
});
|
|
739
|
+
|
|
740
|
+
if (shouldFix("orphaned_completed_units")) {
|
|
741
|
+
const { removePersistedKey } = await import("./auto-recovery.js");
|
|
742
|
+
for (const key of orphaned) {
|
|
743
|
+
removePersistedKey(basePath, key);
|
|
744
|
+
}
|
|
745
|
+
fixesApplied.push(`removed ${orphaned.length} orphaned completed-unit key(s)`);
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
} catch {
|
|
750
|
+
// Non-fatal — completed-units check failed
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
// ── Stale hook state ──────────────────────────────────────────────────
|
|
754
|
+
try {
|
|
755
|
+
const hookStateFile = join(root, "hook-state.json");
|
|
756
|
+
if (existsSync(hookStateFile)) {
|
|
757
|
+
const raw = readFileSync(hookStateFile, "utf-8");
|
|
758
|
+
const state = JSON.parse(raw);
|
|
759
|
+
const hasCycleCounts = state.cycleCounts && typeof state.cycleCounts === "object"
|
|
760
|
+
&& Object.keys(state.cycleCounts).length > 0;
|
|
761
|
+
|
|
762
|
+
// Only flag if there are actual cycle counts AND no auto-mode is running
|
|
763
|
+
if (hasCycleCounts) {
|
|
764
|
+
const lock = readCrashLock(basePath);
|
|
765
|
+
const autoRunning = lock ? isLockProcessAlive(lock) : false;
|
|
766
|
+
|
|
767
|
+
if (!autoRunning) {
|
|
768
|
+
issues.push({
|
|
769
|
+
severity: "info",
|
|
770
|
+
code: "stale_hook_state",
|
|
771
|
+
scope: "project",
|
|
772
|
+
unitId: "project",
|
|
773
|
+
message: `hook-state.json has ${Object.keys(state.cycleCounts).length} residual cycle count(s) from a previous session`,
|
|
774
|
+
file: ".gsd/hook-state.json",
|
|
775
|
+
fixable: true,
|
|
776
|
+
});
|
|
777
|
+
|
|
778
|
+
if (shouldFix("stale_hook_state")) {
|
|
779
|
+
const { clearPersistedHookState } = await import("./post-unit-hooks.js");
|
|
780
|
+
clearPersistedHookState(basePath);
|
|
781
|
+
fixesApplied.push("cleared stale hook-state.json");
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
} catch {
|
|
787
|
+
// Non-fatal — hook state check failed
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
// ── Activity log bloat ────────────────────────────────────────────────
|
|
791
|
+
try {
|
|
792
|
+
const activityDir = join(root, "activity");
|
|
793
|
+
if (existsSync(activityDir)) {
|
|
794
|
+
const files = readdirSync(activityDir);
|
|
795
|
+
let totalSize = 0;
|
|
796
|
+
for (const f of files) {
|
|
797
|
+
try {
|
|
798
|
+
totalSize += statSync(join(activityDir, f)).size;
|
|
799
|
+
} catch {
|
|
800
|
+
// stat failed — skip
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
const totalMB = totalSize / (1024 * 1024);
|
|
805
|
+
const BLOAT_FILE_THRESHOLD = 500;
|
|
806
|
+
const BLOAT_SIZE_MB = 100;
|
|
807
|
+
|
|
808
|
+
if (files.length > BLOAT_FILE_THRESHOLD || totalMB > BLOAT_SIZE_MB) {
|
|
809
|
+
issues.push({
|
|
810
|
+
severity: "warning",
|
|
811
|
+
code: "activity_log_bloat",
|
|
812
|
+
scope: "project",
|
|
813
|
+
unitId: "project",
|
|
814
|
+
message: `Activity logs: ${files.length} files, ${totalMB.toFixed(1)}MB (thresholds: ${BLOAT_FILE_THRESHOLD} files / ${BLOAT_SIZE_MB}MB)`,
|
|
815
|
+
file: ".gsd/activity/",
|
|
816
|
+
fixable: true,
|
|
817
|
+
});
|
|
818
|
+
|
|
819
|
+
if (shouldFix("activity_log_bloat")) {
|
|
820
|
+
const { pruneActivityLogs } = await import("./activity-log.js");
|
|
821
|
+
pruneActivityLogs(activityDir, 7); // 7-day retention
|
|
822
|
+
fixesApplied.push("pruned activity logs (7-day retention)");
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
} catch {
|
|
827
|
+
// Non-fatal — activity log check failed
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
// ── STATE.md health ───────────────────────────────────────────────────
|
|
831
|
+
try {
|
|
832
|
+
const stateFilePath = resolveGsdRootFile(basePath, "STATE");
|
|
833
|
+
const milestonesPath = milestonesDir(basePath);
|
|
834
|
+
|
|
835
|
+
if (existsSync(milestonesPath)) {
|
|
836
|
+
if (!existsSync(stateFilePath)) {
|
|
837
|
+
issues.push({
|
|
838
|
+
severity: "warning",
|
|
839
|
+
code: "state_file_missing",
|
|
840
|
+
scope: "project",
|
|
841
|
+
unitId: "project",
|
|
842
|
+
message: "STATE.md is missing — state display will not work",
|
|
843
|
+
file: ".gsd/STATE.md",
|
|
844
|
+
fixable: true,
|
|
845
|
+
});
|
|
846
|
+
|
|
847
|
+
if (shouldFix("state_file_missing")) {
|
|
848
|
+
const state = await deriveState(basePath);
|
|
849
|
+
await saveFile(stateFilePath, buildStateMarkdown(state));
|
|
850
|
+
fixesApplied.push("created STATE.md from derived state");
|
|
851
|
+
}
|
|
852
|
+
} else {
|
|
853
|
+
// Check if STATE.md is stale by comparing active milestone/slice/phase
|
|
854
|
+
const currentContent = readFileSync(stateFilePath, "utf-8");
|
|
855
|
+
const state = await deriveState(basePath);
|
|
856
|
+
const freshContent = buildStateMarkdown(state);
|
|
857
|
+
|
|
858
|
+
// Extract key fields for comparison — don't compare full content
|
|
859
|
+
// since timestamp/formatting differences are normal
|
|
860
|
+
const extractFields = (content: string) => {
|
|
861
|
+
const milestone = content.match(/\*\*Active Milestone:\*\*\s*(.+)/)?.[1]?.trim() ?? "";
|
|
862
|
+
const slice = content.match(/\*\*Active Slice:\*\*\s*(.+)/)?.[1]?.trim() ?? "";
|
|
863
|
+
const phase = content.match(/\*\*Phase:\*\*\s*(.+)/)?.[1]?.trim() ?? "";
|
|
864
|
+
return { milestone, slice, phase };
|
|
865
|
+
};
|
|
866
|
+
|
|
867
|
+
const current = extractFields(currentContent);
|
|
868
|
+
const fresh = extractFields(freshContent);
|
|
869
|
+
|
|
870
|
+
if (current.milestone !== fresh.milestone || current.slice !== fresh.slice || current.phase !== fresh.phase) {
|
|
871
|
+
issues.push({
|
|
872
|
+
severity: "warning",
|
|
873
|
+
code: "state_file_stale",
|
|
874
|
+
scope: "project",
|
|
875
|
+
unitId: "project",
|
|
876
|
+
message: `STATE.md is stale — shows "${current.phase}" but derived state is "${fresh.phase}"`,
|
|
877
|
+
file: ".gsd/STATE.md",
|
|
878
|
+
fixable: true,
|
|
879
|
+
});
|
|
880
|
+
|
|
881
|
+
if (shouldFix("state_file_stale")) {
|
|
882
|
+
await saveFile(stateFilePath, freshContent);
|
|
883
|
+
fixesApplied.push("rebuilt STATE.md from derived state");
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
} catch {
|
|
889
|
+
// Non-fatal — STATE.md check failed
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
// ── Gitignore drift ───────────────────────────────────────────────────
|
|
893
|
+
try {
|
|
894
|
+
const gitignorePath = join(basePath, ".gitignore");
|
|
895
|
+
if (existsSync(gitignorePath) && nativeIsRepo(basePath)) {
|
|
896
|
+
const content = readFileSync(gitignorePath, "utf-8");
|
|
897
|
+
const existingLines = new Set(
|
|
898
|
+
content.split("\n").map(l => l.trim()).filter(l => l && !l.startsWith("#")),
|
|
899
|
+
);
|
|
900
|
+
|
|
901
|
+
// Check for critical runtime patterns that must be present
|
|
902
|
+
const criticalPatterns = [
|
|
903
|
+
".gsd/activity/",
|
|
904
|
+
".gsd/runtime/",
|
|
905
|
+
".gsd/auto.lock",
|
|
906
|
+
".gsd/gsd.db",
|
|
907
|
+
".gsd/completed-units.json",
|
|
908
|
+
];
|
|
909
|
+
|
|
910
|
+
// If blanket .gsd/ or .gsd is present, all patterns are covered
|
|
911
|
+
const hasBlanketIgnore = existingLines.has(".gsd/") || existingLines.has(".gsd");
|
|
912
|
+
|
|
913
|
+
if (!hasBlanketIgnore) {
|
|
914
|
+
const missing = criticalPatterns.filter(p => !existingLines.has(p));
|
|
915
|
+
if (missing.length > 0) {
|
|
916
|
+
issues.push({
|
|
917
|
+
severity: "warning",
|
|
918
|
+
code: "gitignore_missing_patterns",
|
|
919
|
+
scope: "project",
|
|
920
|
+
unitId: "project",
|
|
921
|
+
message: `${missing.length} critical GSD runtime pattern(s) missing from .gitignore: ${missing.join(", ")}`,
|
|
922
|
+
file: ".gitignore",
|
|
923
|
+
fixable: true,
|
|
924
|
+
});
|
|
925
|
+
|
|
926
|
+
if (shouldFix("gitignore_missing_patterns")) {
|
|
927
|
+
ensureGitignore(basePath);
|
|
928
|
+
fixesApplied.push("added missing GSD runtime patterns to .gitignore");
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
} catch {
|
|
934
|
+
// Non-fatal — gitignore check failed
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
|
|
660
938
|
export async function runGSDDoctor(basePath: string, options?: { fix?: boolean; scope?: string; fixLevel?: "task" | "all" }): Promise<DoctorReport> {
|
|
661
939
|
const issues: DoctorIssue[] = [];
|
|
662
940
|
const fixesApplied: string[] = [];
|
|
@@ -700,6 +978,9 @@ export async function runGSDDoctor(basePath: string, options?: { fix?: boolean;
|
|
|
700
978
|
// Git health checks (orphaned worktrees, stale branches, corrupt merge state, tracked runtime files)
|
|
701
979
|
await checkGitHealth(basePath, issues, fixesApplied, shouldFix);
|
|
702
980
|
|
|
981
|
+
// Runtime health checks (crash locks, completed-units, hook state, activity logs, STATE.md, gitignore)
|
|
982
|
+
await checkRuntimeHealth(basePath, issues, fixesApplied, shouldFix);
|
|
983
|
+
|
|
703
984
|
const milestonesPath = milestonesDir(basePath);
|
|
704
985
|
if (!existsSync(milestonesPath)) {
|
|
705
986
|
return { ok: issues.every(issue => issue.severity !== "error"), basePath, issues, fixesApplied };
|
|
@@ -1,18 +1,97 @@
|
|
|
1
1
|
// GSD Extension — Session/Milestone Export
|
|
2
2
|
// Generate shareable reports of milestone work in JSON or markdown format.
|
|
3
|
-
// Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
|
|
4
3
|
|
|
5
4
|
import type { ExtensionCommandContext } from "@gsd/pi-coding-agent";
|
|
6
5
|
import { writeFileSync, mkdirSync } from "node:fs";
|
|
7
6
|
import { join, basename } from "node:path";
|
|
8
7
|
import {
|
|
9
8
|
getLedger, getProjectTotals, aggregateByPhase, aggregateBySlice,
|
|
10
|
-
aggregateByModel, formatCost, formatTokenCount,
|
|
9
|
+
aggregateByModel, formatCost, formatTokenCount, loadLedgerFromDisk,
|
|
11
10
|
} from "./metrics.js";
|
|
12
11
|
import type { UnitMetrics } from "./metrics.js";
|
|
13
12
|
import { gsdRoot } from "./paths.js";
|
|
14
13
|
import { formatDuration } from "./history.js";
|
|
15
14
|
|
|
15
|
+
/**
|
|
16
|
+
* Write an export file directly, without requiring an ExtensionCommandContext.
|
|
17
|
+
* Used by the visualizer overlay export tab.
|
|
18
|
+
* Returns the output file path, or null on failure.
|
|
19
|
+
*/
|
|
20
|
+
export function writeExportFile(
|
|
21
|
+
basePath: string,
|
|
22
|
+
format: "markdown" | "json",
|
|
23
|
+
visualizerData?: { totals: any; byPhase: any[]; bySlice: any[]; byModel: any[]; units: any[]; criticalPath?: any; remainingSliceCount?: number },
|
|
24
|
+
): string | null {
|
|
25
|
+
const ledger = getLedger();
|
|
26
|
+
let units: UnitMetrics[];
|
|
27
|
+
|
|
28
|
+
if (visualizerData && visualizerData.units.length > 0) {
|
|
29
|
+
units = visualizerData.units;
|
|
30
|
+
} else if (ledger && ledger.units.length > 0) {
|
|
31
|
+
units = ledger.units;
|
|
32
|
+
} else {
|
|
33
|
+
const diskLedger = loadLedgerFromDisk(basePath);
|
|
34
|
+
if (!diskLedger || diskLedger.units.length === 0) return null;
|
|
35
|
+
units = diskLedger.units;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const projectName = basename(basePath);
|
|
39
|
+
const exportDir = gsdRoot(basePath);
|
|
40
|
+
mkdirSync(exportDir, { recursive: true });
|
|
41
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
42
|
+
|
|
43
|
+
if (format === "json") {
|
|
44
|
+
const report = {
|
|
45
|
+
exportedAt: new Date().toISOString(),
|
|
46
|
+
project: projectName,
|
|
47
|
+
totals: visualizerData?.totals ?? getProjectTotals(units),
|
|
48
|
+
byPhase: visualizerData?.byPhase ?? aggregateByPhase(units),
|
|
49
|
+
bySlice: visualizerData?.bySlice ?? aggregateBySlice(units),
|
|
50
|
+
byModel: visualizerData?.byModel ?? aggregateByModel(units),
|
|
51
|
+
units,
|
|
52
|
+
};
|
|
53
|
+
const outPath = join(exportDir, `export-${timestamp}.json`);
|
|
54
|
+
writeFileSync(outPath, JSON.stringify(report, null, 2) + "\n", "utf-8");
|
|
55
|
+
return outPath;
|
|
56
|
+
} else {
|
|
57
|
+
const totals = visualizerData?.totals ?? getProjectTotals(units);
|
|
58
|
+
const phases = visualizerData?.byPhase ?? aggregateByPhase(units);
|
|
59
|
+
const slices = visualizerData?.bySlice ?? aggregateBySlice(units);
|
|
60
|
+
|
|
61
|
+
const md = [
|
|
62
|
+
`# GSD Session Report — ${projectName}`,
|
|
63
|
+
``,
|
|
64
|
+
`**Generated**: ${new Date().toISOString()}`,
|
|
65
|
+
`**Units completed**: ${totals.units}`,
|
|
66
|
+
`**Total cost**: ${formatCost(totals.cost)}`,
|
|
67
|
+
`**Total tokens**: ${formatTokenCount(totals.tokens.total)}`,
|
|
68
|
+
`**Total duration**: ${formatDuration(totals.duration)}`,
|
|
69
|
+
`**Tool calls**: ${totals.toolCalls}`,
|
|
70
|
+
``,
|
|
71
|
+
`## Cost by Phase`,
|
|
72
|
+
``,
|
|
73
|
+
`| Phase | Units | Cost | Tokens | Duration |`,
|
|
74
|
+
`|-------|-------|------|--------|----------|`,
|
|
75
|
+
...phases.map((p: any) =>
|
|
76
|
+
`| ${p.phase} | ${p.units} | ${formatCost(p.cost)} | ${formatTokenCount(p.tokens.total)} | ${formatDuration(p.duration)} |`,
|
|
77
|
+
),
|
|
78
|
+
``,
|
|
79
|
+
`## Cost by Slice`,
|
|
80
|
+
``,
|
|
81
|
+
`| Slice | Units | Cost | Tokens | Duration |`,
|
|
82
|
+
`|-------|-------|------|--------|----------|`,
|
|
83
|
+
...slices.map((s: any) =>
|
|
84
|
+
`| ${s.sliceId} | ${s.units} | ${formatCost(s.cost)} | ${formatTokenCount(s.tokens.total)} | ${formatDuration(s.duration)} |`,
|
|
85
|
+
),
|
|
86
|
+
``,
|
|
87
|
+
].join("\n");
|
|
88
|
+
|
|
89
|
+
const outPath = join(exportDir, `export-${timestamp}.md`);
|
|
90
|
+
writeFileSync(outPath, md, "utf-8");
|
|
91
|
+
return outPath;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
16
95
|
/**
|
|
17
96
|
* Export session/milestone data to JSON or markdown.
|
|
18
97
|
*/
|
|
@@ -21,6 +21,7 @@ import type {
|
|
|
21
21
|
import { checkExistingEnvKeys } from '../get-secrets-from-user.js';
|
|
22
22
|
import { parseRoadmapSlices } from './roadmap-slices.js';
|
|
23
23
|
import { nativeParseRoadmap, nativeExtractSection, nativeParsePlanFile, nativeParseSummaryFile, NATIVE_UNAVAILABLE } from './native-parser-bridge.js';
|
|
24
|
+
import { debugTime, debugCount } from './debug-logger.js';
|
|
24
25
|
|
|
25
26
|
// ─── Parse Cache ──────────────────────────────────────────────────────────
|
|
26
27
|
|
|
@@ -224,9 +225,14 @@ export function parseRoadmap(content: string): Roadmap {
|
|
|
224
225
|
}
|
|
225
226
|
|
|
226
227
|
function _parseRoadmapImpl(content: string): Roadmap {
|
|
228
|
+
const stopTimer = debugTime("parse-roadmap");
|
|
227
229
|
// Try native parser first for better performance
|
|
228
230
|
const nativeResult = nativeParseRoadmap(content);
|
|
229
|
-
if (nativeResult)
|
|
231
|
+
if (nativeResult) {
|
|
232
|
+
stopTimer({ native: true, slices: nativeResult.slices.length, boundaryEntries: nativeResult.boundaryMap.length });
|
|
233
|
+
debugCount("parseRoadmapCalls");
|
|
234
|
+
return nativeResult;
|
|
235
|
+
}
|
|
230
236
|
|
|
231
237
|
const lines = content.split('\n');
|
|
232
238
|
|
|
@@ -265,21 +271,40 @@ function _parseRoadmapImpl(content: string): Roadmap {
|
|
|
265
271
|
let produces = '';
|
|
266
272
|
let consumes = '';
|
|
267
273
|
|
|
268
|
-
|
|
269
|
-
|
|
274
|
+
// Use indexOf-based parsing instead of [\s\S]*? regex to avoid
|
|
275
|
+
// catastrophic backtracking on content with code fences (#468).
|
|
276
|
+
const prodIdx = sectionContent.search(/^Produces:\s*$/m);
|
|
277
|
+
if (prodIdx !== -1) {
|
|
278
|
+
const afterProd = sectionContent.indexOf('\n', prodIdx);
|
|
279
|
+
if (afterProd !== -1) {
|
|
280
|
+
const consIdx = sectionContent.search(/^Consumes/m);
|
|
281
|
+
const endIdx = consIdx !== -1 && consIdx > afterProd ? consIdx : sectionContent.length;
|
|
282
|
+
produces = sectionContent.slice(afterProd + 1, endIdx).trim();
|
|
283
|
+
}
|
|
284
|
+
}
|
|
270
285
|
|
|
271
|
-
const
|
|
272
|
-
if (
|
|
286
|
+
const consLineMatch = sectionContent.match(/^Consumes[^:]*:\s*(.+)$/m);
|
|
287
|
+
if (consLineMatch) {
|
|
288
|
+
consumes = consLineMatch[1].trim();
|
|
289
|
+
}
|
|
273
290
|
if (!consumes) {
|
|
274
|
-
const
|
|
275
|
-
if (
|
|
291
|
+
const consIdx = sectionContent.search(/^Consumes[^:]*:\s*$/m);
|
|
292
|
+
if (consIdx !== -1) {
|
|
293
|
+
const afterCons = sectionContent.indexOf('\n', consIdx);
|
|
294
|
+
if (afterCons !== -1) {
|
|
295
|
+
consumes = sectionContent.slice(afterCons + 1).trim();
|
|
296
|
+
}
|
|
297
|
+
}
|
|
276
298
|
}
|
|
277
299
|
|
|
278
300
|
boundaryMap.push({ fromSlice, toSlice, produces, consumes });
|
|
279
301
|
}
|
|
280
302
|
}
|
|
281
303
|
|
|
282
|
-
|
|
304
|
+
const result = { title, vision, successCriteria, slices, boundaryMap };
|
|
305
|
+
stopTimer({ native: false, slices: slices.length, boundaryEntries: boundaryMap.length });
|
|
306
|
+
debugCount("parseRoadmapCalls");
|
|
307
|
+
return result;
|
|
283
308
|
}
|
|
284
309
|
|
|
285
310
|
// ─── Secrets Manifest Parser ───────────────────────────────────────────────
|
|
@@ -358,9 +383,11 @@ export function parsePlan(content: string): SlicePlan {
|
|
|
358
383
|
}
|
|
359
384
|
|
|
360
385
|
function _parsePlanImpl(content: string): SlicePlan {
|
|
386
|
+
const stopTimer = debugTime("parse-plan");
|
|
361
387
|
// Try native parser first for better performance
|
|
362
388
|
const nativeResult = nativeParsePlanFile(content);
|
|
363
389
|
if (nativeResult) {
|
|
390
|
+
stopTimer({ native: true });
|
|
364
391
|
return {
|
|
365
392
|
id: nativeResult.id,
|
|
366
393
|
title: nativeResult.title,
|
|
@@ -452,7 +479,10 @@ function _parsePlanImpl(content: string): SlicePlan {
|
|
|
452
479
|
const filesSection = extractSection(content, 'Files Likely Touched');
|
|
453
480
|
const filesLikelyTouched = filesSection ? parseBullets(filesSection) : [];
|
|
454
481
|
|
|
455
|
-
|
|
482
|
+
const result = { id, title, goal, demo, mustHaves, tasks, filesLikelyTouched };
|
|
483
|
+
stopTimer({ tasks: tasks.length });
|
|
484
|
+
debugCount("parsePlanCalls");
|
|
485
|
+
return result;
|
|
456
486
|
}
|
|
457
487
|
|
|
458
488
|
// ─── Summary Parser ────────────────────────────────────────────────────────
|
|
@@ -52,6 +52,12 @@ export interface GitPreferences {
|
|
|
52
52
|
* Default: true (planning docs are tracked in git).
|
|
53
53
|
*/
|
|
54
54
|
commit_docs?: boolean;
|
|
55
|
+
/** Script to run after a worktree is created (#597).
|
|
56
|
+
* Receives SOURCE_DIR and WORKTREE_DIR as environment variables.
|
|
57
|
+
* Can be an absolute path or relative to the project root.
|
|
58
|
+
* Failure is non-fatal — logged as a warning.
|
|
59
|
+
*/
|
|
60
|
+
worktree_post_create?: string;
|
|
55
61
|
}
|
|
56
62
|
|
|
57
63
|
export const VALID_BRANCH_NAME = /^[a-zA-Z0-9_\-\/.]+$/;
|