@sechroom/cli 2026.7.5-rc.ef6a4180 → 2026.7.6-rc.a2828c37

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/index.js +93 -20
  2. package/package.json +4 -2
package/dist/index.js CHANGED
@@ -439,6 +439,9 @@ var quiet = false;
439
439
  function setQuiet(q) {
440
440
  quiet = q;
441
441
  }
442
+ function isQuiet() {
443
+ return quiet;
444
+ }
442
445
  function colorOn() {
443
446
  return !quiet && !process.env.NO_COLOR && process.env.FORCE_COLOR !== "0" && Boolean(process.stdout.isTTY);
444
447
  }
@@ -615,6 +618,22 @@ function emitAction(summary, data, json) {
615
618
  process.stdout.write(`${ok("\u2713")} ${summary}
616
619
  `);
617
620
  }
621
+ var GOVERNANCE_QUEUED_PROBLEM_TYPE = "https://sechroom.dev/problems/governance-review-queued";
622
+ function isGovernanceQueued(body) {
623
+ return typeof body === "object" && body !== null && "type" in body && body.type === GOVERNANCE_QUEUED_PROBLEM_TYPE;
624
+ }
625
+ function formatQueuedForApproval(body, json) {
626
+ if (json) return JSON.stringify(body) + "\n";
627
+ const b = body ?? {};
628
+ const detail = typeof b.detail === "string" ? b.detail : void 0;
629
+ const requestId = typeof b.requestId === "string" ? b.requestId : void 0;
630
+ const message = detail ?? "This change requires operator approval before it takes effect.";
631
+ let out = `${warn("\u29D7")} Pending approval \u2014 ${message}
632
+ `;
633
+ if (requestId) out += ` ${style.dim(`request: ${requestId}`)}
634
+ `;
635
+ return out;
636
+ }
618
637
  async function runApi(label, fn) {
619
638
  const s = spinner(label);
620
639
  let res;
@@ -624,6 +643,12 @@ async function runApi(label, fn) {
624
643
  s.fail();
625
644
  fail(err2);
626
645
  }
646
+ const queuedBody = res.data ?? res.error;
647
+ if (res.response?.status === 202 && isGovernanceQueued(queuedBody)) {
648
+ s.stop();
649
+ process.stdout.write(formatQueuedForApproval(queuedBody, isQuiet()));
650
+ process.exit(0);
651
+ }
627
652
  const httpFailed = res.response !== void 0 && !res.response.ok;
628
653
  if (res.error !== void 0 && res.error !== null || httpFailed) {
629
654
  s.fail();
@@ -1403,10 +1428,15 @@ var CLAUDE_HOOK_COMMANDS = {
1403
1428
  SessionStart: "sechroom hook session-start",
1404
1429
  PreCompact: "sechroom hook pre-compact",
1405
1430
  SessionEnd: "sechroom hook session-end",
1406
- // WLP telemetry tap (D-WLP-10) — per-turn executor self-report. No-op (exit 0) unless this
1407
- // checkout is bound to a decomposition+task via `sechroom telemetry bind`, so it's safe to wire
1408
- // for every Claude install. Claude-only (it parses a Claude Code transcript).
1409
- Stop: "sechroom telemetry hook"
1431
+ // WLP telemetry tap (D-WLP-10 + FR-352 Tier 1) — per-turn executor self-report. The one
1432
+ // `telemetry hook` verb dispatches on hook_event_name: Stop/SubagentStop parsed (token/context) +
1433
+ // terminal (turn end), Notification/PermissionDenied → approval. No-op (exit 0) unless this checkout
1434
+ // is bound via `sechroom telemetry bind`, so it's safe to wire for every Claude install; an event a
1435
+ // given Claude Code version doesn't know is inert (never fires). Claude-only.
1436
+ Stop: "sechroom telemetry hook",
1437
+ SubagentStop: "sechroom telemetry hook",
1438
+ Notification: "sechroom telemetry hook",
1439
+ PermissionDenied: "sechroom telemetry hook"
1410
1440
  };
1411
1441
  var CODEX_HOOK_COMMANDS = {
1412
1442
  SessionStart: "sechroom hook session-start",
@@ -5806,7 +5836,7 @@ function registerTelemetry(program2) {
5806
5836
  );
5807
5837
  });
5808
5838
  telemetry.command("hook").description(
5809
- "Per-turn telemetry self-report for a Claude Code Stop hook (reads stdin; no-op unless bound). Fail-soft."
5839
+ "Per-turn telemetry self-report for Claude Code hooks \u2014 Stop/SubagentStop \u2192 parsed + terminal, Notification/PermissionDenied \u2192 approval (reads stdin; no-op unless bound). Fail-soft."
5810
5840
  ).action(async (_opts, cmd) => {
5811
5841
  try {
5812
5842
  const raw = await readStdin2();
@@ -5815,21 +5845,10 @@ function registerTelemetry(program2) {
5815
5845
  const binding = findBinding(cwd);
5816
5846
  if (!binding) return process.exit(0);
5817
5847
  const usage = input.transcript_path ? parseTranscript(input.transcript_path) : null;
5818
- if (!usage) return process.exit(0);
5848
+ const events = buildHookEvents(input, usage, binding.taskId);
5849
+ if (events.length === 0) return process.exit(0);
5819
5850
  const cfg = resolveConfig(cmd.optsWithGlobals());
5820
- await postTelemetry(cfg, binding.decompositionId, [
5821
- {
5822
- taskId: binding.taskId,
5823
- kind: "Parsed",
5824
- tokensIn: usage.tokensIn,
5825
- tokensOut: usage.tokensOut,
5826
- contextUsed: usage.contextUsed,
5827
- contextWindow: usage.contextWindow,
5828
- text: null,
5829
- approvalState: null,
5830
- verdict: null
5831
- }
5832
- ]);
5851
+ await postTelemetry(cfg, binding.decompositionId, events);
5833
5852
  return process.exit(0);
5834
5853
  } catch {
5835
5854
  return process.exit(0);
@@ -5857,7 +5876,12 @@ function registerTelemetry(program2) {
5857
5876
  scope,
5858
5877
  cwd
5859
5878
  });
5860
- const commands = { Stop: "sechroom telemetry hook" };
5879
+ const commands = {
5880
+ Stop: "sechroom telemetry hook",
5881
+ SubagentStop: "sechroom telemetry hook",
5882
+ Notification: "sechroom telemetry hook",
5883
+ PermissionDenied: "sechroom telemetry hook"
5884
+ };
5861
5885
  try {
5862
5886
  const multi = targets.length > 1;
5863
5887
  const results = targets.map((t) => {
@@ -5958,6 +5982,55 @@ function windowFor(model, contextUsed = 0) {
5958
5982
  if (m.includes("[1m]") || m.includes("-1m")) return 1e6;
5959
5983
  return contextUsed > 2e5 ? 1e6 : 2e5;
5960
5984
  }
5985
+ function buildHookEvents(input, usage, taskId) {
5986
+ const events = [];
5987
+ const base = (kind, over) => ({
5988
+ taskId,
5989
+ kind,
5990
+ tokensIn: null,
5991
+ tokensOut: null,
5992
+ contextUsed: null,
5993
+ contextWindow: null,
5994
+ text: null,
5995
+ approvalState: null,
5996
+ verdict: null,
5997
+ ...over
5998
+ });
5999
+ if (usage) {
6000
+ events.push(
6001
+ base("Parsed", {
6002
+ tokensIn: usage.tokensIn,
6003
+ tokensOut: usage.tokensOut,
6004
+ contextUsed: usage.contextUsed,
6005
+ contextWindow: usage.contextWindow
6006
+ })
6007
+ );
6008
+ }
6009
+ switch (input.hook_event_name) {
6010
+ case "PermissionDenied":
6011
+ events.push(
6012
+ base("Approval", {
6013
+ approvalState: "denied",
6014
+ text: input.tool_name ?? input.message ?? null
6015
+ })
6016
+ );
6017
+ break;
6018
+ case "Notification":
6019
+ if (isPermissionNotification(input))
6020
+ events.push(base("Approval", { text: input.message ?? null }));
6021
+ break;
6022
+ case "Stop":
6023
+ case "SubagentStop":
6024
+ events.push(base("Terminal", { text: input.last_assistant_message ?? null }));
6025
+ break;
6026
+ }
6027
+ return events;
6028
+ }
6029
+ function isPermissionNotification(input) {
6030
+ const t = (input.notification_type ?? input.type ?? "").toLowerCase();
6031
+ if (t) return t.includes("permission");
6032
+ return (input.message ?? "").toLowerCase().includes("permission");
6033
+ }
5961
6034
  async function readStdin2() {
5962
6035
  if (process.stdin.isTTY) return "";
5963
6036
  const chunks = [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sechroom/cli",
3
- "version": "2026.7.5-rc.ef6a4180",
3
+ "version": "2026.7.6-rc.a2828c37",
4
4
  "description": "Sechroom CLI — a thin, generated client over the Sechroom HTTP API. An agent/human surface alongside MCP.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",
@@ -36,12 +36,14 @@
36
36
  "openapi-typescript": "^7.4.0",
37
37
  "tsup": "^8.3.0",
38
38
  "tsx": "^4.19.0",
39
- "typescript": "^5.6.0"
39
+ "typescript": "^5.6.0",
40
+ "typescript-7": "npm:typescript@^7.0.2"
40
41
  },
41
42
  "scripts": {
42
43
  "gen": "openapi-typescript \"${SECHROOM_OPENAPI_URL:-https://app.sechroom.ai/api/openapi/v1.json}\" -o src/generated/api.d.ts",
43
44
  "build": "tsup src/index.ts --format esm --target node20 --clean",
44
45
  "dev": "tsx src/index.ts",
46
+ "test": "node --import tsx --test \"src/**/*.test.ts\"",
45
47
  "check-types": "tsc --noEmit"
46
48
  }
47
49
  }