@oh-my-pi/pi-coding-agent 14.5.14 → 14.6.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 +49 -0
- package/package.json +7 -7
- package/src/autoresearch/command-resume.md +5 -8
- package/src/autoresearch/git.ts +41 -51
- package/src/autoresearch/helpers.ts +43 -359
- package/src/autoresearch/index.ts +281 -273
- package/src/autoresearch/prompt-setup.md +43 -0
- package/src/autoresearch/prompt.md +52 -193
- package/src/autoresearch/resume-message.md +2 -8
- package/src/autoresearch/state.ts +59 -166
- package/src/autoresearch/storage.ts +687 -0
- package/src/autoresearch/tools/init-experiment.ts +201 -290
- package/src/autoresearch/tools/log-experiment.ts +304 -517
- package/src/autoresearch/tools/run-experiment.ts +117 -296
- package/src/autoresearch/tools/update-notes.ts +116 -0
- package/src/autoresearch/types.ts +16 -66
- package/src/cli/list-models.ts +66 -0
- package/src/config/settings-schema.ts +1 -1
- package/src/config/settings.ts +20 -1
- package/src/cursor.ts +1 -1
- package/src/edit/index.ts +9 -31
- package/src/edit/line-hash.ts +70 -43
- package/src/edit/modes/hashline.lark +26 -0
- package/src/edit/modes/hashline.ts +898 -1099
- package/src/edit/modes/patch.ts +0 -7
- package/src/edit/modes/replace.ts +0 -4
- package/src/edit/renderer.ts +22 -20
- package/src/edit/streaming.ts +8 -28
- package/src/eval/eval.lark +24 -30
- package/src/eval/js/context-manager.ts +5 -162
- package/src/eval/js/prelude.txt +0 -12
- package/src/eval/parse.ts +129 -129
- package/src/eval/py/prelude.py +1 -219
- package/src/export/html/template.generated.ts +1 -1
- package/src/export/html/template.js +2 -2
- package/src/internal-urls/docs-index.generated.ts +2 -2
- package/src/main.ts +18 -3
- package/src/modes/components/session-observer-overlay.ts +5 -2
- package/src/modes/components/status-line/segments.ts +1 -1
- package/src/modes/components/status-line.ts +3 -5
- package/src/modes/components/tree-selector.ts +4 -5
- package/src/modes/components/welcome.ts +11 -1
- package/src/modes/controllers/command-controller.ts +2 -6
- package/src/modes/controllers/event-controller.ts +7 -5
- package/src/modes/controllers/extension-ui-controller.ts +3 -15
- package/src/modes/controllers/input-controller.ts +0 -1
- package/src/modes/controllers/selector-controller.ts +1 -1
- package/src/modes/interactive-mode.ts +5 -7
- package/src/prompts/system/system-prompt.md +14 -38
- package/src/prompts/tools/ast-edit.md +8 -8
- package/src/prompts/tools/ast-grep.md +10 -10
- package/src/prompts/tools/eval.md +13 -31
- package/src/prompts/tools/find.md +2 -1
- package/src/prompts/tools/hashline.md +66 -57
- package/src/prompts/tools/search.md +2 -2
- package/src/session/agent-session.ts +1 -1
- package/src/session/session-manager.ts +17 -13
- package/src/tools/ast-edit.ts +141 -44
- package/src/tools/ast-grep.ts +112 -36
- package/src/tools/eval.ts +2 -53
- package/src/tools/find.ts +16 -15
- package/src/tools/gh-renderer.ts +184 -59
- package/src/tools/path-utils.ts +36 -196
- package/src/tools/search.ts +56 -35
- package/src/utils/edit-mode.ts +2 -11
- package/src/utils/file-display-mode.ts +1 -1
- package/src/utils/git.ts +59 -24
- package/src/utils/session-color.ts +0 -12
- package/src/utils/title-generator.ts +22 -38
- package/src/autoresearch/apply-contract-to-state.ts +0 -24
- package/src/autoresearch/contract.ts +0 -288
- package/src/edit/modes/atom.lark +0 -29
- package/src/edit/modes/atom.ts +0 -1773
- package/src/prompts/tools/atom.md +0 -150
package/src/edit/modes/patch.ts
CHANGED
|
@@ -1717,13 +1717,6 @@ export async function executePatchSingle(
|
|
|
1717
1717
|
const resolvedPath = resolvePlanPath(session, path);
|
|
1718
1718
|
const resolvedRename = rename ? resolvePlanPath(session, rename) : undefined;
|
|
1719
1719
|
|
|
1720
|
-
if (path.endsWith(".ipynb")) {
|
|
1721
|
-
throw new Error("Cannot edit Jupyter notebooks with the Edit tool. Use the NotebookEdit tool instead.");
|
|
1722
|
-
}
|
|
1723
|
-
if (rename?.endsWith(".ipynb")) {
|
|
1724
|
-
throw new Error("Cannot edit Jupyter notebooks with the Edit tool. Use the NotebookEdit tool instead.");
|
|
1725
|
-
}
|
|
1726
|
-
|
|
1727
1720
|
await assertEditableFile(resolvedPath, path);
|
|
1728
1721
|
|
|
1729
1722
|
const input: PatchInput = { path: resolvedPath, op, rename: resolvedRename, diff };
|
|
@@ -1026,10 +1026,6 @@ export async function executeReplaceSingle(
|
|
|
1026
1026
|
|
|
1027
1027
|
enforcePlanModeWrite(session, path);
|
|
1028
1028
|
|
|
1029
|
-
if (path.endsWith(".ipynb")) {
|
|
1030
|
-
throw new Error("Cannot edit Jupyter notebooks with the Edit tool. Use the NotebookEdit tool instead.");
|
|
1031
|
-
}
|
|
1032
|
-
|
|
1033
1029
|
if (old_text.length === 0) {
|
|
1034
1030
|
throw new Error("old_text must not be empty.");
|
|
1035
1031
|
}
|
package/src/edit/renderer.ts
CHANGED
|
@@ -106,7 +106,7 @@ type EditRenderEntry = {
|
|
|
106
106
|
op?: Operation;
|
|
107
107
|
};
|
|
108
108
|
|
|
109
|
-
interface
|
|
109
|
+
interface HashlineInputRenderSummary {
|
|
110
110
|
entries: Array<{ path: string }>;
|
|
111
111
|
}
|
|
112
112
|
|
|
@@ -309,9 +309,9 @@ function getCallPreview(
|
|
|
309
309
|
}
|
|
310
310
|
|
|
311
311
|
const MISSING_APPLY_PATCH_END_ERROR = "The last line of the patch must be '*** End Patch'";
|
|
312
|
-
const
|
|
312
|
+
const HASHLINE_INPUT_HEADER_PREFIX = "@";
|
|
313
313
|
|
|
314
|
-
function
|
|
314
|
+
function normalizeHashlineInputPreviewPath(rawPath: string): string {
|
|
315
315
|
const trimmed = rawPath.trim();
|
|
316
316
|
if (trimmed.length < 2) return trimmed;
|
|
317
317
|
const first = trimmed[0];
|
|
@@ -322,30 +322,32 @@ function normalizeAtomPreviewPath(rawPath: string): string {
|
|
|
322
322
|
return trimmed;
|
|
323
323
|
}
|
|
324
324
|
|
|
325
|
-
function
|
|
326
|
-
if (!line.startsWith(
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
const previewPath = normalizeAtomPreviewPath(body);
|
|
325
|
+
function parseHashlineInputPreviewHeader(line: string): string | null {
|
|
326
|
+
if (!line.startsWith(HASHLINE_INPUT_HEADER_PREFIX)) return null;
|
|
327
|
+
const body = line.slice(HASHLINE_INPUT_HEADER_PREFIX.length).trim();
|
|
328
|
+
const previewPath = normalizeHashlineInputPreviewPath(body);
|
|
330
329
|
return previewPath.length > 0 ? previewPath : null;
|
|
331
330
|
}
|
|
332
331
|
|
|
333
|
-
function
|
|
332
|
+
function getHashlineInputPaths(input: string): string[] {
|
|
334
333
|
const stripped = input.startsWith("\uFEFF") ? input.slice(1) : input;
|
|
335
334
|
const paths: string[] = [];
|
|
336
335
|
for (const rawLine of stripped.split("\n")) {
|
|
337
336
|
const line = rawLine.replace(/\r$/, "");
|
|
338
|
-
const path =
|
|
337
|
+
const path = parseHashlineInputPreviewHeader(line);
|
|
339
338
|
if (path) paths.push(path);
|
|
340
339
|
}
|
|
341
340
|
return paths;
|
|
342
341
|
}
|
|
343
342
|
|
|
344
|
-
function
|
|
345
|
-
|
|
343
|
+
function getHashlineInputRenderSummary(
|
|
344
|
+
args: EditRenderArgs,
|
|
345
|
+
editMode: EditMode | undefined,
|
|
346
|
+
): HashlineInputRenderSummary | undefined {
|
|
347
|
+
if (editMode !== "hashline" || typeof args.input !== "string") {
|
|
346
348
|
return undefined;
|
|
347
349
|
}
|
|
348
|
-
return { entries:
|
|
350
|
+
return { entries: getHashlineInputPaths(args.input).map(path => ({ path })) };
|
|
349
351
|
}
|
|
350
352
|
|
|
351
353
|
function getApplyPatchRenderSummary(
|
|
@@ -447,10 +449,10 @@ export const editToolRenderer = {
|
|
|
447
449
|
}
|
|
448
450
|
|
|
449
451
|
const editArgs = args as EditRenderArgs;
|
|
450
|
-
const
|
|
452
|
+
const hashlineInputSummary = getHashlineInputRenderSummary(editArgs, renderContext?.editMode);
|
|
451
453
|
const applyPatchSummary = getApplyPatchRenderSummary(editArgs, options.isPartial, renderContext?.editMode);
|
|
452
454
|
const firstApplyPatchEntry = applyPatchSummary?.entries[0];
|
|
453
|
-
const
|
|
455
|
+
const firstHashlineInputEntry = hashlineInputSummary?.entries[0];
|
|
454
456
|
// Extract path from first edit entry when top-level path is absent (new schema)
|
|
455
457
|
const firstEdit = Array.isArray(editArgs.edits) && editArgs.edits.length > 0 ? editArgs.edits[0] : undefined;
|
|
456
458
|
const rawPath =
|
|
@@ -458,7 +460,7 @@ export const editToolRenderer = {
|
|
|
458
460
|
editArgs.path ||
|
|
459
461
|
filePathFromEditEntry(firstEdit?.path) ||
|
|
460
462
|
getPartialJsonEditPath(editArgs) ||
|
|
461
|
-
|
|
463
|
+
firstHashlineInputEntry?.path ||
|
|
462
464
|
firstApplyPatchEntry?.path ||
|
|
463
465
|
"";
|
|
464
466
|
const rename = editArgs.rename || firstEdit?.rename || firstEdit?.move || firstApplyPatchEntry?.rename;
|
|
@@ -468,7 +470,7 @@ export const editToolRenderer = {
|
|
|
468
470
|
options?.spinnerFrame !== undefined ? formatStatusIcon("running", uiTheme, options.spinnerFrame) : "";
|
|
469
471
|
let text = `${formatTitle(getOperationTitle(op), uiTheme)} ${spinner ? `${spinner} ` : ""}${description}`;
|
|
470
472
|
// Show file count hint for multi-file edits
|
|
471
|
-
let fileCount =
|
|
473
|
+
let fileCount = hashlineInputSummary?.entries.length ?? applyPatchSummary?.entries.length ?? 0;
|
|
472
474
|
if (Array.isArray(editArgs.edits)) {
|
|
473
475
|
fileCount = countEditFiles(editArgs.edits);
|
|
474
476
|
}
|
|
@@ -519,14 +521,14 @@ function renderSingleFileResult(
|
|
|
519
521
|
const details = result.details;
|
|
520
522
|
const isError = result.isError ?? (details && "isError" in details ? details.isError : false);
|
|
521
523
|
const firstEdit = args?.edits?.[0];
|
|
522
|
-
const
|
|
523
|
-
const
|
|
524
|
+
const hashlineInputSummary = getHashlineInputRenderSummary(args ?? {}, options.renderContext?.editMode);
|
|
525
|
+
const firstHashlineInputEntry = hashlineInputSummary?.entries[0];
|
|
524
526
|
const rawPath =
|
|
525
527
|
args?.file_path ||
|
|
526
528
|
args?.path ||
|
|
527
529
|
filePathFromEditEntry(firstEdit?.path) ||
|
|
528
530
|
(details && "path" in details ? details.path : "") ||
|
|
529
|
-
|
|
531
|
+
firstHashlineInputEntry?.path ||
|
|
530
532
|
"";
|
|
531
533
|
const op = args?.op || firstEdit?.op || details?.op;
|
|
532
534
|
const rename = args?.rename || firstEdit?.rename || firstEdit?.move || details?.move;
|
package/src/edit/streaming.ts
CHANGED
|
@@ -16,7 +16,7 @@ import type { Theme } from "../modes/theme/theme";
|
|
|
16
16
|
import { type EditMode, resolveEditMode } from "../utils/edit-mode";
|
|
17
17
|
import { computeEditDiff, type DiffError, type DiffResult } from "./diff";
|
|
18
18
|
import { type ApplyPatchEntry, expandApplyPatchToEntries, expandApplyPatchToPreviewEntries } from "./modes/apply-patch";
|
|
19
|
-
import { computeHashlineDiff
|
|
19
|
+
import { computeHashlineDiff } from "./modes/hashline";
|
|
20
20
|
import { computePatchDiff, type PatchEditEntry } from "./modes/patch";
|
|
21
21
|
import type { ReplaceEditEntry } from "./modes/replace";
|
|
22
22
|
|
|
@@ -210,22 +210,22 @@ const patchStrategy: EditStreamingStrategy<PatchArgs> = {
|
|
|
210
210
|
};
|
|
211
211
|
|
|
212
212
|
interface HashlineArgs {
|
|
213
|
+
input?: string;
|
|
213
214
|
path?: string;
|
|
214
|
-
edits?: HashlineToolEdit[];
|
|
215
215
|
__partialJson?: string;
|
|
216
216
|
}
|
|
217
217
|
|
|
218
218
|
const hashlineStrategy: EditStreamingStrategy<HashlineArgs> = {
|
|
219
|
-
extractCompleteEdits(args
|
|
220
|
-
|
|
221
|
-
return { ...args, edits: dropIncompleteLastEdit(args.edits, partialJson, "edits") };
|
|
219
|
+
extractCompleteEdits(args) {
|
|
220
|
+
return args;
|
|
222
221
|
},
|
|
223
222
|
async computeDiffPreview(args, ctx) {
|
|
224
|
-
if (
|
|
223
|
+
if (typeof args.input !== "string" || args.input.length === 0) return null;
|
|
225
224
|
ctx.signal.throwIfAborted();
|
|
226
|
-
const result = await computeHashlineDiff({
|
|
225
|
+
const result = await computeHashlineDiff({ input: args.input, path: args.path }, ctx.cwd);
|
|
227
226
|
ctx.signal.throwIfAborted();
|
|
228
|
-
|
|
227
|
+
if ("error" in result && !args.path) return [{ path: "", error: result.error }];
|
|
228
|
+
return [toPerFilePreview(args.path ?? "", result)];
|
|
229
229
|
},
|
|
230
230
|
renderStreamingFallback() {
|
|
231
231
|
return "";
|
|
@@ -289,32 +289,12 @@ const vimStrategy: EditStreamingStrategy<unknown> = {
|
|
|
289
289
|
},
|
|
290
290
|
};
|
|
291
291
|
|
|
292
|
-
interface AtomArgs {
|
|
293
|
-
input?: string;
|
|
294
|
-
__partialJson?: string;
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
const atomStrategy: EditStreamingStrategy<AtomArgs> = {
|
|
298
|
-
extractCompleteEdits(args) {
|
|
299
|
-
return args;
|
|
300
|
-
},
|
|
301
|
-
async computeDiffPreview() {
|
|
302
|
-
// Atom edits can target file headers plus compact diff statements.
|
|
303
|
-
// We intentionally avoid speculative parsing while args are partial.
|
|
304
|
-
return null;
|
|
305
|
-
},
|
|
306
|
-
renderStreamingFallback() {
|
|
307
|
-
return "";
|
|
308
|
-
},
|
|
309
|
-
};
|
|
310
|
-
|
|
311
292
|
export const EDIT_MODE_STRATEGIES: Record<EditMode, EditStreamingStrategy<unknown>> = {
|
|
312
293
|
replace: replaceStrategy as EditStreamingStrategy<unknown>,
|
|
313
294
|
patch: patchStrategy as EditStreamingStrategy<unknown>,
|
|
314
295
|
hashline: hashlineStrategy as EditStreamingStrategy<unknown>,
|
|
315
296
|
apply_patch: applyPatchStrategy as EditStreamingStrategy<unknown>,
|
|
316
297
|
vim: vimStrategy,
|
|
317
|
-
atom: atomStrategy as EditStreamingStrategy<unknown>,
|
|
318
298
|
};
|
|
319
299
|
|
|
320
300
|
export { resolveEditMode };
|
package/src/eval/eval.lark
CHANGED
|
@@ -1,43 +1,37 @@
|
|
|
1
1
|
%import common.LF
|
|
2
2
|
%import common.WS_INLINE
|
|
3
3
|
|
|
4
|
-
//
|
|
5
|
-
// form. The runtime parser accepts additional lenient shapes (positional
|
|
6
|
-
// title/duration, alias keys, long-form lang tokens, mixed casing, fence
|
|
7
|
-
// runs of any length ≥ 3, etc.) but those are fallback only and MUST NOT
|
|
8
|
-
// be relied on.
|
|
4
|
+
// Canonical Eval input. Each cell is introduced by a header line:
|
|
9
5
|
//
|
|
10
|
-
//
|
|
11
|
-
// (or exactly five) backticks or tildes — five lets callers nest a 3-char
|
|
12
|
-
// fence inside a cell verbatim. The opening fence carries an optional info
|
|
13
|
-
// string with up to four parts, IN THIS ORDER:
|
|
6
|
+
// ===== <info> =====
|
|
14
7
|
//
|
|
15
|
-
//
|
|
8
|
+
// where each side is at least 5 equal signs. The info between the bars is
|
|
9
|
+
// a list of space-separated tokens, all optional, in any order:
|
|
16
10
|
//
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
//
|
|
11
|
+
// py | js | ts language for this cell
|
|
12
|
+
// py:"..." | js:"..." | ts:"..." language plus title shorthand
|
|
13
|
+
// id:"..." cell title (when language unchanged)
|
|
14
|
+
// t:<digits>(ms|s|m)? per-cell timeout (default 30s)
|
|
15
|
+
// rst reset this language's kernel before running
|
|
16
|
+
//
|
|
17
|
+
// Everything between one header line and the next (or end of input) is
|
|
18
|
+
// the cell's code, verbatim. The runtime additionally accepts content
|
|
19
|
+
// before the first header as an implicit default-language cell, but that
|
|
20
|
+
// is lenient fallback only and MUST NOT be relied on.
|
|
22
21
|
|
|
23
22
|
start: cell+
|
|
23
|
+
cell: header LF code_line*
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
backtick_cell: BACKTICKS info? LF code_line* BACKTICKS LF
|
|
28
|
-
tilde_cell: TILDES info? LF code_line* TILDES LF
|
|
29
|
-
|
|
30
|
-
info: lang (WS_INLINE id_attr)? (WS_INLINE t_attr)? (WS_INLINE rst_attr)?
|
|
31
|
-
| id_attr (WS_INLINE t_attr)? (WS_INLINE rst_attr)?
|
|
32
|
-
| t_attr (WS_INLINE rst_attr)?
|
|
33
|
-
| rst_attr
|
|
25
|
+
header: BAR (WS_INLINE attr)+ WS_INLINE BAR
|
|
26
|
+
| BAR WS_INLINE? BAR
|
|
34
27
|
|
|
35
|
-
|
|
36
|
-
id_attr: "id=" /"[^"\r\n]*"/
|
|
37
|
-
t_attr: "t=" /\d+(ms|s|m)?/
|
|
38
|
-
rst_attr: "rst=" /[01]/
|
|
28
|
+
attr: LANG_TITLE | LANG | ID_ATTR | T_ATTR | RST_FLAG
|
|
39
29
|
|
|
40
30
|
code_line: /[^\r\n]*/ LF
|
|
41
31
|
|
|
42
|
-
|
|
43
|
-
|
|
32
|
+
BAR: /={5,}/
|
|
33
|
+
LANG: "py" | "js" | "ts"
|
|
34
|
+
LANG_TITLE: ("py" | "js" | "ts") ":\"" /[^"\r\n]*/ "\""
|
|
35
|
+
ID_ATTR: "id:\"" /[^"\r\n]*/ "\""
|
|
36
|
+
T_ATTR: "t:" /\d+(ms|s|m)?/
|
|
37
|
+
RST_FLAG: "rst"
|
|
@@ -24,12 +24,8 @@ interface VmHelperOptions {
|
|
|
24
24
|
path?: string;
|
|
25
25
|
hidden?: boolean;
|
|
26
26
|
maxDepth?: number;
|
|
27
|
-
ignoreCase?: boolean;
|
|
28
|
-
literal?: boolean;
|
|
29
27
|
limit?: number;
|
|
30
28
|
offset?: number;
|
|
31
|
-
globPattern?: string;
|
|
32
|
-
flags?: string;
|
|
33
29
|
reverse?: boolean;
|
|
34
30
|
unique?: boolean;
|
|
35
31
|
count?: boolean;
|
|
@@ -169,50 +165,6 @@ function clearTrackedTimeout(state: VmContextState, repeat: boolean, timer: Node
|
|
|
169
165
|
state.timers.delete(timer);
|
|
170
166
|
}
|
|
171
167
|
|
|
172
|
-
async function listFiles(
|
|
173
|
-
state: VmContextState,
|
|
174
|
-
pattern: string,
|
|
175
|
-
searchPath: string,
|
|
176
|
-
options: VmHelperOptions,
|
|
177
|
-
): Promise<string[]> {
|
|
178
|
-
const resolved = resolvePath(state, searchPath);
|
|
179
|
-
const hasRecursivePattern = pattern.includes("**");
|
|
180
|
-
const normalizedPattern = hasRecursivePattern ? pattern : `**/${pattern}`;
|
|
181
|
-
const matches = await Array.fromAsync(
|
|
182
|
-
new Bun.Glob(normalizedPattern).scan({
|
|
183
|
-
cwd: resolved,
|
|
184
|
-
dot: options.hidden ?? false,
|
|
185
|
-
absolute: true,
|
|
186
|
-
onlyFiles: false,
|
|
187
|
-
}),
|
|
188
|
-
);
|
|
189
|
-
const limited = matches.slice(0, options.limit ?? 1000).map(match => path.normalize(match));
|
|
190
|
-
return limited.sort();
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
async function grepFile(
|
|
194
|
-
filePath: string,
|
|
195
|
-
pattern: string,
|
|
196
|
-
options: VmHelperOptions,
|
|
197
|
-
): Promise<Array<{ line: number; text: string }>> {
|
|
198
|
-
const content = await Bun.file(filePath).text();
|
|
199
|
-
const lines = content.split(/\r?\n/);
|
|
200
|
-
const matcher = options.literal
|
|
201
|
-
? (line: string) =>
|
|
202
|
-
options.ignoreCase ? line.toLowerCase().includes(pattern.toLowerCase()) : line.includes(pattern)
|
|
203
|
-
: (line: string) => new RegExp(pattern, options.flags ?? (options.ignoreCase ? "i" : "")).test(line);
|
|
204
|
-
const hits: Array<{ line: number; text: string }> = [];
|
|
205
|
-
for (let index = 0; index < lines.length; index++) {
|
|
206
|
-
if (matcher(lines[index])) {
|
|
207
|
-
hits.push({ line: index + 1, text: lines[index] });
|
|
208
|
-
if (hits.length >= (options.limit ?? 200)) {
|
|
209
|
-
break;
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
return hits;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
168
|
async function createHelpers(state: VmContextState) {
|
|
217
169
|
return {
|
|
218
170
|
read: async (rawPath: string, options: VmHelperOptions = {}): Promise<string> => {
|
|
@@ -258,103 +210,6 @@ async function createHelpers(state: VmContextState) {
|
|
|
258
210
|
});
|
|
259
211
|
return target;
|
|
260
212
|
},
|
|
261
|
-
stat: async (
|
|
262
|
-
rawPath: string,
|
|
263
|
-
): Promise<{ path: string; size: number; is_file: boolean; is_dir: boolean; mtime: string }> => {
|
|
264
|
-
const target = resolvePath(state, rawPath);
|
|
265
|
-
const info = await Bun.file(target).stat();
|
|
266
|
-
const result = {
|
|
267
|
-
path: target,
|
|
268
|
-
size: info.size,
|
|
269
|
-
is_file: info.isFile(),
|
|
270
|
-
is_dir: info.isDirectory(),
|
|
271
|
-
mtime: new Date(info.mtimeMs).toISOString(),
|
|
272
|
-
};
|
|
273
|
-
emitStatus(state, { op: "stat", path: target, size: result.size, is_dir: result.is_dir, mtime: result.mtime });
|
|
274
|
-
return result;
|
|
275
|
-
},
|
|
276
|
-
find: async (pattern: string, searchPath = ".", options: VmHelperOptions = {}): Promise<string[]> => {
|
|
277
|
-
const matches = await listFiles(state, pattern, searchPath, options);
|
|
278
|
-
emitStatus(state, {
|
|
279
|
-
op: "find",
|
|
280
|
-
pattern,
|
|
281
|
-
path: resolvePath(state, searchPath),
|
|
282
|
-
count: matches.length,
|
|
283
|
-
matches: matches.slice(0, 20),
|
|
284
|
-
});
|
|
285
|
-
return matches;
|
|
286
|
-
},
|
|
287
|
-
glob: async (pattern: string, searchPath = ".", options: VmHelperOptions = {}): Promise<string[]> => {
|
|
288
|
-
const resolved = resolvePath(state, searchPath);
|
|
289
|
-
const matches = await Array.fromAsync(
|
|
290
|
-
new Bun.Glob(pattern).scan({
|
|
291
|
-
cwd: resolved,
|
|
292
|
-
dot: options.hidden ?? false,
|
|
293
|
-
absolute: true,
|
|
294
|
-
onlyFiles: false,
|
|
295
|
-
}),
|
|
296
|
-
);
|
|
297
|
-
const limited = matches
|
|
298
|
-
.slice(0, options.limit ?? 1000)
|
|
299
|
-
.map(match => path.normalize(match))
|
|
300
|
-
.sort();
|
|
301
|
-
emitStatus(state, {
|
|
302
|
-
op: "glob",
|
|
303
|
-
pattern,
|
|
304
|
-
path: resolved,
|
|
305
|
-
count: limited.length,
|
|
306
|
-
matches: limited.slice(0, 20),
|
|
307
|
-
});
|
|
308
|
-
return limited;
|
|
309
|
-
},
|
|
310
|
-
grep: async (
|
|
311
|
-
pattern: string,
|
|
312
|
-
rawPath: string,
|
|
313
|
-
options: VmHelperOptions = {},
|
|
314
|
-
): Promise<Array<{ line: number; text: string }>> => {
|
|
315
|
-
const filePath = resolvePath(state, rawPath);
|
|
316
|
-
const hits = await grepFile(filePath, pattern, options);
|
|
317
|
-
emitStatus(state, { op: "grep", pattern, path: filePath, count: hits.length, hits: hits.slice(0, 10) });
|
|
318
|
-
return hits;
|
|
319
|
-
},
|
|
320
|
-
rgrep: async (
|
|
321
|
-
pattern: string,
|
|
322
|
-
searchPath = ".",
|
|
323
|
-
options: VmHelperOptions = {},
|
|
324
|
-
): Promise<Array<{ file: string; line: number; text: string }>> => {
|
|
325
|
-
const files = await listFiles(state, options.globPattern ?? "*", searchPath, {
|
|
326
|
-
...options,
|
|
327
|
-
limit: options.limit ?? 100,
|
|
328
|
-
});
|
|
329
|
-
const hits: Array<{ file: string; line: number; text: string }> = [];
|
|
330
|
-
for (const file of files) {
|
|
331
|
-
const fileStat = await Bun.file(file)
|
|
332
|
-
.stat()
|
|
333
|
-
.catch(() => undefined);
|
|
334
|
-
if (!fileStat || fileStat.isDirectory()) continue;
|
|
335
|
-
for (const hit of await grepFile(file, pattern, options)) {
|
|
336
|
-
hits.push({ file, line: hit.line, text: hit.text });
|
|
337
|
-
if (hits.length >= (options.limit ?? 100)) {
|
|
338
|
-
emitStatus(state, {
|
|
339
|
-
op: "rgrep",
|
|
340
|
-
pattern,
|
|
341
|
-
path: resolvePath(state, searchPath),
|
|
342
|
-
count: hits.length,
|
|
343
|
-
hits: hits.slice(0, 10),
|
|
344
|
-
});
|
|
345
|
-
return hits;
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
emitStatus(state, {
|
|
350
|
-
op: "rgrep",
|
|
351
|
-
pattern,
|
|
352
|
-
path: resolvePath(state, searchPath),
|
|
353
|
-
count: hits.length,
|
|
354
|
-
hits: hits.slice(0, 10),
|
|
355
|
-
});
|
|
356
|
-
return hits;
|
|
357
|
-
},
|
|
358
213
|
sortText: (text: string, options: VmHelperOptions = {}): string => {
|
|
359
214
|
const lines = String(text).split(/\r?\n/);
|
|
360
215
|
const deduped = options.unique ? Array.from(new Set(lines)) : lines;
|
|
@@ -403,22 +258,6 @@ async function createHelpers(state: VmContextState) {
|
|
|
403
258
|
emitStatus(state, { op: "counter", unique: counts.size, total: values.length, top: limited.slice(0, 10) });
|
|
404
259
|
return limited;
|
|
405
260
|
},
|
|
406
|
-
sed: async (
|
|
407
|
-
rawPath: string,
|
|
408
|
-
pattern: string,
|
|
409
|
-
replacement: string,
|
|
410
|
-
options: VmHelperOptions = {},
|
|
411
|
-
): Promise<number> => {
|
|
412
|
-
const filePath = resolvePath(state, rawPath);
|
|
413
|
-
const content = await Bun.file(filePath).text();
|
|
414
|
-
const regex = new RegExp(pattern, options.flags ?? "g");
|
|
415
|
-
const matches = content.match(regex);
|
|
416
|
-
const updated = content.replace(regex, replacement);
|
|
417
|
-
await Bun.write(filePath, updated);
|
|
418
|
-
const count = matches?.length ?? 0;
|
|
419
|
-
emitStatus(state, { op: "sed", path: filePath, count });
|
|
420
|
-
return count;
|
|
421
|
-
},
|
|
422
261
|
diff: async (rawA: string, rawB: string): Promise<string> => {
|
|
423
262
|
const fileA = resolvePath(state, rawA);
|
|
424
263
|
const fileB = resolvePath(state, rawB);
|
|
@@ -590,7 +429,10 @@ async function createVmState(
|
|
|
590
429
|
const context = vm.createContext(contextGlobals);
|
|
591
430
|
context.globalThis = context;
|
|
592
431
|
state.context = context;
|
|
593
|
-
vm.runInContext(JAVASCRIPT_PRELUDE_SOURCE, context, {
|
|
432
|
+
vm.runInContext(JAVASCRIPT_PRELUDE_SOURCE, context, {
|
|
433
|
+
filename: "js-prelude.js",
|
|
434
|
+
importModuleDynamically: vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER,
|
|
435
|
+
});
|
|
594
436
|
return state;
|
|
595
437
|
}
|
|
596
438
|
|
|
@@ -691,6 +533,7 @@ export async function executeInVmContext(options: {
|
|
|
691
533
|
const value = vm.runInContext(wrapped.source, state.context, {
|
|
692
534
|
filename: options.filename,
|
|
693
535
|
timeout: options.timeoutMs,
|
|
536
|
+
importModuleDynamically: vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER,
|
|
694
537
|
});
|
|
695
538
|
const awaited = await awaitMaybePromise(value, options.runState.signal);
|
|
696
539
|
displayValue(state, awaited);
|
package/src/eval/js/prelude.txt
CHANGED
|
@@ -7,15 +7,9 @@ if (!globalThis.__omp_js_prelude_loaded__) {
|
|
|
7
7
|
const read = (path, opts = {}) => callHelper("read", path, toOptions(opts));
|
|
8
8
|
const write = async (path, data) => callHelper("writeFile", path, data);
|
|
9
9
|
const append = (path, content) => callHelper("append", path, content);
|
|
10
|
-
const stat = path => callHelper("stat", path);
|
|
11
|
-
const grep = (pattern, path, opts = {}) => callHelper("grep", pattern, path, toOptions(opts));
|
|
12
|
-
const rgrep = (pattern, path = ".", opts = {}) => callHelper("rgrep", pattern, path, toOptions(opts));
|
|
13
|
-
const find = (pattern, path = ".", opts = {}) => callHelper("find", pattern, path, toOptions(opts));
|
|
14
|
-
const glob = (pattern, path = ".", opts = {}) => callHelper("glob", pattern, path, toOptions(opts));
|
|
15
10
|
const sort = (text, opts = {}) => callHelper("sortText", text, toOptions(opts));
|
|
16
11
|
const uniq = (text, opts = {}) => callHelper("uniqText", text, toOptions(opts));
|
|
17
12
|
const counter = (items, opts = {}) => callHelper("counter", items, toOptions(opts));
|
|
18
|
-
const sed = (path, pattern, repl, opts = {}) => callHelper("sed", path, pattern, repl, toOptions(opts));
|
|
19
13
|
const diff = (a, b) => callHelper("diff", a, b);
|
|
20
14
|
const tree = (path = ".", opts = {}) => callHelper("tree", path, toOptions(opts));
|
|
21
15
|
const run = (cmd, opts = {}) => callHelper("run", cmd, toOptions(opts));
|
|
@@ -68,15 +62,9 @@ if (!globalThis.__omp_js_prelude_loaded__) {
|
|
|
68
62
|
globalThis.read = read;
|
|
69
63
|
globalThis.write = write;
|
|
70
64
|
globalThis.append = append;
|
|
71
|
-
globalThis.stat = stat;
|
|
72
|
-
globalThis.grep = grep;
|
|
73
|
-
globalThis.rgrep = rgrep;
|
|
74
|
-
globalThis.find = find;
|
|
75
|
-
globalThis.glob = glob;
|
|
76
65
|
globalThis.sort = sort;
|
|
77
66
|
globalThis.uniq = uniq;
|
|
78
67
|
globalThis.counter = counter;
|
|
79
|
-
globalThis.sed = sed;
|
|
80
68
|
globalThis.diff = diff;
|
|
81
69
|
globalThis.tree = tree;
|
|
82
70
|
globalThis.run = run;
|