@oh-my-pi/pi-coding-agent 14.4.1 → 14.4.4
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/CHANGELOG.md +56 -0
- package/package.json +7 -7
- package/src/cli.ts +0 -1
- package/src/config/prompt-templates.ts +0 -30
- package/src/config/settings-schema.ts +68 -36
- package/src/config/settings.ts +1 -1
- package/src/edit/index.ts +1 -53
- package/src/edit/line-hash.ts +0 -53
- package/src/edit/modes/atom.ts +82 -47
- package/src/edit/modes/hashline.ts +6 -8
- package/src/edit/renderer.ts +6 -8
- package/src/edit/streaming.ts +90 -114
- package/src/export/html/template.generated.ts +1 -1
- package/src/export/html/template.js +10 -15
- package/src/internal-urls/docs-index.generated.ts +1 -2
- package/src/modes/components/session-observer-overlay.ts +635 -295
- package/src/modes/components/settings-defs.ts +1 -5
- package/src/modes/components/tool-execution.ts +2 -5
- package/src/modes/controllers/btw-controller.ts +17 -105
- package/src/modes/controllers/command-controller.ts +16 -5
- package/src/modes/controllers/selector-controller.ts +32 -19
- package/src/modes/controllers/todo-command-controller.ts +537 -0
- package/src/modes/interactive-mode.ts +45 -10
- package/src/modes/types.ts +3 -0
- package/src/modes/utils/ui-helpers.ts +17 -0
- package/src/prompts/system/irc-incoming.md +8 -0
- package/src/prompts/system/subagent-system-prompt.md +8 -0
- package/src/prompts/tools/ast-grep.md +1 -1
- package/src/prompts/tools/atom.md +37 -26
- package/src/prompts/tools/bash.md +2 -2
- package/src/prompts/tools/grep.md +2 -5
- package/src/prompts/tools/irc.md +49 -0
- package/src/prompts/tools/job.md +11 -0
- package/src/prompts/tools/read.md +12 -13
- package/src/prompts/tools/task.md +1 -1
- package/src/prompts/tools/todo-write.md +14 -5
- package/src/registry/agent-registry.ts +139 -0
- package/src/sdk.ts +35 -0
- package/src/session/agent-session.ts +226 -6
- package/src/session/session-manager.ts +13 -0
- package/src/session/session-storage.ts +4 -0
- package/src/session/streaming-output.ts +1 -1
- package/src/slash-commands/builtin-registry.ts +32 -0
- package/src/task/executor.ts +14 -0
- package/src/tools/bash.ts +1 -1
- package/src/tools/fetch.ts +18 -6
- package/src/tools/fs-cache-invalidation.ts +0 -5
- package/src/tools/grep.ts +4 -124
- package/src/tools/index.ts +12 -6
- package/src/tools/irc.ts +258 -0
- package/src/tools/job.ts +489 -0
- package/src/tools/match-line-format.ts +7 -6
- package/src/tools/output-meta.ts +1 -1
- package/src/tools/read.ts +36 -126
- package/src/tools/renderers.ts +2 -0
- package/src/tools/todo-write.ts +243 -12
- package/src/utils/edit-mode.ts +1 -2
- package/src/utils/file-display-mode.ts +0 -3
- package/src/web/search/index.ts +2 -2
- package/src/web/search/provider.ts +3 -0
- package/src/web/search/providers/searxng.ts +238 -0
- package/src/web/search/types.ts +3 -1
- package/src/cli/read-cli.ts +0 -67
- package/src/commands/read.ts +0 -33
- package/src/edit/modes/chunk.ts +0 -832
- package/src/prompts/tools/cancel-job.md +0 -5
- package/src/prompts/tools/chunk-edit.md +0 -158
- package/src/prompts/tools/poll.md +0 -5
- package/src/prompts/tools/read-chunk.md +0 -73
- package/src/tools/cancel-job.ts +0 -95
- package/src/tools/poll-tool.ts +0 -173
|
@@ -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" },
|
|
@@ -353,6 +348,7 @@ const OPTION_PROVIDERS: Partial<Record<SettingPath, OptionProvider>> = {
|
|
|
353
348
|
{ value: "kagi", label: "Kagi", description: "Requires KAGI_API_KEY and Kagi Search API beta access" },
|
|
354
349
|
{ value: "synthetic", label: "Synthetic", description: "Requires SYNTHETIC_API_KEY" },
|
|
355
350
|
{ value: "parallel", label: "Parallel", description: "Requires PARALLEL_API_KEY" },
|
|
351
|
+
{ value: "searxng", label: "SearXNG", description: "Self-hosted metasearch; set searxng.endpoint" },
|
|
356
352
|
],
|
|
357
353
|
"providers.image": [
|
|
358
354
|
{
|
|
@@ -109,7 +109,7 @@ export class ToolExecutionComponent extends Container {
|
|
|
109
109
|
isError?: boolean;
|
|
110
110
|
details?: any;
|
|
111
111
|
};
|
|
112
|
-
// Edit preview state
|
|
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.
|
|
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
|
|
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
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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
|
-
|
|
96
|
-
|
|
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;
|
|
@@ -28,6 +28,7 @@ import { buildHotkeysMarkdown } from "../../modes/utils/hotkeys-markdown";
|
|
|
28
28
|
import { buildToolsMarkdown } from "../../modes/utils/tools-markdown";
|
|
29
29
|
import type { AsyncJobSnapshotItem } from "../../session/agent-session";
|
|
30
30
|
import type { AuthStorage } from "../../session/auth-storage";
|
|
31
|
+
import type { NewSessionOptions } from "../../session/session-manager";
|
|
31
32
|
import { outputMeta } from "../../tools/output-meta";
|
|
32
33
|
import { resolveToCwd, stripOuterDoubleQuotes } from "../../tools/path-utils";
|
|
33
34
|
import { replaceTabs } from "../../tools/render-utils";
|
|
@@ -573,7 +574,7 @@ export class CommandController {
|
|
|
573
574
|
this.ctx.showError("Usage: /memory <view|clear|reset|enqueue|rebuild>");
|
|
574
575
|
}
|
|
575
576
|
|
|
576
|
-
async
|
|
577
|
+
async #runNewSessionFlow(options?: NewSessionOptions, label: string = "New session started"): Promise<void> {
|
|
577
578
|
if (this.ctx.loadingAnimation) {
|
|
578
579
|
this.ctx.loadingAnimation.stop();
|
|
579
580
|
this.ctx.loadingAnimation = undefined;
|
|
@@ -586,7 +587,7 @@ export class CommandController {
|
|
|
586
587
|
await Bun.sleep(10);
|
|
587
588
|
}
|
|
588
589
|
}
|
|
589
|
-
await this.ctx.session.newSession();
|
|
590
|
+
if (!(await this.ctx.session.newSession(options))) return;
|
|
590
591
|
this.ctx.resetObserverRegistry();
|
|
591
592
|
setSessionTerminalTitle(
|
|
592
593
|
this.ctx.sessionManager.getSessionName(),
|
|
@@ -608,13 +609,23 @@ export class CommandController {
|
|
|
608
609
|
this.ctx.pendingTools.clear();
|
|
609
610
|
|
|
610
611
|
this.ctx.chatContainer.addChild(new Spacer(1));
|
|
611
|
-
this.ctx.chatContainer.addChild(
|
|
612
|
-
new Text(`${theme.fg("accent", `${theme.status.success} New session started`)}`, 1, 1),
|
|
613
|
-
);
|
|
612
|
+
this.ctx.chatContainer.addChild(new Text(`${theme.fg("accent", `${theme.status.success} ${label}`)}`, 1, 1));
|
|
614
613
|
await this.ctx.reloadTodos();
|
|
615
614
|
this.ctx.ui.requestRender();
|
|
616
615
|
}
|
|
617
616
|
|
|
617
|
+
async handleClearCommand(): Promise<void> {
|
|
618
|
+
await this.#runNewSessionFlow();
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
async handleDropCommand(): Promise<void> {
|
|
622
|
+
if (!this.ctx.sessionManager.getSessionFile()) {
|
|
623
|
+
this.ctx.showError("Nothing to drop (in-memory session)");
|
|
624
|
+
return;
|
|
625
|
+
}
|
|
626
|
+
await this.#runNewSessionFlow({ drop: true }, "Session dropped");
|
|
627
|
+
}
|
|
628
|
+
|
|
618
629
|
async handleForkCommand(): Promise<void> {
|
|
619
630
|
if (this.ctx.session.isStreaming) {
|
|
620
631
|
this.ctx.showWarning("Wait for the current response to finish or abort it before forking.");
|
|
@@ -1,14 +1,17 @@
|
|
|
1
|
+
import * as os from "node:os";
|
|
2
|
+
import * as path from "node:path";
|
|
1
3
|
import { ThinkingLevel } from "@oh-my-pi/pi-agent-core";
|
|
2
4
|
import { getOAuthProviders, type OAuthProvider } from "@oh-my-pi/pi-ai";
|
|
3
|
-
import type { Component } from "@oh-my-pi/pi-tui";
|
|
5
|
+
import type { Component, OverlayHandle } from "@oh-my-pi/pi-tui";
|
|
4
6
|
import { Input, Loader, Spacer, Text } from "@oh-my-pi/pi-tui";
|
|
5
|
-
import { getAgentDbPath, getProjectDir } from "@oh-my-pi/pi-utils";
|
|
7
|
+
import { getAgentDbPath, getConfigDirName, getProjectDir } from "@oh-my-pi/pi-utils";
|
|
8
|
+
import { invalidate as invalidateFsCache } from "../../capability/fs";
|
|
6
9
|
import { getRoleInfo } from "../../config/model-registry";
|
|
7
10
|
import { formatModelSelectorValue } from "../../config/model-resolver";
|
|
8
11
|
import { settings } from "../../config/settings";
|
|
9
12
|
import { DebugSelectorComponent } from "../../debug";
|
|
10
13
|
import { disableProvider, enableProvider } from "../../discovery";
|
|
11
|
-
import {
|
|
14
|
+
import { clearClaudePluginRootsCache, resolveActiveProjectRegistryPath } from "../../discovery/helpers";
|
|
12
15
|
import {
|
|
13
16
|
getInstalledPluginsRegistryPath,
|
|
14
17
|
getMarketplacesCacheDir,
|
|
@@ -440,7 +443,13 @@ export class SelectorController {
|
|
|
440
443
|
projectInstalledRegistryPath: (await resolveActiveProjectRegistryPath(getProjectDir())) ?? undefined,
|
|
441
444
|
marketplacesCacheDir: getMarketplacesCacheDir(),
|
|
442
445
|
pluginsCacheDir: getPluginsCacheDir(),
|
|
443
|
-
clearPluginRootsCache:
|
|
446
|
+
clearPluginRootsCache: (extraPaths?: readonly string[]) => {
|
|
447
|
+
const home = os.homedir();
|
|
448
|
+
invalidateFsCache(path.join(home, ".claude", "plugins", "installed_plugins.json"));
|
|
449
|
+
invalidateFsCache(path.join(home, getConfigDirName(), "plugins", "installed_plugins.json"));
|
|
450
|
+
for (const p of extraPaths ?? []) invalidateFsCache(p);
|
|
451
|
+
clearClaudePluginRootsCache();
|
|
452
|
+
},
|
|
444
453
|
});
|
|
445
454
|
|
|
446
455
|
const [marketplaces, installed] = await Promise.all([mgr.listMarketplaces(), mgr.listInstalledPlugins()]);
|
|
@@ -969,25 +978,29 @@ export class SelectorController {
|
|
|
969
978
|
|
|
970
979
|
showSessionObserver(registry: SessionObserverRegistry): void {
|
|
971
980
|
const observeKeys = this.ctx.keybindings.getKeys("app.session.observe");
|
|
981
|
+
let cleanup: (() => void) | undefined;
|
|
982
|
+
let overlayHandle: OverlayHandle | undefined;
|
|
972
983
|
|
|
973
|
-
|
|
974
|
-
|
|
984
|
+
const done = () => {
|
|
985
|
+
cleanup?.();
|
|
986
|
+
overlayHandle?.hide();
|
|
987
|
+
this.ctx.ui.requestRender();
|
|
988
|
+
};
|
|
975
989
|
|
|
976
|
-
|
|
977
|
-
registry,
|
|
978
|
-
() => {
|
|
979
|
-
cleanup?.();
|
|
980
|
-
done();
|
|
981
|
-
},
|
|
982
|
-
observeKeys,
|
|
983
|
-
);
|
|
990
|
+
const selector = new SessionObserverOverlayComponent(registry, done, observeKeys);
|
|
984
991
|
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
992
|
+
cleanup = registry.onChange(() => {
|
|
993
|
+
selector.refreshFromRegistry();
|
|
994
|
+
this.ctx.ui.requestRender();
|
|
995
|
+
});
|
|
989
996
|
|
|
990
|
-
|
|
997
|
+
overlayHandle = this.ctx.ui.showOverlay(selector, {
|
|
998
|
+
anchor: "bottom-center",
|
|
999
|
+
width: "100%",
|
|
1000
|
+
maxHeight: "100%",
|
|
1001
|
+
margin: 0,
|
|
991
1002
|
});
|
|
1003
|
+
this.ctx.ui.setFocus(selector);
|
|
1004
|
+
this.ctx.ui.requestRender();
|
|
992
1005
|
}
|
|
993
1006
|
}
|