@oh-my-pi/pi-coding-agent 13.19.0 → 14.0.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.
- package/CHANGELOG.md +277 -2
- package/package.json +86 -20
- package/scripts/format-prompts.ts +2 -2
- package/src/autoresearch/apply-contract-to-state.ts +24 -0
- package/src/autoresearch/contract.ts +0 -44
- package/src/autoresearch/dashboard.ts +1 -2
- package/src/autoresearch/git.ts +91 -0
- package/src/autoresearch/helpers.ts +49 -0
- package/src/autoresearch/index.ts +28 -187
- package/src/autoresearch/prompt.md +26 -9
- package/src/autoresearch/state.ts +0 -6
- package/src/autoresearch/tools/init-experiment.ts +202 -117
- package/src/autoresearch/tools/log-experiment.ts +83 -125
- package/src/autoresearch/tools/run-experiment.ts +48 -10
- package/src/autoresearch/types.ts +2 -2
- package/src/capability/index.ts +4 -2
- package/src/cli/file-processor.ts +3 -3
- package/src/cli/grep-cli.ts +8 -8
- package/src/cli/grievances-cli.ts +78 -0
- package/src/cli/read-cli.ts +67 -0
- package/src/cli/setup-cli.ts +4 -4
- package/src/cli/update-cli.ts +3 -3
- package/src/cli.ts +2 -0
- package/src/commands/grep.ts +6 -1
- package/src/commands/grievances.ts +20 -0
- package/src/commands/read.ts +33 -0
- package/src/commit/agentic/agent.ts +5 -5
- package/src/commit/agentic/index.ts +3 -4
- package/src/commit/agentic/tools/analyze-file.ts +3 -3
- package/src/commit/agentic/validation.ts +1 -1
- package/src/commit/analysis/conventional.ts +4 -4
- package/src/commit/analysis/summary.ts +3 -3
- package/src/commit/changelog/generate.ts +4 -4
- package/src/commit/map-reduce/map-phase.ts +4 -4
- package/src/commit/map-reduce/reduce-phase.ts +4 -4
- package/src/commit/pipeline.ts +3 -4
- package/src/config/model-registry.ts +17 -3
- package/src/config/prompt-templates.ts +44 -226
- package/src/config/resolve-config-value.ts +4 -2
- package/src/config/settings-schema.ts +54 -2
- package/src/config/settings.ts +25 -26
- package/src/dap/client.ts +674 -0
- package/src/dap/config.ts +150 -0
- package/src/dap/defaults.json +211 -0
- package/src/dap/index.ts +4 -0
- package/src/dap/session.ts +1255 -0
- package/src/dap/types.ts +600 -0
- package/src/debug/log-viewer.ts +3 -2
- package/src/discovery/builtin.ts +1 -2
- package/src/discovery/codex.ts +2 -2
- package/src/discovery/github.ts +2 -1
- package/src/discovery/helpers.ts +2 -2
- package/src/discovery/opencode.ts +2 -2
- package/src/edit/diff.ts +818 -0
- package/src/edit/index.ts +309 -0
- package/src/edit/line-hash.ts +67 -0
- package/src/edit/modes/chunk.ts +454 -0
- package/src/{patch → edit/modes}/hashline.ts +741 -361
- package/src/{patch/applicator.ts → edit/modes/patch.ts} +420 -117
- package/src/{patch/fuzzy.ts → edit/modes/replace.ts} +519 -197
- package/src/{patch → edit}/normalize.ts +97 -76
- package/src/{patch/shared.ts → edit/renderer.ts} +181 -108
- package/src/exec/bash-executor.ts +4 -2
- package/src/exec/idle-timeout-watchdog.ts +126 -0
- package/src/exec/non-interactive-env.ts +5 -0
- package/src/extensibility/custom-commands/bundled/ci-green/index.ts +2 -2
- package/src/extensibility/custom-commands/bundled/review/index.ts +36 -15
- package/src/extensibility/custom-commands/loader.ts +1 -2
- package/src/extensibility/custom-tools/loader.ts +34 -11
- package/src/extensibility/extensions/loader.ts +9 -4
- package/src/extensibility/extensions/runner.ts +24 -1
- package/src/extensibility/extensions/types.ts +1 -1
- package/src/extensibility/hooks/loader.ts +5 -6
- package/src/extensibility/hooks/types.ts +1 -1
- package/src/extensibility/plugins/doctor.ts +2 -1
- package/src/extensibility/slash-commands.ts +3 -7
- package/src/index.ts +2 -1
- package/src/internal-urls/docs-index.generated.ts +11 -11
- package/src/ipy/executor.ts +58 -17
- package/src/ipy/gateway-coordinator.ts +6 -4
- package/src/ipy/kernel.ts +45 -22
- package/src/ipy/runtime.ts +2 -2
- package/src/lsp/client.ts +7 -4
- package/src/lsp/clients/lsp-linter-client.ts +4 -4
- package/src/lsp/config.ts +20 -4
- package/src/lsp/defaults.json +688 -154
- package/src/lsp/index.ts +234 -45
- package/src/lsp/lspmux.ts +2 -2
- package/src/lsp/startup-events.ts +13 -0
- package/src/lsp/types.ts +12 -1
- package/src/lsp/utils.ts +8 -1
- package/src/main.ts +102 -46
- package/src/memories/index.ts +4 -5
- package/src/modes/acp/acp-agent.ts +563 -163
- package/src/modes/acp/acp-event-mapper.ts +9 -1
- package/src/modes/acp/acp-mode.ts +4 -2
- package/src/modes/components/agent-dashboard.ts +3 -4
- package/src/modes/components/diff.ts +6 -7
- package/src/modes/components/read-tool-group.ts +6 -12
- package/src/modes/components/session-observer-overlay.ts +21 -12
- package/src/modes/components/settings-defs.ts +5 -0
- package/src/modes/components/tool-execution.ts +1 -1
- package/src/modes/components/welcome.ts +1 -1
- package/src/modes/controllers/btw-controller.ts +2 -2
- package/src/modes/controllers/command-controller.ts +3 -2
- package/src/modes/controllers/input-controller.ts +12 -8
- package/src/modes/index.ts +20 -2
- package/src/modes/interactive-mode.ts +94 -37
- package/src/modes/rpc/host-tools.ts +186 -0
- package/src/modes/rpc/rpc-client.ts +178 -13
- package/src/modes/rpc/rpc-mode.ts +73 -3
- package/src/modes/rpc/rpc-types.ts +53 -1
- package/src/modes/theme/theme.ts +80 -8
- package/src/modes/types.ts +2 -2
- package/src/prompts/review-request.md +6 -0
- package/src/prompts/system/system-prompt.md +2 -1
- package/src/prompts/tools/chunk-edit.md +223 -0
- package/src/prompts/tools/debug.md +43 -0
- package/src/prompts/tools/grep.md +3 -0
- package/src/prompts/tools/lsp.md +5 -5
- package/src/prompts/tools/read-chunk.md +17 -0
- package/src/prompts/tools/read.md +19 -5
- package/src/sdk.ts +190 -154
- package/src/secrets/obfuscator.ts +1 -1
- package/src/session/agent-session.ts +306 -256
- package/src/session/agent-storage.ts +12 -12
- package/src/session/compaction/branch-summarization.ts +3 -3
- package/src/session/compaction/compaction.ts +5 -6
- package/src/session/compaction/utils.ts +3 -3
- package/src/session/history-storage.ts +62 -19
- package/src/session/messages.ts +3 -3
- package/src/session/session-dump-format.ts +203 -0
- package/src/session/session-storage.ts +4 -2
- package/src/session/streaming-output.ts +1 -1
- package/src/session/tool-choice-queue.ts +213 -0
- package/src/slash-commands/builtin-registry.ts +56 -8
- package/src/ssh/connection-manager.ts +2 -2
- package/src/ssh/sshfs-mount.ts +5 -5
- package/src/stt/downloader.ts +4 -4
- package/src/stt/recorder.ts +4 -4
- package/src/stt/transcriber.ts +2 -2
- package/src/system-prompt.ts +21 -13
- package/src/task/agents.ts +5 -6
- package/src/task/commands.ts +2 -5
- package/src/task/executor.ts +4 -4
- package/src/task/index.ts +3 -4
- package/src/task/template.ts +2 -2
- package/src/task/worktree.ts +4 -4
- package/src/tools/ask.ts +2 -3
- package/src/tools/ast-edit.ts +7 -7
- package/src/tools/ast-grep.ts +7 -7
- package/src/tools/auto-generated-guard.ts +36 -41
- package/src/tools/await-tool.ts +2 -2
- package/src/tools/bash.ts +5 -23
- package/src/tools/browser.ts +4 -5
- package/src/tools/calculator.ts +2 -3
- package/src/tools/cancel-job.ts +2 -2
- package/src/tools/checkpoint.ts +3 -3
- package/src/tools/debug.ts +1007 -0
- package/src/tools/exit-plan-mode.ts +2 -3
- package/src/tools/fetch.ts +67 -3
- package/src/tools/find.ts +4 -5
- package/src/tools/fs-cache-invalidation.ts +5 -0
- package/src/tools/gemini-image.ts +13 -5
- package/src/tools/gh.ts +10 -11
- package/src/tools/grep.ts +57 -9
- package/src/tools/index.ts +44 -22
- package/src/tools/inspect-image.ts +4 -4
- package/src/tools/output-meta.ts +1 -1
- package/src/tools/python.ts +19 -6
- package/src/tools/read.ts +198 -67
- package/src/tools/render-mermaid.ts +2 -3
- package/src/tools/render-utils.ts +20 -6
- package/src/tools/renderers.ts +3 -1
- package/src/tools/report-tool-issue.ts +80 -0
- package/src/tools/resolve.ts +70 -39
- package/src/tools/search-tool-bm25.ts +2 -2
- package/src/tools/ssh.ts +2 -2
- package/src/tools/todo-write.ts +2 -2
- package/src/tools/tool-timeouts.ts +1 -0
- package/src/tools/write.ts +5 -6
- package/src/tui/tree-list.ts +3 -1
- package/src/utils/clipboard.ts +80 -0
- package/src/utils/commit-message-generator.ts +2 -3
- package/src/utils/edit-mode.ts +49 -0
- package/src/utils/file-display-mode.ts +6 -5
- package/src/utils/file-mentions.ts +8 -7
- package/src/utils/git.ts +4 -4
- package/src/utils/image-loading.ts +98 -0
- package/src/utils/title-generator.ts +2 -3
- package/src/utils/tools-manager.ts +6 -6
- package/src/web/scrapers/choosealicense.ts +1 -1
- package/src/web/search/index.ts +3 -3
- package/src/autoresearch/command-initialize.md +0 -34
- package/src/patch/diff.ts +0 -433
- package/src/patch/index.ts +0 -888
- package/src/patch/parser.ts +0 -532
- package/src/patch/types.ts +0 -292
- package/src/prompts/agents/oracle.md +0 -77
- package/src/tools/pending-action.ts +0 -49
- package/src/utils/child-process.ts +0 -88
- package/src/utils/frontmatter.ts +0 -117
- package/src/utils/image-input.ts +0 -274
- package/src/utils/mime.ts +0 -53
- package/src/utils/prompt-format.ts +0 -170
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
export type ExecutionAbortReason = "idle-timeout" | "signal";
|
|
2
|
+
|
|
3
|
+
export interface IdleTimeoutWatchdogOptions {
|
|
4
|
+
timeoutMs?: number;
|
|
5
|
+
signal?: AbortSignal;
|
|
6
|
+
hardTimeoutGraceMs: number;
|
|
7
|
+
onAbort?: (reason: ExecutionAbortReason) => void;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export class IdleTimeoutWatchdog {
|
|
11
|
+
#abortController = new AbortController();
|
|
12
|
+
#abortReason?: ExecutionAbortReason;
|
|
13
|
+
#hardTimeoutDeferred = Promise.withResolvers<"hard-timeout">();
|
|
14
|
+
#hardTimeoutGraceMs: number;
|
|
15
|
+
#hardTimeoutTimer?: NodeJS.Timeout;
|
|
16
|
+
#idleTimer?: NodeJS.Timeout;
|
|
17
|
+
#onAbort?: (reason: ExecutionAbortReason) => void;
|
|
18
|
+
#signal?: AbortSignal;
|
|
19
|
+
#signalAbortHandler?: () => void;
|
|
20
|
+
#timeoutMs?: number;
|
|
21
|
+
|
|
22
|
+
constructor(options: IdleTimeoutWatchdogOptions) {
|
|
23
|
+
this.#timeoutMs = options.timeoutMs;
|
|
24
|
+
this.#hardTimeoutGraceMs = options.hardTimeoutGraceMs;
|
|
25
|
+
this.#onAbort = options.onAbort;
|
|
26
|
+
this.#signal = options.signal;
|
|
27
|
+
|
|
28
|
+
if (this.#signal) {
|
|
29
|
+
if (this.#signal.aborted) {
|
|
30
|
+
this.#abort("signal");
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
this.#signalAbortHandler = () => {
|
|
35
|
+
this.#abort("signal");
|
|
36
|
+
};
|
|
37
|
+
this.#signal.addEventListener("abort", this.#signalAbortHandler, { once: true });
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
this.touch();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
get abortedBySignal(): boolean {
|
|
44
|
+
return this.#abortReason === "signal";
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
get hardTimeoutPromise(): Promise<"hard-timeout"> {
|
|
48
|
+
return this.#hardTimeoutDeferred.promise;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
get signal(): AbortSignal {
|
|
52
|
+
return this.#abortController.signal;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
get timedOut(): boolean {
|
|
56
|
+
return this.#abortReason === "idle-timeout";
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
touch(): void {
|
|
60
|
+
if (this.#abortReason || this.#timeoutMs === undefined || this.#timeoutMs <= 0) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (this.#idleTimer) {
|
|
65
|
+
clearTimeout(this.#idleTimer);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
this.#idleTimer = setTimeout(() => {
|
|
69
|
+
this.#abort("idle-timeout");
|
|
70
|
+
}, this.#timeoutMs);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
dispose(): void {
|
|
74
|
+
if (this.#idleTimer) {
|
|
75
|
+
clearTimeout(this.#idleTimer);
|
|
76
|
+
this.#idleTimer = undefined;
|
|
77
|
+
}
|
|
78
|
+
if (this.#hardTimeoutTimer) {
|
|
79
|
+
clearTimeout(this.#hardTimeoutTimer);
|
|
80
|
+
this.#hardTimeoutTimer = undefined;
|
|
81
|
+
}
|
|
82
|
+
if (this.#signal && this.#signalAbortHandler) {
|
|
83
|
+
this.#signal.removeEventListener("abort", this.#signalAbortHandler);
|
|
84
|
+
this.#signalAbortHandler = undefined;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
#abort(reason: ExecutionAbortReason): void {
|
|
89
|
+
if (this.#abortReason) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
this.#abortReason = reason;
|
|
94
|
+
|
|
95
|
+
if (this.#idleTimer) {
|
|
96
|
+
clearTimeout(this.#idleTimer);
|
|
97
|
+
this.#idleTimer = undefined;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (!this.#abortController.signal.aborted) {
|
|
101
|
+
this.#abortController.abort(reason);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
this.#onAbort?.(reason);
|
|
105
|
+
this.#armHardTimeout();
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
#armHardTimeout(): void {
|
|
109
|
+
if (this.#hardTimeoutTimer || this.#hardTimeoutGraceMs <= 0) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
this.#hardTimeoutTimer = setTimeout(() => {
|
|
114
|
+
this.#hardTimeoutDeferred.resolve("hard-timeout");
|
|
115
|
+
}, this.#hardTimeoutGraceMs);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function formatIdleTimeoutMessage(timeoutMs?: number): string {
|
|
120
|
+
if (timeoutMs === undefined) {
|
|
121
|
+
return "Command timed out without output";
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const seconds = Math.max(1, Math.round(timeoutMs / 1000));
|
|
125
|
+
return `Command timed out after ${seconds} seconds without output`;
|
|
126
|
+
}
|
|
@@ -13,6 +13,11 @@ export const NON_INTERACTIVE_ENV: Readonly<Record<string, string>> = {
|
|
|
13
13
|
AWS_PAGER: "",
|
|
14
14
|
HOMEBREW_PAGER: "cat",
|
|
15
15
|
LESS: "FRX",
|
|
16
|
+
// Disable terminal features that can block the process.
|
|
17
|
+
TERM: "dumb",
|
|
18
|
+
GPG_TTY: "not a tty",
|
|
19
|
+
NO_COLOR: "1",
|
|
20
|
+
PYTHONUNBUFFERED: "1",
|
|
16
21
|
// Disable editor and terminal credential prompts.
|
|
17
22
|
GIT_EDITOR: "true",
|
|
18
23
|
VISUAL: "true",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { prompt } from "@oh-my-pi/pi-utils";
|
|
2
2
|
import type { CustomCommand, CustomCommandAPI } from "../../../../extensibility/custom-commands/types";
|
|
3
3
|
import type { HookCommandContext } from "../../../../extensibility/hooks/types";
|
|
4
4
|
import ciGreenRequestTemplate from "../../../../prompts/ci-green-request.md" with { type: "text" };
|
|
@@ -20,6 +20,6 @@ export class GreenCommand implements CustomCommand {
|
|
|
20
20
|
|
|
21
21
|
async execute(_args: string[], _ctx: HookCommandContext): Promise<string> {
|
|
22
22
|
const headTag = await getHeadTag(this.api);
|
|
23
|
-
return
|
|
23
|
+
return prompt.render(ciGreenRequestTemplate, { headTag });
|
|
24
24
|
}
|
|
25
25
|
}
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* rich context for the orchestrating agent to distribute work across
|
|
12
12
|
* multiple reviewer agents based on diff weight and locality.
|
|
13
13
|
*/
|
|
14
|
-
import {
|
|
14
|
+
import { prompt } from "@oh-my-pi/pi-utils";
|
|
15
15
|
import type { CustomCommand, CustomCommandAPI } from "../../../../extensibility/custom-commands/types";
|
|
16
16
|
import type { HookCommandContext } from "../../../../extensibility/hooks/types";
|
|
17
17
|
import reviewRequestTemplate from "../../../../prompts/review-request.md" with { type: "text" };
|
|
@@ -197,7 +197,7 @@ const MAX_FILES_FOR_INLINE_DIFF = 20; // Don't include diff if more files than t
|
|
|
197
197
|
/**
|
|
198
198
|
* Build the full review prompt with diff stats and distribution guidance.
|
|
199
199
|
*/
|
|
200
|
-
function buildReviewPrompt(mode: string, stats: DiffStats, rawDiff: string): string {
|
|
200
|
+
function buildReviewPrompt(mode: string, stats: DiffStats, rawDiff: string, additionalInstructions?: string): string {
|
|
201
201
|
const agentCount = getRecommendedAgentCount(stats);
|
|
202
202
|
const skipDiff = rawDiff.length > MAX_DIFF_CHARS || stats.files.length > MAX_FILES_FOR_INLINE_DIFF;
|
|
203
203
|
const totalLines = stats.totalAdded + stats.totalRemoved;
|
|
@@ -209,7 +209,7 @@ function buildReviewPrompt(mode: string, stats: DiffStats, rawDiff: string): str
|
|
|
209
209
|
hunksPreview: skipDiff ? getDiffPreview(f.hunks, linesPerFile) : "",
|
|
210
210
|
}));
|
|
211
211
|
|
|
212
|
-
return
|
|
212
|
+
return prompt.render(reviewRequestTemplate, {
|
|
213
213
|
mode,
|
|
214
214
|
files: filesWithExt,
|
|
215
215
|
excluded: stats.excluded,
|
|
@@ -221,6 +221,7 @@ function buildReviewPrompt(mode: string, stats: DiffStats, rawDiff: string): str
|
|
|
221
221
|
skipDiff,
|
|
222
222
|
rawDiff: rawDiff.trim(),
|
|
223
223
|
linesPerFile,
|
|
224
|
+
additionalInstructions,
|
|
224
225
|
});
|
|
225
226
|
}
|
|
226
227
|
|
|
@@ -230,17 +231,30 @@ export class ReviewCommand implements CustomCommand {
|
|
|
230
231
|
|
|
231
232
|
constructor(private api: CustomCommandAPI) {}
|
|
232
233
|
|
|
233
|
-
async execute(
|
|
234
|
+
async execute(args: string[], ctx: HookCommandContext): Promise<string | undefined> {
|
|
234
235
|
if (!ctx.hasUI) {
|
|
235
|
-
|
|
236
|
+
const base = "Use the Task tool to run the 'reviewer' agent to review recent code changes.";
|
|
237
|
+
return args.length > 0 ? `${base} Focus: ${args.join(" ")}` : base;
|
|
236
238
|
}
|
|
237
239
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
240
|
+
// Inline args act as additional instructions appended to the generated prompt.
|
|
241
|
+
// When present, skip option 4 (editor) — the args already provide the instructions.
|
|
242
|
+
const extraInstructions = args.length > 0 ? args.join(" ") : undefined;
|
|
243
|
+
|
|
244
|
+
const menuItems = extraInstructions
|
|
245
|
+
? [
|
|
246
|
+
"1. Review against a base branch (PR Style)",
|
|
247
|
+
"2. Review uncommitted changes",
|
|
248
|
+
"3. Review a specific commit",
|
|
249
|
+
]
|
|
250
|
+
: [
|
|
251
|
+
"1. Review against a base branch (PR Style)",
|
|
252
|
+
"2. Review uncommitted changes",
|
|
253
|
+
"3. Review a specific commit",
|
|
254
|
+
"4. Custom review instructions",
|
|
255
|
+
];
|
|
256
|
+
|
|
257
|
+
const mode = await ctx.ui.select("Review Mode", menuItems);
|
|
244
258
|
|
|
245
259
|
if (!mode) return undefined;
|
|
246
260
|
|
|
@@ -282,6 +296,7 @@ export class ReviewCommand implements CustomCommand {
|
|
|
282
296
|
`Reviewing changes between \`${baseBranch}\` and \`${currentBranch}\` (PR-style)`,
|
|
283
297
|
stats,
|
|
284
298
|
diffText,
|
|
299
|
+
extraInstructions,
|
|
285
300
|
);
|
|
286
301
|
}
|
|
287
302
|
|
|
@@ -318,7 +333,12 @@ export class ReviewCommand implements CustomCommand {
|
|
|
318
333
|
return undefined;
|
|
319
334
|
}
|
|
320
335
|
|
|
321
|
-
return buildReviewPrompt(
|
|
336
|
+
return buildReviewPrompt(
|
|
337
|
+
"Reviewing uncommitted changes (staged + unstaged)",
|
|
338
|
+
stats,
|
|
339
|
+
combinedDiff,
|
|
340
|
+
extraInstructions,
|
|
341
|
+
);
|
|
322
342
|
}
|
|
323
343
|
|
|
324
344
|
case 3: {
|
|
@@ -354,7 +374,7 @@ export class ReviewCommand implements CustomCommand {
|
|
|
354
374
|
return undefined;
|
|
355
375
|
}
|
|
356
376
|
|
|
357
|
-
return buildReviewPrompt(`Reviewing commit \`${hash}\``, stats, diffText);
|
|
377
|
+
return buildReviewPrompt(`Reviewing commit \`${hash}\``, stats, diffText, extraInstructions);
|
|
358
378
|
}
|
|
359
379
|
|
|
360
380
|
case 4: {
|
|
@@ -374,11 +394,12 @@ export class ReviewCommand implements CustomCommand {
|
|
|
374
394
|
if (reviewDiff) {
|
|
375
395
|
const stats = parseDiff(reviewDiff);
|
|
376
396
|
// Even if all files filtered, include the custom instructions
|
|
377
|
-
return
|
|
397
|
+
return buildReviewPrompt(
|
|
378
398
|
`Custom review: ${instructions.split("\n")[0].slice(0, 60)}…`,
|
|
379
399
|
stats,
|
|
380
400
|
reviewDiff,
|
|
381
|
-
|
|
401
|
+
instructions,
|
|
402
|
+
);
|
|
382
403
|
}
|
|
383
404
|
|
|
384
405
|
// No diff available, just pass instructions
|
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import * as fs from "node:fs";
|
|
8
8
|
import * as path from "node:path";
|
|
9
|
-
import * as piCodingAgent from "@oh-my-pi/pi-coding-agent";
|
|
10
9
|
import { getAgentDir, getProjectDir, isEnoent, logger } from "@oh-my-pi/pi-utils";
|
|
11
10
|
import * as typebox from "@sinclair/typebox";
|
|
12
11
|
import { getConfigDirs } from "../../config";
|
|
@@ -184,7 +183,7 @@ export async function loadCustomCommands(options: LoadCustomCommandsOptions = {}
|
|
|
184
183
|
exec: (command: string, args: string[], execOptions) =>
|
|
185
184
|
execCommand(command, args, execOptions?.cwd ?? cwd, execOptions),
|
|
186
185
|
typebox,
|
|
187
|
-
pi:
|
|
186
|
+
pi: await import("@oh-my-pi/pi-coding-agent"),
|
|
188
187
|
};
|
|
189
188
|
|
|
190
189
|
// 1. Load bundled commands first (lowest priority - can be overridden)
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* to avoid import resolution issues with custom tools loaded from user directories.
|
|
6
6
|
*/
|
|
7
7
|
import * as path from "node:path";
|
|
8
|
-
import
|
|
8
|
+
import type { AgentToolResult } from "@oh-my-pi/pi-agent-core";
|
|
9
9
|
import { logger } from "@oh-my-pi/pi-utils";
|
|
10
10
|
import * as typebox from "@sinclair/typebox";
|
|
11
11
|
import { toolCapability } from "../../capability/tool";
|
|
@@ -14,7 +14,6 @@ import type { ExecOptions } from "../../exec/exec";
|
|
|
14
14
|
import { execCommand } from "../../exec/exec";
|
|
15
15
|
import type { HookUIContext } from "../../extensibility/hooks/types";
|
|
16
16
|
import { getAllPluginToolPaths } from "../../extensibility/plugins/loader";
|
|
17
|
-
import type { PendingActionStore } from "../../tools/pending-action";
|
|
18
17
|
import { createNoOpUIContext, resolvePath } from "../utils";
|
|
19
18
|
import type { CustomToolAPI, CustomToolFactory, LoadedCustomTool, ToolLoadError } from "./types";
|
|
20
19
|
|
|
@@ -85,7 +84,17 @@ export class CustomToolLoader {
|
|
|
85
84
|
#sharedApi: CustomToolAPI;
|
|
86
85
|
#seenNames: Set<string>;
|
|
87
86
|
|
|
88
|
-
constructor(
|
|
87
|
+
constructor(
|
|
88
|
+
pi: typeof import("@oh-my-pi/pi-coding-agent"),
|
|
89
|
+
cwd: string,
|
|
90
|
+
builtInToolNames: string[],
|
|
91
|
+
pushPendingAction?: (action: {
|
|
92
|
+
label: string;
|
|
93
|
+
sourceToolName: string;
|
|
94
|
+
apply(reason: string): Promise<AgentToolResult<unknown>>;
|
|
95
|
+
reject?(reason: string): Promise<AgentToolResult<unknown> | undefined>;
|
|
96
|
+
}) => void,
|
|
97
|
+
) {
|
|
89
98
|
this.#sharedApi = {
|
|
90
99
|
cwd,
|
|
91
100
|
exec: (command: string, args: string[], options?: ExecOptions) =>
|
|
@@ -94,17 +103,16 @@ export class CustomToolLoader {
|
|
|
94
103
|
hasUI: false,
|
|
95
104
|
logger,
|
|
96
105
|
typebox,
|
|
97
|
-
pi
|
|
106
|
+
pi,
|
|
98
107
|
pushPendingAction: action => {
|
|
99
|
-
if (!
|
|
108
|
+
if (!pushPendingAction) {
|
|
100
109
|
throw new Error("Pending action store unavailable for custom tools in this runtime.");
|
|
101
110
|
}
|
|
102
|
-
|
|
111
|
+
pushPendingAction({
|
|
103
112
|
label: action.label,
|
|
104
113
|
sourceToolName: action.sourceToolName ?? "custom_tool",
|
|
105
114
|
apply: action.apply,
|
|
106
115
|
reject: action.reject,
|
|
107
|
-
details: action.details,
|
|
108
116
|
});
|
|
109
117
|
},
|
|
110
118
|
};
|
|
@@ -155,9 +163,19 @@ export async function loadCustomTools(
|
|
|
155
163
|
pathsWithSources: ToolPathWithSource[],
|
|
156
164
|
cwd: string,
|
|
157
165
|
builtInToolNames: string[],
|
|
158
|
-
|
|
166
|
+
pushPendingAction?: (action: {
|
|
167
|
+
label: string;
|
|
168
|
+
sourceToolName: string;
|
|
169
|
+
apply(reason: string): Promise<AgentToolResult<unknown>>;
|
|
170
|
+
reject?(reason: string): Promise<AgentToolResult<unknown> | undefined>;
|
|
171
|
+
}) => void,
|
|
159
172
|
) {
|
|
160
|
-
const loader = new CustomToolLoader(
|
|
173
|
+
const loader = new CustomToolLoader(
|
|
174
|
+
await import("@oh-my-pi/pi-coding-agent"),
|
|
175
|
+
cwd,
|
|
176
|
+
builtInToolNames,
|
|
177
|
+
pushPendingAction,
|
|
178
|
+
);
|
|
161
179
|
await loader.load(pathsWithSources);
|
|
162
180
|
return {
|
|
163
181
|
tools: loader.tools,
|
|
@@ -182,7 +200,12 @@ export async function discoverAndLoadCustomTools(
|
|
|
182
200
|
configuredPaths: string[],
|
|
183
201
|
cwd: string,
|
|
184
202
|
builtInToolNames: string[],
|
|
185
|
-
|
|
203
|
+
pushPendingAction?: (action: {
|
|
204
|
+
label: string;
|
|
205
|
+
sourceToolName: string;
|
|
206
|
+
apply(reason: string): Promise<AgentToolResult<unknown>>;
|
|
207
|
+
reject?(reason: string): Promise<AgentToolResult<unknown> | undefined>;
|
|
208
|
+
}) => void,
|
|
186
209
|
) {
|
|
187
210
|
const allPathsWithSources: ToolPathWithSource[] = [];
|
|
188
211
|
const seen = new Set<string>();
|
|
@@ -216,5 +239,5 @@ export async function discoverAndLoadCustomTools(
|
|
|
216
239
|
addPath(resolvePath(configPath, cwd), { provider: "config", providerName: "Config", level: "project" });
|
|
217
240
|
}
|
|
218
241
|
|
|
219
|
-
return loadCustomTools(allPathsWithSources, cwd, builtInToolNames,
|
|
242
|
+
return loadCustomTools(allPathsWithSources, cwd, builtInToolNames, pushPendingAction);
|
|
220
243
|
}
|
|
@@ -6,7 +6,6 @@ import * as fs from "node:fs/promises";
|
|
|
6
6
|
import * as path from "node:path";
|
|
7
7
|
import type { ThinkingLevel } from "@oh-my-pi/pi-agent-core";
|
|
8
8
|
import type { ImageContent, Model, TextContent } from "@oh-my-pi/pi-ai";
|
|
9
|
-
import * as piCodingAgent from "@oh-my-pi/pi-coding-agent";
|
|
10
9
|
import type { KeyId } from "@oh-my-pi/pi-tui";
|
|
11
10
|
import { hasFsCode, isEacces, isEnoent, logger } from "@oh-my-pi/pi-utils";
|
|
12
11
|
import type { TSchema } from "@sinclair/typebox";
|
|
@@ -102,7 +101,6 @@ export class ExtensionRuntime implements IExtensionRuntime {
|
|
|
102
101
|
class ConcreteExtensionAPI implements ExtensionAPI, IExtensionRuntime {
|
|
103
102
|
readonly logger = logger;
|
|
104
103
|
readonly typebox = TypeBox;
|
|
105
|
-
readonly pi = piCodingAgent;
|
|
106
104
|
readonly flagValues = new Map<string, boolean | string>();
|
|
107
105
|
readonly pendingProviderRegistrations: Array<{
|
|
108
106
|
name: string;
|
|
@@ -111,6 +109,7 @@ class ConcreteExtensionAPI implements ExtensionAPI, IExtensionRuntime {
|
|
|
111
109
|
}> = [];
|
|
112
110
|
|
|
113
111
|
constructor(
|
|
112
|
+
public readonly pi: typeof import("@oh-my-pi/pi-coding-agent"),
|
|
114
113
|
private readonly extension: Extension,
|
|
115
114
|
private readonly runtime: IExtensionRuntime,
|
|
116
115
|
private readonly cwd: string,
|
|
@@ -265,7 +264,13 @@ async function loadExtension(
|
|
|
265
264
|
}
|
|
266
265
|
|
|
267
266
|
const extension = createExtension(extensionPath, resolvedPath);
|
|
268
|
-
const api = new ConcreteExtensionAPI(
|
|
267
|
+
const api = new ConcreteExtensionAPI(
|
|
268
|
+
await import("@oh-my-pi/pi-coding-agent"),
|
|
269
|
+
extension,
|
|
270
|
+
runtime,
|
|
271
|
+
cwd,
|
|
272
|
+
eventBus,
|
|
273
|
+
);
|
|
269
274
|
await factory(api);
|
|
270
275
|
|
|
271
276
|
return { extension, error: null };
|
|
@@ -286,7 +291,7 @@ export async function loadExtensionFromFactory(
|
|
|
286
291
|
name = "<inline>",
|
|
287
292
|
): Promise<Extension> {
|
|
288
293
|
const extension = createExtension(name, name);
|
|
289
|
-
const api = new ConcreteExtensionAPI(extension, runtime, cwd, eventBus);
|
|
294
|
+
const api = new ConcreteExtensionAPI(await import("@oh-my-pi/pi-coding-agent"), extension, runtime, cwd, eventBus);
|
|
290
295
|
await factory(api);
|
|
291
296
|
return extension;
|
|
292
297
|
}
|
|
@@ -262,6 +262,10 @@ export class ExtensionRunner {
|
|
|
262
262
|
return allFlags;
|
|
263
263
|
}
|
|
264
264
|
|
|
265
|
+
getFlagValues(): Map<string, boolean | string> {
|
|
266
|
+
return new Map(this.runtime.flagValues);
|
|
267
|
+
}
|
|
268
|
+
|
|
265
269
|
setFlagValue(name: string, value: boolean | string): void {
|
|
266
270
|
this.runtime.flagValues.set(name, value);
|
|
267
271
|
}
|
|
@@ -695,7 +699,26 @@ export class ExtensionRunner {
|
|
|
695
699
|
|
|
696
700
|
async emitContext(messages: AgentMessage[]): Promise<AgentMessage[]> {
|
|
697
701
|
const ctx = this.createContext();
|
|
698
|
-
|
|
702
|
+
|
|
703
|
+
// Check if any extensions actually have context handlers before cloning
|
|
704
|
+
let hasContextHandlers = false;
|
|
705
|
+
for (const ext of this.extensions) {
|
|
706
|
+
if (ext.handlers.get("context")?.length) {
|
|
707
|
+
hasContextHandlers = true;
|
|
708
|
+
break;
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
if (!hasContextHandlers) return messages;
|
|
712
|
+
|
|
713
|
+
let currentMessages: AgentMessage[];
|
|
714
|
+
try {
|
|
715
|
+
currentMessages = structuredClone(messages);
|
|
716
|
+
} catch {
|
|
717
|
+
// Messages may contain non-cloneable objects (e.g. in ToolResultMessage.details
|
|
718
|
+
// or ProviderPayload). Fall back to a shallow array clone — extensions should
|
|
719
|
+
// return new message arrays rather than mutating in place.
|
|
720
|
+
currentMessages = [...messages];
|
|
721
|
+
}
|
|
699
722
|
|
|
700
723
|
for (const ext of this.extensions) {
|
|
701
724
|
const handlers = ext.handlers.get("context");
|
|
@@ -28,11 +28,11 @@ import type { Static, TSchema } from "@sinclair/typebox";
|
|
|
28
28
|
import type { Rule } from "../../capability/rule";
|
|
29
29
|
import type { KeybindingsManager } from "../../config/keybindings";
|
|
30
30
|
import type { ModelRegistry } from "../../config/model-registry";
|
|
31
|
+
import type { EditToolDetails } from "../../edit";
|
|
31
32
|
import type { BashResult } from "../../exec/bash-executor";
|
|
32
33
|
import type { ExecOptions, ExecResult } from "../../exec/exec";
|
|
33
34
|
import type { PythonResult } from "../../ipy/executor";
|
|
34
35
|
import type { Theme } from "../../modes/theme/theme";
|
|
35
|
-
import type { EditToolDetails } from "../../patch";
|
|
36
36
|
import type { CompactionPreparation, CompactionResult } from "../../session/compaction";
|
|
37
37
|
import type { CustomMessage } from "../../session/messages";
|
|
38
38
|
import type {
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
* Hook loader - loads TypeScript hook modules using native Bun import.
|
|
3
3
|
*/
|
|
4
4
|
import * as path from "node:path";
|
|
5
|
-
import * as piCodingAgent from "@oh-my-pi/pi-coding-agent";
|
|
6
5
|
import { logger } from "@oh-my-pi/pi-utils";
|
|
7
6
|
import * as typebox from "@sinclair/typebox";
|
|
8
7
|
import { hookCapability } from "../../capability/hook";
|
|
@@ -87,16 +86,16 @@ export interface LoadHooksResult {
|
|
|
87
86
|
* Create a HookAPI instance that collects handlers, renderers, and commands.
|
|
88
87
|
* Returns the API, maps, and functions to set handlers later.
|
|
89
88
|
*/
|
|
90
|
-
function createHookAPI(
|
|
89
|
+
async function createHookAPI(
|
|
91
90
|
handlers: Map<string, HandlerFn[]>,
|
|
92
91
|
cwd: string,
|
|
93
|
-
): {
|
|
92
|
+
): Promise<{
|
|
94
93
|
api: HookAPI;
|
|
95
94
|
messageRenderers: Map<string, HookMessageRenderer>;
|
|
96
95
|
commands: Map<string, RegisteredCommand>;
|
|
97
96
|
setSendMessageHandler: (handler: SendMessageHandler) => void;
|
|
98
97
|
setAppendEntryHandler: (handler: AppendEntryHandler) => void;
|
|
99
|
-
} {
|
|
98
|
+
}> {
|
|
100
99
|
let sendMessageHandler: SendMessageHandler | null = null;
|
|
101
100
|
let appendEntryHandler: AppendEntryHandler | null = null;
|
|
102
101
|
const messageRenderers = new Map<string, HookMessageRenderer>();
|
|
@@ -137,7 +136,7 @@ function createHookAPI(
|
|
|
137
136
|
},
|
|
138
137
|
logger,
|
|
139
138
|
typebox,
|
|
140
|
-
pi:
|
|
139
|
+
pi: await import("@oh-my-pi/pi-coding-agent"),
|
|
141
140
|
} as HookAPI;
|
|
142
141
|
|
|
143
142
|
return {
|
|
@@ -170,7 +169,7 @@ async function loadHook(hookPath: string, cwd: string): Promise<{ hook: LoadedHo
|
|
|
170
169
|
|
|
171
170
|
// Create handlers map and API
|
|
172
171
|
const handlers = new Map<string, HandlerFn[]>();
|
|
173
|
-
const { api, messageRenderers, commands, setSendMessageHandler, setAppendEntryHandler } = createHookAPI(
|
|
172
|
+
const { api, messageRenderers, commands, setSendMessageHandler, setAppendEntryHandler } = await createHookAPI(
|
|
174
173
|
handlers,
|
|
175
174
|
cwd,
|
|
176
175
|
);
|
|
@@ -9,9 +9,9 @@ import type { ImageContent, Message, Model, TextContent, ToolResultMessage } fro
|
|
|
9
9
|
import type { Component, TUI } from "@oh-my-pi/pi-tui";
|
|
10
10
|
import type { Rule } from "../../capability/rule";
|
|
11
11
|
import type { ModelRegistry } from "../../config/model-registry";
|
|
12
|
+
import type { EditToolDetails } from "../../edit";
|
|
12
13
|
import type { ExecOptions, ExecResult } from "../../exec/exec";
|
|
13
14
|
import type { Theme } from "../../modes/theme/theme";
|
|
14
|
-
import type { EditToolDetails } from "../../patch";
|
|
15
15
|
import type { CompactionPreparation, CompactionResult } from "../../session/compaction";
|
|
16
16
|
import type { HookMessage } from "../../session/messages";
|
|
17
17
|
import type {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { $which } from "@oh-my-pi/pi-utils";
|
|
1
2
|
import { theme } from "../../modes/theme/theme";
|
|
2
3
|
import type { DoctorCheck } from "./types";
|
|
3
4
|
|
|
@@ -12,7 +13,7 @@ export async function runDoctorChecks(): Promise<DoctorCheck[]> {
|
|
|
12
13
|
];
|
|
13
14
|
|
|
14
15
|
for (const tool of tools) {
|
|
15
|
-
const path =
|
|
16
|
+
const path = $which(tool.name);
|
|
16
17
|
checks.push({
|
|
17
18
|
name: tool.name,
|
|
18
19
|
status: path ? "ok" : "warning",
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import type { AutocompleteItem } from "@oh-my-pi/pi-tui";
|
|
2
|
+
import { parseFrontmatter, prompt } from "@oh-my-pi/pi-utils";
|
|
2
3
|
import { slashCommandCapability } from "../capability/slash-command";
|
|
3
|
-
import {
|
|
4
|
-
appendInlineArgsFallback,
|
|
5
|
-
renderPromptTemplate,
|
|
6
|
-
templateUsesInlineArgPlaceholders,
|
|
7
|
-
} from "../config/prompt-templates";
|
|
4
|
+
import { appendInlineArgsFallback, templateUsesInlineArgPlaceholders } from "../config/prompt-templates";
|
|
8
5
|
import type { SlashCommand } from "../discovery";
|
|
9
6
|
import { loadCapability } from "../discovery";
|
|
10
7
|
import {
|
|
@@ -14,7 +11,6 @@ import {
|
|
|
14
11
|
} from "../slash-commands/builtin-registry";
|
|
15
12
|
import { EMBEDDED_COMMAND_TEMPLATES } from "../task/commands";
|
|
16
13
|
import { parseCommandArgs, substituteArgs } from "../utils/command-args";
|
|
17
|
-
import { parseFrontmatter } from "../utils/frontmatter";
|
|
18
14
|
|
|
19
15
|
export type SlashCommandSource = "extension" | "prompt" | "skill";
|
|
20
16
|
|
|
@@ -223,7 +219,7 @@ export function expandSlashCommand(text: string, fileCommands: FileSlashCommand[
|
|
|
223
219
|
const argsText = args.join(" ");
|
|
224
220
|
const usesInlineArgPlaceholders = templateUsesInlineArgPlaceholders(fileCommand.content);
|
|
225
221
|
const substituted = substituteArgs(fileCommand.content, args);
|
|
226
|
-
const rendered =
|
|
222
|
+
const rendered = prompt.render(substituted, { args, ARGUMENTS: argsText, arguments: argsText });
|
|
227
223
|
return appendInlineArgsFallback(rendered, argsText, usesInlineArgPlaceholders);
|
|
228
224
|
}
|
|
229
225
|
|
package/src/index.ts
CHANGED
|
@@ -16,6 +16,7 @@ export type * from "./config/prompt-templates";
|
|
|
16
16
|
export * from "./config/prompt-templates";
|
|
17
17
|
export type { RetrySettings, SkillsSettings } from "./config/settings";
|
|
18
18
|
export { Settings, settings } from "./config/settings";
|
|
19
|
+
export * from "./edit/modes/hashline";
|
|
19
20
|
// Custom commands
|
|
20
21
|
export type * from "./extensibility/custom-commands/types";
|
|
21
22
|
export type * from "./extensibility/custom-tools";
|
|
@@ -37,7 +38,6 @@ export * from "./modes";
|
|
|
37
38
|
export * from "./modes/components";
|
|
38
39
|
// Theme utilities for custom tools
|
|
39
40
|
export * from "./modes/theme/theme";
|
|
40
|
-
export * from "./patch/hashline";
|
|
41
41
|
// SDK for programmatic usage
|
|
42
42
|
export * from "./sdk";
|
|
43
43
|
export * from "./session/agent-session";
|
|
@@ -46,6 +46,7 @@ export * from "./session/auth-storage";
|
|
|
46
46
|
// Compaction
|
|
47
47
|
export * from "./session/compaction";
|
|
48
48
|
export * from "./session/messages";
|
|
49
|
+
export * from "./session/session-dump-format";
|
|
49
50
|
export * from "./session/session-manager";
|
|
50
51
|
export * from "./task/executor";
|
|
51
52
|
export type * from "./task/types";
|