@mutagent/cli 0.1.131 → 0.1.133

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 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 readFileSync12, existsSync as existsSync14 } from "fs";
1247
- import { join as join10, dirname as dirname3 } from "path";
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
- return JSON.parse(readFileSync10(path, "utf-8"));
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
- writeFileSync6(stateFilePath(sessionId), JSON.stringify(state), "utf-8");
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 matchedSpan = null;
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
- matchedSpan = entry[1];
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 = matchedSpan?.spanId ?? randomUUID();
10901
- const spanStartTime = matchedSpan?.startTime ?? now;
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,35 +11011,401 @@ 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 } : {}
10924
11234
  }
10925
11235
  ]
10926
11236
  }
10927
11237
  ]);
10928
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 } : {}
11281
+ }
11282
+ ]
11283
+ }
11284
+ ]);
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";
11349
+ var V1_MIGRATIONS = {
11350
+ Stop: ["mutagent hooks claude-code session-end"]
11351
+ };
10929
11352
  var MUTAGENT_HOOKS = {
10930
11353
  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
11354
  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" }] }]
11355
+ PostToolUse: [{ hooks: [{ type: "command", command: "mutagent hooks claude-code post-tool-use" }] }],
11356
+ Stop: [{ hooks: [{ type: "command", command: "mutagent hooks claude-code stop" }] }],
11357
+ SessionEnd: [{ hooks: [{ type: "command", command: "mutagent hooks claude-code session-end" }] }],
11358
+ UserPromptSubmit: [{ hooks: [{ type: "command", command: "mutagent hooks claude-code user-prompt-submit" }] }],
11359
+ SubagentStart: [{ hooks: [{ type: "command", command: "mutagent hooks claude-code subagent-start" }] }],
11360
+ SubagentStop: [{ hooks: [{ type: "command", command: "mutagent hooks claude-code subagent-stop" }] }],
11361
+ PreCompact: [{ hooks: [{ type: "command", command: "mutagent hooks claude-code pre-compact" }] }],
11362
+ PostCompact: [{ hooks: [{ type: "command", command: "mutagent hooks claude-code post-compact" }] }],
11363
+ PostToolUseFailure: [{ hooks: [{ type: "command", command: "mutagent hooks claude-code post-tool-use-failure" }] }]
10934
11364
  };
10935
11365
  function hasCommand(matchers, command) {
10936
11366
  return matchers.some((m) => m.hooks.some((h) => h.command === command));
10937
11367
  }
11368
+ function migrateV1Hooks(settings) {
11369
+ const migrated = [];
11370
+ if (!settings.hooks)
11371
+ return migrated;
11372
+ for (const [event, commandsToRemove] of Object.entries(V1_MIGRATIONS)) {
11373
+ const matchers = settings.hooks[event];
11374
+ if (!matchers)
11375
+ continue;
11376
+ const filtered = matchers.map((matcher) => {
11377
+ const keptHooks = matcher.hooks.filter((h) => {
11378
+ if (commandsToRemove.includes(h.command)) {
11379
+ migrated.push({ event, command: h.command });
11380
+ return false;
11381
+ }
11382
+ return true;
11383
+ });
11384
+ return keptHooks.length > 0 ? { ...matcher, hooks: keptHooks } : null;
11385
+ }).filter(Boolean);
11386
+ if (filtered.length === 0) {
11387
+ delete settings.hooks[event];
11388
+ } else {
11389
+ settings.hooks[event] = filtered;
11390
+ }
11391
+ }
11392
+ return migrated;
11393
+ }
10938
11394
  function installHooks(cwd) {
10939
- const claudeDir = join8(cwd, ".claude");
10940
- const settingsPath = join8(claudeDir, "settings.local.json");
10941
- const existed = existsSync13(settingsPath);
11395
+ const claudeDir = join9(cwd, ".claude");
11396
+ const settingsPath = join9(claudeDir, "settings.local.json");
11397
+ const existed = existsSync14(settingsPath);
10942
11398
  let settings = {};
10943
11399
  if (existed) {
10944
11400
  try {
10945
- settings = JSON.parse(readFileSync10(settingsPath, "utf-8"));
11401
+ settings = JSON.parse(readFileSync11(settingsPath, "utf-8"));
10946
11402
  } catch {
10947
11403
  settings = {};
10948
11404
  }
10949
11405
  }
10950
11406
  const added = [];
10951
11407
  const alreadyPresent = [];
11408
+ const migrated = migrateV1Hooks(settings);
10952
11409
  for (const [event, newMatchers] of Object.entries(MUTAGENT_HOOKS)) {
10953
11410
  if (!newMatchers)
10954
11411
  continue;
@@ -10970,15 +11427,17 @@ function installHooks(cwd) {
10970
11427
  }
10971
11428
  }
10972
11429
  }
10973
- if (added.length > 0) {
10974
- if (!existsSync13(claudeDir)) {
11430
+ if (added.length > 0 || migrated.length > 0) {
11431
+ if (!existsSync14(claudeDir)) {
10975
11432
  mkdirSync5(claudeDir, { recursive: true });
10976
11433
  }
10977
- writeFileSync6(settingsPath, JSON.stringify(settings, null, 2) + `
11434
+ writeFileSync7(settingsPath, JSON.stringify(settings, null, 2) + `
10978
11435
  `, "utf-8");
10979
11436
  }
10980
- return { settingsPath, existed, added, alreadyPresent };
11437
+ return { settingsPath, existed, added, alreadyPresent, migrated };
10981
11438
  }
11439
+
11440
+ // src/commands/hooks/index.ts
10982
11441
  function createHooksCommand() {
10983
11442
  const hooks = new Command19("hooks").description("Hook handlers for AI coding assistants").addHelpText("after", `
10984
11443
  Claude Code Session Telemetry:
@@ -10991,20 +11450,30 @@ Claude Code Session Telemetry:
10991
11450
  "SessionStart": [{"matcher": "startup", "hooks": [{"type": "command", "command": "mutagent hooks claude-code session-start"}]}],
10992
11451
  "Stop": [{"hooks": [{"type": "command", "command": "mutagent hooks claude-code session-end"}]}],
10993
11452
  "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"}]}]
11453
+ "PostToolUse": [{"hooks": [{"type": "command", "command": "mutagent hooks claude-code post-tool-use"}]}],
11454
+ "UserPromptSubmit": [{"hooks": [{"type": "command", "command": "mutagent hooks claude-code user-prompt-submit"}]}],
11455
+ "SubagentStart": [{"hooks": [{"type": "command", "command": "mutagent hooks claude-code subagent-start"}]}],
11456
+ "SubagentStop": [{"hooks": [{"type": "command", "command": "mutagent hooks claude-code subagent-stop"}]}],
11457
+ "PreCompact": [{"hooks": [{"type": "command", "command": "mutagent hooks claude-code pre-compact"}]}],
11458
+ "PostCompact": [{"hooks": [{"type": "command", "command": "mutagent hooks claude-code post-compact"}]}],
11459
+ "PostToolUseFailure": [{"hooks": [{"type": "command", "command": "mutagent hooks claude-code post-tool-use-failure"}]}]
10995
11460
  }
10996
11461
  }
10997
11462
 
10998
- Or run: mutagent init (option 1 installs skill + hooks together)
11463
+ Or run: mutagent hooks install
10999
11464
  `);
11000
11465
  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
11466
  Reads existing .claude/settings.local.json (if present) and deep-merges
11002
- MutagenT telemetry hooks into each event array (SessionStart, Stop,
11003
- PreToolUse, PostToolUse). Skips any hook already present (checked by
11004
- command string) so running this multiple times is safe.
11467
+ MutagenT telemetry hooks into each event array (all 10 events). Skips any
11468
+ hook already present (checked by command string) so running this multiple
11469
+ times is safe.
11005
11470
  `).action((opts) => {
11006
11471
  const targetDir = opts.cwd ?? process.cwd();
11007
11472
  const result = installHooks(targetDir);
11473
+ for (const { event, command } of result.migrated) {
11474
+ process.stderr.write(`[mutagent hooks install] ⚠️ Migrated v1 hook: removed '${event} → ${command}' (v2 wires this as 'SessionEnd')
11475
+ `);
11476
+ }
11008
11477
  if (result.added.length === 0 && result.alreadyPresent.length === 0) {
11009
11478
  process.stdout.write(JSON.stringify({
11010
11479
  success: true,
@@ -11039,6 +11508,27 @@ command string) so running this multiple times is safe.
11039
11508
  claudeCode.command("post-tool-use").description("Handle post-tool-use event").action(async () => {
11040
11509
  await safeExecute(handlePostToolUse);
11041
11510
  });
11511
+ claudeCode.command("user-prompt-submit").description("Handle user prompt submit event (creates turn span)").action(async () => {
11512
+ await safeExecute(handleUserPromptSubmit);
11513
+ });
11514
+ claudeCode.command("stop").description("Handle stop event (closes current turn span)").action(async () => {
11515
+ await safeExecute(handleStop);
11516
+ });
11517
+ claudeCode.command("subagent-start").description("Handle subagent start event (creates nested agent span)").action(async () => {
11518
+ await safeExecute(handleSubagentStart);
11519
+ });
11520
+ claudeCode.command("subagent-stop").description("Handle subagent stop event (closes subagent span)").action(async () => {
11521
+ await safeExecute(handleSubagentStop);
11522
+ });
11523
+ claudeCode.command("pre-compact").description("Handle pre-compact event (opens compaction span)").action(async () => {
11524
+ await safeExecute(handlePreCompact);
11525
+ });
11526
+ claudeCode.command("post-compact").description("Handle post-compact event (closes compaction span with summary)").action(async () => {
11527
+ await safeExecute(handlePostCompact);
11528
+ });
11529
+ claudeCode.command("post-tool-use-failure").description("Handle post-tool-use-failure event (closes failed span with error status)").action(async () => {
11530
+ await safeExecute(handlePostToolUseFailure);
11531
+ });
11042
11532
  return hooks;
11043
11533
  }
11044
11534
 
@@ -11047,8 +11537,8 @@ import { Command as Command20 } from "commander";
11047
11537
  import chalk38 from "chalk";
11048
11538
  init_errors();
11049
11539
  init_config();
11050
- import { readFileSync as readFileSync11 } from "fs";
11051
- import { join as join9, dirname as dirname2 } from "path";
11540
+ import { readFileSync as readFileSync12 } from "fs";
11541
+ import { join as join10, dirname as dirname2 } from "path";
11052
11542
  import { fileURLToPath } from "url";
11053
11543
  var VALID_CATEGORIES = ["bug", "feature", "improvement", "praise"];
11054
11544
  function getCliVersion() {
@@ -11057,8 +11547,8 @@ function getCliVersion() {
11057
11547
  }
11058
11548
  try {
11059
11549
  const __dirname2 = dirname2(fileURLToPath(import.meta.url));
11060
- const pkgPath = join9(__dirname2, "..", "..", "package.json");
11061
- const pkg = JSON.parse(readFileSync11(pkgPath, "utf-8"));
11550
+ const pkgPath = join10(__dirname2, "..", "..", "package.json");
11551
+ const pkg = JSON.parse(readFileSync12(pkgPath, "utf-8"));
11062
11552
  return pkg.version ?? "0.1.1";
11063
11553
  } catch {
11064
11554
  return "0.1.1";
@@ -11183,8 +11673,8 @@ if (process.env.CLI_VERSION) {
11183
11673
  } else {
11184
11674
  try {
11185
11675
  const __dirname2 = dirname3(fileURLToPath2(import.meta.url));
11186
- const pkgPath = join10(__dirname2, "..", "..", "package.json");
11187
- const pkg = JSON.parse(readFileSync12(pkgPath, "utf-8"));
11676
+ const pkgPath = join11(__dirname2, "..", "..", "package.json");
11677
+ const pkg = JSON.parse(readFileSync13(pkgPath, "utf-8"));
11188
11678
  cliVersion = pkg.version ?? cliVersion;
11189
11679
  } catch {}
11190
11680
  }
@@ -11353,12 +11843,12 @@ program.addCommand(createFeedbackCommand());
11353
11843
  var isInteractive = process.stdin.isTTY && !rawArgs.includes("--json") && process.env.CI !== "true";
11354
11844
  var isSkillCommand = rawArgs[0] === "skills" || rawArgs[0] === "hooks";
11355
11845
  if (isInteractive && !isSkillCommand) {
11356
- const skillPath = join10(process.cwd(), ".claude/skills/mutagent-cli/SKILL.md");
11357
- if (!existsSync14(skillPath)) {
11846
+ const skillPath = join11(process.cwd(), ".claude/skills/mutagent-cli/SKILL.md");
11847
+ if (!existsSync15(skillPath)) {
11358
11848
  console.log(chalk39.dim("MutagenT SKILL not installed. Install it for AI agent support? Run:"), chalk39.cyan("mutagent skills install"));
11359
11849
  }
11360
11850
  }
11361
11851
  program.parse();
11362
11852
 
11363
- //# debugId=A0F1B767D1BF2A2364756E2164756E21
11853
+ //# debugId=A1F82C134C46598C64756E2164756E21
11364
11854
  //# sourceMappingURL=cli.js.map