gsd-pi 2.82.0-dev.3709f22a5 → 2.82.0-dev.4285182e8
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 +2 -2
- 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/loop.js +14 -1
- package/dist/resources/extensions/gsd/auto/phases.js +53 -29
- package/dist/resources/extensions/gsd/auto/session.js +4 -0
- package/dist/resources/extensions/gsd/auto/workflow-kernel.js +3 -0
- package/dist/resources/extensions/gsd/auto-direct-dispatch.js +1 -0
- package/dist/resources/extensions/gsd/auto-dispatch.js +13 -19
- package/dist/resources/extensions/gsd/auto-post-unit.js +13 -6
- 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 +14 -8
- 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/catalog.js +7 -1
- package/dist/resources/extensions/gsd/commands/handlers/core.js +1 -0
- package/dist/resources/extensions/gsd/commands/handlers/ops.js +5 -0
- package/dist/resources/extensions/gsd/commands-prefs-wizard.js +7 -2
- package/dist/resources/extensions/gsd/commands-verdict.js +139 -0
- 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/markdown-renderer.js +10 -8
- package/dist/resources/extensions/gsd/paths.js +4 -0
- package/dist/resources/extensions/gsd/queue-reorder-ui.js +30 -13
- package/dist/resources/extensions/gsd/state.js +3 -3
- package/dist/resources/extensions/gsd/status-guards.js +7 -0
- package/dist/resources/extensions/gsd/templates/plan.md +1 -0
- package/dist/resources/extensions/gsd/templates/task-plan.md +6 -0
- package/dist/resources/extensions/gsd/tools/plan-slice.js +3 -5
- package/dist/resources/extensions/gsd/workflow-mcp.js +17 -1
- package/dist/resources/extensions/gsd/worktree-manager.js +1 -1
- package/dist/resources/extensions/ttsr/ttsr-manager.js +3 -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 +11 -11
- package/dist/web/standalone/.next/build-manifest.json +3 -3
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/react-loadable-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 +11 -11
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware-react-loadable-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/dist/web/standalone/.next/static/chunks/8359.65b24fac92188a6b.js +10 -0
- package/dist/web/standalone/.next/static/chunks/9441.ff70bb53f6835771.js +1 -0
- package/dist/web/standalone/.next/static/chunks/{webpack-9a4db269f9ed63ad.js → webpack-855d616060cb6e59.js} +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 +25 -1
- 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 +2 -0
- 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 +24 -10
- 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 +30 -1
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +29 -10
- 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/loop.ts +14 -1
- package/src/resources/extensions/gsd/auto/phases.ts +60 -36
- package/src/resources/extensions/gsd/auto/session.ts +4 -0
- package/src/resources/extensions/gsd/auto/workflow-kernel.ts +5 -1
- package/src/resources/extensions/gsd/auto-direct-dispatch.ts +1 -0
- package/src/resources/extensions/gsd/auto-dispatch.ts +13 -19
- package/src/resources/extensions/gsd/auto-post-unit.ts +14 -6
- 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 +13 -8
- 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/catalog.ts +7 -1
- package/src/resources/extensions/gsd/commands/handlers/core.ts +1 -0
- package/src/resources/extensions/gsd/commands/handlers/ops.ts +5 -0
- package/src/resources/extensions/gsd/commands-prefs-wizard.ts +8 -3
- package/src/resources/extensions/gsd/commands-verdict.ts +202 -0
- 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/markdown-renderer.ts +10 -8
- package/src/resources/extensions/gsd/paths.ts +5 -0
- package/src/resources/extensions/gsd/queue-reorder-ui.ts +31 -13
- package/src/resources/extensions/gsd/state.ts +3 -3
- package/src/resources/extensions/gsd/status-guards.ts +8 -0
- package/src/resources/extensions/gsd/templates/plan.md +1 -0
- package/src/resources/extensions/gsd/templates/task-plan.md +6 -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 +139 -1
- package/src/resources/extensions/gsd/tests/auto-post-unit-step-message.test.ts +6 -5
- 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/commands-verdict.test.ts +378 -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/gsdroot-worktree-detection.test.ts +5 -2
- 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/plan-slice.test.ts +26 -1
- package/src/resources/extensions/gsd/tests/post-unit-state-rebuild.test.ts +84 -0
- 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/quality-gates.test.ts +6 -0
- 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-kernel.test.ts +7 -0
- package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +18 -0
- package/src/resources/extensions/gsd/tools/plan-slice.ts +3 -4
- package/src/resources/extensions/gsd/workflow-mcp.ts +18 -1
- package/src/resources/extensions/gsd/worktree-manager.ts +1 -1
- package/src/resources/extensions/ttsr/ttsr-manager.ts +5 -1
- package/dist/web/standalone/.next/static/chunks/8359.7eb3bb8f8ecf4c01.js +0 -10
- package/dist/web/standalone/.next/static/chunks/9441.1081da1125d1764f.js +0 -1
- /package/dist/web/standalone/.next/static/{kkGf3_VaPFkiDNV_D7Dtl → 78uanrILNOKG-Jpi4itAE}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{kkGf3_VaPFkiDNV_D7Dtl → 78uanrILNOKG-Jpi4itAE}/_ssgManifest.js +0 -0
|
@@ -47,7 +47,7 @@ import { regenerateIfMissing } from "./workflow-projections.js";
|
|
|
47
47
|
import { WorktreeStateProjection } from "./worktree-state-projection.js";
|
|
48
48
|
import { createWorkspace, scopeMilestone } from "./workspace.js";
|
|
49
49
|
import { normalizeWorktreePathForCompare } from "./worktree-root.js";
|
|
50
|
-
import { isDbAvailable, getTask, getSlice, getMilestone, updateTaskStatus, _getAdapter, getVerificationEvidence } from "./gsd-db.js";
|
|
50
|
+
import { isDbAvailable, getDbPath, refreshOpenDatabaseFromDisk, getTask, getSlice, getMilestone, updateTaskStatus, _getAdapter, getVerificationEvidence } from "./gsd-db.js";
|
|
51
51
|
import { renderPlanCheckboxes } from "./markdown-renderer.js";
|
|
52
52
|
import { consumeSignal } from "./session-status-io.js";
|
|
53
53
|
import {
|
|
@@ -371,7 +371,7 @@ export function detectRogueFileWrites(
|
|
|
371
371
|
export const MAX_ARTIFACT_VERIFICATION_RETRIES = 3;
|
|
372
372
|
|
|
373
373
|
export const STEP_COMPLETE_FALLBACK_MESSAGE =
|
|
374
|
-
"Step complete. Run /clear, then /gsd to continue (or /gsd auto to run continuously).";
|
|
374
|
+
"Step complete. Run /clear if you want a clean view, then /gsd next to continue one step (or /gsd auto to run continuously).";
|
|
375
375
|
|
|
376
376
|
export function buildStepCompleteMessage(nextState: import("./types.js").GSDState): string {
|
|
377
377
|
if (nextState.phase === "complete") {
|
|
@@ -379,7 +379,7 @@ export function buildStepCompleteMessage(nextState: import("./types.js").GSDStat
|
|
|
379
379
|
}
|
|
380
380
|
const next = describeNextUnit(nextState);
|
|
381
381
|
return `Step complete. Next: ${next.label}\n`
|
|
382
|
-
+ `Run /clear, then /gsd to continue (or /gsd auto to run continuously).`;
|
|
382
|
+
+ `Run /clear if you want a clean view, then /gsd next to continue one step (or /gsd auto to run continuously).`;
|
|
383
383
|
}
|
|
384
384
|
|
|
385
385
|
/**
|
|
@@ -686,6 +686,14 @@ export async function postUnitPreVerification(pctx: PostUnitContext, opts?: PreV
|
|
|
686
686
|
await new Promise(r => setTimeout(r, 100));
|
|
687
687
|
}
|
|
688
688
|
|
|
689
|
+
const dbPath = getDbPath();
|
|
690
|
+
if (isDbAvailable() && dbPath && dbPath !== ":memory:") {
|
|
691
|
+
const refreshed = refreshOpenDatabaseFromDisk();
|
|
692
|
+
if (!refreshed) {
|
|
693
|
+
logWarning("db", "post-unit database refresh failed; derived state may be stale");
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
|
|
689
697
|
// Turn-level git action (commit | snapshot | status-only)
|
|
690
698
|
if (s.currentUnit) {
|
|
691
699
|
const unit = s.currentUnit;
|
|
@@ -1177,7 +1185,7 @@ export async function postUnitPreVerification(pctx: PostUnitContext, opts?: PreV
|
|
|
1177
1185
|
s.verificationRetryFailureHashes.delete(retryKey);
|
|
1178
1186
|
writeBlockerPlaceholder(s.currentUnit.type, s.currentUnit.id, s.basePath, reason);
|
|
1179
1187
|
ctx.ui.notify(
|
|
1180
|
-
`${s.currentUnit.type} ${s.currentUnit.id} — deterministic policy rejection, wrote blocker placeholder (no retries)
|
|
1188
|
+
`${s.currentUnit.type} ${s.currentUnit.id} — deterministic policy rejection, wrote blocker placeholder (no retries)`,
|
|
1181
1189
|
"warning",
|
|
1182
1190
|
);
|
|
1183
1191
|
// Fall through to "continue" — do NOT enter the retry or db-unavailable paths.
|
|
@@ -1731,8 +1739,8 @@ export async function postUnitPostVerification(pctx: PostUnitContext): Promise<"
|
|
|
1731
1739
|
}
|
|
1732
1740
|
|
|
1733
1741
|
// Step mode → show wizard instead of dispatch.
|
|
1734
|
-
// Without this notify(), /gsd
|
|
1735
|
-
//
|
|
1742
|
+
// Without this notify(), /gsd next finishes a unit and silently exits the
|
|
1743
|
+
// loop, leaving the user with no next-step command.
|
|
1736
1744
|
if (s.stepMode) {
|
|
1737
1745
|
let phaseAfterUnit: string | null = null;
|
|
1738
1746
|
try {
|
|
@@ -201,9 +201,15 @@ export function hasImplementationArtifacts(basePath: string, milestoneId?: strin
|
|
|
201
201
|
// Strategy: check `git diff --name-only` against the merge-base with the
|
|
202
202
|
// main branch. This captures ALL files changed during the milestone's
|
|
203
203
|
// lifetime while running on a milestone branch.
|
|
204
|
-
const
|
|
205
|
-
? readIntegrationBranch(basePath, milestoneId)
|
|
206
|
-
:
|
|
204
|
+
const recordedIntegrationBranch = milestoneId
|
|
205
|
+
? readIntegrationBranch(basePath, milestoneId)
|
|
206
|
+
: null;
|
|
207
|
+
let integrationBranch: string;
|
|
208
|
+
if (recordedIntegrationBranch?.startsWith("milestone/")) {
|
|
209
|
+
integrationBranch = detectMainBranch(basePath);
|
|
210
|
+
} else {
|
|
211
|
+
integrationBranch = recordedIntegrationBranch ?? detectMainBranch(basePath);
|
|
212
|
+
}
|
|
207
213
|
const currentBranch = getCurrentBranch(basePath);
|
|
208
214
|
const branchDiff = getChangedFilesSinceBranch(basePath, integrationBranch);
|
|
209
215
|
if (!branchDiff.ok) return "unknown";
|
|
@@ -556,27 +562,55 @@ function commitMatchesMilestone(basePath: string, message: string, milestoneId:
|
|
|
556
562
|
// rather than Mxx/Sxx/Tyy. Bind those commits back to the milestone when
|
|
557
563
|
// either the commit touched this milestone's artifacts, or — for projects
|
|
558
564
|
// where .gsd/ is gitignored/external (#5033) — the message explicitly
|
|
559
|
-
// names the milestone
|
|
565
|
+
// names the milestone, local GSD state proves the task belongs here, or the
|
|
566
|
+
// commit is implementation-bearing evidence itself (#5100).
|
|
560
567
|
if (/^GSD-Task:\s*S[^/\s]+\/T\S+/m.test(message)) {
|
|
561
568
|
if (files.some((file) => isMilestoneArtifactPath(file, milestoneId))) return true;
|
|
562
569
|
if (commitMessageMentionsMilestone(message, milestoneId)) return true;
|
|
563
|
-
|
|
570
|
+
const taskTrailerOwnership = getTaskOwnershipStatus(basePath, message, milestoneId);
|
|
571
|
+
if (taskTrailerOwnership === true) return true;
|
|
572
|
+
if (taskTrailerOwnership === false) return false;
|
|
573
|
+
// taskTrailerOwnership === null: unknown ownership. Apply fallback only
|
|
574
|
+
// in this case to avoid cross-milestone attribution.
|
|
575
|
+
if (MILESTONE_ID_RE.test(milestoneId) && classifyImplementationFiles(files) === "present") return true;
|
|
564
576
|
}
|
|
565
577
|
|
|
566
578
|
return false;
|
|
567
579
|
}
|
|
568
580
|
|
|
569
|
-
|
|
581
|
+
/**
|
|
582
|
+
* Tri-state task ownership probe.
|
|
583
|
+
* true => DB or local files confirm this milestone owns the task.
|
|
584
|
+
* false => DB is available and this milestone is registered, but task is absent.
|
|
585
|
+
* null => ownership unknown (milestone not in DB yet, or no DB + no local files).
|
|
586
|
+
*/
|
|
587
|
+
function getTaskOwnershipStatus(
|
|
588
|
+
basePath: string,
|
|
589
|
+
message: string,
|
|
590
|
+
milestoneId: string,
|
|
591
|
+
): true | false | null {
|
|
570
592
|
const match = message.match(/^GSD-Task:\s*(S[^/\s]+)\/(T[^\s]+)/m);
|
|
571
|
-
if (!match) return
|
|
593
|
+
if (!match) return null;
|
|
572
594
|
const [, sliceId, taskId] = match;
|
|
573
595
|
|
|
574
|
-
if (
|
|
596
|
+
if (isDbAvailable()) {
|
|
597
|
+
if (!getMilestone(milestoneId)) return null;
|
|
598
|
+
return getTask(milestoneId, sliceId, taskId) ? true : false;
|
|
599
|
+
}
|
|
575
600
|
|
|
601
|
+
// DB unavailable: fallback to local task-file presence.
|
|
576
602
|
const tasksDir = resolveTasksDir(basePath, milestoneId, sliceId);
|
|
577
|
-
if (
|
|
578
|
-
|
|
579
|
-
|
|
603
|
+
if (
|
|
604
|
+
tasksDir
|
|
605
|
+
&& (
|
|
606
|
+
existsSync(join(tasksDir, `${taskId}-PLAN.md`))
|
|
607
|
+
|| existsSync(join(tasksDir, `${taskId}-SUMMARY.md`))
|
|
608
|
+
)
|
|
609
|
+
) {
|
|
610
|
+
return true;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
return null;
|
|
580
614
|
}
|
|
581
615
|
|
|
582
616
|
function commitMessageMentionsMilestone(message: string, milestoneId: string): boolean {
|
|
@@ -42,7 +42,6 @@ import {
|
|
|
42
42
|
nativeCommit,
|
|
43
43
|
nativeGetCurrentBranch,
|
|
44
44
|
nativeDetectMainBranch,
|
|
45
|
-
nativeCheckoutBranch,
|
|
46
45
|
nativeBranchList,
|
|
47
46
|
nativeBranchExists,
|
|
48
47
|
nativeBranchListMerged,
|
|
@@ -56,7 +55,7 @@ import {
|
|
|
56
55
|
detectWorktreeName,
|
|
57
56
|
setActiveMilestoneId,
|
|
58
57
|
} from "./worktree.js";
|
|
59
|
-
import { getAutoWorktreePath, isInAutoWorktree } from "./auto-worktree.js";
|
|
58
|
+
import { getAutoWorktreePath, isInAutoWorktree, checkoutBranchWithStashGuard } from "./auto-worktree.js";
|
|
60
59
|
import { readResourceVersion, cleanStaleRuntimeUnits } from "./auto-worktree.js";
|
|
61
60
|
import { worktreePath as getWorktreeDir, isInsideWorktreesDir } from "./worktree-manager.js";
|
|
62
61
|
import { emitWorktreeOrphaned } from "./worktree-telemetry.js";
|
|
@@ -1170,7 +1169,7 @@ export async function bootstrapAutoSession(
|
|
|
1170
1169
|
isRepo,
|
|
1171
1170
|
);
|
|
1172
1171
|
if (branchToCheckout) {
|
|
1173
|
-
|
|
1172
|
+
checkoutBranchWithStashGuard(base, branchToCheckout, "isolation-none-recovery");
|
|
1174
1173
|
logWarning("bootstrap", `Returned to "${branchToCheckout}" — HEAD was on stale milestone branch "${currentBranch}" (isolation: none does not use milestone branches).`);
|
|
1175
1174
|
}
|
|
1176
1175
|
} catch (err) {
|
|
@@ -105,11 +105,31 @@ async function runValidateMilestonePostCheck(
|
|
|
105
105
|
const { milestone: mid } = parseUnitId(s.currentUnit.id);
|
|
106
106
|
if (!mid) return "continue";
|
|
107
107
|
|
|
108
|
+
const setToolFailureRetry = (message: string): VerificationResult => {
|
|
109
|
+
const retryKey = verificationRetryKey(s.currentUnit!.type, s.currentUnit!.id);
|
|
110
|
+
const attempt = (s.verificationRetryCount.get(retryKey) ?? 0) + 1;
|
|
111
|
+
s.verificationRetryCount.set(retryKey, attempt);
|
|
112
|
+
s.pendingVerificationRetry = {
|
|
113
|
+
unitId: s.currentUnit!.id,
|
|
114
|
+
failureContext: message,
|
|
115
|
+
attempt,
|
|
116
|
+
};
|
|
117
|
+
return "retry";
|
|
118
|
+
};
|
|
119
|
+
|
|
108
120
|
const validationFile = resolveMilestoneFile(s.basePath, mid, "VALIDATION");
|
|
109
|
-
if (!validationFile)
|
|
121
|
+
if (!validationFile) {
|
|
122
|
+
return setToolFailureRetry(
|
|
123
|
+
"You must call gsd_validate_milestone to persist the validation results. No VALIDATION.md was created.",
|
|
124
|
+
);
|
|
125
|
+
}
|
|
110
126
|
|
|
111
127
|
const validationContent = await loadFile(validationFile);
|
|
112
|
-
if (!validationContent)
|
|
128
|
+
if (!validationContent) {
|
|
129
|
+
return setToolFailureRetry(
|
|
130
|
+
"You must call gsd_validate_milestone to persist the validation results. VALIDATION.md exists but is empty.",
|
|
131
|
+
);
|
|
132
|
+
}
|
|
113
133
|
|
|
114
134
|
const verdict = extractVerdict(validationContent);
|
|
115
135
|
if (verdict !== "needs-remediation") {
|
|
@@ -1007,7 +1007,73 @@ export function enterBranchModeForMilestone(
|
|
|
1007
1007
|
});
|
|
1008
1008
|
}
|
|
1009
1009
|
|
|
1010
|
-
|
|
1010
|
+
checkoutBranchWithStashGuard(basePath, branch, `enter-branch-mode:${milestoneId}`);
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
export function checkoutBranchWithStashGuard(
|
|
1014
|
+
basePath: string,
|
|
1015
|
+
branch: string,
|
|
1016
|
+
reason: string,
|
|
1017
|
+
): void {
|
|
1018
|
+
let stashMarker: string | null = null;
|
|
1019
|
+
let stashed = false;
|
|
1020
|
+
|
|
1021
|
+
const status = nativeWorkingTreeStatus(basePath).trim();
|
|
1022
|
+
if (status.length > 0) {
|
|
1023
|
+
stashMarker = `gsd-checkout-stash:${reason}:${process.pid}:${Date.now()}:${process.hrtime.bigint().toString(36)}`;
|
|
1024
|
+
const stashListBefore = execFileSync("git", ["stash", "list"], {
|
|
1025
|
+
cwd: basePath,
|
|
1026
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
1027
|
+
encoding: "utf-8",
|
|
1028
|
+
});
|
|
1029
|
+
execFileSync(
|
|
1030
|
+
"git",
|
|
1031
|
+
["stash", "push", "--include-untracked", "-m", `gsd: checkout stash [${stashMarker}]`],
|
|
1032
|
+
{
|
|
1033
|
+
cwd: basePath,
|
|
1034
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
1035
|
+
encoding: "utf-8",
|
|
1036
|
+
},
|
|
1037
|
+
);
|
|
1038
|
+
const stashListAfter = execFileSync("git", ["stash", "list"], {
|
|
1039
|
+
cwd: basePath,
|
|
1040
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
1041
|
+
encoding: "utf-8",
|
|
1042
|
+
});
|
|
1043
|
+
stashed = stashListAfter !== stashListBefore;
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
// Checkout and stash-restore are split so we can distinguish two failure
|
|
1047
|
+
// modes: (a) checkout failed → HEAD did not move, restore stash and rethrow;
|
|
1048
|
+
// (b) checkout succeeded but stash pop failed → HEAD moved to `branch` but
|
|
1049
|
+
// the working-tree changes remain in the stash list. We surface a distinct
|
|
1050
|
+
// error in case (b) so callers don't assume the branch switch was rolled back.
|
|
1051
|
+
try {
|
|
1052
|
+
nativeCheckoutBranch(basePath, branch);
|
|
1053
|
+
} catch (checkoutErr) {
|
|
1054
|
+
if (stashed) {
|
|
1055
|
+
try {
|
|
1056
|
+
popStashByRef(basePath, stashMarker);
|
|
1057
|
+
} catch (restoreErr) {
|
|
1058
|
+
logWarning("worktree", `git stash pop failed during checkout restore: ${restoreErr instanceof Error ? restoreErr.message : String(restoreErr)}`);
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
throw checkoutErr;
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
if (stashed) {
|
|
1065
|
+
try {
|
|
1066
|
+
popStashByRef(basePath, stashMarker);
|
|
1067
|
+
} catch (popErr) {
|
|
1068
|
+
const msg = popErr instanceof Error ? popErr.message : String(popErr);
|
|
1069
|
+
const wrapped = new Error(
|
|
1070
|
+
`checkout to '${branch}' succeeded but stash restore failed; working tree changes remain in the stash list. Original error: ${msg}`,
|
|
1071
|
+
);
|
|
1072
|
+
const ref = (popErr as { stashRef?: string } | null)?.stashRef;
|
|
1073
|
+
if (ref) (wrapped as { stashRef?: string }).stashRef = ref;
|
|
1074
|
+
throw wrapped;
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1011
1077
|
}
|
|
1012
1078
|
|
|
1013
1079
|
// ─── Public API ────────────────────────────────────────────────────────────
|
|
@@ -1992,14 +2058,6 @@ export function mergeMilestoneToMain(
|
|
|
1992
2058
|
logWarning("worktree", `git stash failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1993
2059
|
}
|
|
1994
2060
|
|
|
1995
|
-
if (needsDbCycle && dbPathToReopen) {
|
|
1996
|
-
try {
|
|
1997
|
-
openDatabase(dbPathToReopen);
|
|
1998
|
-
} catch (err) {
|
|
1999
|
-
logWarning("worktree", `post-stash db reopen failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
2000
|
-
}
|
|
2001
|
-
}
|
|
2002
|
-
|
|
2003
2061
|
// 7b. Clean up stale merge state before attempting squash merge (#2912).
|
|
2004
2062
|
// A leftover MERGE_HEAD (from a previous failed merge, libgit2 native path,
|
|
2005
2063
|
// or interrupted operation) causes `git merge --squash` to refuse with
|
|
@@ -2009,6 +2067,13 @@ export function mergeMilestoneToMain(
|
|
|
2009
2067
|
|
|
2010
2068
|
// 8. Squash merge — auto-resolve .gsd/ state file conflicts (#530)
|
|
2011
2069
|
const mergeResult = nativeMergeSquash(originalBasePath_, milestoneBranch);
|
|
2070
|
+
if (needsDbCycle && dbPathToReopen) {
|
|
2071
|
+
try {
|
|
2072
|
+
openDatabase(dbPathToReopen);
|
|
2073
|
+
} catch (err) {
|
|
2074
|
+
logWarning("worktree", `post-merge db reopen failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
2075
|
+
}
|
|
2076
|
+
}
|
|
2012
2077
|
|
|
2013
2078
|
if (!mergeResult.success) {
|
|
2014
2079
|
// Dirty working tree — the merge was rejected before it started (e.g.
|
|
@@ -1029,6 +1029,8 @@ export async function rerootCommandSession(
|
|
|
1029
1029
|
}
|
|
1030
1030
|
|
|
1031
1031
|
export async function cleanupAfterLoopExit(ctx: ExtensionContext): Promise<void> {
|
|
1032
|
+
const preserveStepSurface = s.preserveStepSurfaceAfterLoopExit;
|
|
1033
|
+
const preservePausedSurface = s.paused;
|
|
1032
1034
|
s.currentUnit = null;
|
|
1033
1035
|
s.active = false;
|
|
1034
1036
|
deactivateGSD();
|
|
@@ -1051,12 +1053,16 @@ export async function cleanupAfterLoopExit(ctx: ExtensionContext): Promise<void>
|
|
|
1051
1053
|
// A transient provider-error pause intentionally leaves the paused badge
|
|
1052
1054
|
// visible so the user still has a resumable auto-mode signal on screen.
|
|
1053
1055
|
if (!s.paused) {
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1056
|
+
if (preserveStepSurface) {
|
|
1057
|
+
s.preserveStepSurfaceAfterLoopExit = false;
|
|
1058
|
+
} else {
|
|
1059
|
+
ctx.ui.setStatus("gsd-auto", undefined);
|
|
1060
|
+
ctx.ui.setWidget("gsd-progress", undefined);
|
|
1061
|
+
if (s.completionStopInProgress) {
|
|
1062
|
+
s.completionStopInProgress = false;
|
|
1063
|
+
}
|
|
1064
|
+
initHealthWidget(ctx);
|
|
1058
1065
|
}
|
|
1059
|
-
initHealthWidget(ctx);
|
|
1060
1066
|
}
|
|
1061
1067
|
|
|
1062
1068
|
// ADR-016 phase 3 (#5693): the stop-path basePath restore + chdir routes
|
|
@@ -1064,7 +1070,7 @@ export async function cleanupAfterLoopExit(ctx: ExtensionContext): Promise<void>
|
|
|
1064
1070
|
// `s.basePath` mutation and the paired `process.chdir` for auto-loop
|
|
1065
1071
|
// transitions. The verb assigns `s.basePath` before any throwable work, so
|
|
1066
1072
|
// a thrown error still leaves basePath restored.
|
|
1067
|
-
if (s.originalBasePath) {
|
|
1073
|
+
if (s.originalBasePath && !preserveStepSurface && !preservePausedSurface) {
|
|
1068
1074
|
try {
|
|
1069
1075
|
buildLifecycle().restoreToProjectRoot();
|
|
1070
1076
|
} catch (err) {
|
|
@@ -1076,7 +1082,7 @@ export async function cleanupAfterLoopExit(ctx: ExtensionContext): Promise<void>
|
|
|
1076
1082
|
}
|
|
1077
1083
|
}
|
|
1078
1084
|
|
|
1079
|
-
if (s.originalBasePath && s.cmdCtx) {
|
|
1085
|
+
if (s.originalBasePath && s.cmdCtx && !preserveStepSurface && !preservePausedSurface) {
|
|
1080
1086
|
const result = await rerootCommandSession(s.cmdCtx, s.originalBasePath);
|
|
1081
1087
|
if (result.status === "cancelled") {
|
|
1082
1088
|
logWarning("engine", "post-loop session re-root was cancelled", { file: "auto.ts", basePath: s.originalBasePath });
|
|
@@ -1731,7 +1737,6 @@ export async function pauseAuto(
|
|
|
1731
1737
|
restoreProjectRootEnv();
|
|
1732
1738
|
restoreMilestoneLockEnv();
|
|
1733
1739
|
s.pendingVerificationRetry = null;
|
|
1734
|
-
s.verificationRetryCount.clear();
|
|
1735
1740
|
ctx?.ui.setStatus("gsd-auto", "paused");
|
|
1736
1741
|
ctx?.ui.setWidget("gsd-progress", undefined);
|
|
1737
1742
|
const resumeCmd = s.stepMode ? "/gsd next" : "/gsd auto";
|
|
@@ -100,6 +100,14 @@ export function isUserInitiatedAbortMessage(message: string | undefined | null):
|
|
|
100
100
|
return /\b(?:claude code process aborted by user|request aborted by user|process aborted by user)\b/i.test(message);
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
+
export function shouldDeferTransientErrorToCoreRetry(
|
|
104
|
+
cls: ErrorClass,
|
|
105
|
+
rawErrorMsg: string,
|
|
106
|
+
): boolean {
|
|
107
|
+
if (!isTransient(cls) || cls.kind === "rate-limit") return false;
|
|
108
|
+
return !/retry failed after \d+ attempts:/i.test(rawErrorMsg);
|
|
109
|
+
}
|
|
110
|
+
|
|
103
111
|
function isBareClaudeCodeSessionSwitchAbortMarker(message: string | undefined | null): boolean {
|
|
104
112
|
if (!message) return false;
|
|
105
113
|
const normalized = message.trim().replace(/\s+/g, " ").toLowerCase();
|
|
@@ -488,7 +496,7 @@ export async function handleAgentEnd(
|
|
|
488
496
|
// Core retries transient failures in-session after this handler.
|
|
489
497
|
// Keep that behavior for non-rate-limit classes to avoid pause/retry races,
|
|
490
498
|
// but let rate-limit continue into model fallback logic below (#4373).
|
|
491
|
-
if (
|
|
499
|
+
if (shouldDeferTransientErrorToCoreRetry(cls, rawErrorMsg)) {
|
|
492
500
|
return;
|
|
493
501
|
}
|
|
494
502
|
|
|
@@ -913,7 +913,7 @@ export function registerDbTools(pi: ExtensionAPI): void {
|
|
|
913
913
|
label: "Skip Slice",
|
|
914
914
|
description:
|
|
915
915
|
"Mark a slice as skipped so auto-mode advances past it without executing. " +
|
|
916
|
-
"Non-closed tasks within the slice are cascaded to skipped so milestone completion is not blocked by leftover pending tasks
|
|
916
|
+
"Non-closed tasks within the slice are cascaded to skipped so milestone completion is not blocked by leftover pending tasks. " +
|
|
917
917
|
"The slice data is preserved for reference. The state machine treats skipped slices like completed ones for dependency satisfaction.",
|
|
918
918
|
promptSnippet: "Skip a GSD slice (mark as skipped, auto-mode will advance past it)",
|
|
919
919
|
promptGuidelines: [
|
|
@@ -748,7 +748,7 @@ function matchesAllowedGlob(absPath: string, basePath: string, globs: readonly s
|
|
|
748
748
|
function blockReason(unitType: string, mode: string, what: string): string {
|
|
749
749
|
return [
|
|
750
750
|
`HARD BLOCK: unit "${unitType}" runs under tools-policy "${mode}" — ${what}.`,
|
|
751
|
-
`This is a mechanical gate enforced by manifest.tools
|
|
751
|
+
`This is a mechanical gate enforced by manifest.tools. You MUST NOT proceed,`,
|
|
752
752
|
`retry the same call, or rationalize past this block. If you need to write user source,`,
|
|
753
753
|
`the work belongs in execute-task, not in a planning unit.`,
|
|
754
754
|
].join(" ");
|
|
@@ -14,7 +14,7 @@ export interface GsdCommandDefinition {
|
|
|
14
14
|
type CompletionMap = Record<string, readonly GsdCommandDefinition[]>;
|
|
15
15
|
|
|
16
16
|
export const GSD_COMMAND_DESCRIPTION =
|
|
17
|
-
"GSD — Get Shit Done: /gsd help|start|templates|next|auto|stop|pause|status|widget|visualize|brief|queue|quick|discuss|capture|triage|dispatch|history|undo|undo-task|reset-slice|rate|skip|export|cleanup|model|mode|prefs|config|keys|hooks|run-hook|skill-health|doctor|debug|logs|forensics|changelog|migrate|remote|steer|knowledge|new-milestone|new-project|parallel|cmux|park|unpark|init|setup|onboarding|inspect|extensions|update|fast|mcp|rethink|workflow|codebase|notifications|ship|do|session-report|backlog|pr-branch|add-tests|scan|language|worktree|eval-review";
|
|
17
|
+
"GSD — Get Shit Done: /gsd help|start|templates|next|auto|stop|pause|status|widget|visualize|brief|queue|quick|discuss|capture|triage|dispatch|verdict|history|undo|undo-task|reset-slice|rate|skip|export|cleanup|model|mode|prefs|config|keys|hooks|run-hook|skill-health|doctor|debug|logs|forensics|changelog|migrate|remote|steer|knowledge|new-milestone|new-project|parallel|cmux|park|unpark|init|setup|onboarding|inspect|extensions|update|fast|mcp|rethink|workflow|codebase|notifications|ship|do|session-report|backlog|pr-branch|add-tests|scan|language|worktree|eval-review";
|
|
18
18
|
|
|
19
19
|
export const TOP_LEVEL_SUBCOMMANDS: readonly GsdCommandDefinition[] = [
|
|
20
20
|
{ cmd: "help", desc: "Categorized command reference with descriptions" },
|
|
@@ -33,6 +33,7 @@ export const TOP_LEVEL_SUBCOMMANDS: readonly GsdCommandDefinition[] = [
|
|
|
33
33
|
{ cmd: "changelog", desc: "Show categorized release notes" },
|
|
34
34
|
{ cmd: "triage", desc: "Manually trigger triage of pending captures" },
|
|
35
35
|
{ cmd: "dispatch", desc: "Dispatch a specific phase directly" },
|
|
36
|
+
{ cmd: "verdict", desc: "Override the recorded milestone validation verdict (pass|needs-attention|needs-remediation)" },
|
|
36
37
|
{ cmd: "history", desc: "View execution history" },
|
|
37
38
|
{ cmd: "undo", desc: "Revert last completed unit" },
|
|
38
39
|
{ cmd: "undo-task", desc: "Reset a specific task's completion state (DB + markdown)" },
|
|
@@ -248,6 +249,11 @@ const NESTED_COMPLETIONS: CompletionMap = {
|
|
|
248
249
|
{ cmd: "uat", desc: "Run user acceptance testing" },
|
|
249
250
|
{ cmd: "replan", desc: "Replan the current slice" },
|
|
250
251
|
],
|
|
252
|
+
verdict: [
|
|
253
|
+
{ cmd: "pass", desc: "Override the milestone validation verdict to pass" },
|
|
254
|
+
{ cmd: "needs-attention", desc: "Override the verdict to needs-attention (requires --rationale)" },
|
|
255
|
+
{ cmd: "needs-remediation", desc: "Override the verdict to needs-remediation (requires --rationale)" },
|
|
256
|
+
],
|
|
251
257
|
rate: [
|
|
252
258
|
{ cmd: "over", desc: "Model was overqualified for this task" },
|
|
253
259
|
{ cmd: "ok", desc: "Model was appropriate for this task" },
|
|
@@ -71,6 +71,7 @@ export function showHelp(ctx: ExtensionCommandContext, args = ""): void {
|
|
|
71
71
|
" /gsd new-project Bootstrap a new project (use --deep for staged project-level discovery)",
|
|
72
72
|
" /gsd quick Execute a quick task without full planning overhead",
|
|
73
73
|
" /gsd dispatch Dispatch a specific phase directly [research|plan|execute|complete|uat|replan]",
|
|
74
|
+
" /gsd verdict <v> Override milestone validation verdict [pass|needs-attention|needs-remediation] [--milestone Mxxx] [--rationale \"...\"]",
|
|
74
75
|
" /gsd parallel Parallel milestone orchestration [start|status|stop|pause|resume|merge|watch]",
|
|
75
76
|
" /gsd workflow Custom workflow lifecycle [new|run|list|validate|pause|resume]",
|
|
76
77
|
"",
|
|
@@ -188,6 +188,11 @@ Examples:
|
|
|
188
188
|
await dispatchDirectPhase(ctx, pi, phase, projectRoot());
|
|
189
189
|
return true;
|
|
190
190
|
}
|
|
191
|
+
if (trimmed === "verdict" || trimmed.startsWith("verdict ")) {
|
|
192
|
+
const { handleVerdict } = await import("../../commands-verdict.js");
|
|
193
|
+
await handleVerdict(trimmed.replace(/^verdict\s*/, "").trim(), ctx, projectRoot());
|
|
194
|
+
return true;
|
|
195
|
+
}
|
|
191
196
|
if (trimmed === "notifications" || trimmed.startsWith("notifications ")) {
|
|
192
197
|
const { handleNotificationsCommand } = await import("./notifications-handler.js");
|
|
193
198
|
await handleNotificationsCommand(trimmed.replace(/^notifications\s*/, "").trim(), ctx, pi);
|
|
@@ -587,10 +587,15 @@ async function configureModels(ctx: ExtensionCommandContext, prefs: Record<strin
|
|
|
587
587
|
const models: Record<string, unknown> = (prefs.models as Record<string, unknown>) ?? {};
|
|
588
588
|
|
|
589
589
|
const availableModels = ctx.modelRegistry.getAvailable();
|
|
590
|
-
|
|
590
|
+
const getAllWithDiscovered = (ctx.modelRegistry as { getAllWithDiscovered?: () => typeof availableModels }).getAllWithDiscovered;
|
|
591
|
+
const availableProviders = new Set(availableModels.map((m) => m.provider));
|
|
592
|
+
const selectableModels = typeof getAllWithDiscovered === "function"
|
|
593
|
+
? getAllWithDiscovered().filter((m) => availableProviders.has(m.provider))
|
|
594
|
+
: availableModels;
|
|
595
|
+
if (selectableModels.length > 0) {
|
|
591
596
|
// Group models by provider, sorted alphabetically
|
|
592
|
-
const byProvider = new Map<string, typeof
|
|
593
|
-
for (const m of
|
|
597
|
+
const byProvider = new Map<string, typeof selectableModels>();
|
|
598
|
+
for (const m of selectableModels) {
|
|
594
599
|
let group = byProvider.get(m.provider);
|
|
595
600
|
if (!group) {
|
|
596
601
|
group = [];
|