@oh-my-pi/pi-coding-agent 14.4.0 → 14.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 (67) hide show
  1. package/CHANGELOG.md +70 -0
  2. package/package.json +7 -7
  3. package/src/cli.ts +0 -1
  4. package/src/config/prompt-templates.ts +1 -31
  5. package/src/config/settings-schema.ts +27 -37
  6. package/src/config/settings.ts +1 -1
  7. package/src/edit/index.ts +1 -53
  8. package/src/edit/line-hash.ts +13 -63
  9. package/src/edit/modes/atom.ts +334 -64
  10. package/src/edit/modes/hashline.ts +19 -26
  11. package/src/edit/renderer.ts +6 -8
  12. package/src/edit/streaming.ts +90 -114
  13. package/src/export/html/template.generated.ts +1 -1
  14. package/src/export/html/template.js +10 -15
  15. package/src/internal-urls/docs-index.generated.ts +1 -2
  16. package/src/lsp/defaults.json +142 -652
  17. package/src/modes/components/session-selector.ts +3 -3
  18. package/src/modes/components/settings-defs.ts +0 -5
  19. package/src/modes/components/tool-execution.ts +2 -5
  20. package/src/modes/controllers/btw-controller.ts +17 -105
  21. package/src/modes/controllers/todo-command-controller.ts +537 -0
  22. package/src/modes/interactive-mode.ts +35 -9
  23. package/src/modes/types.ts +2 -0
  24. package/src/modes/utils/ui-helpers.ts +17 -0
  25. package/src/prompts/system/irc-incoming.md +8 -0
  26. package/src/prompts/system/subagent-system-prompt.md +8 -0
  27. package/src/prompts/tools/ast-edit.md +1 -1
  28. package/src/prompts/tools/ast-grep.md +1 -0
  29. package/src/prompts/tools/atom.md +55 -53
  30. package/src/prompts/tools/bash.md +2 -2
  31. package/src/prompts/tools/grep.md +2 -5
  32. package/src/prompts/tools/irc.md +49 -0
  33. package/src/prompts/tools/job.md +11 -0
  34. package/src/prompts/tools/read.md +12 -13
  35. package/src/prompts/tools/task.md +1 -1
  36. package/src/prompts/tools/todo-write.md +14 -5
  37. package/src/registry/agent-registry.ts +139 -0
  38. package/src/sdk.ts +35 -0
  39. package/src/session/agent-session.ts +217 -5
  40. package/src/session/session-manager.ts +4 -1
  41. package/src/session/streaming-output.ts +1 -1
  42. package/src/slash-commands/builtin-registry.ts +24 -0
  43. package/src/task/executor.ts +14 -0
  44. package/src/tools/bash.ts +1 -1
  45. package/src/tools/fetch.ts +18 -6
  46. package/src/tools/fs-cache-invalidation.ts +0 -5
  47. package/src/tools/grep.ts +5 -125
  48. package/src/tools/index.ts +12 -6
  49. package/src/tools/irc.ts +258 -0
  50. package/src/tools/job.ts +489 -0
  51. package/src/tools/match-line-format.ts +8 -7
  52. package/src/tools/output-meta.ts +1 -1
  53. package/src/tools/read.ts +37 -131
  54. package/src/tools/renderers.ts +2 -0
  55. package/src/tools/todo-write.ts +243 -12
  56. package/src/tools/write.ts +2 -2
  57. package/src/utils/edit-mode.ts +1 -2
  58. package/src/utils/file-display-mode.ts +0 -3
  59. package/src/cli/read-cli.ts +0 -67
  60. package/src/commands/read.ts +0 -33
  61. package/src/edit/modes/chunk.ts +0 -832
  62. package/src/prompts/tools/cancel-job.md +0 -5
  63. package/src/prompts/tools/chunk-edit.md +0 -158
  64. package/src/prompts/tools/poll.md +0 -5
  65. package/src/prompts/tools/read-chunk.md +0 -73
  66. package/src/tools/cancel-job.ts +0 -95
  67. package/src/tools/poll-tool.ts +0 -173
@@ -10,6 +10,7 @@ import {
10
10
  truncateToWidth,
11
11
  visibleWidth,
12
12
  } from "@oh-my-pi/pi-tui";
13
+ import { formatBytes } from "@oh-my-pi/pi-utils";
13
14
  import { theme } from "../../modes/theme/theme";
14
15
  import { matchesAppInterrupt } from "../../modes/utils/keybinding-matchers";
15
16
  import type { SessionInfo } from "../../session/session-manager";
@@ -157,10 +158,9 @@ class SessionList implements Component {
157
158
  lines.push(messageLine);
158
159
  }
159
160
 
160
- // Metadata line: date + message count
161
+ // Metadata line: date + file size
161
162
  const modified = formatDate(session.modified);
162
- const msgCount = `${session.messageCount} message${session.messageCount !== 1 ? "s" : ""}`;
163
- const metadata = ` ${modified} ${theme.sep.dot} ${msgCount}`;
163
+ const metadata = ` ${modified} ${theme.sep.dot} ${formatBytes(session.size)}`;
164
164
  const metadataLine = theme.fg("dim", truncateToWidth(metadata, width));
165
165
 
166
166
  lines.push(metadataLine);
@@ -298,11 +298,6 @@ const OPTION_PROVIDERS: Partial<Record<SettingPath, OptionProvider>> = {
298
298
  { value: "1000", label: "1000 lines" },
299
299
  { value: "5000", label: "5000 lines" },
300
300
  ],
301
- "read.anchorstyle": [
302
- { value: "full", label: "Full", description: "Show the kind prefix and identifier" },
303
- { value: "kind", label: "Kind", description: "Show only the kind prefix plus checksum" },
304
- { value: "bare", label: "Bare", description: "Show only the checksum" },
305
- ],
306
301
  // Todo auto-clear delay
307
302
  "tasks.todoClearDelay": [
308
303
  { value: "0", label: "Instant" },
@@ -109,7 +109,7 @@ export class ToolExecutionComponent extends Container {
109
109
  isError?: boolean;
110
110
  details?: any;
111
111
  };
112
- // Edit preview state (single-file for legacy modes, multi-file for chunk)
112
+ // Edit preview state
113
113
  #editMode?: EditMode;
114
114
  #editDiffPreview?: PerFileDiffPreview[];
115
115
  #editDiffScheduleTimer?: NodeJS.Timeout;
@@ -639,10 +639,7 @@ export class ToolExecutionComponent extends Container {
639
639
  return this.#args;
640
640
  }
641
641
  // Single-file previews feed the existing `previewDiff` channel consumed
642
- // by `formatStreamingDiff` in the renderer. Multi-file previews are
643
- // piped via `renderContext.perFileDiffPreview`, so the args we hand to
644
- // `renderCall` only need the first file's diff to preserve prior
645
- // single-file behavior.
642
+ // by `formatStreamingDiff` in the renderer.
646
643
  const first = previews[0];
647
644
  if (!first?.diff) {
648
645
  return this.#args;
@@ -1,8 +1,5 @@
1
- import type { AgentMessage } from "@oh-my-pi/pi-agent-core";
2
- import { type AssistantMessage, type Context, streamSimple } from "@oh-my-pi/pi-ai";
3
1
  import { prompt } from "@oh-my-pi/pi-utils";
4
2
  import btwUserPrompt from "../../prompts/system/btw-user.md" with { type: "text" };
5
- import { toReasoningEffort } from "../../thinking";
6
3
  import { BtwPanelComponent } from "../components/btw-panel";
7
4
  import type { InteractiveModeContext } from "../types";
8
5
 
@@ -14,14 +11,8 @@ interface BtwRequest {
14
11
 
15
12
  export class BtwController {
16
13
  #activeRequest: BtwRequest | undefined;
17
- readonly #streamFn: typeof streamSimple;
18
14
 
19
- constructor(
20
- private readonly ctx: InteractiveModeContext,
21
- options?: { streamFn?: typeof streamSimple },
22
- ) {
23
- this.#streamFn = options?.streamFn ?? streamSimple;
24
- }
15
+ constructor(private readonly ctx: InteractiveModeContext) {}
25
16
 
26
17
  hasActiveRequest(): boolean {
27
18
  return this.#activeRequest !== undefined;
@@ -61,64 +52,29 @@ export class BtwController {
61
52
  this.ctx.btwContainer.addChild(request.component);
62
53
  this.ctx.ui.requestRender();
63
54
  this.#activeRequest = request;
64
- void this.#runRequest(request, model);
55
+ void this.#runRequest(request);
65
56
  }
66
57
 
67
- async #runRequest(
68
- request: BtwRequest,
69
- model: NonNullable<InteractiveModeContext["session"]["model"]>,
70
- ): Promise<void> {
58
+ async #runRequest(request: BtwRequest): Promise<void> {
71
59
  try {
72
- const apiKey = await this.ctx.session.modelRegistry.getApiKey(model, this.ctx.session.sessionId);
73
- if (!apiKey) {
74
- throw new Error(`No API key for provider: ${model.provider}`);
75
- }
76
-
77
- const llmMessages = await this.ctx.session.convertMessagesToLlm(
78
- [...this.#buildMessageSnapshot(), this.#buildQuestionMessage(request.question)],
79
- request.abortController.signal,
80
- );
81
- const context: Context = {
82
- systemPrompt: this.ctx.session.systemPrompt,
83
- messages: llmMessages,
84
- };
85
- const options = this.ctx.session.prepareSimpleStreamOptions({
86
- apiKey,
87
- sessionId: this.ctx.session.sessionId,
88
- reasoning: toReasoningEffort(this.ctx.session.thinkingLevel),
89
- serviceTier: this.ctx.session.serviceTier,
60
+ const promptText = prompt.render(btwUserPrompt, { question: request.question });
61
+ const { replyText } = await this.ctx.session.runEphemeralTurn({
62
+ promptText,
63
+ onTextDelta: delta => {
64
+ if (this.#isActiveRequest(request)) {
65
+ request.component.appendText(delta);
66
+ }
67
+ },
90
68
  signal: request.abortController.signal,
91
- toolChoice: "none",
92
69
  });
93
- const stream = this.#streamFn(model, context, options);
94
70
 
95
- for await (const event of stream) {
96
- if (!this.#isActiveRequest(request)) {
97
- return;
98
- }
99
- if (event.type === "text_delta") {
100
- request.component.appendText(event.delta);
101
- continue;
102
- }
103
- if (event.type === "done") {
104
- const finalText = this.#assistantText(event.message);
105
- if (finalText) {
106
- request.component.setAnswer(finalText);
107
- }
108
- request.component.markComplete();
109
- return;
110
- }
111
- if (event.type === "error") {
112
- if (event.reason === "aborted" || request.abortController.signal.aborted) {
113
- request.component.markAborted();
114
- } else {
115
- request.component.markError(
116
- this.#assistantText(event.error) || event.error.errorMessage || "BTW request failed.",
117
- );
118
- }
119
- return;
120
- }
71
+ if (!this.#isActiveRequest(request)) {
72
+ return;
121
73
  }
74
+ if (replyText) {
75
+ request.component.setAnswer(replyText);
76
+ }
77
+ request.component.markComplete();
122
78
  } catch (error) {
123
79
  if (!this.#isActiveRequest(request)) {
124
80
  return;
@@ -131,50 +87,6 @@ export class BtwController {
131
87
  }
132
88
  }
133
89
 
134
- #buildQuestionMessage(question: string): AgentMessage {
135
- return {
136
- role: "user",
137
- content: [
138
- {
139
- type: "text",
140
- text: prompt.render(btwUserPrompt, { question }),
141
- },
142
- ],
143
- attribution: "user",
144
- timestamp: Date.now(),
145
- };
146
- }
147
-
148
- #buildMessageSnapshot(): AgentMessage[] {
149
- const messages = this.ctx.session.messages.slice();
150
- if (!this.ctx.session.isStreaming || !this.ctx.streamingMessage) {
151
- return messages;
152
- }
153
- const streamingText = this.ctx.extractAssistantText(this.ctx.streamingMessage);
154
- const lastMessage = messages.at(-1);
155
- if (!streamingText) {
156
- return lastMessage?.role === "assistant" ? messages.slice(0, -1) : messages;
157
- }
158
- const normalizedStreamingMessage: AssistantMessage = {
159
- ...this.ctx.streamingMessage,
160
- content: [{ type: "text", text: streamingText }],
161
- };
162
- if (lastMessage?.role === "assistant") {
163
- return [...messages.slice(0, -1), normalizedStreamingMessage];
164
- }
165
- return [...messages, normalizedStreamingMessage];
166
- }
167
-
168
- #assistantText(message: AssistantMessage): string {
169
- let text = "";
170
- for (const content of message.content) {
171
- if (content.type === "text") {
172
- text += content.text;
173
- }
174
- }
175
- return text.trim();
176
- }
177
-
178
90
  #closeActiveRequest(options: { abort: boolean }): void {
179
91
  const request = this.#activeRequest;
180
92
  if (!request) return;