@jentrix/cli 0.5.18 → 0.5.20

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "jentrix",
3
- "version": "0.2.4",
4
- "description": "Connect Codex tasks to Jentrix projects with trusted lifecycle hooks, alignment, checkpoints, and honest session capture.",
3
+ "version": "0.2.5",
4
+ "description": "Connect Codex tasks to Jentrix with trusted lifecycle hooks, exact-rollout telemetry recovery, typed artifacts, and operator-confirmed alignment.",
5
5
  "author": {
6
6
  "name": "Jentrix",
7
7
  "url": "https://github.com/jentrix-au"
@@ -14,7 +14,7 @@
14
14
  "interface": {
15
15
  "displayName": "Jentrix",
16
16
  "shortDescription": "Connect this Codex task to Jentrix.",
17
- "longDescription": "Align a Codex task to a Jentrix project and task, preserve typed checkpoints, and capture supported lifecycle evidence through trusted hooks.",
17
+ "longDescription": "Align a Codex task to a Jentrix project and task, preserve typed artifacts and checkpoints, capture supported lifecycle evidence through trusted hooks, and recover provider-reported usage from the exact task rollout when hooks are unavailable.",
18
18
  "developerName": "Jentrix",
19
19
  "category": "Productivity",
20
20
  "capabilities": ["Interactive", "Write"],
@@ -1,12 +1,13 @@
1
1
  {
2
- "description": "Trusted Codex lifecycle bridge. Hook payloads are appended locally; compact hooks also ask the Jentrix CLI to preserve the approved capture boundary.",
2
+ "description": "Trusted Codex lifecycle bridge. Structured hook stdin is appended locally; credentials and transcript content never enter argv. Hooks remain inactive until the operator reviews and trusts them. PreCompact/PostCompact additionally preserve the approved capture boundary; capture off records only the boundary, never transcript content.",
3
3
  "hooks": {
4
4
  "SessionStart": [
5
5
  {
6
6
  "hooks": [
7
7
  {
8
8
  "type": "command",
9
- "command": "stacks-runner session-hook --provider codex --event SessionStart"
9
+ "command": "stacks-runner session-hook --provider codex --event SessionStart",
10
+ "timeout": 3
10
11
  }
11
12
  ]
12
13
  }
@@ -16,7 +17,8 @@
16
17
  "hooks": [
17
18
  {
18
19
  "type": "command",
19
- "command": "stacks-runner session-hook --provider codex --event UserPromptSubmit"
20
+ "command": "stacks-runner session-hook --provider codex --event UserPromptSubmit",
21
+ "timeout": 3
20
22
  }
21
23
  ]
22
24
  }
@@ -26,7 +28,8 @@
26
28
  "hooks": [
27
29
  {
28
30
  "type": "command",
29
- "command": "stacks-runner session-hook --provider codex --event PostToolUse"
31
+ "command": "stacks-runner session-hook --provider codex --event PostToolUse",
32
+ "timeout": 3
30
33
  }
31
34
  ]
32
35
  }
@@ -36,7 +39,8 @@
36
39
  "hooks": [
37
40
  {
38
41
  "type": "command",
39
- "command": "stacks-runner session-hook --provider codex --event Stop"
42
+ "command": "stacks-runner session-hook --provider codex --event Stop",
43
+ "timeout": 3
40
44
  }
41
45
  ]
42
46
  }
@@ -47,12 +51,13 @@
47
51
  {
48
52
  "type": "command",
49
53
  "command": "stacks-runner session-hook --provider codex --event PreCompact",
50
- "timeout": 10
54
+ "timeout": 3
51
55
  },
52
56
  {
53
57
  "type": "command",
54
58
  "command": "jentrix session snapshot --event PreCompact",
55
- "timeout": 10
59
+ "timeout": 10,
60
+ "statusMessage": "Preserving the Jentrix capture boundary"
56
61
  }
57
62
  ]
58
63
  }
@@ -63,12 +68,13 @@
63
68
  {
64
69
  "type": "command",
65
70
  "command": "stacks-runner session-hook --provider codex --event PostCompact",
66
- "timeout": 10
71
+ "timeout": 3
67
72
  },
68
73
  {
69
74
  "type": "command",
70
75
  "command": "jentrix session snapshot --event PostCompact",
71
- "timeout": 10
76
+ "timeout": 10,
77
+ "statusMessage": "Recording the Jentrix compaction boundary"
72
78
  }
73
79
  ]
74
80
  }
@@ -78,7 +84,8 @@
78
84
  "hooks": [
79
85
  {
80
86
  "type": "command",
81
- "command": "stacks-runner session-hook --provider codex --event SessionEnd"
87
+ "command": "stacks-runner session-hook --provider codex --event SessionEnd",
88
+ "timeout": 3
82
89
  }
83
90
  ]
84
91
  }
@@ -34,6 +34,13 @@ description: Align the current Codex task to a Jentrix workspace, project, work
34
34
  8. Echo the returned server alignment snapshot verbatim. If `.mcp.json` changed,
35
35
  explain that the current task must reconnect MCP or restart before using it.
36
36
 
37
+ If detection names an exact Codex rollout fallback instead of a lifecycle-hook
38
+ record, say what that means: provider token receipts and the opening prompt remain
39
+ collectable, but prompt/tool/assistant lifecycle events are not. Direct the operator
40
+ to review and trust the plugin hooks with `/hooks`, then start a new Codex task for
41
+ full lifecycle coverage. Never claim `coverage: COMPLETE` for token receipts proves
42
+ that hooks ran.
43
+
37
44
  TRACE capture is off by default. Pass `--capture` only when the operator explicitly
38
45
  approved full hook-derived trace capture. Same-project re-alignment converges;
39
46
  different-project re-alignment must be refused until `jentrix session end`.
@@ -18,5 +18,10 @@ trusted environment and lifecycle-hook context. Relay the command output or erro
18
18
  verbatim. The command starts the hook-watching host in the background and prints
19
19
  its pid; do not start a second host. Never put credentials on the command line.
20
20
 
21
- If trusted identity is unavailable, report the CLI's fallback exactly; do not guess
22
- from Codex session files.
21
+ When lifecycle hooks are unavailable, the CLI may recover telemetry only from the
22
+ exact `rollout-*-${CODEX_THREAD_ID}.jsonl` under the trusted Codex home. Accept that
23
+ exact match; never choose the newest session file. Explain that the fallback keeps
24
+ provider token receipts and opening-prompt provenance, but not prompt/tool/assistant
25
+ lifecycle events. Direct the operator to review and trust the plugin hooks with
26
+ `/hooks`, then start a new Codex task for full lifecycle coverage. If trusted task
27
+ identity or the exact rollout is unavailable, relay the refusal and do not guess.
@@ -8,4 +8,8 @@ description: Show the connected Jentrix session, alignment, capture health, and
8
8
 
9
9
  Run `jentrix session status` and append the session id when the user supplied one.
10
10
  Relay stdout and stderr verbatim, including capture or telemetry warnings. Never turn
11
- null token values into zero or infer provider usage that the hooks did not report.
11
+ null token values into zero or infer provider usage that Codex did not report.
12
+ `coverage: COMPLETE` describes provider token-receipt coverage only; it does not prove
13
+ the lifecycle hooks ran. If the session uses the exact-rollout fallback, report that
14
+ prompt/tool/assistant lifecycle evidence is unavailable and point to `/hooks` plus a
15
+ new Codex task for future full coverage.
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.18";
20969
+ var CLI_VERSION = "0.5.20";
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) {
@@ -22663,6 +22663,23 @@ function hooksDir(deps2, provider) {
22663
22663
  function codexHooksDir(deps2) {
22664
22664
  return hooksDir(deps2, "codex");
22665
22665
  }
22666
+ function readCodexRolloutPath(deps2, sessionId) {
22667
+ const configuredHome = deps2.env.CODEX_HOME?.trim() || null;
22668
+ const userHome = deps2.env.HOME ?? deps2.env.USERPROFILE;
22669
+ const home = configuredHome ?? (userHome ? join2(userHome, ".codex") : null);
22670
+ if (!home) return null;
22671
+ const root = join2(home, "sessions");
22672
+ const suffix = `-${sessionId}.jsonl`;
22673
+ try {
22674
+ const relative = readdirSync(root, {
22675
+ encoding: "utf8",
22676
+ recursive: true
22677
+ }).find((path) => path.endsWith(suffix));
22678
+ return relative ? join2(root, relative) : null;
22679
+ } catch {
22680
+ return null;
22681
+ }
22682
+ }
22666
22683
  function readClaudeHookContext(deps2, readFile = (path) => readFileSync2(path, "utf8"), mtimeOf = transcriptMtime, now = Date.now) {
22667
22684
  const own = deps2.env[CLAUDE_SESSION_ENV]?.trim() || null;
22668
22685
  const home = deps2.env.HOME ?? deps2.env.USERPROFILE;
@@ -22744,11 +22761,12 @@ function readCodexHookContext(deps2, readFile = (path) => readFileSync2(path, "u
22744
22761
  const { starts, endedAt } = ledger;
22745
22762
  if (own) {
22746
22763
  const source = threadId ? CODEX_THREAD_ENV : CODEX_SESSION_ENV;
22747
- const transcriptPath = transcriptForSessionIn(ledger, own);
22764
+ const hookTranscript = transcriptForSessionIn(ledger, own);
22765
+ const transcriptPath = hookTranscript ?? readCodexRolloutPath(deps2, own);
22748
22766
  return {
22749
22767
  sessionId: own,
22750
22768
  transcriptPath,
22751
- basis: `${source}=${own} \u2014 this Codex task's own id${transcriptPath ? " (rollout from its hook record)" : ""}`,
22769
+ basis: `${source}=${own} \u2014 this Codex task's own id${hookTranscript ? " (rollout from its hook record)" : transcriptPath ? " (rollout resolved from Codex sessions)" : ""}`,
22752
22770
  newerElsewhere: null
22753
22771
  };
22754
22772
  }
@@ -22771,7 +22789,7 @@ function readCodexHookContext(deps2, readFile = (path) => readFileSync2(path, "u
22771
22789
  const match = matches[0];
22772
22790
  return match ? {
22773
22791
  sessionId: match.sessionId,
22774
- transcriptPath: transcriptForSessionIn(ledger, match.sessionId),
22792
+ transcriptPath: transcriptForSessionIn(ledger, match.sessionId) ?? readCodexRolloutPath(deps2, match.sessionId),
22775
22793
  basis: `unambiguous SessionStart${match.at ? ` at ${match.at}` : ""} in ${match.cwd}`,
22776
22794
  newerElsewhere: null
22777
22795
  } : null;
@@ -22861,6 +22879,8 @@ async function runSessionAttach(flags, deps2) {
22861
22879
  }
22862
22880
  } else if (flags.provider === "claude" && flags.providerSession && !flags.transcriptPath) {
22863
22881
  flags.transcriptPath = readClaudeHookTranscript(deps2, flags.providerSession) ?? void 0;
22882
+ } else if (flags.provider === "codex" && flags.providerSession && !flags.transcriptPath) {
22883
+ flags.transcriptPath = readCodexRolloutPath(deps2, flags.providerSession) ?? void 0;
22864
22884
  }
22865
22885
  if (flags.provider === "codex") hookDir = codexHooksDir(deps2);
22866
22886
  if (!flags.providerSession) {
@@ -23890,10 +23910,11 @@ function sessionBindingCheck(detected) {
23890
23910
  function detectProvider(flags, deps2) {
23891
23911
  if (flags.provider === "codex") {
23892
23912
  const hook2 = flags.providerSession ? null : readCodexHookContext(deps2);
23913
+ const providerSessionId = flags.providerSession ?? hook2?.sessionId ?? null;
23893
23914
  return {
23894
23915
  provider: "codex",
23895
- providerSessionId: flags.providerSession ?? hook2?.sessionId ?? null,
23896
- transcriptPath: flags.transcriptPath ?? hook2?.transcriptPath ?? null,
23916
+ providerSessionId,
23917
+ transcriptPath: flags.transcriptPath ?? hook2?.transcriptPath ?? (providerSessionId ? readCodexRolloutPath(deps2, providerSessionId) : null),
23897
23918
  basis: flags.providerSession ? `codex thread ${flags.providerSession} (explicit)` : hook2?.basis ?? null,
23898
23919
  newerElsewhere: null,
23899
23920
  transcriptGap: null,
@@ -27503,7 +27524,7 @@ function registerLogoutCommand(program3, deps2, onExit2) {
27503
27524
 
27504
27525
  // src/commands/plugin.ts
27505
27526
  import { existsSync as existsSync4 } from "node:fs";
27506
- import { join as join4, resolve as resolve2 } from "node:path";
27527
+ import { join as join4, resolve as resolve2, win32 } from "node:path";
27507
27528
  var MARKETPLACE_NAME = "jentrix";
27508
27529
  var PLUGIN_REF = "jentrix@jentrix";
27509
27530
  var LEGACY_MARKETPLACE = "stacks";
@@ -27543,6 +27564,14 @@ function persistentPluginRoot(globalNodeModules, ownRoot, exists) {
27543
27564
  function isPluginMarketplaceDir(dir) {
27544
27565
  return existsSync4(join4(dir, ".claude-plugin", "marketplace.json"));
27545
27566
  }
27567
+ function samePluginPath(a, b, win = process.platform === "win32") {
27568
+ const normalize = (raw) => {
27569
+ const bare = raw.replace(/^\\\\\?\\UNC\\/i, "\\\\").replace(/^\\\\\?\\/, "");
27570
+ const abs = win ? win32.resolve(bare) : resolve2(bare);
27571
+ return win ? abs.toLowerCase() : abs;
27572
+ };
27573
+ return normalize(a) === normalize(b);
27574
+ }
27546
27575
  function isCodexPluginMarketplaceDir(dir) {
27547
27576
  return existsSync4(join4(dir, ".agents", "plugins", "marketplace.json"));
27548
27577
  }
@@ -27589,7 +27618,7 @@ async function installCodexPlugin(deps2, codex, pluginDir) {
27589
27618
  const existing = marketplaces.find((row) => row.name === MARKETPLACE_NAME);
27590
27619
  if (existing) {
27591
27620
  const source = existing.marketplaceSource;
27592
- if (source?.sourceType !== "local" || typeof source.source !== "string" || resolve2(source.source) !== resolve2(pluginDir)) {
27621
+ if (source?.sourceType !== "local" || typeof source.source !== "string" || !samePluginPath(source.source, pluginDir)) {
27593
27622
  deps2.writeErr(
27594
27623
  `PLUGIN_MARKETPLACE_CONFLICT: Codex marketplace "${MARKETPLACE_NAME}" points somewhere else \u2014 inspect with \`codex plugin marketplace list\`; if intentional, run \`codex plugin marketplace remove ${MARKETPLACE_NAME}\` and retry`
27595
27624
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jentrix/cli",
3
- "version": "0.5.18",
3
+ "version": "0.5.20",
4
4
  "description": "Command-line client for the Jentrix MCP surface (jentrix tool <name>, generated noun-verb commands).",
5
5
  "keywords": [
6
6
  "jentrix",