gsd-pi 2.80.0-dev.96f4d89d5 → 2.80.0-dev.b2a374d5b
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/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/GSD-WORKFLOW.md +2 -2
- package/dist/resources/extensions/gsd/auto/loop.js +32 -1
- package/dist/resources/extensions/gsd/auto/phases.js +37 -30
- package/dist/resources/extensions/gsd/auto-dispatch.js +10 -0
- package/dist/resources/extensions/gsd/auto-post-unit.js +10 -10
- package/dist/resources/extensions/gsd/auto-prompts.js +111 -1
- package/dist/resources/extensions/gsd/auto-recovery.js +154 -8
- package/dist/resources/extensions/gsd/auto-start.js +2 -3
- package/dist/resources/extensions/gsd/auto.js +16 -3
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +32 -1
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +129 -1
- package/dist/resources/extensions/gsd/clean-root-preflight.js +42 -4
- package/dist/resources/extensions/gsd/commands/dispatcher.js +5 -0
- package/dist/resources/extensions/gsd/commands-extract-learnings.js +17 -12
- package/dist/resources/extensions/gsd/crash-recovery.js +56 -10
- package/dist/resources/extensions/gsd/db-base-schema.js +14 -0
- package/dist/resources/extensions/gsd/db-migration-steps.js +16 -0
- package/dist/resources/extensions/gsd/detection.js +106 -0
- package/dist/resources/extensions/gsd/gsd-db.js +102 -2
- package/dist/resources/extensions/gsd/guided-flow.js +63 -12
- package/dist/resources/extensions/gsd/migrate/command.js +40 -1
- package/dist/resources/extensions/gsd/migration-auto-check.js +87 -0
- package/dist/resources/extensions/gsd/planning-path-scope.js +26 -0
- package/dist/resources/extensions/gsd/prompts/complete-milestone.md +7 -8
- package/dist/resources/extensions/gsd/prompts/parallel-research-slices.md +1 -1
- package/dist/resources/extensions/gsd/prompts/plan-milestone.md +3 -1
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/quick-task.md +1 -5
- package/dist/resources/extensions/gsd/prompts/validate-milestone.md +2 -2
- package/dist/resources/extensions/gsd/quick.js +34 -2
- package/dist/resources/extensions/gsd/safety/evidence-collector.js +10 -2
- package/dist/resources/extensions/gsd/tools/plan-slice.js +9 -0
- package/dist/resources/extensions/gsd/tools/plan-task.js +9 -0
- package/dist/resources/extensions/gsd/unit-runtime.js +11 -0
- package/dist/resources/extensions/gsd/worktree-manager.js +16 -14
- package/dist/tsconfig.extensions.tsbuildinfo +1 -1
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +13 -13
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +13 -13
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/package.json +4 -2
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js +3 -0
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js +76 -0
- package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction/compaction.d.ts +11 -0
- package/packages/pi-coding-agent/dist/core/compaction/compaction.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction/compaction.js +9 -0
- package/packages/pi-coding-agent/dist/core/compaction/compaction.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction-threshold.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/compaction-threshold.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/compaction-threshold.test.js +103 -0
- package/packages/pi-coding-agent/dist/core/compaction-threshold.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/db-snapshot.d.ts +15 -0
- package/packages/pi-coding-agent/dist/core/db-snapshot.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/db-snapshot.js +66 -0
- package/packages/pi-coding-agent/dist/core/db-snapshot.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/db-snapshot.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/db-snapshot.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/db-snapshot.test.js +24 -0
- package/packages/pi-coding-agent/dist/core/db-snapshot.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts +3 -0
- package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.js +17 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.test.js +99 -0
- package/packages/pi-coding-agent/dist/core/extensions/runner.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +7 -0
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +20 -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 +25 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/chat-frame-compaction-tone.test.js +6 -4
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/chat-frame-compaction-tone.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js +50 -19
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.js +2 -2
- package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js +2 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js +7 -6
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +13 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +77 -13
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +68 -2
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.js +53 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +3 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js +36 -27
- package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.js +21 -0
- package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.js +10 -0
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.js +3 -2
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.js.map +1 -1
- package/packages/pi-coding-agent/src/core/agent-session.ts +3 -0
- package/packages/pi-coding-agent/src/core/chat-controller-ordering.test.ts +89 -0
- package/packages/pi-coding-agent/src/core/compaction/compaction.ts +18 -0
- package/packages/pi-coding-agent/src/core/compaction-threshold.test.ts +121 -0
- package/packages/pi-coding-agent/src/core/db-snapshot.test.ts +32 -0
- package/packages/pi-coding-agent/src/core/db-snapshot.ts +66 -0
- package/packages/pi-coding-agent/src/core/extensions/runner.test.ts +110 -0
- package/packages/pi-coding-agent/src/core/extensions/runner.ts +19 -1
- package/packages/pi-coding-agent/src/core/extensions/types.ts +7 -0
- package/packages/pi-coding-agent/src/core/settings-manager.ts +39 -1
- package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/chat-frame-compaction-tone.test.ts +7 -5
- package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-execution.test.ts +68 -19
- package/packages/pi-coding-agent/src/modes/interactive/components/adaptive-layout.ts +2 -2
- package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +1 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/chat-frame.ts +2 -1
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.ts +10 -9
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +88 -13
- package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.test.ts +56 -0
- package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +91 -3
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +3 -0
- package/packages/pi-coding-agent/src/modes/interactive/theme/themes.ts +36 -27
- package/packages/pi-coding-agent/src/modes/interactive/tui-mode.test.ts +25 -0
- package/packages/pi-coding-agent/src/resources/extensions/memory/storage-safety-guard.test.ts +14 -0
- package/packages/pi-coding-agent/src/resources/extensions/memory/storage.ts +3 -2
- package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-tui/dist/tui.d.ts.map +1 -1
- package/packages/pi-tui/dist/tui.js +17 -7
- package/packages/pi-tui/dist/tui.js.map +1 -1
- package/packages/pi-tui/src/tui.ts +19 -7
- package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
- package/pkg/dist/modes/interactive/theme/themes.d.ts.map +1 -1
- package/pkg/dist/modes/interactive/theme/themes.js +36 -27
- package/pkg/dist/modes/interactive/theme/themes.js.map +1 -1
- package/src/resources/GSD-WORKFLOW.md +2 -2
- package/src/resources/extensions/gsd/auto/loop-deps.ts +1 -0
- package/src/resources/extensions/gsd/auto/loop.ts +50 -8
- package/src/resources/extensions/gsd/auto/phases.ts +42 -28
- package/src/resources/extensions/gsd/auto-dispatch.ts +17 -0
- package/src/resources/extensions/gsd/auto-post-unit.ts +10 -10
- package/src/resources/extensions/gsd/auto-prompts.ts +116 -1
- package/src/resources/extensions/gsd/auto-recovery.ts +153 -7
- package/src/resources/extensions/gsd/auto-start.ts +7 -6
- package/src/resources/extensions/gsd/auto.ts +23 -3
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +39 -1
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +135 -1
- package/src/resources/extensions/gsd/clean-root-preflight.ts +41 -3
- package/src/resources/extensions/gsd/commands/dispatcher.ts +6 -0
- package/src/resources/extensions/gsd/commands-extract-learnings.ts +17 -12
- package/src/resources/extensions/gsd/crash-recovery.ts +67 -10
- package/src/resources/extensions/gsd/db-base-schema.ts +15 -0
- package/src/resources/extensions/gsd/db-migration-steps.ts +17 -0
- package/src/resources/extensions/gsd/detection.ts +128 -0
- package/src/resources/extensions/gsd/gsd-db.ts +119 -1
- package/src/resources/extensions/gsd/guided-flow.ts +66 -12
- package/src/resources/extensions/gsd/migrate/command.ts +47 -1
- package/src/resources/extensions/gsd/migration-auto-check.ts +129 -0
- package/src/resources/extensions/gsd/planning-path-scope.ts +35 -0
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +7 -8
- package/src/resources/extensions/gsd/prompts/parallel-research-slices.md +1 -1
- package/src/resources/extensions/gsd/prompts/plan-milestone.md +3 -1
- package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/quick-task.md +1 -5
- package/src/resources/extensions/gsd/prompts/validate-milestone.md +2 -2
- package/src/resources/extensions/gsd/quick.ts +37 -2
- package/src/resources/extensions/gsd/safety/evidence-collector.ts +11 -2
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +170 -1
- package/src/resources/extensions/gsd/tests/clean-root-preflight.test.ts +88 -2
- package/src/resources/extensions/gsd/tests/commands-extract-learnings.test.ts +9 -0
- package/src/resources/extensions/gsd/tests/crash-handler-secondary.test.ts +55 -0
- package/src/resources/extensions/gsd/tests/crash-recovery-via-db.test.ts +22 -0
- package/src/resources/extensions/gsd/tests/db-schema-metadata.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/detection.test.ts +140 -0
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +44 -0
- package/src/resources/extensions/gsd/tests/has-pending-deep-stage.test.ts +33 -1
- package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +48 -0
- package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +127 -0
- package/src/resources/extensions/gsd/tests/plan-slice.test.ts +50 -0
- package/src/resources/extensions/gsd/tests/plan-task.test.ts +21 -0
- package/src/resources/extensions/gsd/tests/prompt-path-audit.test.ts +40 -0
- package/src/resources/extensions/gsd/tests/quick-external-gsd.test.ts +40 -0
- package/src/resources/extensions/gsd/tests/right-sized-workflow-prompts.test.ts +192 -0
- package/src/resources/extensions/gsd/tests/safety-harness-false-positives.test.ts +29 -0
- package/src/resources/extensions/gsd/tests/smart-entry-complete.test.ts +38 -0
- package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +63 -2
- package/src/resources/extensions/gsd/tests/status-db-open.test.ts +9 -0
- package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +7 -0
- package/src/resources/extensions/gsd/tests/uok-plan-v2-wiring.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/worktree-health-dispatch.test.ts +37 -6
- package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +7 -0
- package/src/resources/extensions/gsd/tests/worktree-nested-git-safety.test.ts +9 -2
- package/src/resources/extensions/gsd/tests/worktree-write-gate.test.ts +179 -0
- package/src/resources/extensions/gsd/tools/plan-slice.ts +13 -0
- package/src/resources/extensions/gsd/tools/plan-task.ts +10 -0
- package/src/resources/extensions/gsd/unit-runtime.ts +14 -0
- package/src/resources/extensions/gsd/worktree-manager.ts +15 -4
- /package/dist/web/standalone/.next/static/{y7d--cnc6Cibuva-Lzzw_ → FU0iL9dhdc2IBzEYBDOlY}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{y7d--cnc6Cibuva-Lzzw_ → FU0iL9dhdc2IBzEYBDOlY}/_ssgManifest.js +0 -0
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* flow to show when entering a project directory.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
import { execFileSync } from "node:child_process";
|
|
9
10
|
import { existsSync, openSync, readSync, closeSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
10
11
|
import { dirname, join, parse as parsePath } from "node:path";
|
|
11
12
|
import { homedir } from "node:os";
|
|
@@ -72,6 +73,22 @@ export interface ProjectSignals {
|
|
|
72
73
|
verificationCommands: string[];
|
|
73
74
|
}
|
|
74
75
|
|
|
76
|
+
export type ProjectClassificationKind =
|
|
77
|
+
| "invalid-repo"
|
|
78
|
+
| "greenfield"
|
|
79
|
+
| "untyped-existing"
|
|
80
|
+
| "typed-existing";
|
|
81
|
+
|
|
82
|
+
export interface ProjectClassification {
|
|
83
|
+
kind: ProjectClassificationKind;
|
|
84
|
+
signals: ProjectSignals;
|
|
85
|
+
trackedFiles: string[];
|
|
86
|
+
untrackedFiles: string[];
|
|
87
|
+
contentFiles: string[];
|
|
88
|
+
markers: string[];
|
|
89
|
+
reason: string;
|
|
90
|
+
}
|
|
91
|
+
|
|
75
92
|
// ─── Project File Markers ───────────────────────────────────────────────────────
|
|
76
93
|
|
|
77
94
|
export const PROJECT_FILES = [
|
|
@@ -243,6 +260,7 @@ const TEST_MARKERS = [
|
|
|
243
260
|
const RECURSIVE_SCAN_IGNORED_DIRS = new Set([
|
|
244
261
|
".git",
|
|
245
262
|
".gsd",
|
|
263
|
+
".bg-shell",
|
|
246
264
|
".planning",
|
|
247
265
|
".plans",
|
|
248
266
|
".claude",
|
|
@@ -267,6 +285,8 @@ const RECURSIVE_SCAN_IGNORED_DIRS = new Set([
|
|
|
267
285
|
"out",
|
|
268
286
|
]) as ReadonlySet<string>;
|
|
269
287
|
|
|
288
|
+
const PROJECT_CONTENT_EXCLUDE_DIRS = RECURSIVE_SCAN_IGNORED_DIRS;
|
|
289
|
+
|
|
270
290
|
/** Project file markers safe to detect recursively via suffix matching. */
|
|
271
291
|
const ROOT_ONLY_PROJECT_FILES = new Set<string>([
|
|
272
292
|
".github/workflows",
|
|
@@ -536,6 +556,114 @@ export function detectProjectSignals(basePath: string): ProjectSignals {
|
|
|
536
556
|
};
|
|
537
557
|
}
|
|
538
558
|
|
|
559
|
+
function normalizeGitPath(file: string): string {
|
|
560
|
+
return file.replaceAll("\\", "/").replace(/^\.\//, "");
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
function isProjectContentFile(file: string): boolean {
|
|
564
|
+
const normalized = normalizeGitPath(file);
|
|
565
|
+
if (!normalized || normalized.endsWith("/")) return false;
|
|
566
|
+
if (normalized === ".gitignore" || normalized === ".gitattributes") return false;
|
|
567
|
+
const parts = normalized.split("/");
|
|
568
|
+
if (parts.some((part) => PROJECT_CONTENT_EXCLUDE_DIRS.has(part))) return false;
|
|
569
|
+
if (normalized.endsWith(".DS_Store")) return false;
|
|
570
|
+
return true;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
function runGitLines(basePath: string, args: string[]): string[] {
|
|
574
|
+
try {
|
|
575
|
+
const output = execFileSync("git", args, {
|
|
576
|
+
cwd: basePath,
|
|
577
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
578
|
+
encoding: "utf-8",
|
|
579
|
+
}).trim();
|
|
580
|
+
return output ? output.split("\n").map((line) => line.trim()).filter(Boolean) : [];
|
|
581
|
+
} catch {
|
|
582
|
+
return [];
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
function listTrackedProjectFiles(basePath: string): string[] {
|
|
587
|
+
return runGitLines(basePath, ["ls-files"])
|
|
588
|
+
.map(normalizeGitPath)
|
|
589
|
+
.filter(isProjectContentFile);
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
function listUntrackedProjectFiles(basePath: string): string[] {
|
|
593
|
+
return runGitLines(basePath, ["ls-files", "--others", "--exclude-standard"])
|
|
594
|
+
.map(normalizeGitPath)
|
|
595
|
+
.filter(isProjectContentFile);
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
function hasKnownProjectMarkers(basePath: string, signals: ProjectSignals): boolean {
|
|
599
|
+
if (signals.detectedFiles.length > 0) return true;
|
|
600
|
+
if (signals.xcodePlatforms.length > 0) return true;
|
|
601
|
+
return false;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
/**
|
|
605
|
+
* Classify repo presence separately from ecosystem/tooling markers.
|
|
606
|
+
*
|
|
607
|
+
* Known project files identify tooling. Git-tracked/non-ignored content
|
|
608
|
+
* identifies whether this is an existing project at all. This keeps small
|
|
609
|
+
* static or documentation repos from being mislabeled as greenfield.
|
|
610
|
+
*/
|
|
611
|
+
export function classifyProject(basePath: string): ProjectClassification {
|
|
612
|
+
const signals = detectProjectSignals(basePath);
|
|
613
|
+
const markers = [...signals.detectedFiles];
|
|
614
|
+
|
|
615
|
+
if (!signals.isGitRepo) {
|
|
616
|
+
return {
|
|
617
|
+
kind: "invalid-repo",
|
|
618
|
+
signals,
|
|
619
|
+
trackedFiles: [],
|
|
620
|
+
untrackedFiles: [],
|
|
621
|
+
contentFiles: [],
|
|
622
|
+
markers,
|
|
623
|
+
reason: "missing .git",
|
|
624
|
+
};
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
const trackedFiles = listTrackedProjectFiles(basePath);
|
|
628
|
+
const untrackedFiles = listUntrackedProjectFiles(basePath);
|
|
629
|
+
const contentFiles = [...new Set([...trackedFiles, ...untrackedFiles])];
|
|
630
|
+
const hasMarkers = hasKnownProjectMarkers(basePath, signals);
|
|
631
|
+
|
|
632
|
+
if (hasMarkers) {
|
|
633
|
+
return {
|
|
634
|
+
kind: "typed-existing",
|
|
635
|
+
signals,
|
|
636
|
+
trackedFiles,
|
|
637
|
+
untrackedFiles,
|
|
638
|
+
contentFiles,
|
|
639
|
+
markers,
|
|
640
|
+
reason: markers.length > 0 ? `detected markers: ${markers.join(", ")}` : "detected project structure",
|
|
641
|
+
};
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
if (contentFiles.length > 0) {
|
|
645
|
+
return {
|
|
646
|
+
kind: "untyped-existing",
|
|
647
|
+
signals,
|
|
648
|
+
trackedFiles,
|
|
649
|
+
untrackedFiles,
|
|
650
|
+
contentFiles,
|
|
651
|
+
markers,
|
|
652
|
+
reason: "project content exists but no recognized tooling markers were found",
|
|
653
|
+
};
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
return {
|
|
657
|
+
kind: "greenfield",
|
|
658
|
+
signals,
|
|
659
|
+
trackedFiles,
|
|
660
|
+
untrackedFiles,
|
|
661
|
+
contentFiles,
|
|
662
|
+
markers,
|
|
663
|
+
reason: "no tracked or non-ignored project content",
|
|
664
|
+
};
|
|
665
|
+
}
|
|
666
|
+
|
|
539
667
|
// ─── Xcode Platform Detection ───────────────────────────────────────────────────
|
|
540
668
|
|
|
541
669
|
/** Known SDKROOT values → canonical platform names. */
|
|
@@ -77,6 +77,7 @@ import {
|
|
|
77
77
|
applyMigrationV21StructuredMemories,
|
|
78
78
|
applyMigrationV22QualityGateRepair,
|
|
79
79
|
applyMigrationV23MilestoneQueue,
|
|
80
|
+
applyMigrationV26MilestoneCommitAttributions,
|
|
80
81
|
} from "./db-migration-steps.js";
|
|
81
82
|
import { isMemoriesFtsAvailableSchema, tryCreateMemoriesFtsSchema } from "./db-memory-fts-schema.js";
|
|
82
83
|
import { createDbOpenState, type DbOpenPhase } from "./db-open-state.js";
|
|
@@ -105,7 +106,7 @@ const providerLoader = createSqliteProviderLoader({
|
|
|
105
106
|
writeStderr: (message: string) => process.stderr.write(message),
|
|
106
107
|
});
|
|
107
108
|
|
|
108
|
-
export const SCHEMA_VERSION =
|
|
109
|
+
export const SCHEMA_VERSION = 26;
|
|
109
110
|
|
|
110
111
|
function initSchema(db: DbAdapter, fileBacked: boolean): void {
|
|
111
112
|
if (fileBacked) db.exec("PRAGMA journal_mode=WAL");
|
|
@@ -329,6 +330,11 @@ function migrateSchema(db: DbAdapter): void {
|
|
|
329
330
|
recordSchemaVersion(db, 25);
|
|
330
331
|
}
|
|
331
332
|
|
|
333
|
+
if (currentVersion < 26) {
|
|
334
|
+
applyMigrationV26MilestoneCommitAttributions(db);
|
|
335
|
+
recordSchemaVersion(db, 26);
|
|
336
|
+
}
|
|
337
|
+
|
|
332
338
|
db.exec("COMMIT");
|
|
333
339
|
} catch (err) {
|
|
334
340
|
db.exec("ROLLBACK");
|
|
@@ -1349,6 +1355,45 @@ export function getSliceTasks(milestoneId: string, sliceId: string): TaskRow[] {
|
|
|
1349
1355
|
return rows.map(rowToTask);
|
|
1350
1356
|
}
|
|
1351
1357
|
|
|
1358
|
+
export function getCompletedMilestoneTaskFileHints(milestoneId: string): string[] {
|
|
1359
|
+
if (!currentDb) return [];
|
|
1360
|
+
const rows = currentDb.prepare(
|
|
1361
|
+
`SELECT files, key_files
|
|
1362
|
+
FROM tasks
|
|
1363
|
+
WHERE milestone_id = :mid AND status IN ('complete', 'done')`,
|
|
1364
|
+
).all({ ":mid": milestoneId }) as Array<Record<string, unknown>>;
|
|
1365
|
+
|
|
1366
|
+
const hints = new Set<string>();
|
|
1367
|
+
for (const row of rows) {
|
|
1368
|
+
for (const raw of [row["files"], row["key_files"]]) {
|
|
1369
|
+
for (const file of parseStringArrayColumn(raw)) {
|
|
1370
|
+
const normalized = normalizeRepoPath(file);
|
|
1371
|
+
if (normalized) hints.add(normalized);
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1375
|
+
return [...hints];
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1378
|
+
function parseStringArrayColumn(raw: unknown): string[] {
|
|
1379
|
+
if (Array.isArray(raw)) return raw.filter((entry): entry is string => typeof entry === "string");
|
|
1380
|
+
if (typeof raw !== "string") return [];
|
|
1381
|
+
const trimmed = raw.trim();
|
|
1382
|
+
if (!trimmed) return [];
|
|
1383
|
+
try {
|
|
1384
|
+
const parsed = JSON.parse(trimmed);
|
|
1385
|
+
if (Array.isArray(parsed)) return parsed.filter((entry): entry is string => typeof entry === "string");
|
|
1386
|
+
if (typeof parsed === "string") return [parsed];
|
|
1387
|
+
} catch {
|
|
1388
|
+
return trimmed.split(",");
|
|
1389
|
+
}
|
|
1390
|
+
return [];
|
|
1391
|
+
}
|
|
1392
|
+
|
|
1393
|
+
function normalizeRepoPath(file: string): string {
|
|
1394
|
+
return file.trim().replace(/\\/g, "/").replace(/^\.\/+/, "");
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1352
1397
|
// ─── ADR-011 Phase 2 escalation helpers ──────────────────────────────────
|
|
1353
1398
|
|
|
1354
1399
|
/** Set pause-on-escalation state on a completed task. Mutually exclusive with awaiting_review. */
|
|
@@ -2073,6 +2118,9 @@ export function deleteMilestone(milestoneId: string): void {
|
|
|
2073
2118
|
currentDb!.prepare(
|
|
2074
2119
|
`DELETE FROM artifacts WHERE milestone_id = :mid`,
|
|
2075
2120
|
).run({ ":mid": milestoneId });
|
|
2121
|
+
currentDb!.prepare(
|
|
2122
|
+
`DELETE FROM milestone_commit_attributions WHERE milestone_id = :mid`,
|
|
2123
|
+
).run({ ":mid": milestoneId });
|
|
2076
2124
|
currentDb!.prepare(
|
|
2077
2125
|
`DELETE FROM milestone_leases WHERE milestone_id = :mid`,
|
|
2078
2126
|
).run({ ":mid": milestoneId });
|
|
@@ -2391,6 +2439,75 @@ export function upsertTurnGitTransaction(entry: {
|
|
|
2391
2439
|
});
|
|
2392
2440
|
}
|
|
2393
2441
|
|
|
2442
|
+
export function getMilestoneCommitAttributionShas(milestoneId: string): string[] {
|
|
2443
|
+
if (!currentDb) return [];
|
|
2444
|
+
const rows = currentDb.prepare(
|
|
2445
|
+
`SELECT commit_sha
|
|
2446
|
+
FROM milestone_commit_attributions
|
|
2447
|
+
WHERE milestone_id = :mid
|
|
2448
|
+
ORDER BY created_at, commit_sha`,
|
|
2449
|
+
).all({ ":mid": milestoneId }) as Array<Record<string, unknown>>;
|
|
2450
|
+
return rows
|
|
2451
|
+
.map((row) => typeof row["commit_sha"] === "string" ? row["commit_sha"] : "")
|
|
2452
|
+
.filter(Boolean);
|
|
2453
|
+
}
|
|
2454
|
+
|
|
2455
|
+
export function recordMilestoneCommitAttribution(entry: {
|
|
2456
|
+
commitSha: string;
|
|
2457
|
+
milestoneId: string;
|
|
2458
|
+
sliceId?: string;
|
|
2459
|
+
taskId?: string;
|
|
2460
|
+
source: "recorded" | "backfill";
|
|
2461
|
+
confidence: number;
|
|
2462
|
+
files: string[];
|
|
2463
|
+
createdAt: string;
|
|
2464
|
+
}): void {
|
|
2465
|
+
if (!currentDb) return;
|
|
2466
|
+
transaction(() => {
|
|
2467
|
+
currentDb!.prepare(
|
|
2468
|
+
`INSERT OR REPLACE INTO milestone_commit_attributions (
|
|
2469
|
+
commit_sha, milestone_id, slice_id, task_id, source, confidence, files_json, created_at
|
|
2470
|
+
) VALUES (
|
|
2471
|
+
:commit_sha, :milestone_id, :slice_id, :task_id, :source, :confidence, :files_json, :created_at
|
|
2472
|
+
)`,
|
|
2473
|
+
).run({
|
|
2474
|
+
":commit_sha": entry.commitSha,
|
|
2475
|
+
":milestone_id": entry.milestoneId,
|
|
2476
|
+
":slice_id": entry.sliceId ?? null,
|
|
2477
|
+
":task_id": entry.taskId ?? null,
|
|
2478
|
+
":source": entry.source,
|
|
2479
|
+
":confidence": entry.confidence,
|
|
2480
|
+
":files_json": JSON.stringify(entry.files),
|
|
2481
|
+
":created_at": entry.createdAt,
|
|
2482
|
+
});
|
|
2483
|
+
|
|
2484
|
+
currentDb!.prepare(
|
|
2485
|
+
`INSERT OR IGNORE INTO audit_events (
|
|
2486
|
+
event_id, trace_id, turn_id, caused_by, category, type, ts, payload_json
|
|
2487
|
+
) VALUES (
|
|
2488
|
+
:event_id, :trace_id, :turn_id, :caused_by, :category, :type, :ts, :payload_json
|
|
2489
|
+
)`,
|
|
2490
|
+
).run({
|
|
2491
|
+
":event_id": `milestone-commit-attribution:${entry.milestoneId}:${entry.commitSha}`,
|
|
2492
|
+
":trace_id": "milestone-commit-attribution",
|
|
2493
|
+
":turn_id": null,
|
|
2494
|
+
":caused_by": null,
|
|
2495
|
+
":category": "git",
|
|
2496
|
+
":type": "milestone-commit-attribution-recorded",
|
|
2497
|
+
":ts": entry.createdAt,
|
|
2498
|
+
":payload_json": JSON.stringify({
|
|
2499
|
+
commitSha: entry.commitSha,
|
|
2500
|
+
milestoneId: entry.milestoneId,
|
|
2501
|
+
sliceId: entry.sliceId ?? null,
|
|
2502
|
+
taskId: entry.taskId ?? null,
|
|
2503
|
+
source: entry.source,
|
|
2504
|
+
confidence: entry.confidence,
|
|
2505
|
+
files: entry.files,
|
|
2506
|
+
}),
|
|
2507
|
+
});
|
|
2508
|
+
});
|
|
2509
|
+
}
|
|
2510
|
+
|
|
2394
2511
|
export function insertAuditEvent(entry: {
|
|
2395
2512
|
eventId: string;
|
|
2396
2513
|
traceId: string;
|
|
@@ -2494,6 +2611,7 @@ export function clearEngineHierarchy(): void {
|
|
|
2494
2611
|
currentDb!.exec("DELETE FROM slice_dependencies");
|
|
2495
2612
|
currentDb!.exec("DELETE FROM assessments");
|
|
2496
2613
|
currentDb!.exec("DELETE FROM replan_history");
|
|
2614
|
+
currentDb!.exec("DELETE FROM milestone_commit_attributions");
|
|
2497
2615
|
currentDb!.exec("DELETE FROM tasks");
|
|
2498
2616
|
currentDb!.exec("DELETE FROM slices");
|
|
2499
2617
|
currentDb!.exec("DELETE FROM milestone_leases");
|
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
formatInterruptedSessionRunningMessage,
|
|
31
31
|
formatInterruptedSessionSummary,
|
|
32
32
|
} from "./interrupted-session.js";
|
|
33
|
-
import { listUnitRuntimeRecords, clearUnitRuntimeRecord } from "./unit-runtime.js";
|
|
33
|
+
import { listUnitRuntimeRecords, clearUnitRuntimeRecord, isInFlightRuntimePhase } from "./unit-runtime.js";
|
|
34
34
|
import { resolveExpectedArtifactPath } from "./auto.js";
|
|
35
35
|
import { gsdHome } from "./gsd-home.js";
|
|
36
36
|
import {
|
|
@@ -118,6 +118,22 @@ export function resolveExpectedArtifactPathForScope(
|
|
|
118
118
|
return resolveExpectedArtifactPath(unitType, unitId, scope.workspace.projectRoot);
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
+
async function runQuickTaskChoice(ctx: ExtensionCommandContext, pi: ExtensionAPI): Promise<void> {
|
|
122
|
+
if (!ctx.hasUI) {
|
|
123
|
+
ctx.ui.notify("Run /gsd quick <task> for small bounded work, or /gsd do <task> for natural-language routing.", "info");
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const task = (await ctx.ui.input("Quick task", "Describe the small task to run with /gsd quick"))?.trim();
|
|
128
|
+
if (!task) {
|
|
129
|
+
ctx.ui.notify("Quick task cancelled.", "info");
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const { handleQuick } = await import("./quick.js");
|
|
134
|
+
await handleQuick(task, ctx, pi);
|
|
135
|
+
}
|
|
136
|
+
|
|
121
137
|
/**
|
|
122
138
|
* Scope-based overload of isGhostMilestone.
|
|
123
139
|
* Binds basePath and milestoneId from the scope, ensuring path resolution
|
|
@@ -1786,8 +1802,8 @@ function selfHealRuntimeRecords(basePath: string, ctx: ExtensionContext): { clea
|
|
|
1786
1802
|
cleared++;
|
|
1787
1803
|
continue;
|
|
1788
1804
|
}
|
|
1789
|
-
// Clear records stuck in
|
|
1790
|
-
if (phase
|
|
1805
|
+
// Clear records stuck in an in-flight phase (process died mid-unit).
|
|
1806
|
+
if (isInFlightRuntimePhase(phase)) {
|
|
1791
1807
|
clearUnitRuntimeRecord(basePath, unitType, unitId);
|
|
1792
1808
|
cleared++;
|
|
1793
1809
|
}
|
|
@@ -2042,6 +2058,23 @@ export async function showSmartEntry(
|
|
|
2042
2058
|
}
|
|
2043
2059
|
}
|
|
2044
2060
|
|
|
2061
|
+
if (interrupted.classification !== "recoverable") {
|
|
2062
|
+
try {
|
|
2063
|
+
const { autoImportMarkdownHierarchyIfDbMismatch } = await import("./migration-auto-check.js");
|
|
2064
|
+
const result = await autoImportMarkdownHierarchyIfDbMismatch(basePath);
|
|
2065
|
+
if (result.action === "imported") {
|
|
2066
|
+
ctx.ui.notify(
|
|
2067
|
+
`Recovered migrated planning state into gsd.db (${result.reason}): ${result.afterDb.milestones} milestone(s), ${result.afterDb.slices} slice(s), ${result.afterDb.tasks} task(s).`,
|
|
2068
|
+
"info",
|
|
2069
|
+
);
|
|
2070
|
+
}
|
|
2071
|
+
} catch (err) {
|
|
2072
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2073
|
+
ctx.ui.notify(`GSD could not auto-import existing planning state into gsd.db: ${message}`, "warning");
|
|
2074
|
+
logWarning("guided", `planning state auto-import failed: ${message}`, { file: "guided-flow.ts" });
|
|
2075
|
+
}
|
|
2076
|
+
}
|
|
2077
|
+
|
|
2045
2078
|
// Always derive from the project root — the assessment may have derived
|
|
2046
2079
|
// state from a worktree path that was cleaned up in the stale branch above.
|
|
2047
2080
|
const state = await deriveState(basePath);
|
|
@@ -2064,8 +2097,8 @@ export async function showSmartEntry(
|
|
|
2064
2097
|
// standard wizard below.
|
|
2065
2098
|
{
|
|
2066
2099
|
const prefs = loadEffectiveGSDPreferences(basePath)?.preferences;
|
|
2067
|
-
const {
|
|
2068
|
-
if (
|
|
2100
|
+
const { shouldRunDeepProjectSetup } = await import("./auto-dispatch.js");
|
|
2101
|
+
if (shouldRunDeepProjectSetup(state, prefs, basePath)) {
|
|
2069
2102
|
await startDeepProjectSetupForeground(ctx, pi, basePath, stepMode);
|
|
2070
2103
|
return;
|
|
2071
2104
|
}
|
|
@@ -2134,17 +2167,24 @@ export async function showSmartEntry(
|
|
|
2134
2167
|
title: "GSD — Get Shit Done",
|
|
2135
2168
|
summary: ["No active milestone."],
|
|
2136
2169
|
actions: [
|
|
2170
|
+
{
|
|
2171
|
+
id: "quick_task",
|
|
2172
|
+
label: "Quick task",
|
|
2173
|
+
description: "For small bounded work, run /gsd quick <task> or /gsd do <task>.",
|
|
2174
|
+
recommended: true,
|
|
2175
|
+
},
|
|
2137
2176
|
{
|
|
2138
2177
|
id: "new_milestone",
|
|
2139
2178
|
label: "Create next milestone",
|
|
2140
|
-
description: "Define
|
|
2141
|
-
recommended: true,
|
|
2179
|
+
description: "Define a larger body of work with planning artifacts.",
|
|
2142
2180
|
},
|
|
2143
2181
|
],
|
|
2144
2182
|
notYetMessage: "Run /gsd when ready.",
|
|
2145
2183
|
});
|
|
2146
2184
|
|
|
2147
|
-
if (choice === "
|
|
2185
|
+
if (choice === "quick_task") {
|
|
2186
|
+
await runQuickTaskChoice(ctx, pi);
|
|
2187
|
+
} else if (choice === "new_milestone") {
|
|
2148
2188
|
setPendingAutoStart(basePath, { ctx, pi, basePath, milestoneId: nextId, step: stepMode });
|
|
2149
2189
|
await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, pi, nextId,
|
|
2150
2190
|
`New milestone ${nextId}.`,
|
|
@@ -2181,11 +2221,16 @@ export async function showSmartEntry(
|
|
|
2181
2221
|
title: `GSD — ${milestoneId}: ${milestoneTitle}`,
|
|
2182
2222
|
summary: ["All milestones complete."],
|
|
2183
2223
|
actions: [
|
|
2224
|
+
{
|
|
2225
|
+
id: "quick_task",
|
|
2226
|
+
label: "Quick task",
|
|
2227
|
+
description: "Do a small bounded task without opening a milestone.",
|
|
2228
|
+
recommended: true,
|
|
2229
|
+
},
|
|
2184
2230
|
{
|
|
2185
2231
|
id: "new_milestone",
|
|
2186
2232
|
label: "Start new milestone",
|
|
2187
2233
|
description: "Define and plan the next milestone.",
|
|
2188
|
-
recommended: true,
|
|
2189
2234
|
},
|
|
2190
2235
|
{
|
|
2191
2236
|
id: "status",
|
|
@@ -2196,7 +2241,9 @@ export async function showSmartEntry(
|
|
|
2196
2241
|
notYetMessage: "Run /gsd when ready.",
|
|
2197
2242
|
});
|
|
2198
2243
|
|
|
2199
|
-
if (choice === "
|
|
2244
|
+
if (choice === "quick_task") {
|
|
2245
|
+
await runQuickTaskChoice(ctx, pi);
|
|
2246
|
+
} else if (choice === "new_milestone") {
|
|
2200
2247
|
const milestoneIds = findMilestoneIds(basePath);
|
|
2201
2248
|
const uniqueMilestoneIds = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
|
|
2202
2249
|
const nextId = nextMilestoneIdReserved(milestoneIds, uniqueMilestoneIds, basePath);
|
|
@@ -2300,13 +2347,18 @@ export async function showSmartEntry(
|
|
|
2300
2347
|
const hasContext = !!(contextFile && await loadFile(contextFile));
|
|
2301
2348
|
|
|
2302
2349
|
const actions = [
|
|
2350
|
+
{
|
|
2351
|
+
id: "quick_task",
|
|
2352
|
+
label: "Quick task instead",
|
|
2353
|
+
description: "Use this when the work is small and should not become a milestone.",
|
|
2354
|
+
recommended: true,
|
|
2355
|
+
},
|
|
2303
2356
|
{
|
|
2304
2357
|
id: "plan",
|
|
2305
2358
|
label: "Create roadmap",
|
|
2306
2359
|
description: hasContext
|
|
2307
2360
|
? "Context captured. Decompose into slices with a boundary map."
|
|
2308
2361
|
: "Decompose the milestone into slices with a boundary map.",
|
|
2309
|
-
recommended: true,
|
|
2310
2362
|
},
|
|
2311
2363
|
...(!hasContext ? [{
|
|
2312
2364
|
id: "discuss",
|
|
@@ -2332,7 +2384,9 @@ export async function showSmartEntry(
|
|
|
2332
2384
|
notYetMessage: "Run /gsd when ready.",
|
|
2333
2385
|
});
|
|
2334
2386
|
|
|
2335
|
-
if (choice === "
|
|
2387
|
+
if (choice === "quick_task") {
|
|
2388
|
+
await runQuickTaskChoice(ctx, pi);
|
|
2389
|
+
} else if (choice === "plan") {
|
|
2336
2390
|
setPendingAutoStart(basePath, { ctx, pi, basePath, milestoneId, step: stepMode });
|
|
2337
2391
|
await dispatchWorkflow(
|
|
2338
2392
|
pi,
|
|
@@ -25,6 +25,50 @@ import {
|
|
|
25
25
|
|
|
26
26
|
import type { MigrationPreview } from "./writer.js";
|
|
27
27
|
import { homedir } from "node:os";
|
|
28
|
+
import { ensureDbOpen } from "../bootstrap/dynamic-tools.js";
|
|
29
|
+
import { clearEngineHierarchy, transaction } from "../gsd-db.js";
|
|
30
|
+
import { migrateFromMarkdown } from "../md-importer.js";
|
|
31
|
+
import { invalidateStateCache } from "../state.js";
|
|
32
|
+
|
|
33
|
+
export type MigrationImportCounts = ReturnType<typeof migrateFromMarkdown>;
|
|
34
|
+
|
|
35
|
+
function assertMigrationImportMatchesPreview(imported: MigrationImportCounts, preview: MigrationPreview): void {
|
|
36
|
+
const mismatches: string[] = [];
|
|
37
|
+
if (imported.hierarchy.milestones !== preview.milestoneCount) {
|
|
38
|
+
mismatches.push(`milestones ${imported.hierarchy.milestones}/${preview.milestoneCount}`);
|
|
39
|
+
}
|
|
40
|
+
if (imported.hierarchy.slices !== preview.totalSlices) {
|
|
41
|
+
mismatches.push(`slices ${imported.hierarchy.slices}/${preview.totalSlices}`);
|
|
42
|
+
}
|
|
43
|
+
if (imported.hierarchy.tasks !== preview.totalTasks) {
|
|
44
|
+
mismatches.push(`tasks ${imported.hierarchy.tasks}/${preview.totalTasks}`);
|
|
45
|
+
}
|
|
46
|
+
if (imported.requirements !== preview.requirements.total) {
|
|
47
|
+
mismatches.push(`requirements ${imported.requirements}/${preview.requirements.total}`);
|
|
48
|
+
}
|
|
49
|
+
if (mismatches.length > 0) {
|
|
50
|
+
throw new Error(`migration DB import verification failed: ${mismatches.join(", ")}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export async function importWrittenMigrationToDb(
|
|
55
|
+
basePath: string,
|
|
56
|
+
preview?: MigrationPreview,
|
|
57
|
+
): Promise<MigrationImportCounts> {
|
|
58
|
+
const opened = await ensureDbOpen(basePath);
|
|
59
|
+
if (!opened) {
|
|
60
|
+
throw new Error(`failed to open or create the GSD database at ${basePath}`);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const counts = transaction(() => {
|
|
64
|
+
clearEngineHierarchy();
|
|
65
|
+
const imported = migrateFromMarkdown(basePath);
|
|
66
|
+
if (preview) assertMigrationImportMatchesPreview(imported, preview);
|
|
67
|
+
return imported;
|
|
68
|
+
});
|
|
69
|
+
invalidateStateCache();
|
|
70
|
+
return counts;
|
|
71
|
+
}
|
|
28
72
|
|
|
29
73
|
/** Format preview stats for embedding in the review prompt. */
|
|
30
74
|
function formatPreviewStats(preview: MigrationPreview): string {
|
|
@@ -182,9 +226,10 @@ export async function handleMigrate(
|
|
|
182
226
|
|
|
183
227
|
const result = await writeGSDDirectory(project, process.cwd());
|
|
184
228
|
const gsdPath = gsdRoot(process.cwd());
|
|
229
|
+
const imported = await importWrittenMigrationToDb(process.cwd(), preview);
|
|
185
230
|
|
|
186
231
|
ctx.ui.notify(
|
|
187
|
-
`✓ Migration complete — ${result.paths.length} file(s) written to .gsd
|
|
232
|
+
`✓ Migration complete — ${result.paths.length} file(s) written to .gsd/ and ${imported.hierarchy.milestones}M/${imported.hierarchy.slices}S/${imported.hierarchy.tasks}T imported to the database`,
|
|
188
233
|
"info",
|
|
189
234
|
);
|
|
190
235
|
|
|
@@ -193,6 +238,7 @@ export async function handleMigrate(
|
|
|
193
238
|
title: "Migration written",
|
|
194
239
|
summary: [
|
|
195
240
|
`${result.paths.length} files written to .gsd/`,
|
|
241
|
+
`${imported.hierarchy.milestones} milestone(s), ${imported.hierarchy.slices} slice(s), and ${imported.hierarchy.tasks} task(s) imported to gsd.db`,
|
|
196
242
|
"",
|
|
197
243
|
"The agent can now review the migrated output against GSD-2 standards —",
|
|
198
244
|
"checking structure, content quality, deriveState() round-trip, and",
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
2
|
+
|
|
3
|
+
import { ensureDbOpen } from "./bootstrap/dynamic-tools.js";
|
|
4
|
+
import {
|
|
5
|
+
clearEngineHierarchy,
|
|
6
|
+
getAllMilestones,
|
|
7
|
+
getMilestoneSlices,
|
|
8
|
+
getSliceTasks,
|
|
9
|
+
isDbAvailable,
|
|
10
|
+
transaction,
|
|
11
|
+
} from "./gsd-db.js";
|
|
12
|
+
import { migrateHierarchyToDb } from "./md-importer.js";
|
|
13
|
+
import { parsePlan, parseRoadmap } from "./parsers-legacy.js";
|
|
14
|
+
import {
|
|
15
|
+
milestonesDir,
|
|
16
|
+
resolveMilestoneFile,
|
|
17
|
+
resolveSliceFile,
|
|
18
|
+
} from "./paths.js";
|
|
19
|
+
import { invalidateStateCache } from "./state.js";
|
|
20
|
+
|
|
21
|
+
export interface HierarchyCounts {
|
|
22
|
+
milestones: number;
|
|
23
|
+
slices: number;
|
|
24
|
+
tasks: number;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface MigrationAutoCheckResult {
|
|
28
|
+
action: "none" | "imported";
|
|
29
|
+
reason: "no-markdown" | "in-sync" | "db-empty" | "count-mismatch";
|
|
30
|
+
markdown: HierarchyCounts;
|
|
31
|
+
beforeDb: HierarchyCounts;
|
|
32
|
+
afterDb: HierarchyCounts;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function zeroCounts(): HierarchyCounts {
|
|
36
|
+
return { milestones: 0, slices: 0, tasks: 0 };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function sameCounts(a: HierarchyCounts, b: HierarchyCounts): boolean {
|
|
40
|
+
return a.milestones === b.milestones && a.slices === b.slices && a.tasks === b.tasks;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function countMarkdownHierarchy(basePath: string): HierarchyCounts {
|
|
44
|
+
const root = milestonesDir(basePath);
|
|
45
|
+
if (!existsSync(root)) return zeroCounts();
|
|
46
|
+
|
|
47
|
+
const counts = zeroCounts();
|
|
48
|
+
for (const entry of readdirSync(root, { withFileTypes: true })) {
|
|
49
|
+
if (!entry.isDirectory() || !/^M\d+/.test(entry.name)) continue;
|
|
50
|
+
counts.milestones++;
|
|
51
|
+
|
|
52
|
+
const roadmapPath = resolveMilestoneFile(basePath, entry.name, "ROADMAP");
|
|
53
|
+
if (!roadmapPath || !existsSync(roadmapPath)) continue;
|
|
54
|
+
|
|
55
|
+
const roadmap = parseRoadmap(readFileSync(roadmapPath, "utf-8"));
|
|
56
|
+
counts.slices += roadmap.slices.length;
|
|
57
|
+
|
|
58
|
+
for (const slice of roadmap.slices) {
|
|
59
|
+
const planPath = resolveSliceFile(basePath, entry.name, slice.id, "PLAN");
|
|
60
|
+
if (!planPath || !existsSync(planPath)) continue;
|
|
61
|
+
const plan = parsePlan(readFileSync(planPath, "utf-8"));
|
|
62
|
+
counts.tasks += plan.tasks.length;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return counts;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function countDbHierarchy(): HierarchyCounts {
|
|
70
|
+
if (!isDbAvailable()) return zeroCounts();
|
|
71
|
+
const counts = zeroCounts();
|
|
72
|
+
const milestones = getAllMilestones();
|
|
73
|
+
counts.milestones = milestones.length;
|
|
74
|
+
|
|
75
|
+
for (const milestone of milestones) {
|
|
76
|
+
const slices = getMilestoneSlices(milestone.id);
|
|
77
|
+
counts.slices += slices.length;
|
|
78
|
+
for (const slice of slices) {
|
|
79
|
+
counts.tasks += getSliceTasks(milestone.id, slice.id).length;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return counts;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export async function autoImportMarkdownHierarchyIfDbMismatch(
|
|
87
|
+
basePath: string,
|
|
88
|
+
): Promise<MigrationAutoCheckResult> {
|
|
89
|
+
const markdown = countMarkdownHierarchy(basePath);
|
|
90
|
+
if (sameCounts(markdown, zeroCounts())) {
|
|
91
|
+
return {
|
|
92
|
+
action: "none",
|
|
93
|
+
reason: "no-markdown",
|
|
94
|
+
markdown,
|
|
95
|
+
beforeDb: zeroCounts(),
|
|
96
|
+
afterDb: zeroCounts(),
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const opened = await ensureDbOpen(basePath);
|
|
101
|
+
if (!opened || !isDbAvailable()) {
|
|
102
|
+
throw new Error(`failed to open or create the GSD database at ${basePath}`);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const beforeDb = countDbHierarchy();
|
|
106
|
+
if (sameCounts(markdown, beforeDb)) {
|
|
107
|
+
return { action: "none", reason: "in-sync", markdown, beforeDb, afterDb: beforeDb };
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const reason = sameCounts(beforeDb, zeroCounts()) ? "db-empty" : "count-mismatch";
|
|
111
|
+
const imported = transaction(() => {
|
|
112
|
+
clearEngineHierarchy();
|
|
113
|
+
return migrateHierarchyToDb(basePath);
|
|
114
|
+
});
|
|
115
|
+
invalidateStateCache();
|
|
116
|
+
|
|
117
|
+
const afterDb = {
|
|
118
|
+
milestones: imported.milestones,
|
|
119
|
+
slices: imported.slices,
|
|
120
|
+
tasks: imported.tasks,
|
|
121
|
+
};
|
|
122
|
+
if (!sameCounts(markdown, afterDb)) {
|
|
123
|
+
throw new Error(
|
|
124
|
+
`migration auto-import verification failed: markdown ${markdown.milestones}M/${markdown.slices}S/${markdown.tasks}T, db ${afterDb.milestones}M/${afterDb.slices}S/${afterDb.tasks}T`,
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return { action: "imported", reason, markdown, beforeDb, afterDb };
|
|
129
|
+
}
|