crumbtrail-node 0.1.0 → 0.2.0

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/cli.cjs CHANGED
@@ -215956,6 +215956,7 @@ var SentryError = class extends Error {
215956
215956
  this.status = status;
215957
215957
  this.name = "SentryError";
215958
215958
  }
215959
+ status;
215959
215960
  };
215960
215961
  function sanitizeUrl2(u) {
215961
215962
  try {
@@ -216369,6 +216370,8 @@ var CloudWatchError = class extends Error {
216369
216370
  this.target = target;
216370
216371
  this.name = "CloudWatchError";
216371
216372
  }
216373
+ status;
216374
+ target;
216372
216375
  };
216373
216376
  function isTransient2(error) {
216374
216377
  if (error instanceof CloudWatchError) {
@@ -216781,6 +216784,7 @@ var SplunkError = class extends Error {
216781
216784
  this.status = status;
216782
216785
  this.name = "SplunkError";
216783
216786
  }
216787
+ status;
216784
216788
  };
216785
216789
  function sanitizeUrl3(u) {
216786
216790
  try {
@@ -217131,6 +217135,7 @@ var DatadogError = class extends Error {
217131
217135
  this.status = status;
217132
217136
  this.name = "DatadogError";
217133
217137
  }
217138
+ status;
217134
217139
  };
217135
217140
  function sanitizeUrl4(u) {
217136
217141
  try {
@@ -217494,6 +217499,7 @@ var PostHogError = class extends Error {
217494
217499
  this.status = status;
217495
217500
  this.name = "PostHogError";
217496
217501
  }
217502
+ status;
217497
217503
  };
217498
217504
  function sanitizeUrl5(u) {
217499
217505
  try {
@@ -217884,6 +217890,8 @@ var CloudflareError = class extends Error {
217884
217890
  this.op = op;
217885
217891
  this.name = "CloudflareError";
217886
217892
  }
217893
+ status;
217894
+ op;
217887
217895
  };
217888
217896
  function isTransient6(error) {
217889
217897
  if (error instanceof CloudflareError) {
@@ -223902,6 +223910,8 @@ var AudioProcessingError = class extends Error {
223902
223910
  this.cause = cause;
223903
223911
  this.name = "AudioProcessingError";
223904
223912
  }
223913
+ phase;
223914
+ cause;
223905
223915
  };
223906
223916
  var AudioTranscriptionUnavailableError = class extends Error {
223907
223917
  constructor() {
@@ -225652,12 +225662,12 @@ var import_node_fs10 = __toESM(require("fs"), 1);
225652
225662
  // package.json
225653
225663
  var package_default = {
225654
225664
  name: "crumbtrail-node",
225655
- version: "0.1.0",
225665
+ version: "0.2.0",
225656
225666
  description: "Local Crumbtrail server, MCP evidence tools, Express middleware, and CLI for self-hosted debugging sessions.",
225657
225667
  license: "MIT",
225658
225668
  repository: {
225659
225669
  type: "git",
225660
- url: "https://github.com/oshabana/crumbtrail.git",
225670
+ url: "git+https://github.com/crumbtrail-dev/crumbtrail-js.git",
225661
225671
  directory: "packages/node"
225662
225672
  },
225663
225673
  keywords: [
@@ -225710,6 +225720,10 @@ var package_default = {
225710
225720
  "@types/node": "^25.5.0",
225711
225721
  express: "^5.2.1",
225712
225722
  typescript: "^5.7.0"
225723
+ },
225724
+ homepage: "https://github.com/crumbtrail-dev/crumbtrail-js/tree/main/packages/node#readme",
225725
+ bugs: {
225726
+ url: "https://github.com/crumbtrail-dev/crumbtrail-js/issues"
225713
225727
  }
225714
225728
  };
225715
225729
 
@@ -226360,6 +226374,10 @@ var RequestValidationError = class extends Error {
226360
226374
  this.retryable = retryable;
226361
226375
  this.name = "RequestValidationError";
226362
226376
  }
226377
+ status;
226378
+ publicMessage;
226379
+ code;
226380
+ retryable;
226363
226381
  };
226364
226382
  function serveStatic(res, staticDir, urlPath) {
226365
226383
  const resolved = resolveStaticPath(staticDir, urlPath);
@@ -227089,6 +227107,7 @@ var BoundedTraceSessionCache = class {
227089
227107
  constructor(maxEntries) {
227090
227108
  this.maxEntries = maxEntries;
227091
227109
  }
227110
+ maxEntries;
227092
227111
  entries = /* @__PURE__ */ new Map();
227093
227112
  get(traceId) {
227094
227113
  const existing = this.entries.get(traceId);
@@ -227787,6 +227806,20 @@ function createServer(config) {
227787
227806
  });
227788
227807
  server2.on("close", () => sweeper.stop());
227789
227808
  }
227809
+ if (config.ai?.enabled && config.ai.backfillOnStart) {
227810
+ const sessionDirs = sessions.listSummaries().map((summary) => sessions.getExistingSessionDir(summary.id)).filter((dir) => dir !== void 0);
227811
+ const timer = setTimeout(() => {
227812
+ void backfillAiDiagnoses(
227813
+ sessionDirs,
227814
+ config.ai
227815
+ ).then(
227816
+ (result) => config.ai?.log?.(
227817
+ `Crumbtrail AI diagnosis backfill complete: ${JSON.stringify(result)}`
227818
+ )
227819
+ );
227820
+ }, 0);
227821
+ server2.on("close", () => clearTimeout(timer));
227822
+ }
227790
227823
  if (fastFinalizer) {
227791
227824
  server2.on("close", () => fastFinalizer.stop());
227792
227825
  }
@@ -227836,6 +227869,7 @@ var FixContextError = class extends Error {
227836
227869
  this.code = code;
227837
227870
  this.name = "FixContextError";
227838
227871
  }
227872
+ code;
227839
227873
  };
227840
227874
  function buildFixContext(sessionDirOrId, opts = {}) {
227841
227875
  const sessionDir = resolveSessionDir(sessionDirOrId, opts);
@@ -228895,16 +228929,85 @@ function diffEnvironment(a, b, rules, disabled) {
228895
228929
  addSuppressedRules(rules, beforeRules, afterRules, disabled);
228896
228930
  return [];
228897
228931
  }
228932
+ const envDelta = buildEnvDiff(a, b, disabled);
228898
228933
  return [
228899
228934
  {
228900
228935
  plane: "env",
228901
228936
  kind: "env.snapshot",
228902
228937
  before: a,
228903
228938
  after: b,
228904
- brief: "feature flag, config, release, or build snapshot changed between sessions"
228939
+ brief: envBrief(envDelta),
228940
+ envDelta
228905
228941
  }
228906
228942
  ];
228907
228943
  }
228944
+ function buildEnvDiff(a, b, disabled) {
228945
+ const delta = {
228946
+ flags: channelDelta(recordField(a.flags), recordField(b.flags), disabled),
228947
+ config: channelDelta(
228948
+ recordField(a.config),
228949
+ recordField(b.config),
228950
+ disabled
228951
+ )
228952
+ };
228953
+ const releaseBefore = stringOrUndefined(a.release);
228954
+ const releaseAfter = stringOrUndefined(b.release);
228955
+ if (releaseBefore !== releaseAfter) {
228956
+ delta.release = {
228957
+ ...releaseBefore !== void 0 ? { before: releaseBefore } : {},
228958
+ ...releaseAfter !== void 0 ? { after: releaseAfter } : {}
228959
+ };
228960
+ }
228961
+ const buildBefore = stringOrUndefined(a.build);
228962
+ const buildAfter = stringOrUndefined(b.build);
228963
+ if (buildBefore !== buildAfter) {
228964
+ delta.build = {
228965
+ ...buildBefore !== void 0 ? { before: buildBefore } : {},
228966
+ ...buildAfter !== void 0 ? { after: buildAfter } : {}
228967
+ };
228968
+ }
228969
+ return delta;
228970
+ }
228971
+ function recordField(value) {
228972
+ return isRecord14(value) ? value : {};
228973
+ }
228974
+ function channelDelta(a, b, disabled) {
228975
+ const added = [];
228976
+ const removed = [];
228977
+ const changed = [];
228978
+ for (const key of Object.keys(a).sort()) {
228979
+ if (!(key in b)) {
228980
+ removed.push({ key, before: a[key] });
228981
+ } else if (envValuesDiffer(a[key], b[key], disabled)) {
228982
+ changed.push({ key, before: a[key], after: b[key] });
228983
+ }
228984
+ }
228985
+ for (const key of Object.keys(b).sort()) {
228986
+ if (!(key in a)) added.push({ key, after: b[key] });
228987
+ }
228988
+ return { added, removed, changed };
228989
+ }
228990
+ function envValuesDiffer(before, after, disabled) {
228991
+ const beforeRules = /* @__PURE__ */ new Set();
228992
+ const afterRules = /* @__PURE__ */ new Set();
228993
+ if (canonicalize(before, beforeRules) !== canonicalize(after, afterRules))
228994
+ return true;
228995
+ const rawDiffers = stableStringify(before) !== stableStringify(after);
228996
+ return rawDiffers && hasDisabledRule(beforeRules, afterRules, disabled);
228997
+ }
228998
+ function envBrief(delta) {
228999
+ const counts = delta.flags.added.length + delta.flags.removed.length + delta.flags.changed.length + delta.config.added.length + delta.config.removed.length + delta.config.changed.length + (delta.release ? 1 : 0) + (delta.build ? 1 : 0);
229000
+ const parts = [];
229001
+ const flagCount = delta.flags.added.length + delta.flags.removed.length + delta.flags.changed.length;
229002
+ const configCount = delta.config.added.length + delta.config.removed.length + delta.config.changed.length;
229003
+ if (flagCount > 0) parts.push(`${flagCount} flag(s)`);
229004
+ if (configCount > 0) parts.push(`${configCount} config value(s)`);
229005
+ if (delta.release) parts.push("release");
229006
+ if (delta.build) parts.push("build");
229007
+ if (counts === 0 || parts.length === 0)
229008
+ return "feature flag, config, release, or build snapshot changed between sessions";
229009
+ return `environment delta between sessions: ${parts.join(", ")} changed`;
229010
+ }
228908
229011
  function confidenceFor(divergences) {
228909
229012
  if (divergences.length === 0) return "high";
228910
229013
  if (divergences.some(
@@ -228931,6 +229034,9 @@ async function compareSessions(aDir, bDir, options = {}) {
228931
229034
  ...diffDb(a.dbWrites, b.dbWrites, rules, disabled),
228932
229035
  ...diffEnvironment(a.environment, b.environment, rules, disabled)
228933
229036
  ];
229037
+ const envDelta = divergences.find(
229038
+ (divergence) => divergence.kind === "env.snapshot"
229039
+ )?.envDelta;
228934
229040
  return {
228935
229041
  schemaVersion: SESSION_COMPARE_SCHEMA_VERSION,
228936
229042
  verdict: divergences.length > 0 ? "regression" : "clean",
@@ -228953,20 +229059,187 @@ async function compareSessions(aDir, bDir, options = {}) {
228953
229059
  divergences,
228954
229060
  noise: { suppressedCount: rules.size, rules: [...rules].sort() },
228955
229061
  evidence: divergencesToEvidence(divergences),
228956
- intent: []
229062
+ intent: [],
229063
+ ...envDelta ? { envDelta } : {}
228957
229064
  };
228958
229065
  }
228959
229066
 
228960
229067
  // src/compare/regression-context.ts
228961
229068
  var import_node_fs13 = __toESM(require("fs"), 1);
228962
229069
  var import_node_path13 = __toESM(require("path"), 1);
229070
+
229071
+ // src/compare/report.ts
229072
+ var PLANE_ORDER = ["flow", "network", "db", "env"];
229073
+ var PLANE_LABELS = {
229074
+ flow: "Flow steps",
229075
+ network: "Network calls",
229076
+ db: "Database rows",
229077
+ env: "Environment and flags"
229078
+ };
229079
+ function renderCompareReport(comparison) {
229080
+ const lines = [];
229081
+ const regressed = comparison.verdict === "regression";
229082
+ lines.push(
229083
+ `# Session comparison - ${formatSessionRef(comparison.a)} vs ${formatSessionRef(comparison.b)}`
229084
+ );
229085
+ lines.push("");
229086
+ lines.push(
229087
+ regressed ? `> **Verdict: REGRESSION** (confidence: ${comparison.confidence}) - recorded behavior changed between these sessions.` : `> **Verdict: CLEAN** (confidence: ${comparison.confidence}) - the recorded sessions show no behavioral divergence.`
229088
+ );
229089
+ lines.push("");
229090
+ lines.push(
229091
+ "A is the baseline session; B is the candidate release/build. Every row below is grounded in the recorded evidence of both sessions."
229092
+ );
229093
+ lines.push("");
229094
+ lines.push("## Aligned flow");
229095
+ lines.push("");
229096
+ const { matchedSteps, unmatchedA, unmatchedB } = comparison.alignment;
229097
+ lines.push(
229098
+ `${matchedSteps} step(s) matched by component identity \xB7 ${unmatchedA} only in A \xB7 ${unmatchedB} only in B`
229099
+ );
229100
+ lines.push("");
229101
+ if (comparison.divergences.length > 0) {
229102
+ lines.push("| Plane | Kind | Component | Divergence |");
229103
+ lines.push("|---|---|---|---|");
229104
+ for (const d of comparison.divergences) {
229105
+ const component = d.sig ? `\`${escapeCell(d.sig)}\`` : "\u2014";
229106
+ lines.push(
229107
+ `| **${d.plane}** | \`${d.kind}\` | ${component} | ${escapeCell(d.brief)} |`
229108
+ );
229109
+ }
229110
+ lines.push("");
229111
+ }
229112
+ for (const plane of PLANE_ORDER) {
229113
+ const planeDivergences = comparison.divergences.filter(
229114
+ (d) => d.plane === plane
229115
+ );
229116
+ if (planeDivergences.length === 0) continue;
229117
+ lines.push(`## ${PLANE_LABELS[plane]}`);
229118
+ lines.push("");
229119
+ for (const d of planeDivergences) {
229120
+ lines.push(`### \`${d.kind}\` - ${escapeCell(d.brief)}`);
229121
+ lines.push("");
229122
+ if (d.sig) lines.push(`- Component: \`${d.sig}\``);
229123
+ if (d.requestId) lines.push(`- Request: \`${d.requestId}\``);
229124
+ if (d.table)
229125
+ lines.push(
229126
+ `- Table: \`${d.table}\`${d.pk ? ` \xB7 pk \`${JSON.stringify(d.pk)}\`` : ""}`
229127
+ );
229128
+ lines.push("");
229129
+ if (d.envDelta) {
229130
+ lines.push(...renderEnvDelta(d.envDelta));
229131
+ } else {
229132
+ lines.push("```diff");
229133
+ lines.push(`- before: ${JSON.stringify(d.before)}`);
229134
+ lines.push(`+ after: ${JSON.stringify(d.after)}`);
229135
+ lines.push("```");
229136
+ }
229137
+ lines.push("");
229138
+ }
229139
+ }
229140
+ lines.push("---");
229141
+ lines.push("");
229142
+ lines.push(
229143
+ comparison.noise.suppressedCount > 0 ? `*${comparison.noise.suppressedCount} expected-variance difference(s) suppressed by the noise model (${comparison.noise.rules.join(", ")}).*` : "*No differences were suppressed by the noise model.*"
229144
+ );
229145
+ lines.push("");
229146
+ return lines.join("\n");
229147
+ }
229148
+ function formatComparisonSummary(comparison) {
229149
+ const lines = [];
229150
+ lines.push(
229151
+ `crumbtrail-server compare - ${formatSessionRef(comparison.a)} vs ${formatSessionRef(comparison.b)}`
229152
+ );
229153
+ lines.push(` Schema: ${comparison.schemaVersion}`);
229154
+ lines.push(
229155
+ ` Verdict: ${comparison.verdict.toUpperCase()} (confidence ${comparison.confidence})`
229156
+ );
229157
+ lines.push(
229158
+ ` Alignment: ${comparison.alignment.matchedSteps} matched \xB7 ${comparison.alignment.unmatchedA} only in A \xB7 ${comparison.alignment.unmatchedB} only in B`
229159
+ );
229160
+ lines.push(` Divergences: ${comparison.divergences.length}`);
229161
+ for (const d of comparison.divergences.slice(0, 5)) {
229162
+ lines.push(` [${d.plane}] ${d.kind} - ${d.brief}`);
229163
+ }
229164
+ if (comparison.divergences.length > 5) {
229165
+ lines.push(
229166
+ ` ... and ${comparison.divergences.length - 5} more (use --json or --report for the complete list)`
229167
+ );
229168
+ }
229169
+ const rules = comparison.noise.rules.length > 0 ? ` (${comparison.noise.rules.join(", ")})` : "";
229170
+ lines.push(
229171
+ ` Noise: ${comparison.noise.suppressedCount} suppressed${rules}`
229172
+ );
229173
+ return lines.join("\n");
229174
+ }
229175
+ function escapeCell(text2) {
229176
+ return text2.replace(/\|/g, "\\|").replace(/\r?\n/g, " ");
229177
+ }
229178
+ function sessionRefLabel(ref) {
229179
+ return ref.release ?? ref.sessionId;
229180
+ }
229181
+ function comparisonTitle(comparison) {
229182
+ return `${sessionRefLabel(comparison.a)} vs ${sessionRefLabel(comparison.b)}`;
229183
+ }
229184
+ function renderEnvDelta(delta) {
229185
+ const lines = ["```diff"];
229186
+ renderEnvChannel(lines, "flags", delta.flags);
229187
+ renderEnvChannel(lines, "config", delta.config);
229188
+ if (delta.release) {
229189
+ lines.push(
229190
+ `~ release: ${JSON.stringify(delta.release.before ?? null)} -> ${JSON.stringify(delta.release.after ?? null)}`
229191
+ );
229192
+ }
229193
+ if (delta.build) {
229194
+ lines.push(
229195
+ `~ build: ${JSON.stringify(delta.build.before ?? null)} -> ${JSON.stringify(delta.build.after ?? null)}`
229196
+ );
229197
+ }
229198
+ if (lines.length === 1) lines.push(" (no field-level changes)");
229199
+ lines.push("```");
229200
+ return lines;
229201
+ }
229202
+ function renderEnvChannel(lines, channel, delta) {
229203
+ for (const change of delta.added) {
229204
+ lines.push(
229205
+ `+ ${channel}.${change.key}: ${JSON.stringify(change.after ?? null)}`
229206
+ );
229207
+ }
229208
+ for (const change of delta.removed) {
229209
+ lines.push(
229210
+ `- ${channel}.${change.key}: ${JSON.stringify(change.before ?? null)}`
229211
+ );
229212
+ }
229213
+ for (const change of delta.changed) {
229214
+ lines.push(
229215
+ `~ ${channel}.${change.key}: ${JSON.stringify(change.before ?? null)} -> ${JSON.stringify(change.after ?? null)}`
229216
+ );
229217
+ }
229218
+ }
229219
+ function formatSessionRef(ref) {
229220
+ if (ref.release) {
229221
+ const detail = [
229222
+ `session ${ref.sessionId}`,
229223
+ ref.build ? `build ${ref.build}` : void 0
229224
+ ].filter(Boolean);
229225
+ return `${ref.release} (${detail.join(", ")})`;
229226
+ }
229227
+ return ref.build ? `${ref.sessionId} (build ${ref.build})` : ref.sessionId;
229228
+ }
229229
+
229230
+ // src/compare/regression-context.ts
228963
229231
  var REGRESSION_CONTEXT_SCHEMA_VERSION = "regression-context.v1";
228964
229232
  function buildRegressionContext(comparison, bDir) {
228965
- const requestIds = unique(comparison.divergences.map((d) => d.requestId).filter(isString));
228966
- const times = requestIds.flatMap((requestId) => requestTimesFromIndex(bDir, requestId));
229233
+ const requestIds = unique(
229234
+ comparison.divergences.map((d) => d.requestId).filter(isString)
229235
+ );
229236
+ const times = requestIds.flatMap(
229237
+ (requestId) => requestTimesFromIndex(bDir, requestId)
229238
+ );
228967
229239
  const firstSig = comparison.divergences.map((d) => d.sig).find(isString);
228968
229240
  return {
228969
229241
  schemaVersion: REGRESSION_CONTEXT_SCHEMA_VERSION,
229242
+ title: comparisonTitle(comparison),
228970
229243
  comparison,
228971
229244
  divergent_interaction: firstSig ? interactionForSig(bDir, firstSig) : null,
228972
229245
  causal_window: requestIds.length > 0 ? {
@@ -228984,6 +229257,7 @@ function buildRegressionContext(comparison, bDir) {
228984
229257
  before: d.before,
228985
229258
  after: d.after
228986
229259
  })),
229260
+ env_delta: comparison.envDelta ?? null,
228987
229261
  repro_hint: reproHint(bDir)
228988
229262
  };
228989
229263
  }
@@ -229028,7 +229302,8 @@ function collectRefTimes(value, times) {
229028
229302
  const start = isRecord15(value.start) ? value.start : void 0;
229029
229303
  const end = isRecord15(value.end) ? value.end : void 0;
229030
229304
  for (const candidate of [ref, start, end]) {
229031
- if (typeof candidate?.t === "number" && Number.isFinite(candidate.t)) times.push(candidate.t);
229305
+ if (typeof candidate?.t === "number" && Number.isFinite(candidate.t))
229306
+ times.push(candidate.t);
229032
229307
  }
229033
229308
  }
229034
229309
  function reproHint(sessionDir) {
@@ -229953,12 +230228,29 @@ var McpServer = class {
229953
230228
  "sha"
229954
230229
  ]))
229955
230230
  continue;
229956
- sessions.push(meta);
230231
+ sessions.push(this.withReleaseBuild(meta));
229957
230232
  } catch {
229958
230233
  }
229959
230234
  }
229960
230235
  return textResult(sessions);
229961
230236
  }
230237
+ /**
230238
+ * Surfaces release/build as first-class list-row fields regardless of which
230239
+ * alias the app used (release/releaseId/version, build/buildId/commit/sha), so
230240
+ * an agent can label and group sessions by release without re-reading each
230241
+ * meta. Additive: the raw meta keys are preserved.
230242
+ */
230243
+ withReleaseBuild(meta) {
230244
+ const release = stringField5(meta.release ?? meta.releaseId ?? meta.version);
230245
+ const build = stringField5(
230246
+ meta.build ?? meta.buildId ?? meta.commit ?? meta.sha
230247
+ );
230248
+ return {
230249
+ ...meta,
230250
+ ...release !== void 0 ? { release } : {},
230251
+ ...build !== void 0 ? { build } : {}
230252
+ };
230253
+ }
229962
230254
  sessionMetadataMatches(meta, expected, keys) {
229963
230255
  return keys.some((key) => meta[key] === expected);
229964
230256
  }
@@ -231185,6 +231477,7 @@ var CliConfigError = class extends Error {
231185
231477
  this.code = code;
231186
231478
  this.name = "CliConfigError";
231187
231479
  }
231480
+ code;
231188
231481
  };
231189
231482
  function defaultCliConfig() {
231190
231483
  return {
@@ -232745,6 +233038,7 @@ var InspectError = class extends Error {
232745
233038
  this.code = code;
232746
233039
  this.name = "InspectError";
232747
233040
  }
233041
+ code;
232748
233042
  };
232749
233043
  function inspectSession(sessionDirOrId, opts = {}) {
232750
233044
  const sessionDir = resolveSessionDir2(sessionDirOrId, opts);
@@ -232916,119 +233210,6 @@ function resolveTarget2(target) {
232916
233210
  // src/run-compare.ts
232917
233211
  var import_node_fs20 = __toESM(require("fs"), 1);
232918
233212
  var import_node_path24 = __toESM(require("path"), 1);
232919
-
232920
- // src/compare/report.ts
232921
- var PLANE_ORDER = ["flow", "network", "db", "env"];
232922
- var PLANE_LABELS = {
232923
- flow: "Flow steps",
232924
- network: "Network calls",
232925
- db: "Database rows",
232926
- env: "Environment and flags"
232927
- };
232928
- function renderCompareReport(comparison) {
232929
- const lines = [];
232930
- const regressed = comparison.verdict === "regression";
232931
- lines.push(
232932
- `# Session comparison - ${formatSessionRef(comparison.a)} vs ${formatSessionRef(comparison.b)}`
232933
- );
232934
- lines.push("");
232935
- lines.push(
232936
- regressed ? `> **Verdict: REGRESSION** (confidence: ${comparison.confidence}) - recorded behavior changed between these sessions.` : `> **Verdict: CLEAN** (confidence: ${comparison.confidence}) - the recorded sessions show no behavioral divergence.`
232937
- );
232938
- lines.push("");
232939
- lines.push(
232940
- "A is the baseline session; B is the candidate release/build. Every row below is grounded in the recorded evidence of both sessions."
232941
- );
232942
- lines.push("");
232943
- lines.push("## Aligned flow");
232944
- lines.push("");
232945
- const { matchedSteps, unmatchedA, unmatchedB } = comparison.alignment;
232946
- lines.push(
232947
- `${matchedSteps} step(s) matched by component identity \xB7 ${unmatchedA} only in A \xB7 ${unmatchedB} only in B`
232948
- );
232949
- lines.push("");
232950
- if (comparison.divergences.length > 0) {
232951
- lines.push("| Plane | Kind | Component | Divergence |");
232952
- lines.push("|---|---|---|---|");
232953
- for (const d of comparison.divergences) {
232954
- const component = d.sig ? `\`${escapeCell(d.sig)}\`` : "\u2014";
232955
- lines.push(
232956
- `| **${d.plane}** | \`${d.kind}\` | ${component} | ${escapeCell(d.brief)} |`
232957
- );
232958
- }
232959
- lines.push("");
232960
- }
232961
- for (const plane of PLANE_ORDER) {
232962
- const planeDivergences = comparison.divergences.filter(
232963
- (d) => d.plane === plane
232964
- );
232965
- if (planeDivergences.length === 0) continue;
232966
- lines.push(`## ${PLANE_LABELS[plane]}`);
232967
- lines.push("");
232968
- for (const d of planeDivergences) {
232969
- lines.push(`### \`${d.kind}\` - ${escapeCell(d.brief)}`);
232970
- lines.push("");
232971
- if (d.sig) lines.push(`- Component: \`${d.sig}\``);
232972
- if (d.requestId) lines.push(`- Request: \`${d.requestId}\``);
232973
- if (d.table)
232974
- lines.push(
232975
- `- Table: \`${d.table}\`${d.pk ? ` \xB7 pk \`${JSON.stringify(d.pk)}\`` : ""}`
232976
- );
232977
- lines.push("");
232978
- lines.push("```diff");
232979
- lines.push(`- before: ${JSON.stringify(d.before)}`);
232980
- lines.push(`+ after: ${JSON.stringify(d.after)}`);
232981
- lines.push("```");
232982
- lines.push("");
232983
- }
232984
- }
232985
- lines.push("---");
232986
- lines.push("");
232987
- lines.push(
232988
- comparison.noise.suppressedCount > 0 ? `*${comparison.noise.suppressedCount} expected-variance difference(s) suppressed by the noise model (${comparison.noise.rules.join(", ")}).*` : "*No differences were suppressed by the noise model.*"
232989
- );
232990
- lines.push("");
232991
- return lines.join("\n");
232992
- }
232993
- function formatComparisonSummary(comparison) {
232994
- const lines = [];
232995
- lines.push(
232996
- `crumbtrail-server compare - ${formatSessionRef(comparison.a)} vs ${formatSessionRef(comparison.b)}`
232997
- );
232998
- lines.push(` Schema: ${comparison.schemaVersion}`);
232999
- lines.push(
233000
- ` Verdict: ${comparison.verdict.toUpperCase()} (confidence ${comparison.confidence})`
233001
- );
233002
- lines.push(
233003
- ` Alignment: ${comparison.alignment.matchedSteps} matched \xB7 ${comparison.alignment.unmatchedA} only in A \xB7 ${comparison.alignment.unmatchedB} only in B`
233004
- );
233005
- lines.push(` Divergences: ${comparison.divergences.length}`);
233006
- for (const d of comparison.divergences.slice(0, 5)) {
233007
- lines.push(` [${d.plane}] ${d.kind} - ${d.brief}`);
233008
- }
233009
- if (comparison.divergences.length > 5) {
233010
- lines.push(
233011
- ` ... and ${comparison.divergences.length - 5} more (use --json or --report for the complete list)`
233012
- );
233013
- }
233014
- const rules = comparison.noise.rules.length > 0 ? ` (${comparison.noise.rules.join(", ")})` : "";
233015
- lines.push(
233016
- ` Noise: ${comparison.noise.suppressedCount} suppressed${rules}`
233017
- );
233018
- return lines.join("\n");
233019
- }
233020
- function escapeCell(text2) {
233021
- return text2.replace(/\|/g, "\\|").replace(/\r?\n/g, " ");
233022
- }
233023
- function formatSessionRef(ref) {
233024
- const tags = [
233025
- ref.release ? `release ${ref.release}` : void 0,
233026
- ref.build ? `build ${ref.build}` : void 0
233027
- ].filter(Boolean);
233028
- return tags.length > 0 ? `${ref.sessionId} (${tags.join(", ")})` : ref.sessionId;
233029
- }
233030
-
233031
- // src/run-compare.ts
233032
233213
  async function runCompare(rest) {
233033
233214
  const options = parseCompareArgs(rest);
233034
233215
  if (!options.a || !options.b) {
package/dist/cli.js CHANGED
@@ -21,7 +21,7 @@ import {
21
21
  renderProviderCliOutput,
22
22
  resolveLatestIssue,
23
23
  resolveSessionDirById
24
- } from "./chunk-EASSXKUJ.js";
24
+ } from "./chunk-5HPWIOG3.js";
25
25
  import {
26
26
  __commonJS,
27
27
  __require,
@@ -213062,6 +213062,7 @@ var CliConfigError = class extends Error {
213062
213062
  this.code = code;
213063
213063
  this.name = "CliConfigError";
213064
213064
  }
213065
+ code;
213065
213066
  };
213066
213067
  function defaultCliConfig() {
213067
213068
  return {