@rulemetric/cli 0.6.8 → 0.6.9

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.
@@ -55,8 +55,11 @@ var HooksRun = class _HooksRun extends BaseCommand {
55
55
  }
56
56
  } catch (error) {
57
57
  const code = error.status;
58
- if (code) {
59
- process.exitCode = code;
58
+ if (process.env.RULEMETRIC_HOOK_DEBUG) {
59
+ process.stderr.write(
60
+ `[rulemetric hooks run ${hookName}] non-fatal exit ${code ?? "?"}: ${error.message ?? ""}
61
+ `
62
+ );
60
63
  }
61
64
  }
62
65
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/commands/hooks/run.ts"],
4
- "sourcesContent": ["import { Args } from '@oclif/core';\nimport { execFileSync } from 'node:child_process';\nimport { existsSync } from 'node:fs';\nimport { join, delimiter } from 'node:path';\nimport { BaseCommand } from '../../base-command.js';\n\nconst VALID_HOOKS = ['session-start', 'session-end', 'user-prompt', 'post-tool-use', 'assistant-response'] as const;\n\nexport default class HooksRun extends BaseCommand {\n static override description = 'Execute a hook script (called by AI tool hooks, not for direct use)';\n static override hidden = true;\n\n static override args = {\n hook: Args.string({\n description: 'Hook name to run',\n required: true,\n options: [...VALID_HOOKS],\n }),\n };\n\n async run(): Promise<void> {\n const { args } = await this.parse(HooksRun);\n const hookName = args.hook;\n\n // Resolve bundled script path\n const { scriptsDir } = await import('@rulemetric/hooks/scripts-dir');\n const scriptPath = join(scriptsDir, `${hookName}.sh`);\n\n if (!existsSync(scriptPath)) {\n this.error(`Hook script not found: ${scriptPath}`);\n }\n\n try {\n // Read stdin and pass it to the bash script\n const stdin = await this.readStdin();\n // APPEND the bundled scripts dir to PATH so the shipped `jq` shim resolves\n // when the host has no real jq (macOS ships none). Appended, not prepended,\n // so a real jq earlier on PATH still wins. Every hook `source`s\n // _normalize.sh, which calls bare `jq` under `set -e` \u2014 without this the\n // whole hooks-capture path silently dies on a jq-less machine.\n const augmentedPath = `${process.env.PATH ?? ''}${delimiter}${scriptsDir}`;\n // session-end reimports the full transcript + enrich/link/cleanup \u2014 it\n // budgets ~17s of network internally, so a 10s cap SIGKILLs it mid-reimport\n // and silently drops the session. Give it headroom; other hooks are quick.\n const timeout = hookName === 'session-end' ? 30_000 : 10_000;\n execFileSync('bash', [scriptPath], {\n input: stdin,\n stdio: ['pipe', 'inherit', 'inherit'],\n timeout,\n env: { ...process.env, PATH: augmentedPath },\n });\n\n // Antigravity-specific: tail the conversation transcript and emit\n // user_message / assistant_response / tool_result events that the\n // bash hook can't see. Antigravity does not fire UserPromptSubmit\n // or Stop hooks, and the Gemini streaming traffic bypasses the\n // proxy. First fire for a conversation primes state to the current\n // transcript end and returns immediately \u2014 capture is forward-only.\n if (\n process.env.RULEMETRIC_HOOK_TOOL === 'antigravity' &&\n hookName === 'post-tool-use'\n ) {\n try {\n const { tailAntigravityTranscript } = await import(\n '../../lib/antigravity-transcript.js'\n );\n await tailAntigravityTranscript(stdin);\n } catch (err) {\n process.stderr.write(\n `[antigravity-tailer] ${(err as Error).message}\\n`,\n );\n }\n }\n } catch (error: unknown) {\n // Hooks should not crash the AI tool \u2014 exit silently\n const code = (error as { status?: number }).status;\n if (code) {\n process.exitCode = code;\n }\n }\n }\n\n private readStdin(): Promise<string> {\n return new Promise((resolve) => {\n const chunks: Buffer[] = [];\n process.stdin.on('data', (chunk) => chunks.push(chunk));\n process.stdin.on('end', () => resolve(Buffer.concat(chunks).toString('utf-8')));\n // If stdin is already closed (no piped input), resolve immediately\n if (process.stdin.readableEnded) {\n resolve(Buffer.concat(chunks).toString('utf-8'));\n }\n });\n }\n}\n"],
5
- "mappings": ";;;;;;;;;;;AAAA,SAAS,YAAY;AACrB,SAAS,oBAAoB;AAC7B,SAAS,kBAAkB;AAC3B,SAAS,MAAM,iBAAiB;AAGhC,IAAM,cAAc,CAAC,iBAAiB,eAAe,eAAe,iBAAiB,oBAAoB;AAEzG,IAAqB,WAArB,MAAqB,kBAAiB,YAAY;AAAA,EAChD,OAAgB,cAAc;AAAA,EAC9B,OAAgB,SAAS;AAAA,EAEzB,OAAgB,OAAO;AAAA,IACrB,MAAM,KAAK,OAAO;AAAA,MAChB,aAAa;AAAA,MACb,UAAU;AAAA,MACV,SAAS,CAAC,GAAG,WAAW;AAAA,IAC1B,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,MAAqB;AACzB,UAAM,EAAE,KAAK,IAAI,MAAM,KAAK,MAAM,SAAQ;AAC1C,UAAM,WAAW,KAAK;AAGtB,UAAM,EAAE,WAAW,IAAI,MAAM,OAAO,+BAA+B;AACnE,UAAM,aAAa,KAAK,YAAY,GAAG,QAAQ,KAAK;AAEpD,QAAI,CAAC,WAAW,UAAU,GAAG;AAC3B,WAAK,MAAM,0BAA0B,UAAU,EAAE;AAAA,IACnD;AAEA,QAAI;AAEF,YAAM,QAAQ,MAAM,KAAK,UAAU;AAMnC,YAAM,gBAAgB,GAAG,QAAQ,IAAI,QAAQ,EAAE,GAAG,SAAS,GAAG,UAAU;AAIxE,YAAM,UAAU,aAAa,gBAAgB,MAAS;AACtD,mBAAa,QAAQ,CAAC,UAAU,GAAG;AAAA,QACjC,OAAO;AAAA,QACP,OAAO,CAAC,QAAQ,WAAW,SAAS;AAAA,QACpC;AAAA,QACA,KAAK,EAAE,GAAG,QAAQ,KAAK,MAAM,cAAc;AAAA,MAC7C,CAAC;AAQD,UACE,QAAQ,IAAI,yBAAyB,iBACrC,aAAa,iBACb;AACA,YAAI;AACF,gBAAM,EAAE,0BAA0B,IAAI,MAAM,OAC1C,qCACF;AACA,gBAAM,0BAA0B,KAAK;AAAA,QACvC,SAAS,KAAK;AACZ,kBAAQ,OAAO;AAAA,YACb,wBAAyB,IAAc,OAAO;AAAA;AAAA,UAChD;AAAA,QACF;AAAA,MACF;AAAA,IACF,SAAS,OAAgB;AAEvB,YAAM,OAAQ,MAA8B;AAC5C,UAAI,MAAM;AACR,gBAAQ,WAAW;AAAA,MACrB;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,YAA6B;AACnC,WAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,YAAM,SAAmB,CAAC;AAC1B,cAAQ,MAAM,GAAG,QAAQ,CAAC,UAAU,OAAO,KAAK,KAAK,CAAC;AACtD,cAAQ,MAAM,GAAG,OAAO,MAAM,QAAQ,OAAO,OAAO,MAAM,EAAE,SAAS,OAAO,CAAC,CAAC;AAE9E,UAAI,QAAQ,MAAM,eAAe;AAC/B,gBAAQ,OAAO,OAAO,MAAM,EAAE,SAAS,OAAO,CAAC;AAAA,MACjD;AAAA,IACF,CAAC;AAAA,EACH;AACF;",
4
+ "sourcesContent": ["import { Args } from '@oclif/core';\nimport { execFileSync } from 'node:child_process';\nimport { existsSync } from 'node:fs';\nimport { join, delimiter } from 'node:path';\nimport { BaseCommand } from '../../base-command.js';\n\nconst VALID_HOOKS = ['session-start', 'session-end', 'user-prompt', 'post-tool-use', 'assistant-response'] as const;\n\nexport default class HooksRun extends BaseCommand {\n static override description = 'Execute a hook script (called by AI tool hooks, not for direct use)';\n static override hidden = true;\n\n static override args = {\n hook: Args.string({\n description: 'Hook name to run',\n required: true,\n options: [...VALID_HOOKS],\n }),\n };\n\n async run(): Promise<void> {\n const { args } = await this.parse(HooksRun);\n const hookName = args.hook;\n\n // Resolve bundled script path\n const { scriptsDir } = await import('@rulemetric/hooks/scripts-dir');\n const scriptPath = join(scriptsDir, `${hookName}.sh`);\n\n if (!existsSync(scriptPath)) {\n this.error(`Hook script not found: ${scriptPath}`);\n }\n\n try {\n // Read stdin and pass it to the bash script\n const stdin = await this.readStdin();\n // APPEND the bundled scripts dir to PATH so the shipped `jq` shim resolves\n // when the host has no real jq (macOS ships none). Appended, not prepended,\n // so a real jq earlier on PATH still wins. Every hook `source`s\n // _normalize.sh, which calls bare `jq` under `set -e` \u2014 without this the\n // whole hooks-capture path silently dies on a jq-less machine.\n const augmentedPath = `${process.env.PATH ?? ''}${delimiter}${scriptsDir}`;\n // session-end reimports the full transcript + enrich/link/cleanup \u2014 it\n // budgets ~17s of network internally, so a 10s cap SIGKILLs it mid-reimport\n // and silently drops the session. Give it headroom; other hooks are quick.\n const timeout = hookName === 'session-end' ? 30_000 : 10_000;\n execFileSync('bash', [scriptPath], {\n input: stdin,\n stdio: ['pipe', 'inherit', 'inherit'],\n timeout,\n env: { ...process.env, PATH: augmentedPath },\n });\n\n // Antigravity-specific: tail the conversation transcript and emit\n // user_message / assistant_response / tool_result events that the\n // bash hook can't see. Antigravity does not fire UserPromptSubmit\n // or Stop hooks, and the Gemini streaming traffic bypasses the\n // proxy. First fire for a conversation primes state to the current\n // transcript end and returns immediately \u2014 capture is forward-only.\n if (\n process.env.RULEMETRIC_HOOK_TOOL === 'antigravity' &&\n hookName === 'post-tool-use'\n ) {\n try {\n const { tailAntigravityTranscript } = await import(\n '../../lib/antigravity-transcript.js'\n );\n await tailAntigravityTranscript(stdin);\n } catch (err) {\n process.stderr.write(\n `[antigravity-tailer] ${(err as Error).message}\\n`,\n );\n }\n }\n } catch (error: unknown) {\n // Capture hooks are BEST-EFFORT telemetry \u2014 a non-zero exit here surfaces\n // in the user's session as \"Stop hook error: Failed with non-blocking\n // status code\" (harmless, but alarming litter). Previously we re-propagated\n // the script's exit code, which contradicted this method's own \"exit\n // silently\" intent. Swallow it: always exit 0 so the AI tool never shows a\n // hook error for our telemetry. The failure is still visible for debugging\n // via RULEMETRIC_HOOK_DEBUG (and the script's own _rulemetric_log).\n const code = (error as { status?: number; message?: string }).status;\n if (process.env.RULEMETRIC_HOOK_DEBUG) {\n process.stderr.write(\n `[rulemetric hooks run ${hookName}] non-fatal exit ${code ?? '?'}: ${(error as { message?: string }).message ?? ''}\\n`,\n );\n }\n // process.exitCode intentionally left 0.\n }\n }\n\n private readStdin(): Promise<string> {\n return new Promise((resolve) => {\n const chunks: Buffer[] = [];\n process.stdin.on('data', (chunk) => chunks.push(chunk));\n process.stdin.on('end', () => resolve(Buffer.concat(chunks).toString('utf-8')));\n // If stdin is already closed (no piped input), resolve immediately\n if (process.stdin.readableEnded) {\n resolve(Buffer.concat(chunks).toString('utf-8'));\n }\n });\n }\n}\n"],
5
+ "mappings": ";;;;;;;;;;;AAAA,SAAS,YAAY;AACrB,SAAS,oBAAoB;AAC7B,SAAS,kBAAkB;AAC3B,SAAS,MAAM,iBAAiB;AAGhC,IAAM,cAAc,CAAC,iBAAiB,eAAe,eAAe,iBAAiB,oBAAoB;AAEzG,IAAqB,WAArB,MAAqB,kBAAiB,YAAY;AAAA,EAChD,OAAgB,cAAc;AAAA,EAC9B,OAAgB,SAAS;AAAA,EAEzB,OAAgB,OAAO;AAAA,IACrB,MAAM,KAAK,OAAO;AAAA,MAChB,aAAa;AAAA,MACb,UAAU;AAAA,MACV,SAAS,CAAC,GAAG,WAAW;AAAA,IAC1B,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,MAAqB;AACzB,UAAM,EAAE,KAAK,IAAI,MAAM,KAAK,MAAM,SAAQ;AAC1C,UAAM,WAAW,KAAK;AAGtB,UAAM,EAAE,WAAW,IAAI,MAAM,OAAO,+BAA+B;AACnE,UAAM,aAAa,KAAK,YAAY,GAAG,QAAQ,KAAK;AAEpD,QAAI,CAAC,WAAW,UAAU,GAAG;AAC3B,WAAK,MAAM,0BAA0B,UAAU,EAAE;AAAA,IACnD;AAEA,QAAI;AAEF,YAAM,QAAQ,MAAM,KAAK,UAAU;AAMnC,YAAM,gBAAgB,GAAG,QAAQ,IAAI,QAAQ,EAAE,GAAG,SAAS,GAAG,UAAU;AAIxE,YAAM,UAAU,aAAa,gBAAgB,MAAS;AACtD,mBAAa,QAAQ,CAAC,UAAU,GAAG;AAAA,QACjC,OAAO;AAAA,QACP,OAAO,CAAC,QAAQ,WAAW,SAAS;AAAA,QACpC;AAAA,QACA,KAAK,EAAE,GAAG,QAAQ,KAAK,MAAM,cAAc;AAAA,MAC7C,CAAC;AAQD,UACE,QAAQ,IAAI,yBAAyB,iBACrC,aAAa,iBACb;AACA,YAAI;AACF,gBAAM,EAAE,0BAA0B,IAAI,MAAM,OAC1C,qCACF;AACA,gBAAM,0BAA0B,KAAK;AAAA,QACvC,SAAS,KAAK;AACZ,kBAAQ,OAAO;AAAA,YACb,wBAAyB,IAAc,OAAO;AAAA;AAAA,UAChD;AAAA,QACF;AAAA,MACF;AAAA,IACF,SAAS,OAAgB;AAQvB,YAAM,OAAQ,MAAgD;AAC9D,UAAI,QAAQ,IAAI,uBAAuB;AACrC,gBAAQ,OAAO;AAAA,UACb,yBAAyB,QAAQ,oBAAoB,QAAQ,GAAG,KAAM,MAA+B,WAAW,EAAE;AAAA;AAAA,QACpH;AAAA,MACF;AAAA,IAEF;AAAA,EACF;AAAA,EAEQ,YAA6B;AACnC,WAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,YAAM,SAAmB,CAAC;AAC1B,cAAQ,MAAM,GAAG,QAAQ,CAAC,UAAU,OAAO,KAAK,KAAK,CAAC;AACtD,cAAQ,MAAM,GAAG,OAAO,MAAM,QAAQ,OAAO,OAAO,MAAM,EAAE,SAAS,OAAO,CAAC,CAAC;AAE9E,UAAI,QAAQ,MAAM,eAAe;AAC/B,gBAAQ,OAAO,OAAO,MAAM,EAAE,SAAS,OAAO,CAAC;AAAA,MACjD;AAAA,IACF,CAAC;AAAA,EACH;AACF;",
6
6
  "names": []
7
7
  }
@@ -4066,5 +4066,5 @@
4066
4066
  ]
4067
4067
  }
4068
4068
  },
4069
- "version": "0.6.8"
4069
+ "version": "0.6.9"
4070
4070
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rulemetric/cli",
3
- "version": "0.6.8",
3
+ "version": "0.6.9",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -73,9 +73,9 @@
73
73
  "postgres": "^3.4.0",
74
74
  "react": "^19.0.0",
75
75
  "zod": "^3.24.0",
76
- "@rulemetric/skills-registry": "0.6.8",
77
- "@rulemetric/proxy": "0.6.8",
78
- "@rulemetric/hooks": "0.6.8"
76
+ "@rulemetric/proxy": "0.6.9",
77
+ "@rulemetric/skills-registry": "0.6.9",
78
+ "@rulemetric/hooks": "0.6.9"
79
79
  },
80
80
  "//deps": "Worker (`evals agent`) runtime deps: the @opentelemetry/* set, @sentry/node, and postgres are STATIC top-level imports loaded at worker startup (via bundled @rulemetric/{telemetry,db}) — they MUST be runtime deps or a clean `npm i -g` crashes with ModuleLoadError. `better-sqlite3` is optionalDependencies (below): it's a native module, lazy `require()`'d inside a try/catch (packages/session providers cursor/opencode) so a missing binary degrades gracefully — making it a hard dep would break `npm i -g` on platforms without prebuilt binaries. `resend` stays a devDep: lazy `await import('resend')` guarded by RESEND_API_KEY, which end-user workers never set (provider falls back to noop). Classification verified via import-graph scan of dist/commands/evals/agent.js + per-importer static/lazy check.",
81
81
  "optionalDependencies": {