gsd-pi 2.44.0-dev.73f2fd5 → 2.44.0-dev.8894d5b
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/resources/extensions/gsd/auto/infra-errors.js +0 -3
- package/dist/resources/extensions/gsd/auto/phases.js +36 -36
- package/dist/resources/extensions/gsd/auto-prompts.js +1 -24
- package/dist/resources/extensions/gsd/auto-timers.js +3 -57
- package/dist/resources/extensions/gsd/auto-worktree-sync.js +0 -4
- package/dist/resources/extensions/gsd/auto-worktree.js +6 -9
- package/dist/resources/extensions/gsd/auto.js +3 -30
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +0 -136
- package/dist/resources/extensions/gsd/commands/catalog.js +1 -6
- package/dist/resources/extensions/gsd/commands/handlers/core.js +0 -1
- package/dist/resources/extensions/gsd/commands/handlers/ops.js +0 -5
- package/dist/resources/extensions/gsd/db-writer.js +16 -34
- package/dist/resources/extensions/gsd/doctor.js +0 -8
- package/dist/resources/extensions/gsd/git-service.js +3 -8
- package/dist/resources/extensions/gsd/gsd-db.js +1 -12
- package/dist/resources/extensions/gsd/markdown-renderer.js +1 -1
- package/dist/resources/extensions/gsd/prompts/complete-milestone.md +4 -2
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -6
- package/dist/resources/extensions/gsd/prompts/replan-slice.md +14 -3
- package/dist/resources/extensions/gsd/prompts/validate-milestone.md +37 -7
- package/dist/resources/extensions/gsd/provider-error-pause.js +0 -7
- package/dist/resources/extensions/gsd/tools/plan-slice.js +0 -1
- package/dist/resources/extensions/gsd/tools/plan-task.js +0 -1
- package/dist/resources/extensions/gsd/tools/replan-slice.js +0 -2
- package/dist/resources/extensions/gsd/worktree-resolver.js +0 -6
- package/dist/resources/extensions/mcp-client/index.js +0 -14
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +20 -20
- 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 +20 -20
- 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/auth-storage.d.ts +1 -3
- package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.js +1 -15
- package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts +0 -11
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.js +1 -20
- package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +0 -3
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.js +0 -6
- package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/main.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/main.js +0 -17
- package/packages/pi-coding-agent/dist/main.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts +1 -3
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js +1 -8
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +0 -2
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +0 -12
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +1 -4
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts +2 -5
- package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js +2 -13
- package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.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 +8 -17
- 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.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +3 -7
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/src/core/auth-storage.ts +1 -15
- package/packages/pi-coding-agent/src/core/model-registry.ts +1 -21
- package/packages/pi-coding-agent/src/core/settings-manager.ts +0 -9
- package/packages/pi-coding-agent/src/main.ts +0 -19
- package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +0 -10
- package/packages/pi-coding-agent/src/modes/interactive/components/settings-selector.ts +0 -15
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +1 -3
- package/packages/pi-coding-agent/src/modes/interactive/components/user-message.ts +3 -18
- package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +7 -16
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +1 -8
- package/src/resources/extensions/gsd/auto/infra-errors.ts +0 -3
- package/src/resources/extensions/gsd/auto/phases.ts +48 -45
- package/src/resources/extensions/gsd/auto-prompts.ts +1 -24
- package/src/resources/extensions/gsd/auto-timers.ts +3 -64
- package/src/resources/extensions/gsd/auto-worktree-sync.ts +0 -5
- package/src/resources/extensions/gsd/auto-worktree.ts +6 -9
- package/src/resources/extensions/gsd/auto.ts +3 -37
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +0 -129
- package/src/resources/extensions/gsd/commands/catalog.ts +1 -6
- package/src/resources/extensions/gsd/commands/handlers/core.ts +0 -1
- package/src/resources/extensions/gsd/commands/handlers/ops.ts +0 -5
- package/src/resources/extensions/gsd/db-writer.ts +17 -39
- package/src/resources/extensions/gsd/doctor.ts +1 -7
- package/src/resources/extensions/gsd/git-service.ts +2 -6
- package/src/resources/extensions/gsd/gsd-db.ts +1 -16
- package/src/resources/extensions/gsd/markdown-renderer.ts +1 -1
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +4 -2
- package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -6
- package/src/resources/extensions/gsd/prompts/replan-slice.md +14 -3
- package/src/resources/extensions/gsd/prompts/validate-milestone.md +37 -7
- package/src/resources/extensions/gsd/provider-error-pause.ts +0 -9
- package/src/resources/extensions/gsd/tests/db-writer.test.ts +0 -79
- package/src/resources/extensions/gsd/tests/infra-error.test.ts +2 -20
- package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +7 -11
- package/src/resources/extensions/gsd/tests/tool-naming.test.ts +1 -2
- package/src/resources/extensions/gsd/tools/plan-slice.ts +0 -2
- package/src/resources/extensions/gsd/tools/plan-task.ts +0 -2
- package/src/resources/extensions/gsd/tools/replan-slice.ts +0 -3
- package/src/resources/extensions/gsd/worktree-resolver.ts +0 -7
- package/src/resources/extensions/mcp-client/index.ts +0 -20
- package/dist/resources/extensions/gsd/commands-mcp-status.js +0 -187
- package/dist/resources/extensions/gsd/tools/validate-milestone.js +0 -88
- package/packages/pi-coding-agent/dist/core/local-model-check.d.ts +0 -15
- package/packages/pi-coding-agent/dist/core/local-model-check.d.ts.map +0 -1
- package/packages/pi-coding-agent/dist/core/local-model-check.js +0 -41
- package/packages/pi-coding-agent/dist/core/local-model-check.js.map +0 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.d.ts +0 -2
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.d.ts.map +0 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.js +0 -32
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.js.map +0 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.d.ts +0 -15
- package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.d.ts.map +0 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.js +0 -40
- package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.js.map +0 -1
- package/packages/pi-coding-agent/src/core/local-model-check.ts +0 -45
- package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/timestamp.test.ts +0 -38
- package/packages/pi-coding-agent/src/modes/interactive/components/timestamp.ts +0 -48
- package/src/resources/extensions/gsd/commands-mcp-status.ts +0 -247
- package/src/resources/extensions/gsd/tests/auto-pr-bugs.test.ts +0 -88
- package/src/resources/extensions/gsd/tests/completed-units-metrics-sync.test.ts +0 -114
- package/src/resources/extensions/gsd/tests/est-annotation-timeout.test.ts +0 -120
- package/src/resources/extensions/gsd/tests/mcp-status.test.ts +0 -103
- package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +0 -66
- package/src/resources/extensions/gsd/tests/stop-auto-merge-back.test.ts +0 -67
- package/src/resources/extensions/gsd/tests/terminated-transient.test.ts +0 -49
- package/src/resources/extensions/gsd/tools/validate-milestone.ts +0 -127
- /package/dist/web/standalone/.next/static/{kxxAA66bah_yhPYqLBHE2 → oZMtyM-zfu6Inx-S59cOl}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{kxxAA66bah_yhPYqLBHE2 → oZMtyM-zfu6Inx-S59cOl}/_ssgManifest.js +0 -0
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* local-model-check.ts — Utility to detect if a model baseUrl is local.
|
|
3
|
-
*
|
|
4
|
-
* Leaf module with zero transitive dependencies on TypeScript parameter properties.
|
|
5
|
-
* Used by ModelRegistry and tests.
|
|
6
|
-
*/
|
|
7
|
-
/**
|
|
8
|
-
* Check if a model's baseUrl points to a local endpoint.
|
|
9
|
-
* Returns true for localhost, 127.0.0.1, 0.0.0.0, ::1, or unix socket paths.
|
|
10
|
-
* Returns false if baseUrl is empty (cloud provider) or points to a remote host.
|
|
11
|
-
*/
|
|
12
|
-
export function isLocalModel(model) {
|
|
13
|
-
const url = model.baseUrl;
|
|
14
|
-
if (!url)
|
|
15
|
-
return false;
|
|
16
|
-
// Unix socket paths
|
|
17
|
-
if (url.startsWith("unix://") || url.startsWith("unix:"))
|
|
18
|
-
return true;
|
|
19
|
-
try {
|
|
20
|
-
const parsed = new URL(url);
|
|
21
|
-
const hostname = parsed.hostname;
|
|
22
|
-
if (hostname === "localhost" ||
|
|
23
|
-
hostname === "127.0.0.1" ||
|
|
24
|
-
hostname === "0.0.0.0" ||
|
|
25
|
-
hostname === "::1" ||
|
|
26
|
-
hostname === "[::1]") {
|
|
27
|
-
return true;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
catch {
|
|
31
|
-
// If URL parsing fails, check raw string for local patterns
|
|
32
|
-
if (url.includes("localhost") ||
|
|
33
|
-
url.includes("127.0.0.1") ||
|
|
34
|
-
url.includes("0.0.0.0") ||
|
|
35
|
-
url.includes("[::1]")) {
|
|
36
|
-
return true;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
return false;
|
|
40
|
-
}
|
|
41
|
-
//# sourceMappingURL=local-model-check.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"local-model-check.js","sourceRoot":"","sources":["../../src/core/local-model-check.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,KAA0B;IACtD,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC;IAC1B,IAAI,CAAC,GAAG;QAAE,OAAO,KAAK,CAAC;IAEvB,oBAAoB;IACpB,IAAI,GAAG,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC;QAAE,OAAO,IAAI,CAAC;IAEtE,IAAI,CAAC;QACJ,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;QAC5B,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;QACjC,IACC,QAAQ,KAAK,WAAW;YACxB,QAAQ,KAAK,WAAW;YACxB,QAAQ,KAAK,SAAS;YACtB,QAAQ,KAAK,KAAK;YAClB,QAAQ,KAAK,OAAO,EACnB,CAAC;YACF,OAAO,IAAI,CAAC;QACb,CAAC;IACF,CAAC;IAAC,MAAM,CAAC;QACR,4DAA4D;QAC5D,IACC,GAAG,CAAC,QAAQ,CAAC,WAAW,CAAC;YACzB,GAAG,CAAC,QAAQ,CAAC,WAAW,CAAC;YACzB,GAAG,CAAC,QAAQ,CAAC,SAAS,CAAC;YACvB,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,EACpB,CAAC;YACF,OAAO,IAAI,CAAC;QACb,CAAC;IACF,CAAC;IAED,OAAO,KAAK,CAAC;AACd,CAAC","sourcesContent":["/**\n * local-model-check.ts — Utility to detect if a model baseUrl is local.\n *\n * Leaf module with zero transitive dependencies on TypeScript parameter properties.\n * Used by ModelRegistry and tests.\n */\n\n/**\n * Check if a model's baseUrl points to a local endpoint.\n * Returns true for localhost, 127.0.0.1, 0.0.0.0, ::1, or unix socket paths.\n * Returns false if baseUrl is empty (cloud provider) or points to a remote host.\n */\nexport function isLocalModel(model: { baseUrl: string }): boolean {\n\tconst url = model.baseUrl;\n\tif (!url) return false;\n\n\t// Unix socket paths\n\tif (url.startsWith(\"unix://\") || url.startsWith(\"unix:\")) return true;\n\n\ttry {\n\t\tconst parsed = new URL(url);\n\t\tconst hostname = parsed.hostname;\n\t\tif (\n\t\t\thostname === \"localhost\" ||\n\t\t\thostname === \"127.0.0.1\" ||\n\t\t\thostname === \"0.0.0.0\" ||\n\t\t\thostname === \"::1\" ||\n\t\t\thostname === \"[::1]\"\n\t\t) {\n\t\t\treturn true;\n\t\t}\n\t} catch {\n\t\t// If URL parsing fails, check raw string for local patterns\n\t\tif (\n\t\t\turl.includes(\"localhost\") ||\n\t\t\turl.includes(\"127.0.0.1\") ||\n\t\t\turl.includes(\"0.0.0.0\") ||\n\t\t\turl.includes(\"[::1]\")\n\t\t) {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n"]}
|
package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"timestamp.test.d.ts","sourceRoot":"","sources":["../../../../../src/modes/interactive/components/__tests__/timestamp.test.ts"],"names":[],"mappings":""}
|
package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.js
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { test, describe } from "node:test";
|
|
2
|
-
import assert from "node:assert/strict";
|
|
3
|
-
import { formatTimestamp } from "../timestamp.js";
|
|
4
|
-
describe("formatTimestamp", () => {
|
|
5
|
-
// Use a fixed local timestamp to avoid timezone issues
|
|
6
|
-
const d = new Date(2026, 2, 24, 10, 34, 0); // Mar 24, 2026 10:34:00 local time
|
|
7
|
-
const ts = d.getTime();
|
|
8
|
-
test("date-time-iso format (default)", () => {
|
|
9
|
-
assert.equal(formatTimestamp(ts, "date-time-iso"), "2026-03-24 10:34");
|
|
10
|
-
assert.equal(formatTimestamp(ts), "2026-03-24 10:34"); // default
|
|
11
|
-
});
|
|
12
|
-
test("date-time-us format", () => {
|
|
13
|
-
assert.equal(formatTimestamp(ts, "date-time-us"), "03-24-2026 10:34 AM");
|
|
14
|
-
});
|
|
15
|
-
test("US format handles PM correctly", () => {
|
|
16
|
-
const pm = new Date(2026, 2, 24, 14, 5, 0).getTime();
|
|
17
|
-
assert.equal(formatTimestamp(pm, "date-time-us"), "03-24-2026 2:05 PM");
|
|
18
|
-
});
|
|
19
|
-
test("US format handles noon as 12 PM", () => {
|
|
20
|
-
const noon = new Date(2026, 2, 24, 12, 0, 0).getTime();
|
|
21
|
-
assert.equal(formatTimestamp(noon, "date-time-us"), "03-24-2026 12:00 PM");
|
|
22
|
-
});
|
|
23
|
-
test("US format handles midnight as 12 AM", () => {
|
|
24
|
-
const midnight = new Date(2026, 2, 24, 0, 0, 0).getTime();
|
|
25
|
-
assert.equal(formatTimestamp(midnight, "date-time-us"), "03-24-2026 12:00 AM");
|
|
26
|
-
});
|
|
27
|
-
test("ISO format pads single digit months and days", () => {
|
|
28
|
-
const jan1 = new Date(2026, 0, 1, 9, 5, 0).getTime();
|
|
29
|
-
assert.equal(formatTimestamp(jan1, "date-time-iso"), "2026-01-01 09:05");
|
|
30
|
-
});
|
|
31
|
-
});
|
|
32
|
-
//# sourceMappingURL=timestamp.test.js.map
|
package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"timestamp.test.js","sourceRoot":"","sources":["../../../../../src/modes/interactive/components/__tests__/timestamp.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAElD,QAAQ,CAAC,iBAAiB,EAAE,GAAG,EAAE;IAChC,uDAAuD;IACvD,MAAM,CAAC,GAAG,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,mCAAmC;IAC/E,MAAM,EAAE,GAAG,CAAC,CAAC,OAAO,EAAE,CAAC;IAEvB,IAAI,CAAC,gCAAgC,EAAE,GAAG,EAAE;QAC3C,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,EAAE,EAAE,eAAe,CAAC,EAAE,kBAAkB,CAAC,CAAC;QACvE,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,EAAE,CAAC,EAAE,kBAAkB,CAAC,CAAC,CAAC,UAAU;IAClE,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,qBAAqB,EAAE,GAAG,EAAE;QAChC,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,EAAE,EAAE,cAAc,CAAC,EAAE,qBAAqB,CAAC,CAAC;IAC1E,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,gCAAgC,EAAE,GAAG,EAAE;QAC3C,MAAM,EAAE,GAAG,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;QACrD,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,EAAE,EAAE,cAAc,CAAC,EAAE,oBAAoB,CAAC,CAAC;IACzE,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,iCAAiC,EAAE,GAAG,EAAE;QAC5C,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;QACvD,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,IAAI,EAAE,cAAc,CAAC,EAAE,qBAAqB,CAAC,CAAC;IAC5E,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,qCAAqC,EAAE,GAAG,EAAE;QAChD,MAAM,QAAQ,GAAG,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;QAC1D,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,QAAQ,EAAE,cAAc,CAAC,EAAE,qBAAqB,CAAC,CAAC;IAChF,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,8CAA8C,EAAE,GAAG,EAAE;QACzD,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;QACrD,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,IAAI,EAAE,eAAe,CAAC,EAAE,kBAAkB,CAAC,CAAC;IAC1E,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC","sourcesContent":["import { test, describe } from \"node:test\";\nimport assert from \"node:assert/strict\";\nimport { formatTimestamp } from \"../timestamp.js\";\n\ndescribe(\"formatTimestamp\", () => {\n\t// Use a fixed local timestamp to avoid timezone issues\n\tconst d = new Date(2026, 2, 24, 10, 34, 0); // Mar 24, 2026 10:34:00 local time\n\tconst ts = d.getTime();\n\n\ttest(\"date-time-iso format (default)\", () => {\n\t\tassert.equal(formatTimestamp(ts, \"date-time-iso\"), \"2026-03-24 10:34\");\n\t\tassert.equal(formatTimestamp(ts), \"2026-03-24 10:34\"); // default\n\t});\n\n\ttest(\"date-time-us format\", () => {\n\t\tassert.equal(formatTimestamp(ts, \"date-time-us\"), \"03-24-2026 10:34 AM\");\n\t});\n\n\ttest(\"US format handles PM correctly\", () => {\n\t\tconst pm = new Date(2026, 2, 24, 14, 5, 0).getTime();\n\t\tassert.equal(formatTimestamp(pm, \"date-time-us\"), \"03-24-2026 2:05 PM\");\n\t});\n\n\ttest(\"US format handles noon as 12 PM\", () => {\n\t\tconst noon = new Date(2026, 2, 24, 12, 0, 0).getTime();\n\t\tassert.equal(formatTimestamp(noon, \"date-time-us\"), \"03-24-2026 12:00 PM\");\n\t});\n\n\ttest(\"US format handles midnight as 12 AM\", () => {\n\t\tconst midnight = new Date(2026, 2, 24, 0, 0, 0).getTime();\n\t\tassert.equal(formatTimestamp(midnight, \"date-time-us\"), \"03-24-2026 12:00 AM\");\n\t});\n\n\ttest(\"ISO format pads single digit months and days\", () => {\n\t\tconst jan1 = new Date(2026, 0, 1, 9, 5, 0).getTime();\n\t\tassert.equal(formatTimestamp(jan1, \"date-time-iso\"), \"2026-01-01 09:05\");\n\t});\n});\n"]}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Timestamp formatting for message display.
|
|
3
|
-
*
|
|
4
|
-
* Formats:
|
|
5
|
-
* - "time-date-iso": 10:34 2025-03-24 (default)
|
|
6
|
-
* - "date-time-iso": 2025-03-24 10:34
|
|
7
|
-
* - "time-date-us": 10:34 AM 03/24/2025
|
|
8
|
-
* - "date-time-us": 03/24/2025 10:34 AM
|
|
9
|
-
*/
|
|
10
|
-
export type TimestampFormat = "date-time-iso" | "date-time-us";
|
|
11
|
-
/**
|
|
12
|
-
* Format a timestamp for message display using the specified format.
|
|
13
|
-
*/
|
|
14
|
-
export declare function formatTimestamp(timestamp: number, format?: TimestampFormat): string;
|
|
15
|
-
//# sourceMappingURL=timestamp.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"timestamp.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/timestamp.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,MAAM,eAAe,GAAG,eAAe,GAAG,cAAc,CAAC;AAyB/D;;GAEG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,GAAE,eAAiC,GAAG,MAAM,CASpG"}
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Timestamp formatting for message display.
|
|
3
|
-
*
|
|
4
|
-
* Formats:
|
|
5
|
-
* - "time-date-iso": 10:34 2025-03-24 (default)
|
|
6
|
-
* - "date-time-iso": 2025-03-24 10:34
|
|
7
|
-
* - "time-date-us": 10:34 AM 03/24/2025
|
|
8
|
-
* - "date-time-us": 03/24/2025 10:34 AM
|
|
9
|
-
*/
|
|
10
|
-
function pad2(n) {
|
|
11
|
-
return n.toString().padStart(2, "0");
|
|
12
|
-
}
|
|
13
|
-
function isoDate(d) {
|
|
14
|
-
return `${d.getFullYear()}-${pad2(d.getMonth() + 1)}-${pad2(d.getDate())}`;
|
|
15
|
-
}
|
|
16
|
-
function isoTime(d) {
|
|
17
|
-
return `${pad2(d.getHours())}:${pad2(d.getMinutes())}`;
|
|
18
|
-
}
|
|
19
|
-
function usDate(d) {
|
|
20
|
-
return `${pad2(d.getMonth() + 1)}-${pad2(d.getDate())}-${d.getFullYear()}`;
|
|
21
|
-
}
|
|
22
|
-
function usTime(d) {
|
|
23
|
-
const hours = d.getHours();
|
|
24
|
-
const period = hours >= 12 ? "PM" : "AM";
|
|
25
|
-
const h = hours % 12 || 12;
|
|
26
|
-
return `${h}:${pad2(d.getMinutes())} ${period}`;
|
|
27
|
-
}
|
|
28
|
-
/**
|
|
29
|
-
* Format a timestamp for message display using the specified format.
|
|
30
|
-
*/
|
|
31
|
-
export function formatTimestamp(timestamp, format = "date-time-iso") {
|
|
32
|
-
const d = new Date(timestamp);
|
|
33
|
-
switch (format) {
|
|
34
|
-
case "date-time-iso":
|
|
35
|
-
return `${isoDate(d)} ${isoTime(d)}`;
|
|
36
|
-
case "date-time-us":
|
|
37
|
-
return `${usDate(d)} ${usTime(d)}`;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
//# sourceMappingURL=timestamp.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"timestamp.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/timestamp.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,SAAS,IAAI,CAAC,CAAS;IACtB,OAAO,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;AACtC,CAAC;AAED,SAAS,OAAO,CAAC,CAAO;IACvB,OAAO,GAAG,CAAC,CAAC,WAAW,EAAE,IAAI,IAAI,CAAC,CAAC,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC;AAC5E,CAAC;AAED,SAAS,OAAO,CAAC,CAAO;IACvB,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,EAAE,CAAC;AACxD,CAAC;AAED,SAAS,MAAM,CAAC,CAAO;IACtB,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC;AAC5E,CAAC;AAED,SAAS,MAAM,CAAC,CAAO;IACtB,MAAM,KAAK,GAAG,CAAC,CAAC,QAAQ,EAAE,CAAC;IAC3B,MAAM,MAAM,GAAG,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;IACzC,MAAM,CAAC,GAAG,KAAK,GAAG,EAAE,IAAI,EAAE,CAAC;IAC3B,OAAO,GAAG,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,IAAI,MAAM,EAAE,CAAC;AACjD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,SAAiB,EAAE,SAA0B,eAAe;IAC3F,MAAM,CAAC,GAAG,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC;IAE9B,QAAQ,MAAM,EAAE,CAAC;QAChB,KAAK,eAAe;YACnB,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;QACtC,KAAK,cAAc;YAClB,OAAO,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;IACrC,CAAC;AACF,CAAC","sourcesContent":["/**\n * Timestamp formatting for message display.\n *\n * Formats:\n * - \"time-date-iso\": 10:34 2025-03-24 (default)\n * - \"date-time-iso\": 2025-03-24 10:34\n * - \"time-date-us\": 10:34 AM 03/24/2025\n * - \"date-time-us\": 03/24/2025 10:34 AM\n */\n\nexport type TimestampFormat = \"date-time-iso\" | \"date-time-us\";\n\nfunction pad2(n: number): string {\n\treturn n.toString().padStart(2, \"0\");\n}\n\nfunction isoDate(d: Date): string {\n\treturn `${d.getFullYear()}-${pad2(d.getMonth() + 1)}-${pad2(d.getDate())}`;\n}\n\nfunction isoTime(d: Date): string {\n\treturn `${pad2(d.getHours())}:${pad2(d.getMinutes())}`;\n}\n\nfunction usDate(d: Date): string {\n\treturn `${pad2(d.getMonth() + 1)}-${pad2(d.getDate())}-${d.getFullYear()}`;\n}\n\nfunction usTime(d: Date): string {\n\tconst hours = d.getHours();\n\tconst period = hours >= 12 ? \"PM\" : \"AM\";\n\tconst h = hours % 12 || 12;\n\treturn `${h}:${pad2(d.getMinutes())} ${period}`;\n}\n\n/**\n * Format a timestamp for message display using the specified format.\n */\nexport function formatTimestamp(timestamp: number, format: TimestampFormat = \"date-time-iso\"): string {\n\tconst d = new Date(timestamp);\n\n\tswitch (format) {\n\t\tcase \"date-time-iso\":\n\t\t\treturn `${isoDate(d)} ${isoTime(d)}`;\n\t\tcase \"date-time-us\":\n\t\t\treturn `${usDate(d)} ${usTime(d)}`;\n\t}\n}\n"]}
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* local-model-check.ts — Utility to detect if a model baseUrl is local.
|
|
3
|
-
*
|
|
4
|
-
* Leaf module with zero transitive dependencies on TypeScript parameter properties.
|
|
5
|
-
* Used by ModelRegistry and tests.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Check if a model's baseUrl points to a local endpoint.
|
|
10
|
-
* Returns true for localhost, 127.0.0.1, 0.0.0.0, ::1, or unix socket paths.
|
|
11
|
-
* Returns false if baseUrl is empty (cloud provider) or points to a remote host.
|
|
12
|
-
*/
|
|
13
|
-
export function isLocalModel(model: { baseUrl: string }): boolean {
|
|
14
|
-
const url = model.baseUrl;
|
|
15
|
-
if (!url) return false;
|
|
16
|
-
|
|
17
|
-
// Unix socket paths
|
|
18
|
-
if (url.startsWith("unix://") || url.startsWith("unix:")) return true;
|
|
19
|
-
|
|
20
|
-
try {
|
|
21
|
-
const parsed = new URL(url);
|
|
22
|
-
const hostname = parsed.hostname;
|
|
23
|
-
if (
|
|
24
|
-
hostname === "localhost" ||
|
|
25
|
-
hostname === "127.0.0.1" ||
|
|
26
|
-
hostname === "0.0.0.0" ||
|
|
27
|
-
hostname === "::1" ||
|
|
28
|
-
hostname === "[::1]"
|
|
29
|
-
) {
|
|
30
|
-
return true;
|
|
31
|
-
}
|
|
32
|
-
} catch {
|
|
33
|
-
// If URL parsing fails, check raw string for local patterns
|
|
34
|
-
if (
|
|
35
|
-
url.includes("localhost") ||
|
|
36
|
-
url.includes("127.0.0.1") ||
|
|
37
|
-
url.includes("0.0.0.0") ||
|
|
38
|
-
url.includes("[::1]")
|
|
39
|
-
) {
|
|
40
|
-
return true;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
return false;
|
|
45
|
-
}
|
package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/timestamp.test.ts
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { test, describe } from "node:test";
|
|
2
|
-
import assert from "node:assert/strict";
|
|
3
|
-
import { formatTimestamp } from "../timestamp.js";
|
|
4
|
-
|
|
5
|
-
describe("formatTimestamp", () => {
|
|
6
|
-
// Use a fixed local timestamp to avoid timezone issues
|
|
7
|
-
const d = new Date(2026, 2, 24, 10, 34, 0); // Mar 24, 2026 10:34:00 local time
|
|
8
|
-
const ts = d.getTime();
|
|
9
|
-
|
|
10
|
-
test("date-time-iso format (default)", () => {
|
|
11
|
-
assert.equal(formatTimestamp(ts, "date-time-iso"), "2026-03-24 10:34");
|
|
12
|
-
assert.equal(formatTimestamp(ts), "2026-03-24 10:34"); // default
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
test("date-time-us format", () => {
|
|
16
|
-
assert.equal(formatTimestamp(ts, "date-time-us"), "03-24-2026 10:34 AM");
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
test("US format handles PM correctly", () => {
|
|
20
|
-
const pm = new Date(2026, 2, 24, 14, 5, 0).getTime();
|
|
21
|
-
assert.equal(formatTimestamp(pm, "date-time-us"), "03-24-2026 2:05 PM");
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
test("US format handles noon as 12 PM", () => {
|
|
25
|
-
const noon = new Date(2026, 2, 24, 12, 0, 0).getTime();
|
|
26
|
-
assert.equal(formatTimestamp(noon, "date-time-us"), "03-24-2026 12:00 PM");
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
test("US format handles midnight as 12 AM", () => {
|
|
30
|
-
const midnight = new Date(2026, 2, 24, 0, 0, 0).getTime();
|
|
31
|
-
assert.equal(formatTimestamp(midnight, "date-time-us"), "03-24-2026 12:00 AM");
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
test("ISO format pads single digit months and days", () => {
|
|
35
|
-
const jan1 = new Date(2026, 0, 1, 9, 5, 0).getTime();
|
|
36
|
-
assert.equal(formatTimestamp(jan1, "date-time-iso"), "2026-01-01 09:05");
|
|
37
|
-
});
|
|
38
|
-
});
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Timestamp formatting for message display.
|
|
3
|
-
*
|
|
4
|
-
* Formats:
|
|
5
|
-
* - "time-date-iso": 10:34 2025-03-24 (default)
|
|
6
|
-
* - "date-time-iso": 2025-03-24 10:34
|
|
7
|
-
* - "time-date-us": 10:34 AM 03/24/2025
|
|
8
|
-
* - "date-time-us": 03/24/2025 10:34 AM
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
export type TimestampFormat = "date-time-iso" | "date-time-us";
|
|
12
|
-
|
|
13
|
-
function pad2(n: number): string {
|
|
14
|
-
return n.toString().padStart(2, "0");
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function isoDate(d: Date): string {
|
|
18
|
-
return `${d.getFullYear()}-${pad2(d.getMonth() + 1)}-${pad2(d.getDate())}`;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function isoTime(d: Date): string {
|
|
22
|
-
return `${pad2(d.getHours())}:${pad2(d.getMinutes())}`;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function usDate(d: Date): string {
|
|
26
|
-
return `${pad2(d.getMonth() + 1)}-${pad2(d.getDate())}-${d.getFullYear()}`;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function usTime(d: Date): string {
|
|
30
|
-
const hours = d.getHours();
|
|
31
|
-
const period = hours >= 12 ? "PM" : "AM";
|
|
32
|
-
const h = hours % 12 || 12;
|
|
33
|
-
return `${h}:${pad2(d.getMinutes())} ${period}`;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Format a timestamp for message display using the specified format.
|
|
38
|
-
*/
|
|
39
|
-
export function formatTimestamp(timestamp: number, format: TimestampFormat = "date-time-iso"): string {
|
|
40
|
-
const d = new Date(timestamp);
|
|
41
|
-
|
|
42
|
-
switch (format) {
|
|
43
|
-
case "date-time-iso":
|
|
44
|
-
return `${isoDate(d)} ${isoTime(d)}`;
|
|
45
|
-
case "date-time-us":
|
|
46
|
-
return `${usDate(d)} ${usTime(d)}`;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
@@ -1,247 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MCP Status — `/gsd mcp` command handler.
|
|
3
|
-
*
|
|
4
|
-
* Shows configured MCP servers, their connection status, and available tools.
|
|
5
|
-
*
|
|
6
|
-
* Subcommands:
|
|
7
|
-
* /gsd mcp — Overview of all servers (alias: /gsd mcp status)
|
|
8
|
-
* /gsd mcp status — Same as bare /gsd mcp
|
|
9
|
-
* /gsd mcp check <srv> — Detailed status for a specific server
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import type { ExtensionCommandContext } from "@gsd/pi-coding-agent";
|
|
13
|
-
|
|
14
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
15
|
-
import { join } from "node:path";
|
|
16
|
-
|
|
17
|
-
// ─── Types ──────────────────────────────────────────────────────────────────
|
|
18
|
-
|
|
19
|
-
export interface McpServerStatus {
|
|
20
|
-
name: string;
|
|
21
|
-
transport: "stdio" | "http" | "unknown";
|
|
22
|
-
connected: boolean;
|
|
23
|
-
toolCount: number;
|
|
24
|
-
error: string | undefined;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export interface McpServerDetail extends McpServerStatus {
|
|
28
|
-
tools: string[];
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// ─── Config reader (standalone — does not import mcp-client internals) ──────
|
|
32
|
-
|
|
33
|
-
interface McpServerRawConfig {
|
|
34
|
-
name: string;
|
|
35
|
-
transport: "stdio" | "http" | "unknown";
|
|
36
|
-
command?: string;
|
|
37
|
-
args?: string[];
|
|
38
|
-
url?: string;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function readMcpConfigs(): McpServerRawConfig[] {
|
|
42
|
-
const servers: McpServerRawConfig[] = [];
|
|
43
|
-
const seen = new Set<string>();
|
|
44
|
-
const configPaths = [
|
|
45
|
-
join(process.cwd(), ".mcp.json"),
|
|
46
|
-
join(process.cwd(), ".gsd", "mcp.json"),
|
|
47
|
-
];
|
|
48
|
-
|
|
49
|
-
for (const configPath of configPaths) {
|
|
50
|
-
try {
|
|
51
|
-
if (!existsSync(configPath)) continue;
|
|
52
|
-
const raw = readFileSync(configPath, "utf-8");
|
|
53
|
-
const data = JSON.parse(raw) as Record<string, unknown>;
|
|
54
|
-
const mcpServers = (data.mcpServers ?? data.servers) as
|
|
55
|
-
| Record<string, Record<string, unknown>>
|
|
56
|
-
| undefined;
|
|
57
|
-
if (!mcpServers || typeof mcpServers !== "object") continue;
|
|
58
|
-
|
|
59
|
-
for (const [name, config] of Object.entries(mcpServers)) {
|
|
60
|
-
if (seen.has(name)) continue;
|
|
61
|
-
seen.add(name);
|
|
62
|
-
|
|
63
|
-
const hasCommand = typeof config.command === "string";
|
|
64
|
-
const hasUrl = typeof config.url === "string";
|
|
65
|
-
const transport: McpServerRawConfig["transport"] = hasCommand
|
|
66
|
-
? "stdio"
|
|
67
|
-
: hasUrl
|
|
68
|
-
? "http"
|
|
69
|
-
: "unknown";
|
|
70
|
-
|
|
71
|
-
servers.push({
|
|
72
|
-
name,
|
|
73
|
-
transport,
|
|
74
|
-
...(hasCommand && {
|
|
75
|
-
command: config.command as string,
|
|
76
|
-
args: Array.isArray(config.args) ? (config.args as string[]) : undefined,
|
|
77
|
-
}),
|
|
78
|
-
...(hasUrl && { url: config.url as string }),
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
} catch {
|
|
82
|
-
// Non-fatal — config file may not exist or be malformed
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
return servers;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// ─── Formatters (exported for testing) ──────────────────────────────────────
|
|
90
|
-
|
|
91
|
-
export function formatMcpStatusReport(servers: McpServerStatus[]): string {
|
|
92
|
-
if (servers.length === 0) {
|
|
93
|
-
return [
|
|
94
|
-
"No MCP servers configured.",
|
|
95
|
-
"",
|
|
96
|
-
"Add servers to .mcp.json or .gsd/mcp.json to enable MCP integrations.",
|
|
97
|
-
"See: https://modelcontextprotocol.io/quickstart",
|
|
98
|
-
].join("\n");
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
const lines: string[] = [`MCP Server Status — ${servers.length} server(s)\n`];
|
|
102
|
-
|
|
103
|
-
for (const s of servers) {
|
|
104
|
-
const icon = s.error ? "✗" : s.connected ? "✓" : "○";
|
|
105
|
-
const status = s.error
|
|
106
|
-
? `error: ${s.error}`
|
|
107
|
-
: s.connected
|
|
108
|
-
? `connected — ${s.toolCount} tools`
|
|
109
|
-
: "disconnected";
|
|
110
|
-
lines.push(` ${icon} ${s.name} (${s.transport}) — ${status}`);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
lines.push("");
|
|
114
|
-
lines.push("Use /gsd mcp check <server> for details on a specific server.");
|
|
115
|
-
lines.push("Use mcp_discover to connect and list tools for a server.");
|
|
116
|
-
|
|
117
|
-
return lines.join("\n");
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
export function formatMcpServerDetail(server: McpServerDetail): string {
|
|
121
|
-
const lines: string[] = [`MCP Server: ${server.name}\n`];
|
|
122
|
-
|
|
123
|
-
lines.push(` Transport: ${server.transport}`);
|
|
124
|
-
|
|
125
|
-
if (server.error) {
|
|
126
|
-
lines.push(` Status: error`);
|
|
127
|
-
lines.push(` Error: ${server.error}`);
|
|
128
|
-
} else if (server.connected) {
|
|
129
|
-
lines.push(` Status: connected`);
|
|
130
|
-
lines.push(` Tools: ${server.toolCount}`);
|
|
131
|
-
if (server.tools.length > 0) {
|
|
132
|
-
lines.push("");
|
|
133
|
-
lines.push(" Available tools:");
|
|
134
|
-
for (const tool of server.tools) {
|
|
135
|
-
lines.push(` - ${tool}`);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
} else {
|
|
139
|
-
lines.push(` Status: disconnected`);
|
|
140
|
-
lines.push("");
|
|
141
|
-
lines.push(` Run mcp_discover("${server.name}") to connect and list tools.`);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
return lines.join("\n");
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// ─── Command handler ────────────────────────────────────────────────────────
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Handle `/gsd mcp [status|check <server>]`.
|
|
151
|
-
*/
|
|
152
|
-
export async function handleMcpStatus(
|
|
153
|
-
args: string,
|
|
154
|
-
ctx: ExtensionCommandContext,
|
|
155
|
-
): Promise<void> {
|
|
156
|
-
const trimmed = args.trim().toLowerCase();
|
|
157
|
-
const configs = readMcpConfigs();
|
|
158
|
-
|
|
159
|
-
// /gsd mcp check <server>
|
|
160
|
-
if (trimmed.startsWith("check ")) {
|
|
161
|
-
const serverName = args.trim().slice("check ".length).trim();
|
|
162
|
-
const config = configs.find((c) => c.name === serverName);
|
|
163
|
-
if (!config) {
|
|
164
|
-
const available = configs.map((c) => c.name).join(", ") || "(none)";
|
|
165
|
-
ctx.ui.notify(
|
|
166
|
-
`Unknown MCP server: "${serverName}"\n\nAvailable: ${available}`,
|
|
167
|
-
"warning",
|
|
168
|
-
);
|
|
169
|
-
return;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// Try to get connection/tool info from the mcp-client module if available
|
|
173
|
-
let connected = false;
|
|
174
|
-
let toolNames: string[] = [];
|
|
175
|
-
let error: string | undefined;
|
|
176
|
-
try {
|
|
177
|
-
const mcpClient = await import("../mcp-client/index.js");
|
|
178
|
-
// Access the module's connection state if exported; fall back gracefully
|
|
179
|
-
const mod = mcpClient as Record<string, unknown>;
|
|
180
|
-
if (typeof mod.getConnectionStatus === "function") {
|
|
181
|
-
const status = (mod.getConnectionStatus as (name: string) => { connected: boolean; tools: string[]; error?: string })(serverName);
|
|
182
|
-
connected = status.connected;
|
|
183
|
-
toolNames = status.tools;
|
|
184
|
-
error = status.error;
|
|
185
|
-
}
|
|
186
|
-
} catch {
|
|
187
|
-
// mcp-client may not expose status helpers — that's fine
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
ctx.ui.notify(
|
|
191
|
-
formatMcpServerDetail({
|
|
192
|
-
name: config.name,
|
|
193
|
-
transport: config.transport,
|
|
194
|
-
connected,
|
|
195
|
-
toolCount: toolNames.length,
|
|
196
|
-
tools: toolNames,
|
|
197
|
-
error,
|
|
198
|
-
}),
|
|
199
|
-
"info",
|
|
200
|
-
);
|
|
201
|
-
return;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
// /gsd mcp or /gsd mcp status
|
|
205
|
-
if (!trimmed || trimmed === "status") {
|
|
206
|
-
// Build status for each server
|
|
207
|
-
const statuses: McpServerStatus[] = [];
|
|
208
|
-
|
|
209
|
-
for (const config of configs) {
|
|
210
|
-
let connected = false;
|
|
211
|
-
let toolCount = 0;
|
|
212
|
-
let error: string | undefined;
|
|
213
|
-
|
|
214
|
-
try {
|
|
215
|
-
const mcpClient = await import("../mcp-client/index.js");
|
|
216
|
-
const mod = mcpClient as Record<string, unknown>;
|
|
217
|
-
if (typeof mod.getConnectionStatus === "function") {
|
|
218
|
-
const status = (mod.getConnectionStatus as (name: string) => { connected: boolean; tools: string[]; error?: string })(config.name);
|
|
219
|
-
connected = status.connected;
|
|
220
|
-
toolCount = status.tools.length;
|
|
221
|
-
error = status.error;
|
|
222
|
-
}
|
|
223
|
-
} catch {
|
|
224
|
-
// Fall back to unknown state
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
statuses.push({
|
|
228
|
-
name: config.name,
|
|
229
|
-
transport: config.transport,
|
|
230
|
-
connected,
|
|
231
|
-
toolCount,
|
|
232
|
-
error,
|
|
233
|
-
});
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
ctx.ui.notify(formatMcpStatusReport(statuses), "info");
|
|
237
|
-
return;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
// Unknown subcommand
|
|
241
|
-
ctx.ui.notify(
|
|
242
|
-
"Usage: /gsd mcp [status|check <server>]\n\n" +
|
|
243
|
-
" status Show all MCP server statuses (default)\n" +
|
|
244
|
-
" check <server> Detailed status for a specific server",
|
|
245
|
-
"warning",
|
|
246
|
-
);
|
|
247
|
-
}
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* auto-pr-bugs.test.ts — Regression tests for #2302.
|
|
3
|
-
*
|
|
4
|
-
* Three interacting bugs prevented auto_pr from ever creating a PR:
|
|
5
|
-
* 1. auto_pr was gated on `pushed` (which requires auto_push)
|
|
6
|
-
* 2. Milestone branch was not pushed to remote before PR creation
|
|
7
|
-
* 3. createDraftPR in git-service.ts lacked --head/--base parameters
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import test from "node:test";
|
|
11
|
-
import assert from "node:assert/strict";
|
|
12
|
-
import { readFileSync } from "node:fs";
|
|
13
|
-
import { join } from "node:path";
|
|
14
|
-
|
|
15
|
-
// ─── Bug 1: auto_pr should not depend on auto_push / pushed flag ────────────
|
|
16
|
-
|
|
17
|
-
const autoWorktreeSrcPath = join(import.meta.dirname, "..", "auto-worktree.ts");
|
|
18
|
-
const autoWorktreeSrc = readFileSync(autoWorktreeSrcPath, "utf-8");
|
|
19
|
-
|
|
20
|
-
test("#2302 bug 1: auto_pr condition should not require pushed flag", () => {
|
|
21
|
-
// Find the auto_pr block in mergeMilestoneToMain
|
|
22
|
-
const autoPrIdx = autoWorktreeSrc.indexOf("auto_pr");
|
|
23
|
-
assert.ok(autoPrIdx !== -1, "auto_pr reference exists in auto-worktree.ts");
|
|
24
|
-
|
|
25
|
-
// Get context around the auto_pr check
|
|
26
|
-
const lineStart = autoWorktreeSrc.lastIndexOf("\n", autoPrIdx) + 1;
|
|
27
|
-
const lineEnd = autoWorktreeSrc.indexOf("\n", autoPrIdx);
|
|
28
|
-
const autoPrLine = autoWorktreeSrc.slice(lineStart, lineEnd);
|
|
29
|
-
|
|
30
|
-
// The condition should NOT include `&& pushed`
|
|
31
|
-
assert.ok(
|
|
32
|
-
!autoPrLine.includes("&& pushed"),
|
|
33
|
-
"auto_pr condition should not be gated on pushed flag (auto_push dependency)",
|
|
34
|
-
);
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
// ─── Bug 2: phases.ts should not duplicate PR creation ──────────────────────
|
|
38
|
-
|
|
39
|
-
const phasesSrcPath = join(import.meta.dirname, "..", "auto", "phases.ts");
|
|
40
|
-
const phasesSrc = readFileSync(phasesSrcPath, "utf-8");
|
|
41
|
-
|
|
42
|
-
test("#2302 bug 2: phases.ts should not call createDraftPR (handled by mergeMilestoneToMain)", () => {
|
|
43
|
-
// After fix, phases.ts should not import or call createDraftPR because
|
|
44
|
-
// PR creation is handled inside mergeMilestoneToMain in auto-worktree.ts
|
|
45
|
-
const createDraftPRCalls = phasesSrc.match(/createDraftPR\(/g) || [];
|
|
46
|
-
|
|
47
|
-
assert.equal(
|
|
48
|
-
createDraftPRCalls.length,
|
|
49
|
-
0,
|
|
50
|
-
"phases.ts should not call createDraftPR — it's handled by mergeMilestoneToMain",
|
|
51
|
-
);
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
// ─── Bug 3: createDraftPR should accept head and base branch parameters ─────
|
|
55
|
-
|
|
56
|
-
const gitServiceSrcPath = join(import.meta.dirname, "..", "git-service.ts");
|
|
57
|
-
const gitServiceSrc = readFileSync(gitServiceSrcPath, "utf-8");
|
|
58
|
-
|
|
59
|
-
test("#2302 bug 3: createDraftPR should accept head and base branch parameters", () => {
|
|
60
|
-
// Find the createDraftPR function signature
|
|
61
|
-
const fnIdx = gitServiceSrc.indexOf("function createDraftPR");
|
|
62
|
-
assert.ok(fnIdx !== -1, "createDraftPR function exists");
|
|
63
|
-
|
|
64
|
-
// Get the function signature (up to the closing paren)
|
|
65
|
-
const sigEnd = gitServiceSrc.indexOf(")", fnIdx);
|
|
66
|
-
const signature = gitServiceSrc.slice(fnIdx, sigEnd);
|
|
67
|
-
|
|
68
|
-
// Should have head and base parameters
|
|
69
|
-
assert.ok(
|
|
70
|
-
signature.includes("head") || signature.includes("branch"),
|
|
71
|
-
"createDraftPR should accept a head/branch parameter",
|
|
72
|
-
);
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
test("#2302 bug 3: createDraftPR should pass --head and --base to gh pr create", () => {
|
|
76
|
-
const fnIdx = gitServiceSrc.indexOf("function createDraftPR");
|
|
77
|
-
const fnEnd = gitServiceSrc.indexOf("\n}", fnIdx);
|
|
78
|
-
const fnBody = gitServiceSrc.slice(fnIdx, fnEnd);
|
|
79
|
-
|
|
80
|
-
assert.ok(
|
|
81
|
-
fnBody.includes("--head"),
|
|
82
|
-
"createDraftPR should pass --head to gh pr create",
|
|
83
|
-
);
|
|
84
|
-
assert.ok(
|
|
85
|
-
fnBody.includes("--base"),
|
|
86
|
-
"createDraftPR should pass --base to gh pr create",
|
|
87
|
-
);
|
|
88
|
-
});
|