@mutagent/cli 0.1.130 → 0.1.132
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/bin/cli.js +510 -54
- package/dist/bin/cli.js.map +10 -5
- package/package.json +1 -1
package/dist/bin/cli.js
CHANGED
|
@@ -1243,8 +1243,8 @@ var init_sdk_client = __esm(() => {
|
|
|
1243
1243
|
// src/bin/cli.ts
|
|
1244
1244
|
import { Command as Command21 } from "commander";
|
|
1245
1245
|
import chalk39 from "chalk";
|
|
1246
|
-
import { readFileSync as
|
|
1247
|
-
import { join as
|
|
1246
|
+
import { readFileSync as readFileSync13, existsSync as existsSync15 } from "fs";
|
|
1247
|
+
import { join as join11, dirname as dirname3 } from "path";
|
|
1248
1248
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
1249
1249
|
|
|
1250
1250
|
// src/commands/auth.ts
|
|
@@ -10684,28 +10684,16 @@ Examples:
|
|
|
10684
10684
|
return usage;
|
|
10685
10685
|
}
|
|
10686
10686
|
|
|
10687
|
-
// src/commands/hooks.ts
|
|
10688
|
-
init_config();
|
|
10687
|
+
// src/commands/hooks/index.ts
|
|
10689
10688
|
import { Command as Command19 } from "commander";
|
|
10689
|
+
|
|
10690
|
+
// src/commands/hooks/handlers-core.ts
|
|
10690
10691
|
import { randomUUID } from "crypto";
|
|
10692
|
+
|
|
10693
|
+
// src/commands/hooks/state.ts
|
|
10694
|
+
import { readFileSync as readFileSync10, writeFileSync as writeFileSync6, renameSync, unlinkSync, existsSync as existsSync13 } from "fs";
|
|
10691
10695
|
import { join as join8 } from "path";
|
|
10692
10696
|
import { tmpdir } from "os";
|
|
10693
|
-
import { readFileSync as readFileSync10, writeFileSync as writeFileSync6, unlinkSync, existsSync as existsSync13, mkdirSync as mkdirSync5 } from "fs";
|
|
10694
|
-
async function safeExecute(fn) {
|
|
10695
|
-
try {
|
|
10696
|
-
await fn();
|
|
10697
|
-
} catch (err) {
|
|
10698
|
-
process.stderr.write(`[mutagent hooks] Warning: ${err instanceof Error ? err.message : String(err)}
|
|
10699
|
-
`);
|
|
10700
|
-
}
|
|
10701
|
-
}
|
|
10702
|
-
async function readStdin() {
|
|
10703
|
-
const chunks = [];
|
|
10704
|
-
for await (const chunk of process.stdin) {
|
|
10705
|
-
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
10706
|
-
}
|
|
10707
|
-
return JSON.parse(Buffer.concat(chunks).toString("utf-8"));
|
|
10708
|
-
}
|
|
10709
10697
|
function stateFilePath(sessionId) {
|
|
10710
10698
|
return join8(tmpdir(), `mutagent-hook-${sessionId}.json`);
|
|
10711
10699
|
}
|
|
@@ -10714,13 +10702,23 @@ function readState(sessionId) {
|
|
|
10714
10702
|
if (!existsSync13(path))
|
|
10715
10703
|
return null;
|
|
10716
10704
|
try {
|
|
10717
|
-
|
|
10705
|
+
const raw = JSON.parse(readFileSync10(path, "utf-8"));
|
|
10706
|
+
if (!Array.isArray(raw.parentStack)) {
|
|
10707
|
+
raw.parentStack = [];
|
|
10708
|
+
}
|
|
10709
|
+
if (!Array.isArray(raw.parentStackKinds)) {
|
|
10710
|
+
raw.parentStackKinds = raw.parentStack.map(() => "agent");
|
|
10711
|
+
}
|
|
10712
|
+
return raw;
|
|
10718
10713
|
} catch {
|
|
10719
10714
|
return null;
|
|
10720
10715
|
}
|
|
10721
10716
|
}
|
|
10722
10717
|
function writeState(sessionId, state) {
|
|
10723
|
-
|
|
10718
|
+
const path = stateFilePath(sessionId);
|
|
10719
|
+
const tmpPath = `${path}.${process.pid.toString()}.tmp`;
|
|
10720
|
+
writeFileSync6(tmpPath, JSON.stringify(state), "utf-8");
|
|
10721
|
+
renameSync(tmpPath, path);
|
|
10724
10722
|
}
|
|
10725
10723
|
function deleteState(sessionId) {
|
|
10726
10724
|
const path = stateFilePath(sessionId);
|
|
@@ -10730,6 +10728,30 @@ function deleteState(sessionId) {
|
|
|
10730
10728
|
} catch {}
|
|
10731
10729
|
}
|
|
10732
10730
|
}
|
|
10731
|
+
function pushParent(state, spanId, kind = "agent") {
|
|
10732
|
+
state.parentStack.push(spanId);
|
|
10733
|
+
state.parentStackKinds.push(kind);
|
|
10734
|
+
return state;
|
|
10735
|
+
}
|
|
10736
|
+
function popParent(state) {
|
|
10737
|
+
if (state.parentStack.length === 0)
|
|
10738
|
+
return null;
|
|
10739
|
+
state.parentStackKinds.pop();
|
|
10740
|
+
return state.parentStack.pop() ?? null;
|
|
10741
|
+
}
|
|
10742
|
+
function peekParent(state) {
|
|
10743
|
+
if (state.parentStack.length === 0)
|
|
10744
|
+
return null;
|
|
10745
|
+
return state.parentStack[state.parentStack.length - 1] ?? null;
|
|
10746
|
+
}
|
|
10747
|
+
function peekParentKind(state) {
|
|
10748
|
+
if (state.parentStackKinds.length === 0)
|
|
10749
|
+
return null;
|
|
10750
|
+
return state.parentStackKinds[state.parentStackKinds.length - 1] ?? null;
|
|
10751
|
+
}
|
|
10752
|
+
|
|
10753
|
+
// src/commands/hooks/api.ts
|
|
10754
|
+
init_config();
|
|
10733
10755
|
var API_TIMEOUT_MS = 5000;
|
|
10734
10756
|
async function sendBatchTrace(traces) {
|
|
10735
10757
|
const apiKey = getApiKey();
|
|
@@ -10770,6 +10792,23 @@ async function sendBatchTrace(traces) {
|
|
|
10770
10792
|
clearTimeout(timeout);
|
|
10771
10793
|
}
|
|
10772
10794
|
}
|
|
10795
|
+
|
|
10796
|
+
// src/commands/hooks/handlers-core.ts
|
|
10797
|
+
async function readStdin() {
|
|
10798
|
+
const chunks = [];
|
|
10799
|
+
for await (const chunk of process.stdin) {
|
|
10800
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
10801
|
+
}
|
|
10802
|
+
return JSON.parse(Buffer.concat(chunks).toString("utf-8"));
|
|
10803
|
+
}
|
|
10804
|
+
async function safeExecute(fn) {
|
|
10805
|
+
try {
|
|
10806
|
+
await fn();
|
|
10807
|
+
} catch (err) {
|
|
10808
|
+
process.stderr.write(`[mutagent hooks] Warning: ${err instanceof Error ? err.message : String(err)}
|
|
10809
|
+
`);
|
|
10810
|
+
}
|
|
10811
|
+
}
|
|
10773
10812
|
function getString(input, ...keys) {
|
|
10774
10813
|
for (const key of keys) {
|
|
10775
10814
|
const val = input[key];
|
|
@@ -10790,12 +10829,16 @@ async function handleSessionStart() {
|
|
|
10790
10829
|
throw new Error("Missing session_id in stdin");
|
|
10791
10830
|
const now = new Date().toISOString();
|
|
10792
10831
|
const traceId = `cc-${sessionId}`;
|
|
10832
|
+
const rootSpanId = randomUUID();
|
|
10793
10833
|
const state = {
|
|
10794
10834
|
traceId,
|
|
10795
10835
|
sessionId,
|
|
10796
10836
|
startTime: now,
|
|
10797
|
-
openSpans: {}
|
|
10837
|
+
openSpans: {},
|
|
10838
|
+
parentStack: [],
|
|
10839
|
+
parentStackKinds: []
|
|
10798
10840
|
};
|
|
10841
|
+
pushParent(state, rootSpanId, "agent");
|
|
10799
10842
|
writeState(sessionId, state);
|
|
10800
10843
|
await sendBatchTrace([
|
|
10801
10844
|
{
|
|
@@ -10805,7 +10848,16 @@ async function handleSessionStart() {
|
|
|
10805
10848
|
source: "sdk",
|
|
10806
10849
|
startTime: now,
|
|
10807
10850
|
status: "running",
|
|
10808
|
-
spans: [
|
|
10851
|
+
spans: [
|
|
10852
|
+
{
|
|
10853
|
+
spanId: rootSpanId,
|
|
10854
|
+
name: "Claude Code Session",
|
|
10855
|
+
kind: "agent",
|
|
10856
|
+
startTime: now,
|
|
10857
|
+
status: "running",
|
|
10858
|
+
parentSpanId: null
|
|
10859
|
+
}
|
|
10860
|
+
]
|
|
10809
10861
|
}
|
|
10810
10862
|
]);
|
|
10811
10863
|
}
|
|
@@ -10818,6 +10870,39 @@ async function handleSessionEnd() {
|
|
|
10818
10870
|
const state = readState(sessionId);
|
|
10819
10871
|
const traceId = state?.traceId ?? `cc-${sessionId}`;
|
|
10820
10872
|
const startTime = state?.startTime ?? now;
|
|
10873
|
+
const spansToClose = [];
|
|
10874
|
+
if (state && state.parentStack.length > 0) {
|
|
10875
|
+
const stackCopy = [...state.parentStack];
|
|
10876
|
+
const kindsCopy = [...state.parentStackKinds];
|
|
10877
|
+
for (let i = stackCopy.length - 1;i > 0; i--) {
|
|
10878
|
+
const orphanId = stackCopy[i];
|
|
10879
|
+
const orphanKind = kindsCopy[i] ?? "agent";
|
|
10880
|
+
if (orphanId) {
|
|
10881
|
+
spansToClose.push({
|
|
10882
|
+
spanId: orphanId,
|
|
10883
|
+
name: "orphaned span",
|
|
10884
|
+
kind: orphanKind,
|
|
10885
|
+
startTime: now,
|
|
10886
|
+
endTime: now,
|
|
10887
|
+
status: "error",
|
|
10888
|
+
output: "orphaned at session end",
|
|
10889
|
+
parentSpanId: stackCopy[i - 1] ?? null
|
|
10890
|
+
});
|
|
10891
|
+
}
|
|
10892
|
+
}
|
|
10893
|
+
const rootSpanId = stackCopy[0];
|
|
10894
|
+
if (rootSpanId) {
|
|
10895
|
+
spansToClose.push({
|
|
10896
|
+
spanId: rootSpanId,
|
|
10897
|
+
name: "Claude Code Session",
|
|
10898
|
+
kind: "agent",
|
|
10899
|
+
startTime: state.startTime,
|
|
10900
|
+
endTime: now,
|
|
10901
|
+
status: "completed",
|
|
10902
|
+
parentSpanId: null
|
|
10903
|
+
});
|
|
10904
|
+
}
|
|
10905
|
+
}
|
|
10821
10906
|
await sendBatchTrace([
|
|
10822
10907
|
{
|
|
10823
10908
|
traceId,
|
|
@@ -10827,7 +10912,7 @@ async function handleSessionEnd() {
|
|
|
10827
10912
|
startTime,
|
|
10828
10913
|
endTime: now,
|
|
10829
10914
|
status: "completed",
|
|
10830
|
-
spans:
|
|
10915
|
+
spans: spansToClose
|
|
10831
10916
|
}
|
|
10832
10917
|
]);
|
|
10833
10918
|
deleteState(sessionId);
|
|
@@ -10843,8 +10928,11 @@ async function handlePreToolUse() {
|
|
|
10843
10928
|
traceId: `cc-${sessionId}`,
|
|
10844
10929
|
sessionId,
|
|
10845
10930
|
startTime: now,
|
|
10846
|
-
openSpans: {}
|
|
10931
|
+
openSpans: {},
|
|
10932
|
+
parentStack: [],
|
|
10933
|
+
parentStackKinds: []
|
|
10847
10934
|
};
|
|
10935
|
+
const parentSpanId = peekParent(state);
|
|
10848
10936
|
const spanId = randomUUID();
|
|
10849
10937
|
state.openSpans[spanId] = {
|
|
10850
10938
|
spanId,
|
|
@@ -10868,7 +10956,8 @@ async function handlePreToolUse() {
|
|
|
10868
10956
|
kind: "tool",
|
|
10869
10957
|
startTime: now,
|
|
10870
10958
|
status: "running",
|
|
10871
|
-
input: toolInput
|
|
10959
|
+
...toolInput !== undefined ? { input: toolInput } : {},
|
|
10960
|
+
...parentSpanId !== null ? { parentSpanId } : {}
|
|
10872
10961
|
}
|
|
10873
10962
|
]
|
|
10874
10963
|
}
|
|
@@ -10884,21 +10973,23 @@ async function handlePostToolUse() {
|
|
|
10884
10973
|
const state = readState(sessionId);
|
|
10885
10974
|
const traceId = state?.traceId ?? `cc-${sessionId}`;
|
|
10886
10975
|
const startTime = state?.startTime ?? now;
|
|
10887
|
-
let
|
|
10976
|
+
let matchedSpanId = null;
|
|
10977
|
+
let matchedSpanStart = now;
|
|
10888
10978
|
let matchedKey = null;
|
|
10889
10979
|
if (state?.openSpans) {
|
|
10890
10980
|
const entries = Object.entries(state.openSpans);
|
|
10891
10981
|
for (let i = entries.length - 1;i >= 0; i--) {
|
|
10892
10982
|
const entry = entries[i];
|
|
10893
10983
|
if (entry && entry[1].toolName === toolName) {
|
|
10894
|
-
|
|
10984
|
+
matchedSpanId = entry[1].spanId;
|
|
10985
|
+
matchedSpanStart = entry[1].startTime;
|
|
10895
10986
|
matchedKey = entry[0];
|
|
10896
10987
|
break;
|
|
10897
10988
|
}
|
|
10898
10989
|
}
|
|
10899
10990
|
}
|
|
10900
|
-
const spanId =
|
|
10901
|
-
const spanStartTime =
|
|
10991
|
+
const spanId = matchedSpanId ?? randomUUID();
|
|
10992
|
+
const spanStartTime = matchedSpanStart;
|
|
10902
10993
|
if (state && matchedKey) {
|
|
10903
10994
|
state.openSpans = Object.fromEntries(Object.entries(state.openSpans).filter(([k]) => k !== matchedKey));
|
|
10904
10995
|
writeState(sessionId, state);
|
|
@@ -10920,29 +11011,365 @@ async function handlePostToolUse() {
|
|
|
10920
11011
|
startTime: spanStartTime,
|
|
10921
11012
|
endTime: now,
|
|
10922
11013
|
status: "completed",
|
|
10923
|
-
output: toolOutput
|
|
11014
|
+
...toolOutput !== undefined ? { output: toolOutput } : {}
|
|
11015
|
+
}
|
|
11016
|
+
]
|
|
11017
|
+
}
|
|
11018
|
+
]);
|
|
11019
|
+
}
|
|
11020
|
+
|
|
11021
|
+
// src/commands/hooks/handlers-v2.ts
|
|
11022
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
11023
|
+
async function handleUserPromptSubmit() {
|
|
11024
|
+
const input = await readStdin();
|
|
11025
|
+
const sessionId = getString(input, "session_id", "sessionId");
|
|
11026
|
+
if (!sessionId)
|
|
11027
|
+
throw new Error("Missing session_id in stdin");
|
|
11028
|
+
const now = new Date().toISOString();
|
|
11029
|
+
const state = readState(sessionId) ?? {
|
|
11030
|
+
traceId: `cc-${sessionId}`,
|
|
11031
|
+
sessionId,
|
|
11032
|
+
startTime: now,
|
|
11033
|
+
openSpans: {},
|
|
11034
|
+
parentStack: [],
|
|
11035
|
+
parentStackKinds: []
|
|
11036
|
+
};
|
|
11037
|
+
const parentSpanId = peekParent(state);
|
|
11038
|
+
const turnSpanId = randomUUID2();
|
|
11039
|
+
const prompt = input.prompt ?? input.message ?? input.text;
|
|
11040
|
+
const inputPayload = serializePayload(prompt ? { prompt } : undefined);
|
|
11041
|
+
pushParent(state, turnSpanId, "chain");
|
|
11042
|
+
writeState(sessionId, state);
|
|
11043
|
+
await sendBatchTrace([
|
|
11044
|
+
{
|
|
11045
|
+
traceId: state.traceId,
|
|
11046
|
+
sessionId,
|
|
11047
|
+
name: "Claude Code Session",
|
|
11048
|
+
source: "sdk",
|
|
11049
|
+
startTime: state.startTime,
|
|
11050
|
+
status: "running",
|
|
11051
|
+
spans: [
|
|
11052
|
+
{
|
|
11053
|
+
spanId: turnSpanId,
|
|
11054
|
+
name: "User turn",
|
|
11055
|
+
kind: "chain",
|
|
11056
|
+
startTime: now,
|
|
11057
|
+
status: "running",
|
|
11058
|
+
...inputPayload !== undefined ? { input: inputPayload } : {},
|
|
11059
|
+
...parentSpanId !== null ? { parentSpanId } : {}
|
|
11060
|
+
}
|
|
11061
|
+
]
|
|
11062
|
+
}
|
|
11063
|
+
]);
|
|
11064
|
+
}
|
|
11065
|
+
async function handleStop() {
|
|
11066
|
+
const input = await readStdin();
|
|
11067
|
+
const sessionId = getString(input, "session_id", "sessionId");
|
|
11068
|
+
if (!sessionId)
|
|
11069
|
+
throw new Error("Missing session_id in stdin");
|
|
11070
|
+
const now = new Date().toISOString();
|
|
11071
|
+
const state = readState(sessionId);
|
|
11072
|
+
if (!state)
|
|
11073
|
+
return;
|
|
11074
|
+
if (state.parentStack.length <= 1)
|
|
11075
|
+
return;
|
|
11076
|
+
const topKind = peekParentKind(state);
|
|
11077
|
+
if (topKind !== "chain") {
|
|
11078
|
+
process.stderr.write(`[mutagent hooks] Warning: Stop fired but stack top is kind="${topKind ?? "unknown"}" (expected "chain"). Skipping pop.
|
|
11079
|
+
`);
|
|
11080
|
+
return;
|
|
11081
|
+
}
|
|
11082
|
+
const turnSpanId = popParent(state);
|
|
11083
|
+
writeState(sessionId, state);
|
|
11084
|
+
if (!turnSpanId)
|
|
11085
|
+
return;
|
|
11086
|
+
await sendBatchTrace([
|
|
11087
|
+
{
|
|
11088
|
+
traceId: state.traceId,
|
|
11089
|
+
sessionId,
|
|
11090
|
+
name: "Claude Code Session",
|
|
11091
|
+
source: "sdk",
|
|
11092
|
+
startTime: state.startTime,
|
|
11093
|
+
status: "running",
|
|
11094
|
+
spans: [
|
|
11095
|
+
{
|
|
11096
|
+
spanId: turnSpanId,
|
|
11097
|
+
name: "User turn",
|
|
11098
|
+
kind: "chain",
|
|
11099
|
+
startTime: now,
|
|
11100
|
+
endTime: now,
|
|
11101
|
+
status: "completed",
|
|
11102
|
+
parentSpanId: peekParent(state)
|
|
11103
|
+
}
|
|
11104
|
+
]
|
|
11105
|
+
}
|
|
11106
|
+
]);
|
|
11107
|
+
}
|
|
11108
|
+
async function handleSubagentStart() {
|
|
11109
|
+
const input = await readStdin();
|
|
11110
|
+
const sessionId = getString(input, "session_id", "sessionId");
|
|
11111
|
+
if (!sessionId)
|
|
11112
|
+
throw new Error("Missing session_id in stdin");
|
|
11113
|
+
const now = new Date().toISOString();
|
|
11114
|
+
const state = readState(sessionId) ?? {
|
|
11115
|
+
traceId: `cc-${sessionId}`,
|
|
11116
|
+
sessionId,
|
|
11117
|
+
startTime: now,
|
|
11118
|
+
openSpans: {},
|
|
11119
|
+
parentStack: [],
|
|
11120
|
+
parentStackKinds: []
|
|
11121
|
+
};
|
|
11122
|
+
const parentSpanId = peekParent(state);
|
|
11123
|
+
const agentSpanId = randomUUID2();
|
|
11124
|
+
const agentType = getString(input, "agent_type", "agentType") || "subagent";
|
|
11125
|
+
const agentId = getString(input, "agent_id", "agentId");
|
|
11126
|
+
const agentName = agentId ? `${agentType}:${agentId}` : agentType;
|
|
11127
|
+
pushParent(state, agentSpanId, "agent");
|
|
11128
|
+
writeState(sessionId, state);
|
|
11129
|
+
await sendBatchTrace([
|
|
11130
|
+
{
|
|
11131
|
+
traceId: state.traceId,
|
|
11132
|
+
sessionId,
|
|
11133
|
+
name: "Claude Code Session",
|
|
11134
|
+
source: "sdk",
|
|
11135
|
+
startTime: state.startTime,
|
|
11136
|
+
status: "running",
|
|
11137
|
+
spans: [
|
|
11138
|
+
{
|
|
11139
|
+
spanId: agentSpanId,
|
|
11140
|
+
name: agentName,
|
|
11141
|
+
kind: "agent",
|
|
11142
|
+
startTime: now,
|
|
11143
|
+
status: "running",
|
|
11144
|
+
...parentSpanId !== null ? { parentSpanId } : {}
|
|
11145
|
+
}
|
|
11146
|
+
]
|
|
11147
|
+
}
|
|
11148
|
+
]);
|
|
11149
|
+
}
|
|
11150
|
+
async function handleSubagentStop() {
|
|
11151
|
+
const input = await readStdin();
|
|
11152
|
+
const sessionId = getString(input, "session_id", "sessionId");
|
|
11153
|
+
if (!sessionId)
|
|
11154
|
+
throw new Error("Missing session_id in stdin");
|
|
11155
|
+
const now = new Date().toISOString();
|
|
11156
|
+
const state = readState(sessionId);
|
|
11157
|
+
if (!state) {
|
|
11158
|
+
process.stderr.write(`[mutagent hooks] Warning: SubagentStop received but no state found for session ${sessionId}
|
|
11159
|
+
`);
|
|
11160
|
+
return;
|
|
11161
|
+
}
|
|
11162
|
+
if (state.parentStack.length <= 1) {
|
|
11163
|
+
process.stderr.write(`[mutagent hooks] Warning: SubagentStop with no matching SubagentStart in session ${sessionId}
|
|
11164
|
+
`);
|
|
11165
|
+
return;
|
|
11166
|
+
}
|
|
11167
|
+
const topKind = peekParentKind(state);
|
|
11168
|
+
if (topKind !== "agent") {
|
|
11169
|
+
process.stderr.write(`[mutagent hooks] Warning: SubagentStop but stack top is kind="${topKind ?? "unknown"}" (expected "agent"). Skipping pop.
|
|
11170
|
+
`);
|
|
11171
|
+
return;
|
|
11172
|
+
}
|
|
11173
|
+
const agentSpanId = popParent(state);
|
|
11174
|
+
const newParent = peekParent(state);
|
|
11175
|
+
writeState(sessionId, state);
|
|
11176
|
+
if (!agentSpanId)
|
|
11177
|
+
return;
|
|
11178
|
+
await sendBatchTrace([
|
|
11179
|
+
{
|
|
11180
|
+
traceId: state.traceId,
|
|
11181
|
+
sessionId,
|
|
11182
|
+
name: "Claude Code Session",
|
|
11183
|
+
source: "sdk",
|
|
11184
|
+
startTime: state.startTime,
|
|
11185
|
+
status: "running",
|
|
11186
|
+
spans: [
|
|
11187
|
+
{
|
|
11188
|
+
spanId: agentSpanId,
|
|
11189
|
+
name: "subagent",
|
|
11190
|
+
kind: "agent",
|
|
11191
|
+
startTime: now,
|
|
11192
|
+
endTime: now,
|
|
11193
|
+
status: "completed",
|
|
11194
|
+
...newParent !== null ? { parentSpanId: newParent } : {}
|
|
11195
|
+
}
|
|
11196
|
+
]
|
|
11197
|
+
}
|
|
11198
|
+
]);
|
|
11199
|
+
}
|
|
11200
|
+
async function handlePreCompact() {
|
|
11201
|
+
const input = await readStdin();
|
|
11202
|
+
const sessionId = getString(input, "session_id", "sessionId");
|
|
11203
|
+
if (!sessionId)
|
|
11204
|
+
throw new Error("Missing session_id in stdin");
|
|
11205
|
+
const now = new Date().toISOString();
|
|
11206
|
+
const state = readState(sessionId) ?? {
|
|
11207
|
+
traceId: `cc-${sessionId}`,
|
|
11208
|
+
sessionId,
|
|
11209
|
+
startTime: now,
|
|
11210
|
+
openSpans: {},
|
|
11211
|
+
parentStack: [],
|
|
11212
|
+
parentStackKinds: []
|
|
11213
|
+
};
|
|
11214
|
+
const parentSpanId = peekParent(state);
|
|
11215
|
+
const compactSpanId = randomUUID2();
|
|
11216
|
+
pushParent(state, compactSpanId, "custom");
|
|
11217
|
+
writeState(sessionId, state);
|
|
11218
|
+
await sendBatchTrace([
|
|
11219
|
+
{
|
|
11220
|
+
traceId: state.traceId,
|
|
11221
|
+
sessionId,
|
|
11222
|
+
name: "Claude Code Session",
|
|
11223
|
+
source: "sdk",
|
|
11224
|
+
startTime: state.startTime,
|
|
11225
|
+
status: "running",
|
|
11226
|
+
spans: [
|
|
11227
|
+
{
|
|
11228
|
+
spanId: compactSpanId,
|
|
11229
|
+
name: "Compaction",
|
|
11230
|
+
kind: "custom",
|
|
11231
|
+
startTime: now,
|
|
11232
|
+
status: "running",
|
|
11233
|
+
...parentSpanId !== null ? { parentSpanId } : {}
|
|
11234
|
+
}
|
|
11235
|
+
]
|
|
11236
|
+
}
|
|
11237
|
+
]);
|
|
11238
|
+
}
|
|
11239
|
+
async function handlePostCompact() {
|
|
11240
|
+
const input = await readStdin();
|
|
11241
|
+
const sessionId = getString(input, "session_id", "sessionId");
|
|
11242
|
+
if (!sessionId)
|
|
11243
|
+
throw new Error("Missing session_id in stdin");
|
|
11244
|
+
const now = new Date().toISOString();
|
|
11245
|
+
const state = readState(sessionId);
|
|
11246
|
+
if (!state) {
|
|
11247
|
+
process.stderr.write(`[mutagent hooks] Warning: PostCompact received but no state found for session ${sessionId}
|
|
11248
|
+
`);
|
|
11249
|
+
return;
|
|
11250
|
+
}
|
|
11251
|
+
if (state.parentStack.length <= 1) {
|
|
11252
|
+
process.stderr.write(`[mutagent hooks] Warning: PostCompact with no matching PreCompact in session ${sessionId}
|
|
11253
|
+
`);
|
|
11254
|
+
return;
|
|
11255
|
+
}
|
|
11256
|
+
const compactSpanId = popParent(state);
|
|
11257
|
+
const newParent = peekParent(state);
|
|
11258
|
+
writeState(sessionId, state);
|
|
11259
|
+
if (!compactSpanId)
|
|
11260
|
+
return;
|
|
11261
|
+
const summary = input.summary ?? input.compaction_summary ?? input.compactionSummary;
|
|
11262
|
+
const outputPayload = serializePayload(summary ?? "Context compacted");
|
|
11263
|
+
await sendBatchTrace([
|
|
11264
|
+
{
|
|
11265
|
+
traceId: state.traceId,
|
|
11266
|
+
sessionId,
|
|
11267
|
+
name: "Claude Code Session",
|
|
11268
|
+
source: "sdk",
|
|
11269
|
+
startTime: state.startTime,
|
|
11270
|
+
status: "running",
|
|
11271
|
+
spans: [
|
|
11272
|
+
{
|
|
11273
|
+
spanId: compactSpanId,
|
|
11274
|
+
name: "Compaction",
|
|
11275
|
+
kind: "custom",
|
|
11276
|
+
startTime: now,
|
|
11277
|
+
endTime: now,
|
|
11278
|
+
status: "completed",
|
|
11279
|
+
...outputPayload !== undefined ? { output: outputPayload } : {},
|
|
11280
|
+
...newParent !== null ? { parentSpanId: newParent } : {}
|
|
10924
11281
|
}
|
|
10925
11282
|
]
|
|
10926
11283
|
}
|
|
10927
11284
|
]);
|
|
10928
11285
|
}
|
|
11286
|
+
async function handlePostToolUseFailure() {
|
|
11287
|
+
const input = await readStdin();
|
|
11288
|
+
const sessionId = getString(input, "session_id", "sessionId");
|
|
11289
|
+
const toolName = getString(input, "tool_name", "toolName") || "unknown";
|
|
11290
|
+
if (!sessionId)
|
|
11291
|
+
throw new Error("Missing session_id in stdin");
|
|
11292
|
+
const now = new Date().toISOString();
|
|
11293
|
+
const state = readState(sessionId);
|
|
11294
|
+
if (!state) {
|
|
11295
|
+
process.stderr.write(`[mutagent hooks] Warning: PostToolUseFailure received but no state found for session ${sessionId}
|
|
11296
|
+
`);
|
|
11297
|
+
return;
|
|
11298
|
+
}
|
|
11299
|
+
const traceId = state.traceId;
|
|
11300
|
+
const startTime = state.startTime;
|
|
11301
|
+
let matchedSpanId = null;
|
|
11302
|
+
let matchedSpanStart = now;
|
|
11303
|
+
let matchedKey = null;
|
|
11304
|
+
const entries = Object.entries(state.openSpans);
|
|
11305
|
+
for (let i = entries.length - 1;i >= 0; i--) {
|
|
11306
|
+
const entry = entries[i];
|
|
11307
|
+
if (entry && entry[1].toolName === toolName) {
|
|
11308
|
+
matchedSpanId = entry[1].spanId;
|
|
11309
|
+
matchedSpanStart = entry[1].startTime;
|
|
11310
|
+
matchedKey = entry[0];
|
|
11311
|
+
break;
|
|
11312
|
+
}
|
|
11313
|
+
}
|
|
11314
|
+
if (!matchedSpanId) {
|
|
11315
|
+
process.stderr.write(`[mutagent hooks] Warning: PostToolUseFailure — no matching open span for tool "${toolName}"
|
|
11316
|
+
`);
|
|
11317
|
+
return;
|
|
11318
|
+
}
|
|
11319
|
+
state.openSpans = Object.fromEntries(Object.entries(state.openSpans).filter(([k]) => k !== matchedKey));
|
|
11320
|
+
writeState(sessionId, state);
|
|
11321
|
+
const reason = getString(input, "error", "reason", "message") || "Tool use failed";
|
|
11322
|
+
const errorPayload = serializePayload(reason);
|
|
11323
|
+
await sendBatchTrace([
|
|
11324
|
+
{
|
|
11325
|
+
traceId,
|
|
11326
|
+
sessionId,
|
|
11327
|
+
name: "Claude Code Session",
|
|
11328
|
+
source: "sdk",
|
|
11329
|
+
startTime,
|
|
11330
|
+
status: "running",
|
|
11331
|
+
spans: [
|
|
11332
|
+
{
|
|
11333
|
+
spanId: matchedSpanId,
|
|
11334
|
+
name: toolName,
|
|
11335
|
+
kind: "tool",
|
|
11336
|
+
startTime: matchedSpanStart,
|
|
11337
|
+
endTime: now,
|
|
11338
|
+
status: "error",
|
|
11339
|
+
...errorPayload !== undefined ? { output: errorPayload } : {}
|
|
11340
|
+
}
|
|
11341
|
+
]
|
|
11342
|
+
}
|
|
11343
|
+
]);
|
|
11344
|
+
}
|
|
11345
|
+
|
|
11346
|
+
// src/commands/hooks/install.ts
|
|
11347
|
+
import { readFileSync as readFileSync11, writeFileSync as writeFileSync7, existsSync as existsSync14, mkdirSync as mkdirSync5 } from "fs";
|
|
11348
|
+
import { join as join9 } from "path";
|
|
10929
11349
|
var MUTAGENT_HOOKS = {
|
|
10930
11350
|
SessionStart: [{ matcher: "startup", hooks: [{ type: "command", command: "mutagent hooks claude-code session-start" }] }],
|
|
10931
|
-
Stop: [{ hooks: [{ type: "command", command: "mutagent hooks claude-code session-end" }] }],
|
|
10932
11351
|
PreToolUse: [{ hooks: [{ type: "command", command: "mutagent hooks claude-code pre-tool-use" }] }],
|
|
10933
|
-
PostToolUse: [{ hooks: [{ type: "command", command: "mutagent hooks claude-code post-tool-use" }] }]
|
|
11352
|
+
PostToolUse: [{ hooks: [{ type: "command", command: "mutagent hooks claude-code post-tool-use" }] }],
|
|
11353
|
+
Stop: [{ hooks: [{ type: "command", command: "mutagent hooks claude-code stop" }] }],
|
|
11354
|
+
SessionEnd: [{ hooks: [{ type: "command", command: "mutagent hooks claude-code session-end" }] }],
|
|
11355
|
+
UserPromptSubmit: [{ hooks: [{ type: "command", command: "mutagent hooks claude-code user-prompt-submit" }] }],
|
|
11356
|
+
SubagentStart: [{ hooks: [{ type: "command", command: "mutagent hooks claude-code subagent-start" }] }],
|
|
11357
|
+
SubagentStop: [{ hooks: [{ type: "command", command: "mutagent hooks claude-code subagent-stop" }] }],
|
|
11358
|
+
PreCompact: [{ hooks: [{ type: "command", command: "mutagent hooks claude-code pre-compact" }] }],
|
|
11359
|
+
PostCompact: [{ hooks: [{ type: "command", command: "mutagent hooks claude-code post-compact" }] }],
|
|
11360
|
+
PostToolUseFailure: [{ hooks: [{ type: "command", command: "mutagent hooks claude-code post-tool-use-failure" }] }]
|
|
10934
11361
|
};
|
|
10935
11362
|
function hasCommand(matchers, command) {
|
|
10936
11363
|
return matchers.some((m) => m.hooks.some((h) => h.command === command));
|
|
10937
11364
|
}
|
|
10938
11365
|
function installHooks(cwd) {
|
|
10939
|
-
const claudeDir =
|
|
10940
|
-
const settingsPath =
|
|
10941
|
-
const existed =
|
|
11366
|
+
const claudeDir = join9(cwd, ".claude");
|
|
11367
|
+
const settingsPath = join9(claudeDir, "settings.local.json");
|
|
11368
|
+
const existed = existsSync14(settingsPath);
|
|
10942
11369
|
let settings = {};
|
|
10943
11370
|
if (existed) {
|
|
10944
11371
|
try {
|
|
10945
|
-
settings = JSON.parse(
|
|
11372
|
+
settings = JSON.parse(readFileSync11(settingsPath, "utf-8"));
|
|
10946
11373
|
} catch {
|
|
10947
11374
|
settings = {};
|
|
10948
11375
|
}
|
|
@@ -10971,14 +11398,16 @@ function installHooks(cwd) {
|
|
|
10971
11398
|
}
|
|
10972
11399
|
}
|
|
10973
11400
|
if (added.length > 0) {
|
|
10974
|
-
if (!
|
|
11401
|
+
if (!existsSync14(claudeDir)) {
|
|
10975
11402
|
mkdirSync5(claudeDir, { recursive: true });
|
|
10976
11403
|
}
|
|
10977
|
-
|
|
11404
|
+
writeFileSync7(settingsPath, JSON.stringify(settings, null, 2) + `
|
|
10978
11405
|
`, "utf-8");
|
|
10979
11406
|
}
|
|
10980
11407
|
return { settingsPath, existed, added, alreadyPresent };
|
|
10981
11408
|
}
|
|
11409
|
+
|
|
11410
|
+
// src/commands/hooks/index.ts
|
|
10982
11411
|
function createHooksCommand() {
|
|
10983
11412
|
const hooks = new Command19("hooks").description("Hook handlers for AI coding assistants").addHelpText("after", `
|
|
10984
11413
|
Claude Code Session Telemetry:
|
|
@@ -10991,17 +11420,23 @@ Claude Code Session Telemetry:
|
|
|
10991
11420
|
"SessionStart": [{"matcher": "startup", "hooks": [{"type": "command", "command": "mutagent hooks claude-code session-start"}]}],
|
|
10992
11421
|
"Stop": [{"hooks": [{"type": "command", "command": "mutagent hooks claude-code session-end"}]}],
|
|
10993
11422
|
"PreToolUse": [{"hooks": [{"type": "command", "command": "mutagent hooks claude-code pre-tool-use"}]}],
|
|
10994
|
-
"PostToolUse": [{"hooks": [{"type": "command", "command": "mutagent hooks claude-code post-tool-use"}]}]
|
|
11423
|
+
"PostToolUse": [{"hooks": [{"type": "command", "command": "mutagent hooks claude-code post-tool-use"}]}],
|
|
11424
|
+
"UserPromptSubmit": [{"hooks": [{"type": "command", "command": "mutagent hooks claude-code user-prompt-submit"}]}],
|
|
11425
|
+
"SubagentStart": [{"hooks": [{"type": "command", "command": "mutagent hooks claude-code subagent-start"}]}],
|
|
11426
|
+
"SubagentStop": [{"hooks": [{"type": "command", "command": "mutagent hooks claude-code subagent-stop"}]}],
|
|
11427
|
+
"PreCompact": [{"hooks": [{"type": "command", "command": "mutagent hooks claude-code pre-compact"}]}],
|
|
11428
|
+
"PostCompact": [{"hooks": [{"type": "command", "command": "mutagent hooks claude-code post-compact"}]}],
|
|
11429
|
+
"PostToolUseFailure": [{"hooks": [{"type": "command", "command": "mutagent hooks claude-code post-tool-use-failure"}]}]
|
|
10995
11430
|
}
|
|
10996
11431
|
}
|
|
10997
11432
|
|
|
10998
|
-
Or run: mutagent
|
|
11433
|
+
Or run: mutagent hooks install
|
|
10999
11434
|
`);
|
|
11000
11435
|
hooks.command("install").description("Install MutagenT hooks into .claude/settings.local.json (safe merge — never overwrites existing hooks)").option("--cwd <dir>", "Target directory (defaults to cwd)", process.cwd()).addHelpText("after", `
|
|
11001
11436
|
Reads existing .claude/settings.local.json (if present) and deep-merges
|
|
11002
|
-
MutagenT telemetry hooks into each event array (
|
|
11003
|
-
|
|
11004
|
-
|
|
11437
|
+
MutagenT telemetry hooks into each event array (all 10 events). Skips any
|
|
11438
|
+
hook already present (checked by command string) so running this multiple
|
|
11439
|
+
times is safe.
|
|
11005
11440
|
`).action((opts) => {
|
|
11006
11441
|
const targetDir = opts.cwd ?? process.cwd();
|
|
11007
11442
|
const result = installHooks(targetDir);
|
|
@@ -11039,6 +11474,27 @@ command string) so running this multiple times is safe.
|
|
|
11039
11474
|
claudeCode.command("post-tool-use").description("Handle post-tool-use event").action(async () => {
|
|
11040
11475
|
await safeExecute(handlePostToolUse);
|
|
11041
11476
|
});
|
|
11477
|
+
claudeCode.command("user-prompt-submit").description("Handle user prompt submit event (creates turn span)").action(async () => {
|
|
11478
|
+
await safeExecute(handleUserPromptSubmit);
|
|
11479
|
+
});
|
|
11480
|
+
claudeCode.command("stop").description("Handle stop event (closes current turn span)").action(async () => {
|
|
11481
|
+
await safeExecute(handleStop);
|
|
11482
|
+
});
|
|
11483
|
+
claudeCode.command("subagent-start").description("Handle subagent start event (creates nested agent span)").action(async () => {
|
|
11484
|
+
await safeExecute(handleSubagentStart);
|
|
11485
|
+
});
|
|
11486
|
+
claudeCode.command("subagent-stop").description("Handle subagent stop event (closes subagent span)").action(async () => {
|
|
11487
|
+
await safeExecute(handleSubagentStop);
|
|
11488
|
+
});
|
|
11489
|
+
claudeCode.command("pre-compact").description("Handle pre-compact event (opens compaction span)").action(async () => {
|
|
11490
|
+
await safeExecute(handlePreCompact);
|
|
11491
|
+
});
|
|
11492
|
+
claudeCode.command("post-compact").description("Handle post-compact event (closes compaction span with summary)").action(async () => {
|
|
11493
|
+
await safeExecute(handlePostCompact);
|
|
11494
|
+
});
|
|
11495
|
+
claudeCode.command("post-tool-use-failure").description("Handle post-tool-use-failure event (closes failed span with error status)").action(async () => {
|
|
11496
|
+
await safeExecute(handlePostToolUseFailure);
|
|
11497
|
+
});
|
|
11042
11498
|
return hooks;
|
|
11043
11499
|
}
|
|
11044
11500
|
|
|
@@ -11047,8 +11503,8 @@ import { Command as Command20 } from "commander";
|
|
|
11047
11503
|
import chalk38 from "chalk";
|
|
11048
11504
|
init_errors();
|
|
11049
11505
|
init_config();
|
|
11050
|
-
import { readFileSync as
|
|
11051
|
-
import { join as
|
|
11506
|
+
import { readFileSync as readFileSync12 } from "fs";
|
|
11507
|
+
import { join as join10, dirname as dirname2 } from "path";
|
|
11052
11508
|
import { fileURLToPath } from "url";
|
|
11053
11509
|
var VALID_CATEGORIES = ["bug", "feature", "improvement", "praise"];
|
|
11054
11510
|
function getCliVersion() {
|
|
@@ -11057,8 +11513,8 @@ function getCliVersion() {
|
|
|
11057
11513
|
}
|
|
11058
11514
|
try {
|
|
11059
11515
|
const __dirname2 = dirname2(fileURLToPath(import.meta.url));
|
|
11060
|
-
const pkgPath =
|
|
11061
|
-
const pkg = JSON.parse(
|
|
11516
|
+
const pkgPath = join10(__dirname2, "..", "..", "package.json");
|
|
11517
|
+
const pkg = JSON.parse(readFileSync12(pkgPath, "utf-8"));
|
|
11062
11518
|
return pkg.version ?? "0.1.1";
|
|
11063
11519
|
} catch {
|
|
11064
11520
|
return "0.1.1";
|
|
@@ -11183,8 +11639,8 @@ if (process.env.CLI_VERSION) {
|
|
|
11183
11639
|
} else {
|
|
11184
11640
|
try {
|
|
11185
11641
|
const __dirname2 = dirname3(fileURLToPath2(import.meta.url));
|
|
11186
|
-
const pkgPath =
|
|
11187
|
-
const pkg = JSON.parse(
|
|
11642
|
+
const pkgPath = join11(__dirname2, "..", "..", "package.json");
|
|
11643
|
+
const pkg = JSON.parse(readFileSync13(pkgPath, "utf-8"));
|
|
11188
11644
|
cliVersion = pkg.version ?? cliVersion;
|
|
11189
11645
|
} catch {}
|
|
11190
11646
|
}
|
|
@@ -11353,12 +11809,12 @@ program.addCommand(createFeedbackCommand());
|
|
|
11353
11809
|
var isInteractive = process.stdin.isTTY && !rawArgs.includes("--json") && process.env.CI !== "true";
|
|
11354
11810
|
var isSkillCommand = rawArgs[0] === "skills" || rawArgs[0] === "hooks";
|
|
11355
11811
|
if (isInteractive && !isSkillCommand) {
|
|
11356
|
-
const skillPath =
|
|
11357
|
-
if (!
|
|
11812
|
+
const skillPath = join11(process.cwd(), ".claude/skills/mutagent-cli/SKILL.md");
|
|
11813
|
+
if (!existsSync15(skillPath)) {
|
|
11358
11814
|
console.log(chalk39.dim("MutagenT SKILL not installed. Install it for AI agent support? Run:"), chalk39.cyan("mutagent skills install"));
|
|
11359
11815
|
}
|
|
11360
11816
|
}
|
|
11361
11817
|
program.parse();
|
|
11362
11818
|
|
|
11363
|
-
//# debugId=
|
|
11819
|
+
//# debugId=599B825650E0CCDF64756E2164756E21
|
|
11364
11820
|
//# sourceMappingURL=cli.js.map
|