gsd-pi 2.82.0-dev.3709f22a5 → 2.82.0-dev.3a3c6509d
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 +53 -29
- package/dist/resources/extensions/gsd/auto-direct-dispatch.js +1 -0
- package/dist/resources/extensions/gsd/auto-dispatch.js +12 -18
- package/dist/resources/extensions/gsd/auto-post-unit.js +1 -1
- 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 +0 -1
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +6 -1
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +1 -1
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +1 -1
- 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/forensics.js +3 -3
- 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 +8 -5
- package/dist/resources/extensions/gsd/queue-reorder-ui.js +30 -13
- package/dist/resources/extensions/gsd/state.js +1 -1
- package/dist/resources/extensions/gsd/status-guards.js +7 -0
- package/dist/resources/extensions/gsd/workflow-mcp.js +17 -1
- package/dist/resources/extensions/gsd/worktree-manager.js +1 -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/core/chat-controller-ordering.test.js +44 -3
- package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.js +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +71 -97
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-ordering.test.js +12 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-ordering.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +19 -8
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/src/core/chat-controller-ordering.test.ts +53 -3
- package/packages/pi-coding-agent/src/core/sdk.ts +1 -1
- package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +75 -102
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode-ordering.test.ts +14 -0
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +23 -8
- 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 +60 -36
- package/src/resources/extensions/gsd/auto-direct-dispatch.ts +1 -0
- package/src/resources/extensions/gsd/auto-dispatch.ts +12 -18
- package/src/resources/extensions/gsd/auto-post-unit.ts +1 -1
- 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 +0 -1
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +9 -1
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +1 -1
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +1 -1
- 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/forensics.ts +3 -3
- 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 +8 -5
- package/src/resources/extensions/gsd/queue-reorder-ui.ts +31 -13
- package/src/resources/extensions/gsd/state.ts +1 -1
- package/src/resources/extensions/gsd/status-guards.ts +8 -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-recovery.test.ts +76 -5
- package/src/resources/extensions/gsd/tests/checkout-branch-stash-guard.test.ts +87 -0
- 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/dispatch-complete-milestone-guard.test.ts +27 -0
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +11 -0
- 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/pipeline-variant-dispatch.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +59 -0
- package/src/resources/extensions/gsd/tests/provider-errors.test.ts +18 -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/status-guards.test.ts +13 -1
- package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +17 -0
- 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/src/resources/extensions/gsd/worktree-manager.ts +1 -1
- /package/dist/web/standalone/.next/static/{kkGf3_VaPFkiDNV_D7Dtl → O6femb9LLl3nlgsDaYwS-}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{kkGf3_VaPFkiDNV_D7Dtl → O6femb9LLl3nlgsDaYwS-}/_ssgManifest.js +0 -0
|
@@ -237,6 +237,8 @@ export function readIntegrationBranch(basePath, milestoneId) {
|
|
|
237
237
|
return null;
|
|
238
238
|
}
|
|
239
239
|
}
|
|
240
|
+
/** Re-export for backward compatibility — canonical definitions in branch-patterns.ts */
|
|
241
|
+
export { QUICK_BRANCH_RE, WORKFLOW_BRANCH_RE } from "./branch-patterns.js";
|
|
240
242
|
/**
|
|
241
243
|
* Persist the integration branch for a milestone.
|
|
242
244
|
*
|
|
@@ -247,9 +249,11 @@ export function readIntegrationBranch(basePath, milestoneId) {
|
|
|
247
249
|
*
|
|
248
250
|
* The file is committed immediately so the metadata is persisted in git.
|
|
249
251
|
*/
|
|
250
|
-
/** Re-export for backward compatibility — canonical definitions in branch-patterns.ts */
|
|
251
|
-
export { QUICK_BRANCH_RE, WORKFLOW_BRANCH_RE } from "./branch-patterns.js";
|
|
252
252
|
export function writeIntegrationBranch(basePath, milestoneId, branch) {
|
|
253
|
+
// Never persist milestone branches as integration targets.
|
|
254
|
+
// They are ephemeral execution branches and can cause self-diff corruption.
|
|
255
|
+
if (branch.startsWith("milestone/"))
|
|
256
|
+
return;
|
|
253
257
|
// Don't record slice branches as the integration target
|
|
254
258
|
if (SLICE_BRANCH_RE.test(branch))
|
|
255
259
|
return;
|
|
@@ -54,18 +54,19 @@ const providerLoader = createSqliteProviderLoader({
|
|
|
54
54
|
writeStderr: (message) => process.stderr.write(message),
|
|
55
55
|
});
|
|
56
56
|
export const SCHEMA_VERSION = 28;
|
|
57
|
-
function initSchema(db, fileBacked) {
|
|
57
|
+
function initSchema(db, fileBacked, dbPath) {
|
|
58
|
+
const conservativeFilePragmas = fileBacked && _isLikelyWslDrvFsPathForTest(dbPath);
|
|
58
59
|
if (fileBacked)
|
|
59
|
-
db.exec("PRAGMA journal_mode=WAL");
|
|
60
|
+
db.exec(conservativeFilePragmas ? "PRAGMA journal_mode=DELETE" : "PRAGMA journal_mode=WAL");
|
|
60
61
|
if (fileBacked)
|
|
61
62
|
db.exec("PRAGMA busy_timeout = 5000");
|
|
62
63
|
if (fileBacked)
|
|
63
|
-
db.exec("PRAGMA synchronous = NORMAL");
|
|
64
|
+
db.exec(conservativeFilePragmas ? "PRAGMA synchronous = FULL" : "PRAGMA synchronous = NORMAL");
|
|
64
65
|
if (fileBacked)
|
|
65
66
|
db.exec("PRAGMA auto_vacuum = INCREMENTAL");
|
|
66
67
|
if (fileBacked)
|
|
67
68
|
db.exec("PRAGMA cache_size = -8000"); // 8 MB page cache
|
|
68
|
-
if (fileBacked && process.platform !== "darwin")
|
|
69
|
+
if (fileBacked && !conservativeFilePragmas && process.platform !== "darwin")
|
|
69
70
|
db.exec("PRAGMA mmap_size = 67108864"); // 64 MB mmap
|
|
70
71
|
db.exec("PRAGMA temp_store = MEMORY");
|
|
71
72
|
db.exec("PRAGMA foreign_keys = ON");
|
|
@@ -99,6 +100,19 @@ function initSchema(db, fileBacked) {
|
|
|
99
100
|
}
|
|
100
101
|
migrateSchema(db);
|
|
101
102
|
}
|
|
103
|
+
export function _isLikelyWslDrvFsPathForTest(dbPath) {
|
|
104
|
+
if (!dbPath || process.platform !== "linux")
|
|
105
|
+
return false;
|
|
106
|
+
const drvFsPathPattern = /^\/mnt\/[a-z](?:\/|$)/i;
|
|
107
|
+
if (drvFsPathPattern.test(dbPath))
|
|
108
|
+
return true;
|
|
109
|
+
try {
|
|
110
|
+
return drvFsPathPattern.test(realpathSync(dbPath));
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
102
116
|
/**
|
|
103
117
|
* Create the FTS5 virtual table for memories plus the triggers that keep it
|
|
104
118
|
* in sync with the base table. FTS5 may be unavailable on stripped-down
|
|
@@ -504,7 +518,7 @@ export function openDatabase(path) {
|
|
|
504
518
|
const adapter = createDbAdapter(rawDb);
|
|
505
519
|
const fileBacked = path !== ":memory:";
|
|
506
520
|
try {
|
|
507
|
-
initSchema(adapter, fileBacked);
|
|
521
|
+
initSchema(adapter, fileBacked, path);
|
|
508
522
|
}
|
|
509
523
|
catch (err) {
|
|
510
524
|
// Corrupt freelist: DDL fails with "malformed" but VACUUM can rebuild.
|
|
@@ -512,7 +526,7 @@ export function openDatabase(path) {
|
|
|
512
526
|
if (fileBacked && err instanceof Error && err.message?.includes("malformed")) {
|
|
513
527
|
try {
|
|
514
528
|
adapter.exec("VACUUM");
|
|
515
|
-
initSchema(adapter, fileBacked);
|
|
529
|
+
initSchema(adapter, fileBacked, path);
|
|
516
530
|
process.stderr.write("gsd-db: recovered corrupt database via VACUUM\n");
|
|
517
531
|
}
|
|
518
532
|
catch (retryErr) {
|
|
@@ -18,6 +18,7 @@ import { nativeAddPaths, nativeCommit } from "./native-git-bridge.js";
|
|
|
18
18
|
import { loadEffectiveGSDPreferences } from "./preferences.js";
|
|
19
19
|
import { saveQueueOrder } from "./queue-order.js";
|
|
20
20
|
import { findMilestoneIds, nextMilestoneId } from "./milestone-ids.js";
|
|
21
|
+
import { isFutureMilestoneStatus } from "./status-guards.js";
|
|
21
22
|
// ─── Queue Entry Point ──────────────────────────────────────────────────────
|
|
22
23
|
/**
|
|
23
24
|
* Queue future milestones via conversational intake.
|
|
@@ -48,7 +49,7 @@ export async function showQueue(ctx, pi, basePath) {
|
|
|
48
49
|
return;
|
|
49
50
|
}
|
|
50
51
|
// ── Count pending milestones ────────────────────────────────────────
|
|
51
|
-
const pendingMilestones = state.registry.filter(m => m.status
|
|
52
|
+
const pendingMilestones = state.registry.filter(m => isFutureMilestoneStatus(m.status) || m.status === "active");
|
|
52
53
|
const completeCount = state.registry.filter(m => m.status === "complete").length;
|
|
53
54
|
const parkedCount = state.registry.filter(m => m.status === "parked").length;
|
|
54
55
|
// ── If multiple pending milestones, show queue management hub ──────
|
|
@@ -140,7 +141,7 @@ export async function showQueueAdd(ctx, pi, basePath, state) {
|
|
|
140
141
|
const activePart = state.activeMilestone
|
|
141
142
|
? `Currently executing: ${state.activeMilestone.id} — ${state.activeMilestone.title} (phase: ${state.phase}).`
|
|
142
143
|
: "No milestone currently active.";
|
|
143
|
-
const pendingCount = state.registry.filter(m => m.status
|
|
144
|
+
const pendingCount = state.registry.filter(m => isFutureMilestoneStatus(m.status)).length;
|
|
144
145
|
const completeCount = state.registry.filter(m => m.status === "complete").length;
|
|
145
146
|
const preamble = [
|
|
146
147
|
`Queuing new work onto an existing GSD project.`,
|
|
@@ -223,7 +224,7 @@ export async function buildExistingMilestonesContext(basePath, milestoneIds, sta
|
|
|
223
224
|
}
|
|
224
225
|
// For active/pending/parked milestones, include the roadmap if it exists
|
|
225
226
|
// (shows what's planned but not yet built)
|
|
226
|
-
if (status === "active" || status
|
|
227
|
+
if (status === "active" || isFutureMilestoneStatus(status) || status === "parked") {
|
|
227
228
|
const roadmapFile = resolveMilestoneFile(basePath, mid, "ROADMAP");
|
|
228
229
|
if (roadmapFile) {
|
|
229
230
|
const content = await loadFile(roadmapFile);
|
|
@@ -30,6 +30,7 @@ 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";
|
|
@@ -811,6 +812,7 @@ async function dispatchWorkflow(pi, note, customType = "gsd-run", ctx, unitType,
|
|
|
811
812
|
? ctx.modelRegistry.getProviderAuthMode(ctx.model.provider)
|
|
812
813
|
: undefined,
|
|
813
814
|
baseUrl: result.appliedModel?.baseUrl ?? ctx.model?.baseUrl,
|
|
815
|
+
activeTools: typeof pi.getActiveTools === "function" ? pi.getActiveTools() : [],
|
|
814
816
|
});
|
|
815
817
|
if (compatibilityError) {
|
|
816
818
|
ctx.ui.notify(compatibilityError, "error");
|
|
@@ -1164,7 +1166,7 @@ export async function showDiscuss(ctx, pi, basePath) {
|
|
|
1164
1166
|
// No active milestone (or corrupted milestone with undefined id) —
|
|
1165
1167
|
// check for pending milestones to discuss instead
|
|
1166
1168
|
if (!state.activeMilestone?.id) {
|
|
1167
|
-
const pendingMilestones = state.registry.filter(m => m.status
|
|
1169
|
+
const pendingMilestones = state.registry.filter(m => isFutureMilestoneStatus(m.status));
|
|
1168
1170
|
if (pendingMilestones.length === 0) {
|
|
1169
1171
|
ctx.ui.notify("No active milestone. Run /gsd to create one first.", "warning");
|
|
1170
1172
|
return;
|
|
@@ -1269,7 +1271,7 @@ export async function showDiscuss(ctx, pi, basePath) {
|
|
|
1269
1271
|
const pendingSlices = normSlices.filter(s => !s.done);
|
|
1270
1272
|
if (pendingSlices.length === 0) {
|
|
1271
1273
|
// All slices complete — but queued milestones may still need discussion (#3150)
|
|
1272
|
-
const pendingMilestones = state.registry.filter(m => m.status
|
|
1274
|
+
const pendingMilestones = state.registry.filter(m => isFutureMilestoneStatus(m.status));
|
|
1273
1275
|
if (pendingMilestones.length > 0) {
|
|
1274
1276
|
await showDiscussQueuedMilestone(ctx, pi, basePath, pendingMilestones);
|
|
1275
1277
|
return;
|
|
@@ -1290,7 +1292,7 @@ export async function showDiscuss(ctx, pi, basePath) {
|
|
|
1290
1292
|
// If all pending slices are discussed, check for queued milestones before exiting (#3150)
|
|
1291
1293
|
const allDiscussed = pendingSlices.every(s => discussedMap.get(s.id));
|
|
1292
1294
|
if (allDiscussed) {
|
|
1293
|
-
const pendingMilestones = state.registry.filter(m => m.status
|
|
1295
|
+
const pendingMilestones = state.registry.filter(m => isFutureMilestoneStatus(m.status));
|
|
1294
1296
|
if (pendingMilestones.length > 0) {
|
|
1295
1297
|
await showDiscussQueuedMilestone(ctx, pi, basePath, pendingMilestones);
|
|
1296
1298
|
return;
|
|
@@ -1321,7 +1323,7 @@ export async function showDiscuss(ctx, pi, basePath) {
|
|
|
1321
1323
|
};
|
|
1322
1324
|
});
|
|
1323
1325
|
// Offer access to queued milestones when any exist
|
|
1324
|
-
const pendingMilestones = state.registry.filter(m => m.status
|
|
1326
|
+
const pendingMilestones = state.registry.filter(m => isFutureMilestoneStatus(m.status));
|
|
1325
1327
|
if (pendingMilestones.length > 0) {
|
|
1326
1328
|
actions.push({
|
|
1327
1329
|
id: "discuss_queued_milestone",
|
|
@@ -1385,10 +1387,11 @@ async function showDiscussQueuedMilestone(ctx, pi, basePath, pendingMilestones)
|
|
|
1385
1387
|
const hasContext = !!resolveMilestoneFile(basePath, m.id, "CONTEXT");
|
|
1386
1388
|
const hasDraft = !hasContext && !!resolveMilestoneFile(basePath, m.id, "CONTEXT-DRAFT");
|
|
1387
1389
|
const contextStatus = hasContext ? "context ✓" : hasDraft ? "draft context" : "no context yet";
|
|
1390
|
+
const statusLabel = m.status === "planned" ? "planned" : "queued";
|
|
1388
1391
|
return {
|
|
1389
1392
|
id: m.id,
|
|
1390
1393
|
label: `${m.id}: ${m.title}`,
|
|
1391
|
-
description: `[
|
|
1394
|
+
description: `[${statusLabel}] · ${contextStatus}`,
|
|
1392
1395
|
recommended: i === 0,
|
|
1393
1396
|
};
|
|
1394
1397
|
});
|
|
@@ -24,6 +24,7 @@ export async function showQueueReorder(ctx, completed, pending) {
|
|
|
24
24
|
const items = [...pending];
|
|
25
25
|
let cursor = 0;
|
|
26
26
|
let grabbed = false;
|
|
27
|
+
let scrollOffset = 0;
|
|
27
28
|
let cachedLines;
|
|
28
29
|
let validation;
|
|
29
30
|
// Mutable deps map — tracks removals during this session
|
|
@@ -128,9 +129,11 @@ export async function showQueueReorder(ctx, completed, pending) {
|
|
|
128
129
|
return cachedLines;
|
|
129
130
|
const ui = makeUI(theme, width);
|
|
130
131
|
const lines = [];
|
|
132
|
+
const queueRows = [];
|
|
131
133
|
const push = (...rows) => { for (const r of rows)
|
|
132
134
|
lines.push(...r); };
|
|
133
135
|
const add = (s) => truncateToWidth(s, width);
|
|
136
|
+
let cursorQueueRow = 0;
|
|
134
137
|
const headerText = grabbed ? " Queue Reorder — Moving Item" : " Queue Reorder";
|
|
135
138
|
push(ui.bar(), ui.blank(), ui.header(headerText), ui.blank());
|
|
136
139
|
// Completed milestones (dimmed)
|
|
@@ -153,13 +156,15 @@ export async function showQueueReorder(ctx, completed, pending) {
|
|
|
153
156
|
const num = i + 1;
|
|
154
157
|
const label = item.title && item.title !== item.id ? `${item.id} ${item.title}` : item.id;
|
|
155
158
|
if (isCursor && grabbed) {
|
|
156
|
-
|
|
159
|
+
cursorQueueRow = queueRows.length;
|
|
160
|
+
queueRows.push(add(` ${theme.fg("warning", `▸▸ ${num}. ${label}`)}`));
|
|
157
161
|
}
|
|
158
162
|
else if (isCursor) {
|
|
159
|
-
|
|
163
|
+
cursorQueueRow = queueRows.length;
|
|
164
|
+
queueRows.push(add(` ${theme.fg("accent", `${GLYPH.cursor} ${num}. ${label}`)}`));
|
|
160
165
|
}
|
|
161
166
|
else {
|
|
162
|
-
|
|
167
|
+
queueRows.push(add(` ${theme.fg("text", `${num}. ${label}`)}`));
|
|
163
168
|
}
|
|
164
169
|
// depends_on annotations
|
|
165
170
|
const deps = liveDeps.get(item.id) ?? [];
|
|
@@ -168,34 +173,35 @@ export async function showQueueReorder(ctx, completed, pending) {
|
|
|
168
173
|
continue;
|
|
169
174
|
const pairKey = `${item.id}:${dep}`;
|
|
170
175
|
if (violatedPairs.has(pairKey)) {
|
|
171
|
-
|
|
176
|
+
queueRows.push(add(` ${theme.fg("warning", `${GLYPH.statusWarning} depends_on: ${dep} — auto-removed on confirm`)}`));
|
|
172
177
|
}
|
|
173
178
|
else if (redundantPairs.has(pairKey)) {
|
|
174
|
-
|
|
179
|
+
queueRows.push(add(` ${theme.fg("dim", `↳ depends_on: ${dep} (redundant)`)}`));
|
|
175
180
|
}
|
|
176
181
|
else {
|
|
177
|
-
|
|
182
|
+
queueRows.push(add(` ${theme.fg("dim", `↳ depends_on: ${dep}`)}`));
|
|
178
183
|
}
|
|
179
184
|
}
|
|
180
185
|
// Missing deps
|
|
181
186
|
for (const v of validation.violations.filter(v => v.milestone === item.id && v.type === 'missing_dep')) {
|
|
182
|
-
|
|
187
|
+
queueRows.push(add(` ${theme.fg("error", `${GLYPH.statusWarning} depends_on: ${v.dependsOn} (does not exist)`)}`));
|
|
183
188
|
}
|
|
184
189
|
}
|
|
185
190
|
// Removed deps feedback
|
|
191
|
+
const trailingLines = [];
|
|
186
192
|
if (removedDeps.length > 0) {
|
|
187
|
-
push(ui.blank());
|
|
193
|
+
trailingLines.push(...ui.blank());
|
|
188
194
|
for (const r of removedDeps) {
|
|
189
|
-
|
|
195
|
+
trailingLines.push(add(` ${theme.fg("success", `${GLYPH.statusDone} Removed: ${r.milestone} depends_on ${r.dep}`)}`));
|
|
190
196
|
}
|
|
191
197
|
}
|
|
192
198
|
// Circular warning
|
|
193
199
|
const circ = validation.violations.find(v => v.type === 'circular');
|
|
194
200
|
if (circ) {
|
|
195
|
-
push(ui.blank());
|
|
196
|
-
|
|
201
|
+
trailingLines.push(...ui.blank());
|
|
202
|
+
trailingLines.push(add(` ${theme.fg("error", `${GLYPH.statusWarning} ${circ.message}`)}`));
|
|
197
203
|
}
|
|
198
|
-
push(ui.blank());
|
|
204
|
+
trailingLines.push(...ui.blank());
|
|
199
205
|
// Hints — context-sensitive based on grab state
|
|
200
206
|
const hints = [];
|
|
201
207
|
if (grabbed) {
|
|
@@ -215,7 +221,18 @@ export async function showQueueReorder(ctx, completed, pending) {
|
|
|
215
221
|
hints.push("enter ok");
|
|
216
222
|
}
|
|
217
223
|
hints.push("esc");
|
|
218
|
-
push(ui.hints(hints), ui.bar());
|
|
224
|
+
trailingLines.push(...ui.hints(hints), ...ui.bar());
|
|
225
|
+
const maxOverlayRows = Math.max(10, process.stdout.rows ? Math.floor(process.stdout.rows * 0.8) : 24);
|
|
226
|
+
const availableQueueRows = Math.max(1, maxOverlayRows - lines.length - trailingLines.length);
|
|
227
|
+
const maxScroll = Math.max(0, queueRows.length - availableQueueRows);
|
|
228
|
+
if (cursorQueueRow < scrollOffset) {
|
|
229
|
+
scrollOffset = cursorQueueRow;
|
|
230
|
+
}
|
|
231
|
+
else if (cursorQueueRow >= scrollOffset + availableQueueRows) {
|
|
232
|
+
scrollOffset = cursorQueueRow - availableQueueRows + 1;
|
|
233
|
+
}
|
|
234
|
+
scrollOffset = Math.min(Math.max(scrollOffset, 0), maxScroll);
|
|
235
|
+
lines.push(...queueRows.slice(scrollOffset, scrollOffset + availableQueueRows), ...trailingLines);
|
|
219
236
|
cachedLines = lines;
|
|
220
237
|
return lines;
|
|
221
238
|
}
|
|
@@ -1253,7 +1253,7 @@ export async function _deriveStateImpl(basePath, opts) {
|
|
|
1253
1253
|
const summaryPath = resolveTaskFile(basePath, activeMilestone.id, activeSlice.id, t.id, "SUMMARY");
|
|
1254
1254
|
if (summaryPath && existsSync(summaryPath)) {
|
|
1255
1255
|
t.done = true;
|
|
1256
|
-
logWarning("reconcile", `task ${activeMilestone.id}/${activeSlice.id}/${t.id} reconciled via SUMMARY on disk
|
|
1256
|
+
logWarning("reconcile", `task ${activeMilestone.id}/${activeSlice.id}/${t.id} reconciled via SUMMARY on disk`, { mid: activeMilestone.id, sid: activeSlice.id, tid: t.id });
|
|
1257
1257
|
}
|
|
1258
1258
|
}
|
|
1259
1259
|
const taskProgress = {
|
|
@@ -26,3 +26,10 @@ export function isInactiveStatus(status) {
|
|
|
26
26
|
export function isSkippedForDispatch(status) {
|
|
27
27
|
return isClosedStatus(status) || status === "parked" || isDeferredStatus(status);
|
|
28
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* Returns true when a milestone is future/backlog work (not currently executing).
|
|
31
|
+
* Includes legacy/project-specific alias "planned" for compatibility.
|
|
32
|
+
*/
|
|
33
|
+
export function isFutureMilestoneStatus(status) {
|
|
34
|
+
return status === "pending" || status === "queued" || status === "planned";
|
|
35
|
+
}
|
|
@@ -322,6 +322,16 @@ function hasAskUserQuestionsTool(activeTools) {
|
|
|
322
322
|
return toolSeparator >= 0 && toolName.slice(toolSeparator + 2) === "ask_user_questions";
|
|
323
323
|
});
|
|
324
324
|
}
|
|
325
|
+
function hasRequiredTool(requiredTool, activeTools) {
|
|
326
|
+
return activeTools.some((toolName) => {
|
|
327
|
+
if (toolName === requiredTool)
|
|
328
|
+
return true;
|
|
329
|
+
if (!toolName.startsWith("mcp__"))
|
|
330
|
+
return false;
|
|
331
|
+
const toolSeparator = toolName.indexOf("__", "mcp__".length);
|
|
332
|
+
return toolSeparator >= 0 && toolName.slice(toolSeparator + 2) === requiredTool;
|
|
333
|
+
});
|
|
334
|
+
}
|
|
325
335
|
function workflowMcpStructuredQuestionsOptIn(env = process.env) {
|
|
326
336
|
const value = env.GSD_WORKFLOW_MCP_STRUCTURED_QUESTIONS;
|
|
327
337
|
return value === "1" || value === "true";
|
|
@@ -352,8 +362,14 @@ export function getWorkflowTransportSupportError(provider, requiredTools, option
|
|
|
352
362
|
if (!launch) {
|
|
353
363
|
return `Provider ${providerLabel} cannot run ${surface}${unitLabel}: the GSD workflow MCP server is not configured or discoverable. Detected Claude Code model but no workflow MCP. Please run /gsd mcp init . from your project root. You can also configure GSD_WORKFLOW_MCP_COMMAND, build packages/mcp-server/dist/cli.js, or install gsd-mcp-server on PATH.`;
|
|
354
364
|
}
|
|
355
|
-
const
|
|
365
|
+
const uniqueRequired = [...new Set(requiredTools)];
|
|
366
|
+
const missing = (options.activeTools && options.activeTools.length > 0)
|
|
367
|
+
? uniqueRequired.filter((tool) => !hasRequiredTool(tool, options.activeTools))
|
|
368
|
+
: uniqueRequired.filter((tool) => !MCP_WORKFLOW_TOOL_SURFACE.has(tool));
|
|
356
369
|
if (missing.length === 0)
|
|
357
370
|
return null;
|
|
371
|
+
if (options.activeTools && options.activeTools.length > 0) {
|
|
372
|
+
return `Provider ${providerLabel} cannot run ${surface}${unitLabel}: this unit requires ${missing.join(", ")}, but the active runtime toolset currently exposes only ${options.activeTools.slice().sort().join(", ")}.`;
|
|
373
|
+
}
|
|
358
374
|
return `Provider ${providerLabel} cannot run ${surface}${unitLabel}: this unit requires ${missing.join(", ")}, but the workflow MCP transport currently exposes only ${Array.from(MCP_WORKFLOW_TOOL_SURFACE).sort().join(", ")}.`;
|
|
359
375
|
}
|
|
@@ -491,7 +491,7 @@ export function removeWorktree(basePath, name, opts = {}) {
|
|
|
491
491
|
const nestedGitPath = join(nestedDir, ".git");
|
|
492
492
|
try {
|
|
493
493
|
rmSync(nestedGitPath, { recursive: true, force: true });
|
|
494
|
-
logWarning("reconcile", `Removed nested .git directory from scaffolded project to prevent data loss
|
|
494
|
+
logWarning("reconcile", `Removed nested .git directory from scaffolded project to prevent data loss`, { worktree: name, nestedRepo: nestedDir });
|
|
495
495
|
}
|
|
496
496
|
catch {
|
|
497
497
|
logWarning("reconcile", `Failed to remove nested .git directory — files may be lost as orphaned gitlink`, { worktree: name, nestedRepo: nestedDir });
|