gsd-pi 2.38.0-dev.96dc7fb → 2.38.0-dev.98b44dc
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 +15 -11
- package/dist/app-paths.js +1 -1
- package/dist/extension-registry.js +2 -2
- package/dist/remote-questions-config.js +2 -2
- package/dist/resource-loader.js +34 -1
- package/dist/resources/extensions/browser-tools/index.js +3 -1
- package/dist/resources/extensions/browser-tools/tools/verify.js +97 -0
- package/dist/resources/extensions/env-utils.js +29 -0
- package/dist/resources/extensions/get-secrets-from-user.js +5 -24
- package/dist/resources/extensions/github-sync/cli.js +284 -0
- package/dist/resources/extensions/github-sync/index.js +73 -0
- package/dist/resources/extensions/github-sync/mapping.js +67 -0
- package/dist/resources/extensions/github-sync/sync.js +424 -0
- package/dist/resources/extensions/github-sync/templates.js +118 -0
- package/dist/resources/extensions/github-sync/types.js +7 -0
- package/dist/resources/extensions/gsd/auto/session.js +6 -23
- package/dist/resources/extensions/gsd/auto-dispatch.js +8 -9
- package/dist/resources/extensions/gsd/auto-loop.js +636 -594
- package/dist/resources/extensions/gsd/auto-post-unit.js +99 -70
- package/dist/resources/extensions/gsd/auto-prompts.js +202 -48
- package/dist/resources/extensions/gsd/auto-start.js +7 -1
- package/dist/resources/extensions/gsd/auto-worktree-sync.js +2 -1
- package/dist/resources/extensions/gsd/auto-worktree.js +3 -3
- package/dist/resources/extensions/gsd/auto.js +143 -96
- package/dist/resources/extensions/gsd/commands-extensions.js +3 -2
- package/dist/resources/extensions/gsd/commands-prefs-wizard.js +1 -1
- package/dist/resources/extensions/gsd/commands.js +4 -2
- package/dist/resources/extensions/gsd/context-budget.js +2 -10
- package/dist/resources/extensions/gsd/detection.js +1 -2
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +0 -2
- package/dist/resources/extensions/gsd/doctor-providers.js +30 -11
- package/dist/resources/extensions/gsd/doctor.js +20 -1
- package/dist/resources/extensions/gsd/exit-command.js +2 -1
- package/dist/resources/extensions/gsd/export.js +1 -1
- package/dist/resources/extensions/gsd/files.js +48 -9
- package/dist/resources/extensions/gsd/forensics.js +1 -1
- package/dist/resources/extensions/gsd/git-service.js +30 -12
- package/dist/resources/extensions/gsd/gitignore.js +16 -3
- package/dist/resources/extensions/gsd/guided-flow.js +149 -38
- package/dist/resources/extensions/gsd/health-widget-core.js +32 -70
- package/dist/resources/extensions/gsd/health-widget.js +3 -86
- package/dist/resources/extensions/gsd/index.js +24 -20
- package/dist/resources/extensions/gsd/migrate/parsers.js +1 -1
- package/dist/resources/extensions/gsd/migrate-external.js +18 -1
- package/dist/resources/extensions/gsd/native-git-bridge.js +37 -0
- package/dist/resources/extensions/gsd/paths.js +3 -0
- package/dist/resources/extensions/gsd/preferences-models.js +0 -12
- package/dist/resources/extensions/gsd/preferences-types.js +1 -1
- package/dist/resources/extensions/gsd/preferences-validation.js +59 -11
- package/dist/resources/extensions/gsd/preferences.js +22 -11
- package/dist/resources/extensions/gsd/prompt-loader.js +6 -2
- 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.md +11 -14
- package/dist/resources/extensions/gsd/prompts/execute-task.md +5 -3
- package/dist/resources/extensions/gsd/prompts/guided-complete-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +11 -12
- package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +8 -10
- package/dist/resources/extensions/gsd/prompts/guided-execute-task.md +1 -1
- package/dist/resources/extensions/gsd/prompts/guided-plan-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/guided-plan-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/guided-research-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/guided-resume-task.md +1 -1
- package/dist/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/queue.md +4 -8
- package/dist/resources/extensions/gsd/prompts/reactive-execute.md +11 -8
- package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +1 -1
- package/dist/resources/extensions/gsd/prompts/research-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/research-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/run-uat.md +28 -11
- package/dist/resources/extensions/gsd/prompts/workflow-start.md +2 -2
- package/dist/resources/extensions/gsd/repo-identity.js +21 -4
- package/dist/resources/extensions/gsd/resource-version.js +2 -1
- package/dist/resources/extensions/gsd/roadmap-mutations.js +24 -0
- package/dist/resources/extensions/gsd/state.js +42 -23
- package/dist/resources/extensions/gsd/templates/runtime.md +21 -0
- package/dist/resources/extensions/gsd/templates/task-plan.md +3 -0
- package/dist/resources/extensions/gsd/visualizer-data.js +1 -1
- package/dist/resources/extensions/mcp-client/index.js +14 -1
- package/dist/resources/extensions/remote-questions/status.js +4 -1
- package/dist/resources/extensions/remote-questions/store.js +4 -1
- package/dist/resources/extensions/search-the-web/provider.js +2 -1
- package/dist/resources/extensions/shared/frontmatter.js +1 -1
- package/dist/resources/extensions/subagent/isolation.js +2 -1
- package/dist/resources/extensions/ttsr/rule-loader.js +2 -1
- package/package.json +1 -1
- package/packages/pi-ai/dist/utils/oauth/anthropic.js +2 -2
- package/packages/pi-ai/dist/utils/oauth/anthropic.js.map +1 -1
- package/packages/pi-ai/src/utils/oauth/anthropic.ts +2 -2
- package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.js +205 -7
- package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/skills.d.ts +1 -0
- package/packages/pi-coding-agent/dist/core/skills.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/skills.js +6 -1
- package/packages/pi-coding-agent/dist/core/skills.js.map +1 -1
- package/packages/pi-coding-agent/dist/index.d.ts +1 -1
- package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/index.js +1 -1
- package/packages/pi-coding-agent/dist/index.js.map +1 -1
- package/packages/pi-coding-agent/src/core/extensions/loader.ts +223 -7
- package/packages/pi-coding-agent/src/core/skills.ts +9 -1
- package/packages/pi-coding-agent/src/index.ts +1 -0
- package/src/resources/extensions/browser-tools/index.ts +3 -0
- package/src/resources/extensions/browser-tools/tools/verify.ts +117 -0
- package/src/resources/extensions/env-utils.ts +31 -0
- package/src/resources/extensions/get-secrets-from-user.ts +5 -24
- package/src/resources/extensions/github-sync/cli.ts +364 -0
- package/src/resources/extensions/github-sync/index.ts +93 -0
- package/src/resources/extensions/github-sync/mapping.ts +81 -0
- package/src/resources/extensions/github-sync/sync.ts +556 -0
- package/src/resources/extensions/github-sync/templates.ts +183 -0
- package/src/resources/extensions/github-sync/tests/cli.test.ts +20 -0
- package/src/resources/extensions/github-sync/tests/commit-linking.test.ts +39 -0
- package/src/resources/extensions/github-sync/tests/mapping.test.ts +104 -0
- package/src/resources/extensions/github-sync/tests/templates.test.ts +110 -0
- package/src/resources/extensions/github-sync/types.ts +47 -0
- package/src/resources/extensions/gsd/auto/session.ts +7 -25
- package/src/resources/extensions/gsd/auto-dispatch.ts +7 -9
- package/src/resources/extensions/gsd/auto-loop.ts +526 -545
- package/src/resources/extensions/gsd/auto-post-unit.ts +80 -44
- package/src/resources/extensions/gsd/auto-prompts.ts +247 -50
- package/src/resources/extensions/gsd/auto-start.ts +11 -1
- package/src/resources/extensions/gsd/auto-worktree-sync.ts +3 -1
- package/src/resources/extensions/gsd/auto-worktree.ts +3 -3
- package/src/resources/extensions/gsd/auto.ts +139 -101
- package/src/resources/extensions/gsd/commands-extensions.ts +4 -2
- package/src/resources/extensions/gsd/commands-prefs-wizard.ts +1 -1
- package/src/resources/extensions/gsd/commands.ts +5 -3
- package/src/resources/extensions/gsd/context-budget.ts +2 -12
- package/src/resources/extensions/gsd/detection.ts +2 -2
- package/src/resources/extensions/gsd/docs/preferences-reference.md +0 -2
- package/src/resources/extensions/gsd/doctor-providers.ts +30 -9
- package/src/resources/extensions/gsd/doctor.ts +22 -1
- package/src/resources/extensions/gsd/exit-command.ts +2 -2
- package/src/resources/extensions/gsd/export.ts +1 -1
- package/src/resources/extensions/gsd/files.ts +51 -11
- package/src/resources/extensions/gsd/forensics.ts +1 -1
- package/src/resources/extensions/gsd/git-service.ts +44 -10
- package/src/resources/extensions/gsd/gitignore.ts +17 -3
- package/src/resources/extensions/gsd/guided-flow.ts +177 -44
- package/src/resources/extensions/gsd/health-widget-core.ts +28 -80
- package/src/resources/extensions/gsd/health-widget.ts +3 -89
- package/src/resources/extensions/gsd/index.ts +24 -17
- package/src/resources/extensions/gsd/migrate/parsers.ts +1 -1
- package/src/resources/extensions/gsd/migrate-external.ts +18 -1
- package/src/resources/extensions/gsd/native-git-bridge.ts +37 -0
- package/src/resources/extensions/gsd/paths.ts +4 -0
- package/src/resources/extensions/gsd/preferences-models.ts +0 -12
- package/src/resources/extensions/gsd/preferences-types.ts +4 -4
- package/src/resources/extensions/gsd/preferences-validation.ts +51 -11
- package/src/resources/extensions/gsd/preferences.ts +25 -11
- package/src/resources/extensions/gsd/prompt-loader.ts +7 -2
- 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.md +11 -14
- package/src/resources/extensions/gsd/prompts/execute-task.md +5 -3
- package/src/resources/extensions/gsd/prompts/guided-complete-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +11 -12
- package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +8 -10
- package/src/resources/extensions/gsd/prompts/guided-execute-task.md +1 -1
- package/src/resources/extensions/gsd/prompts/guided-plan-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/guided-plan-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/guided-research-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/guided-resume-task.md +1 -1
- package/src/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/queue.md +4 -8
- package/src/resources/extensions/gsd/prompts/reactive-execute.md +11 -8
- package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +1 -1
- package/src/resources/extensions/gsd/prompts/research-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/research-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/run-uat.md +28 -11
- package/src/resources/extensions/gsd/prompts/workflow-start.md +2 -2
- package/src/resources/extensions/gsd/repo-identity.ts +23 -4
- package/src/resources/extensions/gsd/resource-version.ts +3 -1
- package/src/resources/extensions/gsd/roadmap-mutations.ts +29 -0
- package/src/resources/extensions/gsd/state.ts +39 -21
- package/src/resources/extensions/gsd/templates/runtime.md +21 -0
- package/src/resources/extensions/gsd/templates/task-plan.md +3 -0
- package/src/resources/extensions/gsd/tests/agent-end-retry.test.ts +21 -18
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +122 -68
- package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +4 -3
- package/src/resources/extensions/gsd/tests/derive-state.test.ts +43 -0
- package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +86 -3
- package/src/resources/extensions/gsd/tests/gitignore-tracked-gsd.test.ts +50 -0
- package/src/resources/extensions/gsd/tests/health-widget.test.ts +16 -54
- package/src/resources/extensions/gsd/tests/parsers.test.ts +131 -14
- package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +209 -0
- package/src/resources/extensions/gsd/tests/preferences.test.ts +2 -7
- package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +59 -0
- package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +21 -1
- package/src/resources/extensions/gsd/tests/run-uat.test.ts +16 -4
- package/src/resources/extensions/gsd/tests/skill-activation.test.ts +140 -0
- package/src/resources/extensions/gsd/types.ts +18 -1
- package/src/resources/extensions/gsd/verification-evidence.ts +16 -0
- package/src/resources/extensions/gsd/visualizer-data.ts +1 -1
- package/src/resources/extensions/mcp-client/index.ts +17 -1
- package/src/resources/extensions/remote-questions/status.ts +5 -1
- package/src/resources/extensions/remote-questions/store.ts +5 -1
- package/src/resources/extensions/search-the-web/provider.ts +2 -1
- package/src/resources/extensions/shared/frontmatter.ts +1 -1
- package/src/resources/extensions/subagent/isolation.ts +3 -1
- package/src/resources/extensions/ttsr/rule-loader.ts +3 -1
- package/dist/resources/extensions/gsd/prompt-compressor.js +0 -393
- package/dist/resources/extensions/gsd/semantic-chunker.js +0 -254
- package/dist/resources/extensions/gsd/summary-distiller.js +0 -212
- package/src/resources/extensions/gsd/prompt-compressor.ts +0 -508
- package/src/resources/extensions/gsd/semantic-chunker.ts +0 -336
- package/src/resources/extensions/gsd/summary-distiller.ts +0 -258
- package/src/resources/extensions/gsd/tests/context-compression.test.ts +0 -193
- package/src/resources/extensions/gsd/tests/prompt-compressor.test.ts +0 -529
- package/src/resources/extensions/gsd/tests/semantic-chunker.test.ts +0 -426
- package/src/resources/extensions/gsd/tests/summary-distiller.test.ts +0 -323
- package/src/resources/extensions/gsd/tests/token-optimization-benchmark.test.ts +0 -1272
- package/src/resources/extensions/gsd/tests/token-optimization-prefs.test.ts +0 -164
|
@@ -297,116 +297,163 @@ export async function stopAuto(ctx, pi, reason) {
|
|
|
297
297
|
return;
|
|
298
298
|
const loadedPreferences = loadEffectiveGSDPreferences()?.preferences;
|
|
299
299
|
const reasonSuffix = reason ? ` — ${reason}` : "";
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
clearLock(lockBase());
|
|
303
|
-
if (lockBase())
|
|
304
|
-
releaseSessionLock(lockBase());
|
|
305
|
-
clearSkillSnapshot();
|
|
306
|
-
resetSkillTelemetry();
|
|
307
|
-
// Remove SIGTERM handler registered at auto-mode start
|
|
308
|
-
deregisterSigtermHandler();
|
|
309
|
-
// ── Auto-worktree: exit worktree and reset s.basePath on stop ──
|
|
310
|
-
if (s.currentMilestoneId) {
|
|
311
|
-
const notifyCtx = ctx
|
|
312
|
-
? { notify: ctx.ui.notify.bind(ctx.ui) }
|
|
313
|
-
: { notify: () => { } };
|
|
314
|
-
buildResolver().exitMilestone(s.currentMilestoneId, notifyCtx, {
|
|
315
|
-
preserveBranch: true,
|
|
316
|
-
});
|
|
317
|
-
}
|
|
318
|
-
// ── DB cleanup: close the SQLite connection ──
|
|
319
|
-
if (isDbAvailable()) {
|
|
300
|
+
try {
|
|
301
|
+
// ── Step 1: Timers and locks ──
|
|
320
302
|
try {
|
|
321
|
-
|
|
322
|
-
|
|
303
|
+
clearUnitTimeout();
|
|
304
|
+
if (lockBase())
|
|
305
|
+
clearLock(lockBase());
|
|
306
|
+
if (lockBase())
|
|
307
|
+
releaseSessionLock(lockBase());
|
|
323
308
|
}
|
|
324
309
|
catch (e) {
|
|
325
|
-
debugLog("
|
|
326
|
-
error: e instanceof Error ? e.message : String(e),
|
|
327
|
-
});
|
|
310
|
+
debugLog("stop-cleanup-locks", { error: e instanceof Error ? e.message : String(e) });
|
|
328
311
|
}
|
|
329
|
-
|
|
330
|
-
if (s.originalBasePath) {
|
|
331
|
-
s.basePath = s.originalBasePath;
|
|
312
|
+
// ── Step 2: Skill state ──
|
|
332
313
|
try {
|
|
333
|
-
|
|
314
|
+
clearSkillSnapshot();
|
|
315
|
+
resetSkillTelemetry();
|
|
334
316
|
}
|
|
335
|
-
catch {
|
|
336
|
-
|
|
317
|
+
catch (e) {
|
|
318
|
+
debugLog("stop-cleanup-skills", { error: e instanceof Error ? e.message : String(e) });
|
|
337
319
|
}
|
|
338
|
-
|
|
339
|
-
const ledger = getLedger();
|
|
340
|
-
if (ledger && ledger.units.length > 0) {
|
|
341
|
-
const totals = getProjectTotals(ledger.units);
|
|
342
|
-
ctx?.ui.notify(`Auto-mode stopped${reasonSuffix}. Session: ${formatCost(totals.cost)} · ${formatTokenCount(totals.tokens.total)} tokens · ${ledger.units.length} units`, "info");
|
|
343
|
-
}
|
|
344
|
-
else {
|
|
345
|
-
ctx?.ui.notify(`Auto-mode stopped${reasonSuffix}.`, "info");
|
|
346
|
-
}
|
|
347
|
-
if (s.basePath) {
|
|
320
|
+
// ── Step 3: SIGTERM handler ──
|
|
348
321
|
try {
|
|
349
|
-
|
|
322
|
+
deregisterSigtermHandler();
|
|
350
323
|
}
|
|
351
324
|
catch (e) {
|
|
352
|
-
debugLog("stop-
|
|
353
|
-
error: e instanceof Error ? e.message : String(e),
|
|
354
|
-
});
|
|
325
|
+
debugLog("stop-cleanup-sigterm", { error: e instanceof Error ? e.message : String(e) });
|
|
355
326
|
}
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
327
|
+
// ── Step 4: Auto-worktree exit ──
|
|
328
|
+
try {
|
|
329
|
+
if (s.currentMilestoneId) {
|
|
330
|
+
const notifyCtx = ctx
|
|
331
|
+
? { notify: ctx.ui.notify.bind(ctx.ui) }
|
|
332
|
+
: { notify: () => { } };
|
|
333
|
+
buildResolver().exitMilestone(s.currentMilestoneId, notifyCtx, {
|
|
334
|
+
preserveBranch: true,
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
catch (e) {
|
|
339
|
+
debugLog("stop-cleanup-worktree", { error: e instanceof Error ? e.message : String(e) });
|
|
340
|
+
}
|
|
341
|
+
// ── Step 5: DB cleanup ──
|
|
342
|
+
if (isDbAvailable()) {
|
|
343
|
+
try {
|
|
344
|
+
const { closeDatabase } = await import("./gsd-db.js");
|
|
345
|
+
closeDatabase();
|
|
346
|
+
}
|
|
347
|
+
catch (e) {
|
|
348
|
+
debugLog("db-close-failed", {
|
|
349
|
+
error: e instanceof Error ? e.message : String(e),
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
// ── Step 6: Restore basePath and chdir ──
|
|
354
|
+
try {
|
|
355
|
+
if (s.originalBasePath) {
|
|
356
|
+
s.basePath = s.originalBasePath;
|
|
357
|
+
try {
|
|
358
|
+
process.chdir(s.basePath);
|
|
359
|
+
}
|
|
360
|
+
catch {
|
|
361
|
+
/* best-effort */
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
catch (e) {
|
|
366
|
+
debugLog("stop-cleanup-basepath", { error: e instanceof Error ? e.message : String(e) });
|
|
367
|
+
}
|
|
368
|
+
// ── Step 7: Ledger notification ──
|
|
369
|
+
try {
|
|
370
|
+
const ledger = getLedger();
|
|
371
|
+
if (ledger && ledger.units.length > 0) {
|
|
372
|
+
const totals = getProjectTotals(ledger.units);
|
|
373
|
+
ctx?.ui.notify(`Auto-mode stopped${reasonSuffix}. Session: ${formatCost(totals.cost)} · ${formatTokenCount(totals.tokens.total)} tokens · ${ledger.units.length} units`, "info");
|
|
374
|
+
}
|
|
375
|
+
else {
|
|
376
|
+
ctx?.ui.notify(`Auto-mode stopped${reasonSuffix}.`, "info");
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
catch (e) {
|
|
380
|
+
debugLog("stop-cleanup-ledger", { error: e instanceof Error ? e.message : String(e) });
|
|
381
|
+
}
|
|
382
|
+
// ── Step 8: Rebuild state ──
|
|
383
|
+
if (s.basePath) {
|
|
384
|
+
try {
|
|
385
|
+
await rebuildState(s.basePath);
|
|
386
|
+
}
|
|
387
|
+
catch (e) {
|
|
388
|
+
debugLog("stop-rebuild-state-failed", {
|
|
389
|
+
error: e instanceof Error ? e.message : String(e),
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
// ── Step 9: Cmux sidebar / event log ──
|
|
394
|
+
try {
|
|
395
|
+
clearCmuxSidebar(loadedPreferences);
|
|
396
|
+
logCmuxEvent(loadedPreferences, `Auto-mode stopped${reasonSuffix || ""}.`, reason?.startsWith("Blocked:") ? "warning" : "info");
|
|
397
|
+
}
|
|
398
|
+
catch (e) {
|
|
399
|
+
debugLog("stop-cleanup-cmux", { error: e instanceof Error ? e.message : String(e) });
|
|
400
|
+
}
|
|
401
|
+
// ── Step 10: Debug summary ──
|
|
402
|
+
try {
|
|
403
|
+
if (isDebugEnabled()) {
|
|
404
|
+
const logPath = writeDebugSummary();
|
|
405
|
+
if (logPath) {
|
|
406
|
+
ctx?.ui.notify(`Debug log written → ${logPath}`, "info");
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
catch (e) {
|
|
411
|
+
debugLog("stop-cleanup-debug", { error: e instanceof Error ? e.message : String(e) });
|
|
412
|
+
}
|
|
413
|
+
// ── Step 11: Reset metrics, routing, hooks ──
|
|
414
|
+
try {
|
|
415
|
+
resetMetrics();
|
|
416
|
+
resetRoutingHistory();
|
|
417
|
+
resetHookState();
|
|
418
|
+
if (s.basePath)
|
|
419
|
+
clearPersistedHookState(s.basePath);
|
|
420
|
+
}
|
|
421
|
+
catch (e) {
|
|
422
|
+
debugLog("stop-cleanup-metrics", { error: e instanceof Error ? e.message : String(e) });
|
|
423
|
+
}
|
|
424
|
+
// ── Step 12: Remove paused-session metadata (#1383) ──
|
|
425
|
+
try {
|
|
426
|
+
const pausedPath = join(gsdRoot(s.originalBasePath || s.basePath), "runtime", "paused-session.json");
|
|
427
|
+
if (existsSync(pausedPath))
|
|
428
|
+
unlinkSync(pausedPath);
|
|
429
|
+
}
|
|
430
|
+
catch { /* non-fatal */ }
|
|
431
|
+
// ── Step 13: Restore original model (before reset clears IDs) ──
|
|
432
|
+
try {
|
|
433
|
+
if (pi && ctx && s.originalModelId && s.originalModelProvider) {
|
|
434
|
+
const original = ctx.modelRegistry.find(s.originalModelProvider, s.originalModelId);
|
|
435
|
+
if (original)
|
|
436
|
+
await pi.setModel(original);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
catch (e) {
|
|
440
|
+
debugLog("stop-cleanup-model", { error: e instanceof Error ? e.message : String(e) });
|
|
363
441
|
}
|
|
364
442
|
}
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
s.paused = false;
|
|
379
|
-
s.stepMode = false;
|
|
380
|
-
s.unitDispatchCount.clear();
|
|
381
|
-
s.unitRecoveryCount.clear();
|
|
382
|
-
clearInFlightTools();
|
|
383
|
-
s.lastBudgetAlertLevel = 0;
|
|
384
|
-
s.lastStateRebuildAt = 0;
|
|
385
|
-
s.unitLifetimeDispatches.clear();
|
|
386
|
-
s.currentUnit = null;
|
|
387
|
-
s.autoModeStartModel = null;
|
|
388
|
-
s.currentMilestoneId = null;
|
|
389
|
-
s.originalBasePath = "";
|
|
390
|
-
s.completedUnits = [];
|
|
391
|
-
s.pendingQuickTasks = [];
|
|
392
|
-
clearSliceProgressCache();
|
|
393
|
-
clearActivityLogState();
|
|
394
|
-
resetProactiveHealing();
|
|
395
|
-
s.pendingCrashRecovery = null;
|
|
396
|
-
s.pendingVerificationRetry = null;
|
|
397
|
-
s.verificationRetryCount.clear();
|
|
398
|
-
s.pausedSessionFile = null;
|
|
399
|
-
ctx?.ui.setStatus("gsd-auto", undefined);
|
|
400
|
-
ctx?.ui.setWidget("gsd-progress", undefined);
|
|
401
|
-
ctx?.ui.setFooter(undefined);
|
|
402
|
-
if (pi && ctx && s.originalModelId && s.originalModelProvider) {
|
|
403
|
-
const original = ctx.modelRegistry.find(s.originalModelProvider, s.originalModelId);
|
|
404
|
-
if (original)
|
|
405
|
-
await pi.setModel(original);
|
|
406
|
-
s.originalModelId = null;
|
|
407
|
-
s.originalModelProvider = null;
|
|
443
|
+
finally {
|
|
444
|
+
// ── Critical invariants: these MUST execute regardless of errors ──
|
|
445
|
+
// External cleanup (not covered by session reset)
|
|
446
|
+
clearInFlightTools();
|
|
447
|
+
clearSliceProgressCache();
|
|
448
|
+
clearActivityLogState();
|
|
449
|
+
resetProactiveHealing();
|
|
450
|
+
// UI cleanup
|
|
451
|
+
ctx?.ui.setStatus("gsd-auto", undefined);
|
|
452
|
+
ctx?.ui.setWidget("gsd-progress", undefined);
|
|
453
|
+
ctx?.ui.setFooter(undefined);
|
|
454
|
+
// Reset all session state in one call
|
|
455
|
+
s.reset();
|
|
408
456
|
}
|
|
409
|
-
s.cmdCtx = null;
|
|
410
457
|
}
|
|
411
458
|
/**
|
|
412
459
|
* Pause auto-mode without destroying state. Context is preserved.
|
|
@@ -8,12 +8,13 @@
|
|
|
8
8
|
import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, writeFileSync } from "node:fs";
|
|
9
9
|
import { dirname, join } from "node:path";
|
|
10
10
|
import { homedir } from "node:os";
|
|
11
|
+
const gsdHome = process.env.GSD_HOME || join(homedir(), ".gsd");
|
|
11
12
|
// ─── Registry I/O ───────────────────────────────────────────────────────────
|
|
12
13
|
function getRegistryPath() {
|
|
13
|
-
return join(
|
|
14
|
+
return join(gsdHome, "extensions", "registry.json");
|
|
14
15
|
}
|
|
15
16
|
function getAgentExtensionsDir() {
|
|
16
|
-
return join(
|
|
17
|
+
return join(gsdHome, "agent", "extensions");
|
|
17
18
|
}
|
|
18
19
|
function loadRegistry() {
|
|
19
20
|
const filePath = getRegistryPath();
|
|
@@ -631,7 +631,7 @@ export function serializePreferencesToFrontmatter(prefs) {
|
|
|
631
631
|
"dynamic_routing", "token_profile", "phases", "parallel",
|
|
632
632
|
"auto_visualize", "auto_report",
|
|
633
633
|
"verification_commands", "verification_auto_fix", "verification_max_retries",
|
|
634
|
-
"search_provider", "
|
|
634
|
+
"search_provider", "context_selection",
|
|
635
635
|
];
|
|
636
636
|
const seen = new Set();
|
|
637
637
|
for (const key of orderedKeys) {
|
|
@@ -3,10 +3,12 @@
|
|
|
3
3
|
*
|
|
4
4
|
* One command, one wizard. Routes to smart entry or status.
|
|
5
5
|
*/
|
|
6
|
+
import { importExtensionModule } from "@gsd/pi-coding-agent";
|
|
6
7
|
import { existsSync, readFileSync, readdirSync, unlinkSync } from "node:fs";
|
|
7
8
|
import { homedir } from "node:os";
|
|
8
9
|
import { join } from "node:path";
|
|
9
10
|
import { gsdRoot } from "./paths.js";
|
|
11
|
+
const gsdHome = process.env.GSD_HOME || join(homedir(), ".gsd");
|
|
10
12
|
import { enableDebug } from "./debug-logger.js";
|
|
11
13
|
import { deriveState } from "./state.js";
|
|
12
14
|
import { GSDDashboardOverlay } from "./dashboard-overlay.js";
|
|
@@ -437,7 +439,7 @@ export function registerGSDCommand(pi) {
|
|
|
437
439
|
if (parts.length === 3 && ["enable", "disable", "info"].includes(parts[1])) {
|
|
438
440
|
const idPrefix = parts[2] ?? "";
|
|
439
441
|
try {
|
|
440
|
-
const extDir = join(
|
|
442
|
+
const extDir = join(gsdHome, "agent", "extensions");
|
|
441
443
|
const ids = [];
|
|
442
444
|
for (const entry of readdirSync(extDir, { withFileTypes: true })) {
|
|
443
445
|
if (!entry.isDirectory())
|
|
@@ -528,7 +530,7 @@ export async function handleGSDCommand(args, ctx, pi) {
|
|
|
528
530
|
return;
|
|
529
531
|
}
|
|
530
532
|
if (trimmed === "widget" || trimmed.startsWith("widget ")) {
|
|
531
|
-
const { cycleWidgetMode, setWidgetMode, getWidgetMode } = await import
|
|
533
|
+
const { cycleWidgetMode, setWidgetMode, getWidgetMode } = await importExtensionModule(import.meta.url, "./auto-dashboard.js");
|
|
532
534
|
const arg = trimmed.replace(/^widget\s*/, "").trim();
|
|
533
535
|
if (arg === "full" || arg === "small" || arg === "min" || arg === "off") {
|
|
534
536
|
setWidgetMode(arg);
|
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
* @see D001 (module location), D002 (200K fallback), D003 (section-boundary truncation)
|
|
9
9
|
*/
|
|
10
10
|
import { getCharsPerToken } from "./token-counter.js";
|
|
11
|
-
import { compressToTarget } from "./prompt-compressor.js";
|
|
12
11
|
// ─── Budget ratio constants ──────────────────────────────────────────────────
|
|
13
12
|
// Percentages of total context window allocated to each budget category.
|
|
14
13
|
// These are applied after tokens→chars conversion.
|
|
@@ -132,20 +131,13 @@ export function resolveExecutorContextWindow(registry, preferences, sessionConte
|
|
|
132
131
|
return DEFAULT_CONTEXT_WINDOW;
|
|
133
132
|
}
|
|
134
133
|
/**
|
|
135
|
-
*
|
|
136
|
-
* Returns the content within budget with maximum information preservation.
|
|
134
|
+
* Reduce content to fit within budget using section-boundary truncation.
|
|
137
135
|
*/
|
|
138
136
|
export function reduceToFit(content, budgetChars) {
|
|
139
137
|
if (!content || content.length <= budgetChars) {
|
|
140
138
|
return { content, droppedSections: 0 };
|
|
141
139
|
}
|
|
142
|
-
|
|
143
|
-
const compressed = compressToTarget(content, budgetChars);
|
|
144
|
-
if (compressed.compressedChars <= budgetChars) {
|
|
145
|
-
return { content: compressed.content, droppedSections: 0 };
|
|
146
|
-
}
|
|
147
|
-
// Step 2: Truncate the compressed content at section boundaries
|
|
148
|
-
return truncateAtSectionBoundary(compressed.content, budgetChars);
|
|
140
|
+
return truncateAtSectionBoundary(content, budgetChars);
|
|
149
141
|
}
|
|
150
142
|
// ─── Internal helpers ────────────────────────────────────────────────────────
|
|
151
143
|
/**
|
|
@@ -9,6 +9,7 @@ import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
|
9
9
|
import { join } from "node:path";
|
|
10
10
|
import { homedir } from "node:os";
|
|
11
11
|
import { gsdRoot } from "./paths.js";
|
|
12
|
+
const gsdHome = process.env.GSD_HOME || join(homedir(), ".gsd");
|
|
12
13
|
// ─── Project File Markers ───────────────────────────────────────────────────────
|
|
13
14
|
const PROJECT_FILES = [
|
|
14
15
|
"package.json",
|
|
@@ -309,7 +310,6 @@ function detectVerificationCommands(basePath, detectedFiles, packageManager) {
|
|
|
309
310
|
* Check if global GSD setup exists (has ~/.gsd/ with preferences).
|
|
310
311
|
*/
|
|
311
312
|
export function hasGlobalSetup() {
|
|
312
|
-
const gsdHome = join(homedir(), ".gsd");
|
|
313
313
|
return (existsSync(join(gsdHome, "preferences.md")) ||
|
|
314
314
|
existsSync(join(gsdHome, "PREFERENCES.md")));
|
|
315
315
|
}
|
|
@@ -318,7 +318,6 @@ export function hasGlobalSetup() {
|
|
|
318
318
|
* Returns true if ~/.gsd/ doesn't exist or has no preferences or auth.
|
|
319
319
|
*/
|
|
320
320
|
export function isFirstEverLaunch() {
|
|
321
|
-
const gsdHome = join(homedir(), ".gsd");
|
|
322
321
|
if (!existsSync(gsdHome))
|
|
323
322
|
return true;
|
|
324
323
|
// If we have preferences, not first launch
|
|
@@ -194,8 +194,6 @@ Setting `prefer_skills: []` does **not** disable skill discovery — it just mea
|
|
|
194
194
|
|
|
195
195
|
- `search_provider`: `"brave"`, `"tavily"`, `"ollama"`, `"native"`, or `"auto"` — selects the search backend for research phases. `"native"` forces Anthropic's built-in web search only; provider values force that backend and disable native search; `"auto"` uses the default heuristic. Default: `"auto"`.
|
|
196
196
|
|
|
197
|
-
- `compression_strategy`: `"truncate"` or `"compress"` — controls how context that exceeds the budget is reduced. `"truncate"` (default) drops sections from the end. `"compress"` applies heuristic compression before truncating, preserving more content at the cost of some fidelity. Default: `"truncate"`.
|
|
198
|
-
|
|
199
197
|
- `context_selection`: `"full"` or `"smart"` — controls how files are inlined into context. `"full"` inlines entire files; `"smart"` uses semantic chunking to include only the most relevant sections. Default is derived from `token_profile`.
|
|
200
198
|
|
|
201
199
|
- `parallel`: configures parallel orchestration for running multiple slices concurrently. Keys:
|
|
@@ -28,10 +28,12 @@ function modelToProviderId(model) {
|
|
|
28
28
|
const prefix = model.split("/")[0].toLowerCase();
|
|
29
29
|
// Map known prefixes to registry IDs
|
|
30
30
|
const prefixMap = {
|
|
31
|
+
"anthropic-vertex": "anthropic-vertex",
|
|
31
32
|
openrouter: "openrouter",
|
|
32
33
|
groq: "groq",
|
|
33
34
|
mistral: "mistral",
|
|
34
35
|
google: "google",
|
|
36
|
+
"google-vertex": "google-vertex",
|
|
35
37
|
anthropic: "anthropic",
|
|
36
38
|
openai: "openai",
|
|
37
39
|
"github-copilot": "github-copilot",
|
|
@@ -67,11 +69,19 @@ function collectConfiguredModelProviders() {
|
|
|
67
69
|
}
|
|
68
70
|
const modelEntries = typeof models === "object" ? Object.values(models) : [];
|
|
69
71
|
for (const entry of modelEntries) {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
72
|
+
if (typeof entry === "string") {
|
|
73
|
+
const pid = modelToProviderId(entry);
|
|
74
|
+
if (pid)
|
|
75
|
+
providers.add(pid);
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
if (typeof entry === "object" && entry !== null && "model" in entry) {
|
|
79
|
+
const configuredProvider = "provider" in entry ? entry.provider : undefined;
|
|
80
|
+
if (typeof configuredProvider === "string" && configuredProvider.trim().length > 0) {
|
|
81
|
+
providers.add(configuredProvider);
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
const modelId = String(entry.model);
|
|
75
85
|
const pid = modelToProviderId(modelId);
|
|
76
86
|
if (pid)
|
|
77
87
|
providers.add(pid);
|
|
@@ -88,6 +98,9 @@ function collectConfiguredModelProviders() {
|
|
|
88
98
|
}
|
|
89
99
|
function resolveKey(providerId) {
|
|
90
100
|
const info = PROVIDER_REGISTRY.find(p => p.id === providerId);
|
|
101
|
+
if (providerId === "anthropic-vertex" && process.env.ANTHROPIC_VERTEX_PROJECT_ID) {
|
|
102
|
+
return { found: true, source: "env", backedOff: false };
|
|
103
|
+
}
|
|
91
104
|
// Check auth.json
|
|
92
105
|
const authPath = getAuthPath();
|
|
93
106
|
if (existsSync(authPath)) {
|
|
@@ -138,7 +151,9 @@ function checkLlmProviders() {
|
|
|
138
151
|
const results = [];
|
|
139
152
|
for (const providerId of required) {
|
|
140
153
|
const info = PROVIDER_REGISTRY.find(p => p.id === providerId);
|
|
141
|
-
const label =
|
|
154
|
+
const label = providerId === "anthropic-vertex"
|
|
155
|
+
? "Anthropic Vertex"
|
|
156
|
+
: info?.label ?? providerId;
|
|
142
157
|
const lookup = resolveKey(providerId);
|
|
143
158
|
if (!lookup.found) {
|
|
144
159
|
// Check if a cross-provider can serve this provider's models
|
|
@@ -157,16 +172,20 @@ function checkLlmProviders() {
|
|
|
157
172
|
});
|
|
158
173
|
continue;
|
|
159
174
|
}
|
|
160
|
-
const envVar =
|
|
175
|
+
const envVar = providerId === "anthropic-vertex"
|
|
176
|
+
? "ANTHROPIC_VERTEX_PROJECT_ID"
|
|
177
|
+
: info?.envVar ?? `${providerId.toUpperCase()}_API_KEY`;
|
|
161
178
|
results.push({
|
|
162
179
|
name: providerId,
|
|
163
180
|
label,
|
|
164
181
|
category: "llm",
|
|
165
182
|
status: "error",
|
|
166
|
-
message: `${label} —
|
|
167
|
-
detail:
|
|
168
|
-
?
|
|
169
|
-
:
|
|
183
|
+
message: `${label} — not configured`,
|
|
184
|
+
detail: providerId === "anthropic-vertex"
|
|
185
|
+
? "Set ANTHROPIC_VERTEX_PROJECT_ID and authenticate with Google ADC"
|
|
186
|
+
: info?.hasOAuth
|
|
187
|
+
? `Run /gsd keys to authenticate`
|
|
188
|
+
: `Set ${envVar} or run /gsd keys`,
|
|
170
189
|
required: true,
|
|
171
190
|
});
|
|
172
191
|
}
|
|
@@ -257,10 +257,23 @@ async function markSliceDoneInRoadmap(basePath, milestoneId, sliceId, fixesAppli
|
|
|
257
257
|
fixesApplied.push(`marked ${sliceId} done in ${roadmapPath}`);
|
|
258
258
|
}
|
|
259
259
|
}
|
|
260
|
+
async function markSliceUndoneInRoadmap(basePath, milestoneId, sliceId, fixesApplied) {
|
|
261
|
+
const roadmapPath = resolveMilestoneFile(basePath, milestoneId, "ROADMAP");
|
|
262
|
+
if (!roadmapPath)
|
|
263
|
+
return;
|
|
264
|
+
const content = await loadFile(roadmapPath);
|
|
265
|
+
if (!content)
|
|
266
|
+
return;
|
|
267
|
+
const updated = content.replace(new RegExp(`^(\\s*-\\s+)\\[x\\]\\s+\\*\\*${sliceId}:`, "m"), `$1[ ] **${sliceId}:`);
|
|
268
|
+
if (updated !== content) {
|
|
269
|
+
await saveFile(roadmapPath, updated);
|
|
270
|
+
fixesApplied.push(`unmarked ${sliceId} in ${roadmapPath} (premature completion)`);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
260
273
|
function matchesScope(unitId, scope) {
|
|
261
274
|
if (!scope)
|
|
262
275
|
return true;
|
|
263
|
-
return unitId === scope || unitId.startsWith(`${scope}/`)
|
|
276
|
+
return unitId === scope || unitId.startsWith(`${scope}/`);
|
|
264
277
|
}
|
|
265
278
|
function auditRequirements(content) {
|
|
266
279
|
if (!content)
|
|
@@ -828,6 +841,12 @@ export async function runGSDDoctor(basePath, options) {
|
|
|
828
841
|
file: relSliceFile(basePath, milestoneId, slice.id, "SUMMARY"),
|
|
829
842
|
fixable: true,
|
|
830
843
|
});
|
|
844
|
+
if (!allTasksDone) {
|
|
845
|
+
dryRunCanFix("slice_checked_missing_summary", `uncheck ${slice.id} in roadmap (tasks incomplete)`);
|
|
846
|
+
if (shouldFix("slice_checked_missing_summary")) {
|
|
847
|
+
await markSliceUndoneInRoadmap(basePath, milestoneId, slice.id, fixesApplied);
|
|
848
|
+
}
|
|
849
|
+
}
|
|
831
850
|
}
|
|
832
851
|
if (slice.done && !hasSliceUat) {
|
|
833
852
|
issues.push({
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import { importExtensionModule } from "@gsd/pi-coding-agent";
|
|
1
2
|
export function registerExitCommand(pi, deps = {}) {
|
|
2
3
|
pi.registerCommand("exit", {
|
|
3
4
|
description: "Exit GSD gracefully",
|
|
4
5
|
handler: async (_args, ctx) => {
|
|
5
6
|
// Stop auto-mode first so locks and activity state are cleaned up before shutdown.
|
|
6
|
-
const stopAuto = deps.stopAuto ?? (await import
|
|
7
|
+
const stopAuto = deps.stopAuto ?? (await importExtensionModule(import.meta.url, "./auto.js")).stopAuto;
|
|
7
8
|
await stopAuto(ctx, pi, "Graceful exit");
|
|
8
9
|
ctx.shutdown();
|
|
9
10
|
},
|
|
@@ -5,7 +5,7 @@ import { join, basename } from "node:path";
|
|
|
5
5
|
import { exec } from "node:child_process";
|
|
6
6
|
import { getLedger, getProjectTotals, aggregateByPhase, aggregateBySlice, aggregateByModel, formatCost, formatTokenCount, loadLedgerFromDisk, } from "./metrics.js";
|
|
7
7
|
import { gsdRoot } from "./paths.js";
|
|
8
|
-
import { formatDuration, fileLink } from "../shared/
|
|
8
|
+
import { formatDuration, fileLink } from "../shared/format-utils.js";
|
|
9
9
|
import { getErrorMessage } from "./error-utils.js";
|
|
10
10
|
/**
|
|
11
11
|
* Open a file in the user's default browser.
|
|
@@ -6,8 +6,8 @@ import { promises as fs } from 'node:fs';
|
|
|
6
6
|
import { resolve } from 'node:path';
|
|
7
7
|
import { atomicWriteAsync } from './atomic-write.js';
|
|
8
8
|
import { resolveMilestoneFile, relMilestoneFile, resolveGsdRootFile } from './paths.js';
|
|
9
|
-
import { findMilestoneIds } from './
|
|
10
|
-
import { checkExistingEnvKeys } from '../
|
|
9
|
+
import { findMilestoneIds } from './milestone-ids.js';
|
|
10
|
+
import { checkExistingEnvKeys } from '../env-utils.js';
|
|
11
11
|
import { parseRoadmapSlices } from './roadmap-slices.js';
|
|
12
12
|
import { nativeParseRoadmap, nativeExtractSection, nativeParsePlanFile, nativeParseSummaryFile, NATIVE_UNAVAILABLE } from './native-parser-bridge.js';
|
|
13
13
|
import { debugTime, debugCount } from './debug-logger.js';
|
|
@@ -222,13 +222,48 @@ export function formatSecretsManifest(manifest) {
|
|
|
222
222
|
return lines.join('\n') + '\n';
|
|
223
223
|
}
|
|
224
224
|
// ─── Slice Plan Parser ─────────────────────────────────────────────────────
|
|
225
|
+
function normalizeTaskPlanFrontmatter(frontmatter) {
|
|
226
|
+
const estimatedStepsRaw = frontmatter.estimated_steps;
|
|
227
|
+
const estimatedFilesRaw = frontmatter.estimated_files;
|
|
228
|
+
const skillsUsedRaw = frontmatter.skills_used;
|
|
229
|
+
const parseOptionalNumber = (value) => {
|
|
230
|
+
if (typeof value === 'number' && Number.isFinite(value))
|
|
231
|
+
return value;
|
|
232
|
+
if (typeof value === 'string' && value.trim()) {
|
|
233
|
+
const parsed = parseInt(value, 10);
|
|
234
|
+
if (Number.isFinite(parsed))
|
|
235
|
+
return parsed;
|
|
236
|
+
}
|
|
237
|
+
return undefined;
|
|
238
|
+
};
|
|
239
|
+
const estimated_steps = parseOptionalNumber(estimatedStepsRaw);
|
|
240
|
+
const estimated_files = parseOptionalNumber(estimatedFilesRaw);
|
|
241
|
+
const skills_used = Array.isArray(skillsUsedRaw)
|
|
242
|
+
? skillsUsedRaw.map(v => String(v).trim()).filter(Boolean)
|
|
243
|
+
: typeof skillsUsedRaw === 'string' && skillsUsedRaw.trim()
|
|
244
|
+
? [skillsUsedRaw.trim()]
|
|
245
|
+
: [];
|
|
246
|
+
return {
|
|
247
|
+
...(estimated_steps !== undefined ? { estimated_steps } : {}),
|
|
248
|
+
...(estimated_files !== undefined ? { estimated_files } : {}),
|
|
249
|
+
skills_used,
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
export function parseTaskPlanFile(content) {
|
|
253
|
+
const [fmLines] = splitFrontmatter(content);
|
|
254
|
+
const fm = fmLines ? parseFrontmatterMap(fmLines) : {};
|
|
255
|
+
return {
|
|
256
|
+
frontmatter: normalizeTaskPlanFrontmatter(fm),
|
|
257
|
+
};
|
|
258
|
+
}
|
|
225
259
|
export function parsePlan(content) {
|
|
226
260
|
return cachedParse(content, 'plan', _parsePlanImpl);
|
|
227
261
|
}
|
|
228
262
|
function _parsePlanImpl(content) {
|
|
229
263
|
const stopTimer = debugTime("parse-plan");
|
|
264
|
+
const [, body] = splitFrontmatter(content);
|
|
230
265
|
// Try native parser first for better performance
|
|
231
|
-
const nativeResult = nativeParsePlanFile(
|
|
266
|
+
const nativeResult = nativeParsePlanFile(body);
|
|
232
267
|
if (nativeResult) {
|
|
233
268
|
stopTimer({ native: true });
|
|
234
269
|
return {
|
|
@@ -249,7 +284,7 @@ function _parsePlanImpl(content) {
|
|
|
249
284
|
filesLikelyTouched: nativeResult.filesLikelyTouched,
|
|
250
285
|
};
|
|
251
286
|
}
|
|
252
|
-
const lines =
|
|
287
|
+
const lines = body.split('\n');
|
|
253
288
|
const h1 = lines.find(l => l.startsWith('# '));
|
|
254
289
|
let id = '';
|
|
255
290
|
let title = '';
|
|
@@ -263,11 +298,11 @@ function _parsePlanImpl(content) {
|
|
|
263
298
|
title = h1.slice(2).trim();
|
|
264
299
|
}
|
|
265
300
|
}
|
|
266
|
-
const goal = extractBoldField(
|
|
267
|
-
const demo = extractBoldField(
|
|
268
|
-
const mhSection = extractSection(
|
|
301
|
+
const goal = extractBoldField(body, 'Goal') || '';
|
|
302
|
+
const demo = extractBoldField(body, 'Demo') || '';
|
|
303
|
+
const mhSection = extractSection(body, 'Must-Haves');
|
|
269
304
|
const mustHaves = mhSection ? parseBullets(mhSection) : [];
|
|
270
|
-
const tasksSection = extractSection(
|
|
305
|
+
const tasksSection = extractSection(body, 'Tasks');
|
|
271
306
|
const tasks = [];
|
|
272
307
|
if (tasksSection) {
|
|
273
308
|
const taskLines = tasksSection.split('\n');
|
|
@@ -315,7 +350,7 @@ function _parsePlanImpl(content) {
|
|
|
315
350
|
if (currentTask)
|
|
316
351
|
tasks.push(currentTask);
|
|
317
352
|
}
|
|
318
|
-
const filesSection = extractSection(
|
|
353
|
+
const filesSection = extractSection(body, 'Files Likely Touched');
|
|
319
354
|
const filesLikelyTouched = filesSection ? parseBullets(filesSection) : [];
|
|
320
355
|
const result = { id, title, goal, demo, mustHaves, tasks, filesLikelyTouched };
|
|
321
356
|
stopTimer({ tasks: tasks.length });
|
|
@@ -692,6 +727,10 @@ export function extractUatType(content) {
|
|
|
692
727
|
const rawValue = modeBullet.slice('UAT mode:'.length).trim().toLowerCase();
|
|
693
728
|
if (rawValue.startsWith('artifact-driven'))
|
|
694
729
|
return 'artifact-driven';
|
|
730
|
+
if (rawValue.startsWith('browser-executable'))
|
|
731
|
+
return 'browser-executable';
|
|
732
|
+
if (rawValue.startsWith('runtime-executable'))
|
|
733
|
+
return 'runtime-executable';
|
|
695
734
|
if (rawValue.startsWith('live-runtime'))
|
|
696
735
|
return 'live-runtime';
|
|
697
736
|
if (rawValue.startsWith('human-experience'))
|
|
@@ -21,7 +21,7 @@ import { deriveState } from "./state.js";
|
|
|
21
21
|
import { isAutoActive } from "./auto.js";
|
|
22
22
|
import { loadPrompt } from "./prompt-loader.js";
|
|
23
23
|
import { gsdRoot } from "./paths.js";
|
|
24
|
-
import { formatDuration } from "../shared/
|
|
24
|
+
import { formatDuration } from "../shared/format-utils.js";
|
|
25
25
|
import { getAutoWorktreePath } from "./auto-worktree.js";
|
|
26
26
|
// ─── Entry Point ──────────────────────────────────────────────────────────────
|
|
27
27
|
export async function handleForensics(args, ctx, pi) {
|