@jentrix/cli 0.5.22 → 0.5.23

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/main.js CHANGED
@@ -20966,7 +20966,7 @@ function refreshAccessToken(input) {
20966
20966
 
20967
20967
  // src/client.ts
20968
20968
  var CLI_NAME = "stacks-cli";
20969
- var CLI_VERSION = "0.5.22";
20969
+ var CLI_VERSION = "0.5.23";
20970
20970
  var DEAD_TOKEN_MESSAGE = "authentication failed (HTTP 401): the token is dead or expired \u2014 mint a new PAT at /account/tokens on your Jentrix server and update STACKS_TOKEN (or --token / the config file).";
20971
20971
  var RELOGIN_MESSAGE = "OAuth session expired and could not be refreshed \u2014 run `jentrix login` to sign in again.";
20972
20972
  function originOf2(url2) {
@@ -21386,10 +21386,10 @@ function buildAlignDisclosures(catalog) {
21386
21386
  catalog.liveHostPid !== null ? `keep the live session host (pid ${catalog.liveHostPid}) running` : "start a detached session host (heartbeats + provider usage receipts)"
21387
21387
  );
21388
21388
  disclosures.push(
21389
- catalog.captureMode === "on" ? "TRACE capture: ON \u2014 the full transcript will be recorded" : "TRACE capture: OFF (MVP default) \u2014 typed artifacts only, no transcript"
21389
+ catalog.captureMode === "on" ? "TRACE capture: ON \u2014 the full transcript will be recorded" : catalog.captureMode === "off" ? "TRACE capture: OFF \u2014 typed artifacts only, no transcript" : "TRACE capture: your account default (Account \u2192 Capture; the built-in is OFF \u2014 typed artifacts only). The confirmed value is printed after the align; --capture/--no-capture decides it here instead"
21390
21390
  );
21391
21391
  disclosures.push(
21392
- catalog.skeletonMode === "on" ? "activity skeleton: ON (default) \u2014 content-free counts/timing (turns, tool names, files-touched), independent of capture; --no-skeleton opts out" : "activity skeleton: OFF \u2014 no counts, no timing, no files-touched; the session's Session activity section will read as not observed"
21392
+ catalog.skeletonMode === "on" ? "activity skeleton: ON \u2014 content-free counts/timing (turns, tool names, files-touched), independent of capture; --no-skeleton opts out" : catalog.skeletonMode === "off" ? "activity skeleton: OFF \u2014 no counts, no timing, no files-touched; the session's Session activity section will read as not observed" : "activity skeleton: your account default (Account \u2192 Capture; the built-in is ON \u2014 content-free counts/timing). The confirmed value is printed after the align; --skeleton/--no-skeleton decides it here instead"
21393
21393
  );
21394
21394
  disclosures.push("write the local alignment marker for this checkout");
21395
21395
  const mcp = catalog.mcpConfig;
@@ -22007,6 +22007,10 @@ function upsertAlignmentMarker(file, providerSessionId, marker) {
22007
22007
  sessions[providerSessionId] = marker;
22008
22008
  return { version: 2, sessions, latest: providerSessionId };
22009
22009
  }
22010
+ function findAlignmentMarkerForSession(file, sessionId) {
22011
+ if (!file) return null;
22012
+ return Object.values(file.sessions).find((m) => m.sessionId === sessionId) ?? null;
22013
+ }
22010
22014
  function removeAlignmentMarkerEntry(file, sessionId) {
22011
22015
  if (!file) return null;
22012
22016
  const sessions = Object.fromEntries(
@@ -23035,6 +23039,13 @@ async function runSessionStatus(sessionId, flags, deps2) {
23035
23039
  const deadLocal = open && boundHere && !hasLocalCaptureFootprint(deps2, sessionId);
23036
23040
  const captureOff = session.alignment?.capture === "off";
23037
23041
  const capture = captureOff ? "off (MVP alignment \u2014 typed artifacts only)" : session.captureComplete ? "complete" : deadLocal ? "bound server-side; local capture NOT RUNNING" : open ? "recording (finalizes when the session ends)" : `INCOMPLETE${session.captureError ? ` \u2014 ${String(session.captureError)}` : ""}`;
23042
+ const inspection = await inspectRepository(deps2.cwd(), deps2.git);
23043
+ const alignMarker = inspection ? findAlignmentMarkerForSession(
23044
+ readAlignmentMarkerFile(deps2.configPath, inspection.root),
23045
+ sessionId
23046
+ ) : null;
23047
+ const captureProvenance = alignMarker?.captureSource ? ` \xB7 resolved from ${alignMarker.captureSource}` : "";
23048
+ const skeletonProvenance = alignMarker?.skeletonSource ? ` \xB7 resolved from ${alignMarker.skeletonSource}` : "";
23038
23049
  const local = deadLocal ? [
23039
23050
  "Local capture: NOT RUNNING \u2014 this machine bound the session but no capture host ever started here (no host marker, no spool parts).",
23040
23051
  ` Fix: \`jentrix session end ${sessionId}\` closes it honestly (the capture gap is recorded), or reattach with a transcript path to start capture.`
@@ -23050,9 +23061,9 @@ async function runSessionStatus(sessionId, flags, deps2) {
23050
23061
  `Session ${String(session.id)} \xB7 ${String(session.provider)} \xB7 ${String(session.status)}`,
23051
23062
  `Project: ${String(session.projectName)} (${String(session.projectId)})`,
23052
23063
  `Repository: ${String(session.repoOwnerName)}`,
23053
- `Capture: ${capture}`,
23064
+ `Capture: ${capture}${captureProvenance}`,
23054
23065
  // Evidence floor (D3): the skeleton mode the alignment declared.
23055
- `Skeleton: ${session.alignment?.skeleton === "off" ? "off (--no-skeleton at align)" : "on (content-free activity counts/timing)"}`,
23066
+ `Skeleton: ${session.alignment?.skeleton === "off" ? "off (no activity counts, no timing, no files-touched)" : "on (content-free activity counts/timing)"}${skeletonProvenance}`,
23056
23067
  ...local,
23057
23068
  telemetry.line,
23058
23069
  `Summary artifact: ${session.summaryArtifactId ? String(session.summaryArtifactId) : "\u2014"}`
@@ -24221,10 +24232,11 @@ async function buildCatalog(caller, flags, detection) {
24221
24232
  // Host/capture disclosure inputs are filled by runAlign (they need the
24222
24233
  // checkout root); defaults here are the no-live-host reading.
24223
24234
  liveHostPid: null,
24224
- captureMode: decideCaptureMode(flags.capture, false),
24225
- // JEN-169: only `--no-skeleton` acts (the same rule submit applies at
24226
- // line ~1273), so the disclosure needs no server round trip.
24227
- skeletonMode: flags.skeleton === false ? "off" : "on",
24235
+ captureMode: captureSubmission(flags.capture, false, false) ?? "default",
24236
+ // JEN-169 + capture settings §5: the disclosure states the same tri-state
24237
+ // the submission carries, so "your account default" is never quietly
24238
+ // disclosed as a posture the wizard guessed.
24239
+ skeletonMode: skeletonSubmission(flags.skeleton) ?? "default",
24228
24240
  producer: producerFromFlags(flags),
24229
24241
  tokenBudget: budgetFromFlags(flags),
24230
24242
  preconditionNotices: [],
@@ -24338,6 +24350,18 @@ function decideCaptureMode(requested, liveCapturing) {
24338
24350
  if (requested === false) return "off";
24339
24351
  return liveCapturing ? "on" : "off";
24340
24352
  }
24353
+ function captureSubmission(requested, liveHost, liveCapturing) {
24354
+ if (requested === void 0 && !liveHost) return void 0;
24355
+ return decideCaptureMode(requested, liveCapturing);
24356
+ }
24357
+ function skeletonSubmission(requested) {
24358
+ if (requested === void 0) return void 0;
24359
+ return requested ? "on" : "off";
24360
+ }
24361
+ function captureSourceLabel(requested, liveHost, serverLabel) {
24362
+ if (requested === void 0 && liveHost) return "(live host)";
24363
+ return serverLabel;
24364
+ }
24341
24365
  function keepCurrentAlignment(currentAlignment, projectId, workItem, ownerUserId) {
24342
24366
  if (workItem !== null || !projectId) return null;
24343
24367
  const current = currentAlignment;
@@ -24402,7 +24426,9 @@ ${JSON.stringify(session.alignment, null, 2)}`
24402
24426
  sessionId,
24403
24427
  alignment: session.alignment,
24404
24428
  realigned: false,
24405
- captureMode: (session.alignment.capture ?? "off") === "on" ? "on" : "off"
24429
+ captureMode: (session.alignment.capture ?? "off") === "on" ? "on" : "off",
24430
+ // No align ran, so nothing resolved anything — claim no provenance.
24431
+ captureSources: null
24406
24432
  };
24407
24433
  }
24408
24434
  }
@@ -24453,7 +24479,12 @@ ${JSON.stringify(session.alignment, null, 2)}`
24453
24479
  `cannot turn TRACE capture off: the live session host (pid ${liveHost.pid}) is actively capturing \u2014 end the session (\`jentrix session end\`) to stop it`
24454
24480
  );
24455
24481
  }
24456
- const captureMode = decideCaptureMode(flags.capture, liveCapturing);
24482
+ const captureSubmitted = captureSubmission(
24483
+ flags.capture,
24484
+ liveHost !== null,
24485
+ liveCapturing
24486
+ );
24487
+ const skeletonSubmitted = skeletonSubmission(flags.skeleton);
24457
24488
  if (liveHost && session.taskId && session.taskId !== (taskId ?? null)) {
24458
24489
  const acked = await requestUsageFlush(deps2, sessionId);
24459
24490
  deps2.writeOut(
@@ -24471,14 +24502,22 @@ ${JSON.stringify(session.alignment, null, 2)}`
24471
24502
  ...producer.emoji ? { agentEmoji: producer.emoji } : {}
24472
24503
  } : {},
24473
24504
  ...budget !== null ? { tokenBudget: budget } : {},
24474
- capture: captureMode,
24475
- // Evidence floor (D3): only an explicit --no-skeleton is sent; absent
24476
- // lets the server keep the stored mode (default "on").
24477
- ...flags.skeleton === false ? { skeleton: "off" } : {},
24505
+ ...captureSubmitted !== void 0 ? { capture: captureSubmitted } : {},
24506
+ ...skeletonSubmitted !== void 0 ? { skeleton: skeletonSubmitted } : {},
24478
24507
  expectedUpdatedAt: session.updatedAt
24479
24508
  });
24480
24509
  const alignment = aligned.alignment;
24481
24510
  const skeletonMode = alignment.skeleton === "off" ? "off" : "on";
24511
+ const captureMode = alignment.capture === "on" ? "on" : "off";
24512
+ const serverSources = aligned.captureSources ?? null;
24513
+ const captureSources = serverSources ? {
24514
+ capture: captureSourceLabel(
24515
+ flags.capture,
24516
+ liveHost !== null,
24517
+ serverSources.capture
24518
+ ),
24519
+ skeleton: serverSources.skeleton
24520
+ } : null;
24482
24521
  writeAlignmentMarker(
24483
24522
  deps2.configPath,
24484
24523
  inspection.root,
@@ -24488,6 +24527,12 @@ ${JSON.stringify(session.alignment, null, 2)}`
24488
24527
  projectId: choices.projectId,
24489
24528
  taskId,
24490
24529
  capture: captureMode,
24530
+ // Capture settings D6/S4: `session status` reads the provenance back
24531
+ // from here — the snapshot deliberately stores values only, so the
24532
+ // align that resolved them is the only place the source exists.
24533
+ ...captureSources?.capture ? { captureSource: captureSources.capture } : {},
24534
+ ...captureSources?.skeleton ? { skeletonSource: captureSources.skeleton } : {},
24535
+ skeleton: skeletonMode,
24491
24536
  alignedAt: (/* @__PURE__ */ new Date()).toISOString()
24492
24537
  },
24493
24538
  detected.providerSessionId
@@ -24514,7 +24559,8 @@ ${JSON.stringify(session.alignment, null, 2)}`
24514
24559
  sessionId,
24515
24560
  alignment,
24516
24561
  realigned: Boolean(aligned.realigned),
24517
- captureMode
24562
+ captureMode,
24563
+ captureSources
24518
24564
  };
24519
24565
  }
24520
24566
  if (!liveHost && (detected.provider === "claude" && detected.transcriptPath || detected.provider === "codex" && detected.hookDir)) {
@@ -24558,7 +24604,8 @@ ${JSON.stringify(session.alignment, null, 2)}`
24558
24604
  sessionId,
24559
24605
  alignment,
24560
24606
  realigned: Boolean(aligned.realigned),
24561
- captureMode
24607
+ captureMode,
24608
+ captureSources
24562
24609
  };
24563
24610
  }
24564
24611
  async function requestUsageFlush(deps2, sessionId, timeoutMs = 6e3) {
@@ -24609,10 +24656,11 @@ async function runAlign(flags, deps2) {
24609
24656
  );
24610
24657
  const markerHost = marker ? readLiveHostMarker(deps2, marker.sessionId) : null;
24611
24658
  catalog.liveHostPid = markerHost?.pid ?? null;
24612
- catalog.captureMode = decideCaptureMode(
24659
+ catalog.captureMode = captureSubmission(
24613
24660
  flags.capture,
24661
+ markerHost !== null,
24614
24662
  marker !== null && markerHost !== null && isHostCapturing(deps2, marker.sessionId, markerHost)
24615
- );
24663
+ ) ?? "default";
24616
24664
  const newest = newestAlignmentMarker(deps2.configPath, inspection.root);
24617
24665
  catalog.lastAlignedWorkspaceId = newest?.workspaceId ?? null;
24618
24666
  catalog.binding = {
@@ -25001,6 +25049,7 @@ async function runAlign(flags, deps2) {
25001
25049
  deps2.writeOut(JSON.stringify(result));
25002
25050
  } else {
25003
25051
  const a = result.alignment;
25052
+ const sources = result.captureSources;
25004
25053
  deps2.writeOut(
25005
25054
  [
25006
25055
  `${result.realigned ? "Re-aligned" : "Aligned"} session ${result.sessionId}:`,
@@ -25010,8 +25059,12 @@ async function runAlign(flags, deps2) {
25010
25059
  ` Owner: ${a.owner?.name ?? a.owner?.email ?? "\u2014"}`,
25011
25060
  ` Agent: ${a.agent?.provider ?? "\u2014"}`,
25012
25061
  ` Repo: ${a.repo?.ownerName ?? "\u2014"}@${a.repo?.branch ?? "detached"}`,
25013
- ` Capture: ${a.capture ?? "off"}`,
25014
- ` Skeleton: ${a.skeleton ?? "on"} (content-free activity counts/timing; --no-skeleton opts out)`,
25062
+ // Capture settings D6: each resolved knob names WHERE it came
25063
+ // from, in the server's own words "(flag)", "(this session)",
25064
+ // "(your default)", "(built-in)". A server older than this round
25065
+ // sends none, and then nothing is claimed.
25066
+ ` Capture: ${a.capture ?? "off"}${sources?.capture ? ` ${sources.capture}` : ""}`,
25067
+ ` Skeleton: ${a.skeleton ?? "on"}${sources?.skeleton ? ` ${sources.skeleton}` : ""} \u2014 content-free activity counts/timing; --skeleton/--no-skeleton decides one session, Account \u2192 Capture sets your default`,
25015
25068
  `Alignment snapshot (server-confirmed): ${JSON.stringify(result.alignment)}`
25016
25069
  ].join("\n")
25017
25070
  );
@@ -25072,13 +25125,16 @@ function registerAlignCommand(program3, deps2, onExit2) {
25072
25125
  "token budget for this session \u2014 a breach is recorded and badged, never enforced"
25073
25126
  ).option(
25074
25127
  "--capture",
25075
- "turn TRACE capture ON for this session (MVP default: off; applies from a fresh attach)"
25128
+ "turn TRACE capture ON for this session (overrides your account default; applies from a fresh attach)"
25076
25129
  ).option(
25077
25130
  "--no-capture",
25078
- "keep TRACE capture OFF explicitly \u2014 also corrects a wrongly-on record on re-align"
25131
+ "keep TRACE capture OFF explicitly \u2014 overrides your account default, and corrects a wrongly-on record on re-align"
25132
+ ).option(
25133
+ "--skeleton",
25134
+ "record the content-free activity skeleton for this session explicitly (overrides your account default)"
25079
25135
  ).option(
25080
25136
  "--no-skeleton",
25081
- "opt out of the content-free activity skeleton (event/tool counts, files-touched, timing buckets) for this session \u2014 default on, independent of capture"
25137
+ "opt out of the content-free activity skeleton (event/tool counts, files-touched, timing buckets) for this session \u2014 independent of capture"
25082
25138
  ).addOption(
25083
25139
  new Option(
25084
25140
  "--provider <provider>",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jentrix/cli",
3
- "version": "0.5.22",
3
+ "version": "0.5.23",
4
4
  "description": "Command-line client for the Jentrix MCP surface (jentrix tool <name>, generated noun-verb commands).",
5
5
  "keywords": [
6
6
  "jentrix",
package/surface.json CHANGED
@@ -441,7 +441,7 @@
441
441
  "description": "Optional producer label for this session (\"triage-bot\"). Null = the operator themself; absent leaves it unchanged."
442
442
  },
443
443
  "capture": {
444
- "description": "TRACE capture mode the CLI declared for this session MVP default off.",
444
+ "description": "TRACE capture mode for this session. OMIT to let the server resolve it: this session's previous value, then the operator's account default (Account → Capture), then the built-in off.",
445
445
  "enum": [
446
446
  "off",
447
447
  "on"
@@ -464,7 +464,7 @@
464
464
  "type": "string"
465
465
  },
466
466
  "skeleton": {
467
- "description": "Activity-skeleton mode the CLI declared for this session (--no-skeleton). Default on, independent of capture.",
467
+ "description": "Activity-skeleton mode for this session, independent of capture. OMIT to let the server resolve it: this session's previous value, then the operator's account default, then the built-in on.",
468
468
  "enum": [
469
469
  "on",
470
470
  "off"