harnesstrim 0.0.1 → 0.0.2

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.
Files changed (2) hide show
  1. package/package.json +4 -6
  2. package/src/cli.ts +0 -238
package/package.json CHANGED
@@ -1,21 +1,18 @@
1
1
  {
2
2
  "name": "harnesstrim",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "description": "HarnessTrim CLI: doctor (diagnose token waste), install adapters, run benchmarks.",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "bin": {
8
- "harnesstrim": "./src/cli.ts"
8
+ "harnesstrim": "./dist/cli.mjs"
9
9
  },
10
10
  "files": [
11
11
  "dist",
12
12
  "assets"
13
13
  ],
14
14
  "publishConfig": {
15
- "access": "public",
16
- "bin": {
17
- "harnesstrim": "./dist/cli.mjs"
18
- }
15
+ "access": "public"
19
16
  },
20
17
  "devDependencies": {
21
18
  "@harnesstrim/adapter-claude": "workspace:*",
@@ -29,6 +26,7 @@
29
26
  },
30
27
  "scripts": {
31
28
  "build": "node build.mjs",
29
+ "prepare": "node build.mjs",
32
30
  "prepack": "node build.mjs",
33
31
  "test": "node --test \"src/**/*.test.ts\"",
34
32
  "typecheck": "tsc -p tsconfig.json"
package/src/cli.ts DELETED
@@ -1,238 +0,0 @@
1
- #!/usr/bin/env node
2
- import { parseArgs } from "node:util";
3
- import fs from "node:fs";
4
- import path from "node:path";
5
- import os from "node:os";
6
- import { getPreset, listPresets } from "@harnesstrim/core";
7
- import { inspect } from "./doctor.ts";
8
- import { runInstallOpencode } from "./install.ts";
9
- import { runInstallCodex } from "./install-codex.ts";
10
- import { runInstallClaude } from "./install-claude.ts";
11
- import { runInstallPi } from "./install-pi.ts";
12
- import { runInstallHermes } from "./install-hermes.ts";
13
- import { reduceClaudePayload } from "@harnesstrim/adapter-claude";
14
- import { loadMetrics, DEFAULT_METRICS_PATH } from "./metrics.ts";
15
- import { readStdin, reducePipe } from "./reduce.ts";
16
- import {
17
- renderDoctor,
18
- renderInstall,
19
- renderCodexInstall,
20
- renderClaudeInstall,
21
- renderHermesInstall,
22
- renderPiInstall,
23
- renderMetrics,
24
- renderPresetList,
25
- renderPresetShow,
26
- } from "./render.ts";
27
-
28
- const HELP = `harnesstrim — one token policy for coding harnesses
29
-
30
- Usage:
31
- harnesstrim doctor [dir] Diagnose token-waste signals in a project
32
- harnesstrim install opencode [dir] Wire the adapter into opencode.json (dry-run)
33
- --apply Actually write the change
34
- --preset <name> Bake a policy preset's adapter config in
35
- harnesstrim install codex [dir] Install skills + AGENTS.md reduce-pipe (dry-run)
36
- --apply Actually write the change
37
- harnesstrim install claude [dir] Install skills + PostToolUse reducer hook (dry-run)
38
- --apply Actually write the change
39
- harnesstrim install hermes [dir] Install Hermes plugin (dry-run)
40
- --apply Actually write the change
41
- harnesstrim install pi [dir] Install Pi tool_result extension (dry-run)
42
- --apply Actually write the change
43
- harnesstrim hook claude [--metrics <path>]
44
- PostToolUse hook runtime; --metrics records a TrimEvent per reduction
45
- harnesstrim preset list List policy presets
46
- harnesstrim preset show <name> Show a preset in detail
47
- harnesstrim metrics [path] Summarize adapter telemetry (JSONL)
48
- harnesstrim reduce [--stats] Slim stdin -> stdout (pipe noisy command output)
49
- harnesstrim mcp Start the MCP server (stdio) exposing a reduce tool
50
- harnesstrim bench Run the Tier A reducer micro-benchmark
51
- harnesstrim --help Show this help
52
-
53
- Notes:
54
- - install is dry-run by default; nothing is written without --apply.
55
- - dir defaults to the current directory; metrics path defaults to ${DEFAULT_METRICS_PATH}.
56
- - reduce reads stdin and writes slimmed output to stdout, e.g. npm test 2>&1 | harnesstrim reduce`;
57
-
58
- async function main(argv: string[]): Promise<number> {
59
- const { values, positionals } = parseArgs({
60
- args: argv,
61
- allowPositionals: true,
62
- options: {
63
- help: { type: "boolean", short: "h" },
64
- apply: { type: "boolean" },
65
- preset: { type: "string" },
66
- stats: { type: "boolean" },
67
- "min-length": { type: "string" },
68
- log: { type: "string" },
69
- metrics: { type: "string" },
70
- },
71
- });
72
-
73
- const [command, ...rest] = positionals;
74
-
75
- if (values.help || !command) {
76
- console.log(HELP);
77
- return 0;
78
- }
79
-
80
- switch (command) {
81
- case "doctor": {
82
- const dir = rest[0] ?? process.cwd();
83
- console.log(renderDoctor(inspect(dir)));
84
- return 0;
85
- }
86
- case "install": {
87
- const target = rest[0];
88
- // Hermes uses the user-level Hermes home by default; other adapters keep
89
- // their project-directory default. Pass an explicit directory for a
90
- // project-local or alternate-profile installation.
91
- const dir = rest[1] ?? (target === "hermes" ? os.homedir() : process.cwd());
92
- const apply = values.apply === true;
93
- if (target === "opencode") {
94
- const result = runInstallOpencode(dir, apply, values.preset);
95
- console.log(renderInstall(result, apply));
96
- return 0;
97
- }
98
- if (target === "codex") {
99
- console.log(renderCodexInstall(runInstallCodex(dir, apply), apply));
100
- return 0;
101
- }
102
- if (target === "claude") {
103
- console.log(renderClaudeInstall(runInstallClaude(dir, apply), apply));
104
- return 0;
105
- }
106
- if (target === "hermes") {
107
- console.log(renderHermesInstall(runInstallHermes(dir, apply), apply));
108
- return 0;
109
- }
110
- if (target === "pi") {
111
- console.log(renderPiInstall(runInstallPi(dir, apply), apply));
112
- return 0;
113
- }
114
- console.error(`Unknown install target: ${target ?? "(none)"}. Supported: opencode, codex, claude, hermes, pi.`);
115
- return 1;
116
- }
117
- case "hook": {
118
- const which = rest[0];
119
- if (which !== "claude") {
120
- console.error(`Unknown hook target: ${which ?? "(none)"}. Supported: claude.`);
121
- return 1;
122
- }
123
- const input = await readStdin();
124
- const { response, event } = reduceClaudePayload(input);
125
- process.stdout.write(response);
126
- // --metrics <path>: append a TrimEvent per reduction, read by `harnesstrim metrics`.
127
- if (values.metrics && event) {
128
- try {
129
- const p = path.resolve(values.metrics);
130
- fs.mkdirSync(path.dirname(p), { recursive: true });
131
- fs.appendFileSync(
132
- p,
133
- JSON.stringify({ ts: new Date().toISOString(), harness: "claude", ...event }) + "\n"
134
- );
135
- } catch {
136
- /* telemetry must never break the hook */
137
- }
138
- }
139
- // --log <path>: lightweight debug line (input size, whether it changed).
140
- if (values.log) {
141
- try {
142
- fs.appendFileSync(
143
- values.log,
144
- JSON.stringify({ inputChars: input.length, changed: response !== "{}", responseChars: response.length }) + "\n"
145
- );
146
- } catch {
147
- /* logging must never break the hook */
148
- }
149
- }
150
- return 0;
151
- }
152
- case "preset": {
153
- const sub = rest[0];
154
- if (sub === "list" || sub === undefined) {
155
- console.log(renderPresetList(listPresets()));
156
- return 0;
157
- }
158
- if (sub === "show") {
159
- const name = rest[1];
160
- const preset = name ? getPreset(name) : undefined;
161
- if (!preset) {
162
- console.error(`Unknown preset: ${name ?? "(none)"}. Try \`harnesstrim preset list\`.`);
163
- return 1;
164
- }
165
- console.log(renderPresetShow(preset));
166
- return 0;
167
- }
168
- console.error(`Unknown preset subcommand: ${sub}. Use \`list\` or \`show <name>\`.`);
169
- return 1;
170
- }
171
- case "metrics": {
172
- const path = rest[0] ?? DEFAULT_METRICS_PATH;
173
- console.log(renderMetrics(loadMetrics(path)));
174
- return 0;
175
- }
176
- case "reduce": {
177
- const minLenRaw = values["min-length"];
178
- const minLength = minLenRaw !== undefined ? Number(minLenRaw) : undefined;
179
- if (minLength !== undefined && !Number.isFinite(minLength)) {
180
- console.error(`Invalid --min-length: ${minLenRaw}`);
181
- return 1;
182
- }
183
- const input = await readStdin();
184
- const result = reducePipe(input, minLength);
185
- process.stdout.write(result.output);
186
- if (values.stats) {
187
- const note = result.changed
188
- ? `${result.reducer}: ${result.beforeChars} -> ${result.afterChars} chars`
189
- : "no reduction (no reducer matched or below min-length)";
190
- console.error(`[harnesstrim reduce] ${note}`);
191
- }
192
- return 0;
193
- }
194
- case "mcp": {
195
- const { startStdioServer } = await import("@harnesstrim/mcp");
196
- await startStdioServer();
197
- // startStdioServer resolves once connected; keep the process alive for stdio.
198
- await new Promise<never>(() => {});
199
- return 0;
200
- }
201
- case "bench": {
202
- // The Tier A micro-benchmark runs against the repo's fixtures and writes a
203
- // report back into the repo — it's a monorepo-development tool, kept out of the
204
- // published bundle (see build.mjs). In a standalone install the import fails
205
- // (ERR_MODULE_NOT_FOUND); from a stray location the fixtures are missing
206
- // (ENOENT). Either way, degrade with a clear message instead of a raw stack.
207
- try {
208
- const { runBench } = await import("@harnesstrim/benchmarks/run");
209
- runBench();
210
- } catch (err) {
211
- const code = (err as NodeJS.ErrnoException)?.code;
212
- if (code === "ENOENT" || code === "ERR_MODULE_NOT_FOUND") {
213
- console.error(
214
- "harnesstrim bench is a repository-development command (it reads the benchmark\n" +
215
- "fixtures and writes a report). Run it from a HarnessTrim checkout:\n" +
216
- " git clone https://github.com/giuliastro/HarnessTrim && pnpm install && pnpm exec harnesstrim bench"
217
- );
218
- return 1;
219
- }
220
- throw err;
221
- }
222
- return 0;
223
- }
224
- default:
225
- console.error(`Unknown command: ${command}\n`);
226
- console.log(HELP);
227
- return 1;
228
- }
229
- }
230
-
231
- main(process.argv.slice(2))
232
- .then((code) => {
233
- process.exitCode = code;
234
- })
235
- .catch((err) => {
236
- console.error(`harnesstrim: ${err instanceof Error ? err.message : String(err)}`);
237
- process.exitCode = 1;
238
- });