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
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* Used by init-wizard.ts and guided-flow.ts to determine what onboarding
|
|
6
6
|
* flow to show when entering a project directory.
|
|
7
7
|
*/
|
|
8
|
+
import { execFileSync } from "node:child_process";
|
|
8
9
|
import { existsSync, openSync, readSync, closeSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
9
10
|
import { dirname, join, parse as parsePath } from "node:path";
|
|
10
11
|
import { homedir } from "node:os";
|
|
@@ -171,6 +172,7 @@ const TEST_MARKERS = [
|
|
|
171
172
|
const RECURSIVE_SCAN_IGNORED_DIRS = new Set([
|
|
172
173
|
".git",
|
|
173
174
|
".gsd",
|
|
175
|
+
".bg-shell",
|
|
174
176
|
".planning",
|
|
175
177
|
".plans",
|
|
176
178
|
".claude",
|
|
@@ -194,6 +196,7 @@ const RECURSIVE_SCAN_IGNORED_DIRS = new Set([
|
|
|
194
196
|
"DerivedData",
|
|
195
197
|
"out",
|
|
196
198
|
]);
|
|
199
|
+
const PROJECT_CONTENT_EXCLUDE_DIRS = RECURSIVE_SCAN_IGNORED_DIRS;
|
|
197
200
|
/** Project file markers safe to detect recursively via suffix matching. */
|
|
198
201
|
const ROOT_ONLY_PROJECT_FILES = new Set([
|
|
199
202
|
".github/workflows",
|
|
@@ -429,6 +432,109 @@ export function detectProjectSignals(basePath) {
|
|
|
429
432
|
verificationCommands,
|
|
430
433
|
};
|
|
431
434
|
}
|
|
435
|
+
function normalizeGitPath(file) {
|
|
436
|
+
return file.replaceAll("\\", "/").replace(/^\.\//, "");
|
|
437
|
+
}
|
|
438
|
+
function isProjectContentFile(file) {
|
|
439
|
+
const normalized = normalizeGitPath(file);
|
|
440
|
+
if (!normalized || normalized.endsWith("/"))
|
|
441
|
+
return false;
|
|
442
|
+
if (normalized === ".gitignore" || normalized === ".gitattributes")
|
|
443
|
+
return false;
|
|
444
|
+
const parts = normalized.split("/");
|
|
445
|
+
if (parts.some((part) => PROJECT_CONTENT_EXCLUDE_DIRS.has(part)))
|
|
446
|
+
return false;
|
|
447
|
+
if (normalized.endsWith(".DS_Store"))
|
|
448
|
+
return false;
|
|
449
|
+
return true;
|
|
450
|
+
}
|
|
451
|
+
function runGitLines(basePath, args) {
|
|
452
|
+
try {
|
|
453
|
+
const output = execFileSync("git", args, {
|
|
454
|
+
cwd: basePath,
|
|
455
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
456
|
+
encoding: "utf-8",
|
|
457
|
+
}).trim();
|
|
458
|
+
return output ? output.split("\n").map((line) => line.trim()).filter(Boolean) : [];
|
|
459
|
+
}
|
|
460
|
+
catch {
|
|
461
|
+
return [];
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
function listTrackedProjectFiles(basePath) {
|
|
465
|
+
return runGitLines(basePath, ["ls-files"])
|
|
466
|
+
.map(normalizeGitPath)
|
|
467
|
+
.filter(isProjectContentFile);
|
|
468
|
+
}
|
|
469
|
+
function listUntrackedProjectFiles(basePath) {
|
|
470
|
+
return runGitLines(basePath, ["ls-files", "--others", "--exclude-standard"])
|
|
471
|
+
.map(normalizeGitPath)
|
|
472
|
+
.filter(isProjectContentFile);
|
|
473
|
+
}
|
|
474
|
+
function hasKnownProjectMarkers(basePath, signals) {
|
|
475
|
+
if (signals.detectedFiles.length > 0)
|
|
476
|
+
return true;
|
|
477
|
+
if (signals.xcodePlatforms.length > 0)
|
|
478
|
+
return true;
|
|
479
|
+
return false;
|
|
480
|
+
}
|
|
481
|
+
/**
|
|
482
|
+
* Classify repo presence separately from ecosystem/tooling markers.
|
|
483
|
+
*
|
|
484
|
+
* Known project files identify tooling. Git-tracked/non-ignored content
|
|
485
|
+
* identifies whether this is an existing project at all. This keeps small
|
|
486
|
+
* static or documentation repos from being mislabeled as greenfield.
|
|
487
|
+
*/
|
|
488
|
+
export function classifyProject(basePath) {
|
|
489
|
+
const signals = detectProjectSignals(basePath);
|
|
490
|
+
const markers = [...signals.detectedFiles];
|
|
491
|
+
if (!signals.isGitRepo) {
|
|
492
|
+
return {
|
|
493
|
+
kind: "invalid-repo",
|
|
494
|
+
signals,
|
|
495
|
+
trackedFiles: [],
|
|
496
|
+
untrackedFiles: [],
|
|
497
|
+
contentFiles: [],
|
|
498
|
+
markers,
|
|
499
|
+
reason: "missing .git",
|
|
500
|
+
};
|
|
501
|
+
}
|
|
502
|
+
const trackedFiles = listTrackedProjectFiles(basePath);
|
|
503
|
+
const untrackedFiles = listUntrackedProjectFiles(basePath);
|
|
504
|
+
const contentFiles = [...new Set([...trackedFiles, ...untrackedFiles])];
|
|
505
|
+
const hasMarkers = hasKnownProjectMarkers(basePath, signals);
|
|
506
|
+
if (hasMarkers) {
|
|
507
|
+
return {
|
|
508
|
+
kind: "typed-existing",
|
|
509
|
+
signals,
|
|
510
|
+
trackedFiles,
|
|
511
|
+
untrackedFiles,
|
|
512
|
+
contentFiles,
|
|
513
|
+
markers,
|
|
514
|
+
reason: markers.length > 0 ? `detected markers: ${markers.join(", ")}` : "detected project structure",
|
|
515
|
+
};
|
|
516
|
+
}
|
|
517
|
+
if (contentFiles.length > 0) {
|
|
518
|
+
return {
|
|
519
|
+
kind: "untyped-existing",
|
|
520
|
+
signals,
|
|
521
|
+
trackedFiles,
|
|
522
|
+
untrackedFiles,
|
|
523
|
+
contentFiles,
|
|
524
|
+
markers,
|
|
525
|
+
reason: "project content exists but no recognized tooling markers were found",
|
|
526
|
+
};
|
|
527
|
+
}
|
|
528
|
+
return {
|
|
529
|
+
kind: "greenfield",
|
|
530
|
+
signals,
|
|
531
|
+
trackedFiles,
|
|
532
|
+
untrackedFiles,
|
|
533
|
+
contentFiles,
|
|
534
|
+
markers,
|
|
535
|
+
reason: "no tracked or non-ignored project content",
|
|
536
|
+
};
|
|
537
|
+
}
|
|
432
538
|
// ─── Xcode Platform Detection ───────────────────────────────────────────────────
|
|
433
539
|
/** Known SDKROOT values → canonical platform names. */
|
|
434
540
|
const SDKROOT_MAP = {
|
|
@@ -36,7 +36,7 @@ import { rowToActiveDecision, rowToActiveRequirement, rowToDecision, rowToRequir
|
|
|
36
36
|
import { rowToGate } from "./db-gate-rows.js";
|
|
37
37
|
import { rowToArtifact, rowToMilestone } from "./db-milestone-artifact-rows.js";
|
|
38
38
|
import { backupDatabaseBeforeMigration } from "./db-migration-backup.js";
|
|
39
|
-
import { applyMigrationV2Artifacts, applyMigrationV3Memories, applyMigrationV4DecisionMadeBy, applyMigrationV5HierarchyTables, applyMigrationV6SliceSummaries, applyMigrationV7Dependencies, applyMigrationV8PlanningFields, applyMigrationV9Ordering, applyMigrationV10ReplanTrigger, applyMigrationV11TaskPlanning, applyMigrationV12QualityGates, applyMigrationV13HotPathIndexes, applyMigrationV14SliceDependencies, applyMigrationV15AuditTables, applyMigrationV16EscalationSource, applyMigrationV17TaskEscalation, applyMigrationV18MemorySources, applyMigrationV19MemoryFts, applyMigrationV20MemoryRelations, applyMigrationV21StructuredMemories, applyMigrationV22QualityGateRepair, applyMigrationV23MilestoneQueue, } from "./db-migration-steps.js";
|
|
39
|
+
import { applyMigrationV2Artifacts, applyMigrationV3Memories, applyMigrationV4DecisionMadeBy, applyMigrationV5HierarchyTables, applyMigrationV6SliceSummaries, applyMigrationV7Dependencies, applyMigrationV8PlanningFields, applyMigrationV9Ordering, applyMigrationV10ReplanTrigger, applyMigrationV11TaskPlanning, applyMigrationV12QualityGates, applyMigrationV13HotPathIndexes, applyMigrationV14SliceDependencies, applyMigrationV15AuditTables, applyMigrationV16EscalationSource, applyMigrationV17TaskEscalation, applyMigrationV18MemorySources, applyMigrationV19MemoryFts, applyMigrationV20MemoryRelations, applyMigrationV21StructuredMemories, applyMigrationV22QualityGateRepair, applyMigrationV23MilestoneQueue, applyMigrationV26MilestoneCommitAttributions, } from "./db-migration-steps.js";
|
|
40
40
|
import { isMemoriesFtsAvailableSchema, tryCreateMemoriesFtsSchema } from "./db-memory-fts-schema.js";
|
|
41
41
|
import { createDbOpenState } from "./db-open-state.js";
|
|
42
42
|
import { createRuntimeKvTableV25 } from "./db-runtime-kv-schema.js";
|
|
@@ -52,7 +52,7 @@ const providerLoader = createSqliteProviderLoader({
|
|
|
52
52
|
nodeVersion: process.versions.node,
|
|
53
53
|
writeStderr: (message) => process.stderr.write(message),
|
|
54
54
|
});
|
|
55
|
-
export const SCHEMA_VERSION =
|
|
55
|
+
export const SCHEMA_VERSION = 26;
|
|
56
56
|
function initSchema(db, fileBacked) {
|
|
57
57
|
if (fileBacked)
|
|
58
58
|
db.exec("PRAGMA journal_mode=WAL");
|
|
@@ -246,6 +246,10 @@ function migrateSchema(db) {
|
|
|
246
246
|
createRuntimeKvTableV25(db);
|
|
247
247
|
recordSchemaVersion(db, 25);
|
|
248
248
|
}
|
|
249
|
+
if (currentVersion < 26) {
|
|
250
|
+
applyMigrationV26MilestoneCommitAttributions(db);
|
|
251
|
+
recordSchemaVersion(db, 26);
|
|
252
|
+
}
|
|
249
253
|
db.exec("COMMIT");
|
|
250
254
|
}
|
|
251
255
|
catch (err) {
|
|
@@ -1158,6 +1162,47 @@ export function getSliceTasks(milestoneId, sliceId) {
|
|
|
1158
1162
|
const rows = currentDb.prepare("SELECT * FROM tasks WHERE milestone_id = :mid AND slice_id = :sid ORDER BY sequence, id").all({ ":mid": milestoneId, ":sid": sliceId });
|
|
1159
1163
|
return rows.map(rowToTask);
|
|
1160
1164
|
}
|
|
1165
|
+
export function getCompletedMilestoneTaskFileHints(milestoneId) {
|
|
1166
|
+
if (!currentDb)
|
|
1167
|
+
return [];
|
|
1168
|
+
const rows = currentDb.prepare(`SELECT files, key_files
|
|
1169
|
+
FROM tasks
|
|
1170
|
+
WHERE milestone_id = :mid AND status IN ('complete', 'done')`).all({ ":mid": milestoneId });
|
|
1171
|
+
const hints = new Set();
|
|
1172
|
+
for (const row of rows) {
|
|
1173
|
+
for (const raw of [row["files"], row["key_files"]]) {
|
|
1174
|
+
for (const file of parseStringArrayColumn(raw)) {
|
|
1175
|
+
const normalized = normalizeRepoPath(file);
|
|
1176
|
+
if (normalized)
|
|
1177
|
+
hints.add(normalized);
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
return [...hints];
|
|
1182
|
+
}
|
|
1183
|
+
function parseStringArrayColumn(raw) {
|
|
1184
|
+
if (Array.isArray(raw))
|
|
1185
|
+
return raw.filter((entry) => typeof entry === "string");
|
|
1186
|
+
if (typeof raw !== "string")
|
|
1187
|
+
return [];
|
|
1188
|
+
const trimmed = raw.trim();
|
|
1189
|
+
if (!trimmed)
|
|
1190
|
+
return [];
|
|
1191
|
+
try {
|
|
1192
|
+
const parsed = JSON.parse(trimmed);
|
|
1193
|
+
if (Array.isArray(parsed))
|
|
1194
|
+
return parsed.filter((entry) => typeof entry === "string");
|
|
1195
|
+
if (typeof parsed === "string")
|
|
1196
|
+
return [parsed];
|
|
1197
|
+
}
|
|
1198
|
+
catch {
|
|
1199
|
+
return trimmed.split(",");
|
|
1200
|
+
}
|
|
1201
|
+
return [];
|
|
1202
|
+
}
|
|
1203
|
+
function normalizeRepoPath(file) {
|
|
1204
|
+
return file.trim().replace(/\\/g, "/").replace(/^\.\/+/, "");
|
|
1205
|
+
}
|
|
1161
1206
|
// ─── ADR-011 Phase 2 escalation helpers ──────────────────────────────────
|
|
1162
1207
|
/** Set pause-on-escalation state on a completed task. Mutually exclusive with awaiting_review. */
|
|
1163
1208
|
export function setTaskEscalationPending(milestoneId, sliceId, taskId, artifactPath) {
|
|
@@ -1735,6 +1780,7 @@ export function deleteMilestone(milestoneId) {
|
|
|
1735
1780
|
currentDb.prepare(`DELETE FROM replan_history WHERE milestone_id = :mid`).run({ ":mid": milestoneId });
|
|
1736
1781
|
currentDb.prepare(`DELETE FROM assessments WHERE milestone_id = :mid`).run({ ":mid": milestoneId });
|
|
1737
1782
|
currentDb.prepare(`DELETE FROM artifacts WHERE milestone_id = :mid`).run({ ":mid": milestoneId });
|
|
1783
|
+
currentDb.prepare(`DELETE FROM milestone_commit_attributions WHERE milestone_id = :mid`).run({ ":mid": milestoneId });
|
|
1738
1784
|
currentDb.prepare(`DELETE FROM milestone_leases WHERE milestone_id = :mid`).run({ ":mid": milestoneId });
|
|
1739
1785
|
currentDb.prepare(`DELETE FROM milestones WHERE id = :mid`).run({ ":mid": milestoneId });
|
|
1740
1786
|
});
|
|
@@ -1962,6 +2008,59 @@ export function upsertTurnGitTransaction(entry) {
|
|
|
1962
2008
|
":updated_at": entry.updatedAt,
|
|
1963
2009
|
});
|
|
1964
2010
|
}
|
|
2011
|
+
export function getMilestoneCommitAttributionShas(milestoneId) {
|
|
2012
|
+
if (!currentDb)
|
|
2013
|
+
return [];
|
|
2014
|
+
const rows = currentDb.prepare(`SELECT commit_sha
|
|
2015
|
+
FROM milestone_commit_attributions
|
|
2016
|
+
WHERE milestone_id = :mid
|
|
2017
|
+
ORDER BY created_at, commit_sha`).all({ ":mid": milestoneId });
|
|
2018
|
+
return rows
|
|
2019
|
+
.map((row) => typeof row["commit_sha"] === "string" ? row["commit_sha"] : "")
|
|
2020
|
+
.filter(Boolean);
|
|
2021
|
+
}
|
|
2022
|
+
export function recordMilestoneCommitAttribution(entry) {
|
|
2023
|
+
if (!currentDb)
|
|
2024
|
+
return;
|
|
2025
|
+
transaction(() => {
|
|
2026
|
+
currentDb.prepare(`INSERT OR REPLACE INTO milestone_commit_attributions (
|
|
2027
|
+
commit_sha, milestone_id, slice_id, task_id, source, confidence, files_json, created_at
|
|
2028
|
+
) VALUES (
|
|
2029
|
+
:commit_sha, :milestone_id, :slice_id, :task_id, :source, :confidence, :files_json, :created_at
|
|
2030
|
+
)`).run({
|
|
2031
|
+
":commit_sha": entry.commitSha,
|
|
2032
|
+
":milestone_id": entry.milestoneId,
|
|
2033
|
+
":slice_id": entry.sliceId ?? null,
|
|
2034
|
+
":task_id": entry.taskId ?? null,
|
|
2035
|
+
":source": entry.source,
|
|
2036
|
+
":confidence": entry.confidence,
|
|
2037
|
+
":files_json": JSON.stringify(entry.files),
|
|
2038
|
+
":created_at": entry.createdAt,
|
|
2039
|
+
});
|
|
2040
|
+
currentDb.prepare(`INSERT OR IGNORE INTO audit_events (
|
|
2041
|
+
event_id, trace_id, turn_id, caused_by, category, type, ts, payload_json
|
|
2042
|
+
) VALUES (
|
|
2043
|
+
:event_id, :trace_id, :turn_id, :caused_by, :category, :type, :ts, :payload_json
|
|
2044
|
+
)`).run({
|
|
2045
|
+
":event_id": `milestone-commit-attribution:${entry.milestoneId}:${entry.commitSha}`,
|
|
2046
|
+
":trace_id": "milestone-commit-attribution",
|
|
2047
|
+
":turn_id": null,
|
|
2048
|
+
":caused_by": null,
|
|
2049
|
+
":category": "git",
|
|
2050
|
+
":type": "milestone-commit-attribution-recorded",
|
|
2051
|
+
":ts": entry.createdAt,
|
|
2052
|
+
":payload_json": JSON.stringify({
|
|
2053
|
+
commitSha: entry.commitSha,
|
|
2054
|
+
milestoneId: entry.milestoneId,
|
|
2055
|
+
sliceId: entry.sliceId ?? null,
|
|
2056
|
+
taskId: entry.taskId ?? null,
|
|
2057
|
+
source: entry.source,
|
|
2058
|
+
confidence: entry.confidence,
|
|
2059
|
+
files: entry.files,
|
|
2060
|
+
}),
|
|
2061
|
+
});
|
|
2062
|
+
});
|
|
2063
|
+
}
|
|
1965
2064
|
export function insertAuditEvent(entry) {
|
|
1966
2065
|
if (!currentDb)
|
|
1967
2066
|
return;
|
|
@@ -2048,6 +2147,7 @@ export function clearEngineHierarchy() {
|
|
|
2048
2147
|
currentDb.exec("DELETE FROM slice_dependencies");
|
|
2049
2148
|
currentDb.exec("DELETE FROM assessments");
|
|
2050
2149
|
currentDb.exec("DELETE FROM replan_history");
|
|
2150
|
+
currentDb.exec("DELETE FROM milestone_commit_attributions");
|
|
2051
2151
|
currentDb.exec("DELETE FROM tasks");
|
|
2052
2152
|
currentDb.exec("DELETE FROM slices");
|
|
2053
2153
|
currentDb.exec("DELETE FROM milestone_leases");
|
|
@@ -16,7 +16,7 @@ import { invalidateAllCaches } from "./cache.js";
|
|
|
16
16
|
import { startAutoDetached } from "./auto.js";
|
|
17
17
|
import { clearLock } from "./crash-recovery.js";
|
|
18
18
|
import { assessInterruptedSession, formatInterruptedSessionRunningMessage, formatInterruptedSessionSummary, } from "./interrupted-session.js";
|
|
19
|
-
import { listUnitRuntimeRecords, clearUnitRuntimeRecord } from "./unit-runtime.js";
|
|
19
|
+
import { listUnitRuntimeRecords, clearUnitRuntimeRecord, isInFlightRuntimePhase } from "./unit-runtime.js";
|
|
20
20
|
import { resolveExpectedArtifactPath } from "./auto.js";
|
|
21
21
|
import { gsdHome } from "./gsd-home.js";
|
|
22
22
|
import { gsdRoot, milestonesDir, resolveMilestoneFile, resolveMilestonePath, resolveSliceFile, resolveSlicePath, resolveGsdRootFile, relGsdRootFile, relMilestoneFile, relSliceFile, clearPathCache, } from "./paths.js";
|
|
@@ -71,6 +71,19 @@ export function verifyExpectedArtifactForScope(scope, unitType, unitId) {
|
|
|
71
71
|
export function resolveExpectedArtifactPathForScope(scope, unitType, unitId) {
|
|
72
72
|
return resolveExpectedArtifactPath(unitType, unitId, scope.workspace.projectRoot);
|
|
73
73
|
}
|
|
74
|
+
async function runQuickTaskChoice(ctx, pi) {
|
|
75
|
+
if (!ctx.hasUI) {
|
|
76
|
+
ctx.ui.notify("Run /gsd quick <task> for small bounded work, or /gsd do <task> for natural-language routing.", "info");
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const task = (await ctx.ui.input("Quick task", "Describe the small task to run with /gsd quick"))?.trim();
|
|
80
|
+
if (!task) {
|
|
81
|
+
ctx.ui.notify("Quick task cancelled.", "info");
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
const { handleQuick } = await import("./quick.js");
|
|
85
|
+
await handleQuick(task, ctx, pi);
|
|
86
|
+
}
|
|
74
87
|
/**
|
|
75
88
|
* Scope-based overload of isGhostMilestone.
|
|
76
89
|
* Binds basePath and milestoneId from the scope, ensuring path resolution
|
|
@@ -1469,8 +1482,8 @@ function selfHealRuntimeRecords(basePath, ctx) {
|
|
|
1469
1482
|
cleared++;
|
|
1470
1483
|
continue;
|
|
1471
1484
|
}
|
|
1472
|
-
// Clear records stuck in
|
|
1473
|
-
if (phase
|
|
1485
|
+
// Clear records stuck in an in-flight phase (process died mid-unit).
|
|
1486
|
+
if (isInFlightRuntimePhase(phase)) {
|
|
1474
1487
|
clearUnitRuntimeRecord(basePath, unitType, unitId);
|
|
1475
1488
|
cleared++;
|
|
1476
1489
|
}
|
|
@@ -1695,6 +1708,20 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
|
|
|
1695
1708
|
return;
|
|
1696
1709
|
}
|
|
1697
1710
|
}
|
|
1711
|
+
if (interrupted.classification !== "recoverable") {
|
|
1712
|
+
try {
|
|
1713
|
+
const { autoImportMarkdownHierarchyIfDbMismatch } = await import("./migration-auto-check.js");
|
|
1714
|
+
const result = await autoImportMarkdownHierarchyIfDbMismatch(basePath);
|
|
1715
|
+
if (result.action === "imported") {
|
|
1716
|
+
ctx.ui.notify(`Recovered migrated planning state into gsd.db (${result.reason}): ${result.afterDb.milestones} milestone(s), ${result.afterDb.slices} slice(s), ${result.afterDb.tasks} task(s).`, "info");
|
|
1717
|
+
}
|
|
1718
|
+
}
|
|
1719
|
+
catch (err) {
|
|
1720
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1721
|
+
ctx.ui.notify(`GSD could not auto-import existing planning state into gsd.db: ${message}`, "warning");
|
|
1722
|
+
logWarning("guided", `planning state auto-import failed: ${message}`, { file: "guided-flow.ts" });
|
|
1723
|
+
}
|
|
1724
|
+
}
|
|
1698
1725
|
// Always derive from the project root — the assessment may have derived
|
|
1699
1726
|
// state from a worktree path that was cleaned up in the stale branch above.
|
|
1700
1727
|
const state = await deriveState(basePath);
|
|
@@ -1716,8 +1743,8 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
|
|
|
1716
1743
|
// standard wizard below.
|
|
1717
1744
|
{
|
|
1718
1745
|
const prefs = loadEffectiveGSDPreferences(basePath)?.preferences;
|
|
1719
|
-
const {
|
|
1720
|
-
if (
|
|
1746
|
+
const { shouldRunDeepProjectSetup } = await import("./auto-dispatch.js");
|
|
1747
|
+
if (shouldRunDeepProjectSetup(state, prefs, basePath)) {
|
|
1721
1748
|
await startDeepProjectSetupForeground(ctx, pi, basePath, stepMode);
|
|
1722
1749
|
return;
|
|
1723
1750
|
}
|
|
@@ -1780,16 +1807,24 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
|
|
|
1780
1807
|
title: "GSD — Get Shit Done",
|
|
1781
1808
|
summary: ["No active milestone."],
|
|
1782
1809
|
actions: [
|
|
1810
|
+
{
|
|
1811
|
+
id: "quick_task",
|
|
1812
|
+
label: "Quick task",
|
|
1813
|
+
description: "For small bounded work, run /gsd quick <task> or /gsd do <task>.",
|
|
1814
|
+
recommended: true,
|
|
1815
|
+
},
|
|
1783
1816
|
{
|
|
1784
1817
|
id: "new_milestone",
|
|
1785
1818
|
label: "Create next milestone",
|
|
1786
|
-
description: "Define
|
|
1787
|
-
recommended: true,
|
|
1819
|
+
description: "Define a larger body of work with planning artifacts.",
|
|
1788
1820
|
},
|
|
1789
1821
|
],
|
|
1790
1822
|
notYetMessage: "Run /gsd when ready.",
|
|
1791
1823
|
});
|
|
1792
|
-
if (choice === "
|
|
1824
|
+
if (choice === "quick_task") {
|
|
1825
|
+
await runQuickTaskChoice(ctx, pi);
|
|
1826
|
+
}
|
|
1827
|
+
else if (choice === "new_milestone") {
|
|
1793
1828
|
setPendingAutoStart(basePath, { ctx, pi, basePath, milestoneId: nextId, step: stepMode });
|
|
1794
1829
|
await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, pi, nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone");
|
|
1795
1830
|
}
|
|
@@ -1809,11 +1844,16 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
|
|
|
1809
1844
|
title: `GSD — ${milestoneId}: ${milestoneTitle}`,
|
|
1810
1845
|
summary: ["All milestones complete."],
|
|
1811
1846
|
actions: [
|
|
1847
|
+
{
|
|
1848
|
+
id: "quick_task",
|
|
1849
|
+
label: "Quick task",
|
|
1850
|
+
description: "Do a small bounded task without opening a milestone.",
|
|
1851
|
+
recommended: true,
|
|
1852
|
+
},
|
|
1812
1853
|
{
|
|
1813
1854
|
id: "new_milestone",
|
|
1814
1855
|
label: "Start new milestone",
|
|
1815
1856
|
description: "Define and plan the next milestone.",
|
|
1816
|
-
recommended: true,
|
|
1817
1857
|
},
|
|
1818
1858
|
{
|
|
1819
1859
|
id: "status",
|
|
@@ -1823,7 +1863,10 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
|
|
|
1823
1863
|
],
|
|
1824
1864
|
notYetMessage: "Run /gsd when ready.",
|
|
1825
1865
|
});
|
|
1826
|
-
if (choice === "
|
|
1866
|
+
if (choice === "quick_task") {
|
|
1867
|
+
await runQuickTaskChoice(ctx, pi);
|
|
1868
|
+
}
|
|
1869
|
+
else if (choice === "new_milestone") {
|
|
1827
1870
|
const milestoneIds = findMilestoneIds(basePath);
|
|
1828
1871
|
const uniqueMilestoneIds = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
|
|
1829
1872
|
const nextId = nextMilestoneIdReserved(milestoneIds, uniqueMilestoneIds, basePath);
|
|
@@ -1916,13 +1959,18 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
|
|
|
1916
1959
|
const contextFile = resolveMilestoneFile(basePath, milestoneId, "CONTEXT");
|
|
1917
1960
|
const hasContext = !!(contextFile && await loadFile(contextFile));
|
|
1918
1961
|
const actions = [
|
|
1962
|
+
{
|
|
1963
|
+
id: "quick_task",
|
|
1964
|
+
label: "Quick task instead",
|
|
1965
|
+
description: "Use this when the work is small and should not become a milestone.",
|
|
1966
|
+
recommended: true,
|
|
1967
|
+
},
|
|
1919
1968
|
{
|
|
1920
1969
|
id: "plan",
|
|
1921
1970
|
label: "Create roadmap",
|
|
1922
1971
|
description: hasContext
|
|
1923
1972
|
? "Context captured. Decompose into slices with a boundary map."
|
|
1924
1973
|
: "Decompose the milestone into slices with a boundary map.",
|
|
1925
|
-
recommended: true,
|
|
1926
1974
|
},
|
|
1927
1975
|
...(!hasContext ? [{
|
|
1928
1976
|
id: "discuss",
|
|
@@ -1946,7 +1994,10 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
|
|
|
1946
1994
|
actions,
|
|
1947
1995
|
notYetMessage: "Run /gsd when ready.",
|
|
1948
1996
|
});
|
|
1949
|
-
if (choice === "
|
|
1997
|
+
if (choice === "quick_task") {
|
|
1998
|
+
await runQuickTaskChoice(ctx, pi);
|
|
1999
|
+
}
|
|
2000
|
+
else if (choice === "plan") {
|
|
1950
2001
|
setPendingAutoStart(basePath, { ctx, pi, basePath, milestoneId, step: stepMode });
|
|
1951
2002
|
await dispatchWorkflow(pi, await buildPlanMilestonePrompt(milestoneId, milestoneTitle, basePath), "gsd-run", ctx, "plan-milestone");
|
|
1952
2003
|
}
|
|
@@ -15,6 +15,43 @@ import { fileURLToPath } from "node:url";
|
|
|
15
15
|
import { showNextAction } from "../../shared/tui.js";
|
|
16
16
|
import { validatePlanningDirectory, parsePlanningDirectory, transformToGSD, generatePreview, writeGSDDirectory, } from "./index.js";
|
|
17
17
|
import { homedir } from "node:os";
|
|
18
|
+
import { ensureDbOpen } from "../bootstrap/dynamic-tools.js";
|
|
19
|
+
import { clearEngineHierarchy, transaction } from "../gsd-db.js";
|
|
20
|
+
import { migrateFromMarkdown } from "../md-importer.js";
|
|
21
|
+
import { invalidateStateCache } from "../state.js";
|
|
22
|
+
function assertMigrationImportMatchesPreview(imported, preview) {
|
|
23
|
+
const mismatches = [];
|
|
24
|
+
if (imported.hierarchy.milestones !== preview.milestoneCount) {
|
|
25
|
+
mismatches.push(`milestones ${imported.hierarchy.milestones}/${preview.milestoneCount}`);
|
|
26
|
+
}
|
|
27
|
+
if (imported.hierarchy.slices !== preview.totalSlices) {
|
|
28
|
+
mismatches.push(`slices ${imported.hierarchy.slices}/${preview.totalSlices}`);
|
|
29
|
+
}
|
|
30
|
+
if (imported.hierarchy.tasks !== preview.totalTasks) {
|
|
31
|
+
mismatches.push(`tasks ${imported.hierarchy.tasks}/${preview.totalTasks}`);
|
|
32
|
+
}
|
|
33
|
+
if (imported.requirements !== preview.requirements.total) {
|
|
34
|
+
mismatches.push(`requirements ${imported.requirements}/${preview.requirements.total}`);
|
|
35
|
+
}
|
|
36
|
+
if (mismatches.length > 0) {
|
|
37
|
+
throw new Error(`migration DB import verification failed: ${mismatches.join(", ")}`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export async function importWrittenMigrationToDb(basePath, preview) {
|
|
41
|
+
const opened = await ensureDbOpen(basePath);
|
|
42
|
+
if (!opened) {
|
|
43
|
+
throw new Error(`failed to open or create the GSD database at ${basePath}`);
|
|
44
|
+
}
|
|
45
|
+
const counts = transaction(() => {
|
|
46
|
+
clearEngineHierarchy();
|
|
47
|
+
const imported = migrateFromMarkdown(basePath);
|
|
48
|
+
if (preview)
|
|
49
|
+
assertMigrationImportMatchesPreview(imported, preview);
|
|
50
|
+
return imported;
|
|
51
|
+
});
|
|
52
|
+
invalidateStateCache();
|
|
53
|
+
return counts;
|
|
54
|
+
}
|
|
18
55
|
/** Format preview stats for embedding in the review prompt. */
|
|
19
56
|
function formatPreviewStats(preview) {
|
|
20
57
|
const lines = [
|
|
@@ -126,12 +163,14 @@ export async function handleMigrate(args, ctx, pi) {
|
|
|
126
163
|
ctx.ui.notify("Writing .gsd directory…", "info");
|
|
127
164
|
const result = await writeGSDDirectory(project, process.cwd());
|
|
128
165
|
const gsdPath = gsdRoot(process.cwd());
|
|
129
|
-
|
|
166
|
+
const imported = await importWrittenMigrationToDb(process.cwd(), preview);
|
|
167
|
+
ctx.ui.notify(`✓ 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`, "info");
|
|
130
168
|
// ── Post-write review offer ────────────────────────────────────────────────
|
|
131
169
|
const reviewChoice = await showNextAction(ctx, {
|
|
132
170
|
title: "Migration written",
|
|
133
171
|
summary: [
|
|
134
172
|
`${result.paths.length} files written to .gsd/`,
|
|
173
|
+
`${imported.hierarchy.milestones} milestone(s), ${imported.hierarchy.slices} slice(s), and ${imported.hierarchy.tasks} task(s) imported to gsd.db`,
|
|
135
174
|
"",
|
|
136
175
|
"The agent can now review the migrated output against GSD-2 standards —",
|
|
137
176
|
"checking structure, content quality, deriveState() round-trip, and",
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
2
|
+
import { ensureDbOpen } from "./bootstrap/dynamic-tools.js";
|
|
3
|
+
import { clearEngineHierarchy, getAllMilestones, getMilestoneSlices, getSliceTasks, isDbAvailable, transaction, } from "./gsd-db.js";
|
|
4
|
+
import { migrateHierarchyToDb } from "./md-importer.js";
|
|
5
|
+
import { parsePlan, parseRoadmap } from "./parsers-legacy.js";
|
|
6
|
+
import { milestonesDir, resolveMilestoneFile, resolveSliceFile, } from "./paths.js";
|
|
7
|
+
import { invalidateStateCache } from "./state.js";
|
|
8
|
+
function zeroCounts() {
|
|
9
|
+
return { milestones: 0, slices: 0, tasks: 0 };
|
|
10
|
+
}
|
|
11
|
+
function sameCounts(a, b) {
|
|
12
|
+
return a.milestones === b.milestones && a.slices === b.slices && a.tasks === b.tasks;
|
|
13
|
+
}
|
|
14
|
+
export function countMarkdownHierarchy(basePath) {
|
|
15
|
+
const root = milestonesDir(basePath);
|
|
16
|
+
if (!existsSync(root))
|
|
17
|
+
return zeroCounts();
|
|
18
|
+
const counts = zeroCounts();
|
|
19
|
+
for (const entry of readdirSync(root, { withFileTypes: true })) {
|
|
20
|
+
if (!entry.isDirectory() || !/^M\d+/.test(entry.name))
|
|
21
|
+
continue;
|
|
22
|
+
counts.milestones++;
|
|
23
|
+
const roadmapPath = resolveMilestoneFile(basePath, entry.name, "ROADMAP");
|
|
24
|
+
if (!roadmapPath || !existsSync(roadmapPath))
|
|
25
|
+
continue;
|
|
26
|
+
const roadmap = parseRoadmap(readFileSync(roadmapPath, "utf-8"));
|
|
27
|
+
counts.slices += roadmap.slices.length;
|
|
28
|
+
for (const slice of roadmap.slices) {
|
|
29
|
+
const planPath = resolveSliceFile(basePath, entry.name, slice.id, "PLAN");
|
|
30
|
+
if (!planPath || !existsSync(planPath))
|
|
31
|
+
continue;
|
|
32
|
+
const plan = parsePlan(readFileSync(planPath, "utf-8"));
|
|
33
|
+
counts.tasks += plan.tasks.length;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return counts;
|
|
37
|
+
}
|
|
38
|
+
export function countDbHierarchy() {
|
|
39
|
+
if (!isDbAvailable())
|
|
40
|
+
return zeroCounts();
|
|
41
|
+
const counts = zeroCounts();
|
|
42
|
+
const milestones = getAllMilestones();
|
|
43
|
+
counts.milestones = milestones.length;
|
|
44
|
+
for (const milestone of milestones) {
|
|
45
|
+
const slices = getMilestoneSlices(milestone.id);
|
|
46
|
+
counts.slices += slices.length;
|
|
47
|
+
for (const slice of slices) {
|
|
48
|
+
counts.tasks += getSliceTasks(milestone.id, slice.id).length;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return counts;
|
|
52
|
+
}
|
|
53
|
+
export async function autoImportMarkdownHierarchyIfDbMismatch(basePath) {
|
|
54
|
+
const markdown = countMarkdownHierarchy(basePath);
|
|
55
|
+
if (sameCounts(markdown, zeroCounts())) {
|
|
56
|
+
return {
|
|
57
|
+
action: "none",
|
|
58
|
+
reason: "no-markdown",
|
|
59
|
+
markdown,
|
|
60
|
+
beforeDb: zeroCounts(),
|
|
61
|
+
afterDb: zeroCounts(),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
const opened = await ensureDbOpen(basePath);
|
|
65
|
+
if (!opened || !isDbAvailable()) {
|
|
66
|
+
throw new Error(`failed to open or create the GSD database at ${basePath}`);
|
|
67
|
+
}
|
|
68
|
+
const beforeDb = countDbHierarchy();
|
|
69
|
+
if (sameCounts(markdown, beforeDb)) {
|
|
70
|
+
return { action: "none", reason: "in-sync", markdown, beforeDb, afterDb: beforeDb };
|
|
71
|
+
}
|
|
72
|
+
const reason = sameCounts(beforeDb, zeroCounts()) ? "db-empty" : "count-mismatch";
|
|
73
|
+
const imported = transaction(() => {
|
|
74
|
+
clearEngineHierarchy();
|
|
75
|
+
return migrateHierarchyToDb(basePath);
|
|
76
|
+
});
|
|
77
|
+
invalidateStateCache();
|
|
78
|
+
const afterDb = {
|
|
79
|
+
milestones: imported.milestones,
|
|
80
|
+
slices: imported.slices,
|
|
81
|
+
tasks: imported.tasks,
|
|
82
|
+
};
|
|
83
|
+
if (!sameCounts(markdown, afterDb)) {
|
|
84
|
+
throw new Error(`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`);
|
|
85
|
+
}
|
|
86
|
+
return { action: "imported", reason, markdown, beforeDb, afterDb };
|
|
87
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { isAbsolute, relative, resolve } from "node:path";
|
|
2
|
+
import { normalizePlannedFileReference } from "./files.js";
|
|
3
|
+
function isInsideBase(basePath, candidate) {
|
|
4
|
+
const base = resolve(basePath);
|
|
5
|
+
const abs = resolve(candidate);
|
|
6
|
+
const rel = relative(base, abs);
|
|
7
|
+
return rel === "" || (!!rel && !rel.startsWith("..") && !isAbsolute(rel));
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Planning IO fields are execution contracts. Absolute paths are only safe when
|
|
11
|
+
* they stay inside the active working directory; in worktree mode, an absolute
|
|
12
|
+
* path to the original checkout makes executors edit the wrong tree.
|
|
13
|
+
*/
|
|
14
|
+
export function validatePlanningPathScope(basePath, fields) {
|
|
15
|
+
for (const { field, values } of fields) {
|
|
16
|
+
for (const raw of values) {
|
|
17
|
+
const candidate = normalizePlannedFileReference(raw);
|
|
18
|
+
if (!isAbsolute(candidate))
|
|
19
|
+
continue;
|
|
20
|
+
if (isInsideBase(basePath, candidate))
|
|
21
|
+
continue;
|
|
22
|
+
return `${field} contains absolute path outside working directory: ${candidate}. Use a path relative to ${basePath}.`;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
@@ -16,15 +16,14 @@ Start with what the excerpts give you. Read full files when the section heads si
|
|
|
16
16
|
|
|
17
17
|
**On-demand Read ordering:** Complete all slice SUMMARY Reads you need for cross-slice synthesis, the Decision Re-evaluation table, and LEARNINGS **before** calling `gsd_complete_milestone` (step 12). Once that tool runs, the milestone is marked complete in the DB, so it must be the final persistent milestone-closeout write.
|
|
18
18
|
|
|
19
|
-
###
|
|
19
|
+
### Closeout Review Mode
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
The inlined context includes a validation status block.
|
|
22
22
|
|
|
23
|
-
-
|
|
24
|
-
-
|
|
25
|
-
- Significant tests added or changed -> **tester** coverage check against success criteria.
|
|
23
|
+
- If it says a passing validation artifact is present, treat that artifact as authoritative for success criteria, requirement coverage, verification classes, and cross-slice integration. Do not delegate fresh reviewer/security/tester audits unless the validation artifact is internally inconsistent with the inlined summaries.
|
|
24
|
+
- If validation is missing, stale, non-pass, or internally inconsistent, use `subagent` for review work needing fresh context before drafting LEARNINGS: cross-slice integrations or new public APIs -> **reviewer**; auth, network, parsing, file IO, shell exec, or crypto -> **security**; significant tests added or changed -> **tester**.
|
|
26
25
|
|
|
27
|
-
Subagents report only; they do not write user source. Fold findings into Decision Re-evaluation and LEARNINGS before completion.
|
|
26
|
+
Subagents report only; they do not write user source. Fold any findings into Decision Re-evaluation and LEARNINGS before completion.
|
|
28
27
|
|
|
29
28
|
{{inlinedContext}}
|
|
30
29
|
|
|
@@ -33,8 +32,8 @@ Subagents report only; they do not write user source. Fold findings into Decisio
|
|
|
33
32
|
1. Use the **Milestone Summary** output template from the inlined context above
|
|
34
33
|
2. {{skillActivation}}
|
|
35
34
|
3. **Verify code changes exist.** Compare milestone work against the integration branch (`main`, `master`, or recorded branch), using merge-base as older revision and `HEAD` as newer. If the diff lists non-`.gsd/` files, pass. If `HEAD` equals the integration branch/merge-base, treat it as a self-diff retry: inspect milestone-scoped commit evidence (`GSD-Unit: {{milestoneId}}` or production `GSD-Task: Sxx/Tyy` trailers touching `.gsd/milestones/{{milestoneId}}/`) and verify those commits touched non-`.gsd/` files. Record **verification failure** only when neither source shows implementation files.
|
|
36
|
-
4. Verify every **success criterion** from `{{roadmapPath}}
|
|
37
|
-
5. Verify **definition of done**: all slices `[x]`, summaries exist, and integrations work. Record unmet items as **verification failure**.
|
|
35
|
+
4. Verify every **success criterion** from `{{roadmapPath}}`. If passing validation is present, summarize the validation evidence instead of re-auditing it; otherwise verify with evidence from summaries, tests, or observable behavior. Record unmet criteria as **verification failure**.
|
|
36
|
+
5. Verify **definition of done**: all slices `[x]`, summaries exist, and integrations work. If passing validation is present, trust its integration/verification verdict unless inconsistent with current artifacts. Record unmet items as **verification failure**.
|
|
38
37
|
6. If the roadmap includes a **Horizontal Checklist**, verify each item and note unchecked items in the summary.
|
|
39
38
|
7. Fill the **Decision Re-evaluation** table: compare each key `.gsd/DECISIONS.md` decision from this milestone with what shipped, and flag decisions to revisit.
|
|
40
39
|
8. Validate **requirement status transitions**. For each changed requirement, confirm evidence supports the new status. Requirements may move between Active, Validated, Deferred, Blocked, or Out of Scope only with proof.
|
|
@@ -16,7 +16,7 @@ Dispatch ALL slices simultaneously using the `subagent` tool in **parallel mode*
|
|
|
16
16
|
|
|
17
17
|
1. Call `subagent` with `tasks: [...]` containing one entry per slice below
|
|
18
18
|
2. Wait for ALL subagents to complete
|
|
19
|
-
3. Verify each slice's RESEARCH file was written (check
|
|
19
|
+
3. Verify each slice's RESEARCH file was written (check `.gsd/milestones/{{mid}}/slices/<slice-id>/`)
|
|
20
20
|
4. If a subagent failed to write its RESEARCH file, retry it **once** individually
|
|
21
21
|
5. If it fails a second time, write a partial RESEARCH file for that slice with a `## BLOCKER` section explaining the failure — do NOT retry again
|
|
22
22
|
6. Report which slices completed research and which (if any) needed a blocker note
|