harnesstrim 0.0.3 → 0.0.4

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/dist/cli.mjs +185 -80
  2. package/package.json +1 -1
package/dist/cli.mjs CHANGED
@@ -22681,40 +22681,45 @@ function checkSkills(dir, findings) {
22681
22681
  }
22682
22682
  }
22683
22683
  function checkOpenCode(dir, findings) {
22684
- const raw = readIfExists(path.join(dir, "opencode.json"));
22685
- if (raw === null) {
22684
+ const hasOpencodeProject = readIfExists(path.join(dir, "opencode.json")) !== null || fs.existsSync(path.join(dir, ".opencode"));
22685
+ if (!hasOpencodeProject) {
22686
22686
  findings.push({
22687
22687
  severity: "info",
22688
- title: "No opencode.json found",
22689
- detail: "No OpenCode project config in this directory."
22688
+ title: "No OpenCode project config found",
22689
+ detail: "No opencode.json or .opencode/ in this directory."
22690
22690
  });
22691
22691
  return;
22692
22692
  }
22693
- let config2;
22694
- try {
22695
- config2 = JSON.parse(raw);
22696
- } catch {
22697
- findings.push({
22698
- severity: "warn",
22699
- title: "opencode.json is not valid JSON",
22700
- detail: "Could not parse opencode.json to check plugin wiring."
22701
- });
22702
- return;
22693
+ const wrapper = path.join(dir, ".opencode", "plugin", "harnesstrim.ts");
22694
+ const wrapperPresent = fs.existsSync(wrapper);
22695
+ const raw = readIfExists(path.join(dir, "opencode.json"));
22696
+ let staleEntry = false;
22697
+ if (raw !== null) {
22698
+ try {
22699
+ staleEntry = extractPluginNames(JSON.parse(raw)).some((p) => p.includes("@harnesstrim/adapter-opencode"));
22700
+ } catch {
22701
+ }
22703
22702
  }
22704
- const plugins = extractPluginNames(config2);
22705
- const installed = plugins.some((p) => p.includes("@harnesstrim/adapter-opencode"));
22706
- if (installed) {
22703
+ if (wrapperPresent) {
22707
22704
  findings.push({
22708
22705
  severity: "ok",
22709
- title: "HarnessTrim OpenCode adapter is wired in",
22710
- detail: "@harnesstrim/adapter-opencode is present in opencode.json plugins."
22706
+ title: "HarnessTrim OpenCode adapter is installed",
22707
+ detail: ".opencode/plugin/harnesstrim.ts loads the adapter (OpenCode auto-loads it)."
22711
22708
  });
22712
22709
  } else {
22713
22710
  findings.push({
22714
22711
  severity: "warn",
22715
- title: "HarnessTrim adapter not installed in opencode.json",
22712
+ title: "HarnessTrim adapter not installed for OpenCode",
22716
22713
  detail: "The OpenCode adapter would slim tool output automatically.",
22717
- suggestion: "Run `harnesstrim install opencode` to wire it in."
22714
+ suggestion: "Run `harnesstrim install opencode --apply` to install the local plugin."
22715
+ });
22716
+ }
22717
+ if (staleEntry && !wrapperPresent) {
22718
+ findings.push({
22719
+ severity: "warn",
22720
+ title: "Stale adapter entry in opencode.json",
22721
+ detail: "opencode.json lists @harnesstrim/adapter-opencode, but OpenCode's plugin field is a string array and ignores option tuples \u2014 this entry never loaded.",
22722
+ suggestion: "Run `harnesstrim install opencode --apply` to migrate to the local plugin wrapper."
22718
22723
  });
22719
22724
  }
22720
22725
  }
@@ -22741,70 +22746,151 @@ function inspect(dir) {
22741
22746
  init_src();
22742
22747
  import fs2 from "node:fs";
22743
22748
  import path2 from "node:path";
22749
+ import { spawnSync } from "node:child_process";
22744
22750
  var OPENCODE_PLUGIN_NAME = "@harnesstrim/adapter-opencode";
22745
- function pluginIndex(plugin) {
22746
- return plugin.findIndex(
22747
- (e) => typeof e === "string" && e.includes(OPENCODE_PLUGIN_NAME) || Array.isArray(e) && typeof e[0] === "string" && e[0].includes(OPENCODE_PLUGIN_NAME)
22748
- );
22751
+ var OPENCODE_ADAPTER_VERSION = "^0.0.2";
22752
+ var DEFAULT_OPENCODE_ADAPTER_CONFIG = {
22753
+ mode: "active",
22754
+ telemetry: true,
22755
+ telemetryPath: ".harnesstrim/metrics.jsonl"
22756
+ };
22757
+ var WRAPPER_REL = path2.join(".opencode", "plugin", "harnesstrim.ts");
22758
+ var PKG_REL = path2.join(".opencode", "package.json");
22759
+ var OPENCODE_JSON = "opencode.json";
22760
+ function buildOpencodeWrapper(adapterConfig) {
22761
+ const opts = JSON.stringify(adapterConfig, null, 2).replace(/\n/g, "\n ");
22762
+ return `// Generated by \`harnesstrim install opencode\`. Do not reference this plugin from
22763
+ // opencode.json \u2014 OpenCode's \`plugin\` field is a string array and cannot pass options,
22764
+ // so HarnessTrim options live here and OpenCode auto-loads this file from .opencode/plugin/.
22765
+ // The dependency is declared in ../package.json. Edit the options below freely.
22766
+ import { HarnessTrim } from "${OPENCODE_PLUGIN_NAME}";
22767
+
22768
+ export const HarnessTrimPlugin = async (input) =>
22769
+ HarnessTrim(input, ${opts});
22770
+ `;
22749
22771
  }
22750
- function legacyPluginIndex(plugin) {
22751
- return plugin.findIndex(
22752
- (entry) => typeof entry === "object" && entry !== null && typeof entry.name === "string" && entry.name.includes(OPENCODE_PLUGIN_NAME)
22753
- );
22772
+ function buildOpencodePackageJson(existing) {
22773
+ let base = {};
22774
+ if (existing) {
22775
+ try {
22776
+ const parsed = JSON.parse(existing);
22777
+ if (parsed && typeof parsed === "object") base = parsed;
22778
+ } catch {
22779
+ }
22780
+ }
22781
+ const deps = { ...base.dependencies };
22782
+ deps[OPENCODE_PLUGIN_NAME] = OPENCODE_ADAPTER_VERSION;
22783
+ return JSON.stringify({ ...base, dependencies: deps }, null, 2) + "\n";
22784
+ }
22785
+ function opencodeJsonReferencesAdapter(config2) {
22786
+ const plugin = config2.plugin;
22787
+ if (!Array.isArray(plugin)) return false;
22788
+ return plugin.some((e) => {
22789
+ if (typeof e === "string") return e.includes(OPENCODE_PLUGIN_NAME);
22790
+ if (Array.isArray(e)) return typeof e[0] === "string" && e[0].includes(OPENCODE_PLUGIN_NAME);
22791
+ if (e && typeof e === "object") {
22792
+ const name = e.name;
22793
+ return typeof name === "string" && name.includes(OPENCODE_PLUGIN_NAME);
22794
+ }
22795
+ return false;
22796
+ });
22754
22797
  }
22755
- function planOpencodeInstall(config2, adapterConfig) {
22756
- const base = typeof config2 === "object" && config2 !== null ? { ...config2 } : {};
22757
- const plugin = Array.isArray(base.plugin) ? [...base.plugin] : [];
22758
- const desired = adapterConfig ? [OPENCODE_PLUGIN_NAME, adapterConfig] : OPENCODE_PLUGIN_NAME;
22759
- const idx = pluginIndex(plugin);
22760
- const legacyIdx = idx === -1 ? legacyPluginIndex(plugin) : -1;
22761
- if (legacyIdx !== -1) {
22762
- const legacy = plugin[legacyIdx];
22763
- const options = legacy.options;
22764
- plugin[legacyIdx] = adapterConfig ? desired : typeof options === "object" && options !== null && !Array.isArray(options) ? [OPENCODE_PLUGIN_NAME, options] : OPENCODE_PLUGIN_NAME;
22765
- return { nextConfig: { ...base, plugin }, alreadyInstalled: true, changed: true };
22766
- }
22767
- if (idx === -1) {
22768
- plugin.push(desired);
22769
- return { nextConfig: { ...base, plugin }, alreadyInstalled: false, changed: true };
22770
- }
22771
- if (adapterConfig && JSON.stringify(plugin[idx]) !== JSON.stringify(desired)) {
22772
- plugin[idx] = desired;
22773
- return { nextConfig: { ...base, plugin }, alreadyInstalled: true, changed: true };
22774
- }
22775
- return { nextConfig: { ...base, plugin }, alreadyInstalled: true, changed: false };
22776
- }
22777
- function runInstallOpencode(dir, apply, presetName) {
22798
+ function cleanOpencodeJson(existing) {
22799
+ let config2;
22800
+ try {
22801
+ config2 = JSON.parse(existing);
22802
+ } catch {
22803
+ throw new Error(`${OPENCODE_JSON} exists but is not valid JSON`);
22804
+ }
22805
+ if (!opencodeJsonReferencesAdapter(config2)) return null;
22806
+ const plugin = config2.plugin.filter((e) => {
22807
+ if (typeof e === "string") return !e.includes(OPENCODE_PLUGIN_NAME);
22808
+ if (Array.isArray(e)) return !(typeof e[0] === "string" && e[0].includes(OPENCODE_PLUGIN_NAME));
22809
+ if (e && typeof e === "object") {
22810
+ const name = e.name;
22811
+ return !(typeof name === "string" && name.includes(OPENCODE_PLUGIN_NAME));
22812
+ }
22813
+ return true;
22814
+ });
22815
+ const next = { ...config2 };
22816
+ if (plugin.length === 0) delete next.plugin;
22817
+ else next.plugin = plugin;
22818
+ return { content: JSON.stringify(next, null, 2) + "\n" };
22819
+ }
22820
+ function planOpencodeInstall(input) {
22821
+ const adapterConfig = input.adapterConfig ?? DEFAULT_OPENCODE_ADAPTER_CONFIG;
22822
+ const wrapperContent = buildOpencodeWrapper(adapterConfig);
22823
+ const packageJsonContent = buildOpencodePackageJson(input.existingPackageJson);
22824
+ const opencodeClean = input.existingOpencodeJson ? cleanOpencodeJson(input.existingOpencodeJson) : null;
22825
+ const opencodeJsonContent = opencodeClean?.content ?? null;
22826
+ const wrapperChanged = input.existingWrapper !== wrapperContent;
22827
+ const pkgChanged = input.existingPackageJson !== packageJsonContent;
22828
+ const opencodeChanged = opencodeJsonContent !== null;
22829
+ const alreadyInstalled = input.existingWrapper !== null;
22830
+ const changed = wrapperChanged || pkgChanged || opencodeChanged;
22831
+ return { wrapperContent, packageJsonContent, opencodeJsonContent, alreadyInstalled, changed };
22832
+ }
22833
+ function runInstallOpencode(dir, apply, presetName, installDeps = true) {
22778
22834
  let preset;
22779
- let adapterConfig;
22835
+ let adapterConfig = { ...DEFAULT_OPENCODE_ADAPTER_CONFIG };
22780
22836
  if (presetName) {
22781
22837
  preset = getPreset(presetName);
22782
22838
  if (!preset) throw new Error(`Unknown preset: ${presetName}`);
22783
- adapterConfig = { ...preset.adapter };
22839
+ adapterConfig = { ...adapterConfig, ...preset.adapter };
22784
22840
  }
22785
- const configPath = path2.join(dir, "opencode.json");
22786
- let raw = null;
22787
- try {
22788
- raw = fs2.readFileSync(configPath, "utf8");
22789
- } catch {
22790
- raw = null;
22791
- }
22792
- const existed = raw !== null;
22793
- let config2 = {};
22794
- if (raw !== null) {
22841
+ const wrapperPath = path2.join(dir, WRAPPER_REL);
22842
+ const packageJsonPath = path2.join(dir, PKG_REL);
22843
+ const opencodeJsonPath = path2.join(dir, OPENCODE_JSON);
22844
+ const readOrNull = (p) => {
22795
22845
  try {
22796
- config2 = JSON.parse(raw);
22846
+ return fs2.readFileSync(p, "utf8");
22797
22847
  } catch {
22798
- throw new Error(`${configPath} exists but is not valid JSON`);
22848
+ return null;
22799
22849
  }
22800
- }
22801
- const plan = planOpencodeInstall(config2, adapterConfig);
22850
+ };
22851
+ const existingWrapper = readOrNull(wrapperPath);
22852
+ const existingPackageJson = readOrNull(packageJsonPath);
22853
+ const existingOpencodeJson = readOrNull(opencodeJsonPath);
22854
+ const plan = planOpencodeInstall({
22855
+ existingWrapper,
22856
+ existingPackageJson,
22857
+ existingOpencodeJson,
22858
+ adapterConfig
22859
+ });
22802
22860
  let applied = false;
22861
+ let depsInstalled = null;
22862
+ let depsMessage;
22803
22863
  if (apply && plan.changed) {
22804
- fs2.writeFileSync(configPath, JSON.stringify(plan.nextConfig, null, 2) + "\n");
22864
+ fs2.mkdirSync(path2.dirname(wrapperPath), { recursive: true });
22865
+ fs2.writeFileSync(wrapperPath, plan.wrapperContent);
22866
+ fs2.writeFileSync(packageJsonPath, plan.packageJsonContent);
22867
+ if (plan.opencodeJsonContent !== null) fs2.writeFileSync(opencodeJsonPath, plan.opencodeJsonContent);
22805
22868
  applied = true;
22869
+ if (installDeps) {
22870
+ const npm = process.platform === "win32" ? "npm.cmd" : "npm";
22871
+ const res = spawnSync(npm, ["install", "--silent"], {
22872
+ cwd: path2.dirname(packageJsonPath),
22873
+ encoding: "utf8"
22874
+ });
22875
+ if (res.error || res.status !== 0) {
22876
+ depsInstalled = false;
22877
+ depsMessage = `Could not auto-install the adapter; run \`npm install\` in ${path2.dirname(packageJsonPath)}.`;
22878
+ } else {
22879
+ depsInstalled = true;
22880
+ }
22881
+ }
22806
22882
  }
22807
- return { configPath, existed, applied, preset, ...plan };
22883
+ return {
22884
+ ...plan,
22885
+ wrapperPath,
22886
+ packageJsonPath,
22887
+ opencodeJsonPath,
22888
+ existed: existingWrapper !== null,
22889
+ applied,
22890
+ depsInstalled,
22891
+ depsMessage,
22892
+ preset
22893
+ };
22808
22894
  }
22809
22895
 
22810
22896
  // src/install-codex.ts
@@ -23256,7 +23342,7 @@ function runInstallPi(installDir, apply) {
23256
23342
  // src/install-hermes.ts
23257
23343
  import fs8 from "node:fs";
23258
23344
  import path12 from "node:path";
23259
- import { spawnSync } from "node:child_process";
23345
+ import { spawnSync as spawnSync2 } from "node:child_process";
23260
23346
 
23261
23347
  // ../adapter-hermes/src/index.ts
23262
23348
  import path11 from "node:path";
@@ -23323,7 +23409,7 @@ function runInstallHermes(installDir, apply) {
23323
23409
  fs8.writeFileSync(path12.join(pluginDest, ".installed"), markerFileContent2());
23324
23410
  copiedFiles.push(".installed");
23325
23411
  applied = true;
23326
- const enable = spawnSync("hermes", ["plugins", "enable", HERMES_PLUGIN_NAME], {
23412
+ const enable = spawnSync2("hermes", ["plugins", "enable", HERMES_PLUGIN_NAME], {
23327
23413
  encoding: "utf8"
23328
23414
  });
23329
23415
  if (enable.error) {
@@ -23389,19 +23475,38 @@ function renderDoctor(report) {
23389
23475
  }
23390
23476
  function renderInstall(result, apply) {
23391
23477
  const lines = [];
23392
- if (result.alreadyInstalled) {
23393
- lines.push(`Already installed: ${result.configPath} already wires in the HarnessTrim adapter.`);
23478
+ const wrapper = result.wrapperPath;
23479
+ const pkg = result.packageJsonPath;
23480
+ if (!result.changed) {
23481
+ lines.push(`Already installed and up to date: ${wrapper} loads the HarnessTrim adapter.`);
23482
+ if (result.preset) {
23483
+ lines.push("");
23484
+ lines.push(renderPresetAdvisory(result.preset));
23485
+ }
23394
23486
  return lines.join("\n");
23395
23487
  }
23396
23488
  if (apply) {
23397
- lines.push(`Wrote ${result.configPath}:`);
23398
- lines.push(JSON.stringify(result.nextConfig, null, 2));
23399
- } else {
23400
- lines.push(`Dry run \u2014 no files changed. This is what \`--apply\` would write to ${result.configPath}:`);
23489
+ lines.push(`Installed the OpenCode adapter as a local plugin:`);
23490
+ lines.push(` \u2022 wrote ${wrapper} (auto-loaded by OpenCode from .opencode/plugin/)`);
23491
+ lines.push(` \u2022 wrote ${pkg} (declares @harnesstrim/adapter-opencode)`);
23492
+ if (result.opencodeJsonContent !== null) {
23493
+ lines.push(` \u2022 cleaned the adapter entry out of ${result.opencodeJsonPath} (options live in the wrapper now)`);
23494
+ }
23495
+ if (result.depsInstalled === true) lines.push(` \u2022 installed the .opencode dependency`);
23496
+ else if (result.depsInstalled === false) lines.push(` ! ${result.depsMessage}`);
23401
23497
  lines.push("");
23402
- lines.push(JSON.stringify(result.nextConfig, null, 2));
23498
+ lines.push("Reduction is active; telemetry writes to .harnesstrim/metrics.jsonl.");
23499
+ lines.push("Reload OpenCode so it loads the plugin, then check `harnesstrim metrics`.");
23500
+ } else {
23501
+ lines.push(`Dry run \u2014 no files changed. \`--apply\` would:`);
23502
+ lines.push(` \u2022 write ${wrapper} (local plugin wrapper with the adapter options)`);
23503
+ lines.push(` \u2022 write ${pkg} (declare @harnesstrim/adapter-opencode) and install it`);
23504
+ if (result.opencodeJsonContent !== null) {
23505
+ lines.push(` \u2022 remove the stale adapter entry from ${result.opencodeJsonPath}`);
23506
+ }
23403
23507
  lines.push("");
23404
- lines.push("Re-run with `--apply` to write it.");
23508
+ lines.push("OpenCode's `plugin` config can't pass options, so the adapter is installed as a");
23509
+ lines.push("local plugin file instead. Re-run with `--apply` to write it.");
23405
23510
  }
23406
23511
  if (result.preset) {
23407
23512
  lines.push("");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "harnesstrim",
3
- "version": "0.0.3",
3
+ "version": "0.0.4",
4
4
  "description": "HarnessTrim CLI: doctor (diagnose token waste), install adapters, run benchmarks.",
5
5
  "license": "MIT",
6
6
  "type": "module",