@oh-my-pi/pi-coding-agent 16.4.2 → 16.4.3

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.
Files changed (122) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/dist/cli.js +17514 -17799
  3. package/dist/types/advisor/config.d.ts +4 -3
  4. package/dist/types/commit/agentic/agent.d.ts +1 -0
  5. package/dist/types/config/settings-schema.d.ts +28 -0
  6. package/dist/types/extensibility/plugins/legacy-pi-compat.d.ts +10 -22
  7. package/dist/types/lsp/deferred-diagnostics.d.ts +11 -0
  8. package/dist/types/modes/components/move-overlay.d.ts +1 -1
  9. package/dist/types/modes/components/status-line/component.d.ts +3 -0
  10. package/dist/types/modes/components/status-line/types.d.ts +3 -0
  11. package/dist/types/modes/interactive-mode.d.ts +8 -0
  12. package/dist/types/modes/types.d.ts +2 -0
  13. package/dist/types/session/agent-session.d.ts +16 -0
  14. package/dist/types/task/executor.d.ts +31 -0
  15. package/dist/types/tools/__tests__/vibe-render.test.d.ts +1 -0
  16. package/dist/types/tools/browser/cmux/cmux-tab.d.ts +3 -1
  17. package/dist/types/tools/browser/cmux/rpc.d.ts +20 -0
  18. package/dist/types/tools/browser/run-output.d.ts +25 -0
  19. package/dist/types/tools/browser/tab-worker.d.ts +16 -0
  20. package/dist/types/tools/index.d.ts +1 -0
  21. package/dist/types/tools/vibe.d.ts +161 -0
  22. package/dist/types/utils/changelog.d.ts +35 -1
  23. package/dist/types/vibe/runtime.d.ts +124 -0
  24. package/dist/types/vibe/state.d.ts +4 -0
  25. package/dist/types/web/search/provider.d.ts +2 -0
  26. package/dist/types/web/search/providers/bing.d.ts +14 -0
  27. package/dist/types/web/search/providers/browser-headers.d.ts +9 -0
  28. package/dist/types/web/search/providers/browser-page.d.ts +32 -0
  29. package/dist/types/web/search/providers/ecosia.d.ts +14 -0
  30. package/dist/types/web/search/providers/google.d.ts +13 -0
  31. package/dist/types/web/search/providers/mojeek.d.ts +14 -0
  32. package/dist/types/web/search/providers/public.d.ts +37 -0
  33. package/dist/types/web/search/providers/startpage.d.ts +14 -0
  34. package/dist/types/web/search/providers/yahoo.d.ts +14 -0
  35. package/dist/types/web/search/types.d.ts +28 -0
  36. package/package.json +15 -18
  37. package/scripts/build-binary.ts +56 -73
  38. package/scripts/bundle-dist.ts +36 -40
  39. package/scripts/compile-binary.ts +68 -0
  40. package/scripts/generate-docs-index.ts +3 -93
  41. package/scripts/legacy-pi-virtual-module.ts +192 -0
  42. package/src/advisor/__tests__/advisor.test.ts +13 -0
  43. package/src/advisor/__tests__/config.test.ts +36 -0
  44. package/src/advisor/config.ts +11 -8
  45. package/src/commit/agentic/agent.ts +4 -0
  46. package/src/commit/agentic/index.ts +46 -21
  47. package/src/edit/index.ts +10 -76
  48. package/src/exec/non-interactive-env.ts +0 -1
  49. package/src/extensibility/plugins/legacy-pi-compat.ts +328 -162
  50. package/src/extensibility/plugins/legacy-pi-virtual-modules.d.ts +4 -0
  51. package/src/internal-urls/docs-index.ts +2 -1
  52. package/src/internal-urls/skill-protocol.ts +1 -1
  53. package/src/lsp/deferred-diagnostics.ts +66 -0
  54. package/src/main.ts +13 -13
  55. package/src/mcp/transports/stdio.test.ts +45 -1
  56. package/src/mcp/transports/stdio.ts +6 -3
  57. package/src/modes/acp/acp-agent.ts +65 -1
  58. package/src/modes/acp/acp-event-mapper.ts +5 -0
  59. package/src/modes/acp/acp-mode.ts +11 -0
  60. package/src/modes/components/__tests__/move-overlay.test.ts +16 -1
  61. package/src/modes/components/advisor-config.ts +15 -7
  62. package/src/modes/components/move-overlay.ts +2 -3
  63. package/src/modes/components/status-line/component.ts +6 -0
  64. package/src/modes/components/status-line/segments.ts +6 -0
  65. package/src/modes/components/status-line/types.ts +3 -0
  66. package/src/modes/controllers/command-controller.ts +8 -10
  67. package/src/modes/interactive-mode.ts +117 -1
  68. package/src/modes/types.ts +2 -0
  69. package/src/prompts/system/eager-task.md +2 -2
  70. package/src/prompts/system/system-prompt.md +9 -2
  71. package/src/prompts/system/vibe-mode-active.md +23 -0
  72. package/src/prompts/tools/browser.md +3 -3
  73. package/src/prompts/tools/grep.md +1 -1
  74. package/src/prompts/tools/vibe-kill.md +3 -0
  75. package/src/prompts/tools/vibe-list.md +3 -0
  76. package/src/prompts/tools/vibe-send.md +9 -0
  77. package/src/prompts/tools/vibe-spawn.md +10 -0
  78. package/src/prompts/tools/vibe-turn-result.md +19 -0
  79. package/src/prompts/tools/vibe-wait.md +8 -0
  80. package/src/sdk.ts +5 -0
  81. package/src/session/agent-session.ts +103 -13
  82. package/src/session/snapcompact-inline.ts +3 -19
  83. package/src/slash-commands/builtin-registry.ts +24 -8
  84. package/src/task/agents.ts +0 -2
  85. package/src/task/executor.ts +105 -0
  86. package/src/tools/__tests__/vibe-render.test.ts +210 -0
  87. package/src/tools/bash-skill-urls.ts +1 -1
  88. package/src/tools/browser/cmux/cmux-tab.ts +46 -48
  89. package/src/tools/browser/cmux/rpc.ts +50 -0
  90. package/src/tools/browser/run-output.ts +76 -0
  91. package/src/tools/browser/tab-worker.ts +264 -129
  92. package/src/tools/glob.ts +20 -6
  93. package/src/tools/index.ts +1 -0
  94. package/src/tools/read.ts +17 -9
  95. package/src/tools/renderers.ts +6 -0
  96. package/src/tools/vibe.ts +608 -0
  97. package/src/tools/write.ts +15 -2
  98. package/src/utils/changelog.ts +106 -9
  99. package/src/utils/git.ts +0 -1
  100. package/src/utils/title-generator.ts +70 -7
  101. package/src/vibe/runtime.ts +710 -0
  102. package/src/vibe/state.ts +4 -0
  103. package/src/web/search/index.ts +14 -6
  104. package/src/web/search/provider.ts +37 -1
  105. package/src/web/search/providers/bing.ts +197 -0
  106. package/src/web/search/providers/browser-headers.ts +92 -0
  107. package/src/web/search/providers/browser-page.ts +123 -0
  108. package/src/web/search/providers/duckduckgo.ts +13 -34
  109. package/src/web/search/providers/ecosia.ts +178 -0
  110. package/src/web/search/providers/google.ts +193 -0
  111. package/src/web/search/providers/mojeek.ts +206 -0
  112. package/src/web/search/providers/public.ts +201 -0
  113. package/src/web/search/providers/startpage.ts +213 -0
  114. package/src/web/search/providers/yahoo.ts +179 -0
  115. package/src/web/search/types.ts +35 -0
  116. package/dist/types/extensibility/plugins/legacy-pi-bundled-keys.d.ts +0 -10
  117. package/dist/types/extensibility/plugins/legacy-pi-bundled-registry.d.ts +0 -10
  118. package/scripts/generate-legacy-pi-bundled-registry.ts +0 -420
  119. package/src/extensibility/plugins/legacy-pi-bundled-keys.ts +0 -1011
  120. package/src/extensibility/plugins/legacy-pi-bundled-registry.ts +0 -3430
  121. package/src/internal-urls/docs-index.generated.txt +0 -2
  122. package/src/prompts/agents/plan.md +0 -47
@@ -6,7 +6,7 @@ import { detectChangelogBoundaries } from "../../commit/changelog/detect";
6
6
  import { parseUnreleasedSection } from "../../commit/changelog/parse";
7
7
  import { formatCommitMessage } from "../../commit/message";
8
8
  import { resolvePrimaryModel, resolveSmolModel } from "../../commit/model-selection";
9
- import type { CommitCommandArgs, ConventionalAnalysis } from "../../commit/types";
9
+ import type { CommitCommandArgs, ConventionalAnalysis, NumstatEntry } from "../../commit/types";
10
10
  import { ModelRegistry } from "../../config/model-registry";
11
11
  import { Settings } from "../../config/settings";
12
12
  import { discoverAuthStorage, discoverContextFiles } from "../../sdk";
@@ -122,11 +122,10 @@ export async function runAgenticCommit(args: CommitCommandArgs): Promise<void> {
122
122
  }
123
123
 
124
124
  process.stdout.write("● Starting commit agent...\n");
125
- let commitState: CommitAgentState;
126
- let usedFallback = false;
125
+ let agentSessionCompleted = false;
127
126
 
128
127
  try {
129
- commitState = await runCommitAgentSession({
128
+ await runCommitAgentSession({
130
129
  cwd,
131
130
  model: agentModel,
132
131
  thinkingLevel: agentThinkingLevel,
@@ -139,42 +138,67 @@ export async function runAgenticCommit(args: CommitCommandArgs): Promise<void> {
139
138
  requireChangelog: !args.noChangelog && changelogTargets.length > 0,
140
139
  diffText: diff,
141
140
  existingChangelogEntries,
141
+ onComplete: async commitState => {
142
+ agentSessionCompleted = true;
143
+ await completeAgentCommitState(commitState, {
144
+ cwd,
145
+ dryRun: args.dryRun,
146
+ push: args.push,
147
+ noChangelog: args.noChangelog,
148
+ changelogTargets,
149
+ numstat,
150
+ });
151
+ },
142
152
  });
153
+ return;
143
154
  } catch (error) {
155
+ if (agentSessionCompleted) {
156
+ throw error;
157
+ }
144
158
  const errorMessage = error instanceof Error ? error.message : String(error);
145
159
  process.stderr.write(`Agent error: ${errorMessage}\n`);
146
160
  if (error instanceof Error && error.stack && $env.DEBUG) {
147
161
  process.stderr.write(`${error.stack}\n`);
148
162
  }
149
163
  process.stdout.write("● Using fallback commit generation...\n");
150
- commitState = { proposal: generateFallbackProposal(numstat) };
151
- usedFallback = true;
164
+ const fallbackProposal = generateFallbackProposal(numstat);
165
+ await runSingleCommit(fallbackProposal, { cwd, dryRun: args.dryRun, push: args.push });
166
+ return;
152
167
  }
168
+ }
153
169
 
154
- if (!usedFallback && !commitState.proposal && !commitState.splitProposal) {
170
+ async function completeAgentCommitState(
171
+ commitState: CommitAgentState,
172
+ ctx: CommitExecutionContext & {
173
+ noChangelog: boolean;
174
+ changelogTargets: string[];
175
+ numstat: NumstatEntry[];
176
+ },
177
+ ): Promise<void> {
178
+ let usedFallback = false;
179
+ if (!commitState.proposal && !commitState.splitProposal) {
155
180
  if ($env.PI_COMMIT_NO_FALLBACK?.toLowerCase() !== "true") {
156
181
  process.stdout.write("● Agent did not provide proposal, using fallback...\n");
157
- commitState.proposal = generateFallbackProposal(numstat);
182
+ commitState.proposal = generateFallbackProposal(ctx.numstat);
158
183
  usedFallback = true;
159
184
  }
160
185
  }
161
186
 
162
187
  let updatedChangelogFiles: string[] = [];
163
- if (!args.noChangelog && changelogTargets.length > 0 && !usedFallback) {
188
+ if (!ctx.noChangelog && ctx.changelogTargets.length > 0 && !usedFallback) {
164
189
  if (!commitState.changelogProposal) {
165
- process.stderr.write("Commit agent did not provide changelog entries.\n");
166
- return;
190
+ throw new Error("Commit agent did not provide changelog entries.");
167
191
  }
168
192
  process.stdout.write("● Applying changelog entries...\n");
169
193
  const updated = await applyChangelogProposals({
170
- cwd,
194
+ cwd: ctx.cwd,
171
195
  proposals: commitState.changelogProposal.entries,
172
- dryRun: args.dryRun,
196
+ dryRun: ctx.dryRun,
173
197
  onProgress: message => {
174
198
  process.stdout.write(` ├─ ${message}\n`);
175
199
  },
176
200
  });
177
- updatedChangelogFiles = updated.map(filePath => path.relative(cwd, filePath));
201
+ updatedChangelogFiles = updated.map(filePath => path.relative(ctx.cwd, filePath));
178
202
  if (updated.length > 0) {
179
203
  for (const filePath of updated) {
180
204
  process.stdout.write(` └─ ${filePath}\n`);
@@ -185,21 +209,21 @@ export async function runAgenticCommit(args: CommitCommandArgs): Promise<void> {
185
209
  }
186
210
 
187
211
  if (commitState.proposal) {
188
- await runSingleCommit(commitState.proposal, { cwd, dryRun: args.dryRun, push: args.push });
212
+ await runSingleCommit(commitState.proposal, ctx);
189
213
  return;
190
214
  }
191
215
 
192
216
  if (commitState.splitProposal) {
193
217
  await runSplitCommit(commitState.splitProposal, {
194
- cwd,
195
- dryRun: args.dryRun,
196
- push: args.push,
218
+ cwd: ctx.cwd,
219
+ dryRun: ctx.dryRun,
220
+ push: ctx.push,
197
221
  additionalFiles: updatedChangelogFiles,
198
222
  });
199
223
  return;
200
224
  }
201
225
 
202
- process.stderr.write("Commit agent did not provide a proposal.\n");
226
+ throw new Error("Commit agent did not provide a proposal.");
203
227
  }
204
228
 
205
229
  async function runSingleCommit(proposal: CommitProposal, ctx: CommitExecutionContext): Promise<void> {
@@ -212,6 +236,7 @@ async function runSingleCommit(proposal: CommitProposal, ctx: CommitExecutionCon
212
236
  process.stdout.write(`${commitMessage}\n`);
213
237
  return;
214
238
  }
239
+ process.stdout.write("● Creating commit...\n");
215
240
  await git.commit(ctx.cwd, commitMessage);
216
241
  process.stdout.write("Commit created.\n");
217
242
  if (ctx.push) {
@@ -235,8 +260,7 @@ async function runSplitCommit(
235
260
  const plannedFiles = new Set(plan.commits.flatMap(commit => commit.changes.map(change => change.path)));
236
261
  const missingFiles = stagedFiles.filter(file => !plannedFiles.has(file));
237
262
  if (missingFiles.length > 0) {
238
- process.stderr.write(`Split commit plan missing staged files: ${missingFiles.join(", ")}\n`);
239
- return;
263
+ throw new Error(`Split commit plan missing staged files: ${missingFiles.join(", ")}`);
240
264
  }
241
265
 
242
266
  if (ctx.dryRun) {
@@ -268,6 +292,7 @@ async function runSplitCommit(
268
292
  throw new Error(order.error);
269
293
  }
270
294
 
295
+ process.stdout.write("● Creating split commits...\n");
271
296
  const stagedDiff = await git.diff(ctx.cwd, { cached: true, binary: true });
272
297
  await git.stage.reset(ctx.cwd);
273
298
  for (const commitIndex of order) {
package/src/edit/index.ts CHANGED
@@ -4,19 +4,13 @@ import hashlineDescription from "@oh-my-pi/hashline/prompt.md" with { type: "tex
4
4
  import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
5
5
  import type { ToolExample } from "@oh-my-pi/pi-ai";
6
6
  import { prompt } from "@oh-my-pi/pi-utils";
7
- import {
8
- createLspWritethrough,
9
- type FileDiagnosticsResult,
10
- flushLspWritethroughBatch,
11
- type WritethroughCallback,
12
- type WritethroughDeferredHandle,
13
- writethroughNoop,
14
- } from "../lsp";
7
+ import { createLspWritethrough, flushLspWritethroughBatch, type WritethroughCallback, writethroughNoop } from "../lsp";
8
+ import { DeferredDiagnostics } from "../lsp/deferred-diagnostics";
15
9
  import { getDiagnosticsLedger } from "../lsp/diagnostics-ledger";
16
10
  import applyPatchDescription from "../prompts/tools/apply-patch.md" with { type: "text" };
17
11
  import patchDescription from "../prompts/tools/patch.md" with { type: "text" };
18
12
  import replaceDescription from "../prompts/tools/replace.md" with { type: "text" };
19
- import type { DeferredDiagnosticsEntry, ToolSession } from "../tools";
13
+ import type { ToolSession } from "../tools";
20
14
  import { truncateForPrompt } from "../tools/approval";
21
15
  import { isInternalUrlPath } from "../tools/path-utils";
22
16
  import { type EditMode, normalizeEditMode, resolveEditMode } from "../utils/edit-mode";
@@ -384,12 +378,7 @@ export class EditTool implements AgentTool<TInput> {
384
378
  readonly #fuzzyThreshold: number;
385
379
  readonly #writethrough: WritethroughCallback;
386
380
  readonly #editMode?: EditMode;
387
- readonly #dedupDiagnostics: boolean;
388
- readonly #pendingDeferredFetches = new Map<string, AbortController>();
389
- /** Fallback per-path mutation counter used only when the session does not expose
390
- * a shared one. Prefer `session.bumpFileMutationVersion` so write (and any other
391
- * tool) mutating the same file also invalidates pending late-diagnostics. */
392
- readonly #editVersionByPath = new Map<string, number>();
381
+ readonly #deferredDiagnostics: DeferredDiagnostics;
393
382
 
394
383
  constructor(private readonly session: ToolSession) {
395
384
  const {
@@ -401,10 +390,11 @@ export class EditTool implements AgentTool<TInput> {
401
390
  this.#editMode = resolveConfiguredEditMode(envEditVariant);
402
391
  this.#allowFuzzy = resolveAllowFuzzy(session, editFuzzy);
403
392
  this.#fuzzyThreshold = resolveFuzzyThreshold(session, editFuzzyThreshold);
404
- this.#dedupDiagnostics =
393
+ const deduplicateDiagnostics =
405
394
  (session.enableLsp ?? true) &&
406
395
  session.settings.get("lsp.diagnosticsOnEdit") &&
407
396
  session.settings.get("lsp.diagnosticsDeduplicate");
397
+ this.#deferredDiagnostics = new DeferredDiagnostics(session, deduplicateDiagnostics);
408
398
  this.#writethrough = createEditWritethrough(session);
409
399
  }
410
400
 
@@ -549,7 +539,7 @@ export class EditTool implements AgentTool<TInput> {
549
539
  // doubles as the documented full-file overwrite (patch.md <avoid>).
550
540
  allowCreateOverwrite: true,
551
541
  writethrough: tool.#writethrough,
552
- beginDeferredDiagnosticsForPath: p => tool.#beginDeferredDiagnosticsForPath(p),
542
+ beginDeferredDiagnosticsForPath: p => tool.#deferredDiagnostics.begin(p),
553
543
  }),
554
544
  );
555
545
  return executeSinglePathEntries(path, runs, batchRequest, onUpdate, tool.session.cwd, signal);
@@ -588,7 +578,7 @@ export class EditTool implements AgentTool<TInput> {
588
578
  allowFuzzy: tool.#allowFuzzy,
589
579
  fuzzyThreshold: tool.#fuzzyThreshold,
590
580
  writethrough: tool.#writethrough,
591
- beginDeferredDiagnosticsForPath: p => tool.#beginDeferredDiagnosticsForPath(p),
581
+ beginDeferredDiagnosticsForPath: p => tool.#deferredDiagnostics.begin(p),
592
582
  }),
593
583
  };
594
584
  });
@@ -612,7 +602,7 @@ export class EditTool implements AgentTool<TInput> {
612
602
  signal,
613
603
  batchRequest,
614
604
  writethrough: tool.#writethrough,
615
- beginDeferredDiagnosticsForPath: p => tool.#beginDeferredDiagnosticsForPath(p),
605
+ beginDeferredDiagnosticsForPath: p => tool.#deferredDiagnostics.begin(p),
616
606
  });
617
607
  },
618
608
  },
@@ -638,7 +628,7 @@ export class EditTool implements AgentTool<TInput> {
638
628
  allowFuzzy: tool.#allowFuzzy,
639
629
  fuzzyThreshold: tool.#fuzzyThreshold,
640
630
  writethrough: tool.#writethrough,
641
- beginDeferredDiagnosticsForPath: p => tool.#beginDeferredDiagnosticsForPath(p),
631
+ beginDeferredDiagnosticsForPath: p => tool.#deferredDiagnostics.begin(p),
642
632
  }),
643
633
  );
644
634
  return executeSinglePathEntries(path, runs, batchRequest, onUpdate, tool.session.cwd, signal);
@@ -646,60 +636,4 @@ export class EditTool implements AgentTool<TInput> {
646
636
  },
647
637
  }[this.mode];
648
638
  }
649
-
650
- #beginDeferredDiagnosticsForPath(path: string): WritethroughDeferredHandle {
651
- const existingDeferred = this.#pendingDeferredFetches.get(path);
652
- if (existingDeferred) {
653
- existingDeferred.abort();
654
- this.#pendingDeferredFetches.delete(path);
655
- }
656
-
657
- const deferredController = new AbortController();
658
- const editVersion = this.#bumpFileVersion(path);
659
- return {
660
- onDeferredDiagnostics: (lateDiagnostics: FileDiagnosticsResult) => {
661
- this.#pendingDeferredFetches.delete(path);
662
- this.#injectLateDiagnostics(path, lateDiagnostics, editVersion);
663
- },
664
- signal: deferredController.signal,
665
- finalize: (diagnostics: FileDiagnosticsResult | undefined) => {
666
- if (!diagnostics) {
667
- this.#pendingDeferredFetches.set(path, deferredController);
668
- } else {
669
- deferredController.abort();
670
- }
671
- },
672
- };
673
- }
674
-
675
- #injectLateDiagnostics(path: string, diagnostics: FileDiagnosticsResult, editVersion: number): void {
676
- const effective = this.#dedupDiagnostics
677
- ? getDiagnosticsLedger(this.session).reduce(path, diagnostics)
678
- : diagnostics;
679
- if (this.#dedupDiagnostics && effective.messages.length === 0) return;
680
-
681
- const entry: DeferredDiagnosticsEntry = {
682
- path,
683
- summary: effective.summary ?? "",
684
- messages: effective.messages ?? [],
685
- errored: effective.errored,
686
- // Drop at flush time if a later edit to the same file superseded this fetch.
687
- isStale: () => this.#fileVersion(path) !== editVersion,
688
- };
689
- this.session.queueDeferredDiagnostics?.(entry);
690
- }
691
-
692
- /** Bump the file's mutation counter (session-global when available). */
693
- #bumpFileVersion(path: string): number {
694
- if (this.session.bumpFileMutationVersion) return this.session.bumpFileMutationVersion(path);
695
- const next = (this.#editVersionByPath.get(path) ?? 0) + 1;
696
- this.#editVersionByPath.set(path, next);
697
- return next;
698
- }
699
-
700
- /** Read the file's current mutation counter (session-global when available). */
701
- #fileVersion(path: string): number {
702
- if (this.session.getFileMutationVersion) return this.session.getFileMutationVersion(path);
703
- return this.#editVersionByPath.get(path) ?? 0;
704
- }
705
639
  }
@@ -15,7 +15,6 @@ export const NON_INTERACTIVE_ENV: Readonly<Record<string, string>> = {
15
15
  LESS: "FRX",
16
16
  // Disable terminal features that can block the process.
17
17
  TERM: "dumb",
18
- GPG_TTY: "not a tty",
19
18
  NO_COLOR: "1",
20
19
  PYTHONUNBUFFERED: "1",
21
20
  // Disable editor and terminal credential prompts.