@oh-my-pi/pi-coding-agent 15.4.3 → 15.5.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 +81 -5
- package/dist/types/cli/args.d.ts +2 -0
- package/dist/types/cli/auth-broker-cli.d.ts +1 -1
- package/dist/types/commands/launch.d.ts +8 -0
- package/dist/types/config/settings-schema.d.ts +42 -1
- package/dist/types/edit/index.d.ts +2 -0
- package/dist/types/extensibility/custom-tools/types.d.ts +8 -2
- package/dist/types/extensibility/hooks/types.d.ts +4 -0
- package/dist/types/hashline/executor.d.ts +6 -3
- package/dist/types/lsp/index.d.ts +9 -1
- package/dist/types/mcp/client.d.ts +2 -1
- package/dist/types/mcp/oauth-discovery.d.ts +4 -3
- package/dist/types/mcp/timeout.d.ts +9 -0
- package/dist/types/mcp/types.d.ts +1 -1
- package/dist/types/sdk.d.ts +2 -0
- package/dist/types/session/streaming-output.d.ts +1 -1
- package/dist/types/task/index.d.ts +2 -0
- package/dist/types/task/types.d.ts +4 -0
- package/dist/types/tools/approval.d.ts +46 -0
- package/dist/types/tools/ask.d.ts +1 -0
- package/dist/types/tools/ast-edit.d.ts +2 -0
- package/dist/types/tools/ast-grep.d.ts +1 -0
- package/dist/types/tools/bash.d.ts +11 -1
- package/dist/types/tools/browser.d.ts +2 -0
- package/dist/types/tools/calculator.d.ts +1 -0
- package/dist/types/tools/checkpoint.d.ts +2 -0
- package/dist/types/tools/debug.d.ts +9 -1
- package/dist/types/tools/eval.d.ts +2 -0
- package/dist/types/tools/find.d.ts +10 -0
- package/dist/types/tools/gh.d.ts +2 -1
- package/dist/types/tools/hindsight-recall.d.ts +1 -0
- package/dist/types/tools/hindsight-reflect.d.ts +1 -0
- package/dist/types/tools/hindsight-retain.d.ts +1 -0
- package/dist/types/tools/inspect-image.d.ts +1 -0
- package/dist/types/tools/irc.d.ts +1 -0
- package/dist/types/tools/job.d.ts +1 -0
- package/dist/types/tools/read.d.ts +1 -0
- package/dist/types/tools/recipe/index.d.ts +1 -0
- package/dist/types/tools/render-mermaid.d.ts +1 -0
- package/dist/types/tools/resolve.d.ts +1 -0
- package/dist/types/tools/search-tool-bm25.d.ts +1 -0
- package/dist/types/tools/search.d.ts +1 -0
- package/dist/types/tools/ssh.d.ts +2 -0
- package/dist/types/tools/todo-write.d.ts +1 -0
- package/dist/types/tools/write.d.ts +2 -0
- package/dist/types/tools/yield.d.ts +1 -0
- package/dist/types/web/search/index.d.ts +1 -0
- package/package.json +7 -7
- package/src/cli/args.ts +14 -0
- package/src/cli/auth-broker-cli.ts +171 -22
- package/src/commands/auth-broker.ts +3 -0
- package/src/commands/launch.ts +16 -0
- package/src/config/mcp-schema.json +2 -2
- package/src/config/model-registry.ts +19 -4
- package/src/config/prompt-templates.ts +0 -125
- package/src/config/settings-schema.ts +59 -1
- package/src/config/settings.ts +2 -1
- package/src/dap/session.ts +35 -2
- package/src/discovery/builtin.ts +2 -2
- package/src/discovery/mcp-json.ts +1 -1
- package/src/edit/index.ts +26 -0
- package/src/edit/modes/patch.ts +1 -1
- package/src/edit/streaming.ts +12 -2
- package/src/exec/bash-executor.ts +6 -2
- package/src/extensibility/custom-commands/bundled/review/index.ts +18 -14
- package/src/extensibility/custom-tools/types.ts +16 -2
- package/src/extensibility/extensions/wrapper.ts +36 -1
- package/src/extensibility/hooks/types.ts +8 -1
- package/src/hashline/apply.ts +47 -2
- package/src/hashline/executor.ts +46 -24
- package/src/internal-urls/docs-index.generated.ts +8 -7
- package/src/lsp/edits.ts +82 -29
- package/src/lsp/index.ts +38 -1
- package/src/lsp/utils.ts +1 -1
- package/src/main.ts +6 -0
- package/src/mcp/client.ts +8 -6
- package/src/mcp/oauth-discovery.ts +120 -32
- package/src/mcp/oauth-flow.ts +34 -6
- package/src/mcp/timeout.ts +59 -0
- package/src/mcp/transports/http.ts +42 -44
- package/src/mcp/transports/stdio.ts +8 -5
- package/src/mcp/types.ts +1 -1
- package/src/modes/components/hook-editor.ts +11 -3
- package/src/modes/components/mcp-add-wizard.ts +6 -2
- package/src/modes/components/model-selector.ts +33 -11
- package/src/modes/controllers/command-controller.ts +6 -4
- package/src/modes/controllers/mcp-command-controller.ts +8 -4
- package/src/prompts/review-custom-request.md +22 -0
- package/src/prompts/review-headless-request.md +16 -0
- package/src/prompts/review-request.md +2 -3
- package/src/prompts/system/project-prompt.md +4 -0
- package/src/prompts/tools/debug.md +1 -0
- package/src/prompts/tools/find.md +4 -2
- package/src/prompts/tools/hashline.md +43 -93
- package/src/sdk.ts +47 -73
- package/src/session/agent-session.ts +93 -27
- package/src/session/streaming-output.ts +1 -1
- package/src/slash-commands/helpers/usage-report.ts +3 -1
- package/src/task/executor.ts +11 -0
- package/src/task/index.ts +19 -0
- package/src/task/render.ts +12 -2
- package/src/task/types.ts +4 -0
- package/src/tools/approval.ts +185 -0
- package/src/tools/ask.ts +1 -0
- package/src/tools/ast-edit.ts +25 -1
- package/src/tools/ast-grep.ts +1 -0
- package/src/tools/bash.ts +69 -1
- package/src/tools/browser/tab-supervisor.ts +1 -1
- package/src/tools/browser.ts +15 -0
- package/src/tools/calculator.ts +1 -0
- package/src/tools/checkpoint.ts +2 -0
- package/src/tools/debug.ts +38 -0
- package/src/tools/eval.ts +15 -0
- package/src/tools/find.ts +17 -8
- package/src/tools/gh.ts +21 -1
- package/src/tools/hindsight-recall.ts +1 -0
- package/src/tools/hindsight-reflect.ts +1 -0
- package/src/tools/hindsight-retain.ts +1 -0
- package/src/tools/image-gen.ts +1 -0
- package/src/tools/inspect-image.ts +1 -0
- package/src/tools/irc.ts +1 -0
- package/src/tools/job.ts +1 -0
- package/src/tools/path-utils.ts +14 -1
- package/src/tools/read.ts +1 -0
- package/src/tools/recipe/index.ts +1 -0
- package/src/tools/render-mermaid.ts +1 -0
- package/src/tools/report-tool-issue.ts +1 -0
- package/src/tools/resolve.ts +1 -0
- package/src/tools/review.ts +1 -0
- package/src/tools/search-tool-bm25.ts +1 -0
- package/src/tools/search.ts +1 -0
- package/src/tools/ssh.ts +8 -0
- package/src/tools/todo-write.ts +1 -0
- package/src/tools/write.ts +12 -1
- package/src/tools/yield.ts +1 -0
- package/src/web/search/index.ts +2 -0
|
@@ -139,9 +139,16 @@ export interface HookUIContext {
|
|
|
139
139
|
* Supports Ctrl+G to open external editor ($VISUAL or $EDITOR).
|
|
140
140
|
* @param title - Title describing what is being edited
|
|
141
141
|
* @param prefill - Optional initial text
|
|
142
|
+
* @param options - Optional dialog controls such as an abort signal
|
|
143
|
+
* @param editorOptions - Optional editor behavior; `promptStyle` makes Enter submit and Shift+Enter insert a newline
|
|
142
144
|
* @returns Edited text, or undefined if cancelled (Escape)
|
|
143
145
|
*/
|
|
144
|
-
editor(
|
|
146
|
+
editor(
|
|
147
|
+
title: string,
|
|
148
|
+
prefill?: string,
|
|
149
|
+
options?: { signal?: AbortSignal },
|
|
150
|
+
editorOptions?: { promptStyle?: boolean },
|
|
151
|
+
): Promise<string | undefined>;
|
|
145
152
|
|
|
146
153
|
/**
|
|
147
154
|
* Get the current theme for styling text with ANSI codes.
|
package/src/hashline/apply.ts
CHANGED
|
@@ -264,6 +264,44 @@ function countMatchingSingleStructuralSuffixBoundary(
|
|
|
264
264
|
return shouldDropSingleStructuralBoundary(replacement, replacement.slice(0, -1), expectedBalance) ? 1 : 0;
|
|
265
265
|
}
|
|
266
266
|
|
|
267
|
+
/**
|
|
268
|
+
* Single-line non-structural boundary duplicate detector for replacement
|
|
269
|
+
* groups. Mirrors the same boundary check the pure-insert absorber uses for
|
|
270
|
+
* `ANCHOR↓` (leading) / `ANCHOR↑` (trailing) inserts, but applied to the
|
|
271
|
+
* top/bottom edges of an `A-B:payload` range. Catches mistakes like
|
|
272
|
+
* `103-138:const X = …` where line 102 already reads `const X = …` and the
|
|
273
|
+
* user really meant `103-138!` (delete only).
|
|
274
|
+
*
|
|
275
|
+
* Gated by `options.autoDropPureInsertDuplicates`: the existing 2+-line block
|
|
276
|
+
* absorb already runs unconditionally, and the structural single-line
|
|
277
|
+
* absorber is balance-validated; a non-structural single-line duplicate is
|
|
278
|
+
* ambiguous (could be an intentional `2:foo` over a line that happens to
|
|
279
|
+
* sit next to another `foo`), so we only fire when the user has opted in.
|
|
280
|
+
*/
|
|
281
|
+
function countMatchingSingleNonStructuralPrefixDuplicate(
|
|
282
|
+
fileLines: string[],
|
|
283
|
+
startLine: number,
|
|
284
|
+
replacement: string[],
|
|
285
|
+
): number {
|
|
286
|
+
if (replacement.length === 0 || startLine <= 1) return 0;
|
|
287
|
+
const line = replacement[0];
|
|
288
|
+
if (isStructuralClosingBoundaryLine(line)) return 0;
|
|
289
|
+
if (fileLines[startLine - 2] !== line) return 0;
|
|
290
|
+
return 1;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function countMatchingSingleNonStructuralSuffixDuplicate(
|
|
294
|
+
fileLines: string[],
|
|
295
|
+
endLine: number,
|
|
296
|
+
replacement: string[],
|
|
297
|
+
): number {
|
|
298
|
+
if (replacement.length === 0 || endLine >= fileLines.length) return 0;
|
|
299
|
+
const line = replacement[replacement.length - 1];
|
|
300
|
+
if (isStructuralClosingBoundaryLine(line)) return 0;
|
|
301
|
+
if (fileLines[endLine] !== line) return 0;
|
|
302
|
+
return 1;
|
|
303
|
+
}
|
|
304
|
+
|
|
267
305
|
function hasExternalTargets(lines: Iterable<number>, externalTargetLines: Set<number>): boolean {
|
|
268
306
|
for (const line of lines) {
|
|
269
307
|
if (externalTargetLines.has(line)) return true;
|
|
@@ -552,12 +590,19 @@ function absorbReplacementBoundaryDuplicates(
|
|
|
552
590
|
const deletedBalance = computeDelimiterBalance(
|
|
553
591
|
group.deletes.map(deleteEdit => fileLines[deleteEdit.anchor.line - 1] ?? ""),
|
|
554
592
|
);
|
|
593
|
+
const optInSingleLineAbsorb = options.autoDropPureInsertDuplicates === true;
|
|
555
594
|
const prefixCount =
|
|
556
595
|
countMatchingPrefixBlock(fileLines, startLine, group.replacement) ||
|
|
557
|
-
countMatchingSingleStructuralPrefixBoundary(fileLines, startLine, group.replacement, deletedBalance)
|
|
596
|
+
countMatchingSingleStructuralPrefixBoundary(fileLines, startLine, group.replacement, deletedBalance) ||
|
|
597
|
+
(optInSingleLineAbsorb
|
|
598
|
+
? countMatchingSingleNonStructuralPrefixDuplicate(fileLines, startLine, group.replacement)
|
|
599
|
+
: 0);
|
|
558
600
|
const suffixCount =
|
|
559
601
|
countMatchingSuffixBlock(fileLines, endLine, group.replacement) ||
|
|
560
|
-
countMatchingSingleStructuralSuffixBoundary(fileLines, endLine, group.replacement, deletedBalance)
|
|
602
|
+
countMatchingSingleStructuralSuffixBoundary(fileLines, endLine, group.replacement, deletedBalance) ||
|
|
603
|
+
(optInSingleLineAbsorb
|
|
604
|
+
? countMatchingSingleNonStructuralSuffixDuplicate(fileLines, endLine, group.replacement)
|
|
605
|
+
: 0);
|
|
561
606
|
const prefixLines = contiguousRange(startLine - prefixCount, prefixCount);
|
|
562
607
|
const suffixLines = contiguousRange(endLine + 1, suffixCount);
|
|
563
608
|
const safePrefixCount = hasExternalTargets(prefixLines, allTargetLines) ? 0 : prefixCount;
|
package/src/hashline/executor.ts
CHANGED
|
@@ -30,7 +30,6 @@ type PendingOp =
|
|
|
30
30
|
interface Pending {
|
|
31
31
|
op: PendingOp;
|
|
32
32
|
payload: string[];
|
|
33
|
-
pendingBlanks: number;
|
|
34
33
|
}
|
|
35
34
|
|
|
36
35
|
/**
|
|
@@ -81,16 +80,16 @@ export class HashlineExecutor {
|
|
|
81
80
|
this.#terminated = true;
|
|
82
81
|
return;
|
|
83
82
|
case "header":
|
|
84
|
-
this.#flushPending(
|
|
83
|
+
this.#flushPending();
|
|
85
84
|
return;
|
|
86
85
|
case "blank":
|
|
87
|
-
if (this.#pending) this.#pending.
|
|
86
|
+
if (this.#pending) this.#pending.payload.push("");
|
|
88
87
|
return;
|
|
89
88
|
case "payload":
|
|
90
89
|
this.#handlePayload(token.text, token.lineNum);
|
|
91
90
|
return;
|
|
92
91
|
case "op-delete":
|
|
93
|
-
this.#flushPending(
|
|
92
|
+
this.#flushPending();
|
|
94
93
|
if (token.trailingPayload) {
|
|
95
94
|
throw new Error(
|
|
96
95
|
`line ${token.lineNum}: ${HL_OP_DELETE} deletes only. Payload is forbidden after ${HL_OP_DELETE}; use ${HL_OP_REPLACE} to replace.`,
|
|
@@ -102,32 +101,34 @@ export class HashlineExecutor {
|
|
|
102
101
|
}
|
|
103
102
|
return;
|
|
104
103
|
case "op-insert":
|
|
105
|
-
this.#flushPending(
|
|
104
|
+
this.#flushPending();
|
|
106
105
|
this.#pending = {
|
|
107
106
|
op: { kind: "insert", cursor: token.cursor, lineNum: token.lineNum },
|
|
108
107
|
payload: [token.inlineBody ?? ""],
|
|
109
|
-
pendingBlanks: 0,
|
|
110
108
|
};
|
|
111
109
|
return;
|
|
112
110
|
case "op-replace":
|
|
113
|
-
this.#flushPending(
|
|
111
|
+
this.#flushPending();
|
|
114
112
|
validateRangeOrder(token.range, token.lineNum);
|
|
115
113
|
this.#pending = {
|
|
116
114
|
op: { kind: "replace", range: token.range, lineNum: token.lineNum },
|
|
117
115
|
payload: [token.inlineBody ?? ""],
|
|
118
|
-
pendingBlanks: 0,
|
|
119
116
|
};
|
|
120
117
|
return;
|
|
121
118
|
}
|
|
122
119
|
}
|
|
123
120
|
|
|
124
121
|
/**
|
|
125
|
-
* Flush any open pending op (
|
|
126
|
-
*
|
|
127
|
-
*
|
|
122
|
+
* Flush any open pending op (with its full accumulated payload, blanks
|
|
123
|
+
* included) and return the accumulated edits and warnings. The executor
|
|
124
|
+
* is single-use; reset() is required for reuse.
|
|
125
|
+
* Throws if two replace/delete ops target the same line — that pattern
|
|
126
|
+
* means the diff is painting a before/after picture instead of stating
|
|
127
|
+
* the final state, and applying both would silently duplicate content.
|
|
128
128
|
*/
|
|
129
129
|
end(): { edits: HashlineEdit[]; warnings: string[] } {
|
|
130
|
-
this.#flushPending(
|
|
130
|
+
this.#flushPending();
|
|
131
|
+
this.#validateNoOverlappingDeletes();
|
|
131
132
|
return { edits: this.#edits, warnings: this.#warnings };
|
|
132
133
|
}
|
|
133
134
|
|
|
@@ -140,16 +141,44 @@ export class HashlineExecutor {
|
|
|
140
141
|
this.#terminated = false;
|
|
141
142
|
}
|
|
142
143
|
|
|
144
|
+
/**
|
|
145
|
+
* Each `:` / `!` op contributes a delete edit per line in its range; if
|
|
146
|
+
* any line ends up targeted by deletes originating from two different
|
|
147
|
+
* source ops (distinguished by their `lineNum`), the patch is internally
|
|
148
|
+
* inconsistent. Common shape: a "before" `A-B:` followed by an "after"
|
|
149
|
+
* `A-B:` over the same range, or an `A-B:` that overlaps a later `N!` /
|
|
150
|
+
* `N:`. The applier would run both literally and the file would end up
|
|
151
|
+
* with two copies of the line, not a chosen winner.
|
|
152
|
+
*/
|
|
153
|
+
#validateNoOverlappingDeletes(): void {
|
|
154
|
+
const sourceLinesByAnchor = new Map<number, number[]>();
|
|
155
|
+
for (const edit of this.#edits) {
|
|
156
|
+
if (edit.kind !== "delete") continue;
|
|
157
|
+
let sourceLines = sourceLinesByAnchor.get(edit.anchor.line);
|
|
158
|
+
if (sourceLines === undefined) {
|
|
159
|
+
sourceLines = [];
|
|
160
|
+
sourceLinesByAnchor.set(edit.anchor.line, sourceLines);
|
|
161
|
+
}
|
|
162
|
+
if (!sourceLines.includes(edit.lineNum)) sourceLines.push(edit.lineNum);
|
|
163
|
+
}
|
|
164
|
+
for (const [anchorLine, sourceLines] of sourceLinesByAnchor) {
|
|
165
|
+
if (sourceLines.length < 2) continue;
|
|
166
|
+
const [firstOp, secondOp] = [...sourceLines].sort((a, b) => a - b);
|
|
167
|
+
throw new Error(
|
|
168
|
+
`line ${secondOp}: anchor line ${anchorLine} is already targeted by the ${HL_OP_REPLACE}/${HL_OP_DELETE} op on line ${firstOp}. ` +
|
|
169
|
+
`Issue ONE op per range; payload is only the final desired content, never a before/after pair.`,
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
143
174
|
#handlePayload(text: string, lineNum: number): void {
|
|
144
175
|
if (this.#pending) {
|
|
145
|
-
this.#flushPendingBlanks();
|
|
146
176
|
this.#pending.payload.push(text);
|
|
147
177
|
return;
|
|
148
178
|
}
|
|
149
179
|
|
|
150
|
-
// Whitespace-only payload outside any pending op is
|
|
151
|
-
//
|
|
152
|
-
// only fully-empty lines arrive as `blank` tokens.
|
|
180
|
+
// Whitespace-only payload outside any pending op is silently dropped;
|
|
181
|
+
// fully empty lines arrive as `blank` tokens.
|
|
153
182
|
if (text.trim().length === 0) return;
|
|
154
183
|
// Orphan payload outside any pending op: pick the most specific
|
|
155
184
|
// diagnostic so the model sees the actionable hint.
|
|
@@ -174,16 +203,9 @@ export class HashlineExecutor {
|
|
|
174
203
|
);
|
|
175
204
|
}
|
|
176
205
|
|
|
177
|
-
#
|
|
178
|
-
if (!this.#pending) return;
|
|
179
|
-
for (let count = 0; count < this.#pending.pendingBlanks; count++) this.#pending.payload.push("");
|
|
180
|
-
this.#pending.pendingBlanks = 0;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
#flushPending(includeTrailingBlanks: boolean): void {
|
|
206
|
+
#flushPending(): void {
|
|
184
207
|
const pending = this.#pending;
|
|
185
208
|
if (!pending) return;
|
|
186
|
-
if (includeTrailingBlanks) this.#flushPendingBlanks();
|
|
187
209
|
|
|
188
210
|
const { op, payload } = pending;
|
|
189
211
|
const linesToInsert = payload;
|