gsd-pi 2.82.0-dev.dfbc5f58f → 2.82.0-dev.e7a7f1ed5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +1 -1
- package/dist/resources/extensions/gsd/auto/phases.js +73 -30
- package/dist/resources/extensions/gsd/auto-dashboard.js +66 -1
- package/dist/resources/extensions/gsd/auto-direct-dispatch.js +1 -0
- package/dist/resources/extensions/gsd/auto-dispatch.js +10 -16
- package/dist/resources/extensions/gsd/auto-recovery.js +40 -13
- package/dist/resources/extensions/gsd/auto-start.js +3 -3
- package/dist/resources/extensions/gsd/auto-verification.js +17 -4
- package/dist/resources/extensions/gsd/auto-worktree.js +65 -9
- package/dist/resources/extensions/gsd/auto.js +7 -2
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +27 -6
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +4 -2
- package/dist/resources/extensions/gsd/commands-prefs-wizard.js +7 -2
- package/dist/resources/extensions/gsd/crash-recovery.js +16 -4
- package/dist/resources/extensions/gsd/db/milestone-leases.js +24 -0
- package/dist/resources/extensions/gsd/doctor-git-checks.js +46 -1
- package/dist/resources/extensions/gsd/git-service.js +6 -2
- package/dist/resources/extensions/gsd/gsd-db.js +20 -6
- package/dist/resources/extensions/gsd/guided-flow-queue.js +4 -3
- package/dist/resources/extensions/gsd/guided-flow.js +95 -116
- package/dist/resources/extensions/gsd/guided-unit-context.js +23 -0
- package/dist/resources/extensions/gsd/migration-auto-check.js +12 -17
- package/dist/resources/extensions/gsd/pending-auto-start.js +52 -0
- package/dist/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/complete-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/discuss-headless.md +8 -8
- package/dist/resources/extensions/gsd/prompts/discuss.md +9 -9
- package/dist/resources/extensions/gsd/prompts/guided-discuss-project.md +4 -4
- package/dist/resources/extensions/gsd/prompts/guided-discuss-requirements.md +3 -3
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/queue.md +4 -4
- package/dist/resources/extensions/gsd/prompts/refine-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/rewrite-docs.md +1 -1
- package/dist/resources/extensions/gsd/queue-reorder-ui.js +30 -13
- package/dist/resources/extensions/gsd/smart-entry-routing.js +36 -0
- package/dist/resources/extensions/gsd/state-reconciliation/drift/project-md.js +9 -14
- package/dist/resources/extensions/gsd/state-reconciliation/drift/roadmap.js +19 -24
- package/dist/resources/extensions/gsd/status-guards.js +7 -0
- package/dist/resources/extensions/gsd/workflow-mcp.js +17 -1
- 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 +9 -9
- 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/api/browse-directories/route.js +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 +9 -9
- 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 +1 -1
- package/packages/pi-ai/dist/providers/google-gemini-cli.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/google-gemini-cli.js +5 -0
- package/packages/pi-ai/dist/providers/google-gemini-cli.js.map +1 -1
- package/packages/pi-ai/dist/providers/google-gemini-cli.test.d.ts +2 -0
- package/packages/pi-ai/dist/providers/google-gemini-cli.test.d.ts.map +1 -0
- package/packages/pi-ai/dist/providers/google-gemini-cli.test.js +41 -0
- package/packages/pi-ai/dist/providers/google-gemini-cli.test.js.map +1 -0
- package/packages/pi-ai/src/providers/google-gemini-cli.test.ts +49 -0
- package/packages/pi-ai/src/providers/google-gemini-cli.ts +7 -0
- package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js +24 -6
- package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js.map +1 -1
- package/packages/pi-coding-agent/src/modes/interactive/components/footer.ts +23 -7
- package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-tui/dist/__tests__/terminal.test.d.ts +2 -0
- package/packages/pi-tui/dist/__tests__/terminal.test.d.ts.map +1 -0
- package/packages/pi-tui/dist/__tests__/terminal.test.js +103 -0
- package/packages/pi-tui/dist/__tests__/terminal.test.js.map +1 -0
- package/packages/pi-tui/dist/terminal.d.ts +2 -0
- package/packages/pi-tui/dist/terminal.d.ts.map +1 -1
- package/packages/pi-tui/dist/terminal.js +12 -0
- package/packages/pi-tui/dist/terminal.js.map +1 -1
- package/packages/pi-tui/src/__tests__/terminal.test.ts +121 -0
- package/packages/pi-tui/src/terminal.ts +11 -0
- package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +1 -1
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +9 -0
- package/src/resources/extensions/gsd/auto/phases.ts +83 -37
- package/src/resources/extensions/gsd/auto-dashboard.ts +72 -1
- package/src/resources/extensions/gsd/auto-direct-dispatch.ts +1 -0
- package/src/resources/extensions/gsd/auto-dispatch.ts +10 -16
- package/src/resources/extensions/gsd/auto-recovery.ts +45 -11
- package/src/resources/extensions/gsd/auto-start.ts +2 -3
- package/src/resources/extensions/gsd/auto-verification.ts +22 -2
- package/src/resources/extensions/gsd/auto-worktree.ts +74 -9
- package/src/resources/extensions/gsd/auto.ts +8 -2
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +36 -6
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +4 -2
- package/src/resources/extensions/gsd/commands-prefs-wizard.ts +8 -3
- package/src/resources/extensions/gsd/crash-recovery.ts +16 -2
- package/src/resources/extensions/gsd/db/milestone-leases.ts +26 -0
- package/src/resources/extensions/gsd/doctor-git-checks.ts +45 -1
- package/src/resources/extensions/gsd/doctor-types.ts +1 -0
- package/src/resources/extensions/gsd/git-service.ts +6 -3
- package/src/resources/extensions/gsd/gsd-db.ts +18 -6
- package/src/resources/extensions/gsd/guided-flow-queue.ts +4 -3
- package/src/resources/extensions/gsd/guided-flow.ts +128 -133
- package/src/resources/extensions/gsd/guided-unit-context.ts +30 -0
- package/src/resources/extensions/gsd/migration-auto-check.ts +15 -23
- package/src/resources/extensions/gsd/pending-auto-start.ts +79 -0
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/discuss-headless.md +8 -8
- package/src/resources/extensions/gsd/prompts/discuss.md +9 -9
- package/src/resources/extensions/gsd/prompts/guided-discuss-project.md +4 -4
- package/src/resources/extensions/gsd/prompts/guided-discuss-requirements.md +3 -3
- package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/queue.md +4 -4
- package/src/resources/extensions/gsd/prompts/refine-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/rewrite-docs.md +1 -1
- package/src/resources/extensions/gsd/queue-reorder-ui.ts +31 -13
- package/src/resources/extensions/gsd/smart-entry-routing.ts +77 -0
- package/src/resources/extensions/gsd/state-reconciliation/drift/project-md.ts +12 -15
- package/src/resources/extensions/gsd/state-reconciliation/drift/roadmap.ts +17 -25
- package/src/resources/extensions/gsd/status-guards.ts +8 -0
- package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +71 -0
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +2 -0
- package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +29 -1
- package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +53 -2
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +76 -5
- package/src/resources/extensions/gsd/tests/auto-stop-notification.test.ts +20 -0
- package/src/resources/extensions/gsd/tests/checkout-branch-stash-guard.test.ts +87 -0
- package/src/resources/extensions/gsd/tests/clear-stale-autostart.test.ts +11 -2
- package/src/resources/extensions/gsd/tests/complete-slice.test.ts +5 -9
- package/src/resources/extensions/gsd/tests/crash-recovery-via-db.test.ts +43 -0
- package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +2 -0
- package/src/resources/extensions/gsd/tests/db-authority-regression.test.ts +208 -0
- package/src/resources/extensions/gsd/tests/dispatch-complete-milestone-guard.test.ts +27 -0
- package/src/resources/extensions/gsd/tests/doctor-empty-worktree.test.ts +65 -0
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +11 -0
- package/src/resources/extensions/gsd/tests/guided-discuss-project-prompt-rendering.test.ts +2 -0
- package/src/resources/extensions/gsd/tests/guided-dispatch-root.test.ts +106 -0
- package/src/resources/extensions/gsd/tests/guided-flow-session-isolation.test.ts +59 -11
- package/src/resources/extensions/gsd/tests/guided-tool-contract.test.ts +65 -0
- package/src/resources/extensions/gsd/tests/headless-milestone-parity.test.ts +7 -7
- package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +9 -0
- package/src/resources/extensions/gsd/tests/journal-integration.test.ts +46 -0
- package/src/resources/extensions/gsd/tests/merge-db-cycle.test.ts +179 -0
- package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +26 -18
- package/src/resources/extensions/gsd/tests/pending-autostart-scope.test.ts +29 -5
- package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +2 -0
- package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +59 -0
- package/src/resources/extensions/gsd/tests/provider-errors.test.ts +37 -1
- package/src/resources/extensions/gsd/tests/queue-reorder-ui.test.ts +54 -0
- package/src/resources/extensions/gsd/tests/remediation-completion-guard.test.ts +43 -0
- package/src/resources/extensions/gsd/tests/run-uat-replay-cap.test.ts +2 -3
- package/src/resources/extensions/gsd/tests/smart-entry-routing.test.ts +113 -0
- package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +22 -1
- package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +119 -23
- package/src/resources/extensions/gsd/tests/status-guards.test.ts +13 -1
- package/src/resources/extensions/gsd/tests/validate-milestone-stuck-guard.test.ts +29 -2
- package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +18 -0
- package/src/resources/extensions/gsd/workflow-mcp.ts +18 -1
- /package/dist/web/standalone/.next/static/{q0WYuDVbHeFFYbdd-fei2 → 4dSwdrs__8NwCZggxP9KF}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{q0WYuDVbHeFFYbdd-fei2 → 4dSwdrs__8NwCZggxP9KF}/_ssgManifest.js +0 -0
|
@@ -26,10 +26,11 @@ import { readSessionLockData, isSessionLockProcessAlive } from "./session-lock.j
|
|
|
26
26
|
import { nativeAddAll, nativeCommit, nativeHasCommittedHead, nativeIsRepo, nativeInit } from "./native-git-bridge.js";
|
|
27
27
|
import { isInheritedRepo } from "./repo-identity.js";
|
|
28
28
|
import { ensureGitignore, ensurePreferences, untrackRuntimeFiles } from "./gitignore.js";
|
|
29
|
-
import { loadEffectiveGSDPreferences } from "./preferences.js";
|
|
29
|
+
import { getIsolationMode, loadEffectiveGSDPreferences } from "./preferences.js";
|
|
30
30
|
import { resolveUokFlags } from "./uok/flags.js";
|
|
31
31
|
import { ensurePlanV2Graph, isMissingFinalizedContextResult } from "./uok/plan-v2.js";
|
|
32
32
|
import { detectProjectState, hasGsdBootstrapArtifacts } from "./detection.js";
|
|
33
|
+
import { isFutureMilestoneStatus } from "./status-guards.js";
|
|
33
34
|
import { showProjectInit, offerMigration } from "./init-wizard.js";
|
|
34
35
|
import { validateDirectory } from "./validate-directory.js";
|
|
35
36
|
import { showConfirm } from "../shared/tui.js";
|
|
@@ -43,8 +44,10 @@ import { DISCUSS_TOOLS_ALLOWLIST } from "./constants.js";
|
|
|
43
44
|
import { getWorkflowTransportSupportError, getRequiredWorkflowToolsForGuidedUnit, supportsStructuredQuestions, } from "./workflow-mcp.js";
|
|
44
45
|
import { runPreparation, formatCodebaseBrief, formatPriorContextBrief, } from "./preparation.js";
|
|
45
46
|
import { verifyExpectedArtifact } from "./auto-recovery.js";
|
|
46
|
-
import { createWorkspace, scopeMilestone } from "./workspace.js";
|
|
47
47
|
import { getPendingGate, extractDepthVerificationMilestoneId } from "./bootstrap/write-gate.js";
|
|
48
|
+
import { _getPendingAutoStart, deletePendingAutoStart, hasPendingAutoStart, setPendingAutoStart, } from "./pending-auto-start.js";
|
|
49
|
+
import { clearGuidedUnitContext, setGuidedUnitContext } from "./guided-unit-context.js";
|
|
50
|
+
export { _getPendingAutoStart, clearPendingAutoStart, getDiscussionMilestoneId, setPendingAutoStart, } from "./pending-auto-start.js";
|
|
48
51
|
export function shouldSkipGitBootstrapAfterInit(result) {
|
|
49
52
|
return result.gitEnabled === false;
|
|
50
53
|
}
|
|
@@ -56,6 +59,8 @@ import { deleteRuntimeKv } from "./db/runtime-kv.js";
|
|
|
56
59
|
import { PAUSED_SESSION_KV_KEY } from "./interrupted-session.js";
|
|
57
60
|
import { buildWorkflowDispatchContent } from "./workflow-protocol.js";
|
|
58
61
|
import { isFullGsdToolSurfaceRequested, restoreGsdWorkflowTools, scopeGsdWorkflowToolsForDispatch } from "./bootstrap/register-hooks.js";
|
|
62
|
+
import { resolveActiveTaskChoiceRoute, } from "./smart-entry-routing.js";
|
|
63
|
+
export { resolveGuidedExecuteLaunchMode } from "./smart-entry-routing.js";
|
|
59
64
|
function scheduleAutoStartAfterIdle(ctx, pi, basePath, verboseMode, options, launch = startAutoDetached) {
|
|
60
65
|
const waitForIdle = typeof ctx.waitForIdle === "function"
|
|
61
66
|
? ctx.waitForIdle.bind(ctx)
|
|
@@ -157,7 +162,6 @@ const MAX_PLAN_BLOCKED_RECOVERIES = 3;
|
|
|
157
162
|
// to emit. Accepts any M-prefixed milestone ID (three digits + optional
|
|
158
163
|
// suffix) with optional trailing punctuation.
|
|
159
164
|
const READY_PHRASE_RE = /\bMilestone\s+M\d{3}[A-Z0-9-]*\s+ready\.?/i;
|
|
160
|
-
const pendingAutoStartMap = new Map();
|
|
161
165
|
const pendingDeepProjectSetupMap = new Map();
|
|
162
166
|
const USER_DRIVEN_DEEP_SETUP_UNITS = new Set([
|
|
163
167
|
"discuss-project",
|
|
@@ -183,18 +187,6 @@ This stage is running inside the foreground \`/gsd new-project --deep\` intervie
|
|
|
183
187
|
|
|
184
188
|
- Do NOT call \`ask_user_questions\`, \`AskUserQuestion\`, or ToolSearch to discover user-input tools.
|
|
185
189
|
- Ask one focused round, then stop and wait for the user's normal chat response.`;
|
|
186
|
-
/**
|
|
187
|
-
* Backward-compat bridge: returns a mutable reference to the entry matching
|
|
188
|
-
* basePath, or the sole entry when only one session exists.
|
|
189
|
-
* Exported for testing — internal use only in production code.
|
|
190
|
-
*/
|
|
191
|
-
export function _getPendingAutoStart(basePath) {
|
|
192
|
-
if (basePath)
|
|
193
|
-
return pendingAutoStartMap.get(basePath) ?? null;
|
|
194
|
-
if (pendingAutoStartMap.size === 1)
|
|
195
|
-
return pendingAutoStartMap.values().next().value;
|
|
196
|
-
return null;
|
|
197
|
-
}
|
|
198
190
|
function hasNestedFileOrSymlink(dir) {
|
|
199
191
|
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
200
192
|
if (entry.isFile() || entry.isSymbolicLink())
|
|
@@ -228,28 +220,6 @@ function clearEmptyLegacyDeepSetupPseudoMilestones(basePath, entries) {
|
|
|
228
220
|
}
|
|
229
221
|
return remaining;
|
|
230
222
|
}
|
|
231
|
-
/**
|
|
232
|
-
* Store pending auto-start state for a project.
|
|
233
|
-
* Exported for testing (#2985).
|
|
234
|
-
*/
|
|
235
|
-
export function setPendingAutoStart(basePath, entry) {
|
|
236
|
-
const ws = createWorkspace(entry.basePath);
|
|
237
|
-
const scope = scopeMilestone(ws, entry.milestoneId);
|
|
238
|
-
pendingAutoStartMap.set(basePath, { createdAt: Date.now(), planBlockedRecoveryCount: 0, ...entry, scope });
|
|
239
|
-
}
|
|
240
|
-
/**
|
|
241
|
-
* Clear pending auto-start state.
|
|
242
|
-
* If basePath is given, clears only that project. Otherwise clears all.
|
|
243
|
-
* Exported for testing (#2985).
|
|
244
|
-
*/
|
|
245
|
-
export function clearPendingAutoStart(basePath) {
|
|
246
|
-
if (basePath) {
|
|
247
|
-
pendingAutoStartMap.delete(basePath);
|
|
248
|
-
}
|
|
249
|
-
else {
|
|
250
|
-
pendingAutoStartMap.clear();
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
223
|
export function clearPendingDeepProjectSetup(basePath) {
|
|
254
224
|
if (basePath) {
|
|
255
225
|
pendingDeepProjectSetupMap.delete(basePath);
|
|
@@ -258,22 +228,6 @@ export function clearPendingDeepProjectSetup(basePath) {
|
|
|
258
228
|
pendingDeepProjectSetupMap.clear();
|
|
259
229
|
}
|
|
260
230
|
}
|
|
261
|
-
/**
|
|
262
|
-
* Returns the milestoneId being discussed for the given project.
|
|
263
|
-
* When basePath is omitted and only one session is active, returns that
|
|
264
|
-
* session's milestoneId for backward compatibility. Returns null when
|
|
265
|
-
* multiple sessions exist and basePath is not specified (#2985 Bug 4).
|
|
266
|
-
*/
|
|
267
|
-
export function getDiscussionMilestoneId(basePath) {
|
|
268
|
-
if (basePath) {
|
|
269
|
-
return pendingAutoStartMap.get(basePath)?.milestoneId ?? null;
|
|
270
|
-
}
|
|
271
|
-
// Backward compat: return the sole entry's milestoneId, or null if ambiguous
|
|
272
|
-
if (pendingAutoStartMap.size === 1) {
|
|
273
|
-
return pendingAutoStartMap.values().next().value.milestoneId;
|
|
274
|
-
}
|
|
275
|
-
return null;
|
|
276
|
-
}
|
|
277
231
|
function _getPendingDeepProjectSetup(basePath) {
|
|
278
232
|
if (basePath)
|
|
279
233
|
return pendingDeepProjectSetupMap.get(basePath) ?? null;
|
|
@@ -399,12 +353,12 @@ async function dispatchNextDeepProjectSetupStage(entry) {
|
|
|
399
353
|
entry.currentUnitType = result.unitType;
|
|
400
354
|
entry.currentUnitId = result.unitId;
|
|
401
355
|
entry.createdAt = Date.now();
|
|
402
|
-
await dispatchWorkflow(entry.pi, `${result.prompt}\n\n${FOREGROUND_DEEP_SETUP_QUESTION_POLICY}`, "gsd-run", entry.ctx, result.unitType);
|
|
356
|
+
await dispatchWorkflow(entry.pi, `${result.prompt}\n\n${FOREGROUND_DEEP_SETUP_QUESTION_POLICY}`, "gsd-run", entry.ctx, result.unitType, { basePath: entry.basePath });
|
|
403
357
|
return true;
|
|
404
358
|
}
|
|
405
359
|
/** Called from agent_end to check if auto-mode should start after discuss */
|
|
406
|
-
export function checkAutoStartAfterDiscuss() {
|
|
407
|
-
const entry = _getPendingAutoStart();
|
|
360
|
+
export function checkAutoStartAfterDiscuss(lookupBasePath) {
|
|
361
|
+
const entry = _getPendingAutoStart(lookupBasePath);
|
|
408
362
|
if (!entry)
|
|
409
363
|
return false;
|
|
410
364
|
const { ctx, pi, basePath, milestoneId, step } = entry;
|
|
@@ -569,7 +523,7 @@ export function checkAutoStartAfterDiscuss() {
|
|
|
569
523
|
return false;
|
|
570
524
|
}
|
|
571
525
|
}
|
|
572
|
-
|
|
526
|
+
deletePendingAutoStart(basePath);
|
|
573
527
|
ctx.ui.notify(`Milestone ${milestoneId} ready.`, "success");
|
|
574
528
|
scheduleAutoStartAfterIdle(ctx, pi, basePath, false, { step });
|
|
575
529
|
return true;
|
|
@@ -641,8 +595,8 @@ function hasToolUse(msg) {
|
|
|
641
595
|
* Returns true when a nudge (or give-up) was emitted, signaling the caller to
|
|
642
596
|
* skip `resolveAgentEnd`.
|
|
643
597
|
*/
|
|
644
|
-
export function maybeHandleReadyPhraseWithoutFiles(event) {
|
|
645
|
-
const entry = _getPendingAutoStart();
|
|
598
|
+
export function maybeHandleReadyPhraseWithoutFiles(event, lookupBasePath) {
|
|
599
|
+
const entry = _getPendingAutoStart(lookupBasePath);
|
|
646
600
|
if (!entry)
|
|
647
601
|
return false;
|
|
648
602
|
const { ctx, pi, basePath, milestoneId } = entry;
|
|
@@ -684,7 +638,7 @@ export function maybeHandleReadyPhraseWithoutFiles(event) {
|
|
|
684
638
|
if (entry.readyRejectCount > MAX_READY_REJECTS) {
|
|
685
639
|
// Give up: clear state and tell the user to re-run /gsd. Avoids an
|
|
686
640
|
// infinite nudge loop when the LLM never produces the writes.
|
|
687
|
-
|
|
641
|
+
deletePendingAutoStart(basePath);
|
|
688
642
|
ctx.ui.notify(`Milestone ${milestoneId}: LLM signaled "ready" ${entry.readyRejectCount} times without writing files. ` +
|
|
689
643
|
`Stopping auto-nudge. Run /gsd to try again.`, "error");
|
|
690
644
|
return true;
|
|
@@ -747,10 +701,10 @@ export function resetEmptyTurnCounter(basePath) {
|
|
|
747
701
|
else
|
|
748
702
|
emptyTurnCounterByBase.clear();
|
|
749
703
|
}
|
|
750
|
-
export function maybeHandleEmptyIntentTurn(event, isAuto) {
|
|
704
|
+
export function maybeHandleEmptyIntentTurn(event, isAuto, lookupBasePath) {
|
|
751
705
|
// Gate: only fire when there is system-driven work in flight. Interactive
|
|
752
706
|
// /gsd discuss (user-driven) produces legitimate text-only turns.
|
|
753
|
-
if (!isAuto &&
|
|
707
|
+
if (!isAuto && !hasPendingAutoStart(lookupBasePath))
|
|
754
708
|
return false;
|
|
755
709
|
const lastMsg = event.messages[event.messages.length - 1];
|
|
756
710
|
if (!lastMsg)
|
|
@@ -778,7 +732,7 @@ export function maybeHandleEmptyIntentTurn(event, isAuto) {
|
|
|
778
732
|
return false;
|
|
779
733
|
// Resolve the target basePath + pi for injection. Prefer the pending
|
|
780
734
|
// autostart entry (discuss flow); otherwise we cannot inject.
|
|
781
|
-
const entry = _getPendingAutoStart();
|
|
735
|
+
const entry = _getPendingAutoStart(lookupBasePath);
|
|
782
736
|
if (!entry)
|
|
783
737
|
return false;
|
|
784
738
|
const { ctx, pi, basePath } = entry;
|
|
@@ -817,7 +771,9 @@ function parseMilestoneSequenceFromProject(content) {
|
|
|
817
771
|
}
|
|
818
772
|
return ids;
|
|
819
773
|
}
|
|
820
|
-
|
|
774
|
+
export function resolveGuidedDispatchProjectRoot(basePath) {
|
|
775
|
+
return basePath ?? process.cwd();
|
|
776
|
+
}
|
|
821
777
|
/**
|
|
822
778
|
* Read GSD-WORKFLOW.md and dispatch it to the LLM with a contextual note.
|
|
823
779
|
* This is the only way the wizard triggers work — everything else is the LLM's job.
|
|
@@ -827,12 +783,17 @@ function parseMilestoneSequenceFromProject(content) {
|
|
|
827
783
|
* dispatching. This ensures guided-flow dispatches respect the same
|
|
828
784
|
* per-phase model preferences that auto-mode uses.
|
|
829
785
|
*/
|
|
830
|
-
async function dispatchWorkflow(pi, note, customType = "gsd-run", ctx, unitType) {
|
|
786
|
+
async function dispatchWorkflow(pi, note, customType = "gsd-run", ctx, unitType, options) {
|
|
787
|
+
const resolvedOptions = options ?? {};
|
|
788
|
+
const projectRoot = resolveGuidedDispatchProjectRoot(resolvedOptions.basePath);
|
|
789
|
+
const loadPreferences = resolvedOptions.deps?.loadPreferences ?? loadEffectiveGSDPreferences;
|
|
790
|
+
const selectModel = resolvedOptions.deps?.selectModel ?? selectAndApplyModel;
|
|
791
|
+
const getTransportSupportError = resolvedOptions.deps?.getTransportSupportError ?? getWorkflowTransportSupportError;
|
|
831
792
|
// Route through the dynamic routing pipeline (complexity classification,
|
|
832
793
|
// tier downgrade, fallback chains) — same path as auto-mode dispatches (#2958).
|
|
833
794
|
if (ctx && unitType) {
|
|
834
|
-
const prefs =
|
|
835
|
-
const result = await
|
|
795
|
+
const prefs = loadPreferences(projectRoot)?.preferences;
|
|
796
|
+
const result = await selectModel(ctx, pi, unitType, /* unitId */ "", projectRoot, prefs, /* verbose */ false, /* autoModeStartModel */ null,
|
|
836
797
|
/* retryContext */ undefined, /* isAutoMode */ false);
|
|
837
798
|
if (result.appliedModel) {
|
|
838
799
|
debugLog("guided-flow-model-applied", {
|
|
@@ -841,8 +802,8 @@ async function dispatchWorkflow(pi, note, customType = "gsd-run", ctx, unitType)
|
|
|
841
802
|
routing: result.routing,
|
|
842
803
|
});
|
|
843
804
|
}
|
|
844
|
-
const compatibilityError =
|
|
845
|
-
projectRoot
|
|
805
|
+
const compatibilityError = getTransportSupportError(result.appliedModel?.provider ?? ctx.model?.provider, getRequiredWorkflowToolsForGuidedUnit(unitType), {
|
|
806
|
+
projectRoot,
|
|
846
807
|
surface: "guided flow",
|
|
847
808
|
unitType,
|
|
848
809
|
authMode: result.appliedModel?.provider
|
|
@@ -851,6 +812,7 @@ async function dispatchWorkflow(pi, note, customType = "gsd-run", ctx, unitType)
|
|
|
851
812
|
? ctx.modelRegistry.getProviderAuthMode(ctx.model.provider)
|
|
852
813
|
: undefined,
|
|
853
814
|
baseUrl: result.appliedModel?.baseUrl ?? ctx.model?.baseUrl,
|
|
815
|
+
activeTools: typeof pi.getActiveTools === "function" ? pi.getActiveTools() : [],
|
|
854
816
|
});
|
|
855
817
|
if (compatibilityError) {
|
|
856
818
|
ctx.ui.notify(compatibilityError, "error");
|
|
@@ -894,11 +856,19 @@ async function dispatchWorkflow(pi, note, customType = "gsd-run", ctx, unitType)
|
|
|
894
856
|
}
|
|
895
857
|
const workflowPath = process.env.GSD_WORKFLOW_PATH ?? join(gsdHome(), "agent", "GSD-WORKFLOW.md");
|
|
896
858
|
const workflow = readFileSync(workflowPath, "utf-8");
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
859
|
+
if (unitType)
|
|
860
|
+
setGuidedUnitContext(projectRoot, unitType);
|
|
861
|
+
try {
|
|
862
|
+
pi.sendMessage({
|
|
863
|
+
customType,
|
|
864
|
+
content: buildWorkflowDispatchContent({ workflow, workflowPath, task: note }),
|
|
865
|
+
display: false,
|
|
866
|
+
}, { triggerTurn: true });
|
|
867
|
+
}
|
|
868
|
+
catch (err) {
|
|
869
|
+
clearGuidedUnitContext(projectRoot);
|
|
870
|
+
throw err;
|
|
871
|
+
}
|
|
902
872
|
}
|
|
903
873
|
finally {
|
|
904
874
|
// Restore full tool set after the message is queued. The LLM turn has
|
|
@@ -1086,7 +1056,7 @@ export async function showHeadlessMilestoneCreation(ctx, pi, basePath, seedConte
|
|
|
1086
1056
|
// model/tool routing to skip discuss-flow tool scoping and
|
|
1087
1057
|
// `checkAutoStartAfterDiscuss` guardrails that rely on the
|
|
1088
1058
|
// "discuss-"-prefixed unitType.
|
|
1089
|
-
await dispatchWorkflow(pi, prompt, "gsd-run", ctx, "discuss-milestone");
|
|
1059
|
+
await dispatchWorkflow(pi, prompt, "gsd-run", ctx, "discuss-milestone", { basePath });
|
|
1090
1060
|
}
|
|
1091
1061
|
// ─── Discuss Flow ─────────────────────────────────────────────────────────────
|
|
1092
1062
|
/**
|
|
@@ -1196,7 +1166,7 @@ export async function showDiscuss(ctx, pi, basePath) {
|
|
|
1196
1166
|
// No active milestone (or corrupted milestone with undefined id) —
|
|
1197
1167
|
// check for pending milestones to discuss instead
|
|
1198
1168
|
if (!state.activeMilestone?.id) {
|
|
1199
|
-
const pendingMilestones = state.registry.filter(m => m.status
|
|
1169
|
+
const pendingMilestones = state.registry.filter(m => isFutureMilestoneStatus(m.status));
|
|
1200
1170
|
if (pendingMilestones.length === 0) {
|
|
1201
1171
|
ctx.ui.notify("No active milestone. Run /gsd to create one first.", "warning");
|
|
1202
1172
|
return;
|
|
@@ -1247,7 +1217,7 @@ export async function showDiscuss(ctx, pi, basePath) {
|
|
|
1247
1217
|
? `${basePrompt}\n\n## Prior Discussion (Draft Seed)\n\n${draftContent}`
|
|
1248
1218
|
: basePrompt;
|
|
1249
1219
|
setPendingAutoStart(basePath, { ctx, pi, basePath, milestoneId: mid, step: false });
|
|
1250
|
-
await dispatchWorkflow(pi, seed, "gsd-discuss", ctx, "discuss-milestone");
|
|
1220
|
+
await dispatchWorkflow(pi, seed, "gsd-discuss", ctx, "discuss-milestone", { basePath });
|
|
1251
1221
|
}
|
|
1252
1222
|
else if (choice === "discuss_fresh") {
|
|
1253
1223
|
const discussMilestoneTemplates = inlineTemplate("context", "Context");
|
|
@@ -1258,7 +1228,7 @@ export async function showDiscuss(ctx, pi, basePath) {
|
|
|
1258
1228
|
milestoneId: mid, milestoneTitle, inlinedTemplates: discussMilestoneTemplates, structuredQuestionsAvailable,
|
|
1259
1229
|
commitInstruction: buildDocsCommitInstruction(`docs(${mid}): milestone context from discuss`),
|
|
1260
1230
|
fastPathInstruction: "",
|
|
1261
|
-
}), "gsd-discuss", ctx, "discuss-milestone");
|
|
1231
|
+
}), "gsd-discuss", ctx, "discuss-milestone", { basePath });
|
|
1262
1232
|
}
|
|
1263
1233
|
else if (choice === "skip_milestone") {
|
|
1264
1234
|
const { ensureDbOpen } = await import("./bootstrap/dynamic-tools.js");
|
|
@@ -1267,7 +1237,7 @@ export async function showDiscuss(ctx, pi, basePath) {
|
|
|
1267
1237
|
const uniqueMilestoneIds = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
|
|
1268
1238
|
const nextId = nextMilestoneIdReserved(milestoneIds, uniqueMilestoneIds, basePath);
|
|
1269
1239
|
setPendingAutoStart(basePath, { ctx, pi, basePath, milestoneId: nextId, step: false });
|
|
1270
|
-
await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, pi, nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone");
|
|
1240
|
+
await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, pi, nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone", { basePath });
|
|
1271
1241
|
}
|
|
1272
1242
|
return;
|
|
1273
1243
|
}
|
|
@@ -1301,7 +1271,7 @@ export async function showDiscuss(ctx, pi, basePath) {
|
|
|
1301
1271
|
const pendingSlices = normSlices.filter(s => !s.done);
|
|
1302
1272
|
if (pendingSlices.length === 0) {
|
|
1303
1273
|
// All slices complete — but queued milestones may still need discussion (#3150)
|
|
1304
|
-
const pendingMilestones = state.registry.filter(m => m.status
|
|
1274
|
+
const pendingMilestones = state.registry.filter(m => isFutureMilestoneStatus(m.status));
|
|
1305
1275
|
if (pendingMilestones.length > 0) {
|
|
1306
1276
|
await showDiscussQueuedMilestone(ctx, pi, basePath, pendingMilestones);
|
|
1307
1277
|
return;
|
|
@@ -1322,7 +1292,7 @@ export async function showDiscuss(ctx, pi, basePath) {
|
|
|
1322
1292
|
// If all pending slices are discussed, check for queued milestones before exiting (#3150)
|
|
1323
1293
|
const allDiscussed = pendingSlices.every(s => discussedMap.get(s.id));
|
|
1324
1294
|
if (allDiscussed) {
|
|
1325
|
-
const pendingMilestones = state.registry.filter(m => m.status
|
|
1295
|
+
const pendingMilestones = state.registry.filter(m => isFutureMilestoneStatus(m.status));
|
|
1326
1296
|
if (pendingMilestones.length > 0) {
|
|
1327
1297
|
await showDiscussQueuedMilestone(ctx, pi, basePath, pendingMilestones);
|
|
1328
1298
|
return;
|
|
@@ -1353,7 +1323,7 @@ export async function showDiscuss(ctx, pi, basePath) {
|
|
|
1353
1323
|
};
|
|
1354
1324
|
});
|
|
1355
1325
|
// Offer access to queued milestones when any exist
|
|
1356
|
-
const pendingMilestones = state.registry.filter(m => m.status
|
|
1326
|
+
const pendingMilestones = state.registry.filter(m => isFutureMilestoneStatus(m.status));
|
|
1357
1327
|
if (pendingMilestones.length > 0) {
|
|
1358
1328
|
actions.push({
|
|
1359
1329
|
id: "discuss_queued_milestone",
|
|
@@ -1399,7 +1369,7 @@ export async function showDiscuss(ctx, pi, basePath) {
|
|
|
1399
1369
|
}
|
|
1400
1370
|
const sqAvail = getStructuredQuestionsAvailability(pi, ctx);
|
|
1401
1371
|
const prompt = await buildDiscussSlicePrompt(mid, chosen.id, chosen.title, basePath, { rediscuss: isRediscuss, structuredQuestionsAvailable: sqAvail });
|
|
1402
|
-
await dispatchWorkflow(pi, prompt, "gsd-discuss", ctx, "discuss-slice");
|
|
1372
|
+
await dispatchWorkflow(pi, prompt, "gsd-discuss", ctx, "discuss-slice", { basePath });
|
|
1403
1373
|
// Wait for the discuss session to finish, then loop back to the picker
|
|
1404
1374
|
await ctx.waitForIdle();
|
|
1405
1375
|
invalidateAllCaches();
|
|
@@ -1417,10 +1387,11 @@ async function showDiscussQueuedMilestone(ctx, pi, basePath, pendingMilestones)
|
|
|
1417
1387
|
const hasContext = !!resolveMilestoneFile(basePath, m.id, "CONTEXT");
|
|
1418
1388
|
const hasDraft = !hasContext && !!resolveMilestoneFile(basePath, m.id, "CONTEXT-DRAFT");
|
|
1419
1389
|
const contextStatus = hasContext ? "context ✓" : hasDraft ? "draft context" : "no context yet";
|
|
1390
|
+
const statusLabel = m.status === "planned" ? "planned" : "queued";
|
|
1420
1391
|
return {
|
|
1421
1392
|
id: m.id,
|
|
1422
1393
|
label: `${m.id}: ${m.title}`,
|
|
1423
|
-
description: `[
|
|
1394
|
+
description: `[${statusLabel}] · ${contextStatus}`,
|
|
1424
1395
|
recommended: i === 0,
|
|
1425
1396
|
};
|
|
1426
1397
|
});
|
|
@@ -1501,7 +1472,7 @@ async function dispatchDiscussForMilestone(ctx, pi, basePath, mid, milestoneTitl
|
|
|
1501
1472
|
const prompt = draftContent
|
|
1502
1473
|
? `${basePrompt}\n\n## Prior Discussion (Draft Seed)\n\n${draftContent}`
|
|
1503
1474
|
: basePrompt;
|
|
1504
|
-
await dispatchWorkflow(pi, prompt, "gsd-discuss", ctx, "discuss-milestone");
|
|
1475
|
+
await dispatchWorkflow(pi, prompt, "gsd-discuss", ctx, "discuss-milestone", { basePath });
|
|
1505
1476
|
}
|
|
1506
1477
|
// ─── Smart Entry Point ────────────────────────────────────────────────────────
|
|
1507
1478
|
/**
|
|
@@ -1627,7 +1598,7 @@ async function handleMilestoneActions(ctx, pi, basePath, milestoneId, milestoneT
|
|
|
1627
1598
|
const uniqueMilestoneIds = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
|
|
1628
1599
|
const nextId = nextMilestoneIdReserved(milestoneIds, uniqueMilestoneIds, basePath);
|
|
1629
1600
|
setPendingAutoStart(basePath, { ctx, pi, basePath, milestoneId: nextId, step: stepMode });
|
|
1630
|
-
await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, pi, nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone");
|
|
1601
|
+
await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, pi, nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone", { basePath });
|
|
1631
1602
|
return true;
|
|
1632
1603
|
}
|
|
1633
1604
|
// "back" or null
|
|
@@ -1758,16 +1729,17 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
|
|
|
1758
1729
|
}
|
|
1759
1730
|
if (interrupted.classification !== "recoverable") {
|
|
1760
1731
|
try {
|
|
1761
|
-
const {
|
|
1762
|
-
const result = await
|
|
1763
|
-
if (result.action === "
|
|
1764
|
-
ctx.ui.notify(
|
|
1732
|
+
const { checkMarkdownHierarchyAgainstDb } = await import("./migration-auto-check.js");
|
|
1733
|
+
const result = await checkMarkdownHierarchyAgainstDb(basePath);
|
|
1734
|
+
if (result.action === "recovery-required") {
|
|
1735
|
+
ctx.ui.notify(result.message ??
|
|
1736
|
+
`Markdown planning artifacts do not match the authoritative DB. Run \`${result.recoveryCommand ?? "gsd recover"}\` to import markdown explicitly.`, "warning");
|
|
1765
1737
|
}
|
|
1766
1738
|
}
|
|
1767
1739
|
catch (err) {
|
|
1768
1740
|
const message = err instanceof Error ? err.message : String(err);
|
|
1769
|
-
ctx.ui.notify(`GSD could not
|
|
1770
|
-
logWarning("guided", `planning state
|
|
1741
|
+
ctx.ui.notify(`GSD could not compare markdown planning artifacts with gsd.db: ${message}`, "warning");
|
|
1742
|
+
logWarning("guided", `planning state DB/markdown comparison failed: ${message}`, { file: "guided-flow.ts" });
|
|
1771
1743
|
}
|
|
1772
1744
|
}
|
|
1773
1745
|
// Always derive from the project root — the assessment may have derived
|
|
@@ -1805,17 +1777,17 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
|
|
|
1805
1777
|
// Both /gsd and /gsd auto reach this branch when no milestone exists yet.
|
|
1806
1778
|
// Without this guard, every subsequent /gsd call overwrites the pending auto-start
|
|
1807
1779
|
// and fires another dispatchWorkflow, resetting the conversation mid-interview.
|
|
1808
|
-
if (
|
|
1780
|
+
if (hasPendingAutoStart(basePath)) {
|
|
1809
1781
|
// #3274: If /clear interrupted the discussion, the pending entry is stale.
|
|
1810
1782
|
// Detect staleness: no manifest, no milestone CONTEXT artifact, AND entry is older than
|
|
1811
1783
|
// 30s (avoids race between .set() and LLM writing first artifact).
|
|
1812
|
-
const entry =
|
|
1784
|
+
const entry = _getPendingAutoStart(basePath);
|
|
1813
1785
|
const ageMs = Date.now() - (entry.createdAt || 0);
|
|
1814
1786
|
const manifestExists = existsSync(join(gsdRoot(basePath), "DISCUSSION-MANIFEST.json"));
|
|
1815
1787
|
const milestoneHasContext = !!resolveMilestoneFile(basePath, entry.milestoneId, "CONTEXT");
|
|
1816
1788
|
if (!manifestExists && !milestoneHasContext && ageMs > 30_000) {
|
|
1817
1789
|
// Stale entry from an interrupted discussion — clear and continue
|
|
1818
|
-
|
|
1790
|
+
deletePendingAutoStart(basePath);
|
|
1819
1791
|
}
|
|
1820
1792
|
else {
|
|
1821
1793
|
ctx.ui.notify("Discussion already in progress — answer the question above to continue.", "info");
|
|
@@ -1849,7 +1821,7 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
|
|
|
1849
1821
|
// First ever — skip wizard, just ask directly
|
|
1850
1822
|
ctx.ui.setStatus("gsd-step", "New Milestone · answer the questions above to plan");
|
|
1851
1823
|
setPendingAutoStart(basePath, { ctx, pi, basePath, milestoneId: nextId, step: stepMode });
|
|
1852
|
-
await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, pi, nextId, `New project, milestone ${nextId}. Do NOT read or explore .gsd/ — it's empty scaffolding.`, basePath), "gsd-run", ctx, "discuss-milestone");
|
|
1824
|
+
await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, pi, nextId, `New project, milestone ${nextId}. Do NOT read or explore .gsd/ — it's empty scaffolding.`, basePath), "gsd-run", ctx, "discuss-milestone", { basePath });
|
|
1853
1825
|
}
|
|
1854
1826
|
else {
|
|
1855
1827
|
const choice = await showNextAction(ctx, {
|
|
@@ -1876,7 +1848,7 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
|
|
|
1876
1848
|
else if (choice === "new_milestone") {
|
|
1877
1849
|
ctx.ui.setStatus("gsd-step", "New Milestone · answer the questions above to plan");
|
|
1878
1850
|
setPendingAutoStart(basePath, { ctx, pi, basePath, milestoneId: nextId, step: stepMode });
|
|
1879
|
-
await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, pi, nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone");
|
|
1851
|
+
await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, pi, nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone", { basePath });
|
|
1880
1852
|
}
|
|
1881
1853
|
}
|
|
1882
1854
|
return;
|
|
@@ -1885,7 +1857,7 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
|
|
|
1885
1857
|
const milestoneTitle = state.activeMilestone.title;
|
|
1886
1858
|
if (planV2GateDecision === "recover-missing-context") {
|
|
1887
1859
|
setPendingAutoStart(basePath, { ctx, pi, basePath, milestoneId, step: stepMode });
|
|
1888
|
-
await dispatchWorkflow(pi, await buildDiscussMilestonePrompt(milestoneId, milestoneTitle, basePath, getStructuredQuestionsAvailability(pi, ctx)), "gsd-discuss", ctx, "discuss-milestone");
|
|
1860
|
+
await dispatchWorkflow(pi, await buildDiscussMilestonePrompt(milestoneId, milestoneTitle, basePath, getStructuredQuestionsAvailability(pi, ctx)), "gsd-discuss", ctx, "discuss-milestone", { basePath });
|
|
1889
1861
|
return;
|
|
1890
1862
|
}
|
|
1891
1863
|
// ── All milestones complete → New milestone ──────────────────────────
|
|
@@ -1921,7 +1893,7 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
|
|
|
1921
1893
|
const uniqueMilestoneIds = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
|
|
1922
1894
|
const nextId = nextMilestoneIdReserved(milestoneIds, uniqueMilestoneIds, basePath);
|
|
1923
1895
|
setPendingAutoStart(basePath, { ctx, pi, basePath, milestoneId: nextId, step: stepMode });
|
|
1924
|
-
await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, pi, nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone");
|
|
1896
|
+
await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, pi, nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone", { basePath });
|
|
1925
1897
|
}
|
|
1926
1898
|
else if (choice === "status") {
|
|
1927
1899
|
const { fireStatusViaCommand } = await import("./commands.js");
|
|
@@ -1969,7 +1941,7 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
|
|
|
1969
1941
|
? `${basePrompt}\n\n## Prior Discussion (Draft Seed)\n\n${draftContent}`
|
|
1970
1942
|
: basePrompt;
|
|
1971
1943
|
setPendingAutoStart(basePath, { ctx, pi, basePath, milestoneId, step: stepMode });
|
|
1972
|
-
await dispatchWorkflow(pi, seed, "gsd-discuss", ctx, "discuss-milestone");
|
|
1944
|
+
await dispatchWorkflow(pi, seed, "gsd-discuss", ctx, "discuss-milestone", { basePath });
|
|
1973
1945
|
}
|
|
1974
1946
|
else if (choice === "discuss_fresh") {
|
|
1975
1947
|
const discussMilestoneTemplates = inlineTemplate("context", "Context");
|
|
@@ -1980,14 +1952,14 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
|
|
|
1980
1952
|
milestoneId, milestoneTitle, inlinedTemplates: discussMilestoneTemplates, structuredQuestionsAvailable,
|
|
1981
1953
|
commitInstruction: buildDocsCommitInstruction(`docs(${milestoneId}): milestone context from discuss`),
|
|
1982
1954
|
fastPathInstruction: "",
|
|
1983
|
-
}), "gsd-discuss", ctx, "discuss-milestone");
|
|
1955
|
+
}), "gsd-discuss", ctx, "discuss-milestone", { basePath });
|
|
1984
1956
|
}
|
|
1985
1957
|
else if (choice === "skip_milestone") {
|
|
1986
1958
|
const milestoneIds = findMilestoneIds(basePath);
|
|
1987
1959
|
const uniqueMilestoneIds = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
|
|
1988
1960
|
const nextId = nextMilestoneIdReserved(milestoneIds, uniqueMilestoneIds, basePath);
|
|
1989
1961
|
setPendingAutoStart(basePath, { ctx, pi, basePath, milestoneId: nextId, step: stepMode });
|
|
1990
|
-
await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, pi, nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone");
|
|
1962
|
+
await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, pi, nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone", { basePath });
|
|
1991
1963
|
}
|
|
1992
1964
|
return;
|
|
1993
1965
|
}
|
|
@@ -2051,7 +2023,7 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
|
|
|
2051
2023
|
else if (choice === "plan") {
|
|
2052
2024
|
ctx.ui.setStatus("gsd-step", "Planning Milestone · decomposing into slices");
|
|
2053
2025
|
setPendingAutoStart(basePath, { ctx, pi, basePath, milestoneId, step: stepMode });
|
|
2054
|
-
await dispatchWorkflow(pi, await buildPlanMilestonePrompt(milestoneId, milestoneTitle, basePath), "gsd-run", ctx, "plan-milestone");
|
|
2026
|
+
await dispatchWorkflow(pi, await buildPlanMilestonePrompt(milestoneId, milestoneTitle, basePath), "gsd-run", ctx, "plan-milestone", { basePath });
|
|
2055
2027
|
}
|
|
2056
2028
|
else if (choice === "discuss") {
|
|
2057
2029
|
const discussMilestoneTemplates = inlineTemplate("context", "Context");
|
|
@@ -2061,14 +2033,14 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
|
|
|
2061
2033
|
milestoneId, milestoneTitle, inlinedTemplates: discussMilestoneTemplates, structuredQuestionsAvailable,
|
|
2062
2034
|
commitInstruction: buildDocsCommitInstruction(`docs(${milestoneId}): milestone context from discuss`),
|
|
2063
2035
|
fastPathInstruction: "",
|
|
2064
|
-
}), "gsd-run", ctx, "discuss-milestone");
|
|
2036
|
+
}), "gsd-run", ctx, "discuss-milestone", { basePath });
|
|
2065
2037
|
}
|
|
2066
2038
|
else if (choice === "skip_milestone") {
|
|
2067
2039
|
const milestoneIds = findMilestoneIds(basePath);
|
|
2068
2040
|
const uniqueMilestoneIds = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
|
|
2069
2041
|
const nextId = nextMilestoneIdReserved(milestoneIds, uniqueMilestoneIds, basePath);
|
|
2070
2042
|
setPendingAutoStart(basePath, { ctx, pi, basePath, milestoneId: nextId, step: stepMode });
|
|
2071
|
-
await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, pi, nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone");
|
|
2043
|
+
await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, pi, nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone", { basePath });
|
|
2072
2044
|
}
|
|
2073
2045
|
else if (choice === "discard_milestone") {
|
|
2074
2046
|
const confirmed = await showConfirm(ctx, {
|
|
@@ -2176,11 +2148,11 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
|
|
|
2176
2148
|
});
|
|
2177
2149
|
if (choice === "plan") {
|
|
2178
2150
|
ctx.ui.setStatus("gsd-step", "Slice Planning · answer the questions above");
|
|
2179
|
-
await dispatchWorkflow(pi, await buildPlanSlicePrompt(milestoneId, milestoneTitle, sliceId, sliceTitle, basePath), "gsd-run", ctx, "plan-slice");
|
|
2151
|
+
await dispatchWorkflow(pi, await buildPlanSlicePrompt(milestoneId, milestoneTitle, sliceId, sliceTitle, basePath), "gsd-run", ctx, "plan-slice", { basePath });
|
|
2180
2152
|
}
|
|
2181
2153
|
else if (choice === "discuss") {
|
|
2182
2154
|
const sqAvail = getStructuredQuestionsAvailability(pi, ctx);
|
|
2183
|
-
await dispatchWorkflow(pi, await buildDiscussSlicePrompt(milestoneId, sliceId, sliceTitle, basePath, { rediscuss: hasContext, structuredQuestionsAvailable: sqAvail }), "gsd-run", ctx, "discuss-slice");
|
|
2155
|
+
await dispatchWorkflow(pi, await buildDiscussSlicePrompt(milestoneId, sliceId, sliceTitle, basePath, { rediscuss: hasContext, structuredQuestionsAvailable: sqAvail }), "gsd-run", ctx, "discuss-slice", { basePath });
|
|
2184
2156
|
}
|
|
2185
2157
|
else if (choice === "research") {
|
|
2186
2158
|
const researchTemplates = inlineTemplate("research", "Research");
|
|
@@ -2196,7 +2168,7 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
|
|
|
2196
2168
|
sliceTitle,
|
|
2197
2169
|
extraContext: [researchTemplates],
|
|
2198
2170
|
}),
|
|
2199
|
-
}), "gsd-run", ctx, "research-slice");
|
|
2171
|
+
}), "gsd-run", ctx, "research-slice", { basePath });
|
|
2200
2172
|
}
|
|
2201
2173
|
else if (choice === "status") {
|
|
2202
2174
|
const { fireStatusViaCommand } = await import("./commands.js");
|
|
@@ -2236,7 +2208,7 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
|
|
|
2236
2208
|
});
|
|
2237
2209
|
if (choice === "complete") {
|
|
2238
2210
|
ctx.ui.setStatus("gsd-step", "Completing Slice · review changes above");
|
|
2239
|
-
await dispatchWorkflow(pi, await buildCompleteSlicePrompt(milestoneId, milestoneTitle, sliceId, sliceTitle, basePath), "gsd-run", ctx, "complete-slice");
|
|
2211
|
+
await dispatchWorkflow(pi, await buildCompleteSlicePrompt(milestoneId, milestoneTitle, sliceId, sliceTitle, basePath), "gsd-run", ctx, "complete-slice", { basePath });
|
|
2240
2212
|
}
|
|
2241
2213
|
else if (choice === "status") {
|
|
2242
2214
|
const { fireStatusViaCommand } = await import("./commands.js");
|
|
@@ -2291,11 +2263,18 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
|
|
|
2291
2263
|
],
|
|
2292
2264
|
notYetMessage: "Run /gsd when ready.",
|
|
2293
2265
|
});
|
|
2294
|
-
if (choice === "
|
|
2295
|
-
|
|
2266
|
+
if (choice === "not_yet")
|
|
2267
|
+
return;
|
|
2268
|
+
const route = resolveActiveTaskChoiceRoute({
|
|
2269
|
+
choice: choice,
|
|
2270
|
+
isolationMode: getIsolationMode(basePath),
|
|
2271
|
+
milestoneId,
|
|
2272
|
+
});
|
|
2273
|
+
if (route.kind === "auto-bootstrap") {
|
|
2274
|
+
startAutoDetached(ctx, pi, basePath, route.verboseMode, route.options);
|
|
2296
2275
|
return;
|
|
2297
2276
|
}
|
|
2298
|
-
if (
|
|
2277
|
+
if (route.kind === "guided-dispatch") {
|
|
2299
2278
|
ctx.ui.setStatus("gsd-step", "Executing Task · follow progress above");
|
|
2300
2279
|
if (hasInterrupted) {
|
|
2301
2280
|
await dispatchWorkflow(pi, loadPrompt("guided-resume-task", {
|
|
@@ -2308,17 +2287,17 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
|
|
|
2308
2287
|
taskId,
|
|
2309
2288
|
taskTitle,
|
|
2310
2289
|
}),
|
|
2311
|
-
}), "gsd-run", ctx, "execute-task");
|
|
2290
|
+
}), "gsd-run", ctx, "execute-task", { basePath });
|
|
2312
2291
|
}
|
|
2313
2292
|
else {
|
|
2314
|
-
await dispatchWorkflow(pi, await buildExecuteTaskPrompt(milestoneId, sliceId, sliceTitle, taskId, taskTitle, basePath), "gsd-run", ctx, "execute-task");
|
|
2293
|
+
await dispatchWorkflow(pi, await buildExecuteTaskPrompt(milestoneId, sliceId, sliceTitle, taskId, taskTitle, basePath), "gsd-run", ctx, "execute-task", { basePath });
|
|
2315
2294
|
}
|
|
2316
2295
|
}
|
|
2317
|
-
else if (
|
|
2296
|
+
else if (route.kind === "status") {
|
|
2318
2297
|
const { fireStatusViaCommand } = await import("./commands.js");
|
|
2319
2298
|
await fireStatusViaCommand(ctx);
|
|
2320
2299
|
}
|
|
2321
|
-
else if (
|
|
2300
|
+
else if (route.kind === "milestone-actions") {
|
|
2322
2301
|
const acted = await handleMilestoneActions(ctx, pi, basePath, milestoneId, milestoneTitle, options);
|
|
2323
2302
|
if (acted)
|
|
2324
2303
|
return showSmartEntry(ctx, pi, basePath, options);
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// GSD-2 — Guided workflow Unit context.
|
|
2
|
+
// Tracks the guided Unit whose queued turn should use manifest Tool Contract policy.
|
|
3
|
+
const guidedUnitContextByBasePath = new Map();
|
|
4
|
+
export function setGuidedUnitContext(basePath, unitType) {
|
|
5
|
+
const context = { basePath, unitType, startedAt: Date.now() };
|
|
6
|
+
guidedUnitContextByBasePath.set(basePath, context);
|
|
7
|
+
return context;
|
|
8
|
+
}
|
|
9
|
+
export function getGuidedUnitContext(basePath) {
|
|
10
|
+
if (basePath)
|
|
11
|
+
return guidedUnitContextByBasePath.get(basePath) ?? null;
|
|
12
|
+
if (guidedUnitContextByBasePath.size === 1)
|
|
13
|
+
return guidedUnitContextByBasePath.values().next().value;
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
export function clearGuidedUnitContext(basePath) {
|
|
17
|
+
if (basePath) {
|
|
18
|
+
guidedUnitContextByBasePath.delete(basePath);
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
guidedUnitContextByBasePath.clear();
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
2
2
|
import { ensureDbOpen } from "./bootstrap/dynamic-tools.js";
|
|
3
|
-
import {
|
|
4
|
-
import { migrateHierarchyToDb } from "./md-importer.js";
|
|
3
|
+
import { getAllMilestones, getMilestoneSlices, getSliceTasks, isDbAvailable, } from "./gsd-db.js";
|
|
5
4
|
import { parsePlan, parseRoadmap } from "./parsers-legacy.js";
|
|
6
5
|
import { milestonesDir, resolveMilestoneFile, resolveSliceFile, } from "./paths.js";
|
|
7
|
-
import { invalidateStateCache } from "./state.js";
|
|
8
6
|
function zeroCounts() {
|
|
9
7
|
return { milestones: 0, slices: 0, tasks: 0 };
|
|
10
8
|
}
|
|
@@ -50,7 +48,7 @@ export function countDbHierarchy() {
|
|
|
50
48
|
}
|
|
51
49
|
return counts;
|
|
52
50
|
}
|
|
53
|
-
export async function
|
|
51
|
+
export async function checkMarkdownHierarchyAgainstDb(basePath) {
|
|
54
52
|
const markdown = countMarkdownHierarchy(basePath);
|
|
55
53
|
if (sameCounts(markdown, zeroCounts())) {
|
|
56
54
|
return {
|
|
@@ -70,18 +68,15 @@ export async function autoImportMarkdownHierarchyIfDbMismatch(basePath) {
|
|
|
70
68
|
return { action: "none", reason: "in-sync", markdown, beforeDb, afterDb: beforeDb };
|
|
71
69
|
}
|
|
72
70
|
const reason = sameCounts(beforeDb, zeroCounts()) ? "db-empty" : "count-mismatch";
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
71
|
+
return {
|
|
72
|
+
action: "recovery-required",
|
|
73
|
+
reason,
|
|
74
|
+
markdown,
|
|
75
|
+
beforeDb,
|
|
76
|
+
afterDb: beforeDb,
|
|
77
|
+
recoveryCommand: "gsd recover",
|
|
78
|
+
message: `Markdown planning artifacts (${markdown.milestones}M/${markdown.slices}S/${markdown.tasks}T) ` +
|
|
79
|
+
`do not match the authoritative DB (${beforeDb.milestones}M/${beforeDb.slices}S/${beforeDb.tasks}T). ` +
|
|
80
|
+
"Runtime startup will not import markdown automatically; run explicit GSD recovery if markdown should repopulate the database.",
|
|
82
81
|
};
|
|
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
82
|
}
|