@oh-my-pi/pi-coding-agent 14.3.0 → 14.4.1
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 +98 -1
- package/package.json +7 -7
- package/src/autoresearch/prompt.md +1 -1
- package/src/commit/agentic/prompts/analyze-file.md +1 -1
- package/src/config/model-registry.ts +67 -15
- package/src/config/prompt-templates.ts +5 -5
- package/src/config/settings-schema.ts +4 -4
- package/src/cursor.ts +3 -8
- package/src/discovery/helpers.ts +3 -3
- package/src/edit/diff.ts +50 -47
- package/src/edit/index.ts +86 -57
- package/src/edit/line-hash.ts +743 -24
- package/src/edit/modes/apply-patch.ts +0 -9
- package/src/edit/modes/atom.ts +893 -0
- package/src/edit/modes/chunk.ts +14 -24
- package/src/edit/modes/hashline.ts +193 -146
- package/src/edit/modes/patch.ts +5 -9
- package/src/edit/modes/replace.ts +6 -11
- package/src/edit/renderer.ts +14 -10
- package/src/edit/streaming.ts +50 -16
- package/src/exec/bash-executor.ts +2 -4
- package/src/export/html/template.generated.ts +1 -1
- package/src/export/html/template.js +4 -12
- package/src/extensibility/custom-tools/types.ts +2 -0
- package/src/extensibility/custom-tools/wrapper.ts +2 -1
- package/src/internal-urls/docs-index.generated.ts +2 -2
- package/src/lsp/defaults.json +142 -652
- package/src/lsp/index.ts +1 -1
- package/src/mcp/render.ts +1 -8
- package/src/modes/components/assistant-message.ts +4 -0
- package/src/modes/components/diff.ts +23 -14
- package/src/modes/components/footer.ts +21 -16
- package/src/modes/components/session-selector.ts +3 -3
- package/src/modes/components/settings-defs.ts +6 -1
- package/src/modes/components/todo-reminder.ts +1 -8
- package/src/modes/components/tool-execution.ts +1 -4
- package/src/modes/controllers/selector-controller.ts +1 -1
- package/src/modes/print-mode.ts +8 -0
- package/src/prompts/agents/librarian.md +1 -1
- package/src/prompts/agents/reviewer.md +4 -4
- package/src/prompts/ci-green-request.md +1 -1
- package/src/prompts/review-request.md +1 -1
- package/src/prompts/system/subagent-system-prompt.md +3 -3
- package/src/prompts/system/subagent-yield-reminder.md +11 -0
- package/src/prompts/system/system-prompt.md +3 -0
- package/src/prompts/tools/ask.md +3 -2
- package/src/prompts/tools/ast-edit.md +16 -20
- package/src/prompts/tools/ast-grep.md +19 -24
- package/src/prompts/tools/atom.md +87 -0
- package/src/prompts/tools/chunk-edit.md +37 -161
- package/src/prompts/tools/debug.md +4 -5
- package/src/prompts/tools/exit-plan-mode.md +4 -5
- package/src/prompts/tools/find.md +4 -8
- package/src/prompts/tools/github.md +18 -0
- package/src/prompts/tools/grep.md +4 -5
- package/src/prompts/tools/hashline.md +22 -89
- package/src/prompts/tools/{gemini-image.md → image-gen.md} +1 -1
- package/src/prompts/tools/inspect-image.md +6 -6
- package/src/prompts/tools/lsp.md +1 -1
- package/src/prompts/tools/patch.md +12 -19
- package/src/prompts/tools/python.md +3 -2
- package/src/prompts/tools/read-chunk.md +2 -3
- package/src/prompts/tools/read.md +2 -2
- package/src/prompts/tools/ssh.md +8 -17
- package/src/prompts/tools/todo-write.md +54 -41
- package/src/sdk.ts +14 -9
- package/src/session/agent-session.ts +25 -2
- package/src/session/session-manager.ts +4 -1
- package/src/task/executor.ts +43 -48
- package/src/task/render.ts +11 -13
- package/src/tools/ask.ts +7 -7
- package/src/tools/ast-edit.ts +45 -41
- package/src/tools/ast-grep.ts +77 -85
- package/src/tools/bash.ts +8 -9
- package/src/tools/browser.ts +32 -30
- package/src/tools/calculator.ts +4 -4
- package/src/tools/cancel-job.ts +1 -1
- package/src/tools/checkpoint.ts +2 -2
- package/src/tools/debug.ts +41 -37
- package/src/tools/exit-plan-mode.ts +1 -1
- package/src/tools/find.ts +4 -4
- package/src/tools/gh-renderer.ts +12 -4
- package/src/tools/gh.ts +509 -697
- package/src/tools/grep.ts +116 -131
- package/src/tools/{gemini-image.ts → image-gen.ts} +459 -60
- package/src/tools/index.ts +14 -32
- package/src/tools/inspect-image.ts +3 -3
- package/src/tools/json-tree.ts +114 -114
- package/src/tools/match-line-format.ts +8 -7
- package/src/tools/notebook.ts +8 -7
- package/src/tools/poll-tool.ts +2 -1
- package/src/tools/python.ts +9 -23
- package/src/tools/read.ts +32 -25
- package/src/tools/render-mermaid.ts +1 -1
- package/src/tools/render-utils.ts +18 -0
- package/src/tools/renderers.ts +2 -2
- package/src/tools/report-tool-issue.ts +3 -2
- package/src/tools/resolve.ts +1 -1
- package/src/tools/review.ts +12 -10
- package/src/tools/search-tool-bm25.ts +2 -4
- package/src/tools/ssh.ts +4 -4
- package/src/tools/todo-write.ts +172 -147
- package/src/tools/vim.ts +14 -15
- package/src/tools/write.ts +4 -4
- package/src/tools/{submit-result.ts → yield.ts} +11 -13
- package/src/utils/edit-mode.ts +2 -1
- package/src/utils/file-display-mode.ts +10 -5
- package/src/utils/git.ts +9 -5
- package/src/utils/shell-snapshot.ts +2 -3
- package/src/vim/render.ts +4 -4
- package/src/prompts/system/subagent-submit-reminder.md +0 -11
- package/src/prompts/tools/gh-issue-view.md +0 -11
- package/src/prompts/tools/gh-pr-checkout.md +0 -12
- package/src/prompts/tools/gh-pr-diff.md +0 -12
- package/src/prompts/tools/gh-pr-push.md +0 -12
- package/src/prompts/tools/gh-pr-view.md +0 -11
- package/src/prompts/tools/gh-repo-view.md +0 -11
- package/src/prompts/tools/gh-run-watch.md +0 -12
- package/src/prompts/tools/gh-search-issues.md +0 -11
- package/src/prompts/tools/gh-search-prs.md +0 -11
package/src/lsp/index.ts
CHANGED
|
@@ -1115,11 +1115,11 @@ export class LspTool implements AgentTool<typeof lspSchema, LspToolDetails, Them
|
|
|
1115
1115
|
readonly label = "LSP";
|
|
1116
1116
|
readonly description: string;
|
|
1117
1117
|
readonly parameters = lspSchema;
|
|
1118
|
-
readonly strict = true;
|
|
1119
1118
|
readonly renderCall = renderCall;
|
|
1120
1119
|
readonly renderResult = renderResult;
|
|
1121
1120
|
readonly mergeCallAndResult = true;
|
|
1122
1121
|
readonly inline = true;
|
|
1122
|
+
readonly strict = true;
|
|
1123
1123
|
|
|
1124
1124
|
constructor(private readonly session: ToolSession) {
|
|
1125
1125
|
this.description = prompt.render(lspDescription);
|
package/src/mcp/render.ts
CHANGED
|
@@ -17,7 +17,6 @@ import {
|
|
|
17
17
|
JSON_TREE_SCALAR_LEN_COLLAPSED,
|
|
18
18
|
JSON_TREE_SCALAR_LEN_EXPANDED,
|
|
19
19
|
renderJsonTreeLines,
|
|
20
|
-
stripInternalArgs,
|
|
21
20
|
} from "../tools/json-tree";
|
|
22
21
|
import { formatExpandHint, truncateToWidth } from "../tools/render-utils";
|
|
23
22
|
import { renderStatusLine } from "../tui";
|
|
@@ -58,13 +57,7 @@ export function renderMCPResult(
|
|
|
58
57
|
lines.push(`${theme.fg("dim", "Args")}`);
|
|
59
58
|
const maxDepth = JSON_TREE_MAX_DEPTH_EXPANDED;
|
|
60
59
|
const maxLines = JSON_TREE_MAX_LINES_EXPANDED;
|
|
61
|
-
const tree = renderJsonTreeLines(
|
|
62
|
-
stripInternalArgs(args),
|
|
63
|
-
theme,
|
|
64
|
-
maxDepth,
|
|
65
|
-
maxLines,
|
|
66
|
-
JSON_TREE_SCALAR_LEN_EXPANDED,
|
|
67
|
-
);
|
|
60
|
+
const tree = renderJsonTreeLines(args, theme, maxDepth, maxLines, JSON_TREE_SCALAR_LEN_EXPANDED);
|
|
68
61
|
for (const line of tree.lines) {
|
|
69
62
|
lines.push(line);
|
|
70
63
|
}
|
|
@@ -155,6 +155,10 @@ export class AssistantMessageComponent extends Container {
|
|
|
155
155
|
this.#contentContainer.addChild(new Text(theme.fg("error", `Error: ${errorMsg}`), 1, 0));
|
|
156
156
|
}
|
|
157
157
|
}
|
|
158
|
+
if (message.errorMessage && message.stopReason !== "aborted" && message.stopReason !== "error") {
|
|
159
|
+
this.#contentContainer.addChild(new Spacer(1));
|
|
160
|
+
this.#contentContainer.addChild(new Text(theme.fg("error", `Error: ${message.errorMessage}`), 1, 0));
|
|
161
|
+
}
|
|
158
162
|
|
|
159
163
|
// Token usage metadata
|
|
160
164
|
if (settings.get("display.showTokenUsage") && this.#usageInfo) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { getIndentation } from "@oh-my-pi/pi-utils";
|
|
2
2
|
import * as Diff from "diff";
|
|
3
3
|
import { theme } from "../../modes/theme/theme";
|
|
4
|
-
import { replaceTabs } from "../../tools/render-utils";
|
|
4
|
+
import { type CodeFrameMarker, formatCodeFrameLine, replaceTabs } from "../../tools/render-utils";
|
|
5
5
|
|
|
6
6
|
/** SGR dim on / normal intensity — additive, preserves fg/bg colors. */
|
|
7
7
|
const DIM = "\x1b[2m";
|
|
@@ -37,14 +37,14 @@ function visualizeIndent(text: string, filePath?: string): string {
|
|
|
37
37
|
* Parse diff line to extract prefix, line number, and content.
|
|
38
38
|
* Supported formats: "+123|content" (canonical) and "+123 content" (legacy).
|
|
39
39
|
*/
|
|
40
|
-
function parseDiffLine(line: string): { prefix:
|
|
40
|
+
function parseDiffLine(line: string): { prefix: CodeFrameMarker; lineNum: string; content: string } | null {
|
|
41
41
|
const canonical = line.match(/^([+-\s])(\s*\d+)\|(.*)$/);
|
|
42
42
|
if (canonical) {
|
|
43
|
-
return { prefix: canonical[1], lineNum: canonical[2], content: canonical[3] };
|
|
43
|
+
return { prefix: canonical[1] as CodeFrameMarker, lineNum: canonical[2] ?? "", content: canonical[3] ?? "" };
|
|
44
44
|
}
|
|
45
45
|
const legacy = line.match(/^([+-\s])(?:(\s*\d+)\s)?(.*)$/);
|
|
46
46
|
if (!legacy) return null;
|
|
47
|
-
return { prefix: legacy[1], lineNum: legacy[2] ?? "", content: legacy[3] };
|
|
47
|
+
return { prefix: legacy[1] as CodeFrameMarker, lineNum: legacy[2] ?? "", content: legacy[3] ?? "" };
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
/**
|
|
@@ -108,12 +108,27 @@ export interface RenderDiffOptions {
|
|
|
108
108
|
export function renderDiff(diffText: string, options: RenderDiffOptions = {}): string {
|
|
109
109
|
const lines = diffText.split("\n");
|
|
110
110
|
const result: string[] = [];
|
|
111
|
-
|
|
112
|
-
const
|
|
111
|
+
const parsedLines = lines.map(parseDiffLine);
|
|
112
|
+
const lineNumberWidth = parsedLines.reduce((width, parsed) => {
|
|
113
|
+
const lineNumber = parsed?.lineNum.trim() ?? "";
|
|
114
|
+
return Math.max(width, lineNumber.length);
|
|
115
|
+
}, 0);
|
|
116
|
+
|
|
117
|
+
// Track the line number rendered on the previous emitted line so we can
|
|
118
|
+
// blank out duplicate gutters. Two cases trigger this:
|
|
119
|
+
// 1. Single-line replacement (`-N` followed by `+N`) — the `+N` repeats `N`.
|
|
120
|
+
// 2. Insertion followed by context (`+N` then ` N` if producer used oldLine).
|
|
121
|
+
let prevLineNum = "";
|
|
122
|
+
|
|
123
|
+
const formatLine = (prefix: CodeFrameMarker, lineNum: string, content: string): string => {
|
|
113
124
|
if (lineNum.trim().length === 0) {
|
|
125
|
+
prevLineNum = "";
|
|
114
126
|
return `${prefix}${content}`;
|
|
115
127
|
}
|
|
116
|
-
|
|
128
|
+
const trimmed = lineNum.trim();
|
|
129
|
+
const displayNum = trimmed === prevLineNum ? "" : trimmed;
|
|
130
|
+
prevLineNum = trimmed;
|
|
131
|
+
return formatCodeFrameLine(prefix, displayNum, content, lineNumberWidth);
|
|
117
132
|
};
|
|
118
133
|
|
|
119
134
|
let i = 0;
|
|
@@ -122,13 +137,13 @@ export function renderDiff(diffText: string, options: RenderDiffOptions = {}): s
|
|
|
122
137
|
const parsed = parseDiffLine(line);
|
|
123
138
|
|
|
124
139
|
if (!parsed) {
|
|
140
|
+
prevLineNum = "";
|
|
125
141
|
result.push(theme.fg("toolDiffContext", line));
|
|
126
142
|
i++;
|
|
127
143
|
continue;
|
|
128
144
|
}
|
|
129
145
|
|
|
130
146
|
if (parsed.prefix === "-") {
|
|
131
|
-
// Collect consecutive removed lines
|
|
132
147
|
const removedLines: { lineNum: string; content: string }[] = [];
|
|
133
148
|
while (i < lines.length) {
|
|
134
149
|
const p = parseDiffLine(lines[i]);
|
|
@@ -137,7 +152,6 @@ export function renderDiff(diffText: string, options: RenderDiffOptions = {}): s
|
|
|
137
152
|
i++;
|
|
138
153
|
}
|
|
139
154
|
|
|
140
|
-
// Collect consecutive added lines
|
|
141
155
|
const addedLines: { lineNum: string; content: string }[] = [];
|
|
142
156
|
while (i < lines.length) {
|
|
143
157
|
const p = parseDiffLine(lines[i]);
|
|
@@ -146,8 +160,6 @@ export function renderDiff(diffText: string, options: RenderDiffOptions = {}): s
|
|
|
146
160
|
i++;
|
|
147
161
|
}
|
|
148
162
|
|
|
149
|
-
// Only do intra-line diffing when there's exactly one removed and one added line
|
|
150
|
-
// (indicating a single line modification). Otherwise, show lines as-is.
|
|
151
163
|
if (removedLines.length === 1 && addedLines.length === 1) {
|
|
152
164
|
const removed = removedLines[0];
|
|
153
165
|
const added = addedLines[0];
|
|
@@ -167,7 +179,6 @@ export function renderDiff(diffText: string, options: RenderDiffOptions = {}): s
|
|
|
167
179
|
theme.fg("toolDiffAdded", formatLine("+", added.lineNum, visualizeIndent(addedLine, options.filePath))),
|
|
168
180
|
);
|
|
169
181
|
} else {
|
|
170
|
-
// Show all removed lines first, then all added lines
|
|
171
182
|
for (const removed of removedLines) {
|
|
172
183
|
result.push(
|
|
173
184
|
theme.fg(
|
|
@@ -186,7 +197,6 @@ export function renderDiff(diffText: string, options: RenderDiffOptions = {}): s
|
|
|
186
197
|
}
|
|
187
198
|
}
|
|
188
199
|
} else if (parsed.prefix === "+") {
|
|
189
|
-
// Standalone added line
|
|
190
200
|
result.push(
|
|
191
201
|
theme.fg(
|
|
192
202
|
"toolDiffAdded",
|
|
@@ -195,7 +205,6 @@ export function renderDiff(diffText: string, options: RenderDiffOptions = {}): s
|
|
|
195
205
|
);
|
|
196
206
|
i++;
|
|
197
207
|
} else {
|
|
198
|
-
// Context line
|
|
199
208
|
result.push(
|
|
200
209
|
theme.fg(
|
|
201
210
|
"toolDiffContext",
|
|
@@ -56,22 +56,27 @@ export class FooterComponent implements Component {
|
|
|
56
56
|
this.#gitWatcher = null;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
git.head
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
this.#
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
59
|
+
void git.head
|
|
60
|
+
.resolve(getProjectDir())
|
|
61
|
+
.then(head => {
|
|
62
|
+
if (!head) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
this.#gitWatcher = fs.watch(head.headPath, () => {
|
|
68
|
+
this.#cachedBranch = undefined; // Invalidate cache
|
|
69
|
+
if (this.#onBranchChange) {
|
|
70
|
+
this.#onBranchChange();
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
} catch {
|
|
74
|
+
// Silently fail if we can't watch
|
|
75
|
+
}
|
|
76
|
+
})
|
|
77
|
+
.catch(() => {
|
|
78
|
+
this.#cachedBranch = null;
|
|
79
|
+
});
|
|
75
80
|
}
|
|
76
81
|
|
|
77
82
|
/**
|
|
@@ -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 +
|
|
161
|
+
// Metadata line: date + file size
|
|
161
162
|
const modified = formatDate(session.modified);
|
|
162
|
-
const
|
|
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);
|
|
@@ -355,7 +355,12 @@ const OPTION_PROVIDERS: Partial<Record<SettingPath, OptionProvider>> = {
|
|
|
355
355
|
{ value: "parallel", label: "Parallel", description: "Requires PARALLEL_API_KEY" },
|
|
356
356
|
],
|
|
357
357
|
"providers.image": [
|
|
358
|
-
{
|
|
358
|
+
{
|
|
359
|
+
value: "auto",
|
|
360
|
+
label: "Auto",
|
|
361
|
+
description: "Priority: GPT model image tool > Antigravity > OpenRouter > Gemini",
|
|
362
|
+
},
|
|
363
|
+
{ value: "openai", label: "OpenAI", description: "Uses the active GPT Responses/Codex model" },
|
|
359
364
|
{ value: "gemini", label: "Gemini", description: "Requires GEMINI_API_KEY" },
|
|
360
365
|
{ value: "openrouter", label: "OpenRouter", description: "Requires OPENROUTER_API_KEY" },
|
|
361
366
|
],
|
|
@@ -34,14 +34,7 @@ export class TodoReminderComponent extends Container {
|
|
|
34
34
|
this.#box.addChild(new Text(header, 0, 0));
|
|
35
35
|
this.#box.addChild(new Spacer(1));
|
|
36
36
|
|
|
37
|
-
const todoList = this.todos
|
|
38
|
-
.map(t => {
|
|
39
|
-
const line = ` ${theme.checkbox.unchecked} ${t.content}`;
|
|
40
|
-
if (!t.details) return line;
|
|
41
|
-
const detailLines = t.details.split("\n").map(l => ` ${l}`);
|
|
42
|
-
return [line, ...detailLines].join("\n");
|
|
43
|
-
})
|
|
44
|
-
.join("\n");
|
|
37
|
+
const todoList = this.todos.map(todo => ` ${theme.checkbox.unchecked} ${todo.content}`).join("\n");
|
|
45
38
|
this.#box.addChild(new Text(theme.italic(todoList), 0, 0));
|
|
46
39
|
}
|
|
47
40
|
}
|
|
@@ -27,7 +27,6 @@ import {
|
|
|
27
27
|
JSON_TREE_SCALAR_LEN_COLLAPSED,
|
|
28
28
|
JSON_TREE_SCALAR_LEN_EXPANDED,
|
|
29
29
|
renderJsonTreeLines,
|
|
30
|
-
stripInternalArgs,
|
|
31
30
|
} from "../../tools/json-tree";
|
|
32
31
|
import { PYTHON_DEFAULT_PREVIEW_LINES } from "../../tools/python";
|
|
33
32
|
import { formatExpandHint, replaceTabs, resolveImageOptions, truncateToWidth } from "../../tools/render-utils";
|
|
@@ -743,9 +742,7 @@ export class ToolExecutionComponent extends Container {
|
|
|
743
742
|
lines.push("");
|
|
744
743
|
lines.push(theme.fg("dim", "Args"));
|
|
745
744
|
const tree = renderJsonTreeLines(
|
|
746
|
-
this.#args
|
|
747
|
-
? stripInternalArgs(this.#args as Record<string, unknown>)
|
|
748
|
-
: this.#args,
|
|
745
|
+
this.#args,
|
|
749
746
|
theme,
|
|
750
747
|
JSON_TREE_MAX_DEPTH_EXPANDED,
|
|
751
748
|
JSON_TREE_MAX_LINES_EXPANDED,
|
|
@@ -362,7 +362,7 @@ export class SelectorController {
|
|
|
362
362
|
}
|
|
363
363
|
break;
|
|
364
364
|
case "providers.image":
|
|
365
|
-
if (value === "auto" || value === "gemini" || value === "openrouter") {
|
|
365
|
+
if (value === "auto" || value === "openai" || value === "gemini" || value === "openrouter") {
|
|
366
366
|
setPreferredImageProvider(value);
|
|
367
367
|
}
|
|
368
368
|
break;
|
package/src/modes/print-mode.ts
CHANGED
|
@@ -161,6 +161,14 @@ export async function runPrintMode(session: AgentSession, options: PrintModeOpti
|
|
|
161
161
|
}
|
|
162
162
|
}
|
|
163
163
|
|
|
164
|
+
if (
|
|
165
|
+
assistantMsg.errorMessage &&
|
|
166
|
+
assistantMsg.stopReason !== "error" &&
|
|
167
|
+
assistantMsg.stopReason !== "aborted"
|
|
168
|
+
) {
|
|
169
|
+
process.stderr.write(`${sanitizeText(assistantMsg.errorMessage)}\n`);
|
|
170
|
+
}
|
|
171
|
+
|
|
164
172
|
// Output text content
|
|
165
173
|
for (const content of assistantMsg.content) {
|
|
166
174
|
if (content.type === "text") {
|
|
@@ -98,7 +98,7 @@ Before acting, determine what kind of question this is:
|
|
|
98
98
|
- For API signatures: copy verbatim from source. You **MUST NOT** paraphrase or reconstruct from memory.
|
|
99
99
|
|
|
100
100
|
## 5. Report
|
|
101
|
-
- Call `
|
|
101
|
+
- Call `yield` with structured findings.
|
|
102
102
|
- Every `sources` entry **MUST** include a verbatim excerpt.
|
|
103
103
|
- The `api` array **MUST** contain exact signatures copied from source.
|
|
104
104
|
- Clean up cloned repos: `rm -rf /tmp/librarian-*`.
|
|
@@ -44,7 +44,7 @@ output:
|
|
|
44
44
|
type: number
|
|
45
45
|
file_path:
|
|
46
46
|
metadata:
|
|
47
|
-
description:
|
|
47
|
+
description: Path to affected file
|
|
48
48
|
type: string
|
|
49
49
|
line_start:
|
|
50
50
|
metadata:
|
|
@@ -63,7 +63,7 @@ Your goal is to identify bugs the author would want fixed before merge.
|
|
|
63
63
|
1. Run `git diff` (or `gh pr diff <number>`) to view patch
|
|
64
64
|
2. Read modified files for full context
|
|
65
65
|
3. Call `report_finding` per issue
|
|
66
|
-
4. Call `
|
|
66
|
+
4. Call `yield` with verdict
|
|
67
67
|
|
|
68
68
|
Bash is read-only: `git diff`, `git log`, `git show`, `gh pr diff`. You **MUST NOT** make file edits or trigger builds.
|
|
69
69
|
</procedure>
|
|
@@ -108,10 +108,10 @@ Each `report_finding` requires:
|
|
|
108
108
|
- `body`: One paragraph
|
|
109
109
|
- `priority`: 0-3
|
|
110
110
|
- `confidence`: 0.0-1.0
|
|
111
|
-
- `file_path`:
|
|
111
|
+
- `file_path`: Path to affected file
|
|
112
112
|
- `line_start`, `line_end`: Range ≤10 lines, must overlap diff
|
|
113
113
|
|
|
114
|
-
Final `
|
|
114
|
+
Final `yield` call (payload under `result.data`):
|
|
115
115
|
- `result.data.overall_correctness`: "correct" (no bugs/blockers) or "incorrect"
|
|
116
116
|
- `result.data.explanation`: Plain text, 1-3 sentences summarizing verdict. Don't repeat findings (captured via `report_finding`).
|
|
117
117
|
- `result.data.confidence`: 0.0-1.0
|
|
@@ -4,7 +4,7 @@ Do not stop after a single fix attempt.
|
|
|
4
4
|
</critical>
|
|
5
5
|
|
|
6
6
|
<instruction>
|
|
7
|
-
- Prefer `
|
|
7
|
+
- Prefer the `github` tool with `op: run_watch` and no other arguments if that tool is available.
|
|
8
8
|
- Otherwise use `gh` cli.
|
|
9
9
|
- Use the workflow runs for the current HEAD commit as the source of truth after each push.
|
|
10
10
|
</instruction>
|
|
@@ -40,7 +40,7 @@ Reviewer **MUST**:
|
|
|
40
40
|
2. {{#if skipDiff}}**MUST** run `git diff`/`git show` for assigned files{{else}}**MUST** use diff hunks below (**MUST NOT** re-run git diff){{/if}}
|
|
41
41
|
3. **MAY** read full file context as needed via `read`
|
|
42
42
|
4. Call `report_finding` per issue
|
|
43
|
-
5. Call `
|
|
43
|
+
5. Call `yield` with verdict when done
|
|
44
44
|
|
|
45
45
|
{{#if skipDiff}}
|
|
46
46
|
### Diff Previews
|
|
@@ -15,9 +15,9 @@ If you need additional information, you can find your conversation with the user
|
|
|
15
15
|
{{/if}}
|
|
16
16
|
|
|
17
17
|
{{SECTION_SEPARATOR "Closure"}}
|
|
18
|
-
No TODO tracking, no progress updates. Execute, call `
|
|
18
|
+
No TODO tracking, no progress updates. Execute, call `yield`, done.
|
|
19
19
|
|
|
20
|
-
When finished, you **MUST** call `
|
|
20
|
+
When finished, you **MUST** call `yield` exactly once. This is like writing to a ticket, provide what is required, and close it.
|
|
21
21
|
|
|
22
22
|
This is your only way to return a result. You **MUST NOT** put JSON in plain text, and you **MUST NOT** substitute a text summary for the structured `result.data` parameter.
|
|
23
23
|
|
|
@@ -29,7 +29,7 @@ Your result **MUST** match this TypeScript interface:
|
|
|
29
29
|
{{/if}}
|
|
30
30
|
|
|
31
31
|
{{SECTION_SEPARATOR "Giving Up"}}
|
|
32
|
-
Giving up is a last resort. If truly blocked, you **MUST** call `
|
|
32
|
+
Giving up is a last resort. If truly blocked, you **MUST** call `yield` exactly once with `result.error` describing what you tried and the exact blocker.
|
|
33
33
|
You **MUST NOT** give up due to uncertainty, missing information obtainable via tools or repo context, or needing a design decision you can derive yourself.
|
|
34
34
|
|
|
35
35
|
You **MUST** keep going until this ticket is closed. This matters.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<system-reminder>
|
|
2
|
+
You stopped without calling yield. This is reminder {{retryCount}} of {{maxRetries}}.
|
|
3
|
+
|
|
4
|
+
You **MUST** call yield as your only action now. Choose one:
|
|
5
|
+
- If task is complete: call yield with your result in `result.data`
|
|
6
|
+
- If task failed: call yield with `result.error` describing what happened
|
|
7
|
+
|
|
8
|
+
You **MUST NOT** give up if you can still complete the task through exploration (using available tools or repo context). If you submit an error, you **MUST** include what you tried and the exact blocker.
|
|
9
|
+
|
|
10
|
+
You **MUST NOT** output text without a tool call. You **MUST** call yield to finish.
|
|
11
|
+
</system-reminder>
|
|
@@ -194,6 +194,9 @@ You **MUST NOT** use Python or Bash when a specialized tool exists.
|
|
|
194
194
|
{{#has tools "edit"}}- Use `edit` for surgical text changes, not `sed`.{{/has}}
|
|
195
195
|
{{/ifAny}}
|
|
196
196
|
|
|
197
|
+
### Paths
|
|
198
|
+
- For tools that take a `path` (or path-like field), prefer cwd-relative paths for files inside the cwd. Use absolute paths only when targeting files outside the cwd or when expanding `~`.
|
|
199
|
+
|
|
197
200
|
{{#has tools "lsp"}}
|
|
198
201
|
### LSP guidance
|
|
199
202
|
Use semantic tools for semantic questions:
|
package/src/prompts/tools/ask.md
CHANGED
|
@@ -21,8 +21,9 @@ Asks user when you need clarification or input during task execution.
|
|
|
21
21
|
- **Do NOT include "Other" option** — UI automatically adds "Other (type your own)" to every question.
|
|
22
22
|
</critical>
|
|
23
23
|
|
|
24
|
-
<
|
|
24
|
+
<examples>
|
|
25
|
+
# Single question
|
|
25
26
|
question: "Which authentication method should this API use?"
|
|
26
27
|
options: [{"label": "JWT"}, {"label": "OAuth2"}, {"label": "Session cookies"}]
|
|
27
28
|
recommended: 0
|
|
28
|
-
</
|
|
29
|
+
</examples>
|
|
@@ -2,39 +2,35 @@ Performs structural AST-aware rewrites via native ast-grep.
|
|
|
2
2
|
|
|
3
3
|
<instruction>
|
|
4
4
|
- Use for codemods and structural rewrites where plain text replace is unsafe
|
|
5
|
-
- `path` accepts a comma-separated list
|
|
6
|
-
-
|
|
5
|
+
- `path` is required and accepts a file, directory, glob, comma-separated path list, or internal URL
|
|
6
|
+
- Language is inferred from `path`; narrow `path` to one language for deterministic rewrites
|
|
7
7
|
- Metavariables captured in `pat` (`$A`, `$$$ARGS`) are substituted into that entry's `out` template
|
|
8
8
|
- **Patterns match AST structure, not text.** `$NAME` = one node (captured); `$_` = one without binding; `$$$NAME` = zero-or-more (lazy — stops at next matchable element); `$$$` = zero-or-more without binding. Use `$$$NAME`, **NOT** `$$NAME` — the two-dollar form is invalid. Metavariable names are UPPERCASE and **MUST** be the whole AST node — partial text like `prefix$VAR` or `"hello $NAME"` does NOT work
|
|
9
9
|
- When the same metavariable appears twice, both occurrences **MUST** match identical code (`$A == $A` matches `x == x`, not `x == y`)
|
|
10
|
-
- Rewrite patterns **MUST** parse as a single valid AST node. For method fragments or body snippets that don't parse standalone, wrap in context (e.g. `class $_ { … }`)
|
|
10
|
+
- Rewrite patterns **MUST** parse as a single valid AST node. For method fragments or body snippets that don't parse standalone, wrap in context (e.g. `class $_ { … }`)
|
|
11
11
|
- For TS declarations/methods, tolerate unknown annotations: `async function $NAME($$$ARGS): $_ { $$$BODY }` or `class $_ { method($ARG: $_): $_ { $$$BODY } }`
|
|
12
12
|
- Delete matched code with empty `out`: `{"pat":"console.log($$$)","out":""}`
|
|
13
13
|
- Each rewrite is a 1:1 structural substitution — cannot split one capture across multiple nodes or merge multiple captures into one
|
|
14
14
|
</instruction>
|
|
15
15
|
|
|
16
16
|
<output>
|
|
17
|
-
- Replacement summary, per-file replacement counts, and change diffs
|
|
17
|
+
- Replacement summary, per-file replacement counts, and change diffs as `-LINE+ID|before` / `+LINE+ID|after` lines
|
|
18
18
|
- Parse issues when files cannot be processed
|
|
19
19
|
</output>
|
|
20
20
|
|
|
21
21
|
<examples>
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
- Rewrite a method body fragment by wrapping in parseable context and selecting the method:
|
|
35
|
-
`{"ops":[{"pat":"class $_ { async execute($INPUT: $_) { $$$BEFORE; const $PARSED = $_.parse($INPUT); $$$AFTER } }","out":"class $_ { async execute($INPUT: $_) { $$$BEFORE; const $PARSED = $SCHEMA.parse($INPUT); $$$AFTER } }"}],"sel":"method_definition","lang":"typescript","path":"src/tools/todo.ts"}`
|
|
36
|
-
- Python — convert print calls to logging:
|
|
37
|
-
`{"ops":[{"pat":"print($$$ARGS)","out":"logger.info($$$ARGS)"}],"lang":"python","path":"src/"}`
|
|
22
|
+
# Rename a call site across TypeScript files
|
|
23
|
+
`{"ops":[{"pat":"oldApi($$$ARGS)","out":"newApi($$$ARGS)"}],"path":"src/**/*.ts"}`
|
|
24
|
+
# Delete matching calls
|
|
25
|
+
`{"ops":[{"pat":"console.log($$$ARGS)","out":""}],"path":"src/**/*.ts"}`
|
|
26
|
+
# Rewrite import source path
|
|
27
|
+
`{"ops":[{"pat":"import { $$$IMPORTS } from \"old-package\"","out":"import { $$$IMPORTS } from \"new-package\""}],"path":"src/**/*.ts"}`
|
|
28
|
+
# Modernize to optional chaining (same metavariable enforces identity)
|
|
29
|
+
`{"ops":[{"pat":"$A && $A()","out":"$A?.()"}],"path":"src/**/*.ts"}`
|
|
30
|
+
# Swap two arguments using captures
|
|
31
|
+
`{"ops":[{"pat":"assertEqual($A, $B)","out":"assertEqual($B, $A)"}],"path":"tests/**/*.ts"}`
|
|
32
|
+
# Python — convert print calls to logging
|
|
33
|
+
`{"ops":[{"pat":"print($$$ARGS)","out":"logger.info($$$ARGS)"}],"path":"src/**/*.py"}`
|
|
38
34
|
</examples>
|
|
39
35
|
|
|
40
36
|
<critical>
|
|
@@ -2,46 +2,41 @@ Performs structural code search using AST matching via native ast-grep.
|
|
|
2
2
|
|
|
3
3
|
<instruction>
|
|
4
4
|
- Use when syntax shape matters more than raw text (calls, declarations, specific language constructs)
|
|
5
|
-
- `path` accepts a comma-separated list
|
|
6
|
-
-
|
|
7
|
-
-
|
|
5
|
+
- `path` is required and accepts a file, directory, glob, comma-separated path list, or internal URL
|
|
6
|
+
- Language is inferred from `path`; narrow `path` to one language when mixed-language trees could cause parse noise
|
|
7
|
+
- `pat` is a single AST pattern. Run separate calls for distinct unrelated patterns
|
|
8
8
|
- **Patterns match AST structure, not text** — whitespace/formatting is ignored
|
|
9
9
|
- `$NAME` captures one node; `$_` matches one without binding; `$$$NAME` captures zero-or-more (lazy — stops at next matchable element); `$$$` matches zero-or-more without binding. Use `$$$NAME`, **NOT** `$$NAME` — the two-dollar form is invalid and produces a parse error
|
|
10
10
|
- Metavariable names are UPPERCASE and must be the whole AST node — partial-text like `prefix$VAR`, `"hello $NAME"`, or `a $OP b` does NOT work; match the whole node instead
|
|
11
11
|
- When the same metavariable appears twice, both occurrences **MUST** match identical code (`$A == $A` matches `x == x`, not `x == y`)
|
|
12
|
-
- Patterns **MUST** parse as a single valid AST node for the target language. For method fragments or body snippets that don't parse standalone, wrap in valid context (e.g. `class $_ { … }`)
|
|
13
|
-
- C++ qualified calls used as expression statements need the statement semicolon in the pattern: use `ns::doThing($ARG);`, `$CALLEE($ARG)
|
|
12
|
+
- Patterns **MUST** parse as a single valid AST node for the inferred target language. For method fragments or body snippets that don't parse standalone, wrap in valid context (e.g. `class $_ { … }`)
|
|
13
|
+
- C++ qualified calls used as expression statements need the statement semicolon in the pattern: use `ns::doThing($ARG);`, `$CALLEE($ARG);`, or wrap a statement snippet. Without `;`, tree-sitter-cpp may parse `ns::doThing($ARG)` as declaration-like syntax and return no matches
|
|
14
14
|
- For TS declarations/methods, tolerate unknown annotations: `async function $NAME($$$ARGS): $_ { $$$BODY }` or `class $_ { method($ARG: $_): $_ { $$$BODY } }`
|
|
15
15
|
- Declaration forms are structurally distinct — top-level `function foo`, class method `foo()`, and `const foo = () => {}` are different AST shapes; search the right form before concluding absence
|
|
16
|
-
- Loosest existence check: `pat:
|
|
16
|
+
- Loosest existence check: `pat: "executeBash"` with a narrow `path`
|
|
17
17
|
</instruction>
|
|
18
18
|
|
|
19
19
|
<output>
|
|
20
20
|
- Grouped matches with file path, byte range, line/column ranges, metavariable captures
|
|
21
|
+
- Match lines are anchor-prefixed: `LINE+ID>content` for the matched line and `LINE+ID:content` for surrounding context
|
|
21
22
|
- Summary counts (`totalMatches`, `filesWithMatches`, `filesSearched`) and parse issues when present
|
|
22
23
|
</output>
|
|
23
24
|
|
|
24
25
|
<examples>
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
- Match a function declaration while tolerating any return type annotation (`sel` targets the inner node):
|
|
36
|
-
`{"pat":["async function processItems($$$ARGS): $_ { $$$BODY }"],"sel":"function_declaration","lang":"typescript","path":"src/worker.ts"}`
|
|
37
|
-
- Match a method body fragment by wrapping in parseable context and selecting the method:
|
|
38
|
-
`{"pat":["class $_ { async execute($INPUT: $_) { $$$BEFORE; const $PARSED = $_.parse($INPUT); $$$AFTER } }"],"sel":"method_definition","lang":"typescript","path":"src/tools/todo.ts"}`
|
|
39
|
-
- Loosest existence check for a symbol in one file:
|
|
40
|
-
`{"pat":["processItems"],"sel":"identifier","lang":"typescript","path":"src/worker.ts"}`
|
|
26
|
+
# Search TypeScript files under src
|
|
27
|
+
`{"pat":"console.log($$$)","path":"src/**/*.ts"}`
|
|
28
|
+
# Named imports from a specific package
|
|
29
|
+
`{"pat":"import { $$$IMPORTS } from \"react\"","path":"src/**/*.ts"}`
|
|
30
|
+
# Arrow functions assigned to a const
|
|
31
|
+
`{"pat":"const $NAME = ($$$ARGS) => $BODY","path":"src/utils/**/*.ts"}`
|
|
32
|
+
# Method call on any object, ignoring method name with `$_`
|
|
33
|
+
`{"pat":"logger.$_($$$ARGS)","path":"src/**/*.ts"}`
|
|
34
|
+
# Loosest existence check for a symbol in one file
|
|
35
|
+
`{"pat":"processItems","path":"src/worker.ts"}`
|
|
41
36
|
</examples>
|
|
42
37
|
|
|
43
38
|
<critical>
|
|
44
|
-
- Avoid repo-root
|
|
45
|
-
- Parse issues are query failure, not evidence of absence: repair the pattern or tighten `path
|
|
39
|
+
- Avoid repo-root scans — narrow `path` first
|
|
40
|
+
- Parse issues are query failure, not evidence of absence: repair the pattern or tighten `path` before concluding "no matches"
|
|
46
41
|
- For broad/open-ended exploration across subsystems, use Task tool with explore subagent first
|
|
47
42
|
</critical>
|