gsd-pi 2.60.0-dev.d9052f5 → 2.61.0-dev.7aed0bf
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/ask-user-questions.js +7 -4
- package/dist/resources/extensions/gsd/auto/phases.js +15 -7
- package/dist/resources/extensions/gsd/auto-dashboard.js +21 -8
- package/dist/resources/extensions/gsd/auto-dispatch.js +6 -3
- package/dist/resources/extensions/gsd/auto-model-selection.js +58 -9
- package/dist/resources/extensions/gsd/auto-post-unit.js +3 -2
- package/dist/resources/extensions/gsd/auto-prompts.js +36 -20
- package/dist/resources/extensions/gsd/auto-recovery.js +37 -18
- package/dist/resources/extensions/gsd/auto-start.js +9 -5
- package/dist/resources/extensions/gsd/auto-timers.js +11 -5
- package/dist/resources/extensions/gsd/auto-unit-closeout.js +5 -3
- package/dist/resources/extensions/gsd/auto-verification.js +3 -2
- package/dist/resources/extensions/gsd/auto-worktree.js +120 -55
- package/dist/resources/extensions/gsd/auto.js +39 -17
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +6 -3
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +2 -2
- package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +4 -10
- package/dist/resources/extensions/gsd/bootstrap/journal-tools.js +2 -1
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +7 -0
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +11 -10
- package/dist/resources/extensions/gsd/commands/catalog.js +2 -0
- package/dist/resources/extensions/gsd/commands-codebase.js +48 -21
- package/dist/resources/extensions/gsd/commands-inspect.js +2 -1
- package/dist/resources/extensions/gsd/commands-maintenance.js +32 -19
- package/dist/resources/extensions/gsd/complexity-classifier.js +8 -4
- package/dist/resources/extensions/gsd/custom-verification.js +3 -2
- package/dist/resources/extensions/gsd/gsd-db.js +33 -13
- package/dist/resources/extensions/gsd/guided-flow.js +19 -9
- package/dist/resources/extensions/gsd/init-wizard.js +12 -0
- package/dist/resources/extensions/gsd/markdown-renderer.js +11 -9
- package/dist/resources/extensions/gsd/md-importer.js +5 -4
- package/dist/resources/extensions/gsd/milestone-actions.js +3 -2
- package/dist/resources/extensions/gsd/milestone-ids.js +2 -1
- package/dist/resources/extensions/gsd/model-router.js +156 -121
- package/dist/resources/extensions/gsd/parallel-merge.js +5 -3
- package/dist/resources/extensions/gsd/parallel-orchestrator.js +26 -14
- package/dist/resources/extensions/gsd/preferences-types.js +1 -0
- package/dist/resources/extensions/gsd/preferences-validation.js +45 -0
- package/dist/resources/extensions/gsd/preferences.js +15 -3
- package/dist/resources/extensions/gsd/prompt-loader.js +3 -2
- package/dist/resources/extensions/gsd/prompts/rethink.md +1 -1
- package/dist/resources/extensions/gsd/rule-registry.js +7 -6
- package/dist/resources/extensions/gsd/safe-fs.js +6 -8
- package/dist/resources/extensions/gsd/tools/complete-milestone.js +3 -2
- package/dist/resources/extensions/gsd/tools/complete-slice.js +3 -2
- package/dist/resources/extensions/gsd/tools/complete-task.js +3 -2
- package/dist/resources/extensions/gsd/tools/plan-milestone.js +3 -2
- package/dist/resources/extensions/gsd/tools/plan-slice.js +3 -2
- package/dist/resources/extensions/gsd/tools/plan-task.js +2 -1
- package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +4 -4
- package/dist/resources/extensions/gsd/tools/reopen-slice.js +2 -1
- package/dist/resources/extensions/gsd/tools/reopen-task.js +2 -1
- package/dist/resources/extensions/gsd/tools/replan-slice.js +2 -1
- package/dist/resources/extensions/gsd/tools/validate-milestone.js +2 -1
- package/dist/resources/extensions/gsd/triage-resolution.js +11 -4
- package/dist/resources/extensions/gsd/workflow-events.js +2 -1
- package/dist/resources/extensions/gsd/workflow-logger.js +37 -4
- package/dist/resources/extensions/gsd/workflow-migration.js +14 -12
- package/dist/resources/extensions/gsd/workflow-projections.js +2 -2
- package/dist/resources/extensions/gsd/workflow-reconcile.js +2 -2
- package/dist/resources/extensions/gsd/worktree-manager.js +26 -14
- package/dist/resources/extensions/shared/interview-ui.js +3 -1
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +19 -19
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
- 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/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 +19 -19
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +2 -2
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/package.json +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.js +5 -0
- package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts +2 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.js +16 -0
- package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +26 -0
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/config.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/config.js +6 -1
- package/packages/pi-coding-agent/dist/core/lsp/config.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/defaults.json +2 -2
- package/packages/pi-coding-agent/dist/core/lsp/lsp-legacy-alias.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/lsp/lsp-legacy-alias.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/lsp/lsp-legacy-alias.test.js +47 -0
- package/packages/pi-coding-agent/dist/core/lsp/lsp-legacy-alias.test.js.map +1 -0
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/core/extensions/loader.ts +6 -0
- package/packages/pi-coding-agent/src/core/extensions/runner.ts +19 -0
- package/packages/pi-coding-agent/src/core/extensions/types.ts +26 -0
- package/packages/pi-coding-agent/src/core/lsp/config.ts +7 -1
- package/packages/pi-coding-agent/src/core/lsp/defaults.json +2 -2
- package/packages/pi-coding-agent/src/core/lsp/lsp-legacy-alias.test.ts +70 -0
- package/pkg/package.json +1 -1
- package/src/resources/extensions/ask-user-questions.ts +7 -3
- package/src/resources/extensions/gsd/auto/phases.ts +17 -7
- package/src/resources/extensions/gsd/auto-dashboard.ts +22 -8
- package/src/resources/extensions/gsd/auto-dispatch.ts +7 -3
- package/src/resources/extensions/gsd/auto-model-selection.ts +77 -15
- package/src/resources/extensions/gsd/auto-post-unit.ts +4 -4
- package/src/resources/extensions/gsd/auto-prompts.ts +37 -20
- package/src/resources/extensions/gsd/auto-recovery.ts +38 -18
- package/src/resources/extensions/gsd/auto-start.ts +10 -9
- package/src/resources/extensions/gsd/auto-timers.ts +12 -5
- package/src/resources/extensions/gsd/auto-unit-closeout.ts +6 -2
- package/src/resources/extensions/gsd/auto-verification.ts +3 -6
- package/src/resources/extensions/gsd/auto-worktree.ts +121 -55
- package/src/resources/extensions/gsd/auto.ts +40 -17
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +4 -3
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +2 -2
- package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +4 -16
- package/src/resources/extensions/gsd/bootstrap/journal-tools.ts +2 -1
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +8 -0
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +11 -10
- package/src/resources/extensions/gsd/commands/catalog.ts +2 -0
- package/src/resources/extensions/gsd/commands-codebase.ts +52 -20
- package/src/resources/extensions/gsd/commands-inspect.ts +2 -1
- package/src/resources/extensions/gsd/commands-maintenance.ts +28 -19
- package/src/resources/extensions/gsd/complexity-classifier.ts +9 -4
- package/src/resources/extensions/gsd/custom-verification.ts +3 -2
- package/src/resources/extensions/gsd/gsd-db.ts +12 -14
- package/src/resources/extensions/gsd/guided-flow.ts +9 -8
- package/src/resources/extensions/gsd/init-wizard.ts +12 -0
- package/src/resources/extensions/gsd/markdown-renderer.ts +11 -17
- package/src/resources/extensions/gsd/md-importer.ts +5 -4
- package/src/resources/extensions/gsd/milestone-actions.ts +3 -2
- package/src/resources/extensions/gsd/milestone-ids.ts +2 -1
- package/src/resources/extensions/gsd/model-router.ts +199 -173
- package/src/resources/extensions/gsd/parallel-merge.ts +5 -3
- package/src/resources/extensions/gsd/parallel-orchestrator.ts +18 -14
- package/src/resources/extensions/gsd/preferences-types.ts +13 -0
- package/src/resources/extensions/gsd/preferences-validation.ts +45 -0
- package/src/resources/extensions/gsd/preferences.ts +16 -3
- package/src/resources/extensions/gsd/prompt-loader.ts +3 -2
- package/src/resources/extensions/gsd/prompts/rethink.md +1 -1
- package/src/resources/extensions/gsd/rule-registry.ts +7 -6
- package/src/resources/extensions/gsd/safe-fs.ts +6 -5
- package/src/resources/extensions/gsd/tests/capability-router.test.ts +347 -0
- package/src/resources/extensions/gsd/tests/codebase-generator.test.ts +63 -0
- package/src/resources/extensions/gsd/tests/complexity-classifier.test.ts +27 -2
- package/src/resources/extensions/gsd/tests/db-path-worktree-symlink.test.ts +4 -4
- package/src/resources/extensions/gsd/tests/integration/state-machine-edge-cases.test.ts +1188 -0
- package/src/resources/extensions/gsd/tests/integration/state-machine-runtime-failures.test.ts +841 -0
- package/src/resources/extensions/gsd/tests/model-router.test.ts +403 -3
- package/src/resources/extensions/gsd/tests/preferences.test.ts +62 -0
- package/src/resources/extensions/gsd/tests/remote-questions.test.ts +21 -0
- package/src/resources/extensions/gsd/tests/silent-catch-diagnostics.test.ts +284 -0
- package/src/resources/extensions/gsd/tests/workflow-logger-audit.test.ts +120 -0
- package/src/resources/extensions/gsd/tests/workflow-logger.test.ts +6 -6
- package/src/resources/extensions/gsd/tools/complete-milestone.ts +3 -6
- package/src/resources/extensions/gsd/tools/complete-slice.ts +3 -6
- package/src/resources/extensions/gsd/tools/complete-task.ts +3 -6
- package/src/resources/extensions/gsd/tools/plan-milestone.ts +3 -6
- package/src/resources/extensions/gsd/tools/plan-slice.ts +3 -6
- package/src/resources/extensions/gsd/tools/plan-task.ts +2 -3
- package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +4 -6
- package/src/resources/extensions/gsd/tools/reopen-slice.ts +2 -3
- package/src/resources/extensions/gsd/tools/reopen-task.ts +2 -3
- package/src/resources/extensions/gsd/tools/replan-slice.ts +2 -3
- package/src/resources/extensions/gsd/tools/validate-milestone.ts +2 -3
- package/src/resources/extensions/gsd/triage-resolution.ts +11 -4
- package/src/resources/extensions/gsd/types.ts +1 -0
- package/src/resources/extensions/gsd/workflow-events.ts +2 -1
- package/src/resources/extensions/gsd/workflow-logger.ts +52 -5
- package/src/resources/extensions/gsd/workflow-migration.ts +14 -12
- package/src/resources/extensions/gsd/workflow-projections.ts +2 -2
- package/src/resources/extensions/gsd/workflow-reconcile.ts +2 -2
- package/src/resources/extensions/gsd/worktree-manager.ts +16 -14
- package/src/resources/extensions/shared/interview-ui.ts +3 -1
- package/src/resources/extensions/shared/tests/interview-notes-loop.test.ts +144 -0
- /package/dist/web/standalone/.next/static/{JVkoVYumy0cDhOQISEYdG → b7FOoMHaUb3FPoLNbxar4}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{JVkoVYumy0cDhOQISEYdG → b7FOoMHaUb3FPoLNbxar4}/_ssgManifest.js +0 -0
|
@@ -603,6 +603,22 @@ export interface ModelSelectEvent {
|
|
|
603
603
|
source: ModelSelectSource;
|
|
604
604
|
}
|
|
605
605
|
|
|
606
|
+
/** Fired before model selection runs capability scoring. Extensions can override the selected model. */
|
|
607
|
+
export interface BeforeModelSelectEvent {
|
|
608
|
+
type: "before_model_select";
|
|
609
|
+
unitType: string;
|
|
610
|
+
unitId: string;
|
|
611
|
+
classification: { tier: string; reason: string; downgraded: boolean };
|
|
612
|
+
taskMetadata?: Record<string, unknown>;
|
|
613
|
+
eligibleModels: string[];
|
|
614
|
+
phaseConfig?: { primary: string; fallbacks: string[] };
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
/** Result from before_model_select event handler. Return { modelId } to override selection. */
|
|
618
|
+
export interface BeforeModelSelectResult {
|
|
619
|
+
modelId: string;
|
|
620
|
+
}
|
|
621
|
+
|
|
606
622
|
// ============================================================================
|
|
607
623
|
// User Bash Events
|
|
608
624
|
// ============================================================================
|
|
@@ -1052,6 +1068,14 @@ export interface ExtensionAPI {
|
|
|
1052
1068
|
on(event: "tool_result", handler: ExtensionHandler<ToolResultEvent, ToolResultEventResult>): void;
|
|
1053
1069
|
on(event: "user_bash", handler: ExtensionHandler<UserBashEvent, UserBashEventResult>): void;
|
|
1054
1070
|
on(event: "input", handler: ExtensionHandler<InputEvent, InputEventResult>): void;
|
|
1071
|
+
on(event: "before_model_select", handler: ExtensionHandler<BeforeModelSelectEvent, BeforeModelSelectResult>): void;
|
|
1072
|
+
|
|
1073
|
+
// =========================================================================
|
|
1074
|
+
// Event Emission (for host extensions that orchestrate model selection)
|
|
1075
|
+
// =========================================================================
|
|
1076
|
+
|
|
1077
|
+
/** Emit before_model_select event. Returns override model ID or undefined. */
|
|
1078
|
+
emitBeforeModelSelect(event: Omit<BeforeModelSelectEvent, "type">): Promise<BeforeModelSelectResult | undefined>;
|
|
1055
1079
|
|
|
1056
1080
|
// =========================================================================
|
|
1057
1081
|
// Tool Registration
|
|
@@ -1367,6 +1391,8 @@ export interface ExtensionRuntimeState {
|
|
|
1367
1391
|
*/
|
|
1368
1392
|
registerProvider: (name: string, config: ProviderConfig) => void;
|
|
1369
1393
|
unregisterProvider: (name: string) => void;
|
|
1394
|
+
/** Emit before_model_select event to all registered handlers. Bound by ExtensionRunner. */
|
|
1395
|
+
emitBeforeModelSelect: (event: Omit<BeforeModelSelectEvent, "type">) => Promise<BeforeModelSelectResult | undefined>;
|
|
1370
1396
|
}
|
|
1371
1397
|
|
|
1372
1398
|
/**
|
|
@@ -12,6 +12,11 @@ import type { ServerConfig } from "./types.js";
|
|
|
12
12
|
const require = createRequire(import.meta.url);
|
|
13
13
|
const DEFAULTS = require("./defaults.json") as Record<string, Partial<ServerConfig>>;
|
|
14
14
|
|
|
15
|
+
/** Map legacy server keys to their current names so user overrides still merge. */
|
|
16
|
+
const LEGACY_ALIASES: Record<string, string> = {
|
|
17
|
+
"kotlin-language-server": "kotlin-lsp",
|
|
18
|
+
};
|
|
19
|
+
|
|
15
20
|
export interface LspConfig {
|
|
16
21
|
servers: Record<string, ServerConfig>;
|
|
17
22
|
/** Idle timeout in milliseconds. If set, LSP clients will be shutdown after this period of inactivity. Disabled by default. */
|
|
@@ -109,7 +114,8 @@ function mergeServers(
|
|
|
109
114
|
overrides: Record<string, Partial<ServerConfig>>,
|
|
110
115
|
): Record<string, ServerConfig> {
|
|
111
116
|
const merged: Record<string, ServerConfig> = { ...base };
|
|
112
|
-
for (const [
|
|
117
|
+
for (const [rawName, config] of Object.entries(overrides)) {
|
|
118
|
+
const name = LEGACY_ALIASES[rawName] ?? rawName;
|
|
113
119
|
if (merged[name]) {
|
|
114
120
|
const candidate = { ...merged[name], ...config };
|
|
115
121
|
const normalized = normalizeServerConfig(name, candidate);
|
|
@@ -189,8 +189,8 @@
|
|
|
189
189
|
"fileTypes": [".java"],
|
|
190
190
|
"rootMarkers": ["pom.xml", "build.gradle", "build.gradle.kts", "settings.gradle", ".project"]
|
|
191
191
|
},
|
|
192
|
-
"kotlin-
|
|
193
|
-
"command": "kotlin-
|
|
192
|
+
"kotlin-lsp": {
|
|
193
|
+
"command": "kotlin-lsp",
|
|
194
194
|
"args": [],
|
|
195
195
|
"fileTypes": [".kt", ".kts"],
|
|
196
196
|
"rootMarkers": ["build.gradle", "build.gradle.kts", "pom.xml", "settings.gradle", "settings.gradle.kts"]
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// GSD2 — Regression test for LSP legacy server key aliases
|
|
2
|
+
// Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* When a default server key is renamed (e.g., kotlin-language-server → kotlin-lsp),
|
|
6
|
+
* user overrides referencing the old key must still merge correctly via LEGACY_ALIASES.
|
|
7
|
+
*
|
|
8
|
+
* This test exercises the merge path through loadConfig() with a temp project
|
|
9
|
+
* containing an lsp.json that uses the legacy key.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { describe, it, beforeEach, afterEach } from "node:test";
|
|
13
|
+
import assert from "node:assert/strict";
|
|
14
|
+
import * as fs from "node:fs";
|
|
15
|
+
import * as path from "node:path";
|
|
16
|
+
import * as os from "node:os";
|
|
17
|
+
import { loadConfig } from "./config.js";
|
|
18
|
+
|
|
19
|
+
describe("LSP legacy server key aliases", () => {
|
|
20
|
+
let tmpDir: string;
|
|
21
|
+
|
|
22
|
+
beforeEach(() => {
|
|
23
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "lsp-alias-test-"));
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
afterEach(() => {
|
|
27
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it("merges user override with legacy key 'kotlin-language-server' into 'kotlin-lsp'", () => {
|
|
31
|
+
// Write an lsp.json that uses the old key name with a command that exists (node)
|
|
32
|
+
// so resolveCommand doesn't filter it out.
|
|
33
|
+
const overrideConfig = {
|
|
34
|
+
servers: {
|
|
35
|
+
"kotlin-language-server": {
|
|
36
|
+
command: "node",
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
fs.writeFileSync(
|
|
41
|
+
path.join(tmpDir, "lsp.json"),
|
|
42
|
+
JSON.stringify(overrideConfig),
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
// Also add root markers so the server is detected
|
|
46
|
+
fs.writeFileSync(path.join(tmpDir, "build.gradle.kts"), "");
|
|
47
|
+
|
|
48
|
+
const config = loadConfig(tmpDir);
|
|
49
|
+
|
|
50
|
+
// The merged config should have kotlin-lsp (new key) with the user's command override
|
|
51
|
+
const kotlinServer = config.servers["kotlin-lsp"];
|
|
52
|
+
assert.ok(kotlinServer, "kotlin-lsp should exist in merged config");
|
|
53
|
+
assert.equal(
|
|
54
|
+
kotlinServer.command,
|
|
55
|
+
"node",
|
|
56
|
+
"command should be overridden from user config via legacy alias",
|
|
57
|
+
);
|
|
58
|
+
assert.ok(
|
|
59
|
+
kotlinServer.fileTypes.includes(".kt"),
|
|
60
|
+
"fileTypes should be inherited from defaults",
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
// The old key should NOT appear as a separate entry
|
|
64
|
+
assert.equal(
|
|
65
|
+
config.servers["kotlin-language-server"],
|
|
66
|
+
undefined,
|
|
67
|
+
"legacy key should not appear as separate server",
|
|
68
|
+
);
|
|
69
|
+
});
|
|
70
|
+
});
|
package/pkg/package.json
CHANGED
|
@@ -135,10 +135,14 @@ export default function AskUserQuestions(pi: ExtensionAPI) {
|
|
|
135
135
|
}
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
+
// Try remote first if configured (works in both interactive and headless modes).
|
|
139
|
+
// tryRemoteQuestions returns null when no remote channel is configured, so
|
|
140
|
+
// this is a no-op when the user has not set up Slack/Discord/Telegram.
|
|
141
|
+
const { tryRemoteQuestions } = await import("./remote-questions/manager.js");
|
|
142
|
+
const remoteResult = await tryRemoteQuestions(params.questions, signal);
|
|
143
|
+
if (remoteResult) return { ...remoteResult, details: remoteResult.details as unknown };
|
|
144
|
+
|
|
138
145
|
if (!ctx.hasUI) {
|
|
139
|
-
const { tryRemoteQuestions } = await import("./remote-questions/manager.js");
|
|
140
|
-
const remoteResult = await tryRemoteQuestions(params.questions, signal);
|
|
141
|
-
if (remoteResult) return { ...remoteResult, details: remoteResult.details as unknown };
|
|
142
146
|
return errorResult("Error: UI not available (non-interactive mode)", params.questions);
|
|
143
147
|
}
|
|
144
148
|
|
|
@@ -721,6 +721,8 @@ export async function runGuards(
|
|
|
721
721
|
// ── Stop/Backtrack directive guard (#3487) ──
|
|
722
722
|
// Check for unexecuted stop or backtrack captures BEFORE dispatching any unit.
|
|
723
723
|
// This ensures user "halt" directives are honored immediately.
|
|
724
|
+
// IMPORTANT: Fail-closed — any exception during stop handling still breaks the loop
|
|
725
|
+
// to ensure user halt intent is never silently dropped.
|
|
724
726
|
try {
|
|
725
727
|
const { loadStopCaptures, markCaptureExecuted } = await import("../captures.js");
|
|
726
728
|
const stopCaptures = loadStopCaptures(s.basePath);
|
|
@@ -737,12 +739,10 @@ export async function runGuards(
|
|
|
737
739
|
basename(s.originalBasePath || s.basePath),
|
|
738
740
|
);
|
|
739
741
|
|
|
740
|
-
//
|
|
741
|
-
|
|
742
|
-
markCaptureExecuted(s.basePath, cap.id);
|
|
743
|
-
}
|
|
742
|
+
// Pause first — ensures auto-mode stops even if later steps fail
|
|
743
|
+
await deps.pauseAuto(ctx, pi);
|
|
744
744
|
|
|
745
|
-
// For backtrack captures, write the backtrack trigger
|
|
745
|
+
// For backtrack captures, write the backtrack trigger after pausing
|
|
746
746
|
if (isBacktrack) {
|
|
747
747
|
try {
|
|
748
748
|
const { executeBacktrack } = await import("../triage-resolution.js");
|
|
@@ -752,12 +752,19 @@ export async function runGuards(
|
|
|
752
752
|
}
|
|
753
753
|
}
|
|
754
754
|
|
|
755
|
-
|
|
755
|
+
// Mark captures as executed only after successful pause/transition
|
|
756
|
+
for (const cap of stopCaptures) {
|
|
757
|
+
markCaptureExecuted(s.basePath, cap.id);
|
|
758
|
+
}
|
|
759
|
+
|
|
756
760
|
debugLog("autoLoop", { phase: "exit", reason: isBacktrack ? "user-backtrack" : "user-stop" });
|
|
757
761
|
return { action: "break", reason: isBacktrack ? "user-backtrack" : "user-stop" };
|
|
758
762
|
}
|
|
759
763
|
} catch (e) {
|
|
764
|
+
// Fail-closed: if anything in the stop guard throws, break the loop
|
|
765
|
+
// rather than silently continuing and dropping user halt intent
|
|
760
766
|
debugLog("guards", { phase: "stop-guard-error", error: String(e) });
|
|
767
|
+
return { action: "break", reason: "stop-guard-error" };
|
|
761
768
|
}
|
|
762
769
|
|
|
763
770
|
// Budget ceiling guard
|
|
@@ -1261,7 +1268,9 @@ export async function runUnitPhase(
|
|
|
1261
1268
|
blockers: [],
|
|
1262
1269
|
nextSteps: [],
|
|
1263
1270
|
});
|
|
1264
|
-
} catch { /* non-fatal — anchor is advisory */
|
|
1271
|
+
} catch (err) { /* non-fatal — anchor is advisory */
|
|
1272
|
+
logWarning("engine", `phase anchor failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1273
|
+
}
|
|
1265
1274
|
}
|
|
1266
1275
|
|
|
1267
1276
|
deps.emitJournalEvent({ ts: new Date().toISOString(), flowId: ic.flowId, seq: ic.nextSeq(), eventType: "unit-end", data: { unitType, unitId, status: unitResult.status, artifactVerified, ...(unitResult.errorContext ? { errorContext: unitResult.errorContext } : {}) }, causedBy: { flowId: ic.flowId, seq: unitStartSeq } });
|
|
@@ -1384,3 +1393,4 @@ export async function runFinalize(
|
|
|
1384
1393
|
|
|
1385
1394
|
return { action: "next", data: undefined as void };
|
|
1386
1395
|
}
|
|
1396
|
+
|
|
@@ -31,6 +31,7 @@ import {
|
|
|
31
31
|
getRtkSessionSavings,
|
|
32
32
|
type RtkSessionSavings,
|
|
33
33
|
} from "../shared/rtk-session-stats.js";
|
|
34
|
+
import { logWarning } from "./workflow-logger.js";
|
|
34
35
|
|
|
35
36
|
// ─── UAT Slice Extraction ─────────────────────────────────────────────────────
|
|
36
37
|
|
|
@@ -285,8 +286,9 @@ export function updateSliceProgressCache(base: string, mid: string, activeSid?:
|
|
|
285
286
|
taskDetails = dbTasks.map(t => ({ id: t.id, title: t.title, done: t.status === "complete" || t.status === "done" }));
|
|
286
287
|
}
|
|
287
288
|
}
|
|
288
|
-
} catch {
|
|
289
|
+
} catch (err) {
|
|
289
290
|
// Non-fatal — just omit task count
|
|
291
|
+
logWarning("dashboard", `operation failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
290
292
|
}
|
|
291
293
|
}
|
|
292
294
|
|
|
@@ -297,8 +299,9 @@ export function updateSliceProgressCache(base: string, mid: string, activeSid?:
|
|
|
297
299
|
activeSliceTasks,
|
|
298
300
|
taskDetails,
|
|
299
301
|
};
|
|
300
|
-
} catch {
|
|
302
|
+
} catch (err) {
|
|
301
303
|
// Non-fatal — widget just won't show progress bar
|
|
304
|
+
logWarning("dashboard", `operation failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
302
305
|
}
|
|
303
306
|
}
|
|
304
307
|
|
|
@@ -332,8 +335,9 @@ function refreshLastCommit(basePath: string): void {
|
|
|
332
335
|
};
|
|
333
336
|
}
|
|
334
337
|
lastCommitFetchedAt = Date.now();
|
|
335
|
-
} catch {
|
|
338
|
+
} catch (err) {
|
|
336
339
|
// Non-fatal — just skip last commit display
|
|
340
|
+
logWarning("dashboard", `operation failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
337
341
|
}
|
|
338
342
|
}
|
|
339
343
|
|
|
@@ -376,7 +380,9 @@ function ensureWidgetModeLoaded(): void {
|
|
|
376
380
|
if (saved && WIDGET_MODES.includes(saved as WidgetMode)) {
|
|
377
381
|
widgetMode = saved as WidgetMode;
|
|
378
382
|
}
|
|
379
|
-
} catch { /* non-fatal — use default */
|
|
383
|
+
} catch (err) { /* non-fatal — use default */
|
|
384
|
+
logWarning("dashboard", `operation failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
385
|
+
}
|
|
380
386
|
}
|
|
381
387
|
|
|
382
388
|
/** Persist widget mode to global preferences YAML. */
|
|
@@ -395,7 +401,9 @@ function persistWidgetMode(mode: WidgetMode): void {
|
|
|
395
401
|
content = content.trimEnd() + "\n" + line + "\n";
|
|
396
402
|
}
|
|
397
403
|
writeFileSync(prefsPath, content, "utf-8");
|
|
398
|
-
} catch { /* non-fatal — mode still set in memory */
|
|
404
|
+
} catch (err) { /* non-fatal — mode still set in memory */
|
|
405
|
+
logWarning("dashboard", `file write failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
406
|
+
}
|
|
399
407
|
}
|
|
400
408
|
|
|
401
409
|
/** Cycle to the next widget mode. Returns the new mode. */
|
|
@@ -458,7 +466,9 @@ export function updateProgressWidget(
|
|
|
458
466
|
|
|
459
467
|
// Cache git branch at widget creation time (not per render)
|
|
460
468
|
let cachedBranch: string | null = null;
|
|
461
|
-
try { cachedBranch = getCurrentBranch(accessors.getBasePath()); } catch { /* not in git repo */
|
|
469
|
+
try { cachedBranch = getCurrentBranch(accessors.getBasePath()); } catch (err) { /* not in git repo */
|
|
470
|
+
logWarning("dashboard", `git branch detection failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
471
|
+
}
|
|
462
472
|
|
|
463
473
|
// Cache short pwd (last 2 path segments only) + worktree/branch info
|
|
464
474
|
let widgetPwd: string;
|
|
@@ -495,7 +505,8 @@ export function updateProgressWidget(
|
|
|
495
505
|
const sessionId = ctx.sessionManager.getSessionId();
|
|
496
506
|
const savings = sessionId ? getRtkSessionSavings(accessors.getBasePath(), sessionId) : null;
|
|
497
507
|
cachedRtkLabel = formatRtkSavingsLabel(savings);
|
|
498
|
-
} catch {
|
|
508
|
+
} catch (err) {
|
|
509
|
+
logWarning("dashboard", `RTK savings lookup failed: ${err instanceof Error ? (err as Error).message : String(err)}`);
|
|
499
510
|
cachedRtkLabel = null;
|
|
500
511
|
}
|
|
501
512
|
};
|
|
@@ -519,7 +530,9 @@ export function updateProgressWidget(
|
|
|
519
530
|
}
|
|
520
531
|
refreshRtkLabel();
|
|
521
532
|
cachedLines = undefined;
|
|
522
|
-
} catch { /* non-fatal */
|
|
533
|
+
} catch (err) { /* non-fatal */
|
|
534
|
+
logWarning("dashboard", `DB status update failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
535
|
+
}
|
|
523
536
|
}, 15_000);
|
|
524
537
|
|
|
525
538
|
return {
|
|
@@ -878,3 +891,4 @@ function padToWidth(s: string, colWidth: number): string {
|
|
|
878
891
|
if (vis >= colWidth) return truncateToWidth(s, colWidth, "…");
|
|
879
892
|
return s + " ".repeat(colWidth - vis);
|
|
880
893
|
}
|
|
894
|
+
|
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
buildSliceFileName,
|
|
29
29
|
} from "./paths.js";
|
|
30
30
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
31
|
-
import { logError } from "./workflow-logger.js";
|
|
31
|
+
import { logWarning, logError } from "./workflow-logger.js";
|
|
32
32
|
import { join } from "node:path";
|
|
33
33
|
import { hasImplementationArtifacts } from "./auto-recovery.js";
|
|
34
34
|
import {
|
|
@@ -712,7 +712,9 @@ export const DISPATCH_RULES: DispatchRule[] = [
|
|
|
712
712
|
}
|
|
713
713
|
}
|
|
714
714
|
}
|
|
715
|
-
} catch { /* fall through — don't block on DB errors */
|
|
715
|
+
} catch (err) { /* fall through — don't block on DB errors */
|
|
716
|
+
logWarning("dispatch", `verification class check failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
717
|
+
}
|
|
716
718
|
|
|
717
719
|
return {
|
|
718
720
|
action: "dispatch",
|
|
@@ -754,8 +756,9 @@ export async function resolveDispatch(
|
|
|
754
756
|
try {
|
|
755
757
|
const registry = getRegistry();
|
|
756
758
|
return await registry.evaluateDispatch(ctx);
|
|
757
|
-
} catch {
|
|
759
|
+
} catch (err) {
|
|
758
760
|
// Registry not initialized — fall back to inline loop
|
|
761
|
+
logWarning("dispatch", `registry dispatch failed, falling back to inline rules: ${err instanceof Error ? err.message : String(err)}`);
|
|
759
762
|
}
|
|
760
763
|
|
|
761
764
|
for (const rule of DISPATCH_RULES) {
|
|
@@ -779,3 +782,4 @@ export async function resolveDispatch(
|
|
|
779
782
|
export function getDispatchRuleNames(): string[] {
|
|
780
783
|
return DISPATCH_RULES.map((r) => r.name);
|
|
781
784
|
}
|
|
785
|
+
|
|
@@ -9,8 +9,8 @@ import type { ExtensionAPI, ExtensionContext } from "@gsd/pi-coding-agent";
|
|
|
9
9
|
import type { GSDPreferences } from "./preferences.js";
|
|
10
10
|
import { resolveModelWithFallbacksForUnit, resolveDynamicRoutingConfig } from "./preferences.js";
|
|
11
11
|
import type { ComplexityTier } from "./complexity-classifier.js";
|
|
12
|
-
import { classifyUnitComplexity, tierLabel
|
|
13
|
-
import { resolveModelForComplexity, escalateTier } from "./model-router.js";
|
|
12
|
+
import { classifyUnitComplexity, tierLabel } from "./complexity-classifier.js";
|
|
13
|
+
import { resolveModelForComplexity, escalateTier, getEligibleModels, loadCapabilityOverrides } from "./model-router.js";
|
|
14
14
|
import { getLedger, getProjectTotals } from "./metrics.js";
|
|
15
15
|
import { unitPhaseLabel } from "./auto-dashboard.js";
|
|
16
16
|
|
|
@@ -107,27 +107,89 @@ export async function selectAndApplyModel(
|
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
-
//
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
: undefined;
|
|
114
|
-
|
|
115
|
-
const routingResult = resolveModelForComplexity(
|
|
116
|
-
classification, modelConfig, routingConfig, availableModelIds,
|
|
117
|
-
unitType, taskMeta,
|
|
110
|
+
// Load user capability overrides from preferences (D-17: deep-merged with built-in profiles)
|
|
111
|
+
const capabilityOverrides = loadCapabilityOverrides(
|
|
112
|
+
(prefs as { modelOverrides?: Record<string, { capabilities?: Record<string, number> }> } | undefined) ?? {},
|
|
118
113
|
);
|
|
119
114
|
|
|
115
|
+
// Fire before_model_select hook (ADR-004, D-03)
|
|
116
|
+
// Hook can override model selection entirely by returning { modelId }
|
|
117
|
+
let hookOverride: string | undefined;
|
|
118
|
+
if (routingConfig.hooks !== false) {
|
|
119
|
+
const eligible = getEligibleModels(
|
|
120
|
+
classification.tier,
|
|
121
|
+
availableModelIds,
|
|
122
|
+
routingConfig,
|
|
123
|
+
);
|
|
124
|
+
const hookResult = await pi.emitBeforeModelSelect({
|
|
125
|
+
unitType,
|
|
126
|
+
unitId,
|
|
127
|
+
classification: {
|
|
128
|
+
tier: classification.tier,
|
|
129
|
+
reason: classification.reason,
|
|
130
|
+
downgraded: classification.downgraded,
|
|
131
|
+
},
|
|
132
|
+
taskMetadata: classification.taskMetadata as Record<string, unknown> | undefined,
|
|
133
|
+
eligibleModels: eligible,
|
|
134
|
+
phaseConfig: modelConfig ? {
|
|
135
|
+
primary: modelConfig.primary,
|
|
136
|
+
fallbacks: modelConfig.fallbacks ?? [],
|
|
137
|
+
} : undefined,
|
|
138
|
+
});
|
|
139
|
+
if (hookResult?.modelId) {
|
|
140
|
+
hookOverride = hookResult.modelId;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
let routingResult: ReturnType<typeof resolveModelForComplexity>;
|
|
145
|
+
if (hookOverride) {
|
|
146
|
+
// Hook override bypasses capability scoring entirely
|
|
147
|
+
routingResult = {
|
|
148
|
+
modelId: hookOverride,
|
|
149
|
+
fallbacks: [
|
|
150
|
+
...(modelConfig?.fallbacks ?? []).filter(f => f !== hookOverride),
|
|
151
|
+
...(modelConfig?.primary && modelConfig.primary !== hookOverride ? [modelConfig.primary] : []),
|
|
152
|
+
],
|
|
153
|
+
tier: classification.tier,
|
|
154
|
+
wasDowngraded: hookOverride !== modelConfig?.primary,
|
|
155
|
+
reason: `hook override: ${hookOverride}`,
|
|
156
|
+
selectionMethod: "tier-only",
|
|
157
|
+
};
|
|
158
|
+
} else {
|
|
159
|
+
routingResult = resolveModelForComplexity(
|
|
160
|
+
classification,
|
|
161
|
+
modelConfig,
|
|
162
|
+
routingConfig,
|
|
163
|
+
availableModelIds,
|
|
164
|
+
unitType,
|
|
165
|
+
classification.taskMetadata,
|
|
166
|
+
capabilityOverrides,
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
|
|
120
170
|
if (routingResult.wasDowngraded) {
|
|
121
171
|
effectiveModelConfig = {
|
|
122
172
|
primary: routingResult.modelId,
|
|
123
173
|
fallbacks: routingResult.fallbacks,
|
|
124
174
|
};
|
|
125
175
|
if (verbose) {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
176
|
+
if (routingResult.selectionMethod === "capability-scored" && routingResult.capabilityScores) {
|
|
177
|
+
// Verbose scoring breakdown for capability-scored decisions (D-20)
|
|
178
|
+
const tierLbl = tierLabel(classification.tier);
|
|
179
|
+
const scores = Object.entries(routingResult.capabilityScores)
|
|
180
|
+
.sort(([, a], [, b]) => b - a)
|
|
181
|
+
.map(([id, score]) => `${id}: ${score.toFixed(1)}`)
|
|
182
|
+
.join(", ");
|
|
183
|
+
ctx.ui.notify(
|
|
184
|
+
`Dynamic routing [${tierLbl}]: ${routingResult.modelId} (capability-scored) — ${scores}`,
|
|
185
|
+
"info",
|
|
186
|
+
);
|
|
187
|
+
} else {
|
|
188
|
+
ctx.ui.notify(
|
|
189
|
+
`Dynamic routing [${tierLabel(classification.tier)}]: ${routingResult.modelId} (${classification.reason})`,
|
|
190
|
+
"info",
|
|
191
|
+
);
|
|
192
|
+
}
|
|
131
193
|
}
|
|
132
194
|
}
|
|
133
195
|
routingTierLabel = ` [${tierLabel(classification.tier)}]`;
|
|
@@ -279,8 +279,9 @@ export async function postUnitPreVerification(pctx: PostUnitContext, opts?: PreV
|
|
|
279
279
|
try {
|
|
280
280
|
const { getTaskIssueNumberForCommit } = await import("../github-sync/sync.js");
|
|
281
281
|
ghIssueNumber = getTaskIssueNumberForCommit(s.basePath, mid, sid, tid) ?? undefined;
|
|
282
|
-
} catch {
|
|
282
|
+
} catch (err) {
|
|
283
283
|
// GitHub sync not available — skip
|
|
284
|
+
logWarning("engine", `GitHub issue lookup failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
284
285
|
}
|
|
285
286
|
|
|
286
287
|
taskContext = {
|
|
@@ -558,9 +559,7 @@ export async function postUnitPostVerification(pctx: PostUnitContext): Promise<"
|
|
|
558
559
|
} catch (dbErr) {
|
|
559
560
|
// DB unavailable — fail explicitly rather than silently reverting to markdown mutation.
|
|
560
561
|
// Use 'gsd recover' to rebuild DB state from disk if needed.
|
|
561
|
-
|
|
562
|
-
`gsd: retry state-reset failed (DB unavailable): ${(dbErr as Error).message}. Run 'gsd recover' to reconcile.\n`,
|
|
563
|
-
);
|
|
562
|
+
logError("engine", `retry state-reset failed (DB unavailable): ${(dbErr as Error).message}. Run 'gsd recover' to reconcile.`);
|
|
564
563
|
}
|
|
565
564
|
}
|
|
566
565
|
|
|
@@ -732,3 +731,4 @@ export async function postUnitPostVerification(pctx: PostUnitContext): Promise<"
|
|
|
732
731
|
|
|
733
732
|
return "continue";
|
|
734
733
|
}
|
|
734
|
+
|