harnesstrim 0.0.4 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.mjs +125 -35
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -1166,8 +1166,8 @@ var init_parseUtil = __esm({
|
|
|
1166
1166
|
init_errors();
|
|
1167
1167
|
init_en();
|
|
1168
1168
|
makeIssue = (params) => {
|
|
1169
|
-
const { data, path:
|
|
1170
|
-
const fullPath = [...
|
|
1169
|
+
const { data, path: path16, errorMaps, issueData } = params;
|
|
1170
|
+
const fullPath = [...path16, ...issueData.path || []];
|
|
1171
1171
|
const fullIssue = {
|
|
1172
1172
|
...issueData,
|
|
1173
1173
|
path: fullPath
|
|
@@ -1475,11 +1475,11 @@ var init_types2 = __esm({
|
|
|
1475
1475
|
init_parseUtil();
|
|
1476
1476
|
init_util();
|
|
1477
1477
|
ParseInputLazyPath = class {
|
|
1478
|
-
constructor(parent, value,
|
|
1478
|
+
constructor(parent, value, path16, key) {
|
|
1479
1479
|
this._cachedPath = [];
|
|
1480
1480
|
this.parent = parent;
|
|
1481
1481
|
this.data = value;
|
|
1482
|
-
this._path =
|
|
1482
|
+
this._path = path16;
|
|
1483
1483
|
this._key = key;
|
|
1484
1484
|
}
|
|
1485
1485
|
get path() {
|
|
@@ -5060,10 +5060,10 @@ function assignProp(target, prop, value) {
|
|
|
5060
5060
|
configurable: true
|
|
5061
5061
|
});
|
|
5062
5062
|
}
|
|
5063
|
-
function getElementAtPath(obj,
|
|
5064
|
-
if (!
|
|
5063
|
+
function getElementAtPath(obj, path16) {
|
|
5064
|
+
if (!path16)
|
|
5065
5065
|
return obj;
|
|
5066
|
-
return
|
|
5066
|
+
return path16.reduce((acc, key) => acc?.[key], obj);
|
|
5067
5067
|
}
|
|
5068
5068
|
function promiseAllObject(promisesObj) {
|
|
5069
5069
|
const keys = Object.keys(promisesObj);
|
|
@@ -5312,11 +5312,11 @@ function aborted(x, startIndex = 0) {
|
|
|
5312
5312
|
}
|
|
5313
5313
|
return false;
|
|
5314
5314
|
}
|
|
5315
|
-
function prefixIssues(
|
|
5315
|
+
function prefixIssues(path16, issues) {
|
|
5316
5316
|
return issues.map((iss) => {
|
|
5317
5317
|
var _a;
|
|
5318
5318
|
(_a = iss).path ?? (_a.path = []);
|
|
5319
|
-
iss.path.unshift(
|
|
5319
|
+
iss.path.unshift(path16);
|
|
5320
5320
|
return iss;
|
|
5321
5321
|
});
|
|
5322
5322
|
}
|
|
@@ -17146,8 +17146,8 @@ var require_utils = __commonJS({
|
|
|
17146
17146
|
}
|
|
17147
17147
|
return ind;
|
|
17148
17148
|
}
|
|
17149
|
-
function removeDotSegments(
|
|
17150
|
-
let input =
|
|
17149
|
+
function removeDotSegments(path16) {
|
|
17150
|
+
let input = path16;
|
|
17151
17151
|
const output = [];
|
|
17152
17152
|
let nextSlash = -1;
|
|
17153
17153
|
let len = 0;
|
|
@@ -17399,8 +17399,8 @@ var require_schemes = __commonJS({
|
|
|
17399
17399
|
wsComponent.secure = void 0;
|
|
17400
17400
|
}
|
|
17401
17401
|
if (wsComponent.resourceName) {
|
|
17402
|
-
const [
|
|
17403
|
-
wsComponent.path =
|
|
17402
|
+
const [path16, query] = wsComponent.resourceName.split("?");
|
|
17403
|
+
wsComponent.path = path16 && path16 !== "/" ? path16 : void 0;
|
|
17404
17404
|
wsComponent.query = query;
|
|
17405
17405
|
wsComponent.resourceName = void 0;
|
|
17406
17406
|
}
|
|
@@ -20793,12 +20793,12 @@ var require_dist = __commonJS({
|
|
|
20793
20793
|
throw new Error(`Unknown format "${name}"`);
|
|
20794
20794
|
return f;
|
|
20795
20795
|
};
|
|
20796
|
-
function addFormats(ajv, list,
|
|
20796
|
+
function addFormats(ajv, list, fs12, exportName) {
|
|
20797
20797
|
var _a;
|
|
20798
20798
|
var _b;
|
|
20799
20799
|
(_a = (_b = ajv.opts.code).formats) !== null && _a !== void 0 ? _a : _b.formats = (0, codegen_1._)`require("ajv-formats/dist/formats").${exportName}`;
|
|
20800
20800
|
for (const f of list)
|
|
20801
|
-
ajv.addFormat(f,
|
|
20801
|
+
ajv.addFormat(f, fs12[f]);
|
|
20802
20802
|
}
|
|
20803
20803
|
module.exports = exports = formatsPlugin;
|
|
20804
20804
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
@@ -22554,15 +22554,39 @@ var init_stdio2 = __esm({
|
|
|
22554
22554
|
// ../mcp/src/server.ts
|
|
22555
22555
|
var server_exports = {};
|
|
22556
22556
|
__export(server_exports, {
|
|
22557
|
+
createFileSink: () => createFileSink,
|
|
22557
22558
|
createServer: () => createServer,
|
|
22558
22559
|
runReduceTool: () => runReduceTool,
|
|
22559
22560
|
startStdioServer: () => startStdioServer
|
|
22560
22561
|
});
|
|
22561
|
-
|
|
22562
|
+
import fs10 from "node:fs";
|
|
22563
|
+
import path14 from "node:path";
|
|
22564
|
+
function createFileSink(metricsPath) {
|
|
22565
|
+
return (event) => {
|
|
22566
|
+
try {
|
|
22567
|
+
const p = path14.resolve(metricsPath);
|
|
22568
|
+
fs10.mkdirSync(path14.dirname(p), { recursive: true });
|
|
22569
|
+
fs10.appendFileSync(p, JSON.stringify(event) + "\n");
|
|
22570
|
+
} catch {
|
|
22571
|
+
}
|
|
22572
|
+
};
|
|
22573
|
+
}
|
|
22574
|
+
function runReduceTool(text, minLength, sink = noopSink) {
|
|
22562
22575
|
const result = reduceAuto(text, minLength);
|
|
22576
|
+
if (result.changed) {
|
|
22577
|
+
sink({
|
|
22578
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
22579
|
+
harness: "mcp",
|
|
22580
|
+
tool: "reduce",
|
|
22581
|
+
reducer: result.reducer,
|
|
22582
|
+
beforeChars: text.length,
|
|
22583
|
+
afterChars: result.output.length
|
|
22584
|
+
});
|
|
22585
|
+
}
|
|
22563
22586
|
return { content: [{ type: "text", text: result.output }] };
|
|
22564
22587
|
}
|
|
22565
|
-
function createServer() {
|
|
22588
|
+
function createServer(options = {}) {
|
|
22589
|
+
const sink = options.metricsPath ? createFileSink(options.metricsPath) : noopSink;
|
|
22566
22590
|
const server = new McpServer({ name: "harnesstrim", version: "0.0.1" });
|
|
22567
22591
|
server.registerTool(
|
|
22568
22592
|
"reduce",
|
|
@@ -22574,16 +22598,17 @@ function createServer() {
|
|
|
22574
22598
|
minLength: external_exports.number().optional().describe("Skip reduction for inputs shorter than this many characters (default 400)")
|
|
22575
22599
|
}
|
|
22576
22600
|
},
|
|
22577
|
-
async ({ text, minLength }) => runReduceTool(text, minLength)
|
|
22601
|
+
async ({ text, minLength }) => runReduceTool(text, minLength, sink)
|
|
22578
22602
|
);
|
|
22579
22603
|
return server;
|
|
22580
22604
|
}
|
|
22581
|
-
async function startStdioServer() {
|
|
22582
|
-
const
|
|
22605
|
+
async function startStdioServer(options = {}) {
|
|
22606
|
+
const metricsPath = options.metricsPath ?? process.env.HARNESSTRIM_TELEMETRY_PATH;
|
|
22607
|
+
const server = createServer({ metricsPath });
|
|
22583
22608
|
const transport = new StdioServerTransport();
|
|
22584
22609
|
await server.connect(transport);
|
|
22585
22610
|
}
|
|
22586
|
-
var REDUCE_DESCRIPTION;
|
|
22611
|
+
var noopSink, REDUCE_DESCRIPTION;
|
|
22587
22612
|
var init_server3 = __esm({
|
|
22588
22613
|
"../mcp/src/server.ts"() {
|
|
22589
22614
|
"use strict";
|
|
@@ -22591,6 +22616,8 @@ var init_server3 = __esm({
|
|
|
22591
22616
|
init_stdio2();
|
|
22592
22617
|
init_zod();
|
|
22593
22618
|
init_src();
|
|
22619
|
+
noopSink = () => {
|
|
22620
|
+
};
|
|
22594
22621
|
REDUCE_DESCRIPTION = "Slim noisy text to its signal: keeps failures, errors, assertions and summaries while dropping passing-test noise and generated-file (lockfile/dist) diffs. Pass test-runner output or a git diff and use the returned text instead of the raw output. Deterministic and idempotent; returns the input unchanged if no reducer matches or it is too short.";
|
|
22595
22622
|
}
|
|
22596
22623
|
});
|
|
@@ -22598,8 +22625,8 @@ var init_server3 = __esm({
|
|
|
22598
22625
|
// src/cli.ts
|
|
22599
22626
|
init_src();
|
|
22600
22627
|
import { parseArgs } from "node:util";
|
|
22601
|
-
import
|
|
22602
|
-
import
|
|
22628
|
+
import fs11 from "node:fs";
|
|
22629
|
+
import path15 from "node:path";
|
|
22603
22630
|
import os2 from "node:os";
|
|
22604
22631
|
|
|
22605
22632
|
// src/doctor.ts
|
|
@@ -23187,6 +23214,19 @@ function extractOutputText2(p) {
|
|
|
23187
23214
|
import path7 from "node:path";
|
|
23188
23215
|
var HOOK_COMMAND = "harnesstrim hook claude";
|
|
23189
23216
|
var HOOK_MATCHER = "Bash";
|
|
23217
|
+
var HARNESSTRIM_MARKER2 = "harnesstrim:begin";
|
|
23218
|
+
var REDUCE_INSTRUCTION_SNIPPET2 = `<!-- ${HARNESSTRIM_MARKER2} -->
|
|
23219
|
+
## Token economy (HarnessTrim)
|
|
23220
|
+
|
|
23221
|
+
When a shell command produces long, noisy output \u2014 test runners, \`git diff\`, build logs,
|
|
23222
|
+
large file dumps \u2014 pipe it through the reducer so only the signal enters context:
|
|
23223
|
+
|
|
23224
|
+
<your command> 2>&1 | harnesstrim reduce --metrics .harnesstrim/metrics.jsonl
|
|
23225
|
+
|
|
23226
|
+
This keeps failures, errors, assertions, and summaries while dropping passing-test noise and
|
|
23227
|
+
generated-file (lockfile/dist) diffs, and records what was saved. Prefer the installed skills
|
|
23228
|
+
for output, review, and scaffolding discipline.
|
|
23229
|
+
<!-- harnesstrim:end -->`;
|
|
23190
23230
|
function hasHarnessTrimHook2(settings) {
|
|
23191
23231
|
const hooks = settings.hooks;
|
|
23192
23232
|
const post = hooks?.PostToolUse;
|
|
@@ -23218,12 +23258,16 @@ function planClaudeInstall(input) {
|
|
|
23218
23258
|
action = hasHarnessTrimHook2(settings) ? "present" : "patch";
|
|
23219
23259
|
}
|
|
23220
23260
|
const nextSettings = action === "present" ? settings : addHook(settings);
|
|
23261
|
+
const instructionsAction = input.claudeMdContent === null ? "create" : input.claudeMdContent.includes(HARNESSTRIM_MARKER2) ? "present" : "append";
|
|
23221
23262
|
return {
|
|
23222
23263
|
skillsDest,
|
|
23223
23264
|
skills,
|
|
23224
23265
|
settingsFile: path7.join(input.projectDir, ".claude", "settings.json"),
|
|
23225
23266
|
settingsAction: action,
|
|
23226
|
-
nextSettings
|
|
23267
|
+
nextSettings,
|
|
23268
|
+
instructionsFile: path7.join(input.projectDir, "CLAUDE.md"),
|
|
23269
|
+
instructionsAction,
|
|
23270
|
+
instructionsSnippet: REDUCE_INSTRUCTION_SNIPPET2
|
|
23227
23271
|
};
|
|
23228
23272
|
}
|
|
23229
23273
|
function addHook(settings) {
|
|
@@ -23248,11 +23292,19 @@ function runInstallClaude(dir, apply) {
|
|
|
23248
23292
|
} catch {
|
|
23249
23293
|
settingsJsonContent = null;
|
|
23250
23294
|
}
|
|
23295
|
+
const claudeMdPath = path8.join(dir, "CLAUDE.md");
|
|
23296
|
+
let claudeMdContent = null;
|
|
23297
|
+
try {
|
|
23298
|
+
claudeMdContent = fs6.readFileSync(claudeMdPath, "utf8");
|
|
23299
|
+
} catch {
|
|
23300
|
+
claudeMdContent = null;
|
|
23301
|
+
}
|
|
23251
23302
|
const plan = planClaudeInstall({
|
|
23252
23303
|
projectDir: dir,
|
|
23253
23304
|
skillsSourceDir,
|
|
23254
23305
|
skillNames,
|
|
23255
23306
|
settingsJsonContent,
|
|
23307
|
+
claudeMdContent,
|
|
23256
23308
|
existingSkillNames: existingSkillNames(skillsDest)
|
|
23257
23309
|
});
|
|
23258
23310
|
const copied = [];
|
|
@@ -23267,6 +23319,11 @@ function runInstallClaude(dir, apply) {
|
|
|
23267
23319
|
fs6.mkdirSync(path8.dirname(plan.settingsFile), { recursive: true });
|
|
23268
23320
|
fs6.writeFileSync(plan.settingsFile, JSON.stringify(plan.nextSettings, null, 2) + "\n");
|
|
23269
23321
|
}
|
|
23322
|
+
if (plan.instructionsAction === "create") {
|
|
23323
|
+
fs6.writeFileSync(plan.instructionsFile, plan.instructionsSnippet + "\n");
|
|
23324
|
+
} else if (plan.instructionsAction === "append") {
|
|
23325
|
+
fs6.appendFileSync(plan.instructionsFile, "\n\n" + plan.instructionsSnippet + "\n");
|
|
23326
|
+
}
|
|
23270
23327
|
applied = true;
|
|
23271
23328
|
}
|
|
23272
23329
|
return { plan, applied, copied };
|
|
@@ -23619,7 +23676,19 @@ function renderClaudeInstall(result, apply) {
|
|
|
23619
23676
|
}
|
|
23620
23677
|
}
|
|
23621
23678
|
lines.push("");
|
|
23622
|
-
|
|
23679
|
+
if (plan.instructionsAction === "present") {
|
|
23680
|
+
lines.push(`${plan.instructionsFile}: reduce-pipe instruction already present (no change).`);
|
|
23681
|
+
} else {
|
|
23682
|
+
lines.push(
|
|
23683
|
+
`${plan.instructionsFile}: reduce-pipe instruction ${apply ? plan.instructionsAction === "create" ? "created" : "appended" : "would be added"}.`
|
|
23684
|
+
);
|
|
23685
|
+
}
|
|
23686
|
+
lines.push("");
|
|
23687
|
+
lines.push("Note: `harnesstrim` must be on PATH (used by both the hook and the reduce pipe).");
|
|
23688
|
+
lines.push(
|
|
23689
|
+
"The CLAUDE.md instruction is the effective path today: current Claude Code versions don't apply"
|
|
23690
|
+
);
|
|
23691
|
+
lines.push("the hook's updatedToolOutput, so piping through `harnesstrim reduce` is what saves tokens.");
|
|
23623
23692
|
if (!apply) lines.push("Dry run \u2014 nothing written. Re-run with `--apply`.");
|
|
23624
23693
|
return lines.join("\n");
|
|
23625
23694
|
}
|
|
@@ -23723,8 +23792,11 @@ Usage:
|
|
|
23723
23792
|
harnesstrim preset list List policy presets
|
|
23724
23793
|
harnesstrim preset show <name> Show a preset in detail
|
|
23725
23794
|
harnesstrim metrics [path] Summarize adapter telemetry (JSONL)
|
|
23726
|
-
harnesstrim reduce [--stats]
|
|
23727
|
-
|
|
23795
|
+
harnesstrim reduce [--stats] [--metrics <path>]
|
|
23796
|
+
Slim stdin -> stdout (pipe noisy command output);
|
|
23797
|
+
--metrics records a TrimEvent per reduction
|
|
23798
|
+
harnesstrim mcp [--metrics <path>] Start the MCP server (stdio) exposing a reduce tool;
|
|
23799
|
+
--metrics records a TrimEvent per reduction
|
|
23728
23800
|
harnesstrim bench Run the Tier A reducer micro-benchmark
|
|
23729
23801
|
harnesstrim --help Show this help
|
|
23730
23802
|
|
|
@@ -23774,7 +23846,7 @@ async function main(argv) {
|
|
|
23774
23846
|
console.error("`harnesstrim install codex --global` requires `--hook`.");
|
|
23775
23847
|
return 1;
|
|
23776
23848
|
}
|
|
23777
|
-
console.log(renderCodexGlobalHookInstall(runInstallCodexGlobalHook(
|
|
23849
|
+
console.log(renderCodexGlobalHookInstall(runInstallCodexGlobalHook(path15.join(os2.homedir(), ".codex"), apply), apply));
|
|
23778
23850
|
return 0;
|
|
23779
23851
|
}
|
|
23780
23852
|
console.log(renderCodexInstall(runInstallCodex(dir, apply, values.hook === true), apply));
|
|
@@ -23806,9 +23878,9 @@ async function main(argv) {
|
|
|
23806
23878
|
process.stdout.write(response);
|
|
23807
23879
|
if (values.metrics && event) {
|
|
23808
23880
|
try {
|
|
23809
|
-
const p =
|
|
23810
|
-
|
|
23811
|
-
|
|
23881
|
+
const p = path15.resolve(values.metrics);
|
|
23882
|
+
fs11.mkdirSync(path15.dirname(p), { recursive: true });
|
|
23883
|
+
fs11.appendFileSync(
|
|
23812
23884
|
p,
|
|
23813
23885
|
JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), harness: which, ...event }) + "\n"
|
|
23814
23886
|
);
|
|
@@ -23817,7 +23889,7 @@ async function main(argv) {
|
|
|
23817
23889
|
}
|
|
23818
23890
|
if (values.log) {
|
|
23819
23891
|
try {
|
|
23820
|
-
|
|
23892
|
+
fs11.appendFileSync(
|
|
23821
23893
|
values.log,
|
|
23822
23894
|
JSON.stringify({ inputChars: input.length, changed: response !== "{}", responseChars: response.length }) + "\n"
|
|
23823
23895
|
);
|
|
@@ -23846,8 +23918,8 @@ async function main(argv) {
|
|
|
23846
23918
|
return 1;
|
|
23847
23919
|
}
|
|
23848
23920
|
case "metrics": {
|
|
23849
|
-
const
|
|
23850
|
-
console.log(renderMetrics(loadMetrics(
|
|
23921
|
+
const path16 = rest[0] ?? DEFAULT_METRICS_PATH;
|
|
23922
|
+
console.log(renderMetrics(loadMetrics(path16)));
|
|
23851
23923
|
return 0;
|
|
23852
23924
|
}
|
|
23853
23925
|
case "reduce": {
|
|
@@ -23860,6 +23932,24 @@ async function main(argv) {
|
|
|
23860
23932
|
const input = await readStdin();
|
|
23861
23933
|
const result = reducePipe(input, minLength);
|
|
23862
23934
|
process.stdout.write(result.output);
|
|
23935
|
+
if (values.metrics && result.changed) {
|
|
23936
|
+
try {
|
|
23937
|
+
const p = path15.resolve(values.metrics);
|
|
23938
|
+
fs11.mkdirSync(path15.dirname(p), { recursive: true });
|
|
23939
|
+
fs11.appendFileSync(
|
|
23940
|
+
p,
|
|
23941
|
+
JSON.stringify({
|
|
23942
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
23943
|
+
harness: "pipe",
|
|
23944
|
+
tool: "reduce",
|
|
23945
|
+
reducer: result.reducer,
|
|
23946
|
+
beforeChars: result.beforeChars,
|
|
23947
|
+
afterChars: result.afterChars
|
|
23948
|
+
}) + "\n"
|
|
23949
|
+
);
|
|
23950
|
+
} catch {
|
|
23951
|
+
}
|
|
23952
|
+
}
|
|
23863
23953
|
if (values.stats) {
|
|
23864
23954
|
const note = result.changed ? `${result.reducer}: ${result.beforeChars} -> ${result.afterChars} chars` : "no reduction (no reducer matched or below min-length)";
|
|
23865
23955
|
console.error(`[harnesstrim reduce] ${note}`);
|
|
@@ -23868,7 +23958,7 @@ async function main(argv) {
|
|
|
23868
23958
|
}
|
|
23869
23959
|
case "mcp": {
|
|
23870
23960
|
const { startStdioServer: startStdioServer2 } = await Promise.resolve().then(() => (init_server3(), server_exports));
|
|
23871
|
-
await startStdioServer2();
|
|
23961
|
+
await startStdioServer2(values.metrics ? { metricsPath: values.metrics } : {});
|
|
23872
23962
|
await new Promise(() => {
|
|
23873
23963
|
});
|
|
23874
23964
|
return 0;
|