gsd-pi 2.80.0-dev.8b3a59f5c → 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-start.js +2 -3
- package/dist/resources/extensions/gsd/auto.js +16 -3
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +21 -0
- 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/crash-recovery.js +56 -10
- package/dist/resources/extensions/gsd/detection.js +106 -0
- 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 +12 -12
- 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 +12 -12
- 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 +32 -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 +25 -20
- 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 +1 -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 +26 -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 +15 -5
- 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 +38 -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 +25 -20
- 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 +33 -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 +24 -7
- 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-start.ts +7 -6
- package/src/resources/extensions/gsd/auto.ts +23 -3
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +22 -0
- 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/crash-recovery.ts +67 -10
- package/src/resources/extensions/gsd/detection.ts +128 -0
- 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/clean-root-preflight.test.ts +88 -2
- 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/detection.test.ts +140 -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/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/{k-MDjzPMF62Rg1-FR437h → FU0iL9dhdc2IBzEYBDOlY}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{k-MDjzPMF62Rg1-FR437h → FU0iL9dhdc2IBzEYBDOlY}/_ssgManifest.js +0 -0
|
@@ -32,13 +32,13 @@ import { detectStuck } from "./detect-stuck.js";
|
|
|
32
32
|
import { runUnit } from "./run-unit.js";
|
|
33
33
|
import { debugLog } from "../debug-logger.js";
|
|
34
34
|
import { resolveWorktreeProjectRoot, normalizeWorktreePathForCompare } from "../worktree-root.js";
|
|
35
|
-
import {
|
|
35
|
+
import { classifyProject } from "../detection.js";
|
|
36
36
|
import { MergeConflictError } from "../git-service.js";
|
|
37
37
|
import { setCurrentPhase, clearCurrentPhase } from "../../shared/gsd-phase-state.js";
|
|
38
38
|
import { pauseAutoForProviderError } from "../provider-error-pause.js";
|
|
39
39
|
import { resumeAutoAfterProviderDelay } from "../bootstrap/provider-error-resume.js";
|
|
40
40
|
import { join, basename } from "node:path";
|
|
41
|
-
import { existsSync, cpSync
|
|
41
|
+
import { existsSync, cpSync } from "node:fs";
|
|
42
42
|
import {
|
|
43
43
|
logWarning,
|
|
44
44
|
logError,
|
|
@@ -684,6 +684,7 @@ export async function runPreDispatch(
|
|
|
684
684
|
deps.postflightPopStash(
|
|
685
685
|
s.originalBasePath || s.basePath,
|
|
686
686
|
s.currentMilestoneId!,
|
|
687
|
+
preflightTransition.stashMarker,
|
|
687
688
|
ctx.ui.notify.bind(ctx.ui),
|
|
688
689
|
);
|
|
689
690
|
}
|
|
@@ -797,6 +798,7 @@ export async function runPreDispatch(
|
|
|
797
798
|
deps.postflightPopStash(
|
|
798
799
|
s.originalBasePath || s.basePath,
|
|
799
800
|
s.currentMilestoneId,
|
|
801
|
+
preflightAllComplete.stashMarker,
|
|
800
802
|
ctx.ui.notify.bind(ctx.ui),
|
|
801
803
|
);
|
|
802
804
|
}
|
|
@@ -925,6 +927,7 @@ export async function runPreDispatch(
|
|
|
925
927
|
deps.postflightPopStash(
|
|
926
928
|
s.originalBasePath || s.basePath,
|
|
927
929
|
s.currentMilestoneId,
|
|
930
|
+
preflightComplete.stashMarker,
|
|
928
931
|
ctx.ui.notify.bind(ctx.ui),
|
|
929
932
|
);
|
|
930
933
|
}
|
|
@@ -1484,8 +1487,9 @@ export async function runUnitPhase(
|
|
|
1484
1487
|
// Verify the working directory is a valid git checkout with project
|
|
1485
1488
|
// files before dispatching work. A broken worktree causes agents to
|
|
1486
1489
|
// hallucinate summaries since they cannot read or write any files.
|
|
1487
|
-
// Uses
|
|
1488
|
-
//
|
|
1490
|
+
// Uses project classification so project presence is not conflated with
|
|
1491
|
+
// ecosystem marker detection. Static/minimal repos become untyped-existing.
|
|
1492
|
+
let projectClassification: ReturnType<typeof classifyProject> | null = null;
|
|
1489
1493
|
if (s.basePath && unitType === "execute-task") {
|
|
1490
1494
|
const gitMarker = join(s.basePath, ".git");
|
|
1491
1495
|
const hasGit = deps.existsSync(gitMarker);
|
|
@@ -1496,30 +1500,29 @@ export async function runUnitPhase(
|
|
|
1496
1500
|
await deps.stopAuto(ctx, pi, msg);
|
|
1497
1501
|
return { action: "break", reason: "worktree-invalid" };
|
|
1498
1502
|
}
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
ctx.ui.notify(`Warning: ${s.basePath} has no recognized project files — proceeding as greenfield project`, "warning");
|
|
1503
|
+
projectClassification = classifyProject(s.basePath);
|
|
1504
|
+
if (projectClassification.kind === "invalid-repo") {
|
|
1505
|
+
const msg = `Worktree health check failed: ${s.basePath} classified as invalid-repo (${projectClassification.reason}) — refusing to dispatch ${unitType} ${unitId}`;
|
|
1506
|
+
debugLog("runUnitPhase", { phase: "worktree-health-invalid-repo", basePath: s.basePath, classification: projectClassification });
|
|
1507
|
+
if (projectClassification.reason === "missing .git" && hasGit) {
|
|
1508
|
+
ctx.ui.notify(
|
|
1509
|
+
`Warning: ${s.basePath} project classification could not confirm .git; assuming it has no project content yet — proceeding as greenfield project because worktree health reported .git present`,
|
|
1510
|
+
"warning",
|
|
1511
|
+
);
|
|
1512
|
+
} else {
|
|
1513
|
+
ctx.ui.notify(msg, "error");
|
|
1514
|
+
await deps.stopAuto(ctx, pi, msg);
|
|
1515
|
+
return { action: "break", reason: "worktree-invalid" };
|
|
1516
|
+
}
|
|
1517
|
+
} else if (projectClassification.kind === "greenfield") {
|
|
1518
|
+
debugLog("runUnitPhase", { phase: "worktree-health-greenfield", basePath: s.basePath, classification: projectClassification });
|
|
1519
|
+
ctx.ui.notify(`Warning: ${s.basePath} has no project content yet — proceeding as greenfield project`, "warning");
|
|
1520
|
+
} else if (projectClassification.kind === "untyped-existing") {
|
|
1521
|
+
debugLog("runUnitPhase", { phase: "worktree-health-untyped-existing", basePath: s.basePath, classification: projectClassification });
|
|
1522
|
+
ctx.ui.notify(
|
|
1523
|
+
`Notice: ${s.basePath} has existing project content but no recognized tooling markers — using generic file-level workflow guidance`,
|
|
1524
|
+
"info",
|
|
1525
|
+
);
|
|
1523
1526
|
}
|
|
1524
1527
|
}
|
|
1525
1528
|
|
|
@@ -1598,6 +1601,17 @@ export async function runUnitPhase(
|
|
|
1598
1601
|
// Prompt injection
|
|
1599
1602
|
let finalPrompt = prompt;
|
|
1600
1603
|
|
|
1604
|
+
if (unitType === "execute-task") {
|
|
1605
|
+
projectClassification ??= classifyProject(s.basePath);
|
|
1606
|
+
if (projectClassification.kind === "untyped-existing") {
|
|
1607
|
+
const samples = projectClassification.contentFiles.slice(0, 8).join(", ") || "project files";
|
|
1608
|
+
finalPrompt +=
|
|
1609
|
+
"\n\n**Project classification:** Existing untyped project. No recognized build/tooling markers were detected, " +
|
|
1610
|
+
"so use generic file-level workflow guidance. Task plans and completion summaries must list every concrete " +
|
|
1611
|
+
`project file changed in \`files\` or \`expected_output\`. Detected content sample: ${samples}.`;
|
|
1612
|
+
}
|
|
1613
|
+
}
|
|
1614
|
+
|
|
1601
1615
|
if (s.pendingVerificationRetry) {
|
|
1602
1616
|
const retryCtx = s.pendingVerificationRetry;
|
|
1603
1617
|
s.pendingVerificationRetry = null;
|
|
@@ -211,6 +211,23 @@ export function hasPendingDeepStage(prefs: GSDPreferences | undefined, basePath:
|
|
|
211
211
|
return gate.status === "pending" || gate.status === "blocked";
|
|
212
212
|
}
|
|
213
213
|
|
|
214
|
+
export function shouldRunDeepProjectSetup(
|
|
215
|
+
state: Pick<GSDState, "phase">,
|
|
216
|
+
prefs: GSDPreferences | undefined,
|
|
217
|
+
basePath: string,
|
|
218
|
+
options: { hasSurvivorBranch?: boolean } = {},
|
|
219
|
+
): boolean {
|
|
220
|
+
if (options.hasSurvivorBranch === true) return false;
|
|
221
|
+
if (
|
|
222
|
+
state.phase !== "pre-planning" &&
|
|
223
|
+
state.phase !== "needs-discussion" &&
|
|
224
|
+
state.phase !== "planning"
|
|
225
|
+
) {
|
|
226
|
+
return false;
|
|
227
|
+
}
|
|
228
|
+
return hasPendingDeepStage(prefs, basePath);
|
|
229
|
+
}
|
|
230
|
+
|
|
214
231
|
function missingSliceStop(mid: string, phase: string): DispatchAction {
|
|
215
232
|
return {
|
|
216
233
|
action: "stop",
|
|
@@ -43,7 +43,7 @@ import {
|
|
|
43
43
|
import { regenerateIfMissing } from "./workflow-projections.js";
|
|
44
44
|
import { syncStateToProjectRoot } from "./auto-worktree.js";
|
|
45
45
|
import { normalizeWorktreePathForCompare } from "./worktree-root.js";
|
|
46
|
-
import { isDbAvailable, getTask, getSlice, getMilestone, updateTaskStatus, _getAdapter } from "./gsd-db.js";
|
|
46
|
+
import { isDbAvailable, getTask, getSlice, getMilestone, updateTaskStatus, _getAdapter, getVerificationEvidence } from "./gsd-db.js";
|
|
47
47
|
import { renderPlanCheckboxes } from "./markdown-renderer.js";
|
|
48
48
|
import { consumeSignal } from "./session-status-io.js";
|
|
49
49
|
import {
|
|
@@ -852,22 +852,22 @@ export async function postUnitPreVerification(pctx: PostUnitContext, opts?: PreV
|
|
|
852
852
|
}
|
|
853
853
|
|
|
854
854
|
// Evidence cross-reference (execute-task only)
|
|
855
|
-
//
|
|
856
|
-
//
|
|
857
|
-
//
|
|
858
|
-
// we can still detect units that claimed success but ran no commands.
|
|
855
|
+
// Only compare against concrete command evidence persisted by the task
|
|
856
|
+
// completion tool. A prose Verify field can be satisfied later by the
|
|
857
|
+
// host verification gate, so it is not enough to accuse the unit.
|
|
859
858
|
if (safetyConfig.evidence_cross_reference && s.currentUnit.type === "execute-task") {
|
|
860
859
|
try {
|
|
861
860
|
const actual = getEvidence();
|
|
862
861
|
const bashCalls = actual.filter(e => e.kind === "bash");
|
|
863
|
-
// If the task is marked complete but zero bash commands were run,
|
|
864
|
-
// it's suspicious — the LLM may have fabricated results.
|
|
865
862
|
if (sMid && sSid && sTid && isDbAvailable()) {
|
|
866
863
|
const taskRow = getTask(sMid, sSid, sTid);
|
|
867
|
-
|
|
868
|
-
|
|
864
|
+
const claimedCommands = getVerificationEvidence(sMid, sSid, sTid)
|
|
865
|
+
.map((row) => row.command)
|
|
866
|
+
.filter((command): command is string => typeof command === "string" && command.trim().length > 0);
|
|
867
|
+
if (taskRow?.status === "complete" && claimedCommands.length > 0 && bashCalls.length === 0) {
|
|
868
|
+
logWarning("safety", "task claimed verification command evidence but no execution tool calls were recorded");
|
|
869
869
|
ctx.ui.notify(
|
|
870
|
-
`Safety: task ${sTid}
|
|
870
|
+
`Safety: task ${sTid} claimed command evidence but no execution tool calls were recorded`,
|
|
871
871
|
"warning",
|
|
872
872
|
);
|
|
873
873
|
}
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
import { loadFile, parseContinue, parseSummary, loadActiveOverrides, formatOverridesSection, parseTaskPlanFile } from "./files.js";
|
|
10
10
|
import type { Override, UatType } from "./files.js";
|
|
11
|
-
import { hasVerdict, getUatType } from "./verdict-parser.js";
|
|
11
|
+
import { hasVerdict, getUatType, extractVerdict } from "./verdict-parser.js";
|
|
12
12
|
import { loadPrompt, inlineTemplate } from "./prompt-loader.js";
|
|
13
13
|
import {
|
|
14
14
|
resolveMilestoneFile, resolveSliceFile, resolveSlicePath,
|
|
@@ -39,6 +39,7 @@ import { logWarning } from "./workflow-logger.js";
|
|
|
39
39
|
import { inlineGraphSubgraph } from "./graph-context.js";
|
|
40
40
|
import { buildExtractionStepsBlock } from "./commands-extract-learnings.js";
|
|
41
41
|
import { resolveSkillManifest, warnIfManifestHasMissingSkills } from "./skill-manifest.js";
|
|
42
|
+
import { classifyProject, type ProjectClassification } from "./detection.js";
|
|
42
43
|
|
|
43
44
|
// ─── Preamble Cap ─────────────────────────────────────────────────────────────
|
|
44
45
|
|
|
@@ -80,6 +81,108 @@ function resolveSummaryBudgetChars(): number {
|
|
|
80
81
|
return resolvePromptBudgets().summaryBudgetChars;
|
|
81
82
|
}
|
|
82
83
|
|
|
84
|
+
function formatProjectClassificationForPlanning(classification: ProjectClassification): string {
|
|
85
|
+
const sampleFiles = classification.contentFiles.slice(0, 8);
|
|
86
|
+
const sample = sampleFiles.length > 0 ? sampleFiles.map((file) => `\`${file}\``).join(", ") : "(none)";
|
|
87
|
+
const lines = [
|
|
88
|
+
"### Project Classification",
|
|
89
|
+
"",
|
|
90
|
+
`- **Kind:** ${classification.kind}`,
|
|
91
|
+
`- **Content files:** ${classification.contentFiles.length}`,
|
|
92
|
+
`- **Sample files:** ${sample}`,
|
|
93
|
+
`- **Reason:** ${classification.reason}`,
|
|
94
|
+
"",
|
|
95
|
+
];
|
|
96
|
+
|
|
97
|
+
if (classification.kind === "untyped-existing") {
|
|
98
|
+
if (classification.contentFiles.length <= 2) {
|
|
99
|
+
lines.push(
|
|
100
|
+
"**Workflow sizing:** This is a tiny existing untyped project. Prefer exactly one slice unless the milestone request clearly spans multiple independent user-visible capabilities.",
|
|
101
|
+
);
|
|
102
|
+
} else if (classification.contentFiles.length <= 5) {
|
|
103
|
+
lines.push(
|
|
104
|
+
"**Workflow sizing:** This is a small existing untyped project. Prefer 1-2 slices unless the milestone request clearly spans multiple independent user-visible capabilities.",
|
|
105
|
+
);
|
|
106
|
+
} else {
|
|
107
|
+
lines.push(
|
|
108
|
+
"**Workflow sizing:** Existing untyped project. Use generic file-level workflow guidance and size slices by real capability boundaries, not by missing tooling markers.",
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
} else if (classification.kind === "greenfield") {
|
|
112
|
+
lines.push("**Workflow sizing:** No project content exists yet. Use normal greenfield sizing for the requested scope.");
|
|
113
|
+
} else if (classification.kind === "typed-existing") {
|
|
114
|
+
lines.push("**Workflow sizing:** Known project markers exist. Use normal ecosystem-aware planning guidance.");
|
|
115
|
+
} else {
|
|
116
|
+
lines.push("**Workflow sizing:** Invalid repository state. Planning should surface this as a blocker rather than inventing project structure.");
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return lines.join("\n");
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function normalizeArtifactRef(value: string): string {
|
|
123
|
+
return value.trim().replace(/^[-\s]+/, "").replace(/^["'`]+|["'`]+$/g, "").replaceAll("\\", "/").replace(/^\.\//, "");
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function parseCoveredArtifacts(validationContent: string): Set<string> {
|
|
127
|
+
const covered = new Set<string>();
|
|
128
|
+
const lines = validationContent.split(/\r?\n/);
|
|
129
|
+
let inCoveredArtifacts = false;
|
|
130
|
+
for (const line of lines) {
|
|
131
|
+
if (/^\s*covered[-_]?artifacts\s*:/i.test(line)) {
|
|
132
|
+
inCoveredArtifacts = true;
|
|
133
|
+
const inline = line.split(/covered[-_]?artifacts\s*:/i)[1]?.trim();
|
|
134
|
+
if (inline && inline !== "[]") {
|
|
135
|
+
inline.replace(/^\[|\]$/g, "").split(",").map(normalizeArtifactRef).filter(Boolean).forEach((item) => covered.add(item));
|
|
136
|
+
}
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
if (!inCoveredArtifacts) continue;
|
|
140
|
+
if (/^\S/.test(line) && !/^\s*-/.test(line)) break;
|
|
141
|
+
const item = line.match(/^\s*-\s*(.+)$/)?.[1];
|
|
142
|
+
if (item) covered.add(normalizeArtifactRef(item));
|
|
143
|
+
}
|
|
144
|
+
return covered;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function isValidationFreshOrApplicable(validationContent: string | null, currentArtifacts: string[]): boolean {
|
|
148
|
+
if (!validationContent) return false;
|
|
149
|
+
if (!/validation_metadata:/i.test(validationContent)) return false;
|
|
150
|
+
const coveredArtifacts = parseCoveredArtifacts(validationContent);
|
|
151
|
+
if (coveredArtifacts.size === 0) return false;
|
|
152
|
+
return currentArtifacts
|
|
153
|
+
.map(normalizeArtifactRef)
|
|
154
|
+
.filter(Boolean)
|
|
155
|
+
.every((artifact) => coveredArtifacts.has(artifact));
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function formatCloseoutReviewInstructions(validationContent: string | null, validationRel: string, currentArtifacts: string[]): string {
|
|
159
|
+
const verdict = validationContent ? extractVerdict(validationContent) : null;
|
|
160
|
+
const validationFresh = isValidationFreshOrApplicable(validationContent, currentArtifacts);
|
|
161
|
+
if (verdict === "pass" && validationFresh) {
|
|
162
|
+
return [
|
|
163
|
+
"### Passing Validation Artifact",
|
|
164
|
+
"",
|
|
165
|
+
`A passing validation artifact is present at \`${validationRel}\`. Treat it as authoritative for success criteria, requirement coverage, verification classes, and cross-slice integration.`,
|
|
166
|
+
"",
|
|
167
|
+
"Do not delegate fresh reviewer/security/tester audits and do not redo the validation evidence review unless the artifact is internally inconsistent with the inlined summaries. Focus this unit on final milestone narrative, learnings, PROJECT/requirements updates, and `gsd_complete_milestone`.",
|
|
168
|
+
].join("\n");
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (verdict) {
|
|
172
|
+
return [
|
|
173
|
+
"### Validation Requires Attention",
|
|
174
|
+
"",
|
|
175
|
+
`A validation artifact is present at \`${validationRel}\` with verdict \`${verdict}\`, but it is missing freshness metadata or does not cover current milestone artifacts. Do not treat the milestone as complete unless the issues are resolved and evidence supports completion.`,
|
|
176
|
+
].join("\n");
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return [
|
|
180
|
+
"### No Passing Validation Artifact",
|
|
181
|
+
"",
|
|
182
|
+
`No passing validation artifact was found at \`${validationRel}\`. Use the full closeout review path before completion.`,
|
|
183
|
+
].join("\n");
|
|
184
|
+
}
|
|
185
|
+
|
|
83
186
|
function capPreamble(preamble: string): string {
|
|
84
187
|
// Cap inlined context at min(historical 30K ceiling, scaled inline budget).
|
|
85
188
|
// The ceiling preserves pre-fix behavior for large-window users; the scaled
|
|
@@ -1658,6 +1761,8 @@ export async function buildPlanMilestonePrompt(mid: string, midTitle: string, ba
|
|
|
1658
1761
|
const researchAnchor = readPhaseAnchor(base, mid, "research-milestone");
|
|
1659
1762
|
if (researchAnchor) inlined.push(formatAnchorForPrompt(researchAnchor));
|
|
1660
1763
|
|
|
1764
|
+
inlined.push(formatProjectClassificationForPlanning(classifyProject(base)));
|
|
1765
|
+
|
|
1661
1766
|
inlined.push(await inlineFile(contextPath, contextRel, "Milestone Context"));
|
|
1662
1767
|
const researchInline = await inlineFileOptional(researchPath, researchRel, "Milestone Research");
|
|
1663
1768
|
if (researchInline) inlined.push(researchInline);
|
|
@@ -2348,6 +2453,9 @@ export async function buildCompleteMilestonePrompt(
|
|
|
2348
2453
|
const inlineLevel = level ?? resolveInlineLevel();
|
|
2349
2454
|
const roadmapPath = resolveMilestoneFile(base, mid, "ROADMAP");
|
|
2350
2455
|
const roadmapRel = relMilestoneFile(base, mid, "ROADMAP");
|
|
2456
|
+
const validationPath = resolveMilestoneFile(base, mid, "VALIDATION");
|
|
2457
|
+
const validationRel = relMilestoneFile(base, mid, "VALIDATION");
|
|
2458
|
+
const validationContent = validationPath ? await loadFile(validationPath) : null;
|
|
2351
2459
|
|
|
2352
2460
|
const inlined: string[] = [];
|
|
2353
2461
|
inlined.push(await inlineFile(roadmapPath, roadmapRel, "Milestone Roadmap"));
|
|
@@ -2389,6 +2497,13 @@ export async function buildCompleteMilestonePrompt(
|
|
|
2389
2497
|
`### On-demand Slice Summaries\n\nExcerpted above. Read the full file for any slice when the excerpt's section heads don't carry enough narrative for the milestone summary you're drafting:\n\n${pathList}`,
|
|
2390
2498
|
);
|
|
2391
2499
|
}
|
|
2500
|
+
const validationContext = [
|
|
2501
|
+
formatCloseoutReviewInstructions(validationContent, validationRel, [validationRel, roadmapRel, ...summaryRelPaths]),
|
|
2502
|
+
];
|
|
2503
|
+
if (validationContent) {
|
|
2504
|
+
validationContext.push(`### Milestone Validation\nSource: \`${validationRel}\`\n\n${validationContent.trim()}`);
|
|
2505
|
+
}
|
|
2506
|
+
inlined.unshift(...validationContext);
|
|
2392
2507
|
|
|
2393
2508
|
// Inline root GSD files (skip for minimal — completion can read these if needed)
|
|
2394
2509
|
if (inlineLevel !== "minimal") {
|
|
@@ -664,12 +664,13 @@ export async function bootstrapAutoSession(
|
|
|
664
664
|
}
|
|
665
665
|
|
|
666
666
|
const effectivePrefs = loadEffectiveGSDPreferences(base)?.preferences;
|
|
667
|
-
const
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
667
|
+
const { shouldRunDeepProjectSetup } = await import("./auto-dispatch.js");
|
|
668
|
+
const deepProjectStagePending = shouldRunDeepProjectSetup(
|
|
669
|
+
state,
|
|
670
|
+
effectivePrefs,
|
|
671
|
+
base,
|
|
672
|
+
{ hasSurvivorBranch },
|
|
673
|
+
);
|
|
673
674
|
|
|
674
675
|
if (deepProjectStagePending) {
|
|
675
676
|
// Deep project-level setup runs before the first milestone exists. Let
|
|
@@ -293,11 +293,15 @@ const STATE_REBUILD_MIN_INTERVAL_MS = 30_000;
|
|
|
293
293
|
* the DB is unavailable (e.g. fresh project before init) we skip registration
|
|
294
294
|
* silently rather than blocking session start.
|
|
295
295
|
*/
|
|
296
|
-
function registerAutoWorkerForSession(
|
|
296
|
+
function registerAutoWorkerForSession(
|
|
297
|
+
session: AutoSession,
|
|
298
|
+
projectRootOverride?: string,
|
|
299
|
+
): void {
|
|
297
300
|
if (session.workerId) return; // already registered (e.g. resume re-runs)
|
|
298
301
|
try {
|
|
299
302
|
const projectRootRealpath = normalizeRealPath(
|
|
300
|
-
|
|
303
|
+
projectRootOverride
|
|
304
|
+
?? session.scope?.workspace.projectRoot
|
|
301
305
|
?? (session.originalBasePath || session.basePath),
|
|
302
306
|
);
|
|
303
307
|
session.workerId = registerAutoWorker({ projectRootRealpath });
|
|
@@ -420,6 +424,17 @@ export function _synthesizePausedSessionRecoveryForTest(
|
|
|
420
424
|
return synthesizePausedSessionRecovery(basePath, unitType, unitId, sessionFile);
|
|
421
425
|
}
|
|
422
426
|
|
|
427
|
+
const DETACHED_AUTO_KEEPALIVE_INTERVAL_MS = 30_000;
|
|
428
|
+
|
|
429
|
+
function withDetachedAutoKeepalive<T>(run: Promise<T>): Promise<T> {
|
|
430
|
+
const keepAlive = setInterval(() => {}, DETACHED_AUTO_KEEPALIVE_INTERVAL_MS);
|
|
431
|
+
return run.finally(() => {
|
|
432
|
+
clearInterval(keepAlive);
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
export const _withDetachedAutoKeepaliveForTest = withDetachedAutoKeepalive;
|
|
437
|
+
|
|
423
438
|
export function startAutoDetached(
|
|
424
439
|
ctx: ExtensionCommandContext,
|
|
425
440
|
pi: ExtensionAPI,
|
|
@@ -431,7 +446,7 @@ export function startAutoDetached(
|
|
|
431
446
|
milestoneLock?: string | null;
|
|
432
447
|
},
|
|
433
448
|
): void {
|
|
434
|
-
void startAuto(ctx, pi, base, verboseMode, options).catch((err) => {
|
|
449
|
+
void withDetachedAutoKeepalive(startAuto(ctx, pi, base, verboseMode, options)).catch((err) => {
|
|
435
450
|
const message = getErrorMessage(err);
|
|
436
451
|
ctx.ui.notify(`Auto-start failed: ${message}`, "error");
|
|
437
452
|
logWarning("engine", `auto start error: ${message}`, { file: "auto.ts" });
|
|
@@ -2046,6 +2061,11 @@ export async function startAuto(
|
|
|
2046
2061
|
buildResolver,
|
|
2047
2062
|
};
|
|
2048
2063
|
|
|
2064
|
+
// Register the worker before bootstrap enters a milestone worktree.
|
|
2065
|
+
// This ensures enterMilestone can claim a lease and seed dispatch claims
|
|
2066
|
+
// for crash-recovery fidelity (#5405).
|
|
2067
|
+
registerAutoWorkerForSession(s, base);
|
|
2068
|
+
|
|
2049
2069
|
const ready = await bootstrapAutoSession(
|
|
2050
2070
|
s,
|
|
2051
2071
|
ctx,
|
|
@@ -65,6 +65,26 @@ async function applyDisabledModelProviderPolicy(ctx: ExtensionContext): Promise<
|
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
+
/**
|
|
69
|
+
* Bridge `context_management.compaction_threshold_percent` from GSD preferences
|
|
70
|
+
* into the agent's runtime compaction settings (#5475). The preference is
|
|
71
|
+
* validated to (0.5, 0.95) at load time, but defense-in-depth normalization
|
|
72
|
+
* here protects against a stale or hand-edited prefs file. Calling with
|
|
73
|
+
* `undefined` clears any prior override so a removed preference does not leak.
|
|
74
|
+
*/
|
|
75
|
+
async function applyCompactionThresholdOverride(ctx: ExtensionContext): Promise<void> {
|
|
76
|
+
try {
|
|
77
|
+
const { loadEffectiveGSDPreferences } = await import("../preferences.js");
|
|
78
|
+
const prefs = loadEffectiveGSDPreferences();
|
|
79
|
+
const raw = prefs?.preferences.context_management?.compaction_threshold_percent;
|
|
80
|
+
const value =
|
|
81
|
+
typeof raw === "number" && Number.isFinite(raw) && raw > 0 && raw < 1 ? raw : undefined;
|
|
82
|
+
ctx.setCompactionThresholdOverride(value);
|
|
83
|
+
} catch {
|
|
84
|
+
// Non-fatal: leave any existing override in place.
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
68
88
|
export function resolveNotificationStoreBasePath(cwd: string = process.cwd()): string {
|
|
69
89
|
return resolveWorktreeProjectRoot(cwd);
|
|
70
90
|
}
|
|
@@ -123,6 +143,7 @@ export function registerHooks(
|
|
|
123
143
|
await resetAskUserQuestionsTurnCache();
|
|
124
144
|
await syncServiceTierStatus(ctx);
|
|
125
145
|
await applyDisabledModelProviderPolicy(ctx);
|
|
146
|
+
await applyCompactionThresholdOverride(ctx);
|
|
126
147
|
// Skip MCP auto-prep when running inside an auto-worktree (see session_switch below).
|
|
127
148
|
const { isInAutoWorktree } = await import("../auto-worktree.js");
|
|
128
149
|
if (!isInAutoWorktree(process.cwd())) {
|
|
@@ -172,6 +193,7 @@ export function registerHooks(
|
|
|
172
193
|
clearDiscussionFlowState(process.cwd());
|
|
173
194
|
await syncServiceTierStatus(ctx);
|
|
174
195
|
await applyDisabledModelProviderPolicy(ctx);
|
|
196
|
+
await applyCompactionThresholdOverride(ctx);
|
|
175
197
|
// Skip MCP auto-prep when running inside an auto-worktree. The worktree
|
|
176
198
|
// already has .mcp.json from createAutoWorktree, and re-running the writer
|
|
177
199
|
// post-chdir rewrites the file mid-run (non-idempotent due to cwd-relative
|
|
@@ -21,10 +21,34 @@ import { nativeHasChanges } from "./native-git-bridge.js";
|
|
|
21
21
|
export interface PreflightResult {
|
|
22
22
|
/** true when a stash was pushed and postflightPopStash should be called */
|
|
23
23
|
stashPushed: boolean;
|
|
24
|
+
/** Unique marker embedded in the stash message for targeted restoration */
|
|
25
|
+
stashMarker?: string;
|
|
24
26
|
/** human-readable summary of what happened (empty string for clean trees) */
|
|
25
27
|
summary: string;
|
|
26
28
|
}
|
|
27
29
|
|
|
30
|
+
function findPreflightStashRef(basePath: string, milestoneId: string, stashMarker?: string): string | null {
|
|
31
|
+
const markerPrefix = `gsd-preflight-stash:${milestoneId}:`;
|
|
32
|
+
let fallbackRef: string | null = null;
|
|
33
|
+
try {
|
|
34
|
+
const list = execFileSync("git", ["stash", "list", "--format=%gd%x00%s"], {
|
|
35
|
+
cwd: basePath,
|
|
36
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
37
|
+
encoding: "utf-8",
|
|
38
|
+
env: GIT_NO_PROMPT_ENV,
|
|
39
|
+
});
|
|
40
|
+
for (const line of list.split("\n")) {
|
|
41
|
+
const [ref, subject] = line.split("\x00");
|
|
42
|
+
if (!ref || !subject) continue;
|
|
43
|
+
if (stashMarker && subject.includes(stashMarker)) return ref;
|
|
44
|
+
if (!fallbackRef && subject.includes(markerPrefix)) fallbackRef = ref;
|
|
45
|
+
}
|
|
46
|
+
} catch (err) {
|
|
47
|
+
logWarning("preflight", `stash list failed before restore: ${err instanceof Error ? err.message : String(err)}`);
|
|
48
|
+
}
|
|
49
|
+
return fallbackRef;
|
|
50
|
+
}
|
|
51
|
+
|
|
28
52
|
/**
|
|
29
53
|
* Check the working tree for dirty files before a milestone merge.
|
|
30
54
|
*
|
|
@@ -62,7 +86,8 @@ export function preflightCleanRoot(
|
|
|
62
86
|
|
|
63
87
|
// Push the stash
|
|
64
88
|
try {
|
|
65
|
-
|
|
89
|
+
const stashMarker = `gsd-preflight-stash:${milestoneId}:${process.pid}:${Date.now()}:${process.hrtime.bigint().toString(36)}`;
|
|
90
|
+
execFileSync("git", ["stash", "push", "--include-untracked", "-m", `gsd-preflight-stash [${stashMarker}]`], {
|
|
66
91
|
cwd: basePath,
|
|
67
92
|
stdio: ["ignore", "pipe", "pipe"],
|
|
68
93
|
encoding: "utf-8",
|
|
@@ -70,6 +95,7 @@ export function preflightCleanRoot(
|
|
|
70
95
|
});
|
|
71
96
|
return {
|
|
72
97
|
stashPushed: true,
|
|
98
|
+
stashMarker,
|
|
73
99
|
summary: `Stashed uncommitted changes before merge (milestone ${milestoneId}).`,
|
|
74
100
|
};
|
|
75
101
|
} catch (err) {
|
|
@@ -91,10 +117,19 @@ export function preflightCleanRoot(
|
|
|
91
117
|
export function postflightPopStash(
|
|
92
118
|
basePath: string,
|
|
93
119
|
milestoneId: string,
|
|
120
|
+
stashMarker: string | undefined,
|
|
94
121
|
notify: (message: string, level: "info" | "warning" | "error") => void,
|
|
95
122
|
): void {
|
|
123
|
+
let stashRef: string | null = null;
|
|
96
124
|
try {
|
|
97
|
-
|
|
125
|
+
stashRef = findPreflightStashRef(basePath, milestoneId, stashMarker);
|
|
126
|
+
if (!stashRef) {
|
|
127
|
+
const msg = `No matching GSD preflight stash found for milestone ${milestoneId}; leaving stash list untouched.`;
|
|
128
|
+
logWarning("preflight", msg);
|
|
129
|
+
notify(msg, "warning");
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
execFileSync("git", ["stash", "pop", stashRef], {
|
|
98
133
|
cwd: basePath,
|
|
99
134
|
stdio: ["ignore", "pipe", "pipe"],
|
|
100
135
|
encoding: "utf-8",
|
|
@@ -104,7 +139,10 @@ export function postflightPopStash(
|
|
|
104
139
|
} catch (err) {
|
|
105
140
|
// Pop conflicts mean the merged code collides with the stashed changes.
|
|
106
141
|
// Log a warning — the user needs to resolve manually, but the merge succeeded.
|
|
107
|
-
const
|
|
142
|
+
const restoreHint = stashRef
|
|
143
|
+
? `Run "git stash pop ${stashRef}" or "git stash apply ${stashRef}" manually to restore the correct stash.`
|
|
144
|
+
: `Run "git stash list" to find the matching GSD preflight stash before restoring manually.`;
|
|
145
|
+
const msg = `git stash pop ${stashRef ?? ""}`.trim() + ` failed after merge of milestone ${milestoneId}: ${err instanceof Error ? err.message : String(err)}. ${restoreHint}`;
|
|
108
146
|
logWarning("preflight", msg);
|
|
109
147
|
notify(msg, "warning");
|
|
110
148
|
}
|
|
@@ -45,5 +45,11 @@ export async function handleGSDCommand(
|
|
|
45
45
|
|
|
46
46
|
if (handled) return;
|
|
47
47
|
|
|
48
|
+
if (trimmed.includes(" ")) {
|
|
49
|
+
const { handleDo } = await import("../commands-do.js");
|
|
50
|
+
await handleDo(trimmed, ctx, pi);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
48
54
|
ctx.ui.notify(`Unknown: /gsd ${trimmed}. Run /gsd help for available commands.`, "warning");
|
|
49
55
|
}
|