gsd-pi 2.8.2 → 2.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/dist/cli.js +5 -0
- package/dist/loader.js +1 -1
- package/dist/update-check.d.ts +24 -0
- package/dist/update-check.js +93 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/extensions/types.d.ts +4 -2
- package/node_modules/@gsd/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
- package/node_modules/@gsd/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/client.d.ts +46 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/client.d.ts.map +1 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/client.js +758 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/client.js.map +1 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/config.d.ts +23 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/config.d.ts.map +1 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/config.js +267 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/config.js.map +1 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/edits.d.ts +17 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/edits.d.ts.map +1 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/edits.js +101 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/edits.js.map +1 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/helpers.d.ts +15 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/helpers.d.ts.map +1 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/helpers.js +46 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/helpers.js.map +1 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/index.d.ts +35 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/index.d.ts.map +1 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/index.js +709 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/index.js.map +1 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/lsp-integration.test.d.ts +2 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/lsp-integration.test.d.ts.map +1 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/lsp-integration.test.js +308 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/lsp-integration.test.js.map +1 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/lspmux.d.ts +34 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/lspmux.d.ts.map +1 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/lspmux.js +136 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/lspmux.js.map +1 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/types.d.ts +262 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/types.d.ts.map +1 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/types.js +64 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/types.js.map +1 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/utils.d.ts +50 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/utils.d.ts.map +1 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/utils.js +574 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/lsp/utils.js.map +1 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/slash-commands.d.ts.map +1 -1
- package/node_modules/@gsd/pi-coding-agent/dist/core/slash-commands.js +1 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/slash-commands.js.map +1 -1
- package/node_modules/@gsd/pi-coding-agent/dist/core/tools/index.d.ts +13 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/tools/index.d.ts.map +1 -1
- package/node_modules/@gsd/pi-coding-agent/dist/core/tools/index.js +4 -0
- package/node_modules/@gsd/pi-coding-agent/dist/core/tools/index.js.map +1 -1
- package/node_modules/@gsd/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +10 -1
- package/node_modules/@gsd/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/node_modules/@gsd/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +2 -2
- package/node_modules/@gsd/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/node_modules/@gsd/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +2 -0
- package/node_modules/@gsd/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/node_modules/@gsd/pi-coding-agent/dist/modes/interactive/interactive-mode.js +80 -1
- package/node_modules/@gsd/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/node_modules/@gsd/pi-coding-agent/dist/modes/rpc/rpc-mode.js +1 -1
- package/node_modules/@gsd/pi-coding-agent/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/node_modules/@gsd/pi-coding-agent/dist/modes/rpc/rpc-types.d.ts +5 -0
- package/node_modules/@gsd/pi-coding-agent/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/node_modules/@gsd/pi-coding-agent/dist/modes/rpc/rpc-types.js.map +1 -1
- package/node_modules/@gsd/pi-coding-agent/src/core/extensions/types.ts +4 -2
- package/node_modules/@gsd/pi-coding-agent/src/core/lsp/client.ts +880 -0
- package/node_modules/@gsd/pi-coding-agent/src/core/lsp/config.ts +325 -0
- package/node_modules/@gsd/pi-coding-agent/src/core/lsp/defaults.json +456 -0
- package/node_modules/@gsd/pi-coding-agent/src/core/lsp/edits.ts +109 -0
- package/node_modules/@gsd/pi-coding-agent/src/core/lsp/helpers.ts +54 -0
- package/node_modules/@gsd/pi-coding-agent/src/core/lsp/index.ts +943 -0
- package/node_modules/@gsd/pi-coding-agent/src/core/lsp/lsp-integration.test.ts +407 -0
- package/node_modules/@gsd/pi-coding-agent/src/core/lsp/lsp.md +33 -0
- package/node_modules/@gsd/pi-coding-agent/src/core/lsp/lspmux.ts +199 -0
- package/node_modules/@gsd/pi-coding-agent/src/core/lsp/types.ts +421 -0
- package/node_modules/@gsd/pi-coding-agent/src/core/lsp/utils.ts +682 -0
- package/node_modules/@gsd/pi-coding-agent/src/core/slash-commands.ts +1 -0
- package/node_modules/@gsd/pi-coding-agent/src/core/tools/index.ts +10 -0
- package/node_modules/@gsd/pi-coding-agent/src/modes/interactive/components/settings-selector.ts +2 -2
- package/node_modules/@gsd/pi-coding-agent/src/modes/interactive/interactive-mode.ts +94 -2
- package/node_modules/@gsd/pi-coding-agent/src/modes/rpc/rpc-mode.ts +2 -2
- package/node_modules/@gsd/pi-coding-agent/src/modes/rpc/rpc-types.ts +2 -1
- package/package.json +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +4 -2
- 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/client.d.ts +46 -0
- package/packages/pi-coding-agent/dist/core/lsp/client.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/lsp/client.js +758 -0
- package/packages/pi-coding-agent/dist/core/lsp/client.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/lsp/config.d.ts +23 -0
- package/packages/pi-coding-agent/dist/core/lsp/config.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/lsp/config.js +267 -0
- package/packages/pi-coding-agent/dist/core/lsp/config.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/lsp/edits.d.ts +17 -0
- package/packages/pi-coding-agent/dist/core/lsp/edits.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/lsp/edits.js +101 -0
- package/packages/pi-coding-agent/dist/core/lsp/edits.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/lsp/helpers.d.ts +15 -0
- package/packages/pi-coding-agent/dist/core/lsp/helpers.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/lsp/helpers.js +46 -0
- package/packages/pi-coding-agent/dist/core/lsp/helpers.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/lsp/index.d.ts +35 -0
- package/packages/pi-coding-agent/dist/core/lsp/index.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/lsp/index.js +709 -0
- package/packages/pi-coding-agent/dist/core/lsp/index.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/lsp/lsp-integration.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/lsp/lsp-integration.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/lsp/lsp-integration.test.js +308 -0
- package/packages/pi-coding-agent/dist/core/lsp/lsp-integration.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/lsp/lspmux.d.ts +34 -0
- package/packages/pi-coding-agent/dist/core/lsp/lspmux.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/lsp/lspmux.js +136 -0
- package/packages/pi-coding-agent/dist/core/lsp/lspmux.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/lsp/types.d.ts +262 -0
- package/packages/pi-coding-agent/dist/core/lsp/types.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/lsp/types.js +64 -0
- package/packages/pi-coding-agent/dist/core/lsp/types.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/lsp/utils.d.ts +50 -0
- package/packages/pi-coding-agent/dist/core/lsp/utils.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/lsp/utils.js +574 -0
- package/packages/pi-coding-agent/dist/core/lsp/utils.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/slash-commands.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/slash-commands.js +1 -0
- package/packages/pi-coding-agent/dist/core/slash-commands.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/index.d.ts +13 -0
- package/packages/pi-coding-agent/dist/core/tools/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/index.js +4 -0
- package/packages/pi-coding-agent/dist/core/tools/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +10 -1
- 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 +2 -2
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +80 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.d.ts +5 -0
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.js.map +1 -1
- package/packages/pi-coding-agent/src/core/extensions/types.ts +4 -2
- package/packages/pi-coding-agent/src/core/lsp/client.ts +880 -0
- package/packages/pi-coding-agent/src/core/lsp/config.ts +325 -0
- package/packages/pi-coding-agent/src/core/lsp/defaults.json +456 -0
- package/packages/pi-coding-agent/src/core/lsp/edits.ts +109 -0
- package/packages/pi-coding-agent/src/core/lsp/helpers.ts +54 -0
- package/packages/pi-coding-agent/src/core/lsp/index.ts +943 -0
- package/packages/pi-coding-agent/src/core/lsp/lsp-integration.test.ts +407 -0
- package/packages/pi-coding-agent/src/core/lsp/lsp.md +33 -0
- package/packages/pi-coding-agent/src/core/lsp/lspmux.ts +199 -0
- package/packages/pi-coding-agent/src/core/lsp/types.ts +421 -0
- package/packages/pi-coding-agent/src/core/lsp/utils.ts +682 -0
- package/packages/pi-coding-agent/src/core/slash-commands.ts +1 -0
- package/packages/pi-coding-agent/src/core/tools/index.ts +10 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/settings-selector.ts +2 -2
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +94 -2
- package/packages/pi-coding-agent/src/modes/rpc/rpc-mode.ts +2 -2
- package/packages/pi-coding-agent/src/modes/rpc/rpc-types.ts +2 -1
- package/src/resources/extensions/ask-user-questions.ts +42 -2
- package/src/resources/extensions/bg-shell/index.ts +34 -37
- package/src/resources/extensions/browser-tools/core.d.ts +205 -0
- package/src/resources/extensions/browser-tools/index.ts +2 -2
- package/src/resources/extensions/browser-tools/refs.ts +1 -1
- package/src/resources/extensions/browser-tools/tools/session.ts +1 -1
- package/src/resources/extensions/context7/index.ts +2 -2
- package/src/resources/extensions/get-secrets-from-user.ts +3 -2
- package/src/resources/extensions/google-search/index.ts +1 -1
- package/src/resources/extensions/gsd/auto.ts +126 -12
- package/src/resources/extensions/gsd/commands.ts +218 -3
- package/src/resources/extensions/gsd/doctor.ts +1 -1
- package/src/resources/extensions/gsd/git-service.ts +163 -13
- package/src/resources/extensions/gsd/guided-flow.ts +19 -9
- package/src/resources/extensions/gsd/index.ts +17 -7
- package/src/resources/extensions/gsd/preferences.ts +1 -1
- package/src/resources/extensions/gsd/tests/git-service.test.ts +226 -0
- package/src/resources/extensions/gsd/tests/migrate-command.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/migrate-transformer.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +10 -10
- package/src/resources/extensions/gsd/tests/next-milestone-id.test.ts +87 -0
- package/src/resources/extensions/gsd/tests/worktree.test.ts +352 -0
- package/src/resources/extensions/gsd/types.ts +1 -0
- package/src/resources/extensions/gsd/worktree.ts +20 -1
- package/src/resources/extensions/mac-tools/index.ts +1 -1
- package/src/resources/extensions/search-the-web/command-search-provider.ts +1 -1
- package/src/resources/extensions/search-the-web/format.ts +1 -1
- package/src/resources/extensions/search-the-web/index.ts +5 -5
- package/src/resources/extensions/search-the-web/native-search.ts +5 -6
- package/src/resources/extensions/search-the-web/tool-fetch-page.ts +7 -7
- package/src/resources/extensions/search-the-web/tool-llm-context.ts +11 -11
- package/src/resources/extensions/search-the-web/tool-search.ts +10 -10
- package/src/resources/extensions/shared/interview-ui.ts +2 -2
|
@@ -5,17 +5,21 @@ import { execSync } from "node:child_process";
|
|
|
5
5
|
|
|
6
6
|
import {
|
|
7
7
|
autoCommitCurrentBranch,
|
|
8
|
+
captureIntegrationBranch,
|
|
8
9
|
detectWorktreeName,
|
|
9
10
|
ensureSliceBranch,
|
|
10
11
|
getActiveSliceBranch,
|
|
11
12
|
getCurrentBranch,
|
|
13
|
+
getMainBranch,
|
|
12
14
|
getSliceBranchName,
|
|
13
15
|
isOnSliceBranch,
|
|
14
16
|
mergeSliceToMain,
|
|
15
17
|
parseSliceBranch,
|
|
18
|
+
setActiveMilestoneId,
|
|
16
19
|
SLICE_BRANCH_RE,
|
|
17
20
|
switchToMain,
|
|
18
21
|
} from "../worktree.ts";
|
|
22
|
+
import { readIntegrationBranch } from "../git-service.ts";
|
|
19
23
|
import { deriveState } from "../state.ts";
|
|
20
24
|
import { indexWorkspace } from "../workspace-index.ts";
|
|
21
25
|
|
|
@@ -252,6 +256,354 @@ async function main(): Promise<void> {
|
|
|
252
256
|
|
|
253
257
|
rmSync(base3, { recursive: true, force: true });
|
|
254
258
|
|
|
259
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
260
|
+
// Integration branch — facade-level tests
|
|
261
|
+
//
|
|
262
|
+
// These exercise the same codepath auto.ts uses:
|
|
263
|
+
// captureIntegrationBranch() → setActiveMilestoneId() → getMainBranch()
|
|
264
|
+
// → switchToMain() → mergeSliceToMain()
|
|
265
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
266
|
+
|
|
267
|
+
// ── captureIntegrationBranch on a feature branch ──────────────────────
|
|
268
|
+
|
|
269
|
+
console.log("\n=== captureIntegrationBranch: records current branch ===");
|
|
270
|
+
|
|
271
|
+
{
|
|
272
|
+
const repo = mkdtempSync(join(tmpdir(), "gsd-integ-facade-"));
|
|
273
|
+
run("git init -b main", repo);
|
|
274
|
+
run("git config user.name 'Pi Test'", repo);
|
|
275
|
+
run("git config user.email 'pi@example.com'", repo);
|
|
276
|
+
writeFileSync(join(repo, "README.md"), "init\n");
|
|
277
|
+
run("git add -A && git commit -m init", repo);
|
|
278
|
+
|
|
279
|
+
run("git checkout -b f-123-thing", repo);
|
|
280
|
+
assertEq(getCurrentBranch(repo), "f-123-thing", "on feature branch");
|
|
281
|
+
|
|
282
|
+
captureIntegrationBranch(repo, "M001");
|
|
283
|
+
assertEq(readIntegrationBranch(repo, "M001"), "f-123-thing",
|
|
284
|
+
"captureIntegrationBranch records the current branch");
|
|
285
|
+
|
|
286
|
+
// Verify it was committed (not just written to disk)
|
|
287
|
+
const logOut = run("git log --oneline -1", repo);
|
|
288
|
+
assert(logOut.includes("integration branch"), "metadata committed to git");
|
|
289
|
+
|
|
290
|
+
rmSync(repo, { recursive: true, force: true });
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// ── captureIntegrationBranch is idempotent on same lineage ──────────
|
|
294
|
+
|
|
295
|
+
console.log("\n=== captureIntegrationBranch: idempotent ===");
|
|
296
|
+
|
|
297
|
+
{
|
|
298
|
+
const repo = mkdtempSync(join(tmpdir(), "gsd-integ-idem-"));
|
|
299
|
+
run("git init -b main", repo);
|
|
300
|
+
run("git config user.name 'Pi Test'", repo);
|
|
301
|
+
run("git config user.email 'pi@example.com'", repo);
|
|
302
|
+
writeFileSync(join(repo, "README.md"), "init\n");
|
|
303
|
+
run("git add -A && git commit -m init", repo);
|
|
304
|
+
run("git checkout -b f-first", repo);
|
|
305
|
+
|
|
306
|
+
captureIntegrationBranch(repo, "M001");
|
|
307
|
+
setActiveMilestoneId(repo, "M001");
|
|
308
|
+
assertEq(readIntegrationBranch(repo, "M001"), "f-first",
|
|
309
|
+
"first capture records f-first");
|
|
310
|
+
|
|
311
|
+
// Capture again on the same branch (simulates restart/resume) — should NOT overwrite
|
|
312
|
+
captureIntegrationBranch(repo, "M001");
|
|
313
|
+
assertEq(readIntegrationBranch(repo, "M001"), "f-first",
|
|
314
|
+
"second capture on same branch does not overwrite");
|
|
315
|
+
|
|
316
|
+
// After creating a slice branch (which inherits the metadata commit),
|
|
317
|
+
// capture should still be idempotent
|
|
318
|
+
ensureSliceBranch(repo, "M001", "S01");
|
|
319
|
+
// Now on gsd/M001/S01 — capture should be no-op (slice branch rejected)
|
|
320
|
+
captureIntegrationBranch(repo, "M001");
|
|
321
|
+
switchToMain(repo);
|
|
322
|
+
assertEq(readIntegrationBranch(repo, "M001"), "f-first",
|
|
323
|
+
"capture from slice branch is no-op, original preserved");
|
|
324
|
+
assertEq(getCurrentBranch(repo), "f-first",
|
|
325
|
+
"switchToMain returns to feature branch, confirming integration branch works");
|
|
326
|
+
|
|
327
|
+
rmSync(repo, { recursive: true, force: true });
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// ── captureIntegrationBranch skips slice branches ─────────────────────
|
|
331
|
+
|
|
332
|
+
console.log("\n=== captureIntegrationBranch: skips slice branches ===");
|
|
333
|
+
|
|
334
|
+
{
|
|
335
|
+
const repo = mkdtempSync(join(tmpdir(), "gsd-integ-skip-"));
|
|
336
|
+
run("git init -b main", repo);
|
|
337
|
+
run("git config user.name 'Pi Test'", repo);
|
|
338
|
+
run("git config user.email 'pi@example.com'", repo);
|
|
339
|
+
writeFileSync(join(repo, "README.md"), "init\n");
|
|
340
|
+
run("git add -A && git commit -m init", repo);
|
|
341
|
+
|
|
342
|
+
run("git checkout -b gsd/M001/S01", repo);
|
|
343
|
+
captureIntegrationBranch(repo, "M001");
|
|
344
|
+
|
|
345
|
+
assertEq(readIntegrationBranch(repo, "M001"), null,
|
|
346
|
+
"capture from slice branch is a no-op");
|
|
347
|
+
|
|
348
|
+
rmSync(repo, { recursive: true, force: true });
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// ── setActiveMilestoneId makes getMainBranch return integration branch ─
|
|
352
|
+
|
|
353
|
+
console.log("\n=== setActiveMilestoneId + getMainBranch ===");
|
|
354
|
+
|
|
355
|
+
{
|
|
356
|
+
const repo = mkdtempSync(join(tmpdir(), "gsd-integ-main-"));
|
|
357
|
+
run("git init -b main", repo);
|
|
358
|
+
run("git config user.name 'Pi Test'", repo);
|
|
359
|
+
run("git config user.email 'pi@example.com'", repo);
|
|
360
|
+
writeFileSync(join(repo, "README.md"), "init\n");
|
|
361
|
+
run("git add -A && git commit -m init", repo);
|
|
362
|
+
|
|
363
|
+
run("git checkout -b my-feature", repo);
|
|
364
|
+
captureIntegrationBranch(repo, "M001");
|
|
365
|
+
|
|
366
|
+
// Without milestone set, getMainBranch returns "main"
|
|
367
|
+
setActiveMilestoneId(repo, null);
|
|
368
|
+
assertEq(getMainBranch(repo), "main",
|
|
369
|
+
"getMainBranch returns main without milestone set");
|
|
370
|
+
|
|
371
|
+
// With milestone set, getMainBranch returns feature branch
|
|
372
|
+
setActiveMilestoneId(repo, "M001");
|
|
373
|
+
assertEq(getMainBranch(repo), "my-feature",
|
|
374
|
+
"getMainBranch returns integration branch with milestone set");
|
|
375
|
+
|
|
376
|
+
rmSync(repo, { recursive: true, force: true });
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// ── Full multi-slice lifecycle on a feature branch ────────────────────
|
|
380
|
+
//
|
|
381
|
+
// Simulates what auto.ts does: start on feature branch, capture it,
|
|
382
|
+
// create S01, work, merge S01 back to feature branch, then S02 branches
|
|
383
|
+
// from feature branch (not main), works, merges to feature branch.
|
|
384
|
+
// Main stays untouched throughout.
|
|
385
|
+
|
|
386
|
+
console.log("\n=== Multi-slice lifecycle on feature branch ===");
|
|
387
|
+
|
|
388
|
+
{
|
|
389
|
+
const repo = mkdtempSync(join(tmpdir(), "gsd-integ-multi-"));
|
|
390
|
+
run("git init -b main", repo);
|
|
391
|
+
run("git config user.name 'Pi Test'", repo);
|
|
392
|
+
run("git config user.email 'pi@example.com'", repo);
|
|
393
|
+
writeFileSync(join(repo, "README.md"), "base\n");
|
|
394
|
+
run("git add -A && git commit -m init", repo);
|
|
395
|
+
|
|
396
|
+
// User creates feature branch
|
|
397
|
+
run("git checkout -b feature/big-change", repo);
|
|
398
|
+
writeFileSync(join(repo, "setup.txt"), "feature setup\n");
|
|
399
|
+
run("git add -A && git commit -m 'feat: initial setup'", repo);
|
|
400
|
+
|
|
401
|
+
// auto.ts startup: capture + set milestone
|
|
402
|
+
captureIntegrationBranch(repo, "M001");
|
|
403
|
+
setActiveMilestoneId(repo, "M001");
|
|
404
|
+
|
|
405
|
+
assertEq(getMainBranch(repo), "feature/big-change",
|
|
406
|
+
"multi: getMainBranch returns feature branch");
|
|
407
|
+
|
|
408
|
+
// ── S01 lifecycle ──────────────────────────────────────────────────
|
|
409
|
+
ensureSliceBranch(repo, "M001", "S01");
|
|
410
|
+
assertEq(getCurrentBranch(repo), "gsd/M001/S01", "multi: on S01");
|
|
411
|
+
|
|
412
|
+
// Verify S01 has feature branch content
|
|
413
|
+
assert(existsSync(join(repo, "setup.txt")),
|
|
414
|
+
"multi: S01 inherited feature branch content");
|
|
415
|
+
|
|
416
|
+
writeFileSync(join(repo, "s01-work.txt"), "s01 output\n");
|
|
417
|
+
run("git add -A && git commit -m 'feat(S01): work'", repo);
|
|
418
|
+
|
|
419
|
+
switchToMain(repo);
|
|
420
|
+
assertEq(getCurrentBranch(repo), "feature/big-change",
|
|
421
|
+
"multi: switchToMain goes to feature branch");
|
|
422
|
+
|
|
423
|
+
const s01merge = mergeSliceToMain(repo, "M001", "S01", "First slice");
|
|
424
|
+
assertEq(getCurrentBranch(repo), "feature/big-change",
|
|
425
|
+
"multi: after S01 merge, on feature branch");
|
|
426
|
+
assert(existsSync(join(repo, "s01-work.txt")),
|
|
427
|
+
"multi: S01 work merged to feature branch");
|
|
428
|
+
assert(s01merge.deletedBranch, "multi: S01 branch deleted");
|
|
429
|
+
|
|
430
|
+
// Main should NOT have S01 work
|
|
431
|
+
run("git stash", repo); // stash any .gsd changes
|
|
432
|
+
run("git checkout main", repo);
|
|
433
|
+
assert(!existsSync(join(repo, "s01-work.txt")),
|
|
434
|
+
"multi: main does NOT have S01 work");
|
|
435
|
+
run("git checkout feature/big-change", repo);
|
|
436
|
+
run("git stash pop || true", repo);
|
|
437
|
+
|
|
438
|
+
// ── S02 lifecycle ──────────────────────────────────────────────────
|
|
439
|
+
// S02 should branch from feature/big-change which now has S01's work
|
|
440
|
+
ensureSliceBranch(repo, "M001", "S02");
|
|
441
|
+
assertEq(getCurrentBranch(repo), "gsd/M001/S02", "multi: on S02");
|
|
442
|
+
|
|
443
|
+
// S02 should have S01's merged output (branched from feature branch)
|
|
444
|
+
assert(existsSync(join(repo, "s01-work.txt")),
|
|
445
|
+
"multi: S02 has S01 output (inherited via feature branch)");
|
|
446
|
+
|
|
447
|
+
writeFileSync(join(repo, "s02-work.txt"), "s02 output\n");
|
|
448
|
+
run("git add -A && git commit -m 'feat(S02): work'", repo);
|
|
449
|
+
|
|
450
|
+
switchToMain(repo);
|
|
451
|
+
assertEq(getCurrentBranch(repo), "feature/big-change",
|
|
452
|
+
"multi: switchToMain goes to feature branch after S02");
|
|
453
|
+
|
|
454
|
+
const s02merge = mergeSliceToMain(repo, "M001", "S02", "Second slice");
|
|
455
|
+
assertEq(getCurrentBranch(repo), "feature/big-change",
|
|
456
|
+
"multi: after S02 merge, on feature branch");
|
|
457
|
+
assert(existsSync(join(repo, "s02-work.txt")),
|
|
458
|
+
"multi: S02 work merged to feature branch");
|
|
459
|
+
assert(existsSync(join(repo, "s01-work.txt")),
|
|
460
|
+
"multi: S01 work still on feature branch after S02 merge");
|
|
461
|
+
assert(s02merge.deletedBranch, "multi: S02 branch deleted");
|
|
462
|
+
|
|
463
|
+
// Final check: main still untouched
|
|
464
|
+
run("git stash", repo);
|
|
465
|
+
run("git checkout main", repo);
|
|
466
|
+
assert(!existsSync(join(repo, "s01-work.txt")),
|
|
467
|
+
"multi: main still lacks S01 work at end");
|
|
468
|
+
assert(!existsSync(join(repo, "s02-work.txt")),
|
|
469
|
+
"multi: main still lacks S02 work at end");
|
|
470
|
+
assertEq(readFileSync(join(repo, "README.md"), "utf-8").trim(), "base",
|
|
471
|
+
"multi: main README unchanged");
|
|
472
|
+
|
|
473
|
+
rmSync(repo, { recursive: true, force: true });
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
// ── Resume scenario: milestone ID re-set after restart ────────────────
|
|
477
|
+
//
|
|
478
|
+
// Simulates crash + restart: the cached GitServiceImpl is lost, but the
|
|
479
|
+
// metadata file persists on disk. Re-calling setActiveMilestoneId should
|
|
480
|
+
// restore integration branch resolution.
|
|
481
|
+
|
|
482
|
+
console.log("\n=== Resume: milestone ID re-set restores integration branch ===");
|
|
483
|
+
|
|
484
|
+
{
|
|
485
|
+
const repo = mkdtempSync(join(tmpdir(), "gsd-integ-resume-"));
|
|
486
|
+
run("git init -b main", repo);
|
|
487
|
+
run("git config user.name 'Pi Test'", repo);
|
|
488
|
+
run("git config user.email 'pi@example.com'", repo);
|
|
489
|
+
writeFileSync(join(repo, "README.md"), "init\n");
|
|
490
|
+
run("git add -A && git commit -m init", repo);
|
|
491
|
+
|
|
492
|
+
run("git checkout -b my-feature", repo);
|
|
493
|
+
captureIntegrationBranch(repo, "M001");
|
|
494
|
+
setActiveMilestoneId(repo, "M001");
|
|
495
|
+
|
|
496
|
+
// Create a slice and do some work
|
|
497
|
+
ensureSliceBranch(repo, "M001", "S01");
|
|
498
|
+
writeFileSync(join(repo, "work.txt"), "wip\n");
|
|
499
|
+
run("git add -A && git commit -m 'wip'", repo);
|
|
500
|
+
|
|
501
|
+
// Simulate "restart" — clear milestone ID (fresh service instance)
|
|
502
|
+
setActiveMilestoneId(repo, null);
|
|
503
|
+
assertEq(getMainBranch(repo), "main",
|
|
504
|
+
"resume: getMainBranch returns main when milestone cleared");
|
|
505
|
+
|
|
506
|
+
// Re-set milestone ID (what auto.ts does on resume)
|
|
507
|
+
setActiveMilestoneId(repo, "M001");
|
|
508
|
+
assertEq(getMainBranch(repo), "my-feature",
|
|
509
|
+
"resume: getMainBranch returns feature branch after re-set");
|
|
510
|
+
|
|
511
|
+
// Full lifecycle still works after resume
|
|
512
|
+
switchToMain(repo);
|
|
513
|
+
assertEq(getCurrentBranch(repo), "my-feature",
|
|
514
|
+
"resume: switchToMain goes to feature branch after re-set");
|
|
515
|
+
|
|
516
|
+
const result = mergeSliceToMain(repo, "M001", "S01", "Resume slice");
|
|
517
|
+
assertEq(getCurrentBranch(repo), "my-feature",
|
|
518
|
+
"resume: merge lands on feature branch after re-set");
|
|
519
|
+
assert(existsSync(join(repo, "work.txt")),
|
|
520
|
+
"resume: merged work exists on feature branch");
|
|
521
|
+
|
|
522
|
+
rmSync(repo, { recursive: true, force: true });
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
// ── Backward compat: no metadata file, plain main workflow ────────────
|
|
526
|
+
//
|
|
527
|
+
// Simulates existing projects that were created before this feature.
|
|
528
|
+
// No metadata file exists, milestone ID is set — getMainBranch should
|
|
529
|
+
// still return "main" and the entire slice lifecycle works unchanged.
|
|
530
|
+
|
|
531
|
+
console.log("\n=== Backward compat: no metadata, main workflow ===");
|
|
532
|
+
|
|
533
|
+
{
|
|
534
|
+
const repo = mkdtempSync(join(tmpdir(), "gsd-integ-compat-"));
|
|
535
|
+
run("git init -b main", repo);
|
|
536
|
+
run("git config user.name 'Pi Test'", repo);
|
|
537
|
+
run("git config user.email 'pi@example.com'", repo);
|
|
538
|
+
writeFileSync(join(repo, "README.md"), "init\n");
|
|
539
|
+
run("git add -A && git commit -m init", repo);
|
|
540
|
+
|
|
541
|
+
// Set milestone but DON'T capture integration branch (simulates old project)
|
|
542
|
+
setActiveMilestoneId(repo, "M001");
|
|
543
|
+
|
|
544
|
+
assertEq(getMainBranch(repo), "main",
|
|
545
|
+
"compat: getMainBranch returns main without metadata");
|
|
546
|
+
|
|
547
|
+
// Full lifecycle on main still works
|
|
548
|
+
ensureSliceBranch(repo, "M001", "S01");
|
|
549
|
+
writeFileSync(join(repo, "feature.txt"), "new\n");
|
|
550
|
+
run("git add -A && git commit -m 'feat: work'", repo);
|
|
551
|
+
|
|
552
|
+
switchToMain(repo);
|
|
553
|
+
assertEq(getCurrentBranch(repo), "main",
|
|
554
|
+
"compat: switchToMain goes to main");
|
|
555
|
+
|
|
556
|
+
const result = mergeSliceToMain(repo, "M001", "S01", "Compat slice");
|
|
557
|
+
assertEq(getCurrentBranch(repo), "main",
|
|
558
|
+
"compat: merge lands on main");
|
|
559
|
+
assert(existsSync(join(repo, "feature.txt")),
|
|
560
|
+
"compat: merged work exists on main");
|
|
561
|
+
assert(result.deletedBranch, "compat: branch deleted");
|
|
562
|
+
|
|
563
|
+
rmSync(repo, { recursive: true, force: true });
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
// ── ensureSliceBranch from another slice with integration branch ──────
|
|
567
|
+
//
|
|
568
|
+
// When on gsd/M001/S01 and creating S02, the code falls back to
|
|
569
|
+
// getMainBranch() (not the current slice). With integration branch set,
|
|
570
|
+
// S02 should branch from the feature branch.
|
|
571
|
+
|
|
572
|
+
console.log("\n=== ensureSliceBranch: S02 from S01 uses integration branch as base ===");
|
|
573
|
+
|
|
574
|
+
{
|
|
575
|
+
const repo = mkdtempSync(join(tmpdir(), "gsd-integ-chain-"));
|
|
576
|
+
run("git init -b main", repo);
|
|
577
|
+
run("git config user.name 'Pi Test'", repo);
|
|
578
|
+
run("git config user.email 'pi@example.com'", repo);
|
|
579
|
+
writeFileSync(join(repo, "README.md"), "init\n");
|
|
580
|
+
run("git add -A && git commit -m init", repo);
|
|
581
|
+
|
|
582
|
+
run("git checkout -b dev-branch", repo);
|
|
583
|
+
writeFileSync(join(repo, "dev-only.txt"), "from dev\n");
|
|
584
|
+
run("git add -A && git commit -m 'dev setup'", repo);
|
|
585
|
+
|
|
586
|
+
captureIntegrationBranch(repo, "M001");
|
|
587
|
+
setActiveMilestoneId(repo, "M001");
|
|
588
|
+
|
|
589
|
+
// Create S01 (from dev-branch)
|
|
590
|
+
ensureSliceBranch(repo, "M001", "S01");
|
|
591
|
+
writeFileSync(join(repo, "s01.txt"), "s01\n");
|
|
592
|
+
run("git add -A && git commit -m 's01 work'", repo);
|
|
593
|
+
|
|
594
|
+
// While on S01, create S02 — should fall back to integration branch
|
|
595
|
+
ensureSliceBranch(repo, "M001", "S02");
|
|
596
|
+
assertEq(getCurrentBranch(repo), "gsd/M001/S02", "chain: on S02");
|
|
597
|
+
|
|
598
|
+
// S02 should be based on dev-branch (the integration branch)
|
|
599
|
+
assert(existsSync(join(repo, "dev-only.txt")),
|
|
600
|
+
"chain: S02 has dev-branch content");
|
|
601
|
+
assert(!existsSync(join(repo, "s01.txt")),
|
|
602
|
+
"chain: S02 does NOT have S01 content (not chained from S01)");
|
|
603
|
+
|
|
604
|
+
rmSync(repo, { recursive: true, force: true });
|
|
605
|
+
}
|
|
606
|
+
|
|
255
607
|
rmSync(base, { recursive: true, force: true });
|
|
256
608
|
console.log(`\nResults: ${passed} passed, ${failed} failed`);
|
|
257
609
|
if (failed > 0) process.exit(1);
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
|
|
18
18
|
import { sep } from "node:path";
|
|
19
19
|
|
|
20
|
-
import { GitServiceImpl } from "./git-service.ts";
|
|
20
|
+
import { GitServiceImpl, writeIntegrationBranch } from "./git-service.ts";
|
|
21
21
|
import { loadEffectiveGSDPreferences } from "./preferences.ts";
|
|
22
22
|
|
|
23
23
|
// Re-export MergeSliceResult from the canonical source (D014 — type-only re-export)
|
|
@@ -43,6 +43,25 @@ function getService(basePath: string): GitServiceImpl {
|
|
|
43
43
|
return cachedService;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
/**
|
|
47
|
+
* Set the active milestone ID on the cached GitServiceImpl.
|
|
48
|
+
* This enables integration branch resolution in getMainBranch().
|
|
49
|
+
*/
|
|
50
|
+
export function setActiveMilestoneId(basePath: string, milestoneId: string | null): void {
|
|
51
|
+
getService(basePath).setMilestoneId(milestoneId);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Record the current branch as the integration branch for a milestone.
|
|
56
|
+
* Called once when auto-mode starts — captures where slice branches should
|
|
57
|
+
* merge back to. No-op if already recorded or if on a GSD slice branch.
|
|
58
|
+
*/
|
|
59
|
+
export function captureIntegrationBranch(basePath: string, milestoneId: string): void {
|
|
60
|
+
const svc = getService(basePath);
|
|
61
|
+
const current = svc.getCurrentBranch();
|
|
62
|
+
writeIntegrationBranch(basePath, milestoneId, current);
|
|
63
|
+
}
|
|
64
|
+
|
|
46
65
|
// ─── Pure Utility Functions (unchanged) ────────────────────────────────────
|
|
47
66
|
|
|
48
67
|
/**
|
|
@@ -123,7 +123,7 @@ function execMacAgent(command: string, params?: Record<string, any>): MacAgentRe
|
|
|
123
123
|
stdio: ["pipe", "pipe", "pipe"],
|
|
124
124
|
maxBuffer: 5 * 1024 * 1024, // 5MB — needed for retina screenshot base64 payloads
|
|
125
125
|
});
|
|
126
|
-
stdout = typeof result === "string" ? result : result
|
|
126
|
+
stdout = typeof result === "string" ? result : String(result);
|
|
127
127
|
} catch (err: any) {
|
|
128
128
|
stderr = err.stderr?.toString() || "";
|
|
129
129
|
const isTimeout = err.killed || err.signal === "SIGTERM";
|
|
@@ -45,11 +45,11 @@
|
|
|
45
45
|
*/
|
|
46
46
|
|
|
47
47
|
import type { ExtensionAPI } from "@gsd/pi-coding-agent";
|
|
48
|
-
import { registerSearchTool } from "./tool-search";
|
|
49
|
-
import { registerFetchPageTool } from "./tool-fetch-page";
|
|
50
|
-
import { registerLLMContextTool } from "./tool-llm-context";
|
|
51
|
-
import { registerSearchProviderCommand } from "./command-search-provider.
|
|
52
|
-
import { registerNativeSearchHooks } from "./native-search";
|
|
48
|
+
import { registerSearchTool } from "./tool-search.js";
|
|
49
|
+
import { registerFetchPageTool } from "./tool-fetch-page.js";
|
|
50
|
+
import { registerLLMContextTool } from "./tool-llm-context.js";
|
|
51
|
+
import { registerSearchProviderCommand } from "./command-search-provider.js";
|
|
52
|
+
import { registerNativeSearchHooks } from "./native-search.js";
|
|
53
53
|
|
|
54
54
|
export default function (pi: ExtensionAPI) {
|
|
55
55
|
registerSearchTool(pi);
|
|
@@ -99,12 +99,11 @@ export function registerNativeSearchHooks(pi: NativeSearchPI): { getIsAnthropic:
|
|
|
99
99
|
const payload = event.payload as Record<string, unknown>;
|
|
100
100
|
if (!payload) return;
|
|
101
101
|
|
|
102
|
-
//
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
isAnthropicProvider = true;
|
|
102
|
+
// Only inject native web search for confirmed Anthropic provider.
|
|
103
|
+
// model_select sets isAnthropicProvider via the provider field.
|
|
104
|
+
// Model name prefix is NOT sufficient — other providers (GitHub Copilot,
|
|
105
|
+
// AWS Bedrock, etc.) serve claude-* models through non-Anthropic APIs.
|
|
106
|
+
if (!isAnthropicProvider) return;
|
|
108
107
|
|
|
109
108
|
// Strip thinking blocks from history to avoid signature validation errors
|
|
110
109
|
// caused by the SDK dropping server_tool_use/web_search_tool_result blocks.
|
|
@@ -13,10 +13,10 @@ import { truncateHead, DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES } from "@gsd/pi-codi
|
|
|
13
13
|
import { Text } from "@gsd/pi-tui";
|
|
14
14
|
import { Type } from "@sinclair/typebox";
|
|
15
15
|
|
|
16
|
-
import { LRUTTLCache } from "./cache";
|
|
17
|
-
import { fetchSimple, HttpError } from "./http";
|
|
18
|
-
import { extractDomain } from "./url-utils";
|
|
19
|
-
import { formatPageContent, type FormatPageOptions } from "./format";
|
|
16
|
+
import { LRUTTLCache } from "./cache.js";
|
|
17
|
+
import { fetchSimple, HttpError } from "./http.js";
|
|
18
|
+
import { extractDomain } from "./url-utils.js";
|
|
19
|
+
import { formatPageContent, type FormatPageOptions } from "./format.js";
|
|
20
20
|
|
|
21
21
|
// =============================================================================
|
|
22
22
|
// Cache
|
|
@@ -336,7 +336,7 @@ export function registerFetchPageTool(pi: ExtensionAPI) {
|
|
|
336
336
|
|
|
337
337
|
async execute(toolCallId, params, signal, onUpdate, ctx) {
|
|
338
338
|
if (signal?.aborted) {
|
|
339
|
-
return { content: [{ type: "text", text: "Fetch cancelled." }] };
|
|
339
|
+
return { content: [{ type: "text", text: "Fetch cancelled." }], details: undefined as unknown };
|
|
340
340
|
}
|
|
341
341
|
|
|
342
342
|
const maxChars = params.maxChars ?? 8000;
|
|
@@ -392,7 +392,7 @@ export function registerFetchPageTool(pi: ExtensionAPI) {
|
|
|
392
392
|
}
|
|
393
393
|
|
|
394
394
|
const domain = extractDomain(url);
|
|
395
|
-
onUpdate?.({ content: [{ type: "text", text: `Fetching ${domain}...` }] });
|
|
395
|
+
onUpdate?.({ content: [{ type: "text", text: `Fetching ${domain}...` }], details: undefined as unknown });
|
|
396
396
|
|
|
397
397
|
// ------------------------------------------------------------------
|
|
398
398
|
// Fetch page content
|
|
@@ -439,7 +439,7 @@ export function registerFetchPageTool(pi: ExtensionAPI) {
|
|
|
439
439
|
const finalTruncation = truncateHead(output, { maxLines: DEFAULT_MAX_LINES, maxBytes: DEFAULT_MAX_BYTES });
|
|
440
440
|
let content = finalTruncation.content;
|
|
441
441
|
if (finalTruncation.truncated) {
|
|
442
|
-
const tempFile = await pi.writeTempFile(output, { prefix: "fetch-page-" });
|
|
442
|
+
const tempFile = await (pi as any).writeTempFile(output, { prefix: "fetch-page-" });
|
|
443
443
|
content += `\n\n[Truncated to fit context. Full content: ${tempFile}]`;
|
|
444
444
|
}
|
|
445
445
|
|
|
@@ -21,13 +21,13 @@ import { Text } from "@gsd/pi-tui";
|
|
|
21
21
|
import { Type } from "@sinclair/typebox";
|
|
22
22
|
import { StringEnum } from "@gsd/pi-ai";
|
|
23
23
|
|
|
24
|
-
import { LRUTTLCache } from "./cache";
|
|
25
|
-
import { fetchWithRetryTimed, HttpError, classifyError, type RateLimitInfo } from "./http";
|
|
26
|
-
import { normalizeQuery, extractDomain } from "./url-utils";
|
|
27
|
-
import { formatLLMContext, type LLMContextSnippet, type LLMContextSource } from "./format";
|
|
28
|
-
import type { TavilyResult, TavilySearchResponse } from "./tavily";
|
|
29
|
-
import { publishedDateToAge } from "./tavily";
|
|
30
|
-
import { getTavilyApiKey, resolveSearchProvider } from "./provider";
|
|
24
|
+
import { LRUTTLCache } from "./cache.js";
|
|
25
|
+
import { fetchWithRetryTimed, HttpError, classifyError, type RateLimitInfo } from "./http.js";
|
|
26
|
+
import { normalizeQuery, extractDomain } from "./url-utils.js";
|
|
27
|
+
import { formatLLMContext, type LLMContextSnippet, type LLMContextSource } from "./format.js";
|
|
28
|
+
import type { TavilyResult, TavilySearchResponse } from "./tavily.js";
|
|
29
|
+
import { publishedDateToAge } from "./tavily.js";
|
|
30
|
+
import { getTavilyApiKey, resolveSearchProvider } from "./provider.js";
|
|
31
31
|
|
|
32
32
|
// =============================================================================
|
|
33
33
|
// Types
|
|
@@ -286,7 +286,7 @@ export function registerLLMContextTool(pi: ExtensionAPI) {
|
|
|
286
286
|
|
|
287
287
|
async execute(toolCallId, params, signal, onUpdate, ctx) {
|
|
288
288
|
if (signal?.aborted) {
|
|
289
|
-
return { content: [{ type: "text", text: "Search cancelled." }] };
|
|
289
|
+
return { content: [{ type: "text", text: "Search cancelled." }], details: undefined as unknown };
|
|
290
290
|
}
|
|
291
291
|
|
|
292
292
|
// ------------------------------------------------------------------
|
|
@@ -321,7 +321,7 @@ export function registerLLMContextTool(pi: ExtensionAPI) {
|
|
|
321
321
|
const truncation = truncateHead(output, { maxLines: DEFAULT_MAX_LINES, maxBytes: DEFAULT_MAX_BYTES });
|
|
322
322
|
let content = truncation.content;
|
|
323
323
|
if (truncation.truncated) {
|
|
324
|
-
const tempFile = await pi.writeTempFile(output, { prefix: "llm-context-" });
|
|
324
|
+
const tempFile = await (pi as any).writeTempFile(output, { prefix: "llm-context-" });
|
|
325
325
|
content += `\n\n[Truncated. Full content: ${tempFile}]`;
|
|
326
326
|
}
|
|
327
327
|
|
|
@@ -340,7 +340,7 @@ export function registerLLMContextTool(pi: ExtensionAPI) {
|
|
|
340
340
|
return { content: [{ type: "text", text: content }], details };
|
|
341
341
|
}
|
|
342
342
|
|
|
343
|
-
onUpdate?.({ content: [{ type: "text", text: `Searching & reading about "${params.query}"...` }] });
|
|
343
|
+
onUpdate?.({ content: [{ type: "text", text: `Searching & reading about "${params.query}"...` }], details: undefined as unknown });
|
|
344
344
|
|
|
345
345
|
try {
|
|
346
346
|
// ------------------------------------------------------------------
|
|
@@ -483,7 +483,7 @@ export function registerLLMContextTool(pi: ExtensionAPI) {
|
|
|
483
483
|
let content = truncation.content;
|
|
484
484
|
|
|
485
485
|
if (truncation.truncated) {
|
|
486
|
-
const tempFile = await pi.writeTempFile(output, { prefix: "llm-context-" });
|
|
486
|
+
const tempFile = await (pi as any).writeTempFile(output, { prefix: "llm-context-" });
|
|
487
487
|
content += `\n\n[Truncated. Full content: ${tempFile}]`;
|
|
488
488
|
}
|
|
489
489
|
|
|
@@ -16,12 +16,12 @@ import { Text } from "@gsd/pi-tui";
|
|
|
16
16
|
import { Type } from "@sinclair/typebox";
|
|
17
17
|
import { StringEnum } from "@gsd/pi-ai";
|
|
18
18
|
|
|
19
|
-
import { LRUTTLCache } from "./cache";
|
|
20
|
-
import { fetchWithRetryTimed, fetchWithRetry, classifyError, type RateLimitInfo } from "./http";
|
|
21
|
-
import { normalizeQuery, toDedupeKey, detectFreshness } from "./url-utils";
|
|
22
|
-
import { formatSearchResults, type SearchResultFormatted, type FormatSearchOptions } from "./format";
|
|
23
|
-
import { getTavilyApiKey, resolveSearchProvider } from "./provider";
|
|
24
|
-
import { normalizeTavilyResult, mapFreshnessToTavily, type TavilySearchResponse } from "./tavily";
|
|
19
|
+
import { LRUTTLCache } from "./cache.js";
|
|
20
|
+
import { fetchWithRetryTimed, fetchWithRetry, classifyError, type RateLimitInfo } from "./http.js";
|
|
21
|
+
import { normalizeQuery, toDedupeKey, detectFreshness } from "./url-utils.js";
|
|
22
|
+
import { formatSearchResults, type SearchResultFormatted, type FormatSearchOptions } from "./format.js";
|
|
23
|
+
import { getTavilyApiKey, resolveSearchProvider } from "./provider.js";
|
|
24
|
+
import { normalizeTavilyResult, mapFreshnessToTavily, type TavilySearchResponse } from "./tavily.js";
|
|
25
25
|
|
|
26
26
|
// =============================================================================
|
|
27
27
|
// Types
|
|
@@ -291,7 +291,7 @@ export function registerSearchTool(pi: ExtensionAPI) {
|
|
|
291
291
|
|
|
292
292
|
async execute(toolCallId, params, signal, onUpdate, ctx) {
|
|
293
293
|
if (signal?.aborted) {
|
|
294
|
-
return { content: [{ type: "text", text: "Search cancelled." }] };
|
|
294
|
+
return { content: [{ type: "text", text: "Search cancelled." }], details: undefined as unknown };
|
|
295
295
|
}
|
|
296
296
|
|
|
297
297
|
// ------------------------------------------------------------------
|
|
@@ -365,7 +365,7 @@ export function registerSearchTool(pi: ExtensionAPI) {
|
|
|
365
365
|
const truncation = truncateHead(output, { maxLines: DEFAULT_MAX_LINES, maxBytes: DEFAULT_MAX_BYTES });
|
|
366
366
|
let content = truncation.content;
|
|
367
367
|
if (truncation.truncated) {
|
|
368
|
-
const tempFile = await pi.writeTempFile(output, { prefix: "web-search-" });
|
|
368
|
+
const tempFile = await (pi as any).writeTempFile(output, { prefix: "web-search-" });
|
|
369
369
|
content += `\n\n[Truncated: ${truncation.outputLines}/${truncation.totalLines} lines (${formatSize(truncation.outputBytes)}/${formatSize(truncation.totalBytes)}). Full results: ${tempFile}]`;
|
|
370
370
|
}
|
|
371
371
|
|
|
@@ -387,7 +387,7 @@ export function registerSearchTool(pi: ExtensionAPI) {
|
|
|
387
387
|
return { content: [{ type: "text", text: content }], details };
|
|
388
388
|
}
|
|
389
389
|
|
|
390
|
-
onUpdate?.({ content: [{ type: "text", text: `Searching for "${params.query}"...` }] });
|
|
390
|
+
onUpdate?.({ content: [{ type: "text", text: `Searching for "${params.query}"...` }], details: undefined as unknown });
|
|
391
391
|
|
|
392
392
|
try {
|
|
393
393
|
// ------------------------------------------------------------------
|
|
@@ -484,7 +484,7 @@ export function registerSearchTool(pi: ExtensionAPI) {
|
|
|
484
484
|
let content = truncation.content;
|
|
485
485
|
|
|
486
486
|
if (truncation.truncated) {
|
|
487
|
-
const tempFile = await pi.writeTempFile(output, { prefix: "web-search-" });
|
|
487
|
+
const tempFile = await (pi as any).writeTempFile(output, { prefix: "web-search-" });
|
|
488
488
|
content += `\n\n[Truncated: ${truncation.outputLines}/${truncation.totalLines} lines (${formatSize(truncation.outputBytes)}/${formatSize(truncation.totalBytes)}). Full results: ${tempFile}]`;
|
|
489
489
|
}
|
|
490
490
|
|
|
@@ -105,7 +105,7 @@ export interface WrapUpOptions {
|
|
|
105
105
|
// ─── Constants ────────────────────────────────────────────────────────────────
|
|
106
106
|
|
|
107
107
|
const OTHER_OPTION_LABEL = "None of the above";
|
|
108
|
-
const OTHER_OPTION_DESCRIPTION = "
|
|
108
|
+
const OTHER_OPTION_DESCRIPTION = "Press TAB to add optional notes.";
|
|
109
109
|
|
|
110
110
|
// ─── Wrap-up screen ───────────────────────────────────────────────────────────
|
|
111
111
|
|
|
@@ -593,7 +593,7 @@ export async function showInterviewRound(
|
|
|
593
593
|
hints.push("tab to add notes");
|
|
594
594
|
hints.push(isLast && allAnswered() ? "enter to review" : "enter to next");
|
|
595
595
|
} else {
|
|
596
|
-
|
|
596
|
+
hints.push("tab to add notes");
|
|
597
597
|
if (isMultiQuestion) hints.push("←/→ navigate");
|
|
598
598
|
hints.push(isLast && allAnswered() ? "enter to review" : "enter to next");
|
|
599
599
|
}
|