koneck 2.109.0 → 2.111.0
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/dist/chat.d.ts.map +1 -1
- package/dist/chat.js +4 -3
- package/dist/chat.js.map +1 -1
- package/dist/ink-chat.d.ts.map +1 -1
- package/dist/ink-chat.js +3 -1
- package/dist/ink-chat.js.map +1 -1
- package/dist/run-progress.js +1 -1
- package/dist/run-progress.js.map +1 -1
- package/dist/skills.d.ts +21 -0
- package/dist/skills.d.ts.map +1 -0
- package/dist/skills.js +39 -0
- package/dist/skills.js.map +1 -0
- package/dist/tools.d.ts +30 -0
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +195 -9
- package/dist/tools.js.map +1 -1
- package/dist/web/api.d.ts.map +1 -1
- package/dist/web/api.js +16 -0
- package/dist/web/api.js.map +1 -1
- package/dist/web/ui-client.d.ts.map +1 -1
- package/dist/web/ui-client.js +209 -1
- package/dist/web/ui-client.js.map +1 -1
- package/dist/web/ui-css.d.ts.map +1 -1
- package/dist/web/ui-css.js +27 -0
- package/dist/web/ui-css.js.map +1 -1
- package/package.json +1 -1
package/dist/run-progress.js
CHANGED
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
import { estimateCost, formatCost } from './pricing.js';
|
|
23
23
|
/** Tools whose success means the workspace changed. */
|
|
24
24
|
const MUTATING = new Set([
|
|
25
|
-
'write_file', 'apply_diff', 'search_replace', 'move_file', 'delete_file',
|
|
25
|
+
'write_file', 'apply_diff', 'search_replace', 'multi_edit', 'move_file', 'delete_file',
|
|
26
26
|
]);
|
|
27
27
|
export function summariseProgress(events, plan, agents = 0) {
|
|
28
28
|
const touched = new Set();
|
package/dist/run-progress.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-progress.js","sourceRoot":"","sources":["../src/run-progress.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAGH,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAExD,uDAAuD;AACvD,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC;IACvB,YAAY,EAAE,YAAY,EAAE,gBAAgB,EAAE,WAAW,EAAE,aAAa;
|
|
1
|
+
{"version":3,"file":"run-progress.js","sourceRoot":"","sources":["../src/run-progress.ts"],"names":[],"mappings":"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"}
|
package/dist/skills.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The analysis commands, as prompts both interfaces can send.
|
|
3
|
+
*
|
|
4
|
+
* /architect, /performance and /security were canned messages inside the terminal's command
|
|
5
|
+
* switch — a fixed prompt sent through the ordinary path, exactly like /compact. That made them
|
|
6
|
+
* trivial to run from a prompt and impossible to run from the browser, which is the gap this closes.
|
|
7
|
+
*
|
|
8
|
+
* Kept here rather than duplicated so the two interfaces cannot drift: the web palette and the
|
|
9
|
+
* terminal send the same words. Each is a review that produces findings with file:line references,
|
|
10
|
+
* which is what makes the output something a person can act on rather than an essay.
|
|
11
|
+
*/
|
|
12
|
+
export interface Skill {
|
|
13
|
+
/** The palette/command name. */
|
|
14
|
+
name: string;
|
|
15
|
+
/** One line, for the palette. */
|
|
16
|
+
hint: string;
|
|
17
|
+
/** The message sent to the model. */
|
|
18
|
+
prompt: string;
|
|
19
|
+
}
|
|
20
|
+
export declare const SKILLS: Readonly<Record<string, Skill>>;
|
|
21
|
+
//# sourceMappingURL=skills.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skills.d.ts","sourceRoot":"","sources":["../src/skills.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,MAAM,WAAW,KAAK;IACpB,gCAAgC;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,iCAAiC;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,qCAAqC;IACrC,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,eAAO,MAAM,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAwClD,CAAC"}
|
package/dist/skills.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export const SKILLS = {
|
|
2
|
+
architect: {
|
|
3
|
+
name: 'Architecture review',
|
|
4
|
+
hint: 'overview, coupling and god objects, concrete fixes, a dependency diagram',
|
|
5
|
+
prompt: 'Analyze the current project architecture. Produce:\n'
|
|
6
|
+
+ '1. Overview of the architecture\n'
|
|
7
|
+
+ '2. Current problems (coupling, violations, god objects)\n'
|
|
8
|
+
+ '3. Concrete improvement suggestions\n'
|
|
9
|
+
+ '4. A dependency diagram as a mermaid graph (```mermaid ... ```)\n'
|
|
10
|
+
+ 'Be specific with file paths and module names.',
|
|
11
|
+
},
|
|
12
|
+
performance: {
|
|
13
|
+
name: 'Performance audit',
|
|
14
|
+
hint: 'N+1 queries, leaks, blocking I/O, missing indexes — with file:line',
|
|
15
|
+
prompt: 'Perform a performance audit of this codebase. Find and report:\n'
|
|
16
|
+
+ '1. N+1 query patterns\n'
|
|
17
|
+
+ '2. Memory leaks or unbounded growth\n'
|
|
18
|
+
+ '3. Blocking I/O in async contexts\n'
|
|
19
|
+
+ '4. Slow rendering or re-render triggers (if React)\n'
|
|
20
|
+
+ '5. Missing database indexes\n'
|
|
21
|
+
+ '6. Unnecessary re-computation\n'
|
|
22
|
+
+ 'Include file:line references for each finding.',
|
|
23
|
+
},
|
|
24
|
+
security: {
|
|
25
|
+
name: 'Security audit',
|
|
26
|
+
hint: 'OWASP Top 10, injection, secrets, traversal — each with severity',
|
|
27
|
+
prompt: 'Perform a full security audit of this codebase. Check for:\n'
|
|
28
|
+
+ '- OWASP Top 10 vulnerabilities\n'
|
|
29
|
+
+ '- JWT/session security\n'
|
|
30
|
+
+ '- XSS, CSRF, SSRF vectors\n'
|
|
31
|
+
+ '- SQL/command injection\n'
|
|
32
|
+
+ '- Exposed secrets or API keys\n'
|
|
33
|
+
+ '- Unsafe regex (ReDoS)\n'
|
|
34
|
+
+ '- Insecure dependencies\n'
|
|
35
|
+
+ '- Path traversal\n'
|
|
36
|
+
+ 'Report each finding with file:line and severity (critical/high/medium/low).',
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
//# sourceMappingURL=skills.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skills.js","sourceRoot":"","sources":["../src/skills.ts"],"names":[],"mappings":"AAoBA,MAAM,CAAC,MAAM,MAAM,GAAoC;IACrD,SAAS,EAAE;QACT,IAAI,EAAE,qBAAqB;QAC3B,IAAI,EAAE,0EAA0E;QAChF,MAAM,EACJ,sDAAsD;cACpD,mCAAmC;cACnC,2DAA2D;cAC3D,uCAAuC;cACvC,mEAAmE;cACnE,+CAA+C;KACpD;IACD,WAAW,EAAE;QACX,IAAI,EAAE,mBAAmB;QACzB,IAAI,EAAE,oEAAoE;QAC1E,MAAM,EACJ,kEAAkE;cAChE,yBAAyB;cACzB,uCAAuC;cACvC,qCAAqC;cACrC,sDAAsD;cACtD,+BAA+B;cAC/B,iCAAiC;cACjC,gDAAgD;KACrD;IACD,QAAQ,EAAE;QACR,IAAI,EAAE,gBAAgB;QACtB,IAAI,EAAE,kEAAkE;QACxE,MAAM,EACJ,8DAA8D;cAC5D,kCAAkC;cAClC,0BAA0B;cAC1B,6BAA6B;cAC7B,2BAA2B;cAC3B,iCAAiC;cACjC,0BAA0B;cAC1B,2BAA2B;cAC3B,oBAAoB;cACpB,6EAA6E;KAClF;CACF,CAAC"}
|
package/dist/tools.d.ts
CHANGED
|
@@ -78,6 +78,36 @@ export declare function grepSearch(args: GrepSearchArgs, cwd: string): Promise<s
|
|
|
78
78
|
export declare function moveFile(args: MoveFileArgs, cwd: string): Promise<string>;
|
|
79
79
|
export declare function deleteFile(args: DeleteFileArgs, cwd: string, requireApproval: boolean): Promise<string>;
|
|
80
80
|
export declare function searchReplace(args: SearchReplaceArgs, cwd: string, requireApproval: boolean): Promise<string>;
|
|
81
|
+
export interface MultiEditArgs {
|
|
82
|
+
path: string;
|
|
83
|
+
edits: Array<{
|
|
84
|
+
search: string;
|
|
85
|
+
replace: string;
|
|
86
|
+
replace_all?: boolean;
|
|
87
|
+
}>;
|
|
88
|
+
}
|
|
89
|
+
export declare function multiEdit(args: MultiEditArgs, cwd: string, requireApproval: boolean): Promise<string>;
|
|
90
|
+
/**
|
|
91
|
+
* Finds a search string in text, forgiving whitespace, but only if it lands in exactly one place.
|
|
92
|
+
*
|
|
93
|
+
* Runs of spaces and tabs are treated as equivalent and line endings normalised, which covers the
|
|
94
|
+
* near-totality of "not found" misses: the model has the right lines and the wrong indentation, or
|
|
95
|
+
* LF against the file's CRLF. Returns the actual substring of the file that matched, so the caller
|
|
96
|
+
* replaces the real bytes rather than the model's approximation of them.
|
|
97
|
+
*
|
|
98
|
+
* One place only. A pattern that could match two spots is not a mismatch to forgive, it is an
|
|
99
|
+
* ambiguity to refuse — editing the wrong one of two is worse than editing neither.
|
|
100
|
+
*/
|
|
101
|
+
export declare function fuzzyFindOnce(haystack: string, needle: string): string | null;
|
|
102
|
+
/**
|
|
103
|
+
* The not-found message, showing the file's own nearby text so a correction is possible.
|
|
104
|
+
*
|
|
105
|
+
* "Search string not found. Use read_files and retry" sends the model to read a file it had just
|
|
106
|
+
* read. What it actually needs is to see how what it looked for differs from what is there, so this
|
|
107
|
+
* finds the line most like the search's first line and shows it, which is usually enough to spot a
|
|
108
|
+
* renamed identifier or a changed indent without another full read.
|
|
109
|
+
*/
|
|
110
|
+
export declare function searchNotFound(pathLabel: string, original: string, search: string): string;
|
|
81
111
|
export declare function fetchUrl(args: FetchUrlArgs, signal?: AbortSignal): Promise<string>;
|
|
82
112
|
export declare function webSearch(args: WebSearchArgs, signal?: AbortSignal): Promise<string>;
|
|
83
113
|
/**
|
package/dist/tools.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EACV,cAAc,EACd,aAAa,EACb,aAAa,EACb,aAAa,EACb,kBAAkB,EAClB,WAAW,EACX,cAAc,EACd,cAAc,EACd,YAAY,EACZ,cAAc,EACd,iBAAiB,EACjB,YAAY,EACZ,YAAY,EACZ,aAAa,EACd,MAAM,YAAY,CAAC;AAGpB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhD,eAAO,MAAM,gBAAgB,EAAE,cAAc,
|
|
1
|
+
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EACV,cAAc,EACd,aAAa,EACb,aAAa,EACb,aAAa,EACb,kBAAkB,EAClB,WAAW,EACX,cAAc,EACd,cAAc,EACd,YAAY,EACZ,cAAc,EACd,iBAAiB,EACjB,YAAY,EACZ,YAAY,EACZ,aAAa,EACd,MAAM,YAAY,CAAC;AAGpB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhD,eAAO,MAAM,gBAAgB,EAAE,cAAc,EAgc5C,CAAC;AAIF;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,MAAM,CAAC;AAyClC,wBAAsB,SAAS,CAAC,IAAI,EAAE,aAAa,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA2BjF;AAED,wBAAsB,SAAS,CAC7B,IAAI,EAAE,aAAa,EACnB,GAAG,EAAE,MAAM,EACX,eAAe,EAAE,OAAO,GACvB,OAAO,CAAC,MAAM,CAAC,CA4BjB;AAED,wBAAsB,SAAS,CAC7B,IAAI,EAAE,aAAa,EACnB,GAAG,EAAE,MAAM,EACX,eAAe,EAAE,OAAO,GACvB,OAAO,CAAC,MAAM,CAAC,CA2DjB;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,QAAc,CAAC;AAwB1C,0DAA0D;AAC1D,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED,wBAAgB,cAAc,CAC5B,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,OAAO,SAAK,GACX;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAOlC;AAED,oFAAoF;AACpF,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAG7C;AAGD;;;;;;;;;;;GAWG;AACH,wBAAgB,QAAQ,CACtB,OAAO,EAAE,MAAM,EACf,QAAQ,GAAE,MAAyB,EACnC,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAoBpE;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,GAAE,MAAyB,EACnC,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,MAAM,CAIR;AAED,wBAAsB,cAAc,CAClC,IAAI,EAAE,kBAAkB,EACxB,GAAG,EAAE,MAAM,EACX,eAAe,EAAE,OAAO,EACxB,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,EACrC,OAAO,GAAE,WAAgD,EACzD,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,MAAM,CAAC,CA2JjB;AAED;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,sFACyD,CAAC;AAEtF;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,WAAW,CAG3E;AA+CD,wBAAsB,OAAO,CAAC,IAAI,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA8C7E;AAED,wBAAsB,UAAU,CAAC,IAAI,EAAE,cAAc,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAkBnF;AAiCD,wBAAsB,UAAU,CAAC,IAAI,EAAE,cAAc,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAmDnF;AAuGD,wBAAsB,QAAQ,CAAC,IAAI,EAAE,YAAY,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAqB/E;AAED,wBAAsB,UAAU,CAC9B,IAAI,EAAE,cAAc,EACpB,GAAG,EAAE,MAAM,EACX,eAAe,EAAE,OAAO,GACvB,OAAO,CAAC,MAAM,CAAC,CA4BjB;AAcD,wBAAsB,aAAa,CACjC,IAAI,EAAE,iBAAiB,EACvB,GAAG,EAAE,MAAM,EACX,eAAe,EAAE,OAAO,GACvB,OAAO,CAAC,MAAM,CAAC,CA4DjB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;CAC1E;AAcD,wBAAsB,SAAS,CAC7B,IAAI,EAAE,aAAa,EACnB,GAAG,EAAE,MAAM,EACX,eAAe,EAAE,OAAO,GACvB,OAAO,CAAC,MAAM,CAAC,CAiDjB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAe7E;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAgB1F;AAmBD,wBAAsB,QAAQ,CAAC,IAAI,EAAE,YAAY,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CA6BxF;AA0CD,wBAAsB,SAAS,CAAC,IAAI,EAAE,aAAa,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAyB1F;AAID;;;;;;;;;;;;;GAaG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAcjE;AAED,wBAAsB,QAAQ,CAAC,IAAI,EAAE,YAAY,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAa/E;AAKD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,YAAY,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAyBzD,CAAC;AAEF,oEAAoE;AACpE,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,GAAG,IAAI,CAG1F;AAED,oFAAoF;AACpF,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAGlE;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAoBnF;AA+BD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAmBxE;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,EAAE,CA0C/E;AAGD;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAwBhF;AAGD,iFAAiF;AACjF,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,MAAM,EAAE,CAiBpE;AAGD;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAgBrD;AAED,wBAAsB,YAAY,CAChC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,EACX,eAAe,EAAE,OAAO,EACxB,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,EACrC,OAAO,GAAE,WAAgD,EASzD,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,MAAM,CAAC,CAuDjB;AAID,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAiDD,MAAM,WAAW,WAAW;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,KAAK,CAAC;QAAE,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACnF,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,6DAA6D;IAC7D,IAAI,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3D,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AA0CD,0FAA0F;AAC1F,wBAAgB,cAAc,IAAI,OAAO,cAAc,EAAE,OAAO,GAAG,IAAI,CAEtE;AAED,wFAAwF;AACxF,wBAAsB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CAIlD"}
|
package/dist/tools.js
CHANGED
|
@@ -271,9 +271,10 @@ export const TOOL_DEFINITIONS = [
|
|
|
271
271
|
},
|
|
272
272
|
{
|
|
273
273
|
name: 'search_replace',
|
|
274
|
-
description: 'Find
|
|
275
|
-
'
|
|
276
|
-
'
|
|
274
|
+
description: 'Find a string in a file and replace it. The most reliable editing tool — prefer it over ' +
|
|
275
|
+
'apply_diff for targeted changes. Matches exactly, and falls back to matching that forgives ' +
|
|
276
|
+
'whitespace differences (tabs vs spaces, CRLF vs LF) when the exact string is not found. ' +
|
|
277
|
+
'To change several places in one file, use multi_edit instead of calling this repeatedly.',
|
|
277
278
|
parameters: {
|
|
278
279
|
type: 'object',
|
|
279
280
|
properties: {
|
|
@@ -294,6 +295,34 @@ export const TOOL_DEFINITIONS = [
|
|
|
294
295
|
required: ['path', 'search', 'replace'],
|
|
295
296
|
},
|
|
296
297
|
},
|
|
298
|
+
{
|
|
299
|
+
name: 'multi_edit',
|
|
300
|
+
description: 'Make several search-and-replace edits to ONE file in a single call, applied in order and '
|
|
301
|
+
+ 'all-or-nothing: if any edit does not match, the file is left untouched and the failure '
|
|
302
|
+
+ 'says which one. This is how you change a file in more than one place — five edits here is '
|
|
303
|
+
+ 'one round trip instead of five, which is most of what makes a large task slow. Each edit '
|
|
304
|
+
+ 'matches exactly, then forgiving whitespace, the same as search_replace.',
|
|
305
|
+
parameters: {
|
|
306
|
+
type: 'object',
|
|
307
|
+
properties: {
|
|
308
|
+
path: { type: 'string', description: 'The one file to edit' },
|
|
309
|
+
edits: {
|
|
310
|
+
type: 'array',
|
|
311
|
+
description: 'The edits, applied top to bottom. A later edit sees the earlier ones already made.',
|
|
312
|
+
items: {
|
|
313
|
+
type: 'object',
|
|
314
|
+
properties: {
|
|
315
|
+
search: { type: 'string', description: 'String to find' },
|
|
316
|
+
replace: { type: 'string', description: 'String to put in its place' },
|
|
317
|
+
replace_all: { type: 'boolean', description: 'Replace every occurrence (default: false)' },
|
|
318
|
+
},
|
|
319
|
+
required: ['search', 'replace'],
|
|
320
|
+
},
|
|
321
|
+
},
|
|
322
|
+
},
|
|
323
|
+
required: ['path', 'edits'],
|
|
324
|
+
},
|
|
325
|
+
},
|
|
297
326
|
{
|
|
298
327
|
name: 'fetch_url',
|
|
299
328
|
description: 'Fetch the content of a URL. HTML is stripped to readable text. ' +
|
|
@@ -532,6 +561,22 @@ export async function applyDiff(args, cwd, requireApproval) {
|
|
|
532
561
|
const changes = diffLib.diffLines(original, patched);
|
|
533
562
|
const added = changes.filter(c => c.added).reduce((n, c) => n + (c.count ?? 0), 0);
|
|
534
563
|
const removed = changes.filter(c => c.removed).reduce((n, c) => n + (c.count ?? 0), 0);
|
|
564
|
+
/*
|
|
565
|
+
* A patch that applied but changed nothing is a failure, not a success.
|
|
566
|
+
*
|
|
567
|
+
* diff's applyPatch returns the file unchanged when a hunk's context is found but its additions
|
|
568
|
+
* are already present, or when a malformed diff produces no real hunks. The old code reported that
|
|
569
|
+
* as "Applied diff: +0 -0 lines" — a success line for a write that did nothing. The model saw it
|
|
570
|
+
* for what it was ("the apply_diff reported +0 -0 lines — suspicious"), spent a turn verifying,
|
|
571
|
+
* then a turn on search_replace, then more reading: a whole detour caused by a green message that
|
|
572
|
+
* should have been red. Now it says so and points at the reliable tool.
|
|
573
|
+
*/
|
|
574
|
+
if (added === 0 && removed === 0) {
|
|
575
|
+
return `ERROR: The diff applied but changed nothing in ${args.path} (+0 -0 lines). Either the `
|
|
576
|
+
+ `lines it adds are already present, or its context lines were matched without any real edit `
|
|
577
|
+
+ `between them. Nothing was written. Read the file to see its current state, then use `
|
|
578
|
+
+ `search_replace with the exact text to change — it is more reliable than a diff here.`;
|
|
579
|
+
}
|
|
535
580
|
return `Applied diff to ${args.path}: +${added} -${removed} lines`;
|
|
536
581
|
}
|
|
537
582
|
/**
|
|
@@ -1165,14 +1210,33 @@ export async function searchReplace(args, cwd, requireApproval) {
|
|
|
1165
1210
|
catch {
|
|
1166
1211
|
return `ERROR: Cannot read ${args.path} — file does not exist.`;
|
|
1167
1212
|
}
|
|
1168
|
-
|
|
1213
|
+
let occurrences = countOccurrences(original, args.search);
|
|
1214
|
+
let matchedSearch = args.search;
|
|
1215
|
+
let fuzzy = false;
|
|
1216
|
+
/*
|
|
1217
|
+
* When the exact text is not found, try to find it forgiving whitespace.
|
|
1218
|
+
*
|
|
1219
|
+
* This is the single most common edit failure and it is almost never a real mismatch: the model
|
|
1220
|
+
* has the right lines but a tab where the file has spaces, a trailing space, or CRLF against LF.
|
|
1221
|
+
* Reported with a screenshot — "Search string not found" on a file the model had just read
|
|
1222
|
+
* correctly. So a miss falls back to matching with runs of whitespace treated as equivalent and
|
|
1223
|
+
* line endings normalised, and only when that lands on exactly one place is it used, because a
|
|
1224
|
+
* fuzzy match that could be two places is a guess and a guess must not edit a file.
|
|
1225
|
+
*/
|
|
1169
1226
|
if (occurrences === 0) {
|
|
1170
|
-
|
|
1171
|
-
|
|
1227
|
+
const found = fuzzyFindOnce(original, args.search);
|
|
1228
|
+
if (found) {
|
|
1229
|
+
matchedSearch = found;
|
|
1230
|
+
occurrences = 1;
|
|
1231
|
+
fuzzy = true;
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
if (occurrences === 0) {
|
|
1235
|
+
return searchNotFound(args.path, original, args.search);
|
|
1172
1236
|
}
|
|
1173
1237
|
const patched = args.replace_all
|
|
1174
|
-
? original.split(
|
|
1175
|
-
: original.replace(
|
|
1238
|
+
? original.split(matchedSearch).join(args.replace)
|
|
1239
|
+
: original.replace(matchedSearch, args.replace);
|
|
1176
1240
|
const validation = validateCode(patched, abs);
|
|
1177
1241
|
if (!validation.valid) {
|
|
1178
1242
|
return [
|
|
@@ -1190,7 +1254,126 @@ export async function searchReplace(args, cwd, requireApproval) {
|
|
|
1190
1254
|
const replaced = args.replace_all ? occurrences : 1;
|
|
1191
1255
|
const skipped = args.replace_all ? 0 : occurrences - 1;
|
|
1192
1256
|
const note = skipped > 0 ? ` (${skipped} additional occurrence(s) left unchanged — pass replace_all:true to replace all)` : '';
|
|
1193
|
-
|
|
1257
|
+
// Said when it was a whitespace-forgiving match, because the model should know its exact string
|
|
1258
|
+
// did not match — so the next edit it writes is against what the file really contains.
|
|
1259
|
+
const how = fuzzy ? ' (matched ignoring whitespace differences)' : '';
|
|
1260
|
+
return `Replaced ${replaced} occurrence(s) in ${args.path}${note}${how}`;
|
|
1261
|
+
}
|
|
1262
|
+
/*
|
|
1263
|
+
* Several edits to one file in one call, all-or-nothing.
|
|
1264
|
+
*
|
|
1265
|
+
* Round trips are what make a large task slow — the sidebar that took thirty-one minutes was mostly
|
|
1266
|
+
* the same file read and edited a piece at a time, each piece its own request re-reading the whole
|
|
1267
|
+
* conversation. This changes a file in as many places as needed for one request, and either every
|
|
1268
|
+
* edit lands or none does: a half-applied set of edits leaves a file in a state neither the model
|
|
1269
|
+
* nor the person asked for, which is worse than a clean failure that says which edit missed.
|
|
1270
|
+
*
|
|
1271
|
+
* Each edit is matched the same way search_replace matches — exactly, then forgiving whitespace —
|
|
1272
|
+
* and applied to the result of the ones before it, so an edit may depend on an earlier one.
|
|
1273
|
+
*/
|
|
1274
|
+
export async function multiEdit(args, cwd, requireApproval) {
|
|
1275
|
+
const abs = path.resolve(cwd, args.path);
|
|
1276
|
+
if (!Array.isArray(args.edits) || args.edits.length === 0) {
|
|
1277
|
+
return `ERROR: multi_edit needs a non-empty "edits" array of {search, replace}.`;
|
|
1278
|
+
}
|
|
1279
|
+
let original;
|
|
1280
|
+
try {
|
|
1281
|
+
original = await fs.readFile(abs, 'utf-8');
|
|
1282
|
+
}
|
|
1283
|
+
catch {
|
|
1284
|
+
return `ERROR: Cannot read ${args.path} — file does not exist.`;
|
|
1285
|
+
}
|
|
1286
|
+
// Applied to a working copy first, so nothing is written until every edit has matched.
|
|
1287
|
+
let working = original;
|
|
1288
|
+
for (let i = 0; i < args.edits.length; i++) {
|
|
1289
|
+
const e = args.edits[i];
|
|
1290
|
+
if (typeof e.search !== 'string' || typeof e.replace !== 'string') {
|
|
1291
|
+
return `ERROR: edit ${i + 1} needs a string "search" and "replace". Nothing was written.`;
|
|
1292
|
+
}
|
|
1293
|
+
let target = e.search;
|
|
1294
|
+
if (countOccurrences(working, target) === 0) {
|
|
1295
|
+
const fuzzy = fuzzyFindOnce(working, e.search);
|
|
1296
|
+
if (!fuzzy) {
|
|
1297
|
+
return `ERROR: edit ${i + 1} of ${args.edits.length} did not match `
|
|
1298
|
+
+ `(searched for ${JSON.stringify(e.search.slice(0, 60))}`
|
|
1299
|
+
+ `${e.search.length > 60 ? '…' : ''}). The file is unchanged — fix this edit and send `
|
|
1300
|
+
+ `the whole set again. ${searchNotFound(args.path, working, e.search).replace(/^ERROR: /, '')}`;
|
|
1301
|
+
}
|
|
1302
|
+
target = fuzzy;
|
|
1303
|
+
}
|
|
1304
|
+
working = e.replace_all ? working.split(target).join(e.replace) : working.replace(target, e.replace);
|
|
1305
|
+
}
|
|
1306
|
+
if (working === original) {
|
|
1307
|
+
return `ERROR: The edits matched but changed nothing in ${args.path}. Nothing was written.`;
|
|
1308
|
+
}
|
|
1309
|
+
const validation = validateCode(working, abs);
|
|
1310
|
+
if (!validation.valid) {
|
|
1311
|
+
return [
|
|
1312
|
+
`ERROR: The edits would leave ${args.path} with syntax errors — nothing written.`,
|
|
1313
|
+
...validation.errors.map(e => ` • ${e}`),
|
|
1314
|
+
].join('\n');
|
|
1315
|
+
}
|
|
1316
|
+
if (requireApproval) {
|
|
1317
|
+
tui.renderDiff(original, working, args.path);
|
|
1318
|
+
if (!await tui.confirmDiff(args.path))
|
|
1319
|
+
return `Edits to ${args.path} cancelled by user.`;
|
|
1320
|
+
}
|
|
1321
|
+
await fs.writeFile(abs, working, 'utf-8');
|
|
1322
|
+
return `Applied ${args.edits.length} edit${args.edits.length === 1 ? '' : 's'} to ${args.path}.`;
|
|
1323
|
+
}
|
|
1324
|
+
/**
|
|
1325
|
+
* Finds a search string in text, forgiving whitespace, but only if it lands in exactly one place.
|
|
1326
|
+
*
|
|
1327
|
+
* Runs of spaces and tabs are treated as equivalent and line endings normalised, which covers the
|
|
1328
|
+
* near-totality of "not found" misses: the model has the right lines and the wrong indentation, or
|
|
1329
|
+
* LF against the file's CRLF. Returns the actual substring of the file that matched, so the caller
|
|
1330
|
+
* replaces the real bytes rather than the model's approximation of them.
|
|
1331
|
+
*
|
|
1332
|
+
* One place only. A pattern that could match two spots is not a mismatch to forgive, it is an
|
|
1333
|
+
* ambiguity to refuse — editing the wrong one of two is worse than editing neither.
|
|
1334
|
+
*/
|
|
1335
|
+
export function fuzzyFindOnce(haystack, needle) {
|
|
1336
|
+
const norm = (t) => t.replace(/\r\n/g, '\n').replace(/[ \t]+/g, ' ');
|
|
1337
|
+
const nNeedle = norm(needle).trim();
|
|
1338
|
+
if (nNeedle === '')
|
|
1339
|
+
return null;
|
|
1340
|
+
// Build a regex over the file that matches the needle with flexible whitespace, then read back
|
|
1341
|
+
// the exact original slice at the single match.
|
|
1342
|
+
const escaped = nNeedle.split(/ /).map(part => part.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('[ \\t]+');
|
|
1343
|
+
let re;
|
|
1344
|
+
try {
|
|
1345
|
+
re = new RegExp(escaped.replace(/\n/g, '\\s*\\n\\s*'), 'g');
|
|
1346
|
+
}
|
|
1347
|
+
catch {
|
|
1348
|
+
return null;
|
|
1349
|
+
}
|
|
1350
|
+
const matches = [...haystack.matchAll(re)];
|
|
1351
|
+
return matches.length === 1 ? matches[0][0] : null;
|
|
1352
|
+
}
|
|
1353
|
+
/**
|
|
1354
|
+
* The not-found message, showing the file's own nearby text so a correction is possible.
|
|
1355
|
+
*
|
|
1356
|
+
* "Search string not found. Use read_files and retry" sends the model to read a file it had just
|
|
1357
|
+
* read. What it actually needs is to see how what it looked for differs from what is there, so this
|
|
1358
|
+
* finds the line most like the search's first line and shows it, which is usually enough to spot a
|
|
1359
|
+
* renamed identifier or a changed indent without another full read.
|
|
1360
|
+
*/
|
|
1361
|
+
export function searchNotFound(pathLabel, original, search) {
|
|
1362
|
+
const firstLine = (search.split('\n').find(l => l.trim() !== '') ?? '').trim();
|
|
1363
|
+
const token = firstLine.split(/\s+/).find(w => w.length >= 4) ?? firstLine;
|
|
1364
|
+
let near = '';
|
|
1365
|
+
if (token) {
|
|
1366
|
+
const lines = original.split('\n');
|
|
1367
|
+
const hit = lines.findIndex(l => l.includes(token));
|
|
1368
|
+
if (hit >= 0) {
|
|
1369
|
+
near = '\nThe closest text in the file is:\n'
|
|
1370
|
+
+ lines.slice(Math.max(0, hit - 1), hit + 2).map(l => ' ' + l).join('\n');
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
return `ERROR: Search string not found in ${pathLabel}, even allowing for whitespace differences.`
|
|
1374
|
+
+ near
|
|
1375
|
+
+ `\nThe search must match the file's current text exactly (bar whitespace). If the file `
|
|
1376
|
+
+ `changed, read it again; if the text spans several lines, include them all.`;
|
|
1194
1377
|
}
|
|
1195
1378
|
// ─── fetch_url ────────────────────────────────────────────────────────────────
|
|
1196
1379
|
function stripHtml(html) {
|
|
@@ -1609,6 +1792,7 @@ export function writeTargets(tool, parsed) {
|
|
|
1609
1792
|
case 'write_file':
|
|
1610
1793
|
case 'apply_diff':
|
|
1611
1794
|
case 'search_replace':
|
|
1795
|
+
case 'multi_edit':
|
|
1612
1796
|
case 'delete_file':
|
|
1613
1797
|
return one(args['path']);
|
|
1614
1798
|
case 'move_file':
|
|
@@ -1696,6 +1880,8 @@ signal) {
|
|
|
1696
1880
|
return deleteFile(parsed, cwd, requireApproval);
|
|
1697
1881
|
case 'search_replace':
|
|
1698
1882
|
return searchReplace(parsed, cwd, requireApproval);
|
|
1883
|
+
case 'multi_edit':
|
|
1884
|
+
return multiEdit(parsed, cwd, requireApproval);
|
|
1699
1885
|
case 'fetch_url':
|
|
1700
1886
|
return fetchUrl(parsed, signal);
|
|
1701
1887
|
case 'remember':
|