harnesstrim 0.0.5 → 0.0.6
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/LICENSE +21 -0
- package/assets/adapter-pi/extension/harnesstrim.ts +30 -17
- package/dist/cli.mjs +175 -17
- package/package.json +10 -12
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 HarnessTrim contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
//
|
|
3
3
|
// Pi fires `tool_result` after a tool finishes and before the result reaches the model;
|
|
4
4
|
// handlers chain like middleware and may return a patch ({ content, details, isError }).
|
|
5
|
-
// This extension reduces
|
|
5
|
+
// This extension reduces text chunks in structured tool results (test runners, git diffs, ...)
|
|
6
6
|
// by shelling out to `harnesstrim reduce`, so it is self-contained (no workspace imports)
|
|
7
7
|
// and loads from `~/.pi/agent/extensions/` or `<project>/.pi/extensions/`.
|
|
8
8
|
//
|
|
@@ -12,15 +12,19 @@
|
|
|
12
12
|
// HARNESSTRIM_MINLENGTH=<chars> (default 400)
|
|
13
13
|
import { spawnSync } from "node:child_process";
|
|
14
14
|
|
|
15
|
+
type TextContent = { type: "text"; text: string };
|
|
16
|
+
type ToolContent = TextContent | { type: string; [key: string]: unknown };
|
|
17
|
+
|
|
15
18
|
interface ToolResultEvent {
|
|
16
|
-
content?:
|
|
19
|
+
content?: ToolContent[];
|
|
17
20
|
isError?: boolean;
|
|
18
21
|
}
|
|
19
22
|
interface ExtensionAPI {
|
|
20
23
|
on(event: string, handler: (event: ToolResultEvent, ctx: unknown) => unknown): void;
|
|
21
24
|
}
|
|
22
25
|
|
|
23
|
-
const
|
|
26
|
+
const runtime = globalThis as typeof globalThis & { process?: NodeJS.Process };
|
|
27
|
+
const env = runtime.process?.env ?? {};
|
|
24
28
|
const MODE = env.HARNESSTRIM_MODE ?? "dryrun";
|
|
25
29
|
const MIN_LENGTH = Number(env.HARNESSTRIM_MINLENGTH ?? "400") || 400;
|
|
26
30
|
const MARKER = "[harnesstrim";
|
|
@@ -44,19 +48,28 @@ function reduceViaCli(text: string): string | null {
|
|
|
44
48
|
export default function harnesstrim(pi: ExtensionAPI): void {
|
|
45
49
|
if (MODE === "off") return;
|
|
46
50
|
pi.on("tool_result", async (event) => {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
51
|
+
if (!Array.isArray(event.content)) return;
|
|
52
|
+
|
|
53
|
+
let changed = false;
|
|
54
|
+
const content = event.content.map((chunk) => {
|
|
55
|
+
if (chunk.type !== "text" || typeof chunk.text !== "string") return chunk;
|
|
56
|
+
const text = chunk.text;
|
|
57
|
+
if (text.length < MIN_LENGTH || text.includes(MARKER)) return chunk;
|
|
58
|
+
|
|
59
|
+
const reduced = reduceViaCli(text);
|
|
60
|
+
if (!reduced || reduced.length >= text.length) return chunk;
|
|
61
|
+
|
|
62
|
+
if (MODE === "dryrun") {
|
|
63
|
+
runtime.process?.stderr?.write(
|
|
64
|
+
`[harnesstrim] dryrun tool_result: ${text.length} -> ${reduced.length} chars\n`
|
|
65
|
+
);
|
|
66
|
+
return chunk;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
changed = true;
|
|
70
|
+
return { ...chunk, text: reduced };
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
return changed ? { content } : undefined;
|
|
61
74
|
});
|
|
62
75
|
}
|
package/dist/cli.mjs
CHANGED
|
@@ -581,6 +581,75 @@ ${input.slice(response.index)}`;
|
|
|
581
581
|
}
|
|
582
582
|
});
|
|
583
583
|
|
|
584
|
+
// ../core/src/reducers/lint-output-slim.ts
|
|
585
|
+
function isLintLine(line) {
|
|
586
|
+
return LINT_LINE_RE.test(line);
|
|
587
|
+
}
|
|
588
|
+
var MARKER_PREFIX7, LINT_LINE_RE, MAX_RULES_IN_MARKER, lintOutputSlim;
|
|
589
|
+
var init_lint_output_slim = __esm({
|
|
590
|
+
"../core/src/reducers/lint-output-slim.ts"() {
|
|
591
|
+
"use strict";
|
|
592
|
+
MARKER_PREFIX7 = "[harnesstrim:lint-output-slim]";
|
|
593
|
+
LINT_LINE_RE = /^[\w.\/\\-]+:\d+:\d+\s+(warning|error)\s+([\w@.\/-]+)/;
|
|
594
|
+
MAX_RULES_IN_MARKER = 8;
|
|
595
|
+
lintOutputSlim = {
|
|
596
|
+
name: "lint-output-slim",
|
|
597
|
+
reduce(input) {
|
|
598
|
+
const lines = input.split(/\r?\n/);
|
|
599
|
+
const out = [];
|
|
600
|
+
let droppedTotal = 0;
|
|
601
|
+
let i = 0;
|
|
602
|
+
while (i < lines.length) {
|
|
603
|
+
const line = lines[i];
|
|
604
|
+
if (!isLintLine(line)) {
|
|
605
|
+
out.push(line);
|
|
606
|
+
i++;
|
|
607
|
+
continue;
|
|
608
|
+
}
|
|
609
|
+
const counts = [];
|
|
610
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
611
|
+
let runEnd = i;
|
|
612
|
+
while (runEnd < lines.length && isLintLine(lines[runEnd])) {
|
|
613
|
+
const m = LINT_LINE_RE.exec(lines[runEnd]);
|
|
614
|
+
const severity = m[1] === "error" ? "error" : "warning";
|
|
615
|
+
const rule = m[2];
|
|
616
|
+
const key = `${severity}:${rule}`;
|
|
617
|
+
const existing = byKey.get(key);
|
|
618
|
+
if (existing) {
|
|
619
|
+
existing.count++;
|
|
620
|
+
} else {
|
|
621
|
+
const entry = { severity, rule, count: 1 };
|
|
622
|
+
byKey.set(key, entry);
|
|
623
|
+
counts.push(entry);
|
|
624
|
+
}
|
|
625
|
+
runEnd++;
|
|
626
|
+
}
|
|
627
|
+
const runLength = runEnd - i;
|
|
628
|
+
if (runLength >= 2) {
|
|
629
|
+
const parts = counts.slice(0, MAX_RULES_IN_MARKER).map(
|
|
630
|
+
(c) => `${c.rule} \xD7${c.count}`
|
|
631
|
+
);
|
|
632
|
+
const truncated = counts.length > MAX_RULES_IN_MARKER;
|
|
633
|
+
const suffix = truncated ? `, +${counts.length - MAX_RULES_IN_MARKER} more rule(s)` : "";
|
|
634
|
+
const severities = counts.some((c) => c.severity === "error") && counts.some((c) => c.severity === "warning") ? "error(s) and warning(s)" : counts.some((c) => c.severity === "error") ? "error(s)" : "warning(s)";
|
|
635
|
+
out.push(`${MARKER_PREFIX7} omitted ${runLength} lint line(s) (${severities}: ${parts.join(", ")}${suffix})`);
|
|
636
|
+
droppedTotal += runLength;
|
|
637
|
+
} else {
|
|
638
|
+
for (let j = i; j < runEnd; j++) out.push(lines[j]);
|
|
639
|
+
}
|
|
640
|
+
i = runEnd;
|
|
641
|
+
}
|
|
642
|
+
const output = out.join("\n");
|
|
643
|
+
return {
|
|
644
|
+
output,
|
|
645
|
+
changed: droppedTotal > 0,
|
|
646
|
+
note: droppedTotal > 0 ? `dropped ${droppedTotal} lint noise line(s)` : void 0
|
|
647
|
+
};
|
|
648
|
+
}
|
|
649
|
+
};
|
|
650
|
+
}
|
|
651
|
+
});
|
|
652
|
+
|
|
584
653
|
// ../core/src/reducers/index.ts
|
|
585
654
|
var init_reducers = __esm({
|
|
586
655
|
"../core/src/reducers/index.ts"() {
|
|
@@ -592,6 +661,7 @@ var init_reducers = __esm({
|
|
|
592
661
|
init_json_output_slim();
|
|
593
662
|
init_file_listing_slim();
|
|
594
663
|
init_cron_output_slim();
|
|
664
|
+
init_lint_output_slim();
|
|
595
665
|
}
|
|
596
666
|
});
|
|
597
667
|
|
|
@@ -600,6 +670,7 @@ function pickReducer(text) {
|
|
|
600
670
|
if (GIT_DIFF_RE.test(text)) return gitDiffSlim;
|
|
601
671
|
if (TEST_OUTPUT_RE.test(text)) return testOutputSlim;
|
|
602
672
|
if (CRON_OUTPUT_RE.test(text) && text.length >= 400) return cronOutputSlim;
|
|
673
|
+
if (LINT_OUTPUT_RE.test(text) && text.length >= 400) return lintOutputSlim;
|
|
603
674
|
if (JSON_RE.test(text) && text.length >= 400) return jsonOutputSlim;
|
|
604
675
|
if (FILE_LISTING_RE.test(text) && text.length >= 400) return fileListingSlim;
|
|
605
676
|
if (LONG_TEXT_RE.test(text) && text.length >= 1e3) return genericTextSlim;
|
|
@@ -619,7 +690,7 @@ function reduceAuto(text, minLength = DEFAULT_MIN_LENGTH) {
|
|
|
619
690
|
}
|
|
620
691
|
return { ...result, reducer: reducer.name };
|
|
621
692
|
}
|
|
622
|
-
var DEFAULT_MIN_LENGTH, GIT_DIFF_RE, TEST_OUTPUT_RE, JSON_RE, FILE_LISTING_RE, CRON_OUTPUT_RE, LONG_TEXT_RE;
|
|
693
|
+
var DEFAULT_MIN_LENGTH, GIT_DIFF_RE, TEST_OUTPUT_RE, JSON_RE, FILE_LISTING_RE, CRON_OUTPUT_RE, LINT_OUTPUT_RE, LONG_TEXT_RE;
|
|
623
694
|
var init_dispatch = __esm({
|
|
624
695
|
"../core/src/dispatch.ts"() {
|
|
625
696
|
"use strict";
|
|
@@ -629,12 +700,14 @@ var init_dispatch = __esm({
|
|
|
629
700
|
init_json_output_slim();
|
|
630
701
|
init_file_listing_slim();
|
|
631
702
|
init_cron_output_slim();
|
|
703
|
+
init_lint_output_slim();
|
|
632
704
|
DEFAULT_MIN_LENGTH = 400;
|
|
633
705
|
GIT_DIFF_RE = /^diff --git /m;
|
|
634
706
|
TEST_OUTPUT_RE = /\b\d+\s+(passed|failed)\b|^(PASS|FAIL)\s|::\w.*\b(PASSED|FAILED)\b|=+\s*(FAILURES|short test summary)/im;
|
|
635
707
|
JSON_RE = /^\s*[\[{]/m;
|
|
636
708
|
FILE_LISTING_RE = /(?:^total\s+\d+|^[\-bcdlsp][\-r][\-w][\-xs\-][\-r][\-w][\-xs\-][\-r][\-w][\-xs\-]|^\.\/(?:\.|[^.\s])|^\s*(?:├──|└──|│\s+)|^[\w.\/\-]+\.[a-zA-Z]{1,4}:\d+\|)/m;
|
|
637
709
|
CRON_OUTPUT_RE = /^# Cron Job:.*\n[\s\S]*^## Prompt\s*$[\s\S]*^## Response\s*$/m;
|
|
710
|
+
LINT_OUTPUT_RE = /^[\w.\/\\-]+:\d+:\d+\s+(?:warning|error)\s+[\w@.\/-]+\s/m;
|
|
638
711
|
LONG_TEXT_RE = /^#{1,4}\s.*\n(?:(?!^#{1,4}\s|^diff --git |^```).*\n){5,}/m;
|
|
639
712
|
}
|
|
640
713
|
});
|
|
@@ -22627,7 +22700,7 @@ init_src();
|
|
|
22627
22700
|
import { parseArgs } from "node:util";
|
|
22628
22701
|
import fs11 from "node:fs";
|
|
22629
22702
|
import path15 from "node:path";
|
|
22630
|
-
import
|
|
22703
|
+
import os4 from "node:os";
|
|
22631
22704
|
|
|
22632
22705
|
// src/doctor.ts
|
|
22633
22706
|
import fs from "node:fs";
|
|
@@ -22897,7 +22970,9 @@ function runInstallOpencode(dir, apply, presetName, installDeps = true) {
|
|
|
22897
22970
|
const npm = process.platform === "win32" ? "npm.cmd" : "npm";
|
|
22898
22971
|
const res = spawnSync(npm, ["install", "--silent"], {
|
|
22899
22972
|
cwd: path2.dirname(packageJsonPath),
|
|
22900
|
-
encoding: "utf8"
|
|
22973
|
+
encoding: "utf8",
|
|
22974
|
+
input: "",
|
|
22975
|
+
timeout: 12e4
|
|
22901
22976
|
});
|
|
22902
22977
|
if (res.error || res.status !== 0) {
|
|
22903
22978
|
depsInstalled = false;
|
|
@@ -22922,6 +22997,7 @@ function runInstallOpencode(dir, apply, presetName, installDeps = true) {
|
|
|
22922
22997
|
|
|
22923
22998
|
// src/install-codex.ts
|
|
22924
22999
|
import fs5 from "node:fs";
|
|
23000
|
+
import os from "node:os";
|
|
22925
23001
|
import path6 from "node:path";
|
|
22926
23002
|
|
|
22927
23003
|
// ../adapter-codex/src/index.ts
|
|
@@ -22993,9 +23069,31 @@ function hasHarnessTrimHook(document) {
|
|
|
22993
23069
|
const post = hooks?.PostToolUse;
|
|
22994
23070
|
if (!Array.isArray(post)) return false;
|
|
22995
23071
|
return post.some(
|
|
22996
|
-
(entry) => Array.isArray(entry?.hooks) && entry.hooks.some(
|
|
23072
|
+
(entry) => Array.isArray(entry?.hooks) && entry.hooks.some(
|
|
23073
|
+
(hook) => typeof hook?.command === "string" && hook.command.includes("harnesstrim") && hook.command.includes("hook codex")
|
|
23074
|
+
)
|
|
22997
23075
|
);
|
|
22998
23076
|
}
|
|
23077
|
+
function hasExactHarnessTrimHookCommand(document, command) {
|
|
23078
|
+
const hooks = document.hooks;
|
|
23079
|
+
const post = hooks?.PostToolUse;
|
|
23080
|
+
return Array.isArray(post) && post.some(
|
|
23081
|
+
(entry) => Array.isArray(entry?.hooks) && entry.hooks.some((hook) => hook?.command === command)
|
|
23082
|
+
);
|
|
23083
|
+
}
|
|
23084
|
+
function replaceHarnessTrimHookCommand(document, command) {
|
|
23085
|
+
const hooks = document.hooks;
|
|
23086
|
+
const post = hooks?.PostToolUse;
|
|
23087
|
+
if (!Array.isArray(post)) return;
|
|
23088
|
+
for (const entry of post) {
|
|
23089
|
+
if (!Array.isArray(entry?.hooks)) continue;
|
|
23090
|
+
for (const hook of entry.hooks) {
|
|
23091
|
+
if (typeof hook?.command === "string" && hook.command.includes("harnesstrim") && hook.command.includes("hook codex")) {
|
|
23092
|
+
hook.command = command;
|
|
23093
|
+
}
|
|
23094
|
+
}
|
|
23095
|
+
}
|
|
23096
|
+
}
|
|
22999
23097
|
function planCodexHookInstall(input) {
|
|
23000
23098
|
let document = {};
|
|
23001
23099
|
let action;
|
|
@@ -23012,14 +23110,21 @@ function planCodexHookInstall(input) {
|
|
|
23012
23110
|
throw new Error(".codex/hooks.json must contain a JSON object; refusing to overwrite it.");
|
|
23013
23111
|
}
|
|
23014
23112
|
document = parsed;
|
|
23015
|
-
action = hasHarnessTrimHook(document) ? "present" : "patch";
|
|
23113
|
+
action = hasHarnessTrimHook(document) && (!input.hookCommand || hasExactHarnessTrimHookCommand(document, input.hookCommand)) ? "present" : "patch";
|
|
23016
23114
|
}
|
|
23017
23115
|
if (action === "present") {
|
|
23018
23116
|
return { hooksFile: path3.join(input.projectDir, ".codex", "hooks.json"), action, nextHooks: document };
|
|
23019
23117
|
}
|
|
23118
|
+
if (hasHarnessTrimHook(document)) {
|
|
23119
|
+
replaceHarnessTrimHookCommand(document, input.hookCommand ?? CODEX_HOOK_COMMAND);
|
|
23120
|
+
return { hooksFile: path3.join(input.projectDir, ".codex", "hooks.json"), action, nextHooks: document };
|
|
23121
|
+
}
|
|
23020
23122
|
const hooks = { ...document.hooks ?? {} };
|
|
23021
23123
|
const post = Array.isArray(hooks.PostToolUse) ? [...hooks.PostToolUse] : [];
|
|
23022
|
-
post.push({
|
|
23124
|
+
post.push({
|
|
23125
|
+
matcher: CODEX_HOOK_MATCHER,
|
|
23126
|
+
hooks: [{ type: "command", command: input.hookCommand ?? CODEX_HOOK_COMMAND }]
|
|
23127
|
+
});
|
|
23023
23128
|
hooks.PostToolUse = post;
|
|
23024
23129
|
return {
|
|
23025
23130
|
hooksFile: path3.join(input.projectDir, ".codex", "hooks.json"),
|
|
@@ -23094,6 +23199,13 @@ function existingSkillNames(dest) {
|
|
|
23094
23199
|
}
|
|
23095
23200
|
|
|
23096
23201
|
// src/install-codex.ts
|
|
23202
|
+
function resolveCodexHookCommand() {
|
|
23203
|
+
if (process.platform !== "win32") return void 0;
|
|
23204
|
+
const pnpmHome = process.env.PNPM_HOME ?? path6.join(process.env.LOCALAPPDATA ?? path6.join(os.homedir(), "AppData", "Local"), "pnpm");
|
|
23205
|
+
const shim = path6.join(pnpmHome, "harnesstrim.CMD");
|
|
23206
|
+
if (!fs5.existsSync(shim)) return void 0;
|
|
23207
|
+
return `"${shim}" hook codex --metrics .harnesstrim/metrics.jsonl`;
|
|
23208
|
+
}
|
|
23097
23209
|
function readHooksJson(hooksPath) {
|
|
23098
23210
|
try {
|
|
23099
23211
|
return fs5.readFileSync(hooksPath, "utf8");
|
|
@@ -23127,7 +23239,7 @@ function runInstallCodex(dir, apply, hook = false) {
|
|
|
23127
23239
|
});
|
|
23128
23240
|
const hooksPath = path6.join(dir, ".codex", "hooks.json");
|
|
23129
23241
|
const hooksJsonContent = hook ? readHooksJson(hooksPath) : null;
|
|
23130
|
-
const hookPlan = hook ? planCodexHookInstall({ projectDir: dir, hooksJsonContent }) : null;
|
|
23242
|
+
const hookPlan = hook ? planCodexHookInstall({ projectDir: dir, hooksJsonContent, hookCommand: resolveCodexHookCommand() }) : null;
|
|
23131
23243
|
const copied = [];
|
|
23132
23244
|
let applied = false;
|
|
23133
23245
|
if (apply) {
|
|
@@ -23152,7 +23264,8 @@ function runInstallCodexGlobalHook(codexHome, apply) {
|
|
|
23152
23264
|
// The planner expects the directory that contains .codex; for a user-level config
|
|
23153
23265
|
// the Codex home is itself that directory, so add its parent and use a normal path.
|
|
23154
23266
|
projectDir: path6.dirname(codexHome),
|
|
23155
|
-
hooksJsonContent: readHooksJson(hooksPath)
|
|
23267
|
+
hooksJsonContent: readHooksJson(hooksPath),
|
|
23268
|
+
hookCommand: resolveCodexHookCommand()
|
|
23156
23269
|
});
|
|
23157
23270
|
return { hookPlan, applied: applyHookPlan(hookPlan, apply) };
|
|
23158
23271
|
}
|
|
@@ -23332,12 +23445,13 @@ function runInstallClaude(dir, apply) {
|
|
|
23332
23445
|
// src/install-pi.ts
|
|
23333
23446
|
import fs7 from "node:fs";
|
|
23334
23447
|
import path10 from "node:path";
|
|
23448
|
+
import os2 from "node:os";
|
|
23335
23449
|
|
|
23336
23450
|
// ../adapter-pi/src/index.ts
|
|
23337
23451
|
import path9 from "node:path";
|
|
23338
23452
|
var PI_EXTENSION_NAME = "harnesstrim";
|
|
23339
23453
|
function planPiInstall(input) {
|
|
23340
|
-
const extensionDest = path9.join(input.installDir, ".pi", "extensions", PI_EXTENSION_NAME);
|
|
23454
|
+
const extensionDest = input.scope === "user" ? path9.join(input.installDir, ".pi", "agent", "extensions", PI_EXTENSION_NAME) : path9.join(input.installDir, ".pi", "extensions", PI_EXTENSION_NAME);
|
|
23341
23455
|
return {
|
|
23342
23456
|
extensionDest,
|
|
23343
23457
|
extensionSource: input.extensionSourceDir,
|
|
@@ -23372,16 +23486,18 @@ function markerPresent(dest) {
|
|
|
23372
23486
|
}
|
|
23373
23487
|
function runInstallPi(installDir, apply) {
|
|
23374
23488
|
const extensionSourceDir = resolvePiExtensionSourceDir();
|
|
23375
|
-
const
|
|
23489
|
+
const scope = path10.resolve(installDir) === path10.resolve(os2.homedir()) ? "user" : "project";
|
|
23490
|
+
const dest = scope === "user" ? path10.join(installDir, ".pi", "agent", "extensions", "harnesstrim") : path10.join(installDir, ".pi", "extensions", "harnesstrim");
|
|
23376
23491
|
const plan = planPiInstall({
|
|
23377
23492
|
installDir,
|
|
23378
23493
|
extensionSourceDir,
|
|
23379
23494
|
extensionDirExists: dirExists(dest),
|
|
23380
|
-
markerPresent: markerPresent(dest)
|
|
23495
|
+
markerPresent: markerPresent(dest),
|
|
23496
|
+
scope
|
|
23381
23497
|
});
|
|
23382
23498
|
const copiedFiles = [];
|
|
23383
23499
|
let applied = false;
|
|
23384
|
-
if (apply
|
|
23500
|
+
if (apply) {
|
|
23385
23501
|
fs7.mkdirSync(dest, { recursive: true });
|
|
23386
23502
|
for (const entry of fs7.readdirSync(extensionSourceDir, { withFileTypes: true })) {
|
|
23387
23503
|
if (!entry.isDirectory()) {
|
|
@@ -23485,8 +23601,8 @@ function runInstallHermes(installDir, apply) {
|
|
|
23485
23601
|
init_src();
|
|
23486
23602
|
import fs9 from "node:fs";
|
|
23487
23603
|
import path13 from "node:path";
|
|
23488
|
-
import
|
|
23489
|
-
var HERMES_METRICS_PATH = path13.join(
|
|
23604
|
+
import os3 from "node:os";
|
|
23605
|
+
var HERMES_METRICS_PATH = path13.join(os3.homedir(), ".hermes", "harnesstrim-metrics.jsonl");
|
|
23490
23606
|
var LOCAL_METRICS_PATH = ".harnesstrim/metrics.jsonl";
|
|
23491
23607
|
var DEFAULT_METRICS_PATH = fs9.existsSync(HERMES_METRICS_PATH) ? HERMES_METRICS_PATH : LOCAL_METRICS_PATH;
|
|
23492
23608
|
function loadMetrics(filePath) {
|
|
@@ -23502,9 +23618,46 @@ function loadMetrics(filePath) {
|
|
|
23502
23618
|
return { path: filePath, found: true, summary: summarize(parseTrimEvents(raw)) };
|
|
23503
23619
|
}
|
|
23504
23620
|
|
|
23621
|
+
// package.json
|
|
23622
|
+
var package_default = {
|
|
23623
|
+
name: "harnesstrim",
|
|
23624
|
+
version: "0.0.6",
|
|
23625
|
+
description: "HarnessTrim CLI: doctor (diagnose token waste), install adapters, run benchmarks.",
|
|
23626
|
+
license: "MIT",
|
|
23627
|
+
type: "module",
|
|
23628
|
+
bin: {
|
|
23629
|
+
harnesstrim: "./dist/cli.mjs"
|
|
23630
|
+
},
|
|
23631
|
+
files: [
|
|
23632
|
+
"dist",
|
|
23633
|
+
"assets"
|
|
23634
|
+
],
|
|
23635
|
+
publishConfig: {
|
|
23636
|
+
access: "public"
|
|
23637
|
+
},
|
|
23638
|
+
devDependencies: {
|
|
23639
|
+
"@harnesstrim/adapter-claude": "workspace:*",
|
|
23640
|
+
"@harnesstrim/adapter-codex": "workspace:*",
|
|
23641
|
+
"@harnesstrim/adapter-hermes": "workspace:*",
|
|
23642
|
+
"@harnesstrim/adapter-pi": "workspace:*",
|
|
23643
|
+
"@harnesstrim/benchmarks": "workspace:*",
|
|
23644
|
+
"@harnesstrim/core": "workspace:*",
|
|
23645
|
+
"@harnesstrim/mcp": "workspace:*",
|
|
23646
|
+
esbuild: "^0.25.0"
|
|
23647
|
+
},
|
|
23648
|
+
scripts: {
|
|
23649
|
+
build: "node build.mjs",
|
|
23650
|
+
prepare: "node build.mjs",
|
|
23651
|
+
prepack: "node build.mjs",
|
|
23652
|
+
test: 'node --test "src/**/*.test.ts"',
|
|
23653
|
+
typecheck: "tsc -p tsconfig.json"
|
|
23654
|
+
}
|
|
23655
|
+
};
|
|
23656
|
+
|
|
23505
23657
|
// src/reduce.ts
|
|
23506
23658
|
init_src();
|
|
23507
23659
|
async function readStdin() {
|
|
23660
|
+
if (process.stdin.isTTY) return "";
|
|
23508
23661
|
const chunks = [];
|
|
23509
23662
|
for await (const chunk of process.stdin) {
|
|
23510
23663
|
chunks.push(chunk);
|
|
@@ -23798,7 +23951,7 @@ Usage:
|
|
|
23798
23951
|
harnesstrim mcp [--metrics <path>] Start the MCP server (stdio) exposing a reduce tool;
|
|
23799
23952
|
--metrics records a TrimEvent per reduction
|
|
23800
23953
|
harnesstrim bench Run the Tier A reducer micro-benchmark
|
|
23801
|
-
harnesstrim --
|
|
23954
|
+
harnesstrim --version Print the installed version
|
|
23802
23955
|
|
|
23803
23956
|
Notes:
|
|
23804
23957
|
- install is dry-run by default; nothing is written without --apply.
|
|
@@ -23810,6 +23963,7 @@ async function main(argv) {
|
|
|
23810
23963
|
allowPositionals: true,
|
|
23811
23964
|
options: {
|
|
23812
23965
|
help: { type: "boolean", short: "h" },
|
|
23966
|
+
version: { type: "boolean", short: "v" },
|
|
23813
23967
|
apply: { type: "boolean" },
|
|
23814
23968
|
preset: { type: "string" },
|
|
23815
23969
|
stats: { type: "boolean" },
|
|
@@ -23821,6 +23975,10 @@ async function main(argv) {
|
|
|
23821
23975
|
}
|
|
23822
23976
|
});
|
|
23823
23977
|
const [command, ...rest] = positionals;
|
|
23978
|
+
if (values.version) {
|
|
23979
|
+
console.log(package_default.version);
|
|
23980
|
+
return 0;
|
|
23981
|
+
}
|
|
23824
23982
|
if (values.help || !command) {
|
|
23825
23983
|
console.log(HELP);
|
|
23826
23984
|
return 0;
|
|
@@ -23833,7 +23991,7 @@ async function main(argv) {
|
|
|
23833
23991
|
}
|
|
23834
23992
|
case "install": {
|
|
23835
23993
|
const target = rest[0];
|
|
23836
|
-
const dir = rest[1] ?? (target === "hermes" ?
|
|
23994
|
+
const dir = rest[1] ?? (target === "hermes" ? os4.homedir() : process.cwd());
|
|
23837
23995
|
const apply = values.apply === true;
|
|
23838
23996
|
if (target === "opencode") {
|
|
23839
23997
|
const result = runInstallOpencode(dir, apply, values.preset);
|
|
@@ -23846,7 +24004,7 @@ async function main(argv) {
|
|
|
23846
24004
|
console.error("`harnesstrim install codex --global` requires `--hook`.");
|
|
23847
24005
|
return 1;
|
|
23848
24006
|
}
|
|
23849
|
-
console.log(renderCodexGlobalHookInstall(runInstallCodexGlobalHook(path15.join(
|
|
24007
|
+
console.log(renderCodexGlobalHookInstall(runInstallCodexGlobalHook(path15.join(os4.homedir(), ".codex"), apply), apply));
|
|
23850
24008
|
return 0;
|
|
23851
24009
|
}
|
|
23852
24010
|
console.log(renderCodexInstall(runInstallCodex(dir, apply, values.hook === true), apply));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "harnesstrim",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.6",
|
|
4
4
|
"description": "HarnessTrim CLI: doctor (diagnose token waste), install adapters, run benchmarks.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -15,20 +15,18 @@
|
|
|
15
15
|
"access": "public"
|
|
16
16
|
},
|
|
17
17
|
"devDependencies": {
|
|
18
|
-
"
|
|
19
|
-
"@harnesstrim/adapter-
|
|
20
|
-
"@harnesstrim/adapter-hermes": "
|
|
21
|
-
"@harnesstrim/
|
|
22
|
-
"@harnesstrim/
|
|
23
|
-
"@harnesstrim/
|
|
24
|
-
"@harnesstrim/mcp": "
|
|
25
|
-
"
|
|
18
|
+
"esbuild": "^0.25.0",
|
|
19
|
+
"@harnesstrim/adapter-claude": "0.0.1",
|
|
20
|
+
"@harnesstrim/adapter-hermes": "0.0.1",
|
|
21
|
+
"@harnesstrim/core": "0.0.2",
|
|
22
|
+
"@harnesstrim/adapter-codex": "0.0.1",
|
|
23
|
+
"@harnesstrim/benchmarks": "0.0.1",
|
|
24
|
+
"@harnesstrim/mcp": "0.0.1",
|
|
25
|
+
"@harnesstrim/adapter-pi": "0.0.1"
|
|
26
26
|
},
|
|
27
27
|
"scripts": {
|
|
28
28
|
"build": "node build.mjs",
|
|
29
|
-
"prepare": "node build.mjs",
|
|
30
|
-
"prepack": "node build.mjs",
|
|
31
29
|
"test": "node --test \"src/**/*.test.ts\"",
|
|
32
30
|
"typecheck": "tsc -p tsconfig.json"
|
|
33
31
|
}
|
|
34
|
-
}
|
|
32
|
+
}
|