codiedev 0.10.0-beta.4 → 0.10.0-beta.6

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.
Files changed (157) hide show
  1. package/README.md +11 -5
  2. package/assets/tui-brand-plugin.tsx +14 -2
  3. package/dist/backup/cache.js +1 -5
  4. package/dist/backup/match.js +0 -6
  5. package/dist/backup/run.js +3 -35
  6. package/dist/captureExclusions.js +0 -12
  7. package/dist/captureLog.js +0 -28
  8. package/dist/captureReadiness.js +0 -58
  9. package/dist/captureSignals.js +68 -3
  10. package/dist/cli.js +75 -64
  11. package/dist/codexHookTrust.js +125 -6
  12. package/dist/codexLimitScan.js +2 -109
  13. package/dist/codie.js +50 -114
  14. package/dist/codieBrandInstall.js +0 -18
  15. package/dist/codieLauncher.js +20 -92
  16. package/dist/codieRuntime.js +19 -23
  17. package/dist/codieRuntimeInstall.js +108 -23
  18. package/dist/codieUsage.js +38 -41
  19. package/dist/commands/agentTemplates.generated.js +136 -0
  20. package/dist/commands/agents.js +1066 -0
  21. package/dist/commands/agentsSdk.generated.js +7 -0
  22. package/dist/commands/ask.js +0 -3
  23. package/dist/commands/capture.js +0 -2
  24. package/dist/commands/createTicket.js +0 -10
  25. package/dist/commands/doctor.js +53 -120
  26. package/dist/commands/evals.js +187 -0
  27. package/dist/commands/inbox.js +0 -2
  28. package/dist/commands/library.js +0 -3
  29. package/dist/commands/pack.js +573 -0
  30. package/dist/commands/ping.js +0 -2
  31. package/dist/commands/pull.js +6 -10
  32. package/dist/commands/push.js +2 -12
  33. package/dist/commands/search.js +0 -4
  34. package/dist/commands/send.js +0 -5
  35. package/dist/commands/share.js +0 -4
  36. package/dist/commands/shared.js +0 -35
  37. package/dist/commands/traces.js +344 -0
  38. package/dist/commands/tracesLabel.js +348 -0
  39. package/dist/configDir.js +0 -10
  40. package/dist/connect.js +19 -116
  41. package/dist/connectFlow.js +0 -9
  42. package/dist/copilotHooks.js +391 -0
  43. package/dist/copilotInstallation.js +64 -0
  44. package/dist/cursorHooks.js +38 -30
  45. package/dist/cursorLifecycle.js +109 -0
  46. package/dist/detection.js +0 -30
  47. package/dist/harnessDirs.js +77 -0
  48. package/dist/hook.js +182 -469
  49. package/dist/inboxFormat.js +0 -24
  50. package/dist/launchContext.js +150 -0
  51. package/dist/launcher.js +140 -0
  52. package/dist/limitEvent.js +41 -35
  53. package/dist/limitEventReporter.js +1 -1
  54. package/dist/managedPi.js +240 -0
  55. package/dist/mcp.js +3 -384
  56. package/dist/nativeSteering.js +24 -0
  57. package/dist/packs/codie.js +182 -0
  58. package/dist/packs/harnesses.js +411 -0
  59. package/dist/packs/hookGate.js +256 -0
  60. package/dist/packs/install.js +286 -0
  61. package/dist/packs/manifest.js +126 -0
  62. package/dist/packs/paths.js +81 -0
  63. package/dist/packs/shim.js +214 -0
  64. package/dist/packs/store.js +155 -0
  65. package/dist/packs/sync.js +170 -0
  66. package/dist/piHooks.js +216 -0
  67. package/dist/piRuntime.js +120 -0
  68. package/dist/repoResolver.js +0 -19
  69. package/dist/secret.js +0 -3
  70. package/dist/selfUpdate.js +376 -0
  71. package/dist/skillMarker.js +23 -0
  72. package/dist/subagents.js +135 -0
  73. package/dist/teamSkillContext.js +10 -0
  74. package/dist/transcriptModels.js +6 -8
  75. package/dist/upgradeNudge.js +0 -30
  76. package/dist/utils.js +494 -824
  77. package/dist/version.js +0 -10
  78. package/dist/versionCheck.js +0 -33
  79. package/package.json +8 -9
  80. package/dist/backup/cache.d.ts +0 -14
  81. package/dist/backup/match.d.ts +0 -2
  82. package/dist/backup/run.d.ts +0 -25
  83. package/dist/captureExclusions.d.ts +0 -30
  84. package/dist/captureLog.d.ts +0 -20
  85. package/dist/captureReadiness.d.ts +0 -65
  86. package/dist/captureSignals.d.ts +0 -19
  87. package/dist/cli.d.ts +0 -16
  88. package/dist/codexHookTrust.d.ts +0 -34
  89. package/dist/codexLimitScan.d.ts +0 -64
  90. package/dist/codie.d.ts +0 -2
  91. package/dist/codieBrandInstall.d.ts +0 -11
  92. package/dist/codieLauncher.d.ts +0 -86
  93. package/dist/codieRuntime.d.ts +0 -29
  94. package/dist/codieRuntimeInstall.d.ts +0 -28
  95. package/dist/codieStartup.d.ts +0 -26
  96. package/dist/codieStartup.js +0 -30
  97. package/dist/codieUsage.d.ts +0 -57
  98. package/dist/commands/ask.d.ts +0 -1
  99. package/dist/commands/capture.d.ts +0 -14
  100. package/dist/commands/createTicket.d.ts +0 -27
  101. package/dist/commands/delete.d.ts +0 -1
  102. package/dist/commands/doctor.d.ts +0 -1
  103. package/dist/commands/inbox.d.ts +0 -3
  104. package/dist/commands/library.d.ts +0 -2
  105. package/dist/commands/note.d.ts +0 -1
  106. package/dist/commands/ping.d.ts +0 -1
  107. package/dist/commands/promote.d.ts +0 -1
  108. package/dist/commands/promote.js +0 -53
  109. package/dist/commands/pull.d.ts +0 -2
  110. package/dist/commands/push.d.ts +0 -33
  111. package/dist/commands/react.d.ts +0 -2
  112. package/dist/commands/react.js +0 -40
  113. package/dist/commands/reverseTicket.d.ts +0 -2
  114. package/dist/commands/reverseTicket.js +0 -477
  115. package/dist/commands/search.d.ts +0 -2
  116. package/dist/commands/security.d.ts +0 -1
  117. package/dist/commands/security.js +0 -156
  118. package/dist/commands/send.d.ts +0 -2
  119. package/dist/commands/share.d.ts +0 -2
  120. package/dist/commands/shared.d.ts +0 -64
  121. package/dist/configDir.d.ts +0 -1
  122. package/dist/connect.d.ts +0 -1
  123. package/dist/connectFlow.d.ts +0 -13
  124. package/dist/cursorHooks.d.ts +0 -23
  125. package/dist/detection.d.ts +0 -30
  126. package/dist/hook.d.ts +0 -6
  127. package/dist/inboxFormat.d.ts +0 -38
  128. package/dist/limitEvent.d.ts +0 -16
  129. package/dist/limitEventReporter.d.ts +0 -21
  130. package/dist/mcp.d.ts +0 -12
  131. package/dist/prUrl.d.ts +0 -9
  132. package/dist/prUrl.js +0 -33
  133. package/dist/repoResolver.d.ts +0 -17
  134. package/dist/secret.d.ts +0 -1
  135. package/dist/security.d.ts +0 -102
  136. package/dist/security.js +0 -437
  137. package/dist/securityDeepScan.d.ts +0 -41
  138. package/dist/securityDeepScan.js +0 -248
  139. package/dist/securityDoctor.d.ts +0 -32
  140. package/dist/securityDoctor.js +0 -63
  141. package/dist/securityFeature.d.ts +0 -5
  142. package/dist/securityFeature.js +0 -7
  143. package/dist/securityInstall.d.ts +0 -63
  144. package/dist/securityInstall.js +0 -239
  145. package/dist/securityInventory.d.ts +0 -54
  146. package/dist/securityInventory.js +0 -556
  147. package/dist/securityPilot.d.ts +0 -19
  148. package/dist/securityPilot.js +0 -61
  149. package/dist/securityPolicy.d.ts +0 -33
  150. package/dist/securityPolicy.js +0 -188
  151. package/dist/securitySession.d.ts +0 -6
  152. package/dist/securitySession.js +0 -72
  153. package/dist/transcriptModels.d.ts +0 -18
  154. package/dist/upgradeNudge.d.ts +0 -25
  155. package/dist/utils.d.ts +0 -346
  156. package/dist/version.d.ts +0 -1
  157. package/dist/versionCheck.d.ts +0 -68
package/dist/cli.js CHANGED
@@ -1,19 +1,5 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";
3
- /**
4
- * Codiedev CLI dispatcher.
5
- *
6
- * Subcommands:
7
- * codiedev connect — link agent CLIs to your org
8
- * codiedev push <file> — author/update a spec/review/decision/etc.
9
- * codiedev pull <key> — fetch an artifact by filename key
10
- * codiedev ping <user> "<msg>" — send a message (optionally --with <key>)
11
- * codiedev inbox — list pings (use --unread to filter)
12
- * codiedev read <ping-id> — mark read + show full message
13
- * codiedev note "<text>" — capture a thought
14
- * codiedev promote <artifact-id> — promote an extracted artifact to authored
15
- * codiedev help — show this help
16
- */
17
3
  Object.defineProperty(exports, "__esModule", { value: true });
18
4
  const connect_1 = require("./connect");
19
5
  const push_1 = require("./commands/push");
@@ -23,17 +9,19 @@ const ask_1 = require("./commands/ask");
23
9
  const ping_1 = require("./commands/ping");
24
10
  const inbox_1 = require("./commands/inbox");
25
11
  const note_1 = require("./commands/note");
26
- const promote_1 = require("./commands/promote");
27
- const reverseTicket_1 = require("./commands/reverseTicket");
28
12
  const createTicket_1 = require("./commands/createTicket");
29
13
  const doctor_1 = require("./commands/doctor");
30
14
  const search_1 = require("./commands/search");
31
15
  const library_1 = require("./commands/library");
32
16
  const share_1 = require("./commands/share");
33
17
  const send_1 = require("./commands/send");
34
- const react_1 = require("./commands/react");
35
18
  const capture_1 = require("./commands/capture");
36
- const security_1 = require("./commands/security");
19
+ const pack_1 = require("./commands/pack");
20
+ const traces_1 = require("./commands/traces");
21
+ const agents_1 = require("./commands/agents");
22
+ const evals_1 = require("./commands/evals");
23
+ const hookGate_1 = require("./packs/hookGate");
24
+ const sync_1 = require("./packs/sync");
37
25
  const version_1 = require("./version");
38
26
  const HELP = `
39
27
  Codiedev CLI
@@ -43,14 +31,9 @@ Codiedev CLI
43
31
  someone left off across sessions and providers.
44
32
 
45
33
  Connect:
46
- codiedev connect Link Claude Code / Codex / Cursor to your org
47
- codiedev doctor Verify setup, capture, and Agent Security
48
- codiedev security pilot Run safe synthetic policy checks
49
- codiedev security inventory Inventory MCPs, skills, hooks, plugins,
50
- instructions, and editor extensions
51
- codiedev security scan --deep Run installed open-source scanners too
52
- codiedev security sync Fetch administrator policy locally
53
- codiedev security rollback --yes Restore pre-update hook configuration
34
+ codiedev connect Link Claude Code / Codex / Cursor /
35
+ GitHub Copilot / pi to your org
36
+ codiedev doctor Verify setup and capture
54
37
 
55
38
  Harness:
56
39
  codie Launch the governed Codie harness.
@@ -70,14 +53,6 @@ Artifacts:
70
53
  team's collective memory. Returns an
71
54
  LLM-synthesized answer with citations
72
55
  pointing at sessions + artifacts.
73
- codiedev promote <artifact-id> Promote an auto-extracted artifact
74
- to an authored one
75
- codiedev reverse-ticket [<pr-url>] Generate a Jira ticket draft.
76
- No URL = branch mode (uses your current
77
- local git branch, no PR needed).
78
- Draft lands in /portal/agentic-ticketing.
79
- codiedev reverse-ticket --base <ref> Override the base ref in branch mode
80
- (defaults to origin/main).
81
56
  codiedev create-ticket [<prompt>] Forward-write a scoped ticket plan.
82
57
  Claude investigates the codebase, asks
83
58
  "single repo or multiple? where?", then
@@ -96,6 +71,30 @@ Messaging:
96
71
  codiedev inbox [--unread] [--limit N] Show messages
97
72
  codiedev read <ping-id> Mark read + show full message
98
73
 
74
+ Starter packs:
75
+ codiedev pack list Packs assigned to your team
76
+ codiedev pack install [slug] [--harness claude,codex,copilot,cursor,codie,pi]
77
+ [--dry-run] [--json]
78
+ Install a pack (or all) into every
79
+ detected tool
80
+ codiedev pack status Installed packs, up to date or not
81
+
82
+ Traces (agents on the gateway):
83
+ codiedev agents init | deploy | run | runs | show | secrets | webhook
84
+ Deploy agents and run them in fresh sandboxes
85
+ (codiedev agents help)
86
+ codiedev traces agents Agents you maintain, with saved test cases
87
+ codiedev traces cases <agent> [--dataset name [--version n]] [--model id]
88
+ Pull an agent's test cases (or one dataset)
89
+ into this repo for your coding agent to
90
+ run, optionally on another model
91
+ codiedev traces report <results.json> Send the results back to Traces
92
+ codiedev traces overview | runs | run | score | scores | score-kinds | queue | judge | import | add-example | versions | save-version
93
+ Agents, runs, failures, scores, review queues,
94
+ automatic scoring and datasets (codiedev traces help)
95
+ codiedev evals report <run.json> File one variant's eval scores under an experiment
96
+ codiedev evals last <subject> Latest experiment on a subject + its champion
97
+
99
98
  Discovery:
100
99
  codiedev search "<query>" Semantic search across the team's
101
100
  captured sessions and artifacts
@@ -103,9 +102,6 @@ Discovery:
103
102
  [--type T] [--tag X] [--limit N]
104
103
  List artifacts in your workspace
105
104
 
106
- Feed:
107
- codiedev react <postId> <emoji> Toggle a reaction on a feed post
108
-
109
105
  Privacy:
110
106
  codiedev ignore <path> Never capture this folder or its children
111
107
  codiedev unignore <path> Stop ignoring a folder
@@ -159,6 +155,10 @@ locally before any capture leaves your machine, so an offline or failed portal
159
155
  status sync never weakens it.
160
156
  `.trim();
161
157
  const COMMAND_HELP = {
158
+ pack: pack_1.PACK_HELP,
159
+ traces: traces_1.TRACES_HELP,
160
+ agents: agents_1.AGENTS_HELP,
161
+ evals: evals_1.EVALS_HELP,
162
162
  connect: `
163
163
  codiedev connect — link your agent CLI to Codiedev
164
164
 
@@ -168,8 +168,11 @@ codiedev connect — link your agent CLI to Codiedev
168
168
  - ~/.claude/CLAUDE.md instructions (teaches Claude the commands)
169
169
  - ~/.codex/hooks.json hook (same, for Codex)
170
170
  - ~/.codex/AGENTS.md instructions (same, for Codex)
171
- - ~/.cursor/hooks.json hooks (session, prompt, security, capture)
171
+ - ~/.cursor/hooks.json hooks (session, backup, capture)
172
172
  - ~/.cursor/rules/codiedev.mdc (teaches Cursor the commands)
173
+ - ~/.copilot/hooks/codiedev.json (GitHub Copilot: VS Code + CLI capture)
174
+ - ~/.pi/agent/extensions/codiedev.ts (pi: session, prompt, capture)
175
+ - ~/.pi/agent/AGENTS.md (teaches pi the commands)
173
176
 
174
177
  Re-run anytime to swap accounts or refresh the instruction block.
175
178
 
@@ -275,20 +278,6 @@ codiedev note — capture a passing thought, linked to your session
275
278
  unignore: IGNORE_HELP,
276
279
  ignored: IGNORE_HELP,
277
280
  capture: IGNORE_HELP,
278
- promote: `
279
- codiedev promote — promote an auto-extracted artifact to authored
280
-
281
- Usage:
282
- codiedev promote <artifact-id> [--key filename.md]
283
-
284
- The session-capture pipeline auto-extracts spec/bugfix/decision
285
- artifacts from each agent session. Promote turns one of those into
286
- a first-class authored artifact your team can ping, pull, and
287
- update versions of.
288
-
289
- Find artifact-ids in the portal under Knowledge → Memory, or via
290
- the portal search.
291
- `.trim(),
292
281
  "create-ticket": `
293
282
  codiedev create-ticket — forward-write a scoped ticket plan
294
283
 
@@ -414,8 +403,27 @@ Run 'codiedev help <command>' for each, or see the quick list:
414
403
 
415
404
  Tokens are per-user. Rotate any time by revoking + creating a new one.
416
405
  `.trim();
406
+ const REMOVED_COMMANDS = {
407
+ promote: "Share an auto-saved spec with 'Promote to team' in the portal's My artifacts list.",
408
+ "reverse-ticket": "Use 'codiedev create-ticket' to draft Jira tickets.",
409
+ reverseTicket: "Use 'codiedev create-ticket' to draft Jira tickets.",
410
+ react: "React to feed posts in the portal.",
411
+ };
412
+ function removedCommand(command) {
413
+ console.error(`'codiedev ${command}' has been removed. ${REMOVED_COMMANDS[command] ?? ""}`.trim());
414
+ process.exit(1);
415
+ }
417
416
  async function main() {
418
417
  const [, , command, ...rest] = process.argv;
418
+ if (command === "hook-gate") {
419
+ (0, hookGate_1.runHookGateCli)(rest);
420
+ return;
421
+ }
422
+ if (command && !["pack", "packs", "help", "docs", "version", "--version", "-v", "--help", "-h"].includes(command) && !rest.includes("--json")) {
423
+ const nag = (0, sync_1.packNagLine)();
424
+ if (nag)
425
+ console.error(`\n${nag}\n`);
426
+ }
419
427
  if (!command || command === "--help" || command === "-h") {
420
428
  console.log(HELP);
421
429
  return;
@@ -438,7 +446,6 @@ async function main() {
438
446
  console.log(DOCS);
439
447
  return;
440
448
  }
441
- // Support `codiedev <command> --help` too.
442
449
  if (rest.includes("--help") || rest.includes("-h")) {
443
450
  if (COMMAND_HELP[command]) {
444
451
  console.log(COMMAND_HELP[command]);
@@ -458,9 +465,6 @@ async function main() {
458
465
  case "verify":
459
466
  await (0, doctor_1.runDoctor)(rest);
460
467
  return;
461
- case "security":
462
- await (0, security_1.runSecurityCommand)(rest);
463
- return;
464
468
  case "push":
465
469
  await (0, push_1.runPush)(rest);
466
470
  return;
@@ -479,7 +483,6 @@ async function main() {
479
483
  return;
480
484
  case "inbox":
481
485
  case "check":
482
- // Support `codiedev check inbox` as an alias.
483
486
  if (command === "check" && rest[0] === "inbox") {
484
487
  await (0, inbox_1.runInbox)(rest.slice(1));
485
488
  }
@@ -494,11 +497,10 @@ async function main() {
494
497
  await (0, note_1.runNote)(rest);
495
498
  return;
496
499
  case "promote":
497
- await (0, promote_1.runPromote)(rest);
498
- return;
499
500
  case "reverse-ticket":
500
501
  case "reverseTicket":
501
- await (0, reverseTicket_1.runReverseTicket)(rest);
502
+ case "react":
503
+ removedCommand(command);
502
504
  return;
503
505
  case "create-ticket":
504
506
  case "createTicket":
@@ -511,22 +513,31 @@ async function main() {
511
513
  case "ls":
512
514
  await (0, library_1.runLibrary)(rest);
513
515
  return;
516
+ case "traces":
517
+ await (0, traces_1.runTraces)(rest);
518
+ return;
519
+ case "agents":
520
+ await (0, agents_1.runAgents)(rest);
521
+ return;
522
+ case "evals":
523
+ await (0, evals_1.runEvals)(rest);
524
+ return;
514
525
  case "share":
515
526
  await (0, share_1.runShare)(rest);
516
527
  return;
517
528
  case "send":
518
529
  await (0, send_1.runSend)(rest);
519
530
  return;
520
- case "react":
521
- await (0, react_1.runReact)(rest);
522
- return;
523
531
  case "ignore":
524
532
  case "unignore":
525
533
  case "ignored":
526
534
  await (0, capture_1.runCaptureCommand)([command, ...rest]);
527
535
  return;
536
+ case "pack":
537
+ case "packs":
538
+ await (0, pack_1.runPack)(rest);
539
+ return;
528
540
  case "capture":
529
- // Back-compat alias for the earlier `codiedev capture <sub>` form.
530
541
  await (0, capture_1.runCaptureCommand)(rest);
531
542
  return;
532
543
  default:
@@ -33,10 +33,13 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.codexPluginContentHash = codexPluginContentHash;
36
37
  exports.codexCommandHookHash = codexCommandHookHash;
37
38
  exports.codiedevCodexHookTrustEntries = codiedevCodexHookTrustEntries;
38
39
  exports.writeCodexHookTrust = writeCodexHookTrust;
39
40
  exports.inspectCodexHookTrust = inspectCodexHookTrust;
41
+ exports.packCodexHookTrustEntries = packCodexHookTrustEntries;
42
+ exports.writeCodexPackHookTrust = writeCodexPackHookTrust;
40
43
  const crypto = __importStar(require("crypto"));
41
44
  const fs = __importStar(require("fs"));
42
45
  const path = __importStar(require("path"));
@@ -67,6 +70,42 @@ const MATCHER_EVENTS = new Set([
67
70
  "SubagentStart",
68
71
  "SubagentStop",
69
72
  ]);
73
+ function codexPluginContentHash(root) {
74
+ const hash = crypto.createHash("sha256");
75
+ hash.update("codex-plugin-bundle-v1");
76
+ const u64 = (n) => {
77
+ const b = Buffer.alloc(8);
78
+ b.writeBigUInt64LE(BigInt(n));
79
+ return b;
80
+ };
81
+ const pending = [root];
82
+ while (pending.length) {
83
+ const current = pending.pop();
84
+ const relative = path.relative(root, current).split(path.sep).join("/");
85
+ const name = path.posix.basename(relative);
86
+ if (name === "__pycache__" || name === ".venv" || name.endsWith(".pyc") || name.endsWith(".egg-info"))
87
+ continue;
88
+ const nameBytes = Buffer.from(relative, "utf8");
89
+ hash.update(u64(nameBytes.length));
90
+ hash.update(nameBytes);
91
+ const stat = fs.lstatSync(current);
92
+ if (stat.isDirectory()) {
93
+ hash.update("directory");
94
+ const entries = fs.readdirSync(current).sort((a, b) => Buffer.compare(Buffer.from(a), Buffer.from(b)));
95
+ for (const entry of entries.reverse())
96
+ pending.push(path.join(current, entry));
97
+ }
98
+ else if (stat.isFile()) {
99
+ hash.update("file");
100
+ hash.update(u64(stat.size));
101
+ hash.update(fs.readFileSync(current));
102
+ }
103
+ else {
104
+ throw new Error(`unsupported plugin entry type: ${current}`);
105
+ }
106
+ }
107
+ return `sha256:${hash.digest("hex")}`;
108
+ }
70
109
  function canonicalize(value) {
71
110
  if (Array.isArray(value))
72
111
  return value.map(canonicalize);
@@ -85,8 +124,6 @@ function normalizedTimeout(eventName, value) {
85
124
  return Math.min(3, Math.max(1, configured ?? 1));
86
125
  return Math.max(1, configured ?? 600);
87
126
  }
88
- /** Codex hashes a canonical, normalized hook identity before it will execute a
89
- * user hook. Keep this compatible with codex_config::version_for_toml. */
90
127
  function codexCommandHookHash(eventName, group, handler) {
91
128
  const normalizedHandler = {
92
129
  type: "command",
@@ -155,6 +192,27 @@ function codiedevCodexHookTrustEntries(hooksPath, hooksFile) {
155
192
  }
156
193
  return entries;
157
194
  }
195
+ function stripBlockKeepingForeign(text, begin, end) {
196
+ const foreign = [];
197
+ const stripped = text.replace(new RegExp(`\\n?${begin}\\n([\\s\\S]*?)${end}\\n?`, "g"), (_match, body) => {
198
+ let table = null;
199
+ const flush = () => {
200
+ if (table && table.join("").trim())
201
+ foreign.push(table.join("\n").trimEnd());
202
+ };
203
+ for (const line of body.split("\n")) {
204
+ if (/^\s*\[/.test(line)) {
205
+ flush();
206
+ table = /^\s*\[hooks\.state\./.test(line) ? null : [];
207
+ }
208
+ if (table)
209
+ table.push(line);
210
+ }
211
+ flush();
212
+ return "\n";
213
+ });
214
+ return foreign.length ? `${stripped.trimEnd()}\n\n${foreign.join("\n\n")}\n` : stripped;
215
+ }
158
216
  function tableHeader(key) {
159
217
  return `[hooks.state.${JSON.stringify(key)}]`;
160
218
  }
@@ -173,13 +231,10 @@ function removeTable(lines, header) {
173
231
  }
174
232
  return output;
175
233
  }
176
- /** `codiedev connect` is an explicit install action, so trust only the exact
177
- * Codiedev commands it just wrote. Any later command change produces a new
178
- * hash and Codex disables that hook until connect is run again. */
179
234
  function writeCodexHookTrust(configPath, hooksPath, hooksFile) {
180
235
  const entries = codiedevCodexHookTrustEntries(hooksPath, hooksFile);
181
236
  let existing = fs.existsSync(configPath) ? fs.readFileSync(configPath, "utf8") : "";
182
- existing = existing.replace(new RegExp(`\\n?${TRUST_BEGIN}\\n[\\s\\S]*?${TRUST_END}\\n?`, "g"), "\n");
237
+ existing = stripBlockKeepingForeign(existing, TRUST_BEGIN, TRUST_END);
183
238
  let lines = existing.split("\n");
184
239
  for (const entry of entries)
185
240
  lines = removeTable(lines, tableHeader(entry.key));
@@ -226,3 +281,67 @@ function inspectCodexHookTrust(configPath, hooksPath, hooksFile, requiredSubcomm
226
281
  }).length;
227
282
  return { ok: entries.length > 0 && trusted === entries.length, total: entries.length, trusted };
228
283
  }
284
+ const PACK_TRUST_BEGIN = "# codiedev-pack-trust:begin";
285
+ const PACK_TRUST_END = "# codiedev-pack-trust:end";
286
+ function packCodexHookTrustEntries(hooksPath, hooksFile, plugin) {
287
+ const events = hooksFile?.hooks;
288
+ if (!events || typeof events !== "object")
289
+ return [];
290
+ const source = plugin
291
+ ? `${plugin.id}:${path.relative(plugin.root, hooksPath).split(path.sep).join("/")}`
292
+ : normalizedHooksPath(hooksPath);
293
+ const contentHash = plugin ? codexPluginContentHash(plugin.root) : undefined;
294
+ const entries = [];
295
+ for (const [eventName, groupsValue] of Object.entries(events)) {
296
+ const eventLabel = EVENT_LABELS[eventName];
297
+ if (!eventLabel || !Array.isArray(groupsValue))
298
+ continue;
299
+ groupsValue.forEach((groupValue, groupIndex) => {
300
+ const group = groupValue;
301
+ if (!group || !Array.isArray(group.hooks))
302
+ return;
303
+ group.hooks.forEach((handlerValue, handlerIndex) => {
304
+ const handler = handlerValue;
305
+ if (!handler || handler.type !== "command" || typeof handler.command !== "string")
306
+ return;
307
+ entries.push({
308
+ key: `${source}:${eventLabel}:${groupIndex}:${handlerIndex}`,
309
+ eventName,
310
+ command: handler.command,
311
+ currentHash: codexCommandHookHash(eventName, group, handler),
312
+ ...(contentHash ? { contentHash } : {}),
313
+ });
314
+ });
315
+ });
316
+ }
317
+ return entries;
318
+ }
319
+ function writeCodexPackHookTrust(configPath, entries) {
320
+ let existing = fs.existsSync(configPath) ? fs.readFileSync(configPath, "utf8") : "";
321
+ existing = stripBlockKeepingForeign(existing, PACK_TRUST_BEGIN, PACK_TRUST_END);
322
+ let lines = existing.split("\n");
323
+ for (const entry of entries)
324
+ lines = removeTable(lines, tableHeader(entry.key));
325
+ const base = lines.join("\n").trimEnd();
326
+ if (entries.length === 0) {
327
+ const next = base ? `${base}\n` : "";
328
+ if (!fs.existsSync(configPath) && !next)
329
+ return entries;
330
+ fs.mkdirSync(path.dirname(configPath), { recursive: true, mode: 0o700 });
331
+ fs.writeFileSync(configPath, next, { encoding: "utf8", mode: 0o600 });
332
+ fs.chmodSync(configPath, 0o600);
333
+ return entries;
334
+ }
335
+ const trustBlock = entries.flatMap((entry) => [
336
+ tableHeader(entry.key),
337
+ "enabled = true",
338
+ `trusted_hash = ${JSON.stringify(entry.currentHash)}`,
339
+ ...(entry.contentHash ? [`trusted_content_hash = ${JSON.stringify(entry.contentHash)}`] : []),
340
+ "",
341
+ ]).join("\n");
342
+ const next = `${base}${base ? "\n\n" : ""}${PACK_TRUST_BEGIN}\n${trustBlock}${PACK_TRUST_END}\n`;
343
+ fs.mkdirSync(path.dirname(configPath), { recursive: true, mode: 0o700 });
344
+ fs.writeFileSync(configPath, next, { encoding: "utf8", mode: 0o600 });
345
+ fs.chmodSync(configPath, 0o600);
346
+ return entries;
347
+ }