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
|
@@ -44,10 +44,11 @@ import { readSessionLockData, isSessionLockProcessAlive } from "./session-lock.j
|
|
|
44
44
|
import { nativeAddAll, nativeCommit, nativeHasCommittedHead, nativeIsRepo, nativeInit } from "./native-git-bridge.js";
|
|
45
45
|
import { isInheritedRepo } from "./repo-identity.js";
|
|
46
46
|
import { ensureGitignore, ensurePreferences, untrackRuntimeFiles } from "./gitignore.js";
|
|
47
|
-
import { loadEffectiveGSDPreferences } from "./preferences.js";
|
|
47
|
+
import { getIsolationMode, loadEffectiveGSDPreferences } from "./preferences.js";
|
|
48
48
|
import { resolveUokFlags } from "./uok/flags.js";
|
|
49
49
|
import { ensurePlanV2Graph, isMissingFinalizedContextResult } from "./uok/plan-v2.js";
|
|
50
50
|
import { detectProjectState, hasGsdBootstrapArtifacts } from "./detection.js";
|
|
51
|
+
import { isFutureMilestoneStatus } from "./status-guards.js";
|
|
51
52
|
import { showProjectInit, offerMigration } from "./init-wizard.js";
|
|
52
53
|
import { validateDirectory } from "./validate-directory.js";
|
|
53
54
|
import { showConfirm } from "../shared/tui.js";
|
|
@@ -69,8 +70,24 @@ import {
|
|
|
69
70
|
formatPriorContextBrief,
|
|
70
71
|
} from "./preparation.js";
|
|
71
72
|
import { verifyExpectedArtifact } from "./auto-recovery.js";
|
|
72
|
-
import {
|
|
73
|
+
import type { MilestoneScope } from "./workspace.js";
|
|
73
74
|
import { getPendingGate, extractDepthVerificationMilestoneId } from "./bootstrap/write-gate.js";
|
|
75
|
+
import {
|
|
76
|
+
_getPendingAutoStart,
|
|
77
|
+
clearPendingAutoStart,
|
|
78
|
+
deletePendingAutoStart,
|
|
79
|
+
getDiscussionMilestoneId,
|
|
80
|
+
hasPendingAutoStart,
|
|
81
|
+
setPendingAutoStart,
|
|
82
|
+
} from "./pending-auto-start.js";
|
|
83
|
+
import { clearGuidedUnitContext, setGuidedUnitContext } from "./guided-unit-context.js";
|
|
84
|
+
|
|
85
|
+
export {
|
|
86
|
+
_getPendingAutoStart,
|
|
87
|
+
clearPendingAutoStart,
|
|
88
|
+
getDiscussionMilestoneId,
|
|
89
|
+
setPendingAutoStart,
|
|
90
|
+
} from "./pending-auto-start.js";
|
|
74
91
|
|
|
75
92
|
export function shouldSkipGitBootstrapAfterInit(result: { gitEnabled?: boolean }): boolean {
|
|
76
93
|
return result.gitEnabled === false;
|
|
@@ -92,6 +109,12 @@ import { deleteRuntimeKv } from "./db/runtime-kv.js";
|
|
|
92
109
|
import { PAUSED_SESSION_KV_KEY } from "./interrupted-session.js";
|
|
93
110
|
import { buildWorkflowDispatchContent } from "./workflow-protocol.js";
|
|
94
111
|
import { isFullGsdToolSurfaceRequested, restoreGsdWorkflowTools, scopeGsdWorkflowToolsForDispatch } from "./bootstrap/register-hooks.js";
|
|
112
|
+
import {
|
|
113
|
+
resolveActiveTaskChoiceRoute,
|
|
114
|
+
type ActiveTaskChoice,
|
|
115
|
+
} from "./smart-entry-routing.js";
|
|
116
|
+
|
|
117
|
+
export { resolveGuidedExecuteLaunchMode } from "./smart-entry-routing.js";
|
|
95
118
|
|
|
96
119
|
type AutoStartOptions = Parameters<typeof startAutoDetached>[4];
|
|
97
120
|
type AutoStartLauncher = typeof startAutoDetached;
|
|
@@ -228,26 +251,6 @@ function buildDocsCommitInstruction(_message: string): string {
|
|
|
228
251
|
|
|
229
252
|
// ─── Auto-start after discuss ─────────────────────────────────────────────────
|
|
230
253
|
|
|
231
|
-
/** Pending auto-start context, keyed by basePath for session isolation (#2985). */
|
|
232
|
-
interface PendingAutoStartEntry {
|
|
233
|
-
ctx: ExtensionCommandContext;
|
|
234
|
-
pi: ExtensionAPI;
|
|
235
|
-
basePath: string;
|
|
236
|
-
milestoneId: string; // the milestone being discussed
|
|
237
|
-
step?: boolean; // preserve step mode through discuss → auto transition
|
|
238
|
-
createdAt: number; // timestamp for staleness detection (#3274)
|
|
239
|
-
// #4573: counter for how many times the LLM emitted the ready phrase
|
|
240
|
-
// without writing the required artifacts. Cleared on entry delete/recreate.
|
|
241
|
-
readyRejectCount?: number;
|
|
242
|
-
// C1: scope is pinned at reservation time so path resolution is immune to
|
|
243
|
-
// cwd-drift between discuss and checkAutoStartAfterDiscuss.
|
|
244
|
-
// TODO(C3): basePath becomes redundant once all consumers migrate to scope.
|
|
245
|
-
scope: MilestoneScope;
|
|
246
|
-
// H1: retry counter for Gate 1b plan-blocked recovery. Capped at
|
|
247
|
-
// MAX_PLAN_BLOCKED_RECOVERIES to prevent infinite recovery loops (#5012).
|
|
248
|
-
planBlockedRecoveryCount: number;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
254
|
interface PendingDeepProjectSetupEntry {
|
|
252
255
|
ctx: ExtensionCommandContext;
|
|
253
256
|
pi: ExtensionAPI;
|
|
@@ -273,7 +276,6 @@ const MAX_PLAN_BLOCKED_RECOVERIES = 3;
|
|
|
273
276
|
// suffix) with optional trailing punctuation.
|
|
274
277
|
const READY_PHRASE_RE = /\bMilestone\s+M\d{3}[A-Z0-9-]*\s+ready\.?/i;
|
|
275
278
|
|
|
276
|
-
const pendingAutoStartMap = new Map<string, PendingAutoStartEntry>();
|
|
277
279
|
const pendingDeepProjectSetupMap = new Map<string, PendingDeepProjectSetupEntry>();
|
|
278
280
|
const USER_DRIVEN_DEEP_SETUP_UNITS = new Set([
|
|
279
281
|
"discuss-project",
|
|
@@ -300,17 +302,6 @@ This stage is running inside the foreground \`/gsd new-project --deep\` intervie
|
|
|
300
302
|
- Do NOT call \`ask_user_questions\`, \`AskUserQuestion\`, or ToolSearch to discover user-input tools.
|
|
301
303
|
- Ask one focused round, then stop and wait for the user's normal chat response.`;
|
|
302
304
|
|
|
303
|
-
/**
|
|
304
|
-
* Backward-compat bridge: returns a mutable reference to the entry matching
|
|
305
|
-
* basePath, or the sole entry when only one session exists.
|
|
306
|
-
* Exported for testing — internal use only in production code.
|
|
307
|
-
*/
|
|
308
|
-
export function _getPendingAutoStart(basePath?: string): PendingAutoStartEntry | null {
|
|
309
|
-
if (basePath) return pendingAutoStartMap.get(basePath) ?? null;
|
|
310
|
-
if (pendingAutoStartMap.size === 1) return pendingAutoStartMap.values().next().value!;
|
|
311
|
-
return null;
|
|
312
|
-
}
|
|
313
|
-
|
|
314
305
|
function hasNestedFileOrSymlink(dir: string): boolean {
|
|
315
306
|
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
316
307
|
if (entry.isFile() || entry.isSymbolicLink()) return true;
|
|
@@ -344,29 +335,6 @@ function clearEmptyLegacyDeepSetupPseudoMilestones(basePath: string, entries: st
|
|
|
344
335
|
return remaining;
|
|
345
336
|
}
|
|
346
337
|
|
|
347
|
-
/**
|
|
348
|
-
* Store pending auto-start state for a project.
|
|
349
|
-
* Exported for testing (#2985).
|
|
350
|
-
*/
|
|
351
|
-
export function setPendingAutoStart(basePath: string, entry: { basePath: string; milestoneId: string; ctx?: ExtensionCommandContext; pi?: ExtensionAPI; step?: boolean; createdAt?: number }): void {
|
|
352
|
-
const ws = createWorkspace(entry.basePath);
|
|
353
|
-
const scope = scopeMilestone(ws, entry.milestoneId);
|
|
354
|
-
pendingAutoStartMap.set(basePath, { createdAt: Date.now(), planBlockedRecoveryCount: 0, ...entry, scope } as PendingAutoStartEntry);
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
/**
|
|
358
|
-
* Clear pending auto-start state.
|
|
359
|
-
* If basePath is given, clears only that project. Otherwise clears all.
|
|
360
|
-
* Exported for testing (#2985).
|
|
361
|
-
*/
|
|
362
|
-
export function clearPendingAutoStart(basePath?: string): void {
|
|
363
|
-
if (basePath) {
|
|
364
|
-
pendingAutoStartMap.delete(basePath);
|
|
365
|
-
} else {
|
|
366
|
-
pendingAutoStartMap.clear();
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
|
|
370
338
|
export function clearPendingDeepProjectSetup(basePath?: string): void {
|
|
371
339
|
if (basePath) {
|
|
372
340
|
pendingDeepProjectSetupMap.delete(basePath);
|
|
@@ -375,23 +343,6 @@ export function clearPendingDeepProjectSetup(basePath?: string): void {
|
|
|
375
343
|
}
|
|
376
344
|
}
|
|
377
345
|
|
|
378
|
-
/**
|
|
379
|
-
* Returns the milestoneId being discussed for the given project.
|
|
380
|
-
* When basePath is omitted and only one session is active, returns that
|
|
381
|
-
* session's milestoneId for backward compatibility. Returns null when
|
|
382
|
-
* multiple sessions exist and basePath is not specified (#2985 Bug 4).
|
|
383
|
-
*/
|
|
384
|
-
export function getDiscussionMilestoneId(basePath?: string): string | null {
|
|
385
|
-
if (basePath) {
|
|
386
|
-
return pendingAutoStartMap.get(basePath)?.milestoneId ?? null;
|
|
387
|
-
}
|
|
388
|
-
// Backward compat: return the sole entry's milestoneId, or null if ambiguous
|
|
389
|
-
if (pendingAutoStartMap.size === 1) {
|
|
390
|
-
return pendingAutoStartMap.values().next().value!.milestoneId;
|
|
391
|
-
}
|
|
392
|
-
return null;
|
|
393
|
-
}
|
|
394
|
-
|
|
395
346
|
function _getPendingDeepProjectSetup(basePath?: string): PendingDeepProjectSetupEntry | null {
|
|
396
347
|
if (basePath) return pendingDeepProjectSetupMap.get(basePath) ?? null;
|
|
397
348
|
if (pendingDeepProjectSetupMap.size === 1) return pendingDeepProjectSetupMap.values().next().value!;
|
|
@@ -544,13 +495,14 @@ async function dispatchNextDeepProjectSetupStage(entry: PendingDeepProjectSetupE
|
|
|
544
495
|
"gsd-run",
|
|
545
496
|
entry.ctx,
|
|
546
497
|
result.unitType,
|
|
498
|
+
{ basePath: entry.basePath },
|
|
547
499
|
);
|
|
548
500
|
return true;
|
|
549
501
|
}
|
|
550
502
|
|
|
551
503
|
/** Called from agent_end to check if auto-mode should start after discuss */
|
|
552
|
-
export function checkAutoStartAfterDiscuss(): boolean {
|
|
553
|
-
const entry = _getPendingAutoStart();
|
|
504
|
+
export function checkAutoStartAfterDiscuss(lookupBasePath?: string): boolean {
|
|
505
|
+
const entry = _getPendingAutoStart(lookupBasePath);
|
|
554
506
|
if (!entry) return false;
|
|
555
507
|
|
|
556
508
|
const { ctx, pi, basePath, milestoneId, step } = entry;
|
|
@@ -735,7 +687,7 @@ export function checkAutoStartAfterDiscuss(): boolean {
|
|
|
735
687
|
}
|
|
736
688
|
}
|
|
737
689
|
|
|
738
|
-
|
|
690
|
+
deletePendingAutoStart(basePath);
|
|
739
691
|
ctx.ui.notify(`Milestone ${milestoneId} ready.`, "success");
|
|
740
692
|
scheduleAutoStartAfterIdle(ctx, pi, basePath, false, { step });
|
|
741
693
|
return true;
|
|
@@ -806,8 +758,8 @@ function hasToolUse(msg: any): boolean {
|
|
|
806
758
|
* Returns true when a nudge (or give-up) was emitted, signaling the caller to
|
|
807
759
|
* skip `resolveAgentEnd`.
|
|
808
760
|
*/
|
|
809
|
-
export function maybeHandleReadyPhraseWithoutFiles(event: { messages: any[] }): boolean {
|
|
810
|
-
const entry = _getPendingAutoStart();
|
|
761
|
+
export function maybeHandleReadyPhraseWithoutFiles(event: { messages: any[] }, lookupBasePath?: string): boolean {
|
|
762
|
+
const entry = _getPendingAutoStart(lookupBasePath);
|
|
811
763
|
if (!entry) return false;
|
|
812
764
|
const { ctx, pi, basePath, milestoneId } = entry;
|
|
813
765
|
|
|
@@ -854,7 +806,7 @@ export function maybeHandleReadyPhraseWithoutFiles(event: { messages: any[] }):
|
|
|
854
806
|
if (entry.readyRejectCount > MAX_READY_REJECTS) {
|
|
855
807
|
// Give up: clear state and tell the user to re-run /gsd. Avoids an
|
|
856
808
|
// infinite nudge loop when the LLM never produces the writes.
|
|
857
|
-
|
|
809
|
+
deletePendingAutoStart(basePath);
|
|
858
810
|
ctx.ui.notify(
|
|
859
811
|
`Milestone ${milestoneId}: LLM signaled "ready" ${entry.readyRejectCount} times without writing files. ` +
|
|
860
812
|
`Stopping auto-nudge. Run /gsd to try again.`,
|
|
@@ -935,10 +887,11 @@ export function resetEmptyTurnCounter(basePath?: string): void {
|
|
|
935
887
|
export function maybeHandleEmptyIntentTurn(
|
|
936
888
|
event: { messages: any[] },
|
|
937
889
|
isAuto: boolean,
|
|
890
|
+
lookupBasePath?: string,
|
|
938
891
|
): boolean {
|
|
939
892
|
// Gate: only fire when there is system-driven work in flight. Interactive
|
|
940
893
|
// /gsd discuss (user-driven) produces legitimate text-only turns.
|
|
941
|
-
if (!isAuto &&
|
|
894
|
+
if (!isAuto && !hasPendingAutoStart(lookupBasePath)) return false;
|
|
942
895
|
|
|
943
896
|
const lastMsg = event.messages[event.messages.length - 1];
|
|
944
897
|
if (!lastMsg) return false;
|
|
@@ -965,7 +918,7 @@ export function maybeHandleEmptyIntentTurn(
|
|
|
965
918
|
|
|
966
919
|
// Resolve the target basePath + pi for injection. Prefer the pending
|
|
967
920
|
// autostart entry (discuss flow); otherwise we cannot inject.
|
|
968
|
-
const entry = _getPendingAutoStart();
|
|
921
|
+
const entry = _getPendingAutoStart(lookupBasePath);
|
|
969
922
|
if (!entry) return false;
|
|
970
923
|
const { ctx, pi, basePath } = entry;
|
|
971
924
|
|
|
@@ -1024,6 +977,19 @@ type UIContext = ExtensionContext;
|
|
|
1024
977
|
|
|
1025
978
|
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
|
1026
979
|
|
|
980
|
+
interface DispatchWorkflowOptions {
|
|
981
|
+
basePath?: string;
|
|
982
|
+
deps?: {
|
|
983
|
+
loadPreferences?: typeof loadEffectiveGSDPreferences;
|
|
984
|
+
selectModel?: typeof selectAndApplyModel;
|
|
985
|
+
getTransportSupportError?: typeof getWorkflowTransportSupportError;
|
|
986
|
+
};
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
export function resolveGuidedDispatchProjectRoot(basePath?: string): string {
|
|
990
|
+
return basePath ?? process.cwd();
|
|
991
|
+
}
|
|
992
|
+
|
|
1027
993
|
/**
|
|
1028
994
|
* Read GSD-WORKFLOW.md and dispatch it to the LLM with a contextual note.
|
|
1029
995
|
* This is the only way the wizard triggers work — everything else is the LLM's job.
|
|
@@ -1039,13 +1005,20 @@ async function dispatchWorkflow(
|
|
|
1039
1005
|
customType = "gsd-run",
|
|
1040
1006
|
ctx?: ExtensionContext,
|
|
1041
1007
|
unitType?: string,
|
|
1008
|
+
options?: DispatchWorkflowOptions,
|
|
1042
1009
|
): Promise<void> {
|
|
1010
|
+
const resolvedOptions = options ?? {};
|
|
1011
|
+
const projectRoot = resolveGuidedDispatchProjectRoot(resolvedOptions.basePath);
|
|
1012
|
+
const loadPreferences = resolvedOptions.deps?.loadPreferences ?? loadEffectiveGSDPreferences;
|
|
1013
|
+
const selectModel = resolvedOptions.deps?.selectModel ?? selectAndApplyModel;
|
|
1014
|
+
const getTransportSupportError = resolvedOptions.deps?.getTransportSupportError ?? getWorkflowTransportSupportError;
|
|
1015
|
+
|
|
1043
1016
|
// Route through the dynamic routing pipeline (complexity classification,
|
|
1044
1017
|
// tier downgrade, fallback chains) — same path as auto-mode dispatches (#2958).
|
|
1045
1018
|
if (ctx && unitType) {
|
|
1046
|
-
const prefs =
|
|
1047
|
-
const result = await
|
|
1048
|
-
ctx, pi, unitType, /* unitId */ "",
|
|
1019
|
+
const prefs = loadPreferences(projectRoot)?.preferences;
|
|
1020
|
+
const result = await selectModel(
|
|
1021
|
+
ctx, pi, unitType, /* unitId */ "", projectRoot,
|
|
1049
1022
|
prefs, /* verbose */ false, /* autoModeStartModel */ null,
|
|
1050
1023
|
/* retryContext */ undefined, /* isAutoMode */ false,
|
|
1051
1024
|
);
|
|
@@ -1057,11 +1030,11 @@ async function dispatchWorkflow(
|
|
|
1057
1030
|
});
|
|
1058
1031
|
}
|
|
1059
1032
|
|
|
1060
|
-
const compatibilityError =
|
|
1033
|
+
const compatibilityError = getTransportSupportError(
|
|
1061
1034
|
result.appliedModel?.provider ?? ctx.model?.provider,
|
|
1062
1035
|
getRequiredWorkflowToolsForGuidedUnit(unitType),
|
|
1063
1036
|
{
|
|
1064
|
-
projectRoot
|
|
1037
|
+
projectRoot,
|
|
1065
1038
|
surface: "guided flow",
|
|
1066
1039
|
unitType,
|
|
1067
1040
|
authMode: result.appliedModel?.provider
|
|
@@ -1070,6 +1043,7 @@ async function dispatchWorkflow(
|
|
|
1070
1043
|
? ctx.modelRegistry.getProviderAuthMode(ctx.model.provider)
|
|
1071
1044
|
: undefined,
|
|
1072
1045
|
baseUrl: result.appliedModel?.baseUrl ?? ctx.model?.baseUrl,
|
|
1046
|
+
activeTools: typeof pi.getActiveTools === "function" ? pi.getActiveTools() : [],
|
|
1073
1047
|
},
|
|
1074
1048
|
);
|
|
1075
1049
|
if (compatibilityError) {
|
|
@@ -1119,14 +1093,20 @@ async function dispatchWorkflow(
|
|
|
1119
1093
|
const workflowPath = process.env.GSD_WORKFLOW_PATH ?? join(gsdHome(), "agent", "GSD-WORKFLOW.md");
|
|
1120
1094
|
const workflow = readFileSync(workflowPath, "utf-8");
|
|
1121
1095
|
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1096
|
+
if (unitType) setGuidedUnitContext(projectRoot, unitType);
|
|
1097
|
+
try {
|
|
1098
|
+
pi.sendMessage(
|
|
1099
|
+
{
|
|
1100
|
+
customType,
|
|
1101
|
+
content: buildWorkflowDispatchContent({ workflow, workflowPath, task: note }),
|
|
1102
|
+
display: false,
|
|
1103
|
+
},
|
|
1104
|
+
{ triggerTurn: true },
|
|
1105
|
+
);
|
|
1106
|
+
} catch (err) {
|
|
1107
|
+
clearGuidedUnitContext(projectRoot);
|
|
1108
|
+
throw err;
|
|
1109
|
+
}
|
|
1130
1110
|
} finally {
|
|
1131
1111
|
// Restore full tool set after the message is queued. The LLM turn has
|
|
1132
1112
|
// already captured the scoped set — restoring prevents the narrowed
|
|
@@ -1357,7 +1337,7 @@ export async function showHeadlessMilestoneCreation(
|
|
|
1357
1337
|
// model/tool routing to skip discuss-flow tool scoping and
|
|
1358
1338
|
// `checkAutoStartAfterDiscuss` guardrails that rely on the
|
|
1359
1339
|
// "discuss-"-prefixed unitType.
|
|
1360
|
-
await dispatchWorkflow(pi, prompt, "gsd-run", ctx, "discuss-milestone");
|
|
1340
|
+
await dispatchWorkflow(pi, prompt, "gsd-run", ctx, "discuss-milestone", { basePath });
|
|
1361
1341
|
}
|
|
1362
1342
|
|
|
1363
1343
|
|
|
@@ -1493,7 +1473,7 @@ export async function showDiscuss(
|
|
|
1493
1473
|
// No active milestone (or corrupted milestone with undefined id) —
|
|
1494
1474
|
// check for pending milestones to discuss instead
|
|
1495
1475
|
if (!state.activeMilestone?.id) {
|
|
1496
|
-
const pendingMilestones = state.registry.filter(m => m.status
|
|
1476
|
+
const pendingMilestones = state.registry.filter(m => isFutureMilestoneStatus(m.status));
|
|
1497
1477
|
if (pendingMilestones.length === 0) {
|
|
1498
1478
|
ctx.ui.notify("No active milestone. Run /gsd to create one first.", "warning");
|
|
1499
1479
|
return;
|
|
@@ -1548,7 +1528,7 @@ export async function showDiscuss(
|
|
|
1548
1528
|
? `${basePrompt}\n\n## Prior Discussion (Draft Seed)\n\n${draftContent}`
|
|
1549
1529
|
: basePrompt;
|
|
1550
1530
|
setPendingAutoStart(basePath, { ctx, pi, basePath, milestoneId: mid, step: false });
|
|
1551
|
-
await dispatchWorkflow(pi, seed, "gsd-discuss", ctx, "discuss-milestone");
|
|
1531
|
+
await dispatchWorkflow(pi, seed, "gsd-discuss", ctx, "discuss-milestone", { basePath });
|
|
1552
1532
|
} else if (choice === "discuss_fresh") {
|
|
1553
1533
|
const discussMilestoneTemplates = inlineTemplate("context", "Context");
|
|
1554
1534
|
const structuredQuestionsAvailable = getStructuredQuestionsAvailability(pi, ctx);
|
|
@@ -1558,7 +1538,7 @@ export async function showDiscuss(
|
|
|
1558
1538
|
milestoneId: mid, milestoneTitle, inlinedTemplates: discussMilestoneTemplates, structuredQuestionsAvailable,
|
|
1559
1539
|
commitInstruction: buildDocsCommitInstruction(`docs(${mid}): milestone context from discuss`),
|
|
1560
1540
|
fastPathInstruction: "",
|
|
1561
|
-
}), "gsd-discuss", ctx, "discuss-milestone");
|
|
1541
|
+
}), "gsd-discuss", ctx, "discuss-milestone", { basePath });
|
|
1562
1542
|
} else if (choice === "skip_milestone") {
|
|
1563
1543
|
const { ensureDbOpen } = await import("./bootstrap/dynamic-tools.js");
|
|
1564
1544
|
await ensureDbOpen(basePath);
|
|
@@ -1566,7 +1546,7 @@ export async function showDiscuss(
|
|
|
1566
1546
|
const uniqueMilestoneIds = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
|
|
1567
1547
|
const nextId = nextMilestoneIdReserved(milestoneIds, uniqueMilestoneIds, basePath);
|
|
1568
1548
|
setPendingAutoStart(basePath, { ctx, pi, basePath, milestoneId: nextId, step: false });
|
|
1569
|
-
await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, pi, nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone");
|
|
1549
|
+
await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, pi, nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone", { basePath });
|
|
1570
1550
|
}
|
|
1571
1551
|
return;
|
|
1572
1552
|
}
|
|
@@ -1605,7 +1585,7 @@ export async function showDiscuss(
|
|
|
1605
1585
|
|
|
1606
1586
|
if (pendingSlices.length === 0) {
|
|
1607
1587
|
// All slices complete — but queued milestones may still need discussion (#3150)
|
|
1608
|
-
const pendingMilestones = state.registry.filter(m => m.status
|
|
1588
|
+
const pendingMilestones = state.registry.filter(m => isFutureMilestoneStatus(m.status));
|
|
1609
1589
|
if (pendingMilestones.length > 0) {
|
|
1610
1590
|
await showDiscussQueuedMilestone(ctx, pi, basePath, pendingMilestones);
|
|
1611
1591
|
return;
|
|
@@ -1629,7 +1609,7 @@ export async function showDiscuss(
|
|
|
1629
1609
|
// If all pending slices are discussed, check for queued milestones before exiting (#3150)
|
|
1630
1610
|
const allDiscussed = pendingSlices.every(s => discussedMap.get(s.id));
|
|
1631
1611
|
if (allDiscussed) {
|
|
1632
|
-
const pendingMilestones = state.registry.filter(m => m.status
|
|
1612
|
+
const pendingMilestones = state.registry.filter(m => isFutureMilestoneStatus(m.status));
|
|
1633
1613
|
if (pendingMilestones.length > 0) {
|
|
1634
1614
|
await showDiscussQueuedMilestone(ctx, pi, basePath, pendingMilestones);
|
|
1635
1615
|
return;
|
|
@@ -1665,7 +1645,7 @@ export async function showDiscuss(
|
|
|
1665
1645
|
});
|
|
1666
1646
|
|
|
1667
1647
|
// Offer access to queued milestones when any exist
|
|
1668
|
-
const pendingMilestones = state.registry.filter(m => m.status
|
|
1648
|
+
const pendingMilestones = state.registry.filter(m => isFutureMilestoneStatus(m.status));
|
|
1669
1649
|
if (pendingMilestones.length > 0) {
|
|
1670
1650
|
actions.push({
|
|
1671
1651
|
id: "discuss_queued_milestone",
|
|
@@ -1714,7 +1694,7 @@ export async function showDiscuss(
|
|
|
1714
1694
|
|
|
1715
1695
|
const sqAvail = getStructuredQuestionsAvailability(pi, ctx);
|
|
1716
1696
|
const prompt = await buildDiscussSlicePrompt(mid, chosen.id, chosen.title, basePath, { rediscuss: isRediscuss, structuredQuestionsAvailable: sqAvail });
|
|
1717
|
-
await dispatchWorkflow(pi, prompt, "gsd-discuss", ctx, "discuss-slice");
|
|
1697
|
+
await dispatchWorkflow(pi, prompt, "gsd-discuss", ctx, "discuss-slice", { basePath });
|
|
1718
1698
|
|
|
1719
1699
|
// Wait for the discuss session to finish, then loop back to the picker
|
|
1720
1700
|
await ctx.waitForIdle();
|
|
@@ -1740,10 +1720,11 @@ async function showDiscussQueuedMilestone(
|
|
|
1740
1720
|
const hasContext = !!resolveMilestoneFile(basePath, m.id, "CONTEXT");
|
|
1741
1721
|
const hasDraft = !hasContext && !!resolveMilestoneFile(basePath, m.id, "CONTEXT-DRAFT");
|
|
1742
1722
|
const contextStatus = hasContext ? "context ✓" : hasDraft ? "draft context" : "no context yet";
|
|
1723
|
+
const statusLabel = m.status === "planned" ? "planned" : "queued";
|
|
1743
1724
|
return {
|
|
1744
1725
|
id: m.id,
|
|
1745
1726
|
label: `${m.id}: ${m.title}`,
|
|
1746
|
-
description: `[
|
|
1727
|
+
description: `[${statusLabel}] · ${contextStatus}`,
|
|
1747
1728
|
recommended: i === 0,
|
|
1748
1729
|
};
|
|
1749
1730
|
});
|
|
@@ -1835,7 +1816,7 @@ async function dispatchDiscussForMilestone(
|
|
|
1835
1816
|
const prompt = draftContent
|
|
1836
1817
|
? `${basePrompt}\n\n## Prior Discussion (Draft Seed)\n\n${draftContent}`
|
|
1837
1818
|
: basePrompt;
|
|
1838
|
-
await dispatchWorkflow(pi, prompt, "gsd-discuss", ctx, "discuss-milestone");
|
|
1819
|
+
await dispatchWorkflow(pi, prompt, "gsd-discuss", ctx, "discuss-milestone", { basePath });
|
|
1839
1820
|
}
|
|
1840
1821
|
|
|
1841
1822
|
// ─── Smart Entry Point ────────────────────────────────────────────────────────
|
|
@@ -1978,7 +1959,7 @@ async function handleMilestoneActions(
|
|
|
1978
1959
|
await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, pi, nextId,
|
|
1979
1960
|
`New milestone ${nextId}.`,
|
|
1980
1961
|
basePath
|
|
1981
|
-
), "gsd-run", ctx, "discuss-milestone");
|
|
1962
|
+
), "gsd-run", ctx, "discuss-milestone", { basePath });
|
|
1982
1963
|
return true;
|
|
1983
1964
|
}
|
|
1984
1965
|
|
|
@@ -2124,18 +2105,19 @@ export async function showSmartEntry(
|
|
|
2124
2105
|
|
|
2125
2106
|
if (interrupted.classification !== "recoverable") {
|
|
2126
2107
|
try {
|
|
2127
|
-
const {
|
|
2128
|
-
const result = await
|
|
2129
|
-
if (result.action === "
|
|
2108
|
+
const { checkMarkdownHierarchyAgainstDb } = await import("./migration-auto-check.js");
|
|
2109
|
+
const result = await checkMarkdownHierarchyAgainstDb(basePath);
|
|
2110
|
+
if (result.action === "recovery-required") {
|
|
2130
2111
|
ctx.ui.notify(
|
|
2131
|
-
|
|
2132
|
-
|
|
2112
|
+
result.message ??
|
|
2113
|
+
`Markdown planning artifacts do not match the authoritative DB. Run \`${result.recoveryCommand ?? "gsd recover"}\` to import markdown explicitly.`,
|
|
2114
|
+
"warning",
|
|
2133
2115
|
);
|
|
2134
2116
|
}
|
|
2135
2117
|
} catch (err) {
|
|
2136
2118
|
const message = err instanceof Error ? err.message : String(err);
|
|
2137
|
-
ctx.ui.notify(`GSD could not
|
|
2138
|
-
logWarning("guided", `planning state
|
|
2119
|
+
ctx.ui.notify(`GSD could not compare markdown planning artifacts with gsd.db: ${message}`, "warning");
|
|
2120
|
+
logWarning("guided", `planning state DB/markdown comparison failed: ${message}`, { file: "guided-flow.ts" });
|
|
2139
2121
|
}
|
|
2140
2122
|
}
|
|
2141
2123
|
|
|
@@ -2176,17 +2158,17 @@ export async function showSmartEntry(
|
|
|
2176
2158
|
// Both /gsd and /gsd auto reach this branch when no milestone exists yet.
|
|
2177
2159
|
// Without this guard, every subsequent /gsd call overwrites the pending auto-start
|
|
2178
2160
|
// and fires another dispatchWorkflow, resetting the conversation mid-interview.
|
|
2179
|
-
if (
|
|
2161
|
+
if (hasPendingAutoStart(basePath)) {
|
|
2180
2162
|
// #3274: If /clear interrupted the discussion, the pending entry is stale.
|
|
2181
2163
|
// Detect staleness: no manifest, no milestone CONTEXT artifact, AND entry is older than
|
|
2182
2164
|
// 30s (avoids race between .set() and LLM writing first artifact).
|
|
2183
|
-
const entry =
|
|
2165
|
+
const entry = _getPendingAutoStart(basePath)!;
|
|
2184
2166
|
const ageMs = Date.now() - (entry.createdAt || 0);
|
|
2185
2167
|
const manifestExists = existsSync(join(gsdRoot(basePath), "DISCUSSION-MANIFEST.json"));
|
|
2186
2168
|
const milestoneHasContext = !!resolveMilestoneFile(basePath, entry.milestoneId, "CONTEXT");
|
|
2187
2169
|
if (!manifestExists && !milestoneHasContext && ageMs > 30_000) {
|
|
2188
2170
|
// Stale entry from an interrupted discussion — clear and continue
|
|
2189
|
-
|
|
2171
|
+
deletePendingAutoStart(basePath);
|
|
2190
2172
|
} else {
|
|
2191
2173
|
ctx.ui.notify("Discussion already in progress — answer the question above to continue.", "info");
|
|
2192
2174
|
return;
|
|
@@ -2226,7 +2208,7 @@ export async function showSmartEntry(
|
|
|
2226
2208
|
await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, pi, nextId,
|
|
2227
2209
|
`New project, milestone ${nextId}. Do NOT read or explore .gsd/ — it's empty scaffolding.`,
|
|
2228
2210
|
basePath
|
|
2229
|
-
), "gsd-run", ctx, "discuss-milestone");
|
|
2211
|
+
), "gsd-run", ctx, "discuss-milestone", { basePath });
|
|
2230
2212
|
} else {
|
|
2231
2213
|
const choice = await showNextAction(ctx, {
|
|
2232
2214
|
title: "GSD — Get Shit Done",
|
|
@@ -2255,7 +2237,7 @@ export async function showSmartEntry(
|
|
|
2255
2237
|
await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, pi, nextId,
|
|
2256
2238
|
`New milestone ${nextId}.`,
|
|
2257
2239
|
basePath
|
|
2258
|
-
), "gsd-run", ctx, "discuss-milestone");
|
|
2240
|
+
), "gsd-run", ctx, "discuss-milestone", { basePath });
|
|
2259
2241
|
}
|
|
2260
2242
|
}
|
|
2261
2243
|
return;
|
|
@@ -2277,6 +2259,7 @@ export async function showSmartEntry(
|
|
|
2277
2259
|
"gsd-discuss",
|
|
2278
2260
|
ctx,
|
|
2279
2261
|
"discuss-milestone",
|
|
2262
|
+
{ basePath },
|
|
2280
2263
|
);
|
|
2281
2264
|
return;
|
|
2282
2265
|
}
|
|
@@ -2318,7 +2301,7 @@ export async function showSmartEntry(
|
|
|
2318
2301
|
await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, pi, nextId,
|
|
2319
2302
|
`New milestone ${nextId}.`,
|
|
2320
2303
|
basePath
|
|
2321
|
-
), "gsd-run", ctx, "discuss-milestone");
|
|
2304
|
+
), "gsd-run", ctx, "discuss-milestone", { basePath });
|
|
2322
2305
|
} else if (choice === "status") {
|
|
2323
2306
|
const { fireStatusViaCommand } = await import("./commands.js");
|
|
2324
2307
|
await fireStatusViaCommand(ctx);
|
|
@@ -2368,7 +2351,7 @@ export async function showSmartEntry(
|
|
|
2368
2351
|
? `${basePrompt}\n\n## Prior Discussion (Draft Seed)\n\n${draftContent}`
|
|
2369
2352
|
: basePrompt;
|
|
2370
2353
|
setPendingAutoStart(basePath, { ctx, pi, basePath, milestoneId, step: stepMode });
|
|
2371
|
-
await dispatchWorkflow(pi, seed, "gsd-discuss", ctx, "discuss-milestone");
|
|
2354
|
+
await dispatchWorkflow(pi, seed, "gsd-discuss", ctx, "discuss-milestone", { basePath });
|
|
2372
2355
|
} else if (choice === "discuss_fresh") {
|
|
2373
2356
|
const discussMilestoneTemplates = inlineTemplate("context", "Context");
|
|
2374
2357
|
const structuredQuestionsAvailable = getStructuredQuestionsAvailability(pi, ctx);
|
|
@@ -2378,7 +2361,7 @@ export async function showSmartEntry(
|
|
|
2378
2361
|
milestoneId, milestoneTitle, inlinedTemplates: discussMilestoneTemplates, structuredQuestionsAvailable,
|
|
2379
2362
|
commitInstruction: buildDocsCommitInstruction(`docs(${milestoneId}): milestone context from discuss`),
|
|
2380
2363
|
fastPathInstruction: "",
|
|
2381
|
-
}), "gsd-discuss", ctx, "discuss-milestone");
|
|
2364
|
+
}), "gsd-discuss", ctx, "discuss-milestone", { basePath });
|
|
2382
2365
|
} else if (choice === "skip_milestone") {
|
|
2383
2366
|
const milestoneIds = findMilestoneIds(basePath);
|
|
2384
2367
|
const uniqueMilestoneIds = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
|
|
@@ -2387,7 +2370,7 @@ export async function showSmartEntry(
|
|
|
2387
2370
|
await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, pi, nextId,
|
|
2388
2371
|
`New milestone ${nextId}.`,
|
|
2389
2372
|
basePath
|
|
2390
|
-
), "gsd-run", ctx, "discuss-milestone");
|
|
2373
|
+
), "gsd-run", ctx, "discuss-milestone", { basePath });
|
|
2391
2374
|
}
|
|
2392
2375
|
return;
|
|
2393
2376
|
}
|
|
@@ -2462,6 +2445,7 @@ export async function showSmartEntry(
|
|
|
2462
2445
|
"gsd-run",
|
|
2463
2446
|
ctx,
|
|
2464
2447
|
"plan-milestone",
|
|
2448
|
+
{ basePath },
|
|
2465
2449
|
);
|
|
2466
2450
|
} else if (choice === "discuss") {
|
|
2467
2451
|
const discussMilestoneTemplates = inlineTemplate("context", "Context");
|
|
@@ -2471,7 +2455,7 @@ export async function showSmartEntry(
|
|
|
2471
2455
|
milestoneId, milestoneTitle, inlinedTemplates: discussMilestoneTemplates, structuredQuestionsAvailable,
|
|
2472
2456
|
commitInstruction: buildDocsCommitInstruction(`docs(${milestoneId}): milestone context from discuss`),
|
|
2473
2457
|
fastPathInstruction: "",
|
|
2474
|
-
}), "gsd-run", ctx, "discuss-milestone");
|
|
2458
|
+
}), "gsd-run", ctx, "discuss-milestone", { basePath });
|
|
2475
2459
|
} else if (choice === "skip_milestone") {
|
|
2476
2460
|
const milestoneIds = findMilestoneIds(basePath);
|
|
2477
2461
|
const uniqueMilestoneIds = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
|
|
@@ -2480,7 +2464,7 @@ export async function showSmartEntry(
|
|
|
2480
2464
|
await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, pi, nextId,
|
|
2481
2465
|
`New milestone ${nextId}.`,
|
|
2482
2466
|
basePath
|
|
2483
|
-
), "gsd-run", ctx, "discuss-milestone");
|
|
2467
|
+
), "gsd-run", ctx, "discuss-milestone", { basePath });
|
|
2484
2468
|
} else if (choice === "discard_milestone") {
|
|
2485
2469
|
const confirmed = await showConfirm(ctx, {
|
|
2486
2470
|
title: "Discard milestone?",
|
|
@@ -2595,10 +2579,11 @@ export async function showSmartEntry(
|
|
|
2595
2579
|
"gsd-run",
|
|
2596
2580
|
ctx,
|
|
2597
2581
|
"plan-slice",
|
|
2582
|
+
{ basePath },
|
|
2598
2583
|
);
|
|
2599
2584
|
} else if (choice === "discuss") {
|
|
2600
2585
|
const sqAvail = getStructuredQuestionsAvailability(pi, ctx);
|
|
2601
|
-
await dispatchWorkflow(pi, await buildDiscussSlicePrompt(milestoneId, sliceId, sliceTitle, basePath, { rediscuss: hasContext, structuredQuestionsAvailable: sqAvail }), "gsd-run", ctx, "discuss-slice");
|
|
2586
|
+
await dispatchWorkflow(pi, await buildDiscussSlicePrompt(milestoneId, sliceId, sliceTitle, basePath, { rediscuss: hasContext, structuredQuestionsAvailable: sqAvail }), "gsd-run", ctx, "discuss-slice", { basePath });
|
|
2602
2587
|
} else if (choice === "research") {
|
|
2603
2588
|
const researchTemplates = inlineTemplate("research", "Research");
|
|
2604
2589
|
await dispatchWorkflow(pi, loadPrompt("guided-research-slice", {
|
|
@@ -2613,7 +2598,7 @@ export async function showSmartEntry(
|
|
|
2613
2598
|
sliceTitle,
|
|
2614
2599
|
extraContext: [researchTemplates],
|
|
2615
2600
|
}),
|
|
2616
|
-
}), "gsd-run", ctx, "research-slice");
|
|
2601
|
+
}), "gsd-run", ctx, "research-slice", { basePath });
|
|
2617
2602
|
} else if (choice === "status") {
|
|
2618
2603
|
const { fireStatusViaCommand } = await import("./commands.js");
|
|
2619
2604
|
await fireStatusViaCommand(ctx);
|
|
@@ -2658,6 +2643,7 @@ export async function showSmartEntry(
|
|
|
2658
2643
|
"gsd-run",
|
|
2659
2644
|
ctx,
|
|
2660
2645
|
"complete-slice",
|
|
2646
|
+
{ basePath },
|
|
2661
2647
|
);
|
|
2662
2648
|
} else if (choice === "status") {
|
|
2663
2649
|
const { fireStatusViaCommand } = await import("./commands.js");
|
|
@@ -2714,12 +2700,20 @@ export async function showSmartEntry(
|
|
|
2714
2700
|
notYetMessage: "Run /gsd when ready.",
|
|
2715
2701
|
});
|
|
2716
2702
|
|
|
2717
|
-
if (choice === "
|
|
2718
|
-
|
|
2703
|
+
if (choice === "not_yet") return;
|
|
2704
|
+
|
|
2705
|
+
const route = resolveActiveTaskChoiceRoute({
|
|
2706
|
+
choice: choice as ActiveTaskChoice,
|
|
2707
|
+
isolationMode: getIsolationMode(basePath),
|
|
2708
|
+
milestoneId,
|
|
2709
|
+
});
|
|
2710
|
+
|
|
2711
|
+
if (route.kind === "auto-bootstrap") {
|
|
2712
|
+
startAutoDetached(ctx, pi, basePath, route.verboseMode, route.options);
|
|
2719
2713
|
return;
|
|
2720
2714
|
}
|
|
2721
2715
|
|
|
2722
|
-
if (
|
|
2716
|
+
if (route.kind === "guided-dispatch") {
|
|
2723
2717
|
ctx.ui.setStatus("gsd-step", "Executing Task · follow progress above");
|
|
2724
2718
|
if (hasInterrupted) {
|
|
2725
2719
|
await dispatchWorkflow(pi, loadPrompt("guided-resume-task", {
|
|
@@ -2732,7 +2726,7 @@ export async function showSmartEntry(
|
|
|
2732
2726
|
taskId,
|
|
2733
2727
|
taskTitle,
|
|
2734
2728
|
}),
|
|
2735
|
-
}), "gsd-run", ctx, "execute-task");
|
|
2729
|
+
}), "gsd-run", ctx, "execute-task", { basePath });
|
|
2736
2730
|
} else {
|
|
2737
2731
|
await dispatchWorkflow(
|
|
2738
2732
|
pi,
|
|
@@ -2740,12 +2734,13 @@ export async function showSmartEntry(
|
|
|
2740
2734
|
"gsd-run",
|
|
2741
2735
|
ctx,
|
|
2742
2736
|
"execute-task",
|
|
2737
|
+
{ basePath },
|
|
2743
2738
|
);
|
|
2744
2739
|
}
|
|
2745
|
-
} else if (
|
|
2740
|
+
} else if (route.kind === "status") {
|
|
2746
2741
|
const { fireStatusViaCommand } = await import("./commands.js");
|
|
2747
2742
|
await fireStatusViaCommand(ctx);
|
|
2748
|
-
} else if (
|
|
2743
|
+
} else if (route.kind === "milestone-actions") {
|
|
2749
2744
|
const acted = await handleMilestoneActions(ctx, pi, basePath, milestoneId, milestoneTitle, options);
|
|
2750
2745
|
if (acted) return showSmartEntry(ctx, pi, basePath, options);
|
|
2751
2746
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// GSD-2 — Guided workflow Unit context.
|
|
2
|
+
// Tracks the guided Unit whose queued turn should use manifest Tool Contract policy.
|
|
3
|
+
|
|
4
|
+
export interface GuidedUnitContext {
|
|
5
|
+
basePath: string;
|
|
6
|
+
unitType: string;
|
|
7
|
+
startedAt: number;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const guidedUnitContextByBasePath = new Map<string, GuidedUnitContext>();
|
|
11
|
+
|
|
12
|
+
export function setGuidedUnitContext(basePath: string, unitType: string): GuidedUnitContext {
|
|
13
|
+
const context = { basePath, unitType, startedAt: Date.now() };
|
|
14
|
+
guidedUnitContextByBasePath.set(basePath, context);
|
|
15
|
+
return context;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function getGuidedUnitContext(basePath?: string): GuidedUnitContext | null {
|
|
19
|
+
if (basePath) return guidedUnitContextByBasePath.get(basePath) ?? null;
|
|
20
|
+
if (guidedUnitContextByBasePath.size === 1) return guidedUnitContextByBasePath.values().next().value!;
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function clearGuidedUnitContext(basePath?: string): void {
|
|
25
|
+
if (basePath) {
|
|
26
|
+
guidedUnitContextByBasePath.delete(basePath);
|
|
27
|
+
} else {
|
|
28
|
+
guidedUnitContextByBasePath.clear();
|
|
29
|
+
}
|
|
30
|
+
}
|