@difflab/pi 0.1.0-rc.202609140747.4d45e72.2 → 0.1.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.
@@ -1,6 +1,33 @@
1
- // src/tools/setup.ts
1
+ // src/tools/reload.ts
2
2
  import { defineTool } from "@earendil-works/pi-coding-agent";
3
- import { Type } from "typebox";
3
+ import { z } from "zod";
4
+ var reloadParametersSchema = z.object({});
5
+ var reloadParameters = z.toJSONSchema(reloadParametersSchema, { io: "input" });
6
+ function createDiffpiReloadTool(pi) {
7
+ return defineTool({
8
+ name: "diffpi_reload",
9
+ label: "diffpi reload",
10
+ description: "Reload pi extensions, skills, prompts, themes, and context files after setup changes.",
11
+ promptSnippet: "Reload pi after diffpi_setup installs or updates pi resources",
12
+ promptGuidelines: [
13
+ "Call diffpi_reload after diffpi_setup reports that pi resources changed.",
14
+ "Do not call diffpi_reload after a read-only diffpi_validate run."
15
+ ],
16
+ parameters: reloadParameters,
17
+ executionMode: "sequential",
18
+ execute() {
19
+ pi.sendUserMessage("/diffpi-reload", { deliverAs: "followUp", expandPromptTemplates: true });
20
+ return Promise.resolve({
21
+ content: [{ type: "text", text: "Queued /diffpi-reload as a follow-up command." }],
22
+ details: {}
23
+ });
24
+ }
25
+ });
26
+ }
27
+
28
+ // src/tools/setup.ts
29
+ import { defineTool as defineTool2 } from "@earendil-works/pi-coding-agent";
30
+ import { z as z2 } from "zod";
4
31
 
5
32
  // src/setup.ts
6
33
  import { homedir as homedir4 } from "node:os";
@@ -16,8 +43,8 @@ var mcp = {
16
43
  },
17
44
  async serversEnsure(servers, options = {}) {
18
45
  const path = options.path ?? mcp.globalConfigPath();
19
- const currentText = await readOptional(path);
20
- const current = parseConfig(currentText, path);
46
+ const currentText = await getOptionalFile(path);
47
+ const current = getParsedConfig(currentText, path);
21
48
  const nextServers = { ...current.mcpServers };
22
49
  for (const [name, entry] of Object.entries(servers)) {
23
50
  nextServers[name] = mergeEntry(nextServers[name], entry);
@@ -38,7 +65,7 @@ function mergeEntry(current, required) {
38
65
  merged.env = { ...current?.env, ...required.env };
39
66
  return merged;
40
67
  }
41
- function parseConfig(content, path) {
68
+ function getParsedConfig(content, path) {
42
69
  if (!content?.trim())
43
70
  return { mcpServers: {} };
44
71
  try {
@@ -53,7 +80,7 @@ function parseConfig(content, path) {
53
80
  throw new Error(`Expected a valid pi-mcp-adapter configuration in ${path}.`);
54
81
  }
55
82
  }
56
- async function readOptional(path) {
83
+ async function getOptionalFile(path) {
57
84
  try {
58
85
  return await readFile(path, "utf8");
59
86
  } catch (error) {
@@ -76,6 +103,7 @@ import { constants } from "node:fs";
76
103
  import { access } from "node:fs/promises";
77
104
  import { delimiter, join as join2 } from "node:path";
78
105
  import { spawn } from "node:child_process";
106
+ var MAX_CAPTURED_OUTPUT_LENGTH = 65536;
79
107
  async function findExecutable(name) {
80
108
  if (name.includes("/")) {
81
109
  try {
@@ -124,10 +152,12 @@ async function runChecked(command, args, options = {}) {
124
152
  }
125
153
  function appendBounded(current, next) {
126
154
  const combined = current + next;
127
- return combined.length <= 65536 ? combined : combined.slice(-65536);
155
+ return combined.length <= MAX_CAPTURED_OUTPUT_LENGTH ? combined : combined.slice(-MAX_CAPTURED_OUTPUT_LENGTH);
128
156
  }
129
157
 
130
158
  // src/mise.ts
159
+ var MISE_HOOK_START = "# >>> @difflab/pi mise >>>";
160
+ var MISE_HOOK_END = "# <<< @difflab/pi mise <<<";
131
161
  var mise = {
132
162
  async executableCheck(name = "mise") {
133
163
  return findExecutable(name);
@@ -148,8 +178,8 @@ var mise = {
148
178
  },
149
179
  async hookEnsure(executable, options = {}) {
150
180
  const homeDir = options.homeDir ?? homedir2();
151
- const hook = shellHook(basename(options.shell ?? process.env.SHELL ?? ""), executable, homeDir);
152
- const current = await readOptional2(hook.path);
181
+ const hook = getShellHook(basename(options.shell ?? process.env.SHELL ?? ""), executable, homeDir);
182
+ const current = await getOptionalFile2(hook.path);
153
183
  if (current.includes(MISE_HOOK_START))
154
184
  return { path: hook.path, changed: false, planned: false };
155
185
  if (options.dryRun)
@@ -161,16 +191,16 @@ var mise = {
161
191
  await writeFile2(hook.path, `${current}${separator}${hook.content}`, "utf8");
162
192
  return { path: hook.path, changed: true, planned: false };
163
193
  },
164
- async toolCheckGlobal(executable, tool, minimumMajor = 0) {
194
+ async toolCheckGlobal(executable, tool, minimumVersion) {
165
195
  const result = await run(executable, ["ls", "--global", "--installed", tool, "--json"]);
166
- return result.code === 0 && hasInstalledTool(result.stdout, minimumMajor);
196
+ return result.code === 0 && isToolInstalled(result.stdout, minimumVersion);
167
197
  },
168
198
  async toolInstallGlobal(executable, specification) {
169
199
  await runChecked(executable, ["use", "--global", specification]);
170
200
  },
171
201
  async toolCheckLocal(executable, tool, cwd = process.cwd()) {
172
202
  const result = await run(executable, ["ls", "--local", "--installed", tool, "--json"], { cwd });
173
- return result.code === 0 && hasInstalledTool(result.stdout);
203
+ return result.code === 0 && isToolInstalled(result.stdout);
174
204
  },
175
205
  async toolInstallLocal(executable, specification, cwd = process.cwd()) {
176
206
  await runChecked(executable, ["use", "--path", cwd, specification], { cwd });
@@ -179,40 +209,76 @@ var mise = {
179
209
  await runChecked(executable, ["upgrade"], { cwd: homeDir });
180
210
  }
181
211
  };
182
- var MISE_HOOK_START = "# >>> @difflab/pi mise >>>";
183
- var MISE_HOOK_END = "# <<< @difflab/pi mise <<<";
184
- function shellHook(shell, executable, homeDir) {
185
- const command = shellQuote(executable);
186
- if (shell === "bash") {
187
- return {
188
- path: join3(homeDir, ".bashrc"),
189
- content: `${MISE_HOOK_START}
190
- eval "$(${command} activate bash)"
191
- ${MISE_HOOK_END}
192
- `
193
- };
194
- }
195
- if (shell === "zsh") {
196
- return {
197
- path: join3(homeDir, ".zshrc"),
198
- content: `${MISE_HOOK_START}
212
+ function getShellHook(shell, executable, homeDir) {
213
+ const command = getShellQuoted(executable);
214
+ switch (shell.toLowerCase()) {
215
+ case "zsh":
216
+ return {
217
+ path: join3(homeDir, ".zshrc"),
218
+ content: `${MISE_HOOK_START}
199
219
  eval "$(${command} activate zsh)"
200
220
  ${MISE_HOOK_END}
201
221
  `
202
- };
203
- }
204
- if (shell === "fish") {
205
- return {
206
- path: join3(homeDir, ".config", "fish", "config.fish"),
207
- content: `${MISE_HOOK_START}
222
+ };
223
+ case "fish":
224
+ return {
225
+ path: join3(homeDir, ".config", "fish", "config.fish"),
226
+ content: `${MISE_HOOK_START}
208
227
  ${command} activate fish | source
209
228
  ${MISE_HOOK_END}
210
229
  `
211
- };
230
+ };
231
+ case "nu":
232
+ case "nushell":
233
+ return {
234
+ path: join3(homeDir, ".config", "nushell", "config.nu"),
235
+ content: `${MISE_HOOK_START}
236
+ let mise_bin = ${command}
237
+ let mise_path = $nu.default-config-dir | path join mise.nu
238
+ ^$mise_bin activate nu | save $mise_path --force
239
+ use ($nu.default-config-dir | path join mise.nu)
240
+ ${MISE_HOOK_END}
241
+ `
242
+ };
243
+ case "xonsh":
244
+ return {
245
+ path: join3(homeDir, ".xonshrc"),
246
+ content: `${MISE_HOOK_START}
247
+ execx($(${command} activate xonsh))
248
+ ${MISE_HOOK_END}
249
+ `
250
+ };
251
+ case "elvish":
252
+ return {
253
+ path: join3(homeDir, ".config", "elvish", "rc.elv"),
254
+ content: `${MISE_HOOK_START}
255
+ var mise: = (ns [&])
256
+ eval (${command} activate elvish | slurp) &ns=$mise: &on-end={|ns| set mise: = $ns }
257
+ mise:activate
258
+ ${MISE_HOOK_END}
259
+ `
260
+ };
261
+ case "pwsh":
262
+ case "powershell":
263
+ return {
264
+ path: join3(homeDir, ".config", "powershell", "Microsoft.PowerShell_profile.ps1"),
265
+ content: `${MISE_HOOK_START}
266
+ (& ${command} activate pwsh) | Out-String | Invoke-Expression
267
+ ${MISE_HOOK_END}
268
+ `
269
+ };
270
+ case "bash":
271
+ default:
272
+ return {
273
+ path: join3(homeDir, ".bashrc"),
274
+ content: `${MISE_HOOK_START}
275
+ eval "$(${command} activate bash)"
276
+ ${MISE_HOOK_END}
277
+ `
278
+ };
212
279
  }
213
- throw new Error(`Unsupported shell "${shell || "unknown"}". Supported shells: bash, zsh, fish.`);
214
280
  }
215
- function hasInstalledTool(output, minimumMajor = 0) {
281
+ function isToolInstalled(output, minimumVersion) {
216
282
  try {
217
283
  const value = JSON.parse(output);
218
284
  if (!Array.isArray(value))
@@ -220,17 +286,28 @@ function hasInstalledTool(output, minimumMajor = 0) {
220
286
  return value.some((entry) => {
221
287
  if (!entry || typeof entry !== "object" || !("installed" in entry) || entry.installed !== true)
222
288
  return false;
223
- if (minimumMajor === 0)
289
+ if (!minimumVersion)
224
290
  return true;
225
291
  if (!("version" in entry) || typeof entry.version !== "string")
226
292
  return false;
227
- return Number.parseInt(entry.version, 10) >= minimumMajor;
293
+ return isVersionAtLeast(entry.version, minimumVersion);
228
294
  });
229
295
  } catch {
230
296
  return false;
231
297
  }
232
298
  }
233
- async function readOptional2(path) {
299
+ function isVersionAtLeast(version, minimumVersion) {
300
+ const current = version.match(/^v?(\d+)\.(\d+)\.(\d+)/)?.slice(1).map(Number);
301
+ const minimum = minimumVersion.match(/^v?(\d+)\.(\d+)\.(\d+)/)?.slice(1).map(Number);
302
+ if (!current || !minimum)
303
+ return false;
304
+ for (let index = 0;index < minimum.length; index += 1) {
305
+ if (current[index] !== minimum[index])
306
+ return current[index] > minimum[index];
307
+ }
308
+ return true;
309
+ }
310
+ async function getOptionalFile2(path) {
234
311
  try {
235
312
  return await readFile2(path, "utf8");
236
313
  } catch (error) {
@@ -239,7 +316,7 @@ async function readOptional2(path) {
239
316
  throw error;
240
317
  }
241
318
  }
242
- function shellQuote(value) {
319
+ function getShellQuoted(value) {
243
320
  return `'${value.replaceAll("'", "'\\''")}'`;
244
321
  }
245
322
 
@@ -263,12 +340,12 @@ var pi = {
263
340
  await runChecked(executable, ["install", source]);
264
341
  },
265
342
  agentDir(homeDir = homedir3()) {
266
- return resolveAgentDir(homeDir);
343
+ return getAgentDir(homeDir);
267
344
  },
268
- async skillCheckGlobal(name, agentDir = resolveAgentDir(), sharedSkillsDir = join4(homedir3(), ".agents", "skills")) {
345
+ async skillCheckGlobal(name, agentDir = getAgentDir(), sharedSkillsDir = join4(homedir3(), ".agents", "skills")) {
269
346
  const roots = [join4(agentDir, "skills"), sharedSkillsDir];
270
347
  for (const root of roots) {
271
- if (await readOptional3(join4(root, name, "SKILL.md")) !== undefined)
348
+ if (await getOptionalFile3(join4(root, name, "SKILL.md")) !== undefined)
272
349
  return true;
273
350
  }
274
351
  return false;
@@ -292,8 +369,8 @@ var pi = {
292
369
  ]);
293
370
  },
294
371
  async configEnsure(path, update, dryRun = false) {
295
- const currentText = await readOptional3(path);
296
- const current = parseObject(currentText, path);
372
+ const currentText = await getOptionalFile3(path);
373
+ const current = getParsedObject(currentText, path);
297
374
  const next = update(current);
298
375
  const changed = JSON.stringify(current) !== JSON.stringify(next);
299
376
  if (changed && !dryRun) {
@@ -304,10 +381,10 @@ var pi = {
304
381
  return { path, changed, existed: currentText !== undefined, planned: changed && dryRun };
305
382
  }
306
383
  };
307
- function resolveAgentDir(homeDir = homedir3()) {
384
+ function getAgentDir(homeDir = homedir3()) {
308
385
  return process.env.PI_CODING_AGENT_DIR ?? (process.env.XDG_CONFIG_HOME ? join4(process.env.XDG_CONFIG_HOME, "pi") : join4(homeDir, ".pi", "agent"));
309
386
  }
310
- async function readOptional3(path) {
387
+ async function getOptionalFile3(path) {
311
388
  try {
312
389
  return await readFile3(path, "utf8");
313
390
  } catch (error) {
@@ -316,7 +393,7 @@ async function readOptional3(path) {
316
393
  throw error;
317
394
  }
318
395
  }
319
- function parseObject(content, path) {
396
+ function getParsedObject(content, path) {
320
397
  if (!content?.trim())
321
398
  return {};
322
399
  try {
@@ -328,44 +405,77 @@ function parseObject(content, path) {
328
405
  }
329
406
 
330
407
  // src/setup.ts
408
+ var MISE_DEPENDENCIES = [
409
+ { name: "node", tool: "node", spec: "node@22", minimumVersion: "22.19.0" },
410
+ { name: "zellij", tool: "zellij", spec: "zellij@latest", minimumVersion: undefined },
411
+ { name: "helix", tool: "helix", spec: "helix@latest", minimumVersion: undefined },
412
+ {
413
+ name: "tuicr",
414
+ tool: "github:agavra/tuicr",
415
+ spec: "github:agavra/tuicr@latest",
416
+ minimumVersion: undefined
417
+ },
418
+ {
419
+ name: "context-mode",
420
+ tool: "npm:context-mode",
421
+ spec: "npm:context-mode@latest",
422
+ minimumVersion: undefined
423
+ }
424
+ ];
425
+ var PI_PACKAGES = [
426
+ "npm:@tintinweb/pi-subagents",
427
+ "npm:pi-schedule-prompt",
428
+ "npm:@narumitw/pi-btw",
429
+ "npm:pi-web-access",
430
+ "npm:@gitawego/pi-lsp",
431
+ "npm:context-mode"
432
+ ];
433
+ var PI_SKILL_SOURCES = [
434
+ { repository: "arabold/docs-mcp-server", skills: ["docs-manage", "docs-search", "fetch-url"] },
435
+ { repository: "AminBlg/SimpleEnglish", skills: ["simple-english"] }
436
+ ];
437
+ var MCP_ADAPTER_PACKAGE = "npm:pi-mcp-adapter";
331
438
  async function ensureMise(options = {}) {
332
439
  const homeDir = options.homeDir ?? homedir4();
333
440
  const current = await mise.executableCheck() ?? await mise.executableCheck(join5(homeDir, ".local", "bin", "mise"));
334
441
  if (current)
335
- return { executable: current, action: action("mise", "ready", current) };
336
- progress(options, "Installing mise");
442
+ return { executable: current, action: createSetupAction("mise", "ready", current) };
443
+ reportProgress(options, "Installing mise");
337
444
  const executable = await mise.install({
338
445
  dryRun: options.dryRun,
339
446
  homeDir: options.homeDir,
340
447
  platform: options.platform
341
448
  });
342
- return { executable, action: action("mise", options.dryRun ? "planned" : "installed", executable) };
449
+ return {
450
+ executable,
451
+ action: createSetupAction("mise", options.dryRun ? "planned" : "installed", executable)
452
+ };
343
453
  }
344
454
  async function ensureMiseHooks(miseExecutable, options = {}) {
345
455
  if (options.installMiseHook === false)
346
- return action("mise shell hook", "skipped", "disabled");
456
+ return createSetupAction("mise shell hook", "skipped", "disabled");
347
457
  const result = await mise.hookEnsure(miseExecutable, {
348
458
  dryRun: options.dryRun,
349
459
  homeDir: options.homeDir,
350
460
  shell: options.shell
351
461
  });
352
462
  if (!result.changed)
353
- return action("mise shell hook", "ready", result.path);
354
- return action("mise shell hook", result.planned ? "planned" : "installed", result.path);
463
+ return createSetupAction("mise shell hook", "ready", result.path);
464
+ return createSetupAction("mise shell hook", result.planned ? "planned" : "installed", result.path);
355
465
  }
356
466
  async function ensureMiseDeps(miseExecutable, options = {}) {
357
467
  const canRunMise = Boolean(await mise.executableCheck(miseExecutable));
358
468
  const actions = [];
359
469
  for (const dependency of MISE_DEPENDENCIES) {
360
- const installed = canRunMise && await mise.toolCheckGlobal(miseExecutable, dependency.tool, dependency.minimumMajor);
470
+ const installed = canRunMise && await mise.toolCheckGlobal(miseExecutable, dependency.tool, dependency.minimumVersion);
361
471
  if (installed) {
362
- actions.push(action(dependency.name, "ready", dependency.spec));
472
+ actions.push(createSetupAction(dependency.name, "ready", dependency.spec));
363
473
  continue;
364
474
  }
365
- progress(options, `Installing ${dependency.name} with mise`);
475
+ reportProgress(options, `Installing ${dependency.name} with mise`);
366
476
  if (!options.dryRun)
367
477
  await mise.toolInstallGlobal(miseExecutable, dependency.spec);
368
- actions.push(action(dependency.name, options.dryRun ? "planned" : "installed", dependency.spec));
478
+ actions.push(createSetupAction(dependency.name, options.dryRun ? "planned" : "installed", dependency.spec));
369
479
  }
370
480
  return actions;
371
481
  }
@@ -373,12 +483,12 @@ async function ensurePiPlugins(options = {}) {
373
483
  const actions = await ensurePiPackages(PI_PACKAGES, options);
374
484
  const agentDir = options.agentDir ?? pi.agentDir(options.homeDir);
375
485
  const webSearch = await pi.configEnsure(join5(agentDir, "web-search.json"), (config) => ({ ...config, workflow: "auto-summary" }), options.dryRun);
376
- actions.push(configAction("web search settings", webSearch));
486
+ actions.push(getConfigSetupAction("web search settings", webSearch));
377
487
  const lsp = await pi.configEnsure(join5(agentDir, "pi-lsp.json"), (config) => ({
378
488
  ...config,
379
- progressive: { ...record(config.progressive), enabled: true, inject: "none" }
489
+ progressive: { ...getRecord(config.progressive), enabled: true, inject: "none" }
380
490
  }), options.dryRun);
381
- actions.push(configAction("pi-lsp settings", lsp));
491
+ actions.push(getConfigSetupAction("pi-lsp settings", lsp));
382
492
  return actions;
383
493
  }
384
494
  async function ensurePiSkills(miseExecutable, options = {}) {
@@ -389,17 +499,17 @@ async function ensurePiSkills(miseExecutable, options = {}) {
389
499
  const missing = [];
390
500
  for (const name of source.skills) {
391
501
  if (await pi.skillCheckGlobal(name, agentDir, sharedSkillsDir))
392
- actions.push(action(`pi skill ${name}`, "ready", source.repository));
502
+ actions.push(createSetupAction(`pi skill ${name}`, "ready", source.repository));
393
503
  else
394
504
  missing.push(name);
395
505
  }
396
506
  if (missing.length === 0)
397
507
  continue;
398
- progress(options, `Installing skills from ${source.repository}`);
508
+ reportProgress(options, `Installing skills from ${source.repository}`);
399
509
  if (!options.dryRun)
400
510
  await pi.skillInstallGlobal(miseExecutable, source.repository, missing);
401
511
  for (const name of missing) {
402
- actions.push(action(`pi skill ${name}`, options.dryRun ? "planned" : "installed", source.repository));
512
+ actions.push(createSetupAction(`pi skill ${name}`, options.dryRun ? "planned" : "installed", source.repository));
403
513
  }
404
514
  }
405
515
  return actions;
@@ -431,7 +541,7 @@ async function ensureMcpAdapters(miseExecutable, options = {}) {
431
541
  dryRun: options.dryRun,
432
542
  path: mcp.globalConfigPath(options.homeDir)
433
543
  });
434
- actions.push(configAction("MCP configuration", result));
544
+ actions.push(getConfigSetupAction("MCP configuration", result));
435
545
  return actions;
436
546
  }
437
547
  async function setupPi(options = {}) {
@@ -447,27 +557,6 @@ async function setupPi(options = {}) {
447
557
  restartPi: actions.some((item) => (item.status === "installed" || item.status === "updated") && (item.name.startsWith("pi package ") || item.name.startsWith("pi skill ") || item.name === "MCP configuration" || item.name === "web search settings" || item.name === "pi-lsp settings"))
448
558
  };
449
559
  }
450
- var MISE_DEPENDENCIES = [
451
- { name: "node", tool: "node", spec: "node@22", minimumMajor: 22 },
452
- { name: "zellij", tool: "zellij", spec: "zellij@latest", minimumMajor: 0 },
453
- { name: "helix", tool: "helix", spec: "helix@latest", minimumMajor: 0 },
454
- { name: "tuicr", tool: "github:agavra/tuicr", spec: "github:agavra/tuicr@latest", minimumMajor: 0 },
455
- { name: "context-mode", tool: "npm:context-mode", spec: "npm:context-mode@latest", minimumMajor: 0 }
456
- ];
457
- var PI_PACKAGES = [
458
- "npm:@tintinweb/pi-subagents",
459
- "npm:pi-schedule-prompt",
460
- "npm:@narumitw/pi-btw",
461
- "npm:pi-web-access",
462
- "npm:@gitawego/pi-lsp",
463
- "npm:@juicesharp/rpiv-ask-user-question",
464
- "npm:context-mode"
465
- ];
466
- var PI_SKILL_SOURCES = [
467
- { repository: "arabold/docs-mcp-server", skills: ["docs-manage", "docs-search", "fetch-url"] },
468
- { repository: "AminBlg/SimpleEnglish", skills: ["simple-english"] }
469
- ];
470
- var MCP_ADAPTER_PACKAGE = "npm:pi-mcp-adapter";
471
560
  async function ensurePiPackages(packages, options) {
472
561
  const executable = await pi.executableCheck();
473
562
  if (!executable && !options.dryRun)
@@ -476,65 +565,59 @@ async function ensurePiPackages(packages, options) {
476
565
  const actions = [];
477
566
  for (const source of packages) {
478
567
  if (pi.packageCheck(installed, source)) {
479
- actions.push(action(`pi package ${source}`, "ready", source));
568
+ actions.push(createSetupAction(`pi package ${source}`, "ready", source));
480
569
  continue;
481
570
  }
482
- progress(options, `Installing pi package ${source}`);
571
+ reportProgress(options, `Installing pi package ${source}`);
483
572
  if (!options.dryRun) {
484
573
  await pi.packageInstall(executable, source);
485
574
  installed += `
486
575
  ${source}`;
487
576
  }
488
- actions.push(action(`pi package ${source}`, options.dryRun ? "planned" : "installed", source));
577
+ actions.push(createSetupAction(`pi package ${source}`, options.dryRun ? "planned" : "installed", source));
489
578
  }
490
579
  return actions;
491
580
  }
492
- function configAction(name, result) {
581
+ function getConfigSetupAction(name, result) {
493
582
  if (!result.changed)
494
- return action(name, "ready", result.path);
583
+ return createSetupAction(name, "ready", result.path);
495
584
  if (result.planned)
496
- return action(name, "planned", result.path);
497
- return action(name, result.existed ? "updated" : "installed", result.path);
585
+ return createSetupAction(name, "planned", result.path);
586
+ return createSetupAction(name, result.existed ? "updated" : "installed", result.path);
498
587
  }
499
- function action(name, status, detail) {
588
+ function createSetupAction(name, status, detail) {
500
589
  return { name, status, detail };
501
590
  }
502
- function progress(options, message) {
591
+ function reportProgress(options, message) {
503
592
  options.onProgress?.(message);
504
593
  }
505
- function record(value) {
594
+ function getRecord(value) {
506
595
  return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
507
596
  }
508
597
 
509
598
  // src/tools/setup.ts
510
- var parameters = Type.Object({
511
- issueTracker: Type.Optional(Type.String({
512
- description: "Issue tracker MCP server to configure. Use none unless the user explicitly selects Linear or Jira.",
513
- enum: ["none", "linear", "jira"],
514
- default: "none"
515
- })),
516
- installMiseHook: Type.Optional(Type.Boolean({
517
- description: "Add the mise activation hook to the current shell configuration. Set false only when the user declines.",
518
- default: true
519
- }))
599
+ var setupParametersSchema = z2.object({
600
+ issueTracker: z2.enum(["none", "linear", "jira"]).default("none").describe("Issue tracker MCP server to configure. Use none unless the user explicitly selects Linear or Jira.")
520
601
  });
521
- var diffpiSetupTool = defineTool({
602
+ var setupParameters = z2.toJSONSchema(setupParametersSchema, { io: "input" });
603
+ var diffpiSetupTool = defineTool2({
522
604
  name: "diffpi_setup",
523
605
  label: "diffpi setup",
524
606
  description: "Install or repair the @difflab/pi environment. This mutates user-level tool installations and configuration files.",
525
607
  promptSnippet: "Install or repair @difflab/pi only after the user approves the setup choices",
526
608
  promptGuidelines: [
527
- "Call this tool only when the user explicitly asks to install, configure, or repair the environment.",
528
- "Use ask_user_question for unspecified setup choices before calling this tool.",
529
- 'Use issueTracker="none" unless the user explicitly selects Linear or Jira.',
530
- "Use diffpi_validate instead when the user asks only to inspect or verify setup."
609
+ "Call diffpi_setup only when the user explicitly asks to install, configure, or repair the environment.",
610
+ "Use ask_user_question for unspecified setup choices before calling diffpi_setup.",
611
+ 'Call diffpi_setup with issueTracker="none" unless the user explicitly selects Linear or Jira.',
612
+ "Use diffpi_validate instead of diffpi_setup when the user asks only to inspect or verify setup."
531
613
  ],
532
- parameters,
614
+ parameters: setupParameters,
533
615
  executionMode: "sequential",
534
- async execute(_toolCallId, params, _signal, onUpdate) {
616
+ async execute(_toolCallId, input, _signal, onUpdate) {
617
+ const params = setupParametersSchema.parse(input);
535
618
  const result = await setupPi({
536
- issueTracker: parseIssueTracker(params.issueTracker),
537
- installMiseHook: params.installMiseHook ?? true,
619
+ issueTracker: params.issueTracker,
620
+ installMiseHook: true,
538
621
  onProgress(message) {
539
622
  onUpdate?.({ content: [{ type: "text", text: message }], details: {} });
540
623
  }
@@ -542,35 +625,29 @@ var diffpiSetupTool = defineTool({
542
625
  return formatResult(result, "Setup complete.");
543
626
  }
544
627
  });
545
- var diffpiValidateTool = defineTool({
628
+ var diffpiValidateTool = defineTool2({
546
629
  name: "diffpi_validate",
547
630
  label: "diffpi validate",
548
631
  description: "Inspect the @difflab/pi environment without installing software or changing configuration files.",
549
632
  promptSnippet: "Validate @difflab/pi safely before setup or when the user asks for an environment check",
550
633
  promptGuidelines: [
551
- "Prefer this tool before diffpi_setup when the requested action is unclear.",
552
- "This tool is read-only. Do not describe planned actions as completed changes.",
553
- 'Use issueTracker="none" unless the user explicitly asks to validate Linear or Jira configuration.'
634
+ "Prefer diffpi_validate before diffpi_setup when the requested action is unclear.",
635
+ "diffpi_validate is read-only; do not describe its planned actions as completed changes.",
636
+ 'Call diffpi_validate with issueTracker="none" unless the user asks to validate Linear or Jira configuration.'
554
637
  ],
555
- parameters,
638
+ parameters: setupParameters,
556
639
  executionMode: "sequential",
557
- async execute(_toolCallId, params) {
640
+ async execute(_toolCallId, input) {
641
+ const params = setupParametersSchema.parse(input);
558
642
  const result = await setupPi({
559
- issueTracker: parseIssueTracker(params.issueTracker),
560
- installMiseHook: params.installMiseHook ?? true,
643
+ issueTracker: params.issueTracker,
644
+ installMiseHook: true,
561
645
  dryRun: true
562
646
  });
563
647
  const incomplete = result.actions.some((item) => item.status === "planned");
564
648
  return formatResult(result, incomplete ? "Setup is incomplete." : "Setup is ready.");
565
649
  }
566
650
  });
567
- function parseIssueTracker(value) {
568
- if (!value || value === "none")
569
- return "none";
570
- if (value === "linear" || value === "jira")
571
- return value;
572
- throw new Error(`Unknown issue tracker: ${value}`);
573
- }
574
651
  function formatResult(result, heading) {
575
652
  const changed = result.actions.some((item) => item.status === "installed" || item.status === "updated" || item.status === "planned");
576
653
  const lines = result.actions.map((item) => `${item.status.padEnd(9)} ${item.name}: ${item.detail}`);
@@ -586,9 +663,12 @@ ${lines.join(`
586
663
  }
587
664
 
588
665
  // src/tools/index.ts
589
- var piTools = [diffpiSetupTool, diffpiValidateTool];
666
+ function createPiTools(pi) {
667
+ return [diffpiSetupTool, diffpiValidateTool, createDiffpiReloadTool(pi)];
668
+ }
590
669
  export {
670
+ createDiffpiReloadTool,
671
+ createPiTools,
591
672
  diffpiSetupTool,
592
- diffpiValidateTool,
593
- piTools
673
+ diffpiValidateTool
594
674
  };
@@ -0,0 +1,3 @@
1
+ import { type ExtensionAPI, type ToolDefinition } from '@earendil-works/pi-coding-agent';
2
+ export declare function createDiffpiReloadTool(pi: Pick<ExtensionAPI, 'sendUserMessage'>): ToolDefinition;
3
+ //# sourceMappingURL=reload.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reload.d.ts","sourceRoot":"","sources":["../../src/tools/reload.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,KAAK,YAAY,EAAE,KAAK,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAUrG,wBAAgB,sBAAsB,CAAC,EAAE,EAAE,IAAI,CAAC,YAAY,EAAE,iBAAiB,CAAC,GAAG,cAAc,CAoBhG"}
@@ -1,15 +1,4 @@
1
1
  import { type ToolDefinition } from '@earendil-works/pi-coding-agent';
2
- import { Type } from 'typebox';
3
- import { type SetupResult } from '../setup';
4
- declare const parameters: Type.TObject<{
5
- issueTracker: Type.TOptional<Type.TString>;
6
- installMiseHook: Type.TOptional<Type.TBoolean>;
7
- }>;
8
- export interface ToolDetails {
9
- changed?: boolean;
10
- result?: SetupResult;
11
- }
12
- export declare const diffpiSetupTool: ToolDefinition<typeof parameters, ToolDetails>;
13
- export declare const diffpiValidateTool: ToolDefinition<typeof parameters, ToolDetails>;
14
- export {};
2
+ export declare const diffpiSetupTool: ToolDefinition;
3
+ export declare const diffpiValidateTool: ToolDefinition;
15
4
  //# sourceMappingURL=setup.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../src/tools/setup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,KAAK,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAClF,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAC/B,OAAO,EAAW,KAAK,WAAW,EAAE,MAAM,UAAU,CAAC;AAErD,QAAA,MAAM,UAAU;;;EAed,CAAC;AAEH,MAAM,WAAW,WAAW;IAC1B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,eAAO,MAAM,eAAe,EAAE,cAAc,CAAC,OAAO,UAAU,EAAE,WAAW,CAyBzE,CAAC;AAEH,eAAO,MAAM,kBAAkB,EAAE,cAAc,CAAC,OAAO,UAAU,EAAE,WAAW,CAqB5E,CAAC"}
1
+ {"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../src/tools/setup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,KAAK,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAgBlF,eAAO,MAAM,eAAe,EAAE,cA0B5B,CAAC;AAEH,eAAO,MAAM,kBAAkB,EAAE,cAsB/B,CAAC"}