gsd-pi 2.14.0 → 2.14.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/resources/extensions/gsd/auto.ts +17 -23
- package/dist/resources/extensions/gsd/gitignore.ts +23 -1
- package/dist/resources/extensions/gsd/prompts/discuss.md +10 -4
- package/package.json +1 -1
- package/src/resources/extensions/gsd/auto.ts +17 -23
- package/src/resources/extensions/gsd/gitignore.ts +23 -1
- package/src/resources/extensions/gsd/prompts/discuss.md +10 -4
|
@@ -335,10 +335,12 @@ function startDispatchGapWatchdog(ctx: ExtensionContext, pi: ExtensionAPI): void
|
|
|
335
335
|
|
|
336
336
|
// Auto-mode is active but no unit was dispatched — the state machine stalled.
|
|
337
337
|
// Re-derive state and attempt a fresh dispatch.
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
338
|
+
if (verbose) {
|
|
339
|
+
ctx.ui.notify(
|
|
340
|
+
"Dispatch gap detected — re-evaluating state.",
|
|
341
|
+
"info",
|
|
342
|
+
);
|
|
343
|
+
}
|
|
342
344
|
|
|
343
345
|
try {
|
|
344
346
|
await dispatchNextUnit(ctx, pi);
|
|
@@ -1507,7 +1509,7 @@ async function dispatchNextUnit(
|
|
|
1507
1509
|
): Promise<void> {
|
|
1508
1510
|
if (!active || !cmdCtx) {
|
|
1509
1511
|
if (active && !cmdCtx) {
|
|
1510
|
-
ctx.ui.notify("Auto-mode
|
|
1512
|
+
ctx.ui.notify("Auto-mode session expired. Run /gsd auto to restart.", "info");
|
|
1511
1513
|
}
|
|
1512
1514
|
return;
|
|
1513
1515
|
}
|
|
@@ -1518,7 +1520,7 @@ async function dispatchNextUnit(
|
|
|
1518
1520
|
return; // Another dispatch is in progress — bail silently
|
|
1519
1521
|
}
|
|
1520
1522
|
_dispatching = true;
|
|
1521
|
-
|
|
1523
|
+
try {
|
|
1522
1524
|
// Recursion depth guard: when many units are skipped in sequence (e.g., after
|
|
1523
1525
|
// crash recovery with 10+ completed units), recursive dispatchNextUnit calls
|
|
1524
1526
|
// can freeze the TUI or overflow the stack. Yield generously after MAX_SKIP_DEPTH.
|
|
@@ -1861,7 +1863,7 @@ async function dispatchNextUnit(
|
|
|
1861
1863
|
saveActivityLog(ctx, basePath, currentUnit.type, currentUnit.id);
|
|
1862
1864
|
}
|
|
1863
1865
|
await stopAuto(ctx, pi);
|
|
1864
|
-
ctx.ui.notify(`
|
|
1866
|
+
ctx.ui.notify(`Unhandled phase "${state.phase}" — run /gsd doctor to diagnose.`, "info");
|
|
1865
1867
|
return;
|
|
1866
1868
|
}
|
|
1867
1869
|
}
|
|
@@ -2181,7 +2183,7 @@ async function dispatchNextUnit(
|
|
|
2181
2183
|
const result = await cmdCtx!.newSession();
|
|
2182
2184
|
if (result.cancelled) {
|
|
2183
2185
|
await stopAuto(ctx, pi);
|
|
2184
|
-
ctx.ui.notify("
|
|
2186
|
+
ctx.ui.notify("Auto-mode stopped.", "info");
|
|
2185
2187
|
return;
|
|
2186
2188
|
}
|
|
2187
2189
|
|
|
@@ -2287,7 +2289,7 @@ async function dispatchNextUnit(
|
|
|
2287
2289
|
}
|
|
2288
2290
|
}
|
|
2289
2291
|
if (!model) {
|
|
2290
|
-
ctx.ui.notify(`Model ${modelId} not found
|
|
2292
|
+
if (verbose) ctx.ui.notify(`Model ${modelId} not found, trying fallback.`, "info");
|
|
2291
2293
|
continue;
|
|
2292
2294
|
}
|
|
2293
2295
|
|
|
@@ -2303,25 +2305,14 @@ async function dispatchNextUnit(
|
|
|
2303
2305
|
} else {
|
|
2304
2306
|
const nextModel = modelsToTry[modelsToTry.indexOf(modelId) + 1];
|
|
2305
2307
|
if (nextModel) {
|
|
2306
|
-
ctx.ui.notify(
|
|
2307
|
-
`Failed to set model ${modelId}, trying fallback ${nextModel}...`,
|
|
2308
|
-
"warning",
|
|
2309
|
-
);
|
|
2308
|
+
if (verbose) ctx.ui.notify(`Failed to set model ${modelId}, trying ${nextModel}...`, "info");
|
|
2310
2309
|
} else {
|
|
2311
|
-
ctx.ui.notify(
|
|
2312
|
-
`Failed to set model ${modelId} and all fallbacks exhausted. Using default model.`,
|
|
2313
|
-
"warning",
|
|
2314
|
-
);
|
|
2310
|
+
ctx.ui.notify(`All preferred models unavailable for ${unitType}. Using default.`, "warning");
|
|
2315
2311
|
}
|
|
2316
2312
|
}
|
|
2317
2313
|
}
|
|
2318
2314
|
|
|
2319
|
-
|
|
2320
|
-
ctx.ui.notify(
|
|
2321
|
-
`Could not set any preferred model for ${unitType}. Continuing with default.`,
|
|
2322
|
-
"warning",
|
|
2323
|
-
);
|
|
2324
|
-
}
|
|
2315
|
+
// modelSet=false is already handled by the "all fallbacks exhausted" warning above
|
|
2325
2316
|
}
|
|
2326
2317
|
|
|
2327
2318
|
// Start progress-aware supervision: a soft warning, an idle watchdog, and
|
|
@@ -2434,6 +2425,9 @@ async function dispatchNextUnit(
|
|
|
2434
2425
|
);
|
|
2435
2426
|
await pauseAuto(ctx, pi);
|
|
2436
2427
|
}
|
|
2428
|
+
} finally {
|
|
2429
|
+
_dispatching = false;
|
|
2430
|
+
}
|
|
2437
2431
|
}
|
|
2438
2432
|
|
|
2439
2433
|
// ─── Skill Discovery ──────────────────────────────────────────────────────────
|
|
@@ -87,6 +87,28 @@ export function ensureGitignore(basePath: string): boolean {
|
|
|
87
87
|
existing = readFileSync(gitignorePath, "utf-8");
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
+
// Self-heal: remove blanket ".gsd/" lines from pre-v2.14.0 projects.
|
|
91
|
+
// The blanket ignore prevented planning artifacts (.gsd/milestones/) from
|
|
92
|
+
// being tracked in git, causing artifacts to vanish in worktrees and
|
|
93
|
+
// triggering loop detection failures. Replace with explicit runtime-only
|
|
94
|
+
// ignores so planning files are tracked naturally.
|
|
95
|
+
let modified = false;
|
|
96
|
+
const lines = existing.split("\n");
|
|
97
|
+
const filteredLines = lines.filter(line => {
|
|
98
|
+
const trimmed = line.trim();
|
|
99
|
+
// Remove standalone ".gsd/" lines (blanket ignore) but keep specific
|
|
100
|
+
// .gsd/ subpath patterns like ".gsd/activity/" or ".gsd/auto.lock"
|
|
101
|
+
if (trimmed === ".gsd/" || trimmed === ".gsd") {
|
|
102
|
+
modified = true;
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
return true;
|
|
106
|
+
});
|
|
107
|
+
if (modified) {
|
|
108
|
+
existing = filteredLines.join("\n");
|
|
109
|
+
writeFileSync(gitignorePath, existing, "utf-8");
|
|
110
|
+
}
|
|
111
|
+
|
|
90
112
|
// Parse existing lines (trimmed, ignoring comments and blanks)
|
|
91
113
|
const existingLines = new Set(
|
|
92
114
|
existing
|
|
@@ -98,7 +120,7 @@ export function ensureGitignore(basePath: string): boolean {
|
|
|
98
120
|
// Find patterns not yet present
|
|
99
121
|
const missing = BASELINE_PATTERNS.filter((p) => !existingLines.has(p));
|
|
100
122
|
|
|
101
|
-
if (missing.length === 0) return
|
|
123
|
+
if (missing.length === 0) return modified;
|
|
102
124
|
|
|
103
125
|
// Build the block to append
|
|
104
126
|
const block = [
|
|
@@ -91,13 +91,19 @@ Do not count the reflection step as a question round. Rounds start after reflect
|
|
|
91
91
|
|
|
92
92
|
## Depth Verification
|
|
93
93
|
|
|
94
|
-
Before moving to the wrap-up gate, present a structured depth summary
|
|
94
|
+
Before moving to the wrap-up gate, present a structured depth summary as a checkpoint.
|
|
95
95
|
|
|
96
|
-
|
|
96
|
+
**Print the summary as normal chat text first** — this is where the formatting renders properly. Structure the summary across the depth checklist dimensions using the user's own terminology and framing. Cover: what you understood them to be building, what shaped your understanding most (their emphasis, constraints, concerns), and any areas where you're least confident in your understanding.
|
|
97
97
|
|
|
98
|
-
**
|
|
98
|
+
**Then** use `ask_user_questions` with a short confirmation question — NOT the summary itself. The question field is designed for single sentences, not multi-paragraph summaries.
|
|
99
99
|
|
|
100
|
-
|
|
100
|
+
**Convention:** The question ID must contain `depth_verification` (e.g., `depth_verification_confirm`). This naming convention enables downstream mechanical detection of this step.
|
|
101
|
+
|
|
102
|
+
Example flow:
|
|
103
|
+
1. Print in chat: the full depth summary with markdown formatting (headers, bold, bullets)
|
|
104
|
+
2. Call `ask_user_questions` with: header "Depth Check", question "Did I capture the depth right?", options "Yes, you got it (Recommended)" and "Not quite — let me clarify"
|
|
105
|
+
|
|
106
|
+
If they clarify, absorb the correction and re-verify.
|
|
101
107
|
|
|
102
108
|
## Wrap-up Gate
|
|
103
109
|
|
package/package.json
CHANGED
|
@@ -335,10 +335,12 @@ function startDispatchGapWatchdog(ctx: ExtensionContext, pi: ExtensionAPI): void
|
|
|
335
335
|
|
|
336
336
|
// Auto-mode is active but no unit was dispatched — the state machine stalled.
|
|
337
337
|
// Re-derive state and attempt a fresh dispatch.
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
338
|
+
if (verbose) {
|
|
339
|
+
ctx.ui.notify(
|
|
340
|
+
"Dispatch gap detected — re-evaluating state.",
|
|
341
|
+
"info",
|
|
342
|
+
);
|
|
343
|
+
}
|
|
342
344
|
|
|
343
345
|
try {
|
|
344
346
|
await dispatchNextUnit(ctx, pi);
|
|
@@ -1507,7 +1509,7 @@ async function dispatchNextUnit(
|
|
|
1507
1509
|
): Promise<void> {
|
|
1508
1510
|
if (!active || !cmdCtx) {
|
|
1509
1511
|
if (active && !cmdCtx) {
|
|
1510
|
-
ctx.ui.notify("Auto-mode
|
|
1512
|
+
ctx.ui.notify("Auto-mode session expired. Run /gsd auto to restart.", "info");
|
|
1511
1513
|
}
|
|
1512
1514
|
return;
|
|
1513
1515
|
}
|
|
@@ -1518,7 +1520,7 @@ async function dispatchNextUnit(
|
|
|
1518
1520
|
return; // Another dispatch is in progress — bail silently
|
|
1519
1521
|
}
|
|
1520
1522
|
_dispatching = true;
|
|
1521
|
-
|
|
1523
|
+
try {
|
|
1522
1524
|
// Recursion depth guard: when many units are skipped in sequence (e.g., after
|
|
1523
1525
|
// crash recovery with 10+ completed units), recursive dispatchNextUnit calls
|
|
1524
1526
|
// can freeze the TUI or overflow the stack. Yield generously after MAX_SKIP_DEPTH.
|
|
@@ -1861,7 +1863,7 @@ async function dispatchNextUnit(
|
|
|
1861
1863
|
saveActivityLog(ctx, basePath, currentUnit.type, currentUnit.id);
|
|
1862
1864
|
}
|
|
1863
1865
|
await stopAuto(ctx, pi);
|
|
1864
|
-
ctx.ui.notify(`
|
|
1866
|
+
ctx.ui.notify(`Unhandled phase "${state.phase}" — run /gsd doctor to diagnose.`, "info");
|
|
1865
1867
|
return;
|
|
1866
1868
|
}
|
|
1867
1869
|
}
|
|
@@ -2181,7 +2183,7 @@ async function dispatchNextUnit(
|
|
|
2181
2183
|
const result = await cmdCtx!.newSession();
|
|
2182
2184
|
if (result.cancelled) {
|
|
2183
2185
|
await stopAuto(ctx, pi);
|
|
2184
|
-
ctx.ui.notify("
|
|
2186
|
+
ctx.ui.notify("Auto-mode stopped.", "info");
|
|
2185
2187
|
return;
|
|
2186
2188
|
}
|
|
2187
2189
|
|
|
@@ -2287,7 +2289,7 @@ async function dispatchNextUnit(
|
|
|
2287
2289
|
}
|
|
2288
2290
|
}
|
|
2289
2291
|
if (!model) {
|
|
2290
|
-
ctx.ui.notify(`Model ${modelId} not found
|
|
2292
|
+
if (verbose) ctx.ui.notify(`Model ${modelId} not found, trying fallback.`, "info");
|
|
2291
2293
|
continue;
|
|
2292
2294
|
}
|
|
2293
2295
|
|
|
@@ -2303,25 +2305,14 @@ async function dispatchNextUnit(
|
|
|
2303
2305
|
} else {
|
|
2304
2306
|
const nextModel = modelsToTry[modelsToTry.indexOf(modelId) + 1];
|
|
2305
2307
|
if (nextModel) {
|
|
2306
|
-
ctx.ui.notify(
|
|
2307
|
-
`Failed to set model ${modelId}, trying fallback ${nextModel}...`,
|
|
2308
|
-
"warning",
|
|
2309
|
-
);
|
|
2308
|
+
if (verbose) ctx.ui.notify(`Failed to set model ${modelId}, trying ${nextModel}...`, "info");
|
|
2310
2309
|
} else {
|
|
2311
|
-
ctx.ui.notify(
|
|
2312
|
-
`Failed to set model ${modelId} and all fallbacks exhausted. Using default model.`,
|
|
2313
|
-
"warning",
|
|
2314
|
-
);
|
|
2310
|
+
ctx.ui.notify(`All preferred models unavailable for ${unitType}. Using default.`, "warning");
|
|
2315
2311
|
}
|
|
2316
2312
|
}
|
|
2317
2313
|
}
|
|
2318
2314
|
|
|
2319
|
-
|
|
2320
|
-
ctx.ui.notify(
|
|
2321
|
-
`Could not set any preferred model for ${unitType}. Continuing with default.`,
|
|
2322
|
-
"warning",
|
|
2323
|
-
);
|
|
2324
|
-
}
|
|
2315
|
+
// modelSet=false is already handled by the "all fallbacks exhausted" warning above
|
|
2325
2316
|
}
|
|
2326
2317
|
|
|
2327
2318
|
// Start progress-aware supervision: a soft warning, an idle watchdog, and
|
|
@@ -2434,6 +2425,9 @@ async function dispatchNextUnit(
|
|
|
2434
2425
|
);
|
|
2435
2426
|
await pauseAuto(ctx, pi);
|
|
2436
2427
|
}
|
|
2428
|
+
} finally {
|
|
2429
|
+
_dispatching = false;
|
|
2430
|
+
}
|
|
2437
2431
|
}
|
|
2438
2432
|
|
|
2439
2433
|
// ─── Skill Discovery ──────────────────────────────────────────────────────────
|
|
@@ -87,6 +87,28 @@ export function ensureGitignore(basePath: string): boolean {
|
|
|
87
87
|
existing = readFileSync(gitignorePath, "utf-8");
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
+
// Self-heal: remove blanket ".gsd/" lines from pre-v2.14.0 projects.
|
|
91
|
+
// The blanket ignore prevented planning artifacts (.gsd/milestones/) from
|
|
92
|
+
// being tracked in git, causing artifacts to vanish in worktrees and
|
|
93
|
+
// triggering loop detection failures. Replace with explicit runtime-only
|
|
94
|
+
// ignores so planning files are tracked naturally.
|
|
95
|
+
let modified = false;
|
|
96
|
+
const lines = existing.split("\n");
|
|
97
|
+
const filteredLines = lines.filter(line => {
|
|
98
|
+
const trimmed = line.trim();
|
|
99
|
+
// Remove standalone ".gsd/" lines (blanket ignore) but keep specific
|
|
100
|
+
// .gsd/ subpath patterns like ".gsd/activity/" or ".gsd/auto.lock"
|
|
101
|
+
if (trimmed === ".gsd/" || trimmed === ".gsd") {
|
|
102
|
+
modified = true;
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
return true;
|
|
106
|
+
});
|
|
107
|
+
if (modified) {
|
|
108
|
+
existing = filteredLines.join("\n");
|
|
109
|
+
writeFileSync(gitignorePath, existing, "utf-8");
|
|
110
|
+
}
|
|
111
|
+
|
|
90
112
|
// Parse existing lines (trimmed, ignoring comments and blanks)
|
|
91
113
|
const existingLines = new Set(
|
|
92
114
|
existing
|
|
@@ -98,7 +120,7 @@ export function ensureGitignore(basePath: string): boolean {
|
|
|
98
120
|
// Find patterns not yet present
|
|
99
121
|
const missing = BASELINE_PATTERNS.filter((p) => !existingLines.has(p));
|
|
100
122
|
|
|
101
|
-
if (missing.length === 0) return
|
|
123
|
+
if (missing.length === 0) return modified;
|
|
102
124
|
|
|
103
125
|
// Build the block to append
|
|
104
126
|
const block = [
|
|
@@ -91,13 +91,19 @@ Do not count the reflection step as a question round. Rounds start after reflect
|
|
|
91
91
|
|
|
92
92
|
## Depth Verification
|
|
93
93
|
|
|
94
|
-
Before moving to the wrap-up gate, present a structured depth summary
|
|
94
|
+
Before moving to the wrap-up gate, present a structured depth summary as a checkpoint.
|
|
95
95
|
|
|
96
|
-
|
|
96
|
+
**Print the summary as normal chat text first** — this is where the formatting renders properly. Structure the summary across the depth checklist dimensions using the user's own terminology and framing. Cover: what you understood them to be building, what shaped your understanding most (their emphasis, constraints, concerns), and any areas where you're least confident in your understanding.
|
|
97
97
|
|
|
98
|
-
**
|
|
98
|
+
**Then** use `ask_user_questions` with a short confirmation question — NOT the summary itself. The question field is designed for single sentences, not multi-paragraph summaries.
|
|
99
99
|
|
|
100
|
-
|
|
100
|
+
**Convention:** The question ID must contain `depth_verification` (e.g., `depth_verification_confirm`). This naming convention enables downstream mechanical detection of this step.
|
|
101
|
+
|
|
102
|
+
Example flow:
|
|
103
|
+
1. Print in chat: the full depth summary with markdown formatting (headers, bold, bullets)
|
|
104
|
+
2. Call `ask_user_questions` with: header "Depth Check", question "Did I capture the depth right?", options "Yes, you got it (Recommended)" and "Not quite — let me clarify"
|
|
105
|
+
|
|
106
|
+
If they clarify, absorb the correction and re-verify.
|
|
101
107
|
|
|
102
108
|
## Wrap-up Gate
|
|
103
109
|
|