gsd-pi 2.42.0-dev.97e9e30 → 2.42.0-dev.eedc83f
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 +23 -0
- package/dist/cli.js +15 -1
- package/dist/resource-loader.js +39 -6
- package/dist/resources/extensions/async-jobs/async-bash-tool.js +52 -4
- package/dist/resources/extensions/gsd/auto-prompts.js +1 -1
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +11 -5
- package/dist/resources/extensions/gsd/detection.js +19 -0
- package/dist/resources/extensions/gsd/doctor-checks.js +31 -1
- package/dist/resources/extensions/gsd/doctor-providers.js +10 -0
- package/dist/resources/extensions/gsd/forensics.js +84 -0
- package/dist/resources/extensions/gsd/git-constants.js +1 -0
- package/dist/resources/extensions/gsd/git-service.js +68 -2
- package/dist/resources/extensions/gsd/native-git-bridge.js +1 -0
- package/dist/resources/extensions/gsd/preferences-types.js +1 -0
- package/dist/resources/extensions/gsd/preferences.js +59 -8
- package/dist/resources/extensions/gsd/prompts/forensics.md +12 -5
- package/dist/resources/extensions/gsd/repo-identity.js +46 -5
- package/dist/resources/extensions/gsd/service-tier.js +13 -4
- package/dist/resources/extensions/gsd/session-lock.js +2 -2
- package/dist/resources/extensions/gsd/worktree-resolver.js +2 -2
- package/dist/resources/extensions/mcp-client/index.js +2 -1
- package/dist/resources/extensions/search-the-web/tool-search.js +3 -3
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +12 -12
- 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/api/git/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 +12 -12
- package/dist/web/standalone/.next/server/chunks/229.js +2 -2
- 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/dist/web-mode.d.ts +2 -0
- package/dist/web-mode.js +40 -4
- package/package.json +1 -1
- package/packages/pi-agent-core/dist/agent.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/agent.js +2 -0
- package/packages/pi-agent-core/dist/agent.js.map +1 -1
- package/packages/pi-agent-core/dist/types.d.ts +6 -0
- package/packages/pi-agent-core/dist/types.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/types.js.map +1 -1
- package/packages/pi-agent-core/src/agent.test.ts +53 -0
- package/packages/pi-agent-core/src/agent.ts +3 -0
- package/packages/pi-agent-core/src/types.ts +6 -0
- package/packages/pi-agent-core/tsconfig.json +1 -1
- package/packages/pi-ai/dist/models.d.ts +5 -3
- package/packages/pi-ai/dist/models.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.generated.d.ts +801 -1468
- package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.generated.js +1135 -1588
- package/packages/pi-ai/dist/models.generated.js.map +1 -1
- package/packages/pi-ai/dist/models.js.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/github-copilot.d.ts.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/github-copilot.js +60 -2
- package/packages/pi-ai/dist/utils/oauth/github-copilot.js.map +1 -1
- package/packages/pi-ai/scripts/generate-models.ts +1543 -0
- package/packages/pi-ai/src/models.generated.ts +1140 -1593
- package/packages/pi-ai/src/models.ts +7 -4
- package/packages/pi-ai/src/utils/oauth/github-copilot.ts +74 -2
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js +8 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +7 -0
- package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.js +29 -2
- package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.test.js +60 -0
- package/packages/pi-coding-agent/dist/core/auth-storage.test.js.map +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 +18 -0
- package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/client.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/client.js +23 -0
- package/packages/pi-coding-agent/dist/core/lsp/client.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.js +2 -0
- package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/package-manager.d.ts +6 -0
- package/packages/pi-coding-agent/dist/core/package-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/package-manager.js +63 -11
- package/packages/pi-coding-agent/dist/core/package-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/resource-loader.d.ts +9 -0
- package/packages/pi-coding-agent/dist/core/resource-loader.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/resource-loader.js +20 -6
- package/packages/pi-coding-agent/dist/core/resource-loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/system-prompt.js +6 -5
- package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/extension-editor.js +3 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/extension-editor.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js +9 -6
- package/packages/pi-coding-agent/dist/modes/interactive/components/footer.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 +30 -10
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/src/core/agent-session.ts +7 -1
- package/packages/pi-coding-agent/src/core/auth-storage.test.ts +68 -0
- package/packages/pi-coding-agent/src/core/auth-storage.ts +30 -2
- package/packages/pi-coding-agent/src/core/extensions/loader.ts +18 -0
- package/packages/pi-coding-agent/src/core/lsp/client.ts +29 -0
- package/packages/pi-coding-agent/src/core/model-registry.ts +3 -0
- package/packages/pi-coding-agent/src/core/package-manager.ts +99 -58
- package/packages/pi-coding-agent/src/core/resource-loader.ts +24 -6
- package/packages/pi-coding-agent/src/core/system-prompt.ts +6 -5
- package/packages/pi-coding-agent/src/modes/interactive/components/extension-editor.ts +3 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/footer.ts +10 -6
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +31 -11
- package/src/resources/extensions/async-jobs/async-bash-timeout.test.ts +122 -0
- package/src/resources/extensions/async-jobs/async-bash-tool.ts +40 -4
- package/src/resources/extensions/gsd/auto-prompts.ts +1 -1
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +13 -5
- package/src/resources/extensions/gsd/detection.ts +19 -0
- package/src/resources/extensions/gsd/doctor-checks.ts +32 -1
- package/src/resources/extensions/gsd/doctor-providers.ts +13 -0
- package/src/resources/extensions/gsd/doctor-types.ts +1 -0
- package/src/resources/extensions/gsd/forensics.ts +92 -0
- package/src/resources/extensions/gsd/git-constants.ts +1 -0
- package/src/resources/extensions/gsd/git-service.ts +71 -2
- package/src/resources/extensions/gsd/native-git-bridge.ts +1 -0
- package/src/resources/extensions/gsd/preferences-types.ts +3 -0
- package/src/resources/extensions/gsd/preferences.ts +62 -6
- package/src/resources/extensions/gsd/prompts/forensics.md +12 -5
- package/src/resources/extensions/gsd/repo-identity.ts +48 -5
- package/src/resources/extensions/gsd/service-tier.ts +17 -4
- package/src/resources/extensions/gsd/session-lock.ts +2 -2
- package/src/resources/extensions/gsd/tests/activity-log.test.ts +31 -69
- package/src/resources/extensions/gsd/tests/forensics-dedup.test.ts +48 -0
- package/src/resources/extensions/gsd/tests/forensics-issue-routing.test.ts +43 -0
- package/src/resources/extensions/gsd/tests/git-locale.test.ts +133 -0
- package/src/resources/extensions/gsd/tests/git-service.test.ts +49 -0
- package/src/resources/extensions/gsd/tests/journal.test.ts +82 -127
- package/src/resources/extensions/gsd/tests/manifest-status.test.ts +73 -82
- package/src/resources/extensions/gsd/tests/service-tier.test.ts +30 -1
- package/src/resources/extensions/gsd/tests/symlink-numbered-variants.test.ts +151 -0
- package/src/resources/extensions/gsd/tests/verification-gate.test.ts +156 -263
- package/src/resources/extensions/gsd/tests/worktree-health-dispatch.test.ts +35 -78
- package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +81 -74
- package/src/resources/extensions/gsd/worktree-resolver.ts +2 -2
- package/src/resources/extensions/mcp-client/index.ts +5 -1
- package/src/resources/extensions/search-the-web/tool-search.ts +3 -3
- /package/dist/web/standalone/.next/static/{PXrI5DoWsm7rwAVnEU2rD → JUBX5FUR73jiViQU5a-Cx}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{PXrI5DoWsm7rwAVnEU2rD → JUBX5FUR73jiViQU5a-Cx}/_ssgManifest.js +0 -0
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* rather than hard-coding package.json / src/ only.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import test from "node:test";
|
|
10
|
+
import { describe, test, beforeEach, afterEach } from "node:test";
|
|
11
11
|
import assert from "node:assert/strict";
|
|
12
12
|
import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from "node:fs";
|
|
13
13
|
import { join } from "node:path";
|
|
@@ -67,112 +67,69 @@ test("PROJECT_FILES is exported and contains expected multi-ecosystem entries",
|
|
|
67
67
|
assert.ok(PROJECT_FILES.includes("Package.swift"), "includes Swift marker");
|
|
68
68
|
});
|
|
69
69
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
70
|
+
describe("health check with git repo", () => {
|
|
71
|
+
let dir: string;
|
|
72
|
+
beforeEach(() => { dir = createGitRepo(); });
|
|
73
|
+
afterEach(() => { rmSync(dir, { recursive: true, force: true }); });
|
|
74
|
+
|
|
75
|
+
test("health check passes for Rust project (Cargo.toml, no package.json)", () => {
|
|
73
76
|
writeFileSync(join(dir, "Cargo.toml"), "[package]\nname = \"test\"\n");
|
|
74
77
|
mkdirSync(join(dir, "crates"), { recursive: true });
|
|
75
78
|
assert.ok(wouldPassHealthCheck(dir, existsSync), "Rust project should pass health check");
|
|
76
|
-
}
|
|
77
|
-
rmSync(dir, { recursive: true, force: true });
|
|
78
|
-
}
|
|
79
|
-
});
|
|
79
|
+
});
|
|
80
80
|
|
|
81
|
-
test("health check passes for Go project (go.mod, no package.json)", () => {
|
|
82
|
-
const dir = createGitRepo();
|
|
83
|
-
try {
|
|
81
|
+
test("health check passes for Go project (go.mod, no package.json)", () => {
|
|
84
82
|
writeFileSync(join(dir, "go.mod"), "module example.com/test\n\ngo 1.21\n");
|
|
85
83
|
assert.ok(wouldPassHealthCheck(dir, existsSync), "Go project should pass health check");
|
|
86
|
-
}
|
|
87
|
-
rmSync(dir, { recursive: true, force: true });
|
|
88
|
-
}
|
|
89
|
-
});
|
|
84
|
+
});
|
|
90
85
|
|
|
91
|
-
test("health check passes for Python project (pyproject.toml, no package.json)", () => {
|
|
92
|
-
const dir = createGitRepo();
|
|
93
|
-
try {
|
|
86
|
+
test("health check passes for Python project (pyproject.toml, no package.json)", () => {
|
|
94
87
|
writeFileSync(join(dir, "pyproject.toml"), "[project]\nname = \"test\"\n");
|
|
95
88
|
assert.ok(wouldPassHealthCheck(dir, existsSync), "Python project should pass health check");
|
|
96
|
-
}
|
|
97
|
-
rmSync(dir, { recursive: true, force: true });
|
|
98
|
-
}
|
|
99
|
-
});
|
|
89
|
+
});
|
|
100
90
|
|
|
101
|
-
test("health check passes for Java project (pom.xml, no package.json)", () => {
|
|
102
|
-
const dir = createGitRepo();
|
|
103
|
-
try {
|
|
91
|
+
test("health check passes for Java project (pom.xml, no package.json)", () => {
|
|
104
92
|
writeFileSync(join(dir, "pom.xml"), "<project></project>\n");
|
|
105
93
|
assert.ok(wouldPassHealthCheck(dir, existsSync), "Java project should pass health check");
|
|
106
|
-
}
|
|
107
|
-
rmSync(dir, { recursive: true, force: true });
|
|
108
|
-
}
|
|
109
|
-
});
|
|
94
|
+
});
|
|
110
95
|
|
|
111
|
-
test("health check passes for Swift project (Package.swift, no package.json)", () => {
|
|
112
|
-
const dir = createGitRepo();
|
|
113
|
-
try {
|
|
96
|
+
test("health check passes for Swift project (Package.swift, no package.json)", () => {
|
|
114
97
|
writeFileSync(join(dir, "Package.swift"), "// swift-tools-version:5.7\n");
|
|
115
98
|
assert.ok(wouldPassHealthCheck(dir, existsSync), "Swift project should pass health check");
|
|
116
|
-
}
|
|
117
|
-
rmSync(dir, { recursive: true, force: true });
|
|
118
|
-
}
|
|
119
|
-
});
|
|
99
|
+
});
|
|
120
100
|
|
|
121
|
-
test("health check passes for C/C++ project (CMakeLists.txt, no package.json)", () => {
|
|
122
|
-
const dir = createGitRepo();
|
|
123
|
-
try {
|
|
101
|
+
test("health check passes for C/C++ project (CMakeLists.txt, no package.json)", () => {
|
|
124
102
|
writeFileSync(join(dir, "CMakeLists.txt"), "cmake_minimum_required(VERSION 3.20)\n");
|
|
125
103
|
assert.ok(wouldPassHealthCheck(dir, existsSync), "C/C++ project should pass health check");
|
|
126
|
-
}
|
|
127
|
-
rmSync(dir, { recursive: true, force: true });
|
|
128
|
-
}
|
|
129
|
-
});
|
|
104
|
+
});
|
|
130
105
|
|
|
131
|
-
test("health check passes for Elixir project (mix.exs, no package.json)", () => {
|
|
132
|
-
const dir = createGitRepo();
|
|
133
|
-
try {
|
|
106
|
+
test("health check passes for Elixir project (mix.exs, no package.json)", () => {
|
|
134
107
|
writeFileSync(join(dir, "mix.exs"), "defmodule Test.MixProject do\nend\n");
|
|
135
108
|
assert.ok(wouldPassHealthCheck(dir, existsSync), "Elixir project should pass health check");
|
|
136
|
-
}
|
|
137
|
-
rmSync(dir, { recursive: true, force: true });
|
|
138
|
-
}
|
|
139
|
-
});
|
|
109
|
+
});
|
|
140
110
|
|
|
141
|
-
test("health check passes for JS project (package.json, backward compat)", () => {
|
|
142
|
-
const dir = createGitRepo();
|
|
143
|
-
try {
|
|
111
|
+
test("health check passes for JS project (package.json, backward compat)", () => {
|
|
144
112
|
writeFileSync(join(dir, "package.json"), '{"name":"test"}\n');
|
|
145
113
|
assert.ok(wouldPassHealthCheck(dir, existsSync), "JS project should pass health check");
|
|
146
|
-
}
|
|
147
|
-
rmSync(dir, { recursive: true, force: true });
|
|
148
|
-
}
|
|
149
|
-
});
|
|
114
|
+
});
|
|
150
115
|
|
|
151
|
-
test("health check passes for src/-only project (backward compat)", () => {
|
|
152
|
-
const dir = createGitRepo();
|
|
153
|
-
try {
|
|
116
|
+
test("health check passes for src/-only project (backward compat)", () => {
|
|
154
117
|
mkdirSync(join(dir, "src"), { recursive: true });
|
|
155
118
|
assert.ok(wouldPassHealthCheck(dir, existsSync), "src/-only project should pass health check");
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test("health check fails for empty git repo with no project files", () => {
|
|
122
|
+
assert.ok(!wouldPassHealthCheck(dir, existsSync), "empty git repo should fail health check");
|
|
123
|
+
});
|
|
159
124
|
});
|
|
160
125
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
126
|
+
describe("health check without git repo", () => {
|
|
127
|
+
let dir: string;
|
|
128
|
+
beforeEach(() => { dir = mkdtempSync(join(tmpdir(), "wt-dispatch-test-nogit-")); });
|
|
129
|
+
afterEach(() => { rmSync(dir, { recursive: true, force: true }); });
|
|
130
|
+
|
|
131
|
+
test("health check fails for directory with no .git", () => {
|
|
164
132
|
writeFileSync(join(dir, "Cargo.toml"), "[package]\nname = \"test\"\n");
|
|
165
133
|
assert.ok(!wouldPassHealthCheck(dir, existsSync), "no-git directory should fail health check");
|
|
166
|
-
}
|
|
167
|
-
rmSync(dir, { recursive: true, force: true });
|
|
168
|
-
}
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
test("health check fails for empty git repo with no project files", () => {
|
|
172
|
-
const dir = createGitRepo();
|
|
173
|
-
try {
|
|
174
|
-
assert.ok(!wouldPassHealthCheck(dir, existsSync), "empty git repo should fail health check");
|
|
175
|
-
} finally {
|
|
176
|
-
rmSync(dir, { recursive: true, force: true });
|
|
177
|
-
}
|
|
134
|
+
});
|
|
178
135
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import test from "node:test";
|
|
1
|
+
import { describe, test, beforeEach, afterEach } from "node:test";
|
|
2
2
|
import assert from "node:assert/strict";
|
|
3
3
|
import { mkdtempSync, mkdirSync, rmSync, writeFileSync, existsSync } from "node:fs";
|
|
4
4
|
import { join } from "node:path";
|
|
@@ -73,9 +73,12 @@ test("worktreeBranchName formats branch name", () => {
|
|
|
73
73
|
|
|
74
74
|
// ─── createWorktree ───────────────────────────────────────────────────────────
|
|
75
75
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
76
|
+
describe("createWorktree", () => {
|
|
77
|
+
let base: string;
|
|
78
|
+
beforeEach(() => { base = makeBaseRepo(); });
|
|
79
|
+
afterEach(() => { rmSync(base, { recursive: true, force: true }); });
|
|
80
|
+
|
|
81
|
+
test("creates worktree with correct metadata", () => {
|
|
79
82
|
const info = createWorktree(base, "feature-x");
|
|
80
83
|
assert.strictEqual(info.name, "feature-x", "name should match");
|
|
81
84
|
assert.strictEqual(info.branch, "worktree/feature-x", "branch should be prefixed");
|
|
@@ -88,80 +91,82 @@ test("createWorktree creates worktree with correct metadata", () => {
|
|
|
88
91
|
);
|
|
89
92
|
const branches = run("git branch", base);
|
|
90
93
|
assert.ok(branches.includes("worktree/feature-x"), "branch should be created in base repo");
|
|
91
|
-
}
|
|
92
|
-
rmSync(base, { recursive: true, force: true });
|
|
93
|
-
}
|
|
94
|
-
});
|
|
94
|
+
});
|
|
95
95
|
|
|
96
|
-
test("
|
|
97
|
-
const { base } = makeRepoWithWorktree("feature-x");
|
|
98
|
-
try {
|
|
96
|
+
test("rejects invalid name", () => {
|
|
99
97
|
assert.throws(
|
|
100
|
-
() => createWorktree(base, "
|
|
98
|
+
() => createWorktree(base, "bad name!"),
|
|
101
99
|
(err: Error) => {
|
|
102
100
|
assert.ok(
|
|
103
|
-
err.message.includes("
|
|
104
|
-
`expected "
|
|
101
|
+
err.message.includes("Invalid worktree name"),
|
|
102
|
+
`expected "Invalid worktree name" in error, got: ${err.message}`,
|
|
105
103
|
);
|
|
106
104
|
return true;
|
|
107
105
|
},
|
|
108
|
-
"should throw on
|
|
106
|
+
"should throw on invalid worktree name",
|
|
109
107
|
);
|
|
110
|
-
}
|
|
111
|
-
rmSync(base, { recursive: true, force: true });
|
|
112
|
-
}
|
|
108
|
+
});
|
|
113
109
|
});
|
|
114
110
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
111
|
+
describe("createWorktree — duplicate rejection", () => {
|
|
112
|
+
let base: string;
|
|
113
|
+
beforeEach(() => {
|
|
114
|
+
const repo = makeRepoWithWorktree("feature-x");
|
|
115
|
+
base = repo.base;
|
|
116
|
+
});
|
|
117
|
+
afterEach(() => { rmSync(base, { recursive: true, force: true }); });
|
|
118
|
+
|
|
119
|
+
test("rejects duplicate name", () => {
|
|
118
120
|
assert.throws(
|
|
119
|
-
() => createWorktree(base, "
|
|
121
|
+
() => createWorktree(base, "feature-x"),
|
|
120
122
|
(err: Error) => {
|
|
121
123
|
assert.ok(
|
|
122
|
-
err.message.includes("
|
|
123
|
-
`expected "
|
|
124
|
+
err.message.includes("already exists"),
|
|
125
|
+
`expected "already exists" in error, got: ${err.message}`,
|
|
124
126
|
);
|
|
125
127
|
return true;
|
|
126
128
|
},
|
|
127
|
-
"should throw on
|
|
129
|
+
"should throw on duplicate worktree name",
|
|
128
130
|
);
|
|
129
|
-
}
|
|
130
|
-
rmSync(base, { recursive: true, force: true });
|
|
131
|
-
}
|
|
131
|
+
});
|
|
132
132
|
});
|
|
133
133
|
|
|
134
134
|
// ─── listWorktrees ────────────────────────────────────────────────────────────
|
|
135
135
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
136
|
+
describe("listWorktrees", () => {
|
|
137
|
+
let base: string;
|
|
138
|
+
beforeEach(() => {
|
|
139
|
+
const repo = makeRepoWithWorktree("feature-x");
|
|
140
|
+
base = repo.base;
|
|
141
|
+
});
|
|
142
|
+
afterEach(() => { rmSync(base, { recursive: true, force: true }); });
|
|
143
|
+
|
|
144
|
+
test("returns active worktrees", () => {
|
|
139
145
|
const list = listWorktrees(base);
|
|
140
146
|
assert.strictEqual(list.length, 1, "should list exactly one worktree");
|
|
141
147
|
assert.strictEqual(list[0]!.name, "feature-x", "name should match");
|
|
142
148
|
assert.strictEqual(list[0]!.branch, "worktree/feature-x", "branch should match");
|
|
143
149
|
assert.ok(list[0]!.exists, "exists flag should be true");
|
|
144
|
-
}
|
|
145
|
-
rmSync(base, { recursive: true, force: true });
|
|
146
|
-
}
|
|
147
|
-
});
|
|
150
|
+
});
|
|
148
151
|
|
|
149
|
-
test("
|
|
150
|
-
const { base } = makeRepoWithWorktree("feature-x");
|
|
151
|
-
try {
|
|
152
|
+
test("returns empty after removal", () => {
|
|
152
153
|
removeWorktree(base, "feature-x");
|
|
153
154
|
const list = listWorktrees(base);
|
|
154
155
|
assert.strictEqual(list.length, 0, "should have no worktrees after removal");
|
|
155
|
-
}
|
|
156
|
-
rmSync(base, { recursive: true, force: true });
|
|
157
|
-
}
|
|
156
|
+
});
|
|
158
157
|
});
|
|
159
158
|
|
|
160
159
|
// ─── diffWorktreeGSD ─────────────────────────────────────────────────────────
|
|
161
160
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
161
|
+
describe("diffWorktreeGSD and getWorktreeGSDDiff", () => {
|
|
162
|
+
let base: string;
|
|
163
|
+
beforeEach(() => {
|
|
164
|
+
const repo = makeRepoWithChanges("feature-x");
|
|
165
|
+
base = repo.base;
|
|
166
|
+
});
|
|
167
|
+
afterEach(() => { rmSync(base, { recursive: true, force: true }); });
|
|
168
|
+
|
|
169
|
+
test("detects added and modified GSD files", () => {
|
|
165
170
|
const diff = diffWorktreeGSD(base, "feature-x");
|
|
166
171
|
assert.ok(diff.added.length > 0, "should have added files");
|
|
167
172
|
assert.ok(
|
|
@@ -174,58 +179,60 @@ test("diffWorktreeGSD detects added and modified GSD files", () => {
|
|
|
174
179
|
"M001 roadmap should be in modified files",
|
|
175
180
|
);
|
|
176
181
|
assert.strictEqual(diff.removed.length, 0, "should have no removed files");
|
|
177
|
-
}
|
|
178
|
-
rmSync(base, { recursive: true, force: true });
|
|
179
|
-
}
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
// ─── getWorktreeGSDDiff ───────────────────────────────────────────────────────
|
|
182
|
+
});
|
|
183
183
|
|
|
184
|
-
test("
|
|
185
|
-
const { base } = makeRepoWithChanges("feature-x");
|
|
186
|
-
try {
|
|
184
|
+
test("returns patch content", () => {
|
|
187
185
|
const fullDiff = getWorktreeGSDDiff(base, "feature-x");
|
|
188
186
|
assert.ok(fullDiff.includes("M002"), "diff should mention M002");
|
|
189
187
|
assert.ok(fullDiff.includes("updated"), "diff should mention the update");
|
|
190
|
-
}
|
|
191
|
-
rmSync(base, { recursive: true, force: true });
|
|
192
|
-
}
|
|
188
|
+
});
|
|
193
189
|
});
|
|
194
190
|
|
|
195
191
|
// ─── getWorktreeLog ───────────────────────────────────────────────────────────
|
|
196
192
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
193
|
+
describe("getWorktreeLog", () => {
|
|
194
|
+
let base: string;
|
|
195
|
+
beforeEach(() => {
|
|
196
|
+
const repo = makeRepoWithChanges("feature-x");
|
|
197
|
+
base = repo.base;
|
|
198
|
+
});
|
|
199
|
+
afterEach(() => { rmSync(base, { recursive: true, force: true }); });
|
|
200
|
+
|
|
201
|
+
test("shows commits", () => {
|
|
200
202
|
const log = getWorktreeLog(base, "feature-x");
|
|
201
203
|
assert.ok(log.includes("add M002"), "log should include the commit message");
|
|
202
|
-
}
|
|
203
|
-
rmSync(base, { recursive: true, force: true });
|
|
204
|
-
}
|
|
204
|
+
});
|
|
205
205
|
});
|
|
206
206
|
|
|
207
207
|
// ─── removeWorktree ───────────────────────────────────────────────────────────
|
|
208
208
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
209
|
+
describe("removeWorktree", () => {
|
|
210
|
+
let base: string;
|
|
211
|
+
let wtPath: string;
|
|
212
|
+
beforeEach(() => {
|
|
213
|
+
const repo = makeRepoWithWorktree("feature-x");
|
|
214
|
+
base = repo.base;
|
|
215
|
+
wtPath = repo.wtPath;
|
|
216
|
+
});
|
|
217
|
+
afterEach(() => { rmSync(base, { recursive: true, force: true }); });
|
|
218
|
+
|
|
219
|
+
test("removes directory and branch", () => {
|
|
212
220
|
removeWorktree(base, "feature-x", { deleteBranch: true });
|
|
213
221
|
assert.ok(!existsSync(wtPath), "worktree directory should be gone");
|
|
214
222
|
const branches = run("git branch", base);
|
|
215
223
|
assert.ok(!branches.includes("worktree/feature-x"), "branch should be deleted");
|
|
216
|
-
}
|
|
217
|
-
rmSync(base, { recursive: true, force: true });
|
|
218
|
-
}
|
|
224
|
+
});
|
|
219
225
|
});
|
|
220
226
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
227
|
+
describe("removeWorktree — missing worktree", () => {
|
|
228
|
+
let base: string;
|
|
229
|
+
beforeEach(() => { base = makeBaseRepo(); });
|
|
230
|
+
afterEach(() => { rmSync(base, { recursive: true, force: true }); });
|
|
231
|
+
|
|
232
|
+
test("on missing worktree does not throw", () => {
|
|
224
233
|
assert.doesNotThrow(
|
|
225
234
|
() => removeWorktree(base, "nonexistent"),
|
|
226
235
|
"should not throw when worktree does not exist",
|
|
227
236
|
);
|
|
228
|
-
}
|
|
229
|
-
rmSync(base, { recursive: true, force: true });
|
|
230
|
-
}
|
|
237
|
+
});
|
|
231
238
|
});
|
|
@@ -410,10 +410,10 @@ export class WorktreeResolver {
|
|
|
410
410
|
});
|
|
411
411
|
// Surface a clear, actionable error. The worktree and milestone branch are
|
|
412
412
|
// intentionally preserved — nothing has been deleted. The user can retry
|
|
413
|
-
// /complete-milestone or merge manually once the underlying issue is fixed
|
|
413
|
+
// /gsd dispatch complete-milestone or merge manually once the underlying issue is fixed
|
|
414
414
|
// (e.g. checkout to wrong branch, unresolved conflicts). (#1668)
|
|
415
415
|
ctx.notify(
|
|
416
|
-
`Milestone merge failed: ${msg}. Your worktree and milestone branch are preserved — retry /complete-milestone or merge manually.`,
|
|
416
|
+
`Milestone merge failed: ${msg}. Your worktree and milestone branch are preserved — retry /gsd dispatch complete-milestone or merge manually.`,
|
|
417
417
|
"warning",
|
|
418
418
|
);
|
|
419
419
|
|
|
@@ -149,7 +149,11 @@ async function getOrConnect(name: string, signal?: AbortSignal): Promise<Client>
|
|
|
149
149
|
stderr: "pipe",
|
|
150
150
|
});
|
|
151
151
|
} else if (config.transport === "http" && config.url) {
|
|
152
|
-
|
|
152
|
+
const resolvedUrl = config.url.replace(
|
|
153
|
+
/\$\{([^}]+)\}/g,
|
|
154
|
+
(_, name) => process.env[name] ?? "",
|
|
155
|
+
);
|
|
156
|
+
transport = new StreamableHTTPClientTransport(new URL(resolvedUrl));
|
|
153
157
|
} else {
|
|
154
158
|
throw new Error(`Server "${name}" has unsupported transport: ${config.transport}`);
|
|
155
159
|
}
|
|
@@ -398,16 +398,16 @@ export function registerSearchTool(pi: ExtensionAPI) {
|
|
|
398
398
|
// with brief interruptions every MAX_CONSECUTIVE_DUPES+1 calls.
|
|
399
399
|
if (cacheKey === lastSearchKey) {
|
|
400
400
|
consecutiveDupeCount++;
|
|
401
|
-
if (consecutiveDupeCount
|
|
401
|
+
if (consecutiveDupeCount > MAX_CONSECUTIVE_DUPES) {
|
|
402
402
|
return {
|
|
403
|
-
content: [{ type: "text" as const, text: `⚠️ Search loop detected: the query "${params.query}" has been searched ${consecutiveDupeCount
|
|
403
|
+
content: [{ type: "text" as const, text: `⚠️ Search loop detected: the query "${params.query}" has been searched ${consecutiveDupeCount} times consecutively with identical results. The information you need is already in the previous search results above. Stop searching and use those results to proceed with your task.` }],
|
|
404
404
|
isError: true,
|
|
405
405
|
details: { errorKind: "search_loop", error: "Consecutive duplicate search detected" } satisfies Partial<SearchDetails>,
|
|
406
406
|
};
|
|
407
407
|
}
|
|
408
408
|
} else {
|
|
409
409
|
lastSearchKey = cacheKey;
|
|
410
|
-
consecutiveDupeCount =
|
|
410
|
+
consecutiveDupeCount = 1;
|
|
411
411
|
}
|
|
412
412
|
|
|
413
413
|
const cached = searchCache.get(cacheKey);
|
|
File without changes
|
|
File without changes
|