@sechroom/cli 2026.7.11 → 2026.7.13
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/index.js +116 -21
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -1403,10 +1403,15 @@ var CLAUDE_HOOK_COMMANDS = {
|
|
|
1403
1403
|
SessionStart: "sechroom hook session-start",
|
|
1404
1404
|
PreCompact: "sechroom hook pre-compact",
|
|
1405
1405
|
SessionEnd: "sechroom hook session-end",
|
|
1406
|
-
// WLP telemetry tap (D-WLP-10) — per-turn executor self-report.
|
|
1407
|
-
//
|
|
1408
|
-
//
|
|
1409
|
-
|
|
1406
|
+
// WLP telemetry tap (D-WLP-10 + FR-352 Tier 1) — per-turn executor self-report. The one
|
|
1407
|
+
// `telemetry hook` verb dispatches on hook_event_name: Stop/SubagentStop → parsed (token/context) +
|
|
1408
|
+
// terminal (turn end), Notification/PermissionDenied → approval. No-op (exit 0) unless this checkout
|
|
1409
|
+
// is bound via `sechroom telemetry bind`, so it's safe to wire for every Claude install; an event a
|
|
1410
|
+
// given Claude Code version doesn't know is inert (never fires). Claude-only.
|
|
1411
|
+
Stop: "sechroom telemetry hook",
|
|
1412
|
+
SubagentStop: "sechroom telemetry hook",
|
|
1413
|
+
Notification: "sechroom telemetry hook",
|
|
1414
|
+
PermissionDenied: "sechroom telemetry hook"
|
|
1410
1415
|
};
|
|
1411
1416
|
var CODEX_HOOK_COMMANDS = {
|
|
1412
1417
|
SessionStart: "sechroom hook session-start",
|
|
@@ -2566,6 +2571,9 @@ function registerClose(program2) {
|
|
|
2566
2571
|
).option(
|
|
2567
2572
|
"--no-status-flip",
|
|
2568
2573
|
"Skip the status flip on a BARE task. (Managed runs never flip here \u2014 the run engine reflects the verdict onto the task on resume.)"
|
|
2574
|
+
).option(
|
|
2575
|
+
"--to-version <n>",
|
|
2576
|
+
"Pin the Reference edge at this task version \u2014 the DISPATCHED version the executor worked against (D-continuity-2). Default: the task's current version (status flips are metadata edits that don't bump the version, so current == dispatched in the normal case; pass this when the task's content changed between dispatch and close)."
|
|
2569
2577
|
).option("--source <source>", "Source / lane stamp", "cli").addHelpText(
|
|
2570
2578
|
"after",
|
|
2571
2579
|
`
|
|
@@ -2599,6 +2607,7 @@ Examples:
|
|
|
2599
2607
|
);
|
|
2600
2608
|
const client = await makeClient(cfg);
|
|
2601
2609
|
let matchTags;
|
|
2610
|
+
let dispatchedVersion;
|
|
2602
2611
|
if (opts.decomposition) {
|
|
2603
2612
|
const run = await runApi(
|
|
2604
2613
|
"Reading run contract",
|
|
@@ -2616,6 +2625,8 @@ Examples:
|
|
|
2616
2625
|
`run ${opts.decomposition} is awaiting task ${run.awaitingTaskId}, not ${opts.task} \u2014 refusing to mis-key the resume.`
|
|
2617
2626
|
);
|
|
2618
2627
|
matchTags = await_.matchTags;
|
|
2628
|
+
if (typeof await_.dispatchedTaskVersion === "number")
|
|
2629
|
+
dispatchedVersion = await_.dispatchedTaskVersion;
|
|
2619
2630
|
} else {
|
|
2620
2631
|
matchTags = [`wlp-task:${opts.task}`];
|
|
2621
2632
|
}
|
|
@@ -2642,11 +2653,34 @@ Examples:
|
|
|
2642
2653
|
}
|
|
2643
2654
|
})
|
|
2644
2655
|
);
|
|
2656
|
+
let toVersion;
|
|
2657
|
+
if (opts.toVersion !== void 0) {
|
|
2658
|
+
toVersion = Number(opts.toVersion);
|
|
2659
|
+
if (!Number.isInteger(toVersion) || toVersion < 1)
|
|
2660
|
+
fail(
|
|
2661
|
+
`--to-version must be an integer >= 1 \u2014 got '${opts.toVersion}'`
|
|
2662
|
+
);
|
|
2663
|
+
} else if (dispatchedVersion !== void 0) {
|
|
2664
|
+
toVersion = dispatchedVersion;
|
|
2665
|
+
} else {
|
|
2666
|
+
const task = await runApi(
|
|
2667
|
+
"Reading task version",
|
|
2668
|
+
async () => client.GET("/memories/{memoryId}", {
|
|
2669
|
+
params: { path: { memoryId: opts.task } }
|
|
2670
|
+
})
|
|
2671
|
+
);
|
|
2672
|
+
const v = task?.item?.currentVersion ?? task?.currentVersion;
|
|
2673
|
+
if (typeof v !== "number" || v < 1)
|
|
2674
|
+
fail(
|
|
2675
|
+
`could not read task ${opts.task} version to pin the Reference edge \u2014 pass --to-version <n>`
|
|
2676
|
+
);
|
|
2677
|
+
toVersion = v;
|
|
2678
|
+
}
|
|
2645
2679
|
const edge = await runApi(
|
|
2646
2680
|
"Wiring Reference edge",
|
|
2647
2681
|
async () => client.POST("/memories/{memoryId}/relationships", {
|
|
2648
2682
|
params: { path: { memoryId: closeout.id } },
|
|
2649
|
-
body: { toMemoryId: opts.task, type: "Reference" }
|
|
2683
|
+
body: { toMemoryId: opts.task, type: "Reference", toVersion }
|
|
2650
2684
|
})
|
|
2651
2685
|
);
|
|
2652
2686
|
let taskStatus;
|
|
@@ -2862,6 +2896,7 @@ function registerDecomposition(program2) {
|
|
|
2862
2896
|
Examples:
|
|
2863
2897
|
$ sechroom decomposition decompose mem_XXXX
|
|
2864
2898
|
$ sechroom decomposition execute sug_XXXX
|
|
2899
|
+
$ sechroom decomposition publish-run sug_XXXX
|
|
2865
2900
|
$ sechroom decomposition accept sug_XXXX
|
|
2866
2901
|
$ sechroom decomposition reject sug_XXXX --reason "wrong shape"`
|
|
2867
2902
|
);
|
|
@@ -2899,6 +2934,23 @@ Examples:
|
|
|
2899
2934
|
cmd.optsWithGlobals().json
|
|
2900
2935
|
);
|
|
2901
2936
|
});
|
|
2937
|
+
decomposition.command("publish-run <decompositionId>").description(
|
|
2938
|
+
"Publish an accepted decomposition's context pack on demand (POST /decompositions/{id}/publish-run)"
|
|
2939
|
+
).action(async (decompositionId, _opts, cmd) => {
|
|
2940
|
+
const cfg = resolveConfig(cmd.optsWithGlobals());
|
|
2941
|
+
const data = await runApi("Publishing context pack", async () => {
|
|
2942
|
+
const client = await makeClient(cfg);
|
|
2943
|
+
return client.POST("/decompositions/{id}/publish-run", {
|
|
2944
|
+
params: { path: { id: decompositionId } },
|
|
2945
|
+
body: {}
|
|
2946
|
+
});
|
|
2947
|
+
});
|
|
2948
|
+
emitAction(
|
|
2949
|
+
`published ${style.bold(decompositionId)} \u2192 run ${style.bold(data.runRecordId)} (${data.outcome})`,
|
|
2950
|
+
data,
|
|
2951
|
+
cmd.optsWithGlobals().json
|
|
2952
|
+
);
|
|
2953
|
+
});
|
|
2902
2954
|
decomposition.command("accept <decompositionId>").description(
|
|
2903
2955
|
"Accept a Pending decomposition \u2014 promote + ratify its Tasks (POST /decompositions/{id}/accept)"
|
|
2904
2956
|
).action(async (decompositionId, _opts, cmd) => {
|
|
@@ -5759,7 +5811,7 @@ function registerTelemetry(program2) {
|
|
|
5759
5811
|
);
|
|
5760
5812
|
});
|
|
5761
5813
|
telemetry.command("hook").description(
|
|
5762
|
-
"Per-turn telemetry self-report for
|
|
5814
|
+
"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."
|
|
5763
5815
|
).action(async (_opts, cmd) => {
|
|
5764
5816
|
try {
|
|
5765
5817
|
const raw = await readStdin2();
|
|
@@ -5768,21 +5820,10 @@ function registerTelemetry(program2) {
|
|
|
5768
5820
|
const binding = findBinding(cwd);
|
|
5769
5821
|
if (!binding) return process.exit(0);
|
|
5770
5822
|
const usage = input.transcript_path ? parseTranscript(input.transcript_path) : null;
|
|
5771
|
-
|
|
5823
|
+
const events = buildHookEvents(input, usage, binding.taskId);
|
|
5824
|
+
if (events.length === 0) return process.exit(0);
|
|
5772
5825
|
const cfg = resolveConfig(cmd.optsWithGlobals());
|
|
5773
|
-
await postTelemetry(cfg, binding.decompositionId,
|
|
5774
|
-
{
|
|
5775
|
-
taskId: binding.taskId,
|
|
5776
|
-
kind: "Parsed",
|
|
5777
|
-
tokensIn: usage.tokensIn,
|
|
5778
|
-
tokensOut: usage.tokensOut,
|
|
5779
|
-
contextUsed: usage.contextUsed,
|
|
5780
|
-
contextWindow: usage.contextWindow,
|
|
5781
|
-
text: null,
|
|
5782
|
-
approvalState: null,
|
|
5783
|
-
verdict: null
|
|
5784
|
-
}
|
|
5785
|
-
]);
|
|
5826
|
+
await postTelemetry(cfg, binding.decompositionId, events);
|
|
5786
5827
|
return process.exit(0);
|
|
5787
5828
|
} catch {
|
|
5788
5829
|
return process.exit(0);
|
|
@@ -5810,7 +5851,12 @@ function registerTelemetry(program2) {
|
|
|
5810
5851
|
scope,
|
|
5811
5852
|
cwd
|
|
5812
5853
|
});
|
|
5813
|
-
const commands = {
|
|
5854
|
+
const commands = {
|
|
5855
|
+
Stop: "sechroom telemetry hook",
|
|
5856
|
+
SubagentStop: "sechroom telemetry hook",
|
|
5857
|
+
Notification: "sechroom telemetry hook",
|
|
5858
|
+
PermissionDenied: "sechroom telemetry hook"
|
|
5859
|
+
};
|
|
5814
5860
|
try {
|
|
5815
5861
|
const multi = targets.length > 1;
|
|
5816
5862
|
const results = targets.map((t) => {
|
|
@@ -5911,6 +5957,55 @@ function windowFor(model, contextUsed = 0) {
|
|
|
5911
5957
|
if (m.includes("[1m]") || m.includes("-1m")) return 1e6;
|
|
5912
5958
|
return contextUsed > 2e5 ? 1e6 : 2e5;
|
|
5913
5959
|
}
|
|
5960
|
+
function buildHookEvents(input, usage, taskId) {
|
|
5961
|
+
const events = [];
|
|
5962
|
+
const base = (kind, over) => ({
|
|
5963
|
+
taskId,
|
|
5964
|
+
kind,
|
|
5965
|
+
tokensIn: null,
|
|
5966
|
+
tokensOut: null,
|
|
5967
|
+
contextUsed: null,
|
|
5968
|
+
contextWindow: null,
|
|
5969
|
+
text: null,
|
|
5970
|
+
approvalState: null,
|
|
5971
|
+
verdict: null,
|
|
5972
|
+
...over
|
|
5973
|
+
});
|
|
5974
|
+
if (usage) {
|
|
5975
|
+
events.push(
|
|
5976
|
+
base("Parsed", {
|
|
5977
|
+
tokensIn: usage.tokensIn,
|
|
5978
|
+
tokensOut: usage.tokensOut,
|
|
5979
|
+
contextUsed: usage.contextUsed,
|
|
5980
|
+
contextWindow: usage.contextWindow
|
|
5981
|
+
})
|
|
5982
|
+
);
|
|
5983
|
+
}
|
|
5984
|
+
switch (input.hook_event_name) {
|
|
5985
|
+
case "PermissionDenied":
|
|
5986
|
+
events.push(
|
|
5987
|
+
base("Approval", {
|
|
5988
|
+
approvalState: "denied",
|
|
5989
|
+
text: input.tool_name ?? input.message ?? null
|
|
5990
|
+
})
|
|
5991
|
+
);
|
|
5992
|
+
break;
|
|
5993
|
+
case "Notification":
|
|
5994
|
+
if (isPermissionNotification(input))
|
|
5995
|
+
events.push(base("Approval", { text: input.message ?? null }));
|
|
5996
|
+
break;
|
|
5997
|
+
case "Stop":
|
|
5998
|
+
case "SubagentStop":
|
|
5999
|
+
events.push(base("Terminal", { text: input.last_assistant_message ?? null }));
|
|
6000
|
+
break;
|
|
6001
|
+
}
|
|
6002
|
+
return events;
|
|
6003
|
+
}
|
|
6004
|
+
function isPermissionNotification(input) {
|
|
6005
|
+
const t = (input.notification_type ?? input.type ?? "").toLowerCase();
|
|
6006
|
+
if (t) return t.includes("permission");
|
|
6007
|
+
return (input.message ?? "").toLowerCase().includes("permission");
|
|
6008
|
+
}
|
|
5914
6009
|
async function readStdin2() {
|
|
5915
6010
|
if (process.stdin.isTTY) return "";
|
|
5916
6011
|
const chunks = [];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sechroom/cli",
|
|
3
|
-
"version": "2026.7.
|
|
3
|
+
"version": "2026.7.13",
|
|
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,7 +36,8 @@
|
|
|
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",
|