harnery 0.46.1 → 0.47.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/README.md +24 -0
- package/bin/README.md +10 -0
- package/bin/agent-coord.mjs +3 -0
- package/bin/agent-hook.mjs +3 -0
- package/bin/harn.mjs +3 -0
- package/bin/run.mjs +43 -0
- package/dist/commander.d.ts +18 -6
- package/dist/commander.d.ts.map +1 -1
- package/dist/commander.js +74 -2
- package/dist/commands/agents.d.ts +25 -0
- package/dist/commands/agents.d.ts.map +1 -1
- package/dist/commands/agents.js +70 -27
- package/dist/commands/autoupdate.d.ts +12 -0
- package/dist/commands/autoupdate.d.ts.map +1 -0
- package/dist/commands/autoupdate.js +190 -0
- package/dist/commands/computer.d.ts +45 -0
- package/dist/commands/computer.d.ts.map +1 -0
- package/dist/commands/computer.js +509 -0
- package/dist/commands/docs.js +3 -1
- package/dist/commands/fetch.js +11 -1
- package/dist/commands/grep.d.ts +72 -0
- package/dist/commands/grep.d.ts.map +1 -1
- package/dist/commands/grep.js +200 -47
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +15 -9
- package/dist/commands/rm.js +2 -2
- package/dist/commands/web.d.ts.map +1 -1
- package/dist/commands/web.js +2 -0
- package/dist/core/agents/canonical-emit.d.ts.map +1 -1
- package/dist/core/agents/canonical-emit.js +2 -3
- package/dist/core/agents/coord-bin.d.ts +3 -0
- package/dist/core/agents/coord-bin.d.ts.map +1 -1
- package/dist/core/agents/coord-bin.js +6 -0
- package/dist/core/agents/coord-client.d.ts.map +1 -1
- package/dist/core/agents/coord-client.js +6 -2
- package/dist/core/agents/render/prompt-context.d.ts +55 -7
- package/dist/core/agents/render/prompt-context.d.ts.map +1 -1
- package/dist/core/agents/render/prompt-context.js +122 -48
- package/dist/core/agents/state/proc-start.d.ts +3 -1
- package/dist/core/agents/state/proc-start.d.ts.map +1 -1
- package/dist/core/agents/state/proc-start.js +26 -4
- package/dist/core/autoupdate/engine.d.ts +97 -0
- package/dist/core/autoupdate/engine.d.ts.map +1 -0
- package/dist/core/autoupdate/engine.js +548 -0
- package/dist/core/autoupdate/fingerprint.d.ts +16 -0
- package/dist/core/autoupdate/fingerprint.d.ts.map +1 -0
- package/dist/core/autoupdate/fingerprint.js +73 -0
- package/dist/core/autoupdate/git.d.ts +30 -0
- package/dist/core/autoupdate/git.d.ts.map +1 -0
- package/dist/core/autoupdate/git.js +82 -0
- package/dist/core/autoupdate/index.d.ts +6 -0
- package/dist/core/autoupdate/index.d.ts.map +1 -0
- package/dist/core/autoupdate/index.js +4 -0
- package/dist/core/autoupdate/state.d.ts +59 -0
- package/dist/core/autoupdate/state.d.ts.map +1 -0
- package/dist/core/autoupdate/state.js +214 -0
- package/dist/core/autoupdate/types.d.ts +113 -0
- package/dist/core/autoupdate/types.d.ts.map +1 -0
- package/dist/core/autoupdate/types.js +35 -0
- package/dist/core/config.d.ts +53 -1
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +114 -1
- package/dist/core/events/v3/bootstrap.js +1 -0
- package/dist/core/events/v3/runtime-build.d.ts.map +1 -1
- package/dist/core/events/v3/runtime-build.js +1 -0
- package/dist/core/hooks/adapter/wiring.d.ts +12 -2
- package/dist/core/hooks/adapter/wiring.d.ts.map +1 -1
- package/dist/core/hooks/adapter/wiring.js +38 -8
- package/dist/core/hooks/autoupdate-cue.d.ts +29 -0
- package/dist/core/hooks/autoupdate-cue.d.ts.map +1 -0
- package/dist/core/hooks/autoupdate-cue.js +53 -0
- package/dist/core/hooks/cli.js +28 -3
- package/dist/core/hooks/effects/index.js +4 -4
- package/dist/core/hooks/resolve/coord-root.js +1 -0
- package/dist/core/semantic/contract.d.ts +3 -3
- package/dist/core/semantic/service.d.ts.map +1 -1
- package/dist/core/semantic/service.js +4 -2
- package/dist/core/storage/modes.d.ts +1 -1
- package/dist/core/storage/modes.d.ts.map +1 -1
- package/dist/core/storage/modes.js +5 -1
- package/dist/core/workflow/durable-record.d.ts +2 -0
- package/dist/core/workflow/durable-record.d.ts.map +1 -1
- package/dist/core/workflow/durable-record.js +13 -1
- package/dist/core/workflow/engine.js +4 -0
- package/dist/core/workflow/spawn-claude.d.ts +7 -0
- package/dist/core/workflow/spawn-claude.d.ts.map +1 -1
- package/dist/core/workflow/spawn-claude.js +16 -4
- package/dist/core/workflow/specialists.d.ts.map +1 -1
- package/dist/core/workflow/specialists.js +1 -0
- package/dist/core/workflow/types.d.ts +10 -0
- package/dist/core/workflow/types.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/leases.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/leases.js +1 -9
- package/dist/lib/computer/cua.d.ts +50 -0
- package/dist/lib/computer/cua.d.ts.map +1 -0
- package/dist/lib/computer/cua.js +474 -0
- package/dist/lib/computer/host.d.ts +19 -0
- package/dist/lib/computer/host.d.ts.map +1 -0
- package/dist/lib/computer/host.js +72 -0
- package/dist/lib/computer/lease.d.ts +13 -0
- package/dist/lib/computer/lease.d.ts.map +1 -0
- package/dist/lib/computer/lease.js +269 -0
- package/dist/lib/computer/types.d.ts +163 -0
- package/dist/lib/computer/types.d.ts.map +1 -0
- package/dist/lib/computer/types.js +10 -0
- package/dist/lib/docs-metadata-v2.d.ts.map +1 -1
- package/dist/lib/docs-metadata-v2.js +6 -5
- package/dist/lib/exec.d.ts.map +1 -1
- package/dist/lib/exec.js +1 -0
- package/dist/lib/guarded-remove.d.ts.map +1 -1
- package/dist/lib/guarded-remove.js +5 -4
- package/dist/lib/headless/index.d.ts +33 -0
- package/dist/lib/headless/index.d.ts.map +1 -1
- package/dist/lib/headless/index.js +38 -2
- package/dist/lib/http/client.d.ts +2 -9
- package/dist/lib/http/client.d.ts.map +1 -1
- package/dist/lib/http/client.js +30 -9
- package/dist/lib/instructions/apply.d.ts +9 -0
- package/dist/lib/instructions/apply.d.ts.map +1 -1
- package/dist/lib/instructions/apply.js +92 -14
- package/dist/lib/instructions/git-hooks.d.ts.map +1 -1
- package/dist/lib/instructions/git-hooks.js +8 -4
- package/dist/lib/instructions/templates.d.ts +11 -0
- package/dist/lib/instructions/templates.d.ts.map +1 -1
- package/dist/lib/instructions/templates.js +81 -1
- package/package.json +5 -5
- package/schemas/config.schema.json +89 -1
- package/src/commander.ts +98 -8
- package/src/commands/agents.ts +100 -32
- package/src/commands/autoupdate.ts +250 -0
- package/src/commands/computer.ts +682 -0
- package/src/commands/docs.ts +3 -1
- package/src/commands/fetch.ts +11 -1
- package/src/commands/grep.ts +274 -59
- package/src/commands/init.ts +17 -7
- package/src/commands/rm.ts +2 -2
- package/src/commands/web.ts +2 -0
- package/src/core/agents/canonical-emit.ts +2 -3
- package/src/core/agents/coord-bin.ts +30 -0
- package/src/core/agents/coord-client.ts +6 -2
- package/src/core/agents/render/prompt-context.ts +152 -53
- package/src/core/agents/state/proc-start.ts +29 -5
- package/src/core/autoupdate/engine.ts +691 -0
- package/src/core/autoupdate/fingerprint.ts +89 -0
- package/src/core/autoupdate/git.ts +120 -0
- package/src/core/autoupdate/index.ts +29 -0
- package/src/core/autoupdate/state.ts +269 -0
- package/src/core/autoupdate/types.ts +135 -0
- package/src/core/config.ts +142 -1
- package/src/core/events/v3/bootstrap.ts +1 -0
- package/src/core/events/v3/runtime-build.ts +1 -0
- package/src/core/hooks/adapter/wiring.ts +51 -6
- package/src/core/hooks/autoupdate-cue.ts +72 -0
- package/src/core/hooks/cli.ts +27 -1
- package/src/core/hooks/effects/index.ts +4 -4
- package/src/core/hooks/resolve/coord-root.ts +1 -0
- package/src/core/semantic/service.ts +6 -2
- package/src/core/storage/modes.ts +7 -1
- package/src/core/workflow/durable-record.ts +15 -1
- package/src/core/workflow/engine.ts +3 -0
- package/src/core/workflow/spawn-claude.ts +16 -4
- package/src/core/workflow/specialists.ts +1 -0
- package/src/core/workflow/types.ts +10 -0
- package/src/core/workflow/workspaces/leases.ts +1 -9
- package/src/lib/computer/cua.ts +572 -0
- package/src/lib/computer/host.ts +87 -0
- package/src/lib/computer/lease.ts +299 -0
- package/src/lib/computer/types.ts +177 -0
- package/src/lib/docs-metadata-v2.ts +24 -5
- package/src/lib/exec.ts +1 -0
- package/src/lib/guarded-remove.ts +5 -3
- package/src/lib/headless/index.ts +66 -2
- package/src/lib/http/client.ts +33 -19
- package/src/lib/instructions/apply.ts +100 -12
- package/src/lib/instructions/git-hooks.ts +8 -4
- package/src/lib/instructions/templates.ts +95 -1
package/dist/commands/grep.js
CHANGED
|
@@ -32,7 +32,27 @@ import { resolveSearchEngine } from "../lib/tools/ripgrep.js";
|
|
|
32
32
|
* envelope `{ pattern, mode, engine, and_patterns, without_patterns, repos,
|
|
33
33
|
* total_matches, total_files, truncated, elapsed_ms }`. Matches are sorted
|
|
34
34
|
* (file, then line) for stable output across runs and engines.
|
|
35
|
+
*
|
|
36
|
+
* Output and paging: a search shows every result row unless `--limit <n>`
|
|
37
|
+
* sets a page size, and clips each printed line to DEFAULT_MAX_COLUMNS
|
|
38
|
+
* characters (`--max-columns 0` turns clipping off). 500 columns keeps any
|
|
39
|
+
* hand-written source line and long prose lines while cutting minified or
|
|
40
|
+
* generated ones; on the v2 tune split a 300-column clip lost a claim that
|
|
41
|
+
* 500 kept, at about 8% more tokens than 300. There is
|
|
42
|
+
* no default row bound: on the context bench's v2 tune split, every default
|
|
43
|
+
* from 50 to 300 rows lost answer evidence that the unbounded search found
|
|
44
|
+
* (plan 2026-10-31_context-round-trips, Track D). When rows are left out, a count pass
|
|
45
|
+
* (the same search with `-c`, which prints one short row per file) supplies
|
|
46
|
+
* the totals, and the last line states how many matches and files were left
|
|
47
|
+
* out and the `--page <n>` that continues. Pages are cut from one global order
|
|
48
|
+
* (repo order, then file path, then line), so `--page 2` starts exactly where
|
|
49
|
+
* page 1 stopped, whichever engine ran. A content page is fetched by searching
|
|
50
|
+
* only the files that hold its rows.
|
|
35
51
|
*/
|
|
52
|
+
/** Characters kept per printed line unless --max-columns says otherwise. */
|
|
53
|
+
export const DEFAULT_MAX_COLUMNS = 500;
|
|
54
|
+
/** Explicit file arguments per engine call when fetching a content page. */
|
|
55
|
+
const PAGE_FILE_BATCH = 200;
|
|
36
56
|
const DEFAULT_EXCLUDE_DIRS = [
|
|
37
57
|
".git",
|
|
38
58
|
".cache",
|
|
@@ -90,7 +110,9 @@ export function registerGrepCommand(program, emit, context) {
|
|
|
90
110
|
.option("-A, --after-context <n>", "Print N lines after each match (overrides -C's after side)")
|
|
91
111
|
.option("-B, --before-context <n>", "Print N lines before each match (overrides -C's before side)")
|
|
92
112
|
.option("--max-count <n>", "Stop after N matches per file")
|
|
93
|
-
.option("--limit <n>", "
|
|
113
|
+
.option("--limit <n>", "Show at most N result rows per page (default: every row)")
|
|
114
|
+
.option("--page <n>", "Show page N of --limit-sized pages (1-based; default 1; needs --limit)")
|
|
115
|
+
.option("--max-columns <n>", `Clip each printed line to N characters (default ${DEFAULT_MAX_COLUMNS}; 0 = no clipping)`)
|
|
94
116
|
.option("--and <pattern>", "Only show files that ALSO contain this pattern (file-level, repeatable)", collect, [])
|
|
95
117
|
.option("--without <pattern>", "Drop files that contain this pattern (file-level, repeatable)", collect, [])
|
|
96
118
|
.option("-q, --quiet", "No output; exit 0 if any match exists, 1 if none")
|
|
@@ -167,6 +189,10 @@ function normalizeLangs(lang) {
|
|
|
167
189
|
/** Validate numerics, resolve context sides, expand langs, enforce the compatibility matrix. */
|
|
168
190
|
function normalizeOpts(opts) {
|
|
169
191
|
const limit = opts.limit ? parseIntOpt("--limit", opts.limit, 1) : Number.POSITIVE_INFINITY;
|
|
192
|
+
const page = opts.page ? parseIntOpt("--page", opts.page, 1) : 1;
|
|
193
|
+
const maxColumnsRaw = opts.maxColumns !== undefined
|
|
194
|
+
? parseIntOpt("--max-columns", opts.maxColumns, 0)
|
|
195
|
+
: DEFAULT_MAX_COLUMNS;
|
|
170
196
|
const maxCount = opts.maxCount ? parseIntOpt("--max-count", opts.maxCount, 1) : undefined;
|
|
171
197
|
const c = opts.context !== undefined ? parseIntOpt("-C/--context", opts.context, 0) : 0;
|
|
172
198
|
const before = opts.beforeContext !== undefined
|
|
@@ -212,15 +238,21 @@ function normalizeOpts(opts) {
|
|
|
212
238
|
[opts.filesOnly, "-l/--files-only"],
|
|
213
239
|
[opts.count, "-c/--count"],
|
|
214
240
|
[opts.limit, "--limit"],
|
|
241
|
+
[opts.page, "--page"],
|
|
215
242
|
];
|
|
216
243
|
for (const [set, flag] of rejected) {
|
|
217
244
|
if (set)
|
|
218
245
|
throw new Error(`-q/--quiet does not combine with ${flag}`);
|
|
219
246
|
}
|
|
220
247
|
}
|
|
248
|
+
if (opts.page && !opts.limit)
|
|
249
|
+
throw new Error("--page needs --limit (the page size)");
|
|
221
250
|
return {
|
|
222
251
|
// -q only needs existence: one accepted primary row settles the exit code.
|
|
223
252
|
limit: opts.quiet ? 1 : limit,
|
|
253
|
+
offset: opts.quiet || !Number.isFinite(limit) ? 0 : (page - 1) * limit,
|
|
254
|
+
page,
|
|
255
|
+
maxColumns: maxColumnsRaw === 0 ? Number.POSITIVE_INFINITY : maxColumnsRaw,
|
|
224
256
|
maxCount,
|
|
225
257
|
before,
|
|
226
258
|
after,
|
|
@@ -240,54 +272,110 @@ export async function runGrep(pattern, paths, opts, context) {
|
|
|
240
272
|
// Host-injected default excludes (generated mirrors, vendored trees, ...)
|
|
241
273
|
// ride the same --no-default-excludes gate as the built-in list.
|
|
242
274
|
const hostExcludeDirs = opts.noDefaultExcludes ? [] : (context?.grepExcludeDirs ?? []);
|
|
243
|
-
//
|
|
244
|
-
//
|
|
245
|
-
//
|
|
246
|
-
//
|
|
247
|
-
|
|
248
|
-
const
|
|
249
|
-
// In --all-repos mode the parent scan prunes submodule dirs — each
|
|
250
|
-
// submodule gets its own scoped scan, so descending from the parent
|
|
251
|
-
// would double-scan and double-report every submodule match. This
|
|
252
|
-
// pruning is correctness (one repo owns each match), so it applies
|
|
253
|
-
// even when --no-default-excludes is set.
|
|
275
|
+
// In --all-repos mode the parent scan prunes submodule dirs — each
|
|
276
|
+
// submodule gets its own scoped scan, so descending from the parent would
|
|
277
|
+
// double-scan and double-report every submodule match. This pruning is
|
|
278
|
+
// correctness (one repo owns each match), so it applies even when
|
|
279
|
+
// --no-default-excludes is set.
|
|
280
|
+
const extraDirsFor = (repo) => {
|
|
254
281
|
const dedupeDirs = opts.allRepos && repo.name === "parent" ? (context?.submodules ?? []) : [];
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
282
|
+
return [...hostExcludeDirs, ...dedupeDirs];
|
|
283
|
+
};
|
|
284
|
+
const scan = (repo, scanPaths, scanOpts, scanNorm, acceptLimit) => runGrepInRepo(pattern, scanPaths, scanOpts, scanNorm, repo.cwd, repo.name, acceptLimit, engine, rgBin, extraDirsFor(repo));
|
|
285
|
+
const byPosition = (a, b) => a.file < b.file ? -1 : a.file > b.file ? 1 : a.line - b.line;
|
|
286
|
+
// Every path yields, per repo, the selected rows (sorted) plus what the
|
|
287
|
+
// whole search found there, so the page window can be stated exactly.
|
|
288
|
+
let selected;
|
|
289
|
+
let available;
|
|
290
|
+
let after; // rows exist in this repo after the page window
|
|
291
|
+
if (opts.quiet) {
|
|
292
|
+
// Existence only: one accepted row settles the exit code, so the engine is
|
|
293
|
+
// stopped at the first hit and no count pass runs.
|
|
294
|
+
selected = await Promise.all(repos.map((repo) => scan(repo, paths, opts, norm, 1)));
|
|
295
|
+
available = selected.map((rows) => ({
|
|
296
|
+
matches: rows.length,
|
|
297
|
+
files: new Set(rows.map((m) => m.file)),
|
|
298
|
+
}));
|
|
299
|
+
after = selected.map(() => false);
|
|
300
|
+
}
|
|
301
|
+
else if (!Number.isFinite(norm.limit) || opts.files || opts.filesOnly || opts.count) {
|
|
302
|
+
// No page size, or a mode whose rows are files (one short row each): collect the
|
|
303
|
+
// complete result, then cut the page from its sorted order.
|
|
304
|
+
const all = await Promise.all(repos.map((repo) => scan(repo, paths, opts, norm, Number.POSITIVE_INFINITY)));
|
|
305
|
+
for (const rows of all)
|
|
306
|
+
rows.sort(byPosition);
|
|
307
|
+
available = all.map((rows) => ({
|
|
308
|
+
matches: rows.length,
|
|
309
|
+
files: new Set(rows.map((m) => m.file)),
|
|
310
|
+
}));
|
|
311
|
+
const windows = pageWindows(all.map((rows) => rows.map(() => 1)), norm.offset, norm.limit);
|
|
312
|
+
selected = all.map((rows, i) => rows.slice(windows[i]?.start ?? 0, windows[i]?.end ?? 0));
|
|
313
|
+
after = all.map((rows, i) => (windows[i]?.end ?? 0) < rows.length);
|
|
314
|
+
}
|
|
315
|
+
else {
|
|
316
|
+
// Content page. The count pass reports one row per file (path + matching
|
|
317
|
+
// line count) for the whole search; the page window is computed from those
|
|
318
|
+
// counts, and only the files holding the window's rows are searched again.
|
|
319
|
+
const countOpts = { ...opts, count: true };
|
|
320
|
+
const counts = await Promise.all(repos.map((repo) => scan(repo, paths, countOpts, norm, Number.POSITIVE_INFINITY)));
|
|
321
|
+
for (const rows of counts)
|
|
322
|
+
rows.sort(byPosition);
|
|
323
|
+
available = counts.map((rows) => ({
|
|
324
|
+
matches: rows.reduce((sum, r) => sum + r.line, 0),
|
|
325
|
+
files: new Set(rows.map((r) => r.file)),
|
|
326
|
+
}));
|
|
327
|
+
// Expand per-file counts into a row index space without materializing rows.
|
|
328
|
+
const windows = pageWindows(counts.map((rows) => rows.map((r) => r.line)), norm.offset, norm.limit);
|
|
329
|
+
// The selected files already passed --and/--without, so the page fetch
|
|
330
|
+
// skips the membership scans.
|
|
331
|
+
const fetchNorm = { ...norm, andPatterns: [], withoutPatterns: [] };
|
|
332
|
+
selected = await Promise.all(repos.map(async (repo, i) => {
|
|
333
|
+
const w = windows[i];
|
|
334
|
+
const rows = counts[i] ?? [];
|
|
335
|
+
if (!w || w.start >= w.end)
|
|
336
|
+
return [];
|
|
337
|
+
const picks = sliceFiles(rows, w.start, w.end);
|
|
338
|
+
const fetched = [];
|
|
339
|
+
for (let b = 0; b < picks.length; b += PAGE_FILE_BATCH) {
|
|
340
|
+
const batch = picks.slice(b, b + PAGE_FILE_BATCH);
|
|
341
|
+
fetched.push(...(await scan(repo, batch.map((p) => p.file), opts, fetchNorm, Number.POSITIVE_INFINITY)));
|
|
342
|
+
}
|
|
343
|
+
fetched.sort(byPosition);
|
|
344
|
+
const out = [];
|
|
345
|
+
for (const pick of picks) {
|
|
346
|
+
const inFile = fetched.filter((m) => m.file === pick.file);
|
|
347
|
+
out.push(...inFile.slice(pick.skip, pick.skip + pick.take));
|
|
348
|
+
}
|
|
349
|
+
return out;
|
|
350
|
+
}));
|
|
351
|
+
after = counts.map((_, i) => (windows[i]?.end ?? 0) < (available[i]?.matches ?? 0));
|
|
352
|
+
}
|
|
258
353
|
const allRepoResults = [];
|
|
259
354
|
let totalMatches = 0;
|
|
260
355
|
const filesSeen = new Set();
|
|
261
|
-
let
|
|
262
|
-
let
|
|
356
|
+
let availableMatches = 0;
|
|
357
|
+
let availableFiles = 0;
|
|
263
358
|
for (let i = 0; i < repos.length; i++) {
|
|
264
359
|
const repo = repos[i];
|
|
265
360
|
if (!repo)
|
|
266
361
|
continue;
|
|
267
|
-
|
|
268
|
-
collected.sort((a, b) => (a.file < b.file ? -1 : a.file > b.file ? 1 : a.line - b.line));
|
|
269
|
-
const take = Number.isFinite(budget)
|
|
270
|
-
? Math.min(collected.length, Math.max(0, budget))
|
|
271
|
-
: collected.length;
|
|
272
|
-
let matches = collected.slice(0, take);
|
|
273
|
-
// truncated only when an accepted primary row was actually omitted —
|
|
274
|
-
// the lookahead row (or a later-repo surplus) is the proof.
|
|
275
|
-
const repoTruncated = collected.length > take;
|
|
276
|
-
if (repoTruncated)
|
|
277
|
-
truncated = true;
|
|
278
|
-
if (Number.isFinite(budget))
|
|
279
|
-
budget -= take;
|
|
362
|
+
let matches = selected[i] ?? [];
|
|
280
363
|
totalMatches += matches.length;
|
|
364
|
+
availableMatches += available[i]?.matches ?? 0;
|
|
365
|
+
availableFiles += available[i]?.files.size ?? 0;
|
|
281
366
|
for (const m of matches)
|
|
282
367
|
filesSeen.add(`${repo.name}/${m.file}`);
|
|
283
|
-
// Context is materialized only for the SELECTED rows, after
|
|
284
|
-
//
|
|
285
|
-
//
|
|
368
|
+
// Context is materialized only for the SELECTED rows, after paging, so
|
|
369
|
+
// context rows never consume the limit and trailing context is never cut
|
|
370
|
+
// off by an engine kill.
|
|
286
371
|
if (!opts.files && !opts.filesOnly && !opts.count && (norm.before > 0 || norm.after > 0)) {
|
|
287
372
|
matches = await materializeContext(matches, repo.cwd, norm.before, norm.after);
|
|
288
373
|
}
|
|
289
|
-
|
|
374
|
+
if (Number.isFinite(norm.maxColumns))
|
|
375
|
+
matches = matches.map((m) => clip(m, norm.maxColumns));
|
|
376
|
+
allRepoResults.push({ name: repo.name, cwd: repo.cwd, matches, truncated: after[i] ?? false });
|
|
290
377
|
}
|
|
378
|
+
const truncated = allRepoResults.some((r) => r.truncated);
|
|
291
379
|
return {
|
|
292
380
|
pattern,
|
|
293
381
|
mode: opts.files ? "files" : opts.literal ? "literal" : "regex",
|
|
@@ -298,9 +386,58 @@ export async function runGrep(pattern, paths, opts, context) {
|
|
|
298
386
|
total_matches: totalMatches,
|
|
299
387
|
total_files: filesSeen.size,
|
|
300
388
|
truncated,
|
|
389
|
+
limit: Number.isFinite(norm.limit) && !opts.quiet ? norm.limit : null,
|
|
390
|
+
page: opts.quiet ? 1 : norm.page,
|
|
391
|
+
available_matches: availableMatches,
|
|
392
|
+
available_files: availableFiles,
|
|
393
|
+
omitted_matches: availableMatches - totalMatches,
|
|
394
|
+
omitted_files: availableFiles - filesSeen.size,
|
|
395
|
+
next_page: truncated && !opts.quiet ? norm.page + 1 : null,
|
|
301
396
|
elapsed_ms: Date.now() - started,
|
|
302
397
|
};
|
|
303
398
|
}
|
|
399
|
+
/**
|
|
400
|
+
* Cut the page window [offset, offset + limit) out of one global row order:
|
|
401
|
+
* repos in order, each repo's rows in sorted order. `weights[i][j]` is how many
|
|
402
|
+
* rows item j of repo i stands for (1 for a row, a file's count in the count
|
|
403
|
+
* pass). Returns each repo's window as row indices local to that repo.
|
|
404
|
+
*/
|
|
405
|
+
function pageWindows(weights, offset, limit) {
|
|
406
|
+
const end = offset + limit;
|
|
407
|
+
let base = 0;
|
|
408
|
+
return weights.map((items) => {
|
|
409
|
+
const size = items.reduce((sum, w) => sum + w, 0);
|
|
410
|
+
const start = Math.min(size, Math.max(0, offset - base));
|
|
411
|
+
const stop = Math.min(size, Math.max(0, end - base));
|
|
412
|
+
base += size;
|
|
413
|
+
return { start, end: stop };
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
/** Which files (and which of their rows) a local row window covers. */
|
|
417
|
+
function sliceFiles(counts, start, end) {
|
|
418
|
+
const picks = [];
|
|
419
|
+
let pos = 0;
|
|
420
|
+
for (const row of counts) {
|
|
421
|
+
const fileStart = pos;
|
|
422
|
+
const fileEnd = pos + row.line;
|
|
423
|
+
pos = fileEnd;
|
|
424
|
+
if (fileEnd <= start)
|
|
425
|
+
continue;
|
|
426
|
+
if (fileStart >= end)
|
|
427
|
+
break;
|
|
428
|
+
const skip = Math.max(0, start - fileStart);
|
|
429
|
+
const take = Math.min(fileEnd, end) - fileStart - skip;
|
|
430
|
+
if (take > 0)
|
|
431
|
+
picks.push({ file: row.file, skip, take });
|
|
432
|
+
}
|
|
433
|
+
return picks;
|
|
434
|
+
}
|
|
435
|
+
/** Clip a row's text to `max` characters, recording how many were removed. */
|
|
436
|
+
function clip(m, max) {
|
|
437
|
+
if (m.text.length <= max)
|
|
438
|
+
return m;
|
|
439
|
+
return { ...m, text: m.text.slice(0, max), clipped: m.text.length - max };
|
|
440
|
+
}
|
|
304
441
|
function resolveRepos(opts, context) {
|
|
305
442
|
const repoRoot = context?.repoRoot;
|
|
306
443
|
const submodules = context?.submodules;
|
|
@@ -773,7 +910,8 @@ async function materializeContext(selected, cwd, before, after) {
|
|
|
773
910
|
function normalizeFile(file) {
|
|
774
911
|
return file.startsWith("./") ? file.slice(2) : file;
|
|
775
912
|
}
|
|
776
|
-
|
|
913
|
+
/** Exported for tests (not part of the package exports map). */
|
|
914
|
+
export function renderResult(r, opts) {
|
|
777
915
|
const lines = [];
|
|
778
916
|
const showRepoHeader = r.repos.length > 1;
|
|
779
917
|
for (const repo of r.repos) {
|
|
@@ -797,27 +935,42 @@ function renderResult(r, opts) {
|
|
|
797
935
|
else if (opts.count) {
|
|
798
936
|
lines.push(`${m.file}:${m.line}`);
|
|
799
937
|
}
|
|
800
|
-
else if (m.kind === "context") {
|
|
801
|
-
lines.push(`${m.file}-${m.line}-${m.text}`);
|
|
802
|
-
}
|
|
803
938
|
else {
|
|
804
|
-
|
|
939
|
+
const sep = m.kind === "context" ? "-" : ":";
|
|
940
|
+
const more = m.clipped ? ` …[+${m.clipped} chars]` : "";
|
|
941
|
+
lines.push(`${m.file}${sep}${m.line}${sep}${m.text}${more}`);
|
|
805
942
|
}
|
|
806
943
|
prev = m;
|
|
807
944
|
}
|
|
808
|
-
if (repo.truncated) {
|
|
809
|
-
lines.push(" (truncated; raise --limit)");
|
|
810
|
-
}
|
|
811
945
|
}
|
|
812
|
-
|
|
946
|
+
const plural = (n, word) => `${n} ${word}${n === 1 ? "" : "s"}`;
|
|
947
|
+
const matchWord = (n) => `${n} match${n === 1 ? "" : "es"}`;
|
|
948
|
+
const fileRows = Boolean(opts.files || opts.filesOnly || opts.count);
|
|
949
|
+
if (r.total_matches === 0 && r.available_matches === 0) {
|
|
813
950
|
const modeTag = r.mode === "literal" ? " (literal)" : r.mode === "files" ? " (files)" : "";
|
|
814
951
|
lines.push(`(no matches for /${r.pattern}/${modeTag})`);
|
|
815
952
|
}
|
|
816
|
-
else if (r.
|
|
953
|
+
else if (r.omitted_matches > 0) {
|
|
954
|
+
// Something was left out: state the window, what was left out, and how to
|
|
955
|
+
// continue. Never printed when the page shows everything.
|
|
956
|
+
const first = r.limit === null ? 1 : (r.page - 1) * r.limit + 1;
|
|
957
|
+
const last = first + r.total_matches - 1;
|
|
958
|
+
const shown = r.total_matches === 0
|
|
959
|
+
? `Page ${r.page} is past the end`
|
|
960
|
+
: fileRows
|
|
961
|
+
? `Showing ${first}-${last} of ${plural(r.available_matches, "file")}`
|
|
962
|
+
: `Showing matches ${first}-${last} of ${r.available_matches} (in ${plural(r.available_files, "file")})`;
|
|
963
|
+
const left = fileRows
|
|
964
|
+
? `${plural(r.omitted_matches, "file")} left out`
|
|
965
|
+
: `${matchWord(r.omitted_matches)} in ${plural(r.omitted_files, "other file")} left out`;
|
|
966
|
+
const next = r.next_page !== null ? `. Next: --page ${r.next_page}` : "";
|
|
967
|
+
lines.push("");
|
|
968
|
+
lines.push(`${shown}; ${left}${next}. (${r.elapsed_ms}ms, ${r.engine})`);
|
|
969
|
+
}
|
|
970
|
+
else if (r.repos.length > 1) {
|
|
817
971
|
lines.push("");
|
|
818
|
-
const summary = `${r.total_matches}
|
|
819
|
-
|
|
820
|
-
lines.push(`${summary}${tail} (${r.elapsed_ms}ms, ${r.engine})`);
|
|
972
|
+
const summary = `${matchWord(r.total_matches)} across ${plural(r.total_files, "file")}`;
|
|
973
|
+
lines.push(`${summary} (${r.elapsed_ms}ms, ${r.engine})`);
|
|
821
974
|
}
|
|
822
975
|
return lines.join("\n");
|
|
823
976
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAOH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAOH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAUnD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AAOxE,OAAO,EAAiB,KAAK,SAAS,EAAE,KAAK,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAKlG,OAAO,EAeL,KAAK,YAAY,EAEjB,KAAK,aAAa,EACnB,MAAM,iCAAiC,CAAC;AA0CzC;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EAAE,EACnB,SAAS,CAAC,EAAE,SAAS,EAAE,GACtB;IAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAM3C;AA8FD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAsU/F;AAkBD;;;;GAIG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,mBAAmB,GAAG,MAAM,EAAE,CAiBjF;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,SAAS,GAAG,MAAM,GAAG,IAAI,CAOvE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,SAAS,CACvB,QAAQ,EAAE,YAAY,EACtB,IAAI,EAAE,WAAW,EACjB,aAAa,EAAE,MAAM,EACrB,OAAO,EAAE,SAAS,EAClB,OAAO,CAAC,EAAE,aAAa,GACtB;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAmEvE;AA2GD;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,YAAY,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAa1F;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAmDhG;AASD;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAkCxF"}
|
package/dist/commands/init.js
CHANGED
|
@@ -24,14 +24,14 @@ import { createHash } from "node:crypto";
|
|
|
24
24
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
25
25
|
import { dirname, relative, resolve } from "node:path";
|
|
26
26
|
import { fileURLToPath } from "node:url";
|
|
27
|
-
import { codexWindowsBridgeConfig, DEFAULT_BIN_NAME, pinnedBinName, stripJsonComments, } from "../core/config.js";
|
|
27
|
+
import { codexWindowsBridgeConfig, DEFAULT_BIN_NAME, pinnedBinName, portableHookCommands, stripJsonComments, } from "../core/config.js";
|
|
28
28
|
import { canonicalJsonV3 } from "../core/events/v3/canonical.js";
|
|
29
29
|
import { ADAPTER_CAPABILITY_PROFILES_V3 } from "../core/events/v3/capabilities.js";
|
|
30
30
|
import { EVENT_V3_SCHEMA_DIGEST } from "../core/events/v3/generated.js";
|
|
31
31
|
import { initializeEventLedgerV3, readEventV3ControlState, sha256V3, } from "../core/events/v3/index.js";
|
|
32
32
|
import { ADAPTER_SPECS } from "../core/hooks/adapter/events.js";
|
|
33
33
|
import { checkOpenCodePlugin, installOpenCodePlugin, } from "../core/hooks/adapter/opencode-plugin.js";
|
|
34
|
-
import { agentHookPathForProject, applyCursorStopLoopLimit, CURSOR_STOP_FOLLOWUP_LOOP_LIMIT, commandWiresSubcommand, diffWiring, expectedCommandWindows, groupCommands, hookCommand, hookTimeoutFor, isAgentHookCommand, makeEntry, orderHandlerKeys, summarizeAdapterWiring, } from "../core/hooks/adapter/wiring.js";
|
|
34
|
+
import { agentHookPathForProject, applyCursorStopLoopLimit, CURSOR_STOP_FOLLOWUP_LOOP_LIMIT, commandWiresSubcommand, diffWiring, expectedCommandWindows, groupCommands, hookCommand, hookTimeoutFor, isAgentHookCommand, makeEntry, orderHandlerKeys, projectWiringOptions, summarizeAdapterWiring, } from "../core/hooks/adapter/wiring.js";
|
|
35
35
|
import { detectInstalledAdapters } from "../core/workflow/adapters.js";
|
|
36
36
|
import { applyIndexerExclusions } from "../lib/indexer-exclusions.js";
|
|
37
37
|
import { applyInstructions, checkInstructions, } from "../lib/instructions/apply.js";
|
|
@@ -77,7 +77,8 @@ export function resolveInitAdapters(projectRoot, requested, installed) {
|
|
|
77
77
|
/** Drift check for one adapter's runtime wiring (settings hooks or the OpenCode plugin). */
|
|
78
78
|
function checkAdapterHooks(projectRoot, adapter, bin) {
|
|
79
79
|
const spec = ADAPTER_SPECS[adapter];
|
|
80
|
-
const
|
|
80
|
+
const options = projectWiringOptions(projectRoot);
|
|
81
|
+
const agentHook = agentHookPathForProject(projectRoot, HARNERY_ROOT, options);
|
|
81
82
|
if (spec.installMode === "opencode-plugin") {
|
|
82
83
|
// No hooks map to diff: the plugin directory is the wiring.
|
|
83
84
|
const plugin = checkOpenCodePlugin(projectRoot, { binName: bin, agentHook });
|
|
@@ -105,7 +106,7 @@ function checkAdapterHooks(projectRoot, adapter, bin) {
|
|
|
105
106
|
const hookDiff = diffWiring(settings, spec, {
|
|
106
107
|
agentHookPath: agentHook,
|
|
107
108
|
adapter,
|
|
108
|
-
|
|
109
|
+
...options,
|
|
109
110
|
});
|
|
110
111
|
drift =
|
|
111
112
|
hookDiff.missing.length > 0 ||
|
|
@@ -354,7 +355,8 @@ export function registerInitCommand(program, emit, binName) {
|
|
|
354
355
|
for (const adapter of adapters) {
|
|
355
356
|
const spec = ADAPTER_SPECS[adapter];
|
|
356
357
|
const settingsPath = resolve(projectRoot, spec.settingsFile);
|
|
357
|
-
const
|
|
358
|
+
const portableCommands = portableHookCommands(projectRoot);
|
|
359
|
+
const agentHook = agentHookPathForProject(projectRoot, HARNERY_ROOT, { portableCommands });
|
|
358
360
|
if (spec.installMode === "opencode-plugin") {
|
|
359
361
|
// OpenCode has no hooks map. Install the plugin directory instead; the
|
|
360
362
|
// instructions + skills step below still runs (AGENTS.md and
|
|
@@ -385,9 +387,9 @@ export function registerInitCommand(program, emit, binName) {
|
|
|
385
387
|
}
|
|
386
388
|
const bridge = codexWindowsBridgeConfig(projectRoot);
|
|
387
389
|
if (bridge.error && spec.windowsBridgeHooks) {
|
|
388
|
-
actions.push(`⚠ ${bridge.error}; writing ${adapter} hooks without
|
|
390
|
+
actions.push(`⚠ ${bridge.error}; writing ${adapter} hooks without the Windows bridge command`);
|
|
389
391
|
}
|
|
390
|
-
const { wired, already, removed, upgraded } = wireHooks(settings, spec, agentHook, adapter, { windowsBridgeEntryPoint: bridge.entryPoint });
|
|
392
|
+
const { wired, already, removed, upgraded } = wireHooks(settings, spec, agentHook, adapter, { windowsBridgeEntryPoint: bridge.entryPoint, portableCommands });
|
|
391
393
|
if (wired === 0 && removed === 0 && upgraded === 0) {
|
|
392
394
|
actions.push(`· all ${spec.events.length} ${adapter} hooks already wired in ${rel(projectRoot, settingsPath)}`);
|
|
393
395
|
}
|
|
@@ -440,6 +442,7 @@ export function registerInitCommand(program, emit, binName) {
|
|
|
440
442
|
}
|
|
441
443
|
function gitBuild(root) {
|
|
442
444
|
const result = spawnSync("git", ["-C", root, "rev-parse", "HEAD"], {
|
|
445
|
+
windowsHide: true,
|
|
443
446
|
encoding: "utf8",
|
|
444
447
|
timeout: 5000,
|
|
445
448
|
});
|
|
@@ -505,7 +508,7 @@ export function wireHooks(settings, spec, agentHookPath, adapter, options) {
|
|
|
505
508
|
}
|
|
506
509
|
}
|
|
507
510
|
for (const { settingsKey, subcommand } of spec.events) {
|
|
508
|
-
const command = hookCommand(spec, agentHookPath, subcommand, adapter);
|
|
511
|
+
const command = hookCommand(spec, agentHookPath, subcommand, adapter, options);
|
|
509
512
|
const timeout = hookTimeoutFor(spec, subcommand);
|
|
510
513
|
const commandWindows = expectedCommandWindows(spec, agentHookPath, subcommand, adapter, options);
|
|
511
514
|
const handlerFields = {
|
|
@@ -836,7 +839,10 @@ function renderInstructionsOnly(projectRoot, binName, dryRun, actions, warnings
|
|
|
836
839
|
return `${head}\n root: ${projectRoot}\n${actions.map((action) => ` ${action}`).join("\n")}${warnBlock}${tail}`;
|
|
837
840
|
}
|
|
838
841
|
function gitTopLevel() {
|
|
839
|
-
const r = spawnSync("git", ["rev-parse", "--show-toplevel"], {
|
|
842
|
+
const r = spawnSync("git", ["rev-parse", "--show-toplevel"], {
|
|
843
|
+
encoding: "utf8",
|
|
844
|
+
windowsHide: true,
|
|
845
|
+
});
|
|
840
846
|
const out = r.status === 0 ? r.stdout.trim() : "";
|
|
841
847
|
return out || null;
|
|
842
848
|
}
|
package/dist/commands/rm.js
CHANGED
|
@@ -11,11 +11,11 @@ export function registerRmCommand(program, emit, context) {
|
|
|
11
11
|
program
|
|
12
12
|
.command("rm <paths...>")
|
|
13
13
|
.description("Preview guarded removal of untracked files; --yes applies permanent deletion")
|
|
14
|
-
.requiredOption("--root <directory>", "Allowed directory; each target must be strictly inside it")
|
|
14
|
+
.requiredOption("--root <directory>", "Allowed directory; relative targets resolve under it, and each target must be strictly inside it")
|
|
15
15
|
.option("-r, --recursive", "Permit directory removal after inspecting its complete contents")
|
|
16
16
|
.option("--clean-clone", "Remove only clean standalone clones inside one owned artifact; refuse local-only commits and worktrees")
|
|
17
17
|
.option("--entry-limit <n>", "Maximum inspected entries, 1-100000 (default 10000); larger limits require one current caller-owned managed artifact as --root", (value) => Number(value))
|
|
18
|
-
.option("--allow-protected <path>", "Remove this one protected path (Git metadata, credentials, Harnery state, keys) inside a target; repeat per path", (value, previous = []) => [...previous, value])
|
|
18
|
+
.option("--allow-protected <path>", "Remove this one protected path (Git metadata, credentials, Harnery state, keys) inside a target; relative paths resolve under --root; repeat per path", (value, previous = []) => [...previous, value])
|
|
19
19
|
.option("--yes", "Permanently remove validated targets")
|
|
20
20
|
.option("--dry-run", "Preview only, even with --yes")
|
|
21
21
|
.option("--json", "Emit the versioned removal report")
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"web.d.ts","sourceRoot":"","sources":["../../src/commands/web.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AA8CnD;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAM1D;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAKrE;AAED;;;;;;GAMG;AACH,wBAAgB,6BAA6B,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAKhF;AAED,wBAAgB,4BAA4B,CAC1C,SAAS,EAAE,MAAM,EACjB,GAAG,GAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAe,GAC9D,MAAM,CAQR;AAmDD;;;;GAIG;AACH,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAevE;AAsBD,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"web.d.ts","sourceRoot":"","sources":["../../src/commands/web.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AA8CnD;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAM1D;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAKrE;AAED;;;;;;GAMG;AACH,wBAAgB,6BAA6B,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAKhF;AAED,wBAAgB,4BAA4B,CAC1C,SAAS,EAAE,MAAM,EACjB,GAAG,GAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAe,GAC9D,MAAM,CAQR;AAmDD;;;;GAIG;AACH,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAevE;AAsBD,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,GAAG,IAAI,CA6N5E"}
|
package/dist/commands/web.js
CHANGED
|
@@ -234,6 +234,7 @@ export function registerWebCommand(program, emit) {
|
|
|
234
234
|
if (heapMb > 0)
|
|
235
235
|
emit.log(`heap ceiling · ${heapMb} MB`, "info");
|
|
236
236
|
const child = spawn(webRunner(), ["run", mode], {
|
|
237
|
+
windowsHide: true,
|
|
237
238
|
cwd: root,
|
|
238
239
|
env: {
|
|
239
240
|
...process.env,
|
|
@@ -324,6 +325,7 @@ export function registerWebCommand(program, emit) {
|
|
|
324
325
|
if (heapMb > 0)
|
|
325
326
|
emit.log(`heap ceiling · ${heapMb} MB`, "info");
|
|
326
327
|
const child = spawn(webRunner(), ["run", "start"], {
|
|
328
|
+
windowsHide: true,
|
|
327
329
|
cwd: root,
|
|
328
330
|
env: {
|
|
329
331
|
...process.env,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"canonical-emit.d.ts","sourceRoot":"","sources":["../../../src/core/agents/canonical-emit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;
|
|
1
|
+
{"version":3,"file":"canonical-emit.d.ts","sourceRoot":"","sources":["../../../src/core/agents/canonical-emit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,KAAK,gBAAgB,EAAgC,MAAM,4BAA4B,CAAC;AACjG,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,kCAAkC,CAAC;AAGtF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAE7D,MAAM,MAAM,sBAAsB,GAC9B,6BAA6B,GAC7B;IACE,UAAU,EAAE,yBAAyB,CAAC;IACtC,SAAS,EAAE,QAAQ,GAAG,SAAS,GAAG,MAAM,CAAC;IACzC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sBAAsB,CAAC,EAAE,MAAM,CAAC;CACjC,CAAC;AAEN,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,gBAAgB,CAAC;IAC1B,WAAW,EAAE,sBAAsB,CAAC;CACrC;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,KAAK,GAAE,MAAsB,GAAG,MAAM,GAAG,IAAI,CAE5E;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CA4C5D;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,gBAAgB,CAE/E;AAED,kFAAkF;AAClF,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,IAAI,CAAC,SAAS,EAAE,mBAAmB,GAAG,YAAY,CAAC,GAAG,IAAI,GAAG,SAAS,EAC3E,QAAQ,EAAE,MAAM,GACf,MAAM,CAQR"}
|
|
@@ -9,9 +9,8 @@
|
|
|
9
9
|
* a 15-second safety budget; loaded-host checks normally finish in a few
|
|
10
10
|
* seconds and record the observation before the parent command exits.
|
|
11
11
|
*/
|
|
12
|
-
import { spawnSync } from "node:child_process";
|
|
13
12
|
import { eventAdapterIdV3FromPlatform } from "../events/v3/adapter-id.js";
|
|
14
|
-
import { coordBinPath } from "./coord-bin.js";
|
|
13
|
+
import { coordBinPath, spawnCoordBinSync } from "./coord-bin.js";
|
|
15
14
|
import { resolveCoordRoot } from "./coord-client.js";
|
|
16
15
|
/**
|
|
17
16
|
* Resolve the coord root for a canonical emit.
|
|
@@ -45,7 +44,7 @@ export function emitEventV3(input) {
|
|
|
45
44
|
];
|
|
46
45
|
// Pin cwd + HARNERY_COORD_ROOT_OVERRIDE so the child writes to the SAME
|
|
47
46
|
// stream we resolved, regardless of where the caller's shell is cd'd.
|
|
48
|
-
const result =
|
|
47
|
+
const result = spawnCoordBinSync(binary, args, {
|
|
49
48
|
encoding: "utf8",
|
|
50
49
|
stdio: ["pipe", "ignore", "pipe"],
|
|
51
50
|
input: JSON.stringify(input.observation),
|
|
@@ -20,7 +20,10 @@
|
|
|
20
20
|
* host whose harnery copy is not the one executing (a vendored tree, a shim)
|
|
21
21
|
* still resolves.
|
|
22
22
|
*/
|
|
23
|
+
import { type SpawnSyncOptions, type SpawnSyncOptionsWithStringEncoding, type SpawnSyncReturns } from "node:child_process";
|
|
23
24
|
export type CoordBinName = "agent-coord" | "agent-hook" | "harn";
|
|
25
|
+
export declare function spawnCoordBinSync(binary: string, args: readonly string[], options: SpawnSyncOptionsWithStringEncoding): SpawnSyncReturns<string>;
|
|
26
|
+
export declare function spawnCoordBinSync(binary: string, args: readonly string[], options?: SpawnSyncOptions): SpawnSyncReturns<string | Buffer>;
|
|
24
27
|
/**
|
|
25
28
|
* Absolute path to one of harnery's bundled binaries, or null when no candidate
|
|
26
29
|
* exists on disk. Callers must handle null — a missing helper is a real state
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"coord-bin.d.ts","sourceRoot":"","sources":["../../../src/core/agents/coord-bin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;
|
|
1
|
+
{"version":3,"file":"coord-bin.d.ts","sourceRoot":"","sources":["../../../src/core/agents/coord-bin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EACL,KAAK,gBAAgB,EACrB,KAAK,kCAAkC,EACvC,KAAK,gBAAgB,EAEtB,MAAM,oBAAoB,CAAC;AAM5B,MAAM,MAAM,YAAY,GAAG,aAAa,GAAG,YAAY,GAAG,MAAM,CAAC;AAEjE,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,OAAO,EAAE,kCAAkC,GAC1C,gBAAgB,CAAC,MAAM,CAAC,CAAC;AAC5B,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,OAAO,CAAC,EAAE,gBAAgB,GACzB,gBAAgB,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC;AAerC;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,YAAY,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAKzF;AAED,2EAA2E;AAC3E,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,YAAY,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,EAAE,CAuB1F"}
|
|
@@ -20,10 +20,16 @@
|
|
|
20
20
|
* host whose harnery copy is not the one executing (a vendored tree, a shim)
|
|
21
21
|
* still resolves.
|
|
22
22
|
*/
|
|
23
|
+
import { spawnSync, } from "node:child_process";
|
|
23
24
|
import { existsSync } from "node:fs";
|
|
24
25
|
import { dirname, join, resolve } from "node:path";
|
|
25
26
|
import { fileURLToPath } from "node:url";
|
|
26
27
|
import { coordEnv } from "../../lib/env.js";
|
|
28
|
+
/** Execute the native launcher on Windows, preserving stdin, argv and blocking exit codes. */
|
|
29
|
+
export function spawnCoordBinSync(binary, args, options = {}) {
|
|
30
|
+
const native = process.platform === "win32";
|
|
31
|
+
return spawnSync(native ? process.execPath : binary, native ? [`${binary}.mjs`, ...args] : [...args], { windowsHide: true, ...options });
|
|
32
|
+
}
|
|
27
33
|
/**
|
|
28
34
|
* Absolute path to one of harnery's bundled binaries, or null when no candidate
|
|
29
35
|
* exists on disk. Callers must handle null — a missing helper is a real state
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"coord-client.d.ts","sourceRoot":"","sources":["../../../src/core/agents/coord-client.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAeH;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAWhE;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,KAAK,IAAI,MAAM,CAI5E;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAOhF;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,IAAI,MAAM,GAAG,IAAI,CAE5C;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,GAAE,MAAsB,GAAG,MAAM,GAAG,IAAI,CA+B7E;AAED,iEAAiE;AACjE,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAE7D;AAeD;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAGtD;AAiED,qEAAqE;AACrE,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAIvD;AAED,+EAA+E;AAC/E,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAG1D;AAED,mFAAmF;AACnF,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAExE;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"coord-client.d.ts","sourceRoot":"","sources":["../../../src/core/agents/coord-client.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAeH;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAWhE;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,KAAK,IAAI,MAAM,CAI5E;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAOhF;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,IAAI,MAAM,GAAG,IAAI,CAE5C;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,GAAE,MAAsB,GAAG,MAAM,GAAG,IAAI,CA+B7E;AAED,iEAAiE;AACjE,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAE7D;AAeD;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAGtD;AAiED,qEAAqE;AACrE,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAIvD;AAED,+EAA+E;AAC/E,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAG1D;AAED,mFAAmF;AACnF,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAExE;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CA4CxD;AAgCD;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,IAAI,MAAM,GAAG,IAAI,CAE5C;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,IAAI;IACxC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,KAAK,GAAG,QAAQ,GAAG,iBAAiB,GAAG,aAAa,GAAG,kBAAkB,GAAG,MAAM,CAAC;CAC5F,CAsEA;AAED;;;;;;;;;GASG;AACH,wBAAgB,4BAA4B,CAC1C,IAAI,EAAE,MAAM,GACX;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAsB5C;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG;IAClD,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,QAAQ,GAAG,iBAAiB,GAAG,MAAM,CAAC;CAC/C,CAyBA;AAyBD,sFAAsF;AACtF;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,GAAG,IAAI,CAEtD;AAuCD;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAEpE;AAED;;;;;;GAMG;AACH,wBAAgB,gCAAgC,CAC9C,IAAI,EAAE,MAAM,GACX;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG,IAAI,CAmB9C;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAOpE"}
|
|
@@ -203,7 +203,7 @@ function gitRevParse(cwd, flag) {
|
|
|
203
203
|
// `cwd` must exist or the spawn itself fails; callers pass paths that may
|
|
204
204
|
// not (a project dir from a stale env var), so treat any failure as "no
|
|
205
205
|
// answer" rather than letting it throw.
|
|
206
|
-
const r = spawnSync("git", ["rev-parse", flag], { encoding: "utf8", cwd });
|
|
206
|
+
const r = spawnSync("git", ["rev-parse", flag], { encoding: "utf8", cwd, windowsHide: true });
|
|
207
207
|
if (r.status === 0)
|
|
208
208
|
value = r.stdout.trim() || null;
|
|
209
209
|
}
|
|
@@ -287,6 +287,7 @@ export function pidStartToken(pid) {
|
|
|
287
287
|
// two callers with different environments must not disagree about one
|
|
288
288
|
// process.
|
|
289
289
|
const out = spawnSync("ps", ["-o", "lstart=", "-p", String(pid)], {
|
|
290
|
+
windowsHide: true,
|
|
290
291
|
encoding: "utf8",
|
|
291
292
|
timeout: 2000,
|
|
292
293
|
env: { ...process.env, TZ: "UTC", LC_ALL: "C" },
|
|
@@ -651,7 +652,10 @@ function readPpid(pid) {
|
|
|
651
652
|
}
|
|
652
653
|
// Portable fallback: `ps -o ppid= -p <pid>` works on macOS/BSD/Linux.
|
|
653
654
|
try {
|
|
654
|
-
const out = spawnSync("ps", ["-o", "ppid=", "-p", String(pid)], {
|
|
655
|
+
const out = spawnSync("ps", ["-o", "ppid=", "-p", String(pid)], {
|
|
656
|
+
encoding: "utf8",
|
|
657
|
+
windowsHide: true,
|
|
658
|
+
});
|
|
655
659
|
if (out.status === 0) {
|
|
656
660
|
const parsed = Number.parseInt(out.stdout.trim(), 10);
|
|
657
661
|
if (Number.isFinite(parsed) && parsed > 0)
|