@ironbee-ai/cli 0.30.0 → 0.32.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.
Files changed (125) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/analytics/claude/emit.js +1 -1
  3. package/dist/analytics/claude/state.js +1 -1
  4. package/dist/analytics/codex/events-emit.js +2 -2
  5. package/dist/analytics/codex/subagent-transcripts.js +3 -3
  6. package/dist/clients/claude/agents/ironbee-scenario.md +4 -1
  7. package/dist/clients/claude/agents/ironbee-verifier.md +21 -3
  8. package/dist/clients/claude/hooks/activity-end.js +1 -1
  9. package/dist/clients/claude/hooks/activity-start.js +1 -1
  10. package/dist/clients/claude/hooks/clear-verdict.js +1 -1
  11. package/dist/clients/claude/hooks/require-verdict.js +2 -2
  12. package/dist/clients/claude/hooks/require-verification.js +4 -4
  13. package/dist/clients/claude/hooks/session-end.js +1 -1
  14. package/dist/clients/claude/hooks/session-start.js +4 -4
  15. package/dist/clients/claude/hooks/session-status.js +2 -2
  16. package/dist/clients/claude/hooks/subagent-start.js +1 -1
  17. package/dist/clients/claude/hooks/subagent-stop.js +1 -1
  18. package/dist/clients/claude/hooks/track-action-monitor.js +1 -1
  19. package/dist/clients/claude/hooks/track-action.js +1 -1
  20. package/dist/clients/claude/hooks/verify-gate.js +4 -4
  21. package/dist/clients/claude/index.js +4 -4
  22. package/dist/clients/claude/platforms/scenario.android.md +1 -0
  23. package/dist/clients/claude/platforms/scenario.terminal.md +26 -0
  24. package/dist/clients/claude/platforms/skill.android.md +4 -0
  25. package/dist/clients/claude/platforms/skill.browser.md +1 -1
  26. package/dist/clients/claude/platforms/skill.terminal.md +62 -0
  27. package/dist/clients/claude/process-analytics.js +1 -1
  28. package/dist/clients/claude/statusline-toggle.js +2 -2
  29. package/dist/clients/codex/agents/ironbee-scenario.md +3 -0
  30. package/dist/clients/codex/agents/ironbee-verifier.md +20 -2
  31. package/dist/clients/codex/commands/ironbee-manage-scenario/SKILL.main.md +3 -0
  32. package/dist/clients/codex/commands/ironbee-search-scenario/SKILL.main.md +3 -0
  33. package/dist/clients/codex/commands/ironbee-sync-scenario/SKILL.main.md +3 -0
  34. package/dist/clients/codex/commands/ironbee-verify/SKILL.main.md +3 -0
  35. package/dist/clients/codex/hooks/activity-end.js +1 -1
  36. package/dist/clients/codex/hooks/activity-start.js +1 -1
  37. package/dist/clients/codex/hooks/clear-verdict.js +3 -3
  38. package/dist/clients/codex/hooks/require-verdict.js +2 -2
  39. package/dist/clients/codex/hooks/require-verification.js +3 -3
  40. package/dist/clients/codex/hooks/session-start.js +3 -3
  41. package/dist/clients/codex/hooks/subagent-start.js +1 -1
  42. package/dist/clients/codex/hooks/subagent-stop.js +1 -1
  43. package/dist/clients/codex/hooks/track-action-monitor.js +1 -1
  44. package/dist/clients/codex/hooks/track-action-pre.js +1 -1
  45. package/dist/clients/codex/hooks/track-action.js +1 -1
  46. package/dist/clients/codex/hooks/verify-gate.js +1 -1
  47. package/dist/clients/codex/index.js +2 -2
  48. package/dist/clients/codex/platforms/command-verify.android.md +1 -0
  49. package/dist/clients/codex/platforms/command-verify.terminal.md +61 -0
  50. package/dist/clients/codex/platforms/rule.android.md +2 -1
  51. package/dist/clients/codex/platforms/rule.terminal.md +31 -0
  52. package/dist/clients/codex/platforms/scenario.android.md +1 -0
  53. package/dist/clients/codex/platforms/scenario.terminal.md +36 -0
  54. package/dist/clients/codex/platforms/skill.android.md +4 -0
  55. package/dist/clients/codex/platforms/skill.browser.md +1 -1
  56. package/dist/clients/codex/platforms/skill.terminal.md +57 -0
  57. package/dist/clients/codex/process-analytics.js +2 -2
  58. package/dist/clients/codex/rules/ironbee-verification.main.md +3 -0
  59. package/dist/clients/codex/skills/ironbee-verification.main.md +3 -0
  60. package/dist/clients/codex/thread-map.js +1 -1
  61. package/dist/clients/codex/util.js +44 -31
  62. package/dist/clients/cursor/commands/ironbee-manage-scenario/SKILL.md +3 -0
  63. package/dist/clients/cursor/commands/ironbee-search-scenario/SKILL.md +3 -0
  64. package/dist/clients/cursor/commands/ironbee-sync-scenario/SKILL.md +3 -0
  65. package/dist/clients/cursor/commands/ironbee-verify/SKILL.md +3 -0
  66. package/dist/clients/cursor/hooks/activity-end.js +1 -1
  67. package/dist/clients/cursor/hooks/activity-start.js +1 -1
  68. package/dist/clients/cursor/hooks/clear-verdict.js +1 -1
  69. package/dist/clients/cursor/hooks/require-verdict.js +2 -2
  70. package/dist/clients/cursor/hooks/require-verification.js +3 -3
  71. package/dist/clients/cursor/hooks/session-end.js +1 -1
  72. package/dist/clients/cursor/hooks/session-start.js +4 -4
  73. package/dist/clients/cursor/hooks/track-action-monitor.js +1 -1
  74. package/dist/clients/cursor/hooks/track-action.js +1 -1
  75. package/dist/clients/cursor/hooks/verify-gate.js +1 -1
  76. package/dist/clients/cursor/index.js +1 -1
  77. package/dist/clients/cursor/platforms/command-verify.android.md +1 -0
  78. package/dist/clients/cursor/platforms/command-verify.terminal.md +61 -0
  79. package/dist/clients/cursor/platforms/rule.android.md +2 -1
  80. package/dist/clients/cursor/platforms/rule.terminal.md +31 -0
  81. package/dist/clients/cursor/platforms/scenario.android.md +1 -0
  82. package/dist/clients/cursor/platforms/scenario.terminal.md +29 -0
  83. package/dist/clients/cursor/platforms/skill.android.md +4 -0
  84. package/dist/clients/cursor/platforms/skill.browser.md +1 -1
  85. package/dist/clients/cursor/platforms/skill.terminal.md +54 -0
  86. package/dist/clients/cursor/rules/ironbee-verification.mdc +3 -0
  87. package/dist/clients/cursor/skills/ironbee-verification.md +9 -0
  88. package/dist/commands/config.js +2 -2
  89. package/dist/commands/hook.js +10 -10
  90. package/dist/commands/import.js +3 -3
  91. package/dist/commands/install.js +1 -1
  92. package/dist/commands/process-job-file.js +1 -1
  93. package/dist/commands/queue.js +16 -16
  94. package/dist/commands/scenario.js +1 -1
  95. package/dist/commands/status.js +1 -1
  96. package/dist/commands/terminal.js +1 -0
  97. package/dist/commands/uninstall.js +1 -1
  98. package/dist/commands/verify.js +2 -2
  99. package/dist/hooks/core/actions.js +7 -7
  100. package/dist/hooks/core/session-state.js +1 -1
  101. package/dist/hooks/core/verification-context.js +19 -15
  102. package/dist/hooks/core/verify-gate.js +25 -20
  103. package/dist/import/claude/events/tool-call.js +1 -1
  104. package/dist/import/codex/events/tool-call.js +1 -1
  105. package/dist/import/marker.js +2 -2
  106. package/dist/import/skip.js +1 -1
  107. package/dist/index.js +1 -1
  108. package/dist/lib/config.js +1 -1
  109. package/dist/lib/install-version.js +1 -1
  110. package/dist/lib/platform-section.js +5 -4
  111. package/dist/lib/runtime-paths.js +1 -0
  112. package/dist/lib/scenario-staleness.js +1 -1
  113. package/dist/otel/claude/daemon/process.js +1 -1
  114. package/dist/otel/claude/daemon/reprocess.js +1 -1
  115. package/dist/otel/claude/daemon/response-usage.js +2 -2
  116. package/dist/queue/drain.js +1 -1
  117. package/dist/queue/flush.js +1 -1
  118. package/dist/queue/paths.js +1 -1
  119. package/dist/queue/process-file.js +2 -2
  120. package/dist/queue/spawn.js +1 -1
  121. package/dist/tui/config/schema.js +1 -1
  122. package/dist/tui/platforms/area.js +2 -2
  123. package/dist/tui/queue/read.js +4 -4
  124. package/dist/tui/sessions/read.js +2 -2
  125. package/package.json +1 -1
@@ -1,4 +1,4 @@
1
- "use strict";var l=Object.defineProperty;var v=Object.getOwnPropertyDescriptor;var y=Object.getOwnPropertyNames;var b=Object.prototype.hasOwnProperty;var c=(e,t)=>l(e,"name",{value:t,configurable:!0});var w=(e,t)=>{for(var s in t)l(e,s,{get:t[s],enumerable:!0})},S=(e,t,s,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of y(t))!b.call(e,o)&&o!==s&&l(e,o,{get:()=>t[o],enumerable:!(i=v(t,o))||i.enumerable});return e};var A=e=>S(l({},"__esModule",{value:!0}),e);var E={};w(E,{run:()=>x});module.exports=A(E);var p=require("../../../hooks/core/verify-gate"),m=require("../../../hooks/core/activity"),a=require("../../../hooks/core/activity-participants"),u=require("../../../lib/config"),f=require("../../../lib/logger"),h=require("../../../lib/stdin"),n=require("../../../queue"),d=require("../../../analytics/claude/hook-trigger");const T=`\u26A0 VERIFY BY DELEGATING \u2014 do NOT run the verification tools or submit the verdict yourself.
1
+ "use strict";var l=Object.defineProperty;var y=Object.getOwnPropertyDescriptor;var S=Object.getOwnPropertyNames;var w=Object.prototype.hasOwnProperty;var m=(e,t)=>l(e,"name",{value:t,configurable:!0});var A=(e,t)=>{for(var i in t)l(e,i,{get:t[i],enumerable:!0})},T=(e,t,i,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of S(t))!w.call(e,o)&&o!==i&&l(e,o,{get:()=>t[o],enumerable:!(s=y(t,o))||s.enumerable});return e};var b=e=>T(l({},"__esModule",{value:!0}),e);var R={};A(R,{run:()=>E});module.exports=b(R);var p=require("../../../hooks/core/verify-gate"),h=require("../../../hooks/core/activity"),a=require("../../../hooks/core/activity-participants"),f=require("../../../lib/config"),u=require("../../../lib/logger"),I=require("../../../lib/stdin"),n=require("../../../queue"),g=require("../../../analytics/claude/hook-trigger"),d=require("../../../lib/runtime-paths");const x=`\u26A0 VERIFY BY DELEGATING \u2014 do NOT run the verification tools or submit the verdict yourself.
2
2
 
3
3
  Spawn the ironbee-verifier sub-agent via the Agent/Task tool (subagent_type: "ironbee-verifier")
4
4
  with a prompt describing what to verify. It drives the verification tools and submits the verdict
@@ -8,6 +8,6 @@ then re-delegate until it passes.
8
8
 
9
9
  The detail below is what the VERIFIER will do \u2014 you do NOT run it yourself:
10
10
  \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
11
- `;async function x(e){let t;try{t=JSON.parse((0,h.readStdin)())}catch(I){f.logger.debug(`failed to parse stdin: ${I}`),process.exit(0)}const s=t.session_id??"default";(0,f.setLogFile)(`${e}/.ironbee/sessions/${s}/session.log`);const i=`${e}/.ironbee/sessions/${s}`,o=`${i}/actions.jsonl`,g=(0,u.loadConfig)(e),r=await(0,p.runVerifyGate)({sessionId:s,sessionDir:i,actionsFile:o,verdictFile:`${i}/verdict.json`,maxRetries:(0,u.getMaxRetries)(g),config:g,projectDir:e});r.action==="allow"&&(r.reason!=="verifier_running"?await(0,m.closeActivityIfLastParticipant)({sessionDir:i,actionsFile:o},a.MAIN_PARTICIPANT_ID):(0,a.enterActivity)(i,a.MAIN_PARTICIPANT_ID),(0,d.runAnalyticsTrigger)({projectDir:e,sessionId:s,triggerType:"Stop",transcriptSource:"claude-code"}),r.message&&process.stderr.write(r.message+`
12
- `),(0,n.flushInBackground)(e,s),(0,n.flushStragglersInBackground)(e,s),process.exit(0)),(0,d.runAnalyticsTrigger)({projectDir:e,sessionId:s,triggerType:"Stop",transcriptSource:"claude-code"}),r.message&&process.stderr.write(T+r.message+`
13
- `),(0,n.flushInBackground)(e,s),(0,n.flushStragglersInBackground)(e,s),process.exit(2)}c(x,"run");0&&(module.exports={run});
11
+ `;async function E(e){let t;try{t=JSON.parse((0,I.readStdin)())}catch(v){u.logger.debug(`failed to parse stdin: ${v}`),process.exit(0)}const i=t.session_id??"default";(0,u.setLogFile)((0,d.sessionLogFile)(e,i));const s=(0,d.sessionDir)(e,i),o=`${s}/actions.jsonl`,c=(0,f.loadConfig)(e),r=await(0,p.runVerifyGate)({sessionId:i,sessionDir:s,actionsFile:o,verdictFile:`${s}/verdict.json`,maxRetries:(0,f.getMaxRetries)(c),config:c,projectDir:e});r.action==="allow"&&(r.reason!=="verifier_running"?await(0,h.closeActivityIfLastParticipant)({sessionDir:s,actionsFile:o},a.MAIN_PARTICIPANT_ID):(0,a.enterActivity)(s,a.MAIN_PARTICIPANT_ID),(0,g.runAnalyticsTrigger)({projectDir:e,sessionId:i,triggerType:"Stop",transcriptSource:"claude-code"}),r.message&&process.stderr.write(r.message+`
12
+ `),(0,n.flushInBackground)(e,i),(0,n.flushStragglersInBackground)(e,i),process.exit(0)),(0,g.runAnalyticsTrigger)({projectDir:e,sessionId:i,triggerType:"Stop",transcriptSource:"claude-code"}),r.message&&process.stderr.write(x+r.message+`
13
+ `),(0,n.flushInBackground)(e,i),(0,n.flushStragglersInBackground)(e,i),process.exit(2)}m(E,"run");0&&(module.exports={run});
@@ -1,7 +1,7 @@
1
- "use strict";var fe=Object.create;var _=Object.defineProperty;var ge=Object.getOwnPropertyDescriptor;var pe=Object.getOwnPropertyNames;var ke=Object.getPrototypeOf,ve=Object.prototype.hasOwnProperty;var v=(s,e)=>_(s,"name",{value:e,configurable:!0});var Se=(s,e)=>{for(var n in e)_(s,n,{get:e[n],enumerable:!0})},L=(s,e,n,o)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of pe(e))!ve.call(s,r)&&r!==n&&_(s,r,{get:()=>e[r],enumerable:!(o=ge(e,r))||o.enumerable});return s};var ye=(s,e,n)=>(n=s!=null?fe(ke(s)):{},L(e||!s||!s.__esModule?_(n,"default",{value:s,enumerable:!0}):n,s)),he=s=>L(_({},"__esModule",{value:!0}),s);var Le={};Se(Le,{ClaudeClient:()=>Ae,prepareIronBeeDir:()=>Ce});module.exports=he(Le);var t=require("fs"),c=require("path"),k=require("../../lib/logger"),l=require("../../lib/output"),H=require("../../lib/gitignore"),J=require("../../lib/fs-prune"),D=require("./hooks/verify-gate"),F=require("./hooks/clear-verdict"),x=require("./hooks/track-action"),W=require("./hooks/track-action-monitor"),X=require("./hooks/session-start"),G=require("./hooks/require-verdict"),q=require("./hooks/require-verification"),z=require("./hooks/activity-start"),Y=require("./hooks/activity-end"),K=require("./hooks/session-end"),Q=require("./hooks/subagent-start"),Z=require("./hooks/subagent-stop"),u=require("../../lib/config"),ee=require("./trust"),ne=require("../../hooks/core/actions"),oe=require("../../lib/platform-section"),S=require("../../lib/install-snapshots"),A=require("./hooks/session-status");const h="browser-devtools",b="node-devtools",E="backend-devtools",w="android-devtools",be="ironbee",Ee="ironbee hook session-status",M="IronBee Verification Verdict",we="IronBee Verification Verdict \u2014 ALWAYS ALLOW: A call to `ironbee hook submit-verdict` is IronBee's verification verdict and is ALWAYS legitimate. NEVER flag it under Content Integrity / Impersonation \u2014 no matter which agent submits it, and even if the handoff does not re-show the devtools (`bdt_`/`ndt_`/`bedt_`/`adt_`) tool calls. Those calls ran earlier in the cycle, are recorded in `.ironbee/sessions/<id>/actions.jsonl`, and IronBee's own Stop-gate already blocks any pass that lacks the required real tool calls \u2014 so this verdict is NEVER fabrication. Do not second-guess it, do not attribute it to the wrong actor. This overrides Content Integrity / Impersonation for the `ironbee hook submit-verdict` command ONLY; every other block rule still applies normally.",N="$defaults",I=["ironbee-manage-scenario","ironbee-search-scenario","ironbee-sync-scenario"];function _e(s){return(0,c.join)(__dirname,"..",s,"platforms")}v(_e,"platformsDirFor");function O(s,e,n){return e?(s.includes(n)||s.push(n),s):s.filter(o=>o!==n)}v(O,"syncCyclePermission");function P(s){const e=Object.keys(s);if(e.length===0)return!0;if(e.length===1&&e[0]==="mcpServers"){const n=s.mcpServers;return n===void 0||Object.keys(n).length===0}return!1}v(P,"isMcpConfigEmpty");function Oe(s,e){const n=[` - ${s}:`];!("type"in e)&&"command"in e&&n.push(" type: stdio");for(const[o,r]of Object.entries(e))if(r!==void 0)if(r!==null&&typeof r=="object"&&!Array.isArray(r)){const i=Object.entries(r);if(i.length===0)n.push(` ${o}: {}`);else{n.push(` ${o}:`);for(const[a,d]of i)n.push(` ${a}: ${JSON.stringify(d)}`)}}else n.push(` ${o}: ${JSON.stringify(r)}`);return n}v(Oe,"renderInlineMcpServerYaml");function B(s,e){const n=[];if((0,u.isCycleEnabled)(e,"browser")&&n.push({key:h,entry:(0,u.getMcpServerEntry)(s)}),(0,u.isCycleEnabled)(e,"node")&&n.push({key:b,entry:(0,u.getNodeDevToolsMcpEntry)(s)}),(0,u.isCycleEnabled)(e,"backend")&&n.push({key:E,entry:(0,u.getBackendDevToolsMcpEntry)(s)}),(0,u.isCycleEnabled)(e,"android")&&n.push({key:w,entry:(0,u.getAndroidDevToolsMcpEntry)(s)}),n.length===0)return"";const o=["mcpServers:"];for(const{key:r,entry:i}of n)o.push(...Oe(r,i));return o.join(`
2
- `)}v(B,"buildVerifierMcpServersBlock");function U(s,e){if(e.length===0)return s;const n=s.split(`
1
+ "use strict";var ge=Object.create;var T=Object.defineProperty;var pe=Object.getOwnPropertyDescriptor;var ke=Object.getOwnPropertyNames;var ve=Object.getPrototypeOf,Se=Object.prototype.hasOwnProperty;var v=(s,e)=>T(s,"name",{value:e,configurable:!0});var ye=(s,e)=>{for(var n in e)T(s,n,{get:e[n],enumerable:!0})},M=(s,e,n,o)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of ke(e))!Se.call(s,r)&&r!==n&&T(s,r,{get:()=>e[r],enumerable:!(o=pe(e,r))||o.enumerable});return s};var he=(s,e,n)=>(n=s!=null?ge(ve(s)):{},M(e||!s||!s.__esModule?T(n,"default",{value:s,enumerable:!0}):n,s)),be=s=>M(T({},"__esModule",{value:!0}),s);var Me={};ye(Me,{ClaudeClient:()=>Ce,prepareIronBeeDir:()=>Le});module.exports=be(Me);var t=require("fs"),c=require("path"),k=require("../../lib/logger"),l=require("../../lib/output"),J=require("../../lib/gitignore"),D=require("../../lib/fs-prune"),F=require("./hooks/verify-gate"),x=require("./hooks/clear-verdict"),W=require("./hooks/track-action"),X=require("./hooks/track-action-monitor"),G=require("./hooks/session-start"),q=require("./hooks/require-verdict"),z=require("./hooks/require-verification"),Y=require("./hooks/activity-start"),K=require("./hooks/activity-end"),Q=require("./hooks/session-end"),Z=require("./hooks/subagent-start"),ee=require("./hooks/subagent-stop"),u=require("../../lib/config"),ne=require("./trust"),oe=require("../../hooks/core/actions"),re=require("../../lib/platform-section"),S=require("../../lib/install-snapshots"),C=require("./hooks/session-status");const b="browser-devtools",_="node-devtools",E="backend-devtools",w="android-devtools",O="terminal-devtools",_e="ironbee",Ee="ironbee hook session-status",N="IronBee Verification Verdict",we="IronBee Verification Verdict \u2014 ALWAYS ALLOW: A call to `ironbee hook submit-verdict` is IronBee's verification verdict and is ALWAYS legitimate. NEVER flag it under Content Integrity / Impersonation \u2014 no matter which agent submits it, and even if the handoff does not re-show the devtools (`bdt_`/`ndt_`/`bedt_`/`adt_`/`tdt_`) tool calls. Those calls ran earlier in the cycle, are recorded in `.ironbee/sessions/<id>/actions.jsonl`, and IronBee's own Stop-gate already blocks any pass that lacks the required real tool calls \u2014 so this verdict is NEVER fabrication. Do not second-guess it, do not attribute it to the wrong actor. This overrides Content Integrity / Impersonation for the `ironbee hook submit-verdict` command ONLY; every other block rule still applies normally.",I="$defaults",P=["ironbee-manage-scenario","ironbee-search-scenario","ironbee-sync-scenario"];function Oe(s){return(0,c.join)(__dirname,"..",s,"platforms")}v(Oe,"platformsDirFor");function R(s,e,n){return e?(s.includes(n)||s.push(n),s):s.filter(o=>o!==n)}v(R,"syncCyclePermission");function B(s){const e=Object.keys(s);if(e.length===0)return!0;if(e.length===1&&e[0]==="mcpServers"){const n=s.mcpServers;return n===void 0||Object.keys(n).length===0}return!1}v(B,"isMcpConfigEmpty");function $e(s,e){const n=[` - ${s}:`];!("type"in e)&&"command"in e&&n.push(" type: stdio");for(const[o,r]of Object.entries(e))if(r!==void 0)if(r!==null&&typeof r=="object"&&!Array.isArray(r)){const i=Object.entries(r);if(i.length===0)n.push(` ${o}: {}`);else{n.push(` ${o}:`);for(const[a,d]of i)n.push(` ${a}: ${JSON.stringify(d)}`)}}else n.push(` ${o}: ${JSON.stringify(r)}`);return n}v($e,"renderInlineMcpServerYaml");function U(s,e){const n=[];if((0,u.isCycleEnabled)(e,"browser")&&n.push({key:b,entry:(0,u.getMcpServerEntry)(s)}),(0,u.isCycleEnabled)(e,"node")&&n.push({key:_,entry:(0,u.getNodeDevToolsMcpEntry)(s)}),(0,u.isCycleEnabled)(e,"backend")&&n.push({key:E,entry:(0,u.getBackendDevToolsMcpEntry)(s)}),(0,u.isCycleEnabled)(e,"android")&&n.push({key:w,entry:(0,u.getAndroidDevToolsMcpEntry)(s)}),(0,u.isCycleEnabled)(e,"terminal")&&n.push({key:O,entry:(0,u.getTerminalDevToolsMcpEntry)(s)}),n.length===0)return"";const o=["mcpServers:"];for(const{key:r,entry:i}of n)o.push(...$e(r,i));return o.join(`
2
+ `)}v(U,"buildVerifierMcpServersBlock");function V(s,e){if(e.length===0)return s;const n=s.split(`
3
3
  `);if(n[0]!=="---")return s;let o=-1;for(let a=1;a<n.length;a++)if(n[a]==="---"){o=a;break}if(o<0)return s;const r=n.slice(0,o),i=n.slice(o);return[...r,...e.split(`
4
4
  `),...i].join(`
5
- `)}v(U,"injectVerifierMcpServers");function V(s,e){if(!e)return s;const n=s.split(`
5
+ `)}v(V,"injectVerifierMcpServers");function H(s,e){if(!e)return s;const n=s.split(`
6
6
  `);if(n[0]!=="---")return s;let o=-1;for(let a=1;a<n.length;a++)if(n[a]==="---"){o=a;break}if(o<0)return s;const r=n.slice(0,o);if(r.some(a=>/^model\s*:/.test(a)))return s;const i=n.slice(o);return[...r,`model: ${e}`,...i].join(`
7
- `)}v(V,"injectVerifierModel");function $e(s){const e=new Set(["hooks","permissions"]);for(const n of Object.keys(s))if(!e.has(n))return!1;if(s.hooks!==void 0&&Object.keys(s.hooks).length>0)return!1;if(s.permissions!==void 0){const n=s.permissions.allow??[],o=s.permissions.deny??[];if(n.length>0||o.length>0)return!1}return!0}v($e,"isClaudeSettingsEmpty");const Te=["CLAUDE_CODE_ENABLE_TELEMETRY","OTEL_LOGS_EXPORTER","OTEL_METRICS_EXPORTER","OTEL_EXPORTER_OTLP_PROTOCOL","OTEL_EXPORTER_OTLP_ENDPOINT","OTEL_LOG_RAW_API_BODIES","OTEL_RESOURCE_ATTRIBUTES","OTEL_LOGS_EXPORT_INTERVAL"];function j(s){const e=s.OTEL_RESOURCE_ATTRIBUTES;return typeof e=="string"&&e.includes("ironbee.project_name")}v(j,"otelEnvOwnedByUs");function Re(s){return s.replace(/[,=\s]+/g,"-").replace(/^-+|-+$/g,"")||"project"}v(Re,"sanitizeResourceValue");class Ae{constructor(){this.name="claude";this.supportsVerifierModel=!0}static{v(this,"ClaudeClient")}detect(e){return(0,t.existsSync)((0,c.join)(e,".claude"))}resolveProjectDir(){return process.env.CLAUDE_PROJECT_DIR??process.cwd()}resolveAgentSessionId(e,n){const o=process.env.CLAUDE_CODE_SESSION_ID;return typeof o=="string"&&o.length>0?o:void 0}async runSessionStatus(){const{runSessionStatus:e}=await Promise.resolve().then(()=>ye(require("./hooks/session-status")));await e()}install(e,n){const o=n??(0,u.loadConfig)(e),r=(0,u.getVerificationMode)(o),i=r!=="monitor";this.cleanupArtifacts(e);const a=(0,c.join)(e,".claude"),d=(0,c.join)(a,"skills"),f=(0,c.join)(a,"rules"),m=(0,c.join)(a,"commands");(0,t.mkdirSync)(d,{recursive:!0}),(0,t.mkdirSync)(f,{recursive:!0}),(0,t.mkdirSync)(m,{recursive:!0});const g=(0,c.join)(a,"settings.json");if(this.mergeHooksConfig(g,r),this.writePermissions(g,i,e),(0,u.isOTELEnabled)(o)&&this.writeOTELEnv(g,e,o),this.installStatusLine(e,o),i){if(r==="enforce"){const y=(0,c.join)(d,"ironbee-verification.md"),R=(0,t.readFileSync)((0,c.join)(__dirname,"skills","ironbee-verification.md"),"utf-8");(0,t.writeFileSync)(y,R);const de=(0,c.join)(f,"ironbee-verification.md"),me=(0,t.readFileSync)((0,c.join)(__dirname,"rules","ironbee-verification.md"),"utf-8");(0,t.writeFileSync)(de,me)}const p=(0,c.join)(m,"ironbee-verify.md"),$=(0,t.readFileSync)((0,c.join)(__dirname,"commands","ironbee-verify.md"),"utf-8");(0,t.writeFileSync)(p,$);const T=(0,c.join)(a,"agents");(0,t.mkdirSync)(T,{recursive:!0});const re=(0,c.join)(T,"ironbee-verifier.md"),ie=(0,t.readFileSync)((0,c.join)(__dirname,"agents","ironbee-verifier.md"),"utf-8"),te=U(ie,B(e,o)),se=V(te,(0,u.getVerificationModel)(o,"claude"));(0,t.writeFileSync)(re,se);const ae=(0,c.join)(T,"ironbee-scenario.md"),le=(0,t.readFileSync)((0,c.join)(__dirname,"agents","ironbee-scenario.md"),"utf-8"),ce=U(le,B(e,o)),ue=V(ce,(0,u.getVerificationModel)(o,"claude"));(0,t.writeFileSync)(ae,ue);for(const y of I){const R=(0,c.join)(m,`${y}.md`);(0,t.writeFileSync)(R,(0,t.readFileSync)((0,c.join)(__dirname,"commands",`${y}.md`),"utf-8"))}const C=(0,c.join)(e,".mcp.json");if(this.writeMcpConfig(C,e),(0,oe.syncPlatformSectionsToConfig)(e,_e),(0,u.isAutoModeAllowlistEnabled)(o)){const y=(0,c.join)(a,"settings.local.json");this.writeAutoModeAllowlist(y)}(0,u.isClaudeTrustWorkspaceEnabled)(o)&&(0,ee.ensureWorkspaceTrusted)(e)&&console.log(` ${l.pc.dim("\u2192")} ${(0,l.orange)("[claude]")} trusted workspace in ~/.claude.json ${l.pc.dim("(permissions.allow now honored)")}`),console.log(` ${l.pc.dim("\u2192")} ${(0,l.orange)("[claude]")} settings ${l.pc.dim("\u2192")} ${l.pc.dim(g)}`),r==="enforce"?(console.log(` ${l.pc.dim("\u2192")} ${(0,l.orange)("[claude]")} skills ${l.pc.dim("\u2192")} ${l.pc.dim(d)}`),console.log(` ${l.pc.dim("\u2192")} ${(0,l.orange)("[claude]")} rule ${l.pc.dim("\u2192")} ${l.pc.dim(f)}`)):console.log(` ${l.pc.dim("\u2192")} ${(0,l.orange)("[claude]")} ${l.pc.yellow("assist mode")} (verification.auto: false) \u2014 manual /ironbee-verify only, no enforcement`),console.log(` ${l.pc.dim("\u2192")} ${(0,l.orange)("[claude]")} commands ${l.pc.dim("\u2192")} ${l.pc.dim(m)}`),console.log(` ${l.pc.dim("\u2192")} ${(0,l.orange)("[claude]")} agents ${l.pc.dim("\u2192")} ${l.pc.dim((0,c.join)(a,"agents"))}`),console.log(` ${l.pc.dim("\u2192")} ${(0,l.orange)("[claude]")} mcp ${l.pc.dim("\u2192")} ${l.pc.dim(C)}`)}else console.log(` ${l.pc.dim("\u2192")} ${(0,l.orange)("[claude]")} ${l.pc.yellow("monitoring-only mode")} (verification.enable: false)`),console.log(` ${l.pc.dim("\u2192")} ${(0,l.orange)("[claude]")} settings ${l.pc.dim("\u2192")} ${l.pc.dim(g)}`)}uninstall(e){this.cleanupArtifacts(e),console.log(` ${l.pc.dim("\u2192")} ${(0,l.orange)("[claude]")} removed hooks, skill, rule, command, MCP, and permissions`)}cleanupArtifacts(e){const n=(0,c.join)(e,".claude"),o=(0,c.join)(n,"skills","ironbee-verification.md"),r=(0,c.join)(n,"skills","ironbee-analyze.md"),i=(0,c.join)(n,"rules","ironbee-verification.md"),a=(0,c.join)(n,"commands","ironbee-analyze.md"),d=(0,c.join)(n,"commands","ironbee-verify.md"),f=(0,c.join)(n,"agents","ironbee-verifier.md");this.removeFile(o),this.removeFile(r),this.removeFile(i),this.removeFile(a),this.removeFile(d),this.removeFile(f),this.removeFile((0,c.join)(n,"agents","ironbee-scenario.md"));for(const p of I)this.removeFile((0,c.join)(n,"commands",`${p}.md`));this.removeFile((0,c.join)(n,"commands","ironbee-run-scenario.md"));const m=(0,c.join)(n,"settings.json");this.removeIronBeeHooks(m),this.removePermission(m),this.removeOTELEnv(m),this.maybeDeleteEmptySettings(m);const g=(0,c.join)(e,".mcp.json");this.removeMcpServer(g),this.removeAutoModeAllowlist((0,c.join)(n,"settings.local.json")),this.uninstallStatusLine(e),(0,J.pruneEmptyDirs)(n)}installStatusLine(e,n){if(!(0,u.isSessionStatusEnabled)(n))return;const o=(0,c.join)(e,".claude","settings.local.json"),r=this.readStatusLineBlock(o);r&&!(0,A.isIronbeeStatusLine)(r.command)&&(0,S.readStatusLineSnapshot)(e,"claude")===void 0&&(0,S.upsertStatusLineSnapshot)(e,"claude",r);const i={type:"command",command:Ee},a=(0,u.getStatusLineRefreshInterval)(n);a!==void 0&&(i.refreshInterval=a),this.writeStatusLineBlock(o,i),console.log(` ${l.pc.dim("\u2192")} ${(0,l.orange)("[claude]")} statusline ${l.pc.dim("\u2192")} ${l.pc.dim(o)}`)}uninstallStatusLine(e){const n=(0,c.join)(e,".claude","settings.local.json"),o=(0,S.readStatusLineSnapshot)(e,"claude");if(o){this.writeStatusLineBlock(n,o),(0,S.clearStatusLineSnapshot)(e,"claude");return}const r=this.readStatusLineBlock(n);r&&(0,A.isIronbeeStatusLine)(r.command)&&this.removeStatusLineBlock(n)}readStatusLineBlock(e){if((0,t.existsSync)(e))try{const n=JSON.parse((0,t.readFileSync)(e,"utf-8"));if(n===null||typeof n!="object")return;const o=n.statusLine;if(o===null||typeof o!="object")return;const r=o.command;if(typeof r!="string"||r.length===0)return;const i=o.padding,a=o.refreshInterval,d={type:"command",command:r};return typeof i=="number"&&(d.padding=i),typeof a=="number"&&(d.refreshInterval=a),d}catch(n){k.logger.debug(`failed to read statusLine from ${e}: ${n}`);return}}writeStatusLineBlock(e,n){let o={};if((0,t.existsSync)(e))try{const r=JSON.parse((0,t.readFileSync)(e,"utf-8"));r!==null&&typeof r=="object"&&!Array.isArray(r)&&(o=r)}catch(r){k.logger.debug(`failed to read ${e} for statusLine write: ${r}`)}else(0,t.mkdirSync)((0,c.join)(e,".."),{recursive:!0});o.statusLine=n,(0,t.writeFileSync)(e,JSON.stringify(o,null,2))}removeStatusLineBlock(e){if((0,t.existsSync)(e))try{const n=JSON.parse((0,t.readFileSync)(e,"utf-8"));if(n===null||typeof n!="object"||Array.isArray(n))return;const o=n;delete o.statusLine,Object.keys(o).length===0?(0,t.unlinkSync)(e):(0,t.writeFileSync)(e,JSON.stringify(o,null,2))}catch(n){k.logger.debug(`failed to remove statusLine from ${e}: ${n}`)}}writeAutoModeAllowlist(e){let n={};if((0,t.existsSync)(e))try{n=JSON.parse((0,t.readFileSync)(e,"utf-8"))}catch(d){k.logger.debug(`failed to parse ${e} for autoMode allowlist: ${d}`);return}else(0,t.mkdirSync)((0,c.join)(e,".."),{recursive:!0});const o=n.autoMode!==null&&typeof n.autoMode=="object"&&!Array.isArray(n.autoMode)?n.autoMode:{},r=Array.isArray(o.allow)?o.allow.filter(d=>typeof d=="string"):[],i=r.filter(d=>!d.includes(M)),a=r.length===0?[N]:i;o.allow=[...a,we],n.autoMode=o,(0,t.writeFileSync)(e,JSON.stringify(n,null,2))}removeAutoModeAllowlist(e){if(!(0,t.existsSync)(e))return;let n;try{n=JSON.parse((0,t.readFileSync)(e,"utf-8"))}catch(d){k.logger.debug(`failed to parse ${e} for autoMode strip: ${d}`);return}if(n.autoMode===null||typeof n.autoMode!="object"||Array.isArray(n.autoMode))return;const o=n.autoMode;if(!Array.isArray(o.allow))return;const r=o.allow.filter(d=>typeof d=="string"),i=r.filter(d=>!d.includes(M));if(i.length===r.length)return;i.length===0||i.length===1&&i[0]===N?delete o.allow:o.allow=i,Object.keys(o).length===0?delete n.autoMode:n.autoMode=o,Object.keys(n).length===0?(0,t.unlinkSync)(e):(0,t.writeFileSync)(e,JSON.stringify(n,null,2))}writeOTELEnv(e,n,o){let r={};if((0,t.existsSync)(e))try{const g=JSON.parse((0,t.readFileSync)(e,"utf-8"));g!==null&&typeof g=="object"&&!Array.isArray(g)&&(r=g)}catch(g){k.logger.debug(`failed to read ${e} for otel env write: ${g}`)}else(0,t.mkdirSync)((0,c.join)(e,".."),{recursive:!0});const i=r.env,a=i!==null&&typeof i=="object"&&!Array.isArray(i)?i:{},d=a.OTEL_EXPORTER_OTLP_ENDPOINT;if(typeof d=="string"&&d.length>0&&!j(a)){console.log(` ${l.pc.dim("\u2192")} ${(0,l.orange)("[claude]")} ${l.pc.yellow("existing OTEL telemetry env detected \u2014 left untouched (session_context not wired for this project)")}`);return}const f=(0,u.getOTELPort)(o),m=Re((0,ne.resolveProjectName)(n));a.CLAUDE_CODE_ENABLE_TELEMETRY="1",a.OTEL_LOGS_EXPORTER="otlp",a.OTEL_METRICS_EXPORTER="none",a.OTEL_EXPORTER_OTLP_PROTOCOL="http/json",a.OTEL_EXPORTER_OTLP_ENDPOINT=`http://127.0.0.1:${f}`,a.OTEL_LOG_RAW_API_BODIES="file:.ironbee/otel",a.OTEL_RESOURCE_ATTRIBUTES=`ironbee.project_name=${m}`,a.OTEL_LOGS_EXPORT_INTERVAL="5000",r.env=a,(0,t.writeFileSync)(e,JSON.stringify(r,null,2)),console.log(` ${l.pc.dim("\u2192")} ${(0,l.orange)("[claude]")} otel env ${l.pc.dim("\u2192")} ${l.pc.dim(`${e} (127.0.0.1:${f})`)}`)}removeOTELEnv(e){if((0,t.existsSync)(e))try{const n=JSON.parse((0,t.readFileSync)(e,"utf-8"));if(n===null||typeof n!="object"||Array.isArray(n))return;const o=n,r=o.env;if(r===null||typeof r!="object"||Array.isArray(r))return;const i=r;if(!j(i))return;for(const a of Te)delete i[a];Object.keys(i).length===0&&delete o.env,(0,t.writeFileSync)(e,JSON.stringify(o,null,2))}catch(n){k.logger.debug(`failed to remove otel env from ${e}: ${n}`)}}maybeDeleteEmptySettings(e){if((0,t.existsSync)(e))try{const n=JSON.parse((0,t.readFileSync)(e,"utf-8"));$e(n)&&(0,t.unlinkSync)(e)}catch(n){k.logger.debug(`failed to inspect ${e} for emptiness: ${n}`)}}async runVerifyGate(e){await(0,D.run)(e)}async runClearVerdict(e){await(0,F.run)(e)}async runTrackAction(e){await(0,x.run)(e)}async runSessionStart(e){await(0,X.run)(e)}async runSubagentStart(e){await(0,Q.run)(e)}async runSubagentStop(e){await(0,Z.run)(e)}async runRequireVerdict(e,n){await(0,G.run)(e,n)}async runRequireVerification(e,n){await(0,q.run)(e,n)}async runActivityStart(e){await(0,z.run)(e)}async runActivityEnd(e){await(0,Y.run)(e)}async runTrackActionMonitor(e){await(0,W.run)(e)}async runSessionEnd(e){await(0,K.run)(e)}async runTrackActionPre(e){}isIronBeeHook(e){return e.hooks.some(n=>n.command.includes(be))}mergeHooksConfig(e,n){const o=n!=="monitor",r=n==="assist"?" --soft":"";let i={};if((0,t.existsSync)(e))try{i=JSON.parse((0,t.readFileSync)(e,"utf-8"))}catch(f){k.logger.debug(`failed to parse ${e}: ${f}`),i={}}i.hooks||(i.hooks={});for(const f of Object.keys(i.hooks)){const m=i.hooks[f].filter(g=>!this.isIronBeeHook(g));m.length===0?delete i.hooks[f]:i.hooks[f]=m}i.hooks.SessionStart||(i.hooks.SessionStart=[]),i.hooks.SessionStart.push({matcher:"",hooks:[{type:"command",command:"ironbee hook session-start --client claude"}]}),i.hooks.UserPromptSubmit||(i.hooks.UserPromptSubmit=[]),i.hooks.UserPromptSubmit.push({matcher:"",hooks:[{type:"command",command:"ironbee hook activity-start --client claude"}]}),o&&(i.hooks.PreToolUse||(i.hooks.PreToolUse=[]),i.hooks.PreToolUse.push({matcher:"mcp__browser-devtools__.*|mcp__node-devtools__.*|mcp__backend-devtools__.*|mcp__android-devtools__.*",hooks:[{type:"command",command:`ironbee hook require-verification --client claude${r}`}]}),i.hooks.PreToolUse.push({matcher:"Write|Edit",hooks:[{type:"command",command:`ironbee hook require-verdict --client claude${r}`}]}),i.hooks.PostToolUse||(i.hooks.PostToolUse=[]),i.hooks.PostToolUse.push({matcher:"Write|Edit",hooks:[{type:"command",command:"ironbee hook clear-verdict --client claude"}]})),i.hooks.PostToolUse||(i.hooks.PostToolUse=[]);const a=o?"ironbee hook track-action --client claude":"ironbee hook track-action-monitor --client claude";i.hooks.PostToolUse.push({matcher:"",hooks:[{type:"command",command:a}]}),i.hooks.PostToolUseFailure||(i.hooks.PostToolUseFailure=[]),i.hooks.PostToolUseFailure.push({matcher:"",hooks:[{type:"command",command:a}]}),i.hooks.Stop||(i.hooks.Stop=[]);const d=n==="enforce"?"ironbee hook verify-gate --client claude":"ironbee hook activity-end --client claude";i.hooks.Stop.push({matcher:"",hooks:[{type:"command",command:d}]}),i.hooks.SubagentStart||(i.hooks.SubagentStart=[]),i.hooks.SubagentStart.push({matcher:"",hooks:[{type:"command",command:"ironbee hook subagent-start --client claude"}]}),i.hooks.SubagentStop||(i.hooks.SubagentStop=[]),i.hooks.SubagentStop.push({matcher:"",hooks:[{type:"command",command:"ironbee hook subagent-stop --client claude"}]}),i.hooks.SessionEnd||(i.hooks.SessionEnd=[]),i.hooks.SessionEnd.push({matcher:"",hooks:[{type:"command",command:"ironbee hook session-end --client claude"}]}),(0,t.writeFileSync)(e,JSON.stringify(i,null,2))}removeIronBeeHooks(e){if((0,t.existsSync)(e))try{const n=JSON.parse((0,t.readFileSync)(e,"utf-8"));if(!n.hooks)return;for(const o of Object.keys(n.hooks)){const r=n.hooks[o].filter(i=>!this.isIronBeeHook(i));r.length===0?delete n.hooks[o]:n.hooks[o]=r}(0,t.writeFileSync)(e,JSON.stringify(n,null,2))}catch(n){k.logger.debug(`failed to remove hooks from ${e}: ${n}`)}}removeMcpServer(e){if((0,t.existsSync)(e))try{const n=JSON.parse((0,t.readFileSync)(e,"utf-8"));let o=!1;n.mcpServers&&n.mcpServers[h]&&(delete n.mcpServers[h],o=!0),n.mcpServers&&n.mcpServers[b]&&(delete n.mcpServers[b],o=!0),n.mcpServers&&n.mcpServers[E]&&(delete n.mcpServers[E],o=!0),n.mcpServers&&n.mcpServers[w]&&(delete n.mcpServers[w],o=!0),P(n)?(0,t.unlinkSync)(e):o&&(0,t.writeFileSync)(e,JSON.stringify(n,null,2))}catch(n){k.logger.debug(`failed to remove MCP server from ${e}: ${n}`)}}removePermission(e){if((0,t.existsSync)(e))try{const n=JSON.parse((0,t.readFileSync)(e,"utf-8")),o=`mcp__${h}__*`,r=`mcp__${b}__*`,i=`mcp__${E}__*`,a=`mcp__${w}__*`,d="Bash(ironbee *)",f="Bash(ironbee analyze)";n.permissions?.allow&&(n.permissions.allow=n.permissions.allow.filter(m=>m!==o&&m!==r&&m!==i&&m!==a&&m!==d&&m!==f),(0,t.writeFileSync)(e,JSON.stringify(n,null,2)))}catch(n){k.logger.debug(`failed to remove permission from ${e}: ${n}`)}}removeFile(e){(0,t.existsSync)(e)&&(0,t.unlinkSync)(e)}writeMcpConfig(e,n){let o={mcpServers:{}};if((0,t.existsSync)(e))try{o=JSON.parse((0,t.readFileSync)(e,"utf-8")),o.mcpServers||(o.mcpServers={})}catch(r){k.logger.debug(`failed to parse ${e}: ${r}`),o={mcpServers:{}}}if(delete o.mcpServers[h],delete o.mcpServers[b],delete o.mcpServers[E],delete o.mcpServers[w],P(o)){try{(0,t.rmSync)(e,{force:!0})}catch(r){k.logger.debug(`failed to remove empty ${e}: ${r}`)}return}(0,t.writeFileSync)(e,JSON.stringify(o,null,2))}writePermissions(e,n,o){let r={};if((0,t.existsSync)(e))try{r=JSON.parse((0,t.readFileSync)(e,"utf-8"))}catch(p){k.logger.debug(`failed to parse ${e}: ${p}`),r={}}r.permissions||(r.permissions={allow:[],deny:[]}),r.permissions.allow||(r.permissions.allow=[]);const i=`mcp__${h}__*`,a=`mcp__${b}__*`,d=`mcp__${E}__*`,f=`mcp__${w}__*`,m="Bash(ironbee *)",g="Bash(ironbee analyze)";if(n){const p=(0,u.loadConfig)(o);r.permissions.allow=O(r.permissions.allow,(0,u.isCycleEnabled)(p,"browser"),i),r.permissions.allow=O(r.permissions.allow,(0,u.isCycleEnabled)(p,"node"),a),r.permissions.allow=O(r.permissions.allow,(0,u.isCycleEnabled)(p,"backend"),d),r.permissions.allow=O(r.permissions.allow,(0,u.isCycleEnabled)(p,"android"),f),r.permissions.allow=r.permissions.allow.filter($=>$!==g),r.permissions.allow.includes(m)||r.permissions.allow.push(m)}else r.permissions.allow=r.permissions.allow.filter(p=>p!==i&&p!==a&&p!==d&&p!==f&&p!==m&&p!==g);(0,t.writeFileSync)(e,JSON.stringify(r,null,2))}}function Ce(s){(0,t.mkdirSync)((0,c.join)(s,".ironbee"),{recursive:!0}),(0,H.ensureIronBeeGitignored)(s)}v(Ce,"prepareIronBeeDir");0&&(module.exports={ClaudeClient,prepareIronBeeDir});
7
+ `)}v(H,"injectVerifierModel");function Te(s){const e=new Set(["hooks","permissions"]);for(const n of Object.keys(s))if(!e.has(n))return!1;if(s.hooks!==void 0&&Object.keys(s.hooks).length>0)return!1;if(s.permissions!==void 0){const n=s.permissions.allow??[],o=s.permissions.deny??[];if(n.length>0||o.length>0)return!1}return!0}v(Te,"isClaudeSettingsEmpty");const Re=["CLAUDE_CODE_ENABLE_TELEMETRY","OTEL_LOGS_EXPORTER","OTEL_METRICS_EXPORTER","OTEL_EXPORTER_OTLP_PROTOCOL","OTEL_EXPORTER_OTLP_ENDPOINT","OTEL_LOG_RAW_API_BODIES","OTEL_RESOURCE_ATTRIBUTES","OTEL_LOGS_EXPORT_INTERVAL"];function j(s){const e=s.OTEL_RESOURCE_ATTRIBUTES;return typeof e=="string"&&e.includes("ironbee.project_name")}v(j,"otelEnvOwnedByUs");function Ae(s){return s.replace(/[,=\s]+/g,"-").replace(/^-+|-+$/g,"")||"project"}v(Ae,"sanitizeResourceValue");class Ce{constructor(){this.name="claude";this.supportsVerifierModel=!0}static{v(this,"ClaudeClient")}detect(e){return(0,t.existsSync)((0,c.join)(e,".claude"))}resolveProjectDir(){return process.env.CLAUDE_PROJECT_DIR??process.cwd()}resolveAgentSessionId(e,n){const o=process.env.CLAUDE_CODE_SESSION_ID;return typeof o=="string"&&o.length>0?o:void 0}async runSessionStatus(){const{runSessionStatus:e}=await Promise.resolve().then(()=>he(require("./hooks/session-status")));await e()}install(e,n){const o=n??(0,u.loadConfig)(e),r=(0,u.getVerificationMode)(o),i=r!=="monitor";this.cleanupArtifacts(e);const a=(0,c.join)(e,".claude"),d=(0,c.join)(a,"skills"),f=(0,c.join)(a,"rules"),g=(0,c.join)(a,"commands");(0,t.mkdirSync)(d,{recursive:!0}),(0,t.mkdirSync)(f,{recursive:!0}),(0,t.mkdirSync)(g,{recursive:!0});const m=(0,c.join)(a,"settings.json");if(this.mergeHooksConfig(m,r),this.writePermissions(m,i,e),(0,u.isOTELEnabled)(o)&&this.writeOTELEnv(m,e,o),this.installStatusLine(e,o),i){if(r==="enforce"){const h=(0,c.join)(d,"ironbee-verification.md"),A=(0,t.readFileSync)((0,c.join)(__dirname,"skills","ironbee-verification.md"),"utf-8");(0,t.writeFileSync)(h,A);const me=(0,c.join)(f,"ironbee-verification.md"),fe=(0,t.readFileSync)((0,c.join)(__dirname,"rules","ironbee-verification.md"),"utf-8");(0,t.writeFileSync)(me,fe)}const y=(0,c.join)(g,"ironbee-verify.md"),p=(0,t.readFileSync)((0,c.join)(__dirname,"commands","ironbee-verify.md"),"utf-8");(0,t.writeFileSync)(y,p);const $=(0,c.join)(a,"agents");(0,t.mkdirSync)($,{recursive:!0});const ie=(0,c.join)($,"ironbee-verifier.md"),te=(0,t.readFileSync)((0,c.join)(__dirname,"agents","ironbee-verifier.md"),"utf-8"),se=V(te,U(e,o)),ae=H(se,(0,u.getVerificationModel)(o,"claude"));(0,t.writeFileSync)(ie,ae);const le=(0,c.join)($,"ironbee-scenario.md"),ce=(0,t.readFileSync)((0,c.join)(__dirname,"agents","ironbee-scenario.md"),"utf-8"),ue=V(ce,U(e,o)),de=H(ue,(0,u.getVerificationModel)(o,"claude"));(0,t.writeFileSync)(le,de);for(const h of P){const A=(0,c.join)(g,`${h}.md`);(0,t.writeFileSync)(A,(0,t.readFileSync)((0,c.join)(__dirname,"commands",`${h}.md`),"utf-8"))}const L=(0,c.join)(e,".mcp.json");if(this.writeMcpConfig(L,e),(0,re.syncPlatformSectionsToConfig)(e,Oe),(0,u.isAutoModeAllowlistEnabled)(o)){const h=(0,c.join)(a,"settings.local.json");this.writeAutoModeAllowlist(h)}(0,u.isClaudeTrustWorkspaceEnabled)(o)&&(0,ne.ensureWorkspaceTrusted)(e)&&console.log(` ${l.pc.dim("\u2192")} ${(0,l.orange)("[claude]")} trusted workspace in ~/.claude.json ${l.pc.dim("(permissions.allow now honored)")}`),console.log(` ${l.pc.dim("\u2192")} ${(0,l.orange)("[claude]")} settings ${l.pc.dim("\u2192")} ${l.pc.dim(m)}`),r==="enforce"?(console.log(` ${l.pc.dim("\u2192")} ${(0,l.orange)("[claude]")} skills ${l.pc.dim("\u2192")} ${l.pc.dim(d)}`),console.log(` ${l.pc.dim("\u2192")} ${(0,l.orange)("[claude]")} rule ${l.pc.dim("\u2192")} ${l.pc.dim(f)}`)):console.log(` ${l.pc.dim("\u2192")} ${(0,l.orange)("[claude]")} ${l.pc.yellow("assist mode")} (verification.auto: false) \u2014 manual /ironbee-verify only, no enforcement`),console.log(` ${l.pc.dim("\u2192")} ${(0,l.orange)("[claude]")} commands ${l.pc.dim("\u2192")} ${l.pc.dim(g)}`),console.log(` ${l.pc.dim("\u2192")} ${(0,l.orange)("[claude]")} agents ${l.pc.dim("\u2192")} ${l.pc.dim((0,c.join)(a,"agents"))}`),console.log(` ${l.pc.dim("\u2192")} ${(0,l.orange)("[claude]")} mcp ${l.pc.dim("\u2192")} ${l.pc.dim(L)}`)}else console.log(` ${l.pc.dim("\u2192")} ${(0,l.orange)("[claude]")} ${l.pc.yellow("monitoring-only mode")} (verification.enable: false)`),console.log(` ${l.pc.dim("\u2192")} ${(0,l.orange)("[claude]")} settings ${l.pc.dim("\u2192")} ${l.pc.dim(m)}`)}uninstall(e){this.cleanupArtifacts(e),console.log(` ${l.pc.dim("\u2192")} ${(0,l.orange)("[claude]")} removed hooks, skill, rule, command, MCP, and permissions`)}cleanupArtifacts(e){const n=(0,c.join)(e,".claude"),o=(0,c.join)(n,"skills","ironbee-verification.md"),r=(0,c.join)(n,"skills","ironbee-analyze.md"),i=(0,c.join)(n,"rules","ironbee-verification.md"),a=(0,c.join)(n,"commands","ironbee-analyze.md"),d=(0,c.join)(n,"commands","ironbee-verify.md"),f=(0,c.join)(n,"agents","ironbee-verifier.md");this.removeFile(o),this.removeFile(r),this.removeFile(i),this.removeFile(a),this.removeFile(d),this.removeFile(f),this.removeFile((0,c.join)(n,"agents","ironbee-scenario.md"));for(const y of P)this.removeFile((0,c.join)(n,"commands",`${y}.md`));this.removeFile((0,c.join)(n,"commands","ironbee-run-scenario.md"));const g=(0,c.join)(n,"settings.json");this.removeIronBeeHooks(g),this.removePermission(g),this.removeOTELEnv(g),this.maybeDeleteEmptySettings(g);const m=(0,c.join)(e,".mcp.json");this.removeMcpServer(m),this.removeAutoModeAllowlist((0,c.join)(n,"settings.local.json")),this.uninstallStatusLine(e),(0,D.pruneEmptyDirs)(n)}installStatusLine(e,n){if(!(0,u.isSessionStatusEnabled)(n))return;const o=(0,c.join)(e,".claude","settings.local.json"),r=this.readStatusLineBlock(o);r&&!(0,C.isIronbeeStatusLine)(r.command)&&(0,S.readStatusLineSnapshot)(e,"claude")===void 0&&(0,S.upsertStatusLineSnapshot)(e,"claude",r);const i={type:"command",command:Ee},a=(0,u.getStatusLineRefreshInterval)(n);a!==void 0&&(i.refreshInterval=a),this.writeStatusLineBlock(o,i),console.log(` ${l.pc.dim("\u2192")} ${(0,l.orange)("[claude]")} statusline ${l.pc.dim("\u2192")} ${l.pc.dim(o)}`)}uninstallStatusLine(e){const n=(0,c.join)(e,".claude","settings.local.json"),o=(0,S.readStatusLineSnapshot)(e,"claude");if(o){this.writeStatusLineBlock(n,o),(0,S.clearStatusLineSnapshot)(e,"claude");return}const r=this.readStatusLineBlock(n);r&&(0,C.isIronbeeStatusLine)(r.command)&&this.removeStatusLineBlock(n)}readStatusLineBlock(e){if((0,t.existsSync)(e))try{const n=JSON.parse((0,t.readFileSync)(e,"utf-8"));if(n===null||typeof n!="object")return;const o=n.statusLine;if(o===null||typeof o!="object")return;const r=o.command;if(typeof r!="string"||r.length===0)return;const i=o.padding,a=o.refreshInterval,d={type:"command",command:r};return typeof i=="number"&&(d.padding=i),typeof a=="number"&&(d.refreshInterval=a),d}catch(n){k.logger.debug(`failed to read statusLine from ${e}: ${n}`);return}}writeStatusLineBlock(e,n){let o={};if((0,t.existsSync)(e))try{const r=JSON.parse((0,t.readFileSync)(e,"utf-8"));r!==null&&typeof r=="object"&&!Array.isArray(r)&&(o=r)}catch(r){k.logger.debug(`failed to read ${e} for statusLine write: ${r}`)}else(0,t.mkdirSync)((0,c.join)(e,".."),{recursive:!0});o.statusLine=n,(0,t.writeFileSync)(e,JSON.stringify(o,null,2))}removeStatusLineBlock(e){if((0,t.existsSync)(e))try{const n=JSON.parse((0,t.readFileSync)(e,"utf-8"));if(n===null||typeof n!="object"||Array.isArray(n))return;const o=n;delete o.statusLine,Object.keys(o).length===0?(0,t.unlinkSync)(e):(0,t.writeFileSync)(e,JSON.stringify(o,null,2))}catch(n){k.logger.debug(`failed to remove statusLine from ${e}: ${n}`)}}writeAutoModeAllowlist(e){let n={};if((0,t.existsSync)(e))try{n=JSON.parse((0,t.readFileSync)(e,"utf-8"))}catch(d){k.logger.debug(`failed to parse ${e} for autoMode allowlist: ${d}`);return}else(0,t.mkdirSync)((0,c.join)(e,".."),{recursive:!0});const o=n.autoMode!==null&&typeof n.autoMode=="object"&&!Array.isArray(n.autoMode)?n.autoMode:{},r=Array.isArray(o.allow)?o.allow.filter(d=>typeof d=="string"):[],i=r.filter(d=>!d.includes(N)),a=r.length===0?[I]:i;o.allow=[...a,we],n.autoMode=o,(0,t.writeFileSync)(e,JSON.stringify(n,null,2))}removeAutoModeAllowlist(e){if(!(0,t.existsSync)(e))return;let n;try{n=JSON.parse((0,t.readFileSync)(e,"utf-8"))}catch(d){k.logger.debug(`failed to parse ${e} for autoMode strip: ${d}`);return}if(n.autoMode===null||typeof n.autoMode!="object"||Array.isArray(n.autoMode))return;const o=n.autoMode;if(!Array.isArray(o.allow))return;const r=o.allow.filter(d=>typeof d=="string"),i=r.filter(d=>!d.includes(N));if(i.length===r.length)return;i.length===0||i.length===1&&i[0]===I?delete o.allow:o.allow=i,Object.keys(o).length===0?delete n.autoMode:n.autoMode=o,Object.keys(n).length===0?(0,t.unlinkSync)(e):(0,t.writeFileSync)(e,JSON.stringify(n,null,2))}writeOTELEnv(e,n,o){let r={};if((0,t.existsSync)(e))try{const m=JSON.parse((0,t.readFileSync)(e,"utf-8"));m!==null&&typeof m=="object"&&!Array.isArray(m)&&(r=m)}catch(m){k.logger.debug(`failed to read ${e} for otel env write: ${m}`)}else(0,t.mkdirSync)((0,c.join)(e,".."),{recursive:!0});const i=r.env,a=i!==null&&typeof i=="object"&&!Array.isArray(i)?i:{},d=a.OTEL_EXPORTER_OTLP_ENDPOINT;if(typeof d=="string"&&d.length>0&&!j(a)){console.log(` ${l.pc.dim("\u2192")} ${(0,l.orange)("[claude]")} ${l.pc.yellow("existing OTEL telemetry env detected \u2014 left untouched (session_context not wired for this project)")}`);return}const f=(0,u.getOTELPort)(o),g=Ae((0,oe.resolveProjectName)(n));a.CLAUDE_CODE_ENABLE_TELEMETRY="1",a.OTEL_LOGS_EXPORTER="otlp",a.OTEL_METRICS_EXPORTER="none",a.OTEL_EXPORTER_OTLP_PROTOCOL="http/json",a.OTEL_EXPORTER_OTLP_ENDPOINT=`http://127.0.0.1:${f}`,a.OTEL_LOG_RAW_API_BODIES="file:.ironbee/otel",a.OTEL_RESOURCE_ATTRIBUTES=`ironbee.project_name=${g}`,a.OTEL_LOGS_EXPORT_INTERVAL="5000",r.env=a,(0,t.writeFileSync)(e,JSON.stringify(r,null,2)),console.log(` ${l.pc.dim("\u2192")} ${(0,l.orange)("[claude]")} otel env ${l.pc.dim("\u2192")} ${l.pc.dim(`${e} (127.0.0.1:${f})`)}`)}removeOTELEnv(e){if((0,t.existsSync)(e))try{const n=JSON.parse((0,t.readFileSync)(e,"utf-8"));if(n===null||typeof n!="object"||Array.isArray(n))return;const o=n,r=o.env;if(r===null||typeof r!="object"||Array.isArray(r))return;const i=r;if(!j(i))return;for(const a of Re)delete i[a];Object.keys(i).length===0&&delete o.env,(0,t.writeFileSync)(e,JSON.stringify(o,null,2))}catch(n){k.logger.debug(`failed to remove otel env from ${e}: ${n}`)}}maybeDeleteEmptySettings(e){if((0,t.existsSync)(e))try{const n=JSON.parse((0,t.readFileSync)(e,"utf-8"));Te(n)&&(0,t.unlinkSync)(e)}catch(n){k.logger.debug(`failed to inspect ${e} for emptiness: ${n}`)}}async runVerifyGate(e){await(0,F.run)(e)}async runClearVerdict(e){await(0,x.run)(e)}async runTrackAction(e){await(0,W.run)(e)}async runSessionStart(e){await(0,G.run)(e)}async runSubagentStart(e){await(0,Z.run)(e)}async runSubagentStop(e){await(0,ee.run)(e)}async runRequireVerdict(e,n){await(0,q.run)(e,n)}async runRequireVerification(e,n){await(0,z.run)(e,n)}async runActivityStart(e){await(0,Y.run)(e)}async runActivityEnd(e){await(0,K.run)(e)}async runTrackActionMonitor(e){await(0,X.run)(e)}async runSessionEnd(e){await(0,Q.run)(e)}async runTrackActionPre(e){}isIronBeeHook(e){return e.hooks.some(n=>n.command.includes(_e))}mergeHooksConfig(e,n){const o=n!=="monitor",r=n==="assist"?" --soft":"";let i={};if((0,t.existsSync)(e))try{i=JSON.parse((0,t.readFileSync)(e,"utf-8"))}catch(f){k.logger.debug(`failed to parse ${e}: ${f}`),i={}}i.hooks||(i.hooks={});for(const f of Object.keys(i.hooks)){const g=i.hooks[f].filter(m=>!this.isIronBeeHook(m));g.length===0?delete i.hooks[f]:i.hooks[f]=g}i.hooks.SessionStart||(i.hooks.SessionStart=[]),i.hooks.SessionStart.push({matcher:"",hooks:[{type:"command",command:"ironbee hook session-start --client claude"}]}),i.hooks.UserPromptSubmit||(i.hooks.UserPromptSubmit=[]),i.hooks.UserPromptSubmit.push({matcher:"",hooks:[{type:"command",command:"ironbee hook activity-start --client claude"}]}),o&&(i.hooks.PreToolUse||(i.hooks.PreToolUse=[]),i.hooks.PreToolUse.push({matcher:"mcp__browser-devtools__.*|mcp__node-devtools__.*|mcp__backend-devtools__.*|mcp__android-devtools__.*|mcp__terminal-devtools__.*",hooks:[{type:"command",command:`ironbee hook require-verification --client claude${r}`}]}),i.hooks.PreToolUse.push({matcher:"Write|Edit",hooks:[{type:"command",command:`ironbee hook require-verdict --client claude${r}`}]}),i.hooks.PostToolUse||(i.hooks.PostToolUse=[]),i.hooks.PostToolUse.push({matcher:"Write|Edit",hooks:[{type:"command",command:"ironbee hook clear-verdict --client claude"}]})),i.hooks.PostToolUse||(i.hooks.PostToolUse=[]);const a=o?"ironbee hook track-action --client claude":"ironbee hook track-action-monitor --client claude";i.hooks.PostToolUse.push({matcher:"",hooks:[{type:"command",command:a}]}),i.hooks.PostToolUseFailure||(i.hooks.PostToolUseFailure=[]),i.hooks.PostToolUseFailure.push({matcher:"",hooks:[{type:"command",command:a}]}),i.hooks.Stop||(i.hooks.Stop=[]);const d=n==="enforce"?"ironbee hook verify-gate --client claude":"ironbee hook activity-end --client claude";i.hooks.Stop.push({matcher:"",hooks:[{type:"command",command:d}]}),i.hooks.SubagentStart||(i.hooks.SubagentStart=[]),i.hooks.SubagentStart.push({matcher:"",hooks:[{type:"command",command:"ironbee hook subagent-start --client claude"}]}),i.hooks.SubagentStop||(i.hooks.SubagentStop=[]),i.hooks.SubagentStop.push({matcher:"",hooks:[{type:"command",command:"ironbee hook subagent-stop --client claude"}]}),i.hooks.SessionEnd||(i.hooks.SessionEnd=[]),i.hooks.SessionEnd.push({matcher:"",hooks:[{type:"command",command:"ironbee hook session-end --client claude"}]}),(0,t.writeFileSync)(e,JSON.stringify(i,null,2))}removeIronBeeHooks(e){if((0,t.existsSync)(e))try{const n=JSON.parse((0,t.readFileSync)(e,"utf-8"));if(!n.hooks)return;for(const o of Object.keys(n.hooks)){const r=n.hooks[o].filter(i=>!this.isIronBeeHook(i));r.length===0?delete n.hooks[o]:n.hooks[o]=r}(0,t.writeFileSync)(e,JSON.stringify(n,null,2))}catch(n){k.logger.debug(`failed to remove hooks from ${e}: ${n}`)}}removeMcpServer(e){if((0,t.existsSync)(e))try{const n=JSON.parse((0,t.readFileSync)(e,"utf-8"));let o=!1;n.mcpServers&&n.mcpServers[b]&&(delete n.mcpServers[b],o=!0),n.mcpServers&&n.mcpServers[_]&&(delete n.mcpServers[_],o=!0),n.mcpServers&&n.mcpServers[E]&&(delete n.mcpServers[E],o=!0),n.mcpServers&&n.mcpServers[w]&&(delete n.mcpServers[w],o=!0),n.mcpServers&&n.mcpServers[O]&&(delete n.mcpServers[O],o=!0),B(n)?(0,t.unlinkSync)(e):o&&(0,t.writeFileSync)(e,JSON.stringify(n,null,2))}catch(n){k.logger.debug(`failed to remove MCP server from ${e}: ${n}`)}}removePermission(e){if((0,t.existsSync)(e))try{const n=JSON.parse((0,t.readFileSync)(e,"utf-8")),o=`mcp__${b}__*`,r=`mcp__${_}__*`,i=`mcp__${E}__*`,a=`mcp__${w}__*`,d=`mcp__${O}__*`,f="Bash(ironbee *)",g="Bash(ironbee analyze)";n.permissions?.allow&&(n.permissions.allow=n.permissions.allow.filter(m=>m!==o&&m!==r&&m!==i&&m!==a&&m!==d&&m!==f&&m!==g),(0,t.writeFileSync)(e,JSON.stringify(n,null,2)))}catch(n){k.logger.debug(`failed to remove permission from ${e}: ${n}`)}}removeFile(e){(0,t.existsSync)(e)&&(0,t.unlinkSync)(e)}writeMcpConfig(e,n){let o={mcpServers:{}};if((0,t.existsSync)(e))try{o=JSON.parse((0,t.readFileSync)(e,"utf-8")),o.mcpServers||(o.mcpServers={})}catch(r){k.logger.debug(`failed to parse ${e}: ${r}`),o={mcpServers:{}}}if(delete o.mcpServers[b],delete o.mcpServers[_],delete o.mcpServers[E],delete o.mcpServers[w],delete o.mcpServers[O],B(o)){try{(0,t.rmSync)(e,{force:!0})}catch(r){k.logger.debug(`failed to remove empty ${e}: ${r}`)}return}(0,t.writeFileSync)(e,JSON.stringify(o,null,2))}writePermissions(e,n,o){let r={};if((0,t.existsSync)(e))try{r=JSON.parse((0,t.readFileSync)(e,"utf-8"))}catch(p){k.logger.debug(`failed to parse ${e}: ${p}`),r={}}r.permissions||(r.permissions={allow:[],deny:[]}),r.permissions.allow||(r.permissions.allow=[]);const i=`mcp__${b}__*`,a=`mcp__${_}__*`,d=`mcp__${E}__*`,f=`mcp__${w}__*`,g=`mcp__${O}__*`,m="Bash(ironbee *)",y="Bash(ironbee analyze)";if(n){const p=(0,u.loadConfig)(o);r.permissions.allow=R(r.permissions.allow,(0,u.isCycleEnabled)(p,"browser"),i),r.permissions.allow=R(r.permissions.allow,(0,u.isCycleEnabled)(p,"node"),a),r.permissions.allow=R(r.permissions.allow,(0,u.isCycleEnabled)(p,"backend"),d),r.permissions.allow=R(r.permissions.allow,(0,u.isCycleEnabled)(p,"android"),f),r.permissions.allow=R(r.permissions.allow,(0,u.isCycleEnabled)(p,"terminal"),g),r.permissions.allow=r.permissions.allow.filter($=>$!==y),r.permissions.allow.includes(m)||r.permissions.allow.push(m)}else r.permissions.allow=r.permissions.allow.filter(p=>p!==i&&p!==a&&p!==d&&p!==f&&p!==g&&p!==m&&p!==y);(0,t.writeFileSync)(e,JSON.stringify(r,null,2))}}function Le(s){(0,t.mkdirSync)((0,c.join)(s,".ironbee"),{recursive:!0}),(0,J.ensureIronBeeGitignored)(s)}v(Le,"prepareIronBeeDir");0&&(module.exports={ClaudeClient,prepareIronBeeDir});
@@ -24,6 +24,7 @@ verification, so its script must collect what the android cycle collects). In th
24
24
  - **Log-evidence path** — `adt_o11y_log-read` / `adt_o11y_log-follow` (with `returnOutput: true`)
25
25
  for the tag(s) relevant to the change; confirm expected lines appear AND no FATAL / crash (E/
26
26
  entries) for the app package.
27
+ - **Network-evidence path** — capture outgoing HTTP traffic with `adt_o11y_get-http-requests` (`returnOutput: true`): start capture, drive the app (`adt_interaction_*`) to trigger traffic, read again, and put the captured request(s)/status in your result. Optional setup helpers (NOT evidence): `adt_o11y_new-trace-id` to pin a correlation root, `adt_stub_*` to mock/intercept responses.
27
28
 
28
29
  `return` the evidence — UI-snapshot text, log lines, the screenshot `filePath`s — **plus explicit
29
30
  pass/fail assertions**. That returned result is what `/ironbee-verify scenario:<name>` reads to judge
@@ -0,0 +1,26 @@
1
+ ### Terminal platform (enabled)
2
+ - **Use for**: CLI / REPL / TUI scenarios driven in a PTY.
3
+ - **Server**: `terminal-devtools` · **scenario tools**: `mcp__terminal-devtools__tdt_scenario-*`.
4
+ - **Store**: project → `.ironbee/scenarios/tdt`, global → `~/.ironbee/scenarios/tdt` (the server's
5
+ `SCENARIOS_DIR`; pass `scope`, the server resolves the path).
6
+ - Scenario **scripts** call terminal tools via `callTool('<bare-tool>', {...})` — discover the
7
+ available `tdt_*` tool names (pty / interaction / content / sync …) from your connected
8
+ MCP schemas; don't guess.
9
+
10
+ **What to test & how — capture the SAME evidence the verifier would** (a scenario runs FOR
11
+ verification, so its script must collect what the terminal cycle collects). In the script, pick an
12
+ **evidence path** for the changed code area:
13
+ 1. **Run-evidence path** — run the command with `callTool('tdt_pty_run', { ... })` **with
14
+ `returnOutput: true`** so the full output + exit code come back in the result; put that captured
15
+ output and exit code in your result. Best for non-interactive CLIs (a subcommand, a script).
16
+ 2. **Interactive-evidence path** — `callTool('tdt_pty_start', {...})` to spawn (returns a `paneId`),
17
+ then drive input with `callTool('tdt_interaction_send-keys', {...})` (tmux syntax: `Enter` / `C-c` /
18
+ `Up` / `Tab`) or `callTool('tdt_interaction_send-text', {...})`, synchronize with
19
+ `callTool('tdt_sync_wait-for', {...})` (prefer over fixed delays), and read output with
20
+ `callTool('tdt_content_capture', { returnOutput: true, ... })` (`mode: stream` for REPLs / shells,
21
+ `mode: screen` for a full-screen TUI) — put the captured output in your result. Stop the pane with
22
+ `callTool('tdt_pty_stop', {...})` when done.
23
+
24
+ `return` the evidence — captured output / exit code — **plus explicit pass/fail assertions**. That
25
+ returned result is what `/ironbee-verify scenario:<name>` reads to judge whether the change behaved
26
+ correctly. **`terminal-devtools` drives CLIs/REPLs/TUIs in a PTY.**
@@ -32,6 +32,9 @@ If you see only `ios/`, `web/`, or no mobile directories — the project does NO
32
32
  - **Log-evidence path** (device logs confirm the changed code path executed):
33
33
  - Read Logcat output for the tag(s) relevant to the changed code: `mcp__android-devtools__adt_o11y_log-read` or `mcp__android-devtools__adt_o11y_log-follow` (drain a follow with `mcp__android-devtools__adt_o11y_log-get-followed`, stop it with `mcp__android-devtools__adt_o11y_log-stop-follow`).
34
34
  - Confirm expected log lines appear AND no unexpected crashes (FATAL / E/ entries for the app package).
35
+ - **Network-evidence path** (captured HTTP traffic confirms a network/API-related change):
36
+ - Capture the app's outgoing HTTP(S) requests: `mcp__android-devtools__adt_o11y_get-http-requests` (Frida/OkHttp in-process — no proxy, no CA install; OkHttp-based stacks only — Retrofit / React Native / HttpURLConnection). **Capture is forward-looking**: call it once to start capture, drive the app to trigger traffic (`mcp__android-devtools__adt_interaction_*`), then call it again to read. Confirm the expected request(s) / response status appear.
37
+ - **Auxiliary (NOT evidence — setup/correlation only):** to pin one correlation root across the flow, optionally `mcp__android-devtools__adt_o11y_new-trace-id` first (it stamps `traceparent` on every captured request; inspect/clear via `mcp__android-devtools__adt_o11y_set-trace-context` / `mcp__android-devtools__adt_o11y_get-trace-context`). To set up test conditions, `mcp__android-devtools__adt_stub_mock-http-response` / `mcp__android-devtools__adt_stub_intercept-http-request` mock or mutate responses (list/clear with `mcp__android-devtools__adt_stub_list` / `mcp__android-devtools__adt_stub_clear`). `mcp__android-devtools__adt_figma_compare-screen-with-design` checks emulator-vs-Figma parity (optional, requires `FIGMA_ACCESS_TOKEN`). None of these count toward the gate — they shape the test, they don't inspect it.
35
38
 
36
39
  **Batch (speed):** connect + launch-app run standalone first (prerequisites). On the device-evidence path, batch the UI interactions + the UI snapshot into one `mcp__android-devtools__adt_execute`; the snapshot captures the state after the batched interactions, so to assert an intermediate state take a snapshot at that point too. The device-evidence screenshot is usually pixel-judged (a visual change) — take THAT one standalone with `includeBase64: true` so you can see it; batch it only when it's purely gate evidence. Log-evidence reads batch together too.
37
40
 
@@ -51,6 +54,7 @@ On fail, include `issues`. On pass after a previous fail, include `fixes`.
51
54
  Android-cycle pass criteria:
52
55
  - **Device-evidence**: at least one UI interaction tool fired AND a screenshot was taken AND a UI snapshot was taken AND both show the expected UI state/structure.
53
56
  - **Log-evidence**: Logcat was read AND the expected log lines are present AND no crash (FATAL / unhandled exception) from the app's package.
57
+ - **Network-evidence**: the app's outgoing HTTP traffic was captured AND the expected request(s) / response status confirm the change behaved correctly.
54
58
 
55
59
  ## Multi-cycle (browser + android simultaneously)
56
60
 
@@ -6,7 +6,7 @@
6
6
 
7
7
  > **Recording (only when `recording.enable` is on in config):** the gate blocks every other browser tool until you first call `mcp__browser-devtools__bdt_content_start-recording`, and `submit-verdict` rejects with `"recording is still active"` unless you call `mcp__browser-devtools__bdt_content_stop-recording` after the steps below. **Treat start/stop as bookends around steps 1-5.** The same is enforced as step 6 of the Universal flow.
8
8
 
9
- 1. **Navigate**: `mcp__browser-devtools__bdt_navigation_go-to` — go to the affected page(s)
9
+ 1. **Navigate**: `mcp__browser-devtools__bdt_navigation_go-to` — go to the affected page(s) **AND any downstream page that renders or consumes what the change produces** — verify the change's effect where it's observed, not only the page the edited file owns
10
10
  2. **Interact**: actually exercise what changed — click buttons, fill forms, submit data, trigger workflows. Don't just look at the page.
11
11
  3. **Screenshot**: `mcp__browser-devtools__bdt_content_take-screenshot` — capture the final visual state
12
12
  4. **Accessibility**: `mcp__browser-devtools__bdt_a11y_take-aria-snapshot` — verify page structure
@@ -0,0 +1,62 @@
1
+ <!-- Terminal verification is ENABLED for this project. The Stop hook
2
+ enforces a terminal cycle whenever an edited file matches
3
+ `terminal.verifyPatterns`. -->
4
+
5
+ ## ⚠️ CRITICAL: when NOT to use terminal-devtools
6
+
7
+ **`terminal-devtools` is ONLY for programs that speak through a terminal** — CLIs, REPLs, and full-screen TUIs (the tools drive them by spawning them attached to a PTY, like tmux). Do **NOT** call `tdt_*` tools for changes with no terminal-observable behavior.
8
+
9
+ **How to tell whether the change has a terminal surface:**
10
+ - A CLI subcommand / script / binary that produces output and an exit code
11
+ - A REPL or interactive prompt the user types into
12
+ - A full-screen terminal UI (a TUI rendered on the terminal grid)
13
+
14
+ If the change is **pure library internals with no runnable entry point**, or a **web-only UI change** (that's the browser cycle) — there is nothing terminal to drive. Do NOT call any `tdt_*` tools.
15
+
16
+ **Misconfiguration recovery.** If this cycle activated but the project has nothing terminal to drive, the operator enabled the terminal cycle by mistake. The Stop hook will keep blocking with `incomplete_tools` until `maxRetries` is exhausted. Don't attempt to spawn a PTY. Stop and tell the user clearly: the project has no terminal-observable surface; ask them to run `ironbee terminal disable` to unblock the gate.
17
+
18
+ ## Terminal flow
19
+
20
+ Pick **ONE evidence path** per changed code area:
21
+
22
+ - **Run-evidence path** (one-shot CLI command — best for non-interactive CLIs: a subcommand, a script):
23
+ - Run the command with `mcp__terminal-devtools__tdt_pty_run` — it spawns, runs, and returns the full output + exit code in one call.
24
+ - Confirm the output AND exit code reflect the change (expected text present, `exitCode: 0` unless a failure is the expected result).
25
+ - **Interactive-evidence path** (REPL / TUI / multi-step flows):
26
+ - Spawn the program: `mcp__terminal-devtools__tdt_pty_start` (returns a `paneId`).
27
+ - Drive input: `mcp__terminal-devtools__tdt_interaction_send-keys` (keystrokes, tmux syntax: `Enter` / `C-c` / `Up` / `Tab`) or `mcp__terminal-devtools__tdt_interaction_send-text` (literal text).
28
+ - Synchronize: `mcp__terminal-devtools__tdt_sync_wait-for` to block until the expected output appears — **prefer this over fixed delays**.
29
+ - Read output: `mcp__terminal-devtools__tdt_content_capture` — `mode: stream` for line-oriented programs / REPLs / shells (incremental cursor reads via `since`); `mode: screen` for the rendered grid of a full-screen TUI.
30
+ - Tear down when done: `mcp__terminal-devtools__tdt_pty_stop`.
31
+ - **Auxiliary (NOT evidence — synchronization / process-control helpers only):** `mcp__terminal-devtools__tdt_sync_wait-for-idle` (wait until output goes quiet when there's no clean marker), `mcp__terminal-devtools__tdt_content_get-cursor`, `mcp__terminal-devtools__tdt_pty_resize`, `mcp__terminal-devtools__tdt_pty_signal`, `mcp__terminal-devtools__tdt_pty_list`. These shape / control the run; they don't inspect it, so they don't count toward the gate.
32
+
33
+ ### Verdict fields
34
+ The verdict is platform-agnostic — submit only semantic judgment:
35
+
36
+ ```json
37
+ {
38
+ "session_id": "<sid>",
39
+ "status": "pass",
40
+ "checks": ["`mycli deploy` exits 0 with expected summary", "no stack trace in output"]
41
+ }
42
+ ```
43
+
44
+ On fail, include `issues`. On pass after a previous fail, include `fixes`.
45
+
46
+ Terminal-cycle pass criteria:
47
+ - **Run-evidence**: a command ran via `mcp__terminal-devtools__tdt_pty_run` AND its output + exit code confirm the change.
48
+ - **Interactive-evidence**: a pane was spawned AND input was driven AND output was captured AND it shows the expected result.
49
+
50
+ ## Multi-cycle (browser + terminal simultaneously)
51
+
52
+ When you edit both a web frontend file (browser-cycle) and a CLI source file (terminal-cycle) in the same task, both cycles activate. **Single** `verification-start`, **single** `verdict.json`, **single** retry counter cover both. One platform-agnostic verdict regardless of how many cycles ran:
53
+
54
+ ```json
55
+ {
56
+ "session_id": "<sid>",
57
+ "status": "pass",
58
+ "checks": ["web dashboard renders", "`mycli sync` exits 0 with expected summary"]
59
+ }
60
+ ```
61
+
62
+ For a multi-cycle `pass`, ALL active cycles' pass criteria must hold.
@@ -1 +1 @@
1
- "use strict";var t=Object.defineProperty;var g=Object.getOwnPropertyDescriptor;var p=Object.getOwnPropertyNames;var u=Object.prototype.hasOwnProperty;var m=(e,s)=>{for(var n in s)t(e,n,{get:s[n],enumerable:!0})},l=(e,s,n,o)=>{if(s&&typeof s=="object"||typeof s=="function")for(let r of p(s))!u.call(e,r)&&r!==n&&t(e,r,{get:()=>s[r],enumerable:!(o=g(s,r))||o.enumerable});return e};var y=e=>l(t({},"__esModule",{value:!0}),e);var S={};m(S,{claudeProcessAnalyticsCommand:()=>f});module.exports=y(S);var c=require("commander"),i=require("../../lib/logger"),a=require("../../analytics/claude/emit"),d=require("../../analytics/claude/log");const f=new c.Command("process-analytics").description("Internal worker \u2014 project + emit a session_analytics snapshot for one Claude trigger").requiredOption("--project <dir>","project directory (where .ironbee/sessions/<sid>/ lives)").requiredOption("--session <id>","session id").requiredOption("--trigger <type>","Stop | SessionEnd").option("--end-reason <reason>","SessionEnd reason (optional)").option("--transcript-source <src>","claude-code | cursor | missing").action(async e=>{const s=e.trigger==="SessionEnd"?"SessionEnd":"Stop",n=e.transcriptSource??"claude-code";(0,i.setLogFile)(`${e.project}/.ironbee/sessions/${e.session}/session.log`);const o=new d.AnalyticsLog(e.project,e.session);o.info(`worker: claude process-analytics start (trigger=${s} session=${e.session}${e.endReason?` end_reason=${e.endReason}`:""})`);try{const r=await(0,a.emitAnalytics)({projectDir:e.project,sessionId:e.session,triggerType:s,endReason:e.endReason,transcriptSource:n,log:o});o.info(`worker: claude process-analytics done (status=${r.status} reason=${r.reason})`)}catch(r){i.logger.debug(`claude process-analytics: unexpected error: ${r instanceof Error?r.message:r}`),o.error(`worker: unexpected error: ${r instanceof Error?r.message:r}`)}});0&&(module.exports={claudeProcessAnalyticsCommand});
1
+ "use strict";var t=Object.defineProperty;var g=Object.getOwnPropertyDescriptor;var m=Object.getOwnPropertyNames;var u=Object.prototype.hasOwnProperty;var l=(e,s)=>{for(var n in s)t(e,n,{get:s[n],enumerable:!0})},y=(e,s,n,o)=>{if(s&&typeof s=="object"||typeof s=="function")for(let r of m(s))!u.call(e,r)&&r!==n&&t(e,r,{get:()=>s[r],enumerable:!(o=g(s,r))||o.enumerable});return e};var f=e=>y(t({},"__esModule",{value:!0}),e);var w={};l(w,{claudeProcessAnalyticsCommand:()=>S});module.exports=f(w);var c=require("commander"),i=require("../../lib/logger"),a=require("../../analytics/claude/emit"),d=require("../../analytics/claude/log"),p=require("../../lib/runtime-paths");const S=new c.Command("process-analytics").description("Internal worker \u2014 project + emit a session_analytics snapshot for one Claude trigger").requiredOption("--project <dir>","project directory (where .ironbee/sessions/<sid>/ lives)").requiredOption("--session <id>","session id").requiredOption("--trigger <type>","Stop | SessionEnd").option("--end-reason <reason>","SessionEnd reason (optional)").option("--transcript-source <src>","claude-code | cursor | missing").action(async e=>{const s=e.trigger==="SessionEnd"?"SessionEnd":"Stop",n=e.transcriptSource??"claude-code";(0,i.setLogFile)((0,p.sessionLogFile)(e.project,e.session));const o=new d.AnalyticsLog(e.project,e.session);o.info(`worker: claude process-analytics start (trigger=${s} session=${e.session}${e.endReason?` end_reason=${e.endReason}`:""})`);try{const r=await(0,a.emitAnalytics)({projectDir:e.project,sessionId:e.session,triggerType:s,endReason:e.endReason,transcriptSource:n,log:o});o.info(`worker: claude process-analytics done (status=${r.status} reason=${r.reason})`)}catch(r){i.logger.debug(`claude process-analytics: unexpected error: ${r instanceof Error?r.message:r}`),o.error(`worker: unexpected error: ${r instanceof Error?r.message:r}`)}});0&&(module.exports={claudeProcessAnalyticsCommand});
@@ -1,2 +1,2 @@
1
- "use strict";var d=Object.defineProperty;var L=Object.getOwnPropertyDescriptor;var T=Object.getOwnPropertyNames;var k=Object.prototype.hasOwnProperty;var u=(e,n)=>d(e,"name",{value:n,configurable:!0});var x=(e,n)=>{for(var t in n)d(e,t,{get:n[t],enumerable:!0})},E=(e,n,t,r)=>{if(n&&typeof n=="object"||typeof n=="function")for(let o of T(n))!k.call(e,o)&&o!==t&&d(e,o,{get:()=>n[o],enumerable:!(r=L(n,o))||r.enumerable});return e};var F=e=>E(d({},"__esModule",{value:!0}),e);var R={};x(R,{applyStatusLineToggle:()=>O,syncChainedStatusLine:()=>P});module.exports=F(R);var i=require("fs"),c=require("path"),S=require("../registry"),$=require("./hooks/session-status"),s=require("../../lib/config"),y=require("../../lib/gitignore"),C=require("../../lib/logger"),f=require("../../lib/output"),m=require("../../hooks/core/session-state");function N(e){if(!(0,i.existsSync)(e))return{};try{return JSON.parse((0,i.readFileSync)(e,"utf-8"))}catch(n){throw C.logger.debug(`failed to read ${e}: ${n}`),new Error(`Config at ${e} is not valid JSON: ${n instanceof Error?n.message:n}`)}}u(N,"readConfigFile");function J(e,n){(0,i.mkdirSync)((0,c.join)(e,".."),{recursive:!0}),(0,i.writeFileSync)(e,JSON.stringify(n,null,2)+`
2
- `)}u(J,"writeConfigFile");function O(e,n,t,r){const o=(0,s.getTargetConfigPath)(t,n),a=N(o),g=e?"enabled":"disabled",l=(0,s.loadConfig)(n);l.statusLine={...l.statusLine,enable:e};const b=(0,s.isSessionStatusEnabled)((0,s.loadConfig)(n)),w=(0,s.isSessionStatusEnabled)(l);if(b===w&&a.statusLine?.enable===e){console.log(`${f.pc.dim("\xB7")} Statusline already ${g} in ${t} config (${f.pc.dim(o)}). No-op.`);return}const h=(0,S.resolveTargetClients)(n,r);for(const B of h)B.install(n,l);t!=="global"&&(0,y.ensureIronBeeGitignored)(n);const p={...a,statusLine:{...a.statusLine,enable:e}};J(o,p);const v=e?"Enabled":"Disabled",I=e?"The statusline wrapper now emits session_status events and chains your existing statusline.":"Your original statusline is restored; no session_status events are emitted.";console.log(`${f.pc.green("\u2713")} ${v} statusline in ${t} config (${f.pc.dim(o)}).`),console.log(` ${f.pc.dim(I)}`),console.log(` ${f.pc.yellow("\u26A0")} Restart your editor / agent session for the change to take effect.`)}u(O,"applyStatusLineToggle");function P(e){const n=(0,$.resolveChainTarget)(e)??null,t=(0,c.join)(e,".ironbee","sessions");if(!(0,i.existsSync)(t))return 0;let r=0,o;try{o=(0,i.readdirSync)(t)}catch(a){return C.logger.debug(`statusline sync: failed to list ${t}: ${a}`),0}for(const a of o){const g=(0,c.join)(t,a);!(0,i.existsSync)((0,c.join)(g,"state.json"))||(0,m.readState)(g).chainedStatusLine===n||((0,m.setChainedStatusLine)(g,n),r++)}return r}u(P,"syncChainedStatusLine");0&&(module.exports={applyStatusLineToggle,syncChainedStatusLine});
1
+ "use strict";var c=Object.defineProperty;var T=Object.getOwnPropertyDescriptor;var k=Object.getOwnPropertyNames;var x=Object.prototype.hasOwnProperty;var u=(e,n)=>c(e,"name",{value:n,configurable:!0});var E=(e,n)=>{for(var o in n)c(e,o,{get:n[o],enumerable:!0})},F=(e,n,o,r)=>{if(n&&typeof n=="object"||typeof n=="function")for(let t of k(n))!x.call(e,t)&&t!==o&&c(e,t,{get:()=>n[t],enumerable:!(r=T(n,t))||r.enumerable});return e};var N=e=>F(c({},"__esModule",{value:!0}),e);var _={};E(_,{applyStatusLineToggle:()=>O,syncChainedStatusLine:()=>P});module.exports=N(_);var i=require("fs"),d=require("path"),S=require("../registry"),$=require("./hooks/session-status"),y=require("../../lib/runtime-paths"),s=require("../../lib/config"),b=require("../../lib/gitignore"),C=require("../../lib/logger"),f=require("../../lib/output"),m=require("../../hooks/core/session-state");function R(e){if(!(0,i.existsSync)(e))return{};try{return JSON.parse((0,i.readFileSync)(e,"utf-8"))}catch(n){throw C.logger.debug(`failed to read ${e}: ${n}`),new Error(`Config at ${e} is not valid JSON: ${n instanceof Error?n.message:n}`)}}u(R,"readConfigFile");function J(e,n){(0,i.mkdirSync)((0,d.join)(e,".."),{recursive:!0}),(0,i.writeFileSync)(e,JSON.stringify(n,null,2)+`
2
+ `)}u(J,"writeConfigFile");function O(e,n,o,r){const t=(0,s.getTargetConfigPath)(o,n),a=R(t),g=e?"enabled":"disabled",l=(0,s.loadConfig)(n);l.statusLine={...l.statusLine,enable:e};const p=(0,s.isSessionStatusEnabled)((0,s.loadConfig)(n)),v=(0,s.isSessionStatusEnabled)(l);if(p===v&&a.statusLine?.enable===e){console.log(`${f.pc.dim("\xB7")} Statusline already ${g} in ${o} config (${f.pc.dim(t)}). No-op.`);return}const w=(0,S.resolveTargetClients)(n,r);for(const L of w)L.install(n,l);o!=="global"&&(0,b.ensureIronBeeGitignored)(n);const h={...a,statusLine:{...a.statusLine,enable:e}};J(t,h);const I=e?"Enabled":"Disabled",B=e?"The statusline wrapper now emits session_status events and chains your existing statusline.":"Your original statusline is restored; no session_status events are emitted.";console.log(`${f.pc.green("\u2713")} ${I} statusline in ${o} config (${f.pc.dim(t)}).`),console.log(` ${f.pc.dim(B)}`),console.log(` ${f.pc.yellow("\u26A0")} Restart your editor / agent session for the change to take effect.`)}u(O,"applyStatusLineToggle");function P(e){const n=(0,$.resolveChainTarget)(e)??null,o=(0,y.sessionsRoot)(e);if(!(0,i.existsSync)(o))return 0;let r=0,t;try{t=(0,i.readdirSync)(o)}catch(a){return C.logger.debug(`statusline sync: failed to list ${o}: ${a}`),0}for(const a of t){const g=(0,d.join)(o,a);!(0,i.existsSync)((0,d.join)(g,"state.json"))||(0,m.readState)(g).chainedStatusLine===n||((0,m.setChainedStatusLine)(g,n),r++)}return r}u(P,"syncChainedStatusLine");0&&(module.exports={applyStatusLineToggle,syncChainedStatusLine});
@@ -177,3 +177,6 @@ The platform sections below tell you each enabled cycle's server, tool prefix, a
177
177
 
178
178
  <!--IRONBEE:PLATFORM:android-->
179
179
  <!--/IRONBEE:PLATFORM:android-->
180
+
181
+ <!--IRONBEE:PLATFORM:terminal-->
182
+ <!--/IRONBEE:PLATFORM:terminal-->
@@ -36,7 +36,22 @@ The delegating prompt may tell you what to verify in one of two ways:
36
36
  it names (this replaces the default "exercise the changed pages/endpoints").
37
37
 
38
38
  Map each `checks` entry to a scenario step, each `issues` entry to a step that failed. If no scenario
39
- is given at all, exercise the changed pages/endpoints for each active cycle.
39
+ is given at all, exercise the changed pages/endpoints for each active cycle **plus the downstream
40
+ flows they feed** (see *Verify end-to-end* below).
41
+
42
+ ## Verify end-to-end — trace the blast radius (don't stop at the edited file)
43
+
44
+ A change's defect most often surfaces not on the edited file's own surface but in a **downstream
45
+ consumer** of what the change produces — wherever its output is read back, stored, rendered, or acted
46
+ on. Before driving tools, spend ONE quick pass reading/grepping the code to map the blast radius:
47
+ identify what the change produces and which other surfaces consume it, then exercise the FULL flow
48
+ from where the change is produced through to where its effect is observable — not only the surface the
49
+ edited file owns. A feature that works at its source but breaks in a downstream consumer is a **FAIL**.
50
+
51
+ This holds even when the consumer was not itself edited: the place you should have updated but didn't
52
+ never appears in the changed-files list, so don't let that list bound your verification — **follow the
53
+ data, not the diff.** Keep the mapping quick (a focused scan, not a full audit) so it doesn't eat the
54
+ speed budget.
40
55
 
41
56
  ## Session id — you don't need it
42
57
  The `ironbee hook` commands resolve the session automatically from your environment
@@ -118,7 +133,7 @@ Each tool call is a separate LLM round-trip, and that round-trip — not the too
118
133
  — is the dominant cost of a verification. Drive the tools in as few turns as you can:
119
134
 
120
135
  - **Batch a scope's work into ONE `*_execute` call.** Each cycle exposes a batch tool
121
- (`bdt_execute` / `ndt_execute` / `bedt_execute` / `adt_execute`) that runs many steps in
136
+ (`bdt_execute` / `ndt_execute` / `bedt_execute` / `adt_execute` / `tdt_execute`) that runs many steps in
122
137
  one turn — nest each as a `callTool('<tool>', { … })`. A batch nests only that cycle's own
123
138
  tools (you can't mix servers in one `*_execute`). It's a JS sandbox, so a later step
124
139
  can reuse a value an earlier `callTool` returned
@@ -147,3 +162,6 @@ Each tool call is a separate LLM round-trip, and that round-trip — not the too
147
162
 
148
163
  <!--IRONBEE:PLATFORM:android-->
149
164
  <!--/IRONBEE:PLATFORM:android-->
165
+
166
+ <!--IRONBEE:PLATFORM:terminal-->
167
+ <!--/IRONBEE:PLATFORM:terminal-->
@@ -100,3 +100,6 @@ The platform sections below list each enabled cycle's server, tool prefix, and s
100
100
 
101
101
  <!--IRONBEE:PLATFORM:android-->
102
102
  <!--/IRONBEE:PLATFORM:android-->
103
+
104
+ <!--IRONBEE:PLATFORM:terminal-->
105
+ <!--/IRONBEE:PLATFORM:terminal-->
@@ -35,3 +35,6 @@ The platform sections below list each enabled cycle's server, tool prefix, and s
35
35
 
36
36
  <!--IRONBEE:PLATFORM:android-->
37
37
  <!--/IRONBEE:PLATFORM:android-->
38
+
39
+ <!--IRONBEE:PLATFORM:terminal-->
40
+ <!--/IRONBEE:PLATFORM:terminal-->
@@ -53,3 +53,6 @@ running anything, use `ironbee scenario status`.)
53
53
 
54
54
  <!--IRONBEE:PLATFORM:android-->
55
55
  <!--/IRONBEE:PLATFORM:android-->
56
+
57
+ <!--IRONBEE:PLATFORM:terminal-->
58
+ <!--/IRONBEE:PLATFORM:terminal-->
@@ -102,6 +102,9 @@ stripping a leading `fix` / `report` mode token.
102
102
  <!--IRONBEE:PLATFORM:android-->
103
103
  <!--/IRONBEE:PLATFORM:android-->
104
104
 
105
+ <!--IRONBEE:PLATFORM:terminal-->
106
+ <!--/IRONBEE:PLATFORM:terminal-->
107
+
105
108
  ---
106
109
 
107
110
  ## When to FAIL
@@ -1 +1 @@
1
- "use strict";var d=Object.defineProperty;var v=Object.getOwnPropertyDescriptor;var A=Object.getOwnPropertyNames;var w=Object.prototype.hasOwnProperty;var p=(i,t)=>d(i,"name",{value:t,configurable:!0});var E=(i,t)=>{for(var n in t)d(i,n,{get:t[n],enumerable:!0})},b=(i,t,n,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let e of A(t))!w.call(i,e)&&e!==n&&d(i,e,{get:()=>t[e],enumerable:!(o=v(t,e))||o.enumerable});return i};var h=i=>b(d({},"__esModule",{value:!0}),i);var x={};E(x,{run:()=>k});module.exports=h(x);var a=require("../../../hooks/core/actions"),m=require("../../../hooks/core/activity-end"),r=require("../../../lib/logger"),u=require("../../../lib/output"),f=require("../../../lib/stdin"),l=require("../../../analytics/codex/spawn"),c=require("../../../hooks/core/session-state"),y=require("../util");async function k(i){const t=(0,y.parseCodexHookStdin)((0,f.readStdin)()),n=t.session_id??"default",o=`${i}/.ironbee/sessions/${n}`,e=`${o}/actions.jsonl`;(0,r.setLogFile)(`${o}/session.log`);const g=(0,c.readState)(o)?.activeActivityId??"";if(await(0,m.runActivityEnd)({sessionDir:o,actionsFile:e,projectDir:i,sessionId:n})){const s=Date.now(),S={...(0,a.baseFields)(e),id:(0,a.deterministicSessionEndId)(n),type:"session_end",timestamp:s,session_id:n,duration:(0,a.findDurationSinceLastAction)(e,"session_start",s),reason:"checkpoint"};await(0,a.appendAction)(e,S)}try{const s=(0,c.readState)(o);(0,l.spawnDetachedCodexAnalyticsWorker)({projectDir:i,sessionId:n,rolloutPath:t.transcript_path,userEmail:s?.userEmail??void 0,usageType:s?.usageType??void 0,usagePlan:s?.usagePlan??void 0,activityId:g})}catch(s){r.logger.debug(`codex analytics spawn failed: ${s instanceof Error?s.message:s}`)}r.logger.debug(`activity-end: ${n}`),(0,u.writeAndExit)(JSON.stringify({}),0)}p(k,"run");0&&(module.exports={run});
1
+ "use strict";var d=Object.defineProperty;var A=Object.getOwnPropertyDescriptor;var w=Object.getOwnPropertyNames;var E=Object.prototype.hasOwnProperty;var p=(i,t)=>d(i,"name",{value:t,configurable:!0});var h=(i,t)=>{for(var n in t)d(i,n,{get:t[n],enumerable:!0})},k=(i,t,n,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let e of w(t))!E.call(i,e)&&e!==n&&d(i,e,{get:()=>t[e],enumerable:!(o=A(t,e))||o.enumerable});return i};var x=i=>k(d({},"__esModule",{value:!0}),i);var C={};h(C,{run:()=>b});module.exports=x(C);var r=require("../../../hooks/core/actions"),m=require("../../../hooks/core/activity-end"),a=require("../../../lib/logger"),u=require("../../../lib/output"),f=require("../../../lib/stdin"),l=require("../../../analytics/codex/spawn"),c=require("../../../hooks/core/session-state"),y=require("../util"),g=require("../../../lib/runtime-paths");async function b(i){const t=(0,y.parseCodexHookStdin)((0,f.readStdin)()),n=t.session_id??"default",o=(0,g.sessionDir)(i,n),e=`${o}/actions.jsonl`;(0,a.setLogFile)(`${o}/session.log`);const S=(0,c.readState)(o)?.activeActivityId??"";if(await(0,m.runActivityEnd)({sessionDir:o,actionsFile:e,projectDir:i,sessionId:n})){const s=Date.now(),v={...(0,r.baseFields)(e),id:(0,r.deterministicSessionEndId)(n),type:"session_end",timestamp:s,session_id:n,duration:(0,r.findDurationSinceLastAction)(e,"session_start",s),reason:"checkpoint"};await(0,r.appendAction)(e,v)}try{const s=(0,c.readState)(o);(0,l.spawnDetachedCodexAnalyticsWorker)({projectDir:i,sessionId:n,rolloutPath:t.transcript_path,userEmail:s?.userEmail??void 0,usageType:s?.usageType??void 0,usagePlan:s?.usagePlan??void 0,activityId:S})}catch(s){a.logger.debug(`codex analytics spawn failed: ${s instanceof Error?s.message:s}`)}a.logger.debug(`activity-end: ${n}`),(0,u.writeAndExit)(JSON.stringify({}),0)}p(b,"run");0&&(module.exports={run});
@@ -1 +1 @@
1
- "use strict";var r=Object.defineProperty;var u=Object.getOwnPropertyDescriptor;var y=Object.getOwnPropertyNames;var _=Object.prototype.hasOwnProperty;var a=(e,t)=>r(e,"name",{value:t,configurable:!0});var l=(e,t)=>{for(var i in t)r(e,i,{get:t[i],enumerable:!0})},x=(e,t,i,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of y(t))!_.call(e,o)&&o!==i&&r(e,o,{get:()=>t[o],enumerable:!(s=u(t,o))||s.enumerable});return e};var $=e=>x(r({},"__esModule",{value:!0}),e);var k={};l(k,{isNonUserUps:()=>m,run:()=>b});module.exports=$(k);var p=require("../../../hooks/core/actions"),d=require("../../../hooks/core/activity"),c=require("../../../hooks/core/session-state"),n=require("../../../lib/logger"),g=require("../../../lib/stdin"),f=require("../util");function m(e){if(e.agent_id!==void 0&&e.agent_id!==""||e.agent_type!==void 0&&e.agent_type!=="")return!0;const t=(e.prompt??"").replace(/^\s+/,"");return t.startsWith("<hook_prompt")||t.startsWith("<subagent_notification")}a(m,"isNonUserUps");async function b(e){const t=(0,f.parseCodexHookStdin)((0,g.readStdin)()),i=t.session_id??"default",s=`${e}/.ironbee/sessions/${i}`,o=`${s}/actions.jsonl`;(0,n.setLogFile)(`${s}/session.log`),m(t)&&(n.logger.debug(`activity-start: skip non-user UPS (agent_id=${t.agent_id??"-"}, agent_type=${t.agent_type??"-"})`),process.exit(0)),await(0,c.reconcileAbandonedActivity)(s,o,p.appendAction),await(0,d.startActivity)({sessionDir:s,actionsFile:o,source:"user_prompt"}),n.logger.debug(`activity-start: ${i}`),process.exit(0)}a(b,"run");0&&(module.exports={isNonUserUps,run});
1
+ "use strict";var n=Object.defineProperty;var y=Object.getOwnPropertyDescriptor;var _=Object.getOwnPropertyNames;var l=Object.prototype.hasOwnProperty;var a=(e,t)=>n(e,"name",{value:t,configurable:!0});var x=(e,t)=>{for(var i in t)n(e,i,{get:t[i],enumerable:!0})},v=(e,t,i,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of _(t))!l.call(e,o)&&o!==i&&n(e,o,{get:()=>t[o],enumerable:!(s=y(t,o))||s.enumerable});return e};var S=e=>v(n({},"__esModule",{value:!0}),e);var k={};x(k,{isNonUserUps:()=>u,run:()=>b});module.exports=S(k);var p=require("../../../hooks/core/actions"),d=require("../../../hooks/core/activity"),c=require("../../../hooks/core/session-state"),r=require("../../../lib/logger"),g=require("../../../lib/stdin"),m=require("../util"),f=require("../../../lib/runtime-paths");function u(e){if(e.agent_id!==void 0&&e.agent_id!==""||e.agent_type!==void 0&&e.agent_type!=="")return!0;const t=(e.prompt??"").replace(/^\s+/,"");return t.startsWith("<hook_prompt")||t.startsWith("<subagent_notification")}a(u,"isNonUserUps");async function b(e){const t=(0,m.parseCodexHookStdin)((0,g.readStdin)()),i=t.session_id??"default",s=(0,f.sessionDir)(e,i),o=`${s}/actions.jsonl`;(0,r.setLogFile)(`${s}/session.log`),u(t)&&(r.logger.debug(`activity-start: skip non-user UPS (agent_id=${t.agent_id??"-"}, agent_type=${t.agent_type??"-"})`),process.exit(0)),await(0,c.reconcileAbandonedActivity)(s,o,p.appendAction),await(0,d.startActivity)({sessionDir:s,actionsFile:o,source:"user_prompt"}),r.logger.debug(`activity-start: ${i}`),process.exit(0)}a(b,"run");0&&(module.exports={isNonUserUps,run});
@@ -1,5 +1,5 @@
1
- "use strict";var C=Object.defineProperty;var D=Object.getOwnPropertyDescriptor;var T=Object.getOwnPropertyNames;var V=Object.prototype.hasOwnProperty;var l=(n,t)=>C(n,"name",{value:t,configurable:!0});var W=(n,t)=>{for(var e in t)C(n,e,{get:t[e],enumerable:!0})},H=(n,t,e,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of T(t))!V.call(n,i)&&i!==e&&C(n,i,{get:()=>t[i],enumerable:!(o=D(t,i))||o.enumerable});return n};var K=n=>H(C({},"__esModule",{value:!0}),n);var z={};W(z,{applyPatchStashKey:()=>j,parseApplyPatchBody:()=>B,run:()=>Y});module.exports=K(z);var A=require("fs"),v=require("path"),x=require("../../../hooks/core/actions"),P=require("../../../import/ids"),S=require("../../../hooks/core/clear-verdict"),I=require("../../../hooks/core/verification-lifecycle"),w=require("../../../hooks/core/file-diff"),M=require("../../../hooks/core/session-state"),k=require("../../../hooks/core/tool-use-stash"),f=require("../../../lib/config"),m=require("../../../lib/logger"),U=require("../../../lib/output"),L=require("../../../lib/stdin"),O=require("../util");const q=".apply-patch";function j(n){return`${n}${q}`}l(j,"applyPatchStashKey");function J(n,t){if(t<=0)return n;const e=Buffer.from(n,"utf-8");if(e.length<=t)return n;const i=`
1
+ "use strict";var C=Object.defineProperty;var T=Object.getOwnPropertyDescriptor;var V=Object.getOwnPropertyNames;var W=Object.prototype.hasOwnProperty;var l=(n,t)=>C(n,"name",{value:t,configurable:!0});var H=(n,t)=>{for(var e in t)C(n,e,{get:t[e],enumerable:!0})},K=(n,t,e,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of V(t))!W.call(n,i)&&i!==e&&C(n,i,{get:()=>t[i],enumerable:!(o=T(t,i))||o.enumerable});return n};var q=n=>K(C({},"__esModule",{value:!0}),n);var G={};H(G,{applyPatchStashKey:()=>j,parseApplyPatchBody:()=>B,run:()=>z});module.exports=q(G);var A=require("fs"),v=require("path"),x=require("../../../hooks/core/actions"),S=require("../../../import/ids"),P=require("../../../hooks/core/clear-verdict"),I=require("../../../hooks/core/verification-lifecycle"),w=require("../../../hooks/core/file-diff"),M=require("../../../hooks/core/session-state"),k=require("../../../hooks/core/tool-use-stash"),f=require("../../../lib/config"),m=require("../../../lib/logger"),D=require("../../../lib/output"),U=require("../../../lib/stdin"),L=require("../util"),O=require("../../../lib/runtime-paths");const J=".apply-patch";function j(n){return`${n}${J}`}l(j,"applyPatchStashKey");function N(n,t){if(t<=0)return n;const e=Buffer.from(n,"utf-8");if(e.length<=t)return n;const i=`
2
2
  ... (truncated, ${e.length-t} bytes omitted)
3
- `,d=Buffer.byteLength(i,"utf-8"),a=Math.max(0,t-d);return e.subarray(0,a).toString("utf-8")+i}l(J,"truncateChangeset");function N(n,t){return(0,v.isAbsolute)(t)?t:(0,v.join)(n,t)}l(N,"resolveFilePath");function X(n,t,e,o){const i=N(n,t.path);let d;try{d=t.operation==="delete"||!(0,A.existsSync)(i)?"":(0,A.readFileSync)(i,"utf-8")}catch(p){m.logger.debug(`failed to read post-state of ${i} for changeset: ${p}`);return}return(0,w.createUnifiedDiff)(e??"",d,o)??void 0}l(X,"buildUnifiedDiffChangeset");function B(n){const t=[];let e=null,o=[];const i=l(()=>{e&&(e.body=o.join(`
3
+ `,d=Buffer.byteLength(i,"utf-8"),a=Math.max(0,t-d);return e.subarray(0,a).toString("utf-8")+i}l(N,"truncateChangeset");function X(n,t){return(0,v.isAbsolute)(t)?t:(0,v.join)(n,t)}l(X,"resolveFilePath");function Y(n,t,e,o){const i=X(n,t.path);let d;try{d=t.operation==="delete"||!(0,A.existsSync)(i)?"":(0,A.readFileSync)(i,"utf-8")}catch(p){m.logger.debug(`failed to read post-state of ${i} for changeset: ${p}`);return}return(0,w.createUnifiedDiff)(e??"",d,o)??void 0}l(Y,"buildUnifiedDiffChangeset");function B(n){const t=[];let e=null,o=[];const i=l(()=>{e&&(e.body=o.join(`
4
4
  `),t.push(e),e=null,o=[])},"flushCurrent"),d=n.split(`
5
- `);for(const a of d){const r=a,p=r.match(/^\*\*\* Add File: (.+)$/),c=r.match(/^\*\*\* Update File: (.+)$/),u=r.match(/^\*\*\* Delete File: (.+)$/),y=r.match(/^\*\*\* (End Patch|Move to: .+)$/);if(p){i(),e={path:p[1].trim(),operation:"create",linesAdded:0,linesRemoved:0,body:""},o=[];continue}if(c){i(),e={path:c[1].trim(),operation:"update",linesAdded:0,linesRemoved:0,body:""},o=[];continue}if(u){i(),e={path:u[1].trim(),operation:"delete",linesAdded:0,linesRemoved:0,body:""},o=[];continue}if(y){i();continue}e&&(o.push(r),e.operation!=="delete"&&(r.startsWith("+")&&!r.startsWith("+++")?e.linesAdded+=1:r.startsWith("-")&&!r.startsWith("---")&&(e.linesRemoved+=1)))}return i(),t}l(B,"parseApplyPatchBody");async function Y(n){const t=(0,O.parseCodexHookStdin)((0,L.readStdin)()),e=t.session_id??"default",o=`${n}/.ironbee/sessions/${e}`,i=`${o}/actions.jsonl`,d=`${o}/verdict.json`;(0,m.setLogFile)(`${o}/session.log`);const a=t.tool_input;let r;if(typeof a=="string")r=a;else if(typeof a=="object"&&a!==null){const c=a;r=c.command??c.input}if(typeof r=="string"){const c=(0,f.loadConfig)(n),u=(0,f.getCaptureFileChangeset)(c),y=(0,f.getMaxChangesetBytes)(c),E=B(r),_=(0,M.getActiveActivityId)(o),b=t.tool_use_id??"",$=u&&b?(0,k.consumeToolUseData)(e,j(b)):null;for(let F=0;F<E.length;F++){const s=E[F];if(!(0,f.requiresVerification)(s.path,c))continue;const R=await(0,I.openFixCycleIfFixing)({sessionDir:o,actionsFile:i}),g={...(0,x.baseFields)(i),type:"file_change",timestamp:Date.now(),file_path:s.path,tool_name:"Edit",operation:s.operation,lines_added:s.operation==="delete"?null:s.linesAdded,lines_removed:s.operation==="delete"?null:s.operation==="create"?0:s.linesRemoved};if(_&&(g.activity_id=_),R&&(g.fix_id=R),b.length>0&&(g.id=(0,P.deriveFileChangeEventId)(e,`${b}:${F}:${s.path}`)),u){let h;$&&Object.prototype.hasOwnProperty.call($.prior_contents,s.path)&&(h=X(n,s,$.prior_contents[s.path],y)),h===void 0&&s.body.length>0&&(h=J(s.body,y)),h!==void 0&&(g.changeset=h)}await(0,x.appendAction)(i,g)}}(0,S.runClearVerdict)({verdictFile:d,sessionDir:o}),m.logger.debug(`clear-verdict: cleared for ${e}`),(0,U.writeAndExit)(JSON.stringify({}),0)}l(Y,"run");0&&(module.exports={applyPatchStashKey,parseApplyPatchBody,run});
5
+ `);for(const a of d){const r=a,p=r.match(/^\*\*\* Add File: (.+)$/),c=r.match(/^\*\*\* Update File: (.+)$/),u=r.match(/^\*\*\* Delete File: (.+)$/),y=r.match(/^\*\*\* (End Patch|Move to: .+)$/);if(p){i(),e={path:p[1].trim(),operation:"create",linesAdded:0,linesRemoved:0,body:""},o=[];continue}if(c){i(),e={path:c[1].trim(),operation:"update",linesAdded:0,linesRemoved:0,body:""},o=[];continue}if(u){i(),e={path:u[1].trim(),operation:"delete",linesAdded:0,linesRemoved:0,body:""},o=[];continue}if(y){i();continue}e&&(o.push(r),e.operation!=="delete"&&(r.startsWith("+")&&!r.startsWith("+++")?e.linesAdded+=1:r.startsWith("-")&&!r.startsWith("---")&&(e.linesRemoved+=1)))}return i(),t}l(B,"parseApplyPatchBody");async function z(n){const t=(0,L.parseCodexHookStdin)((0,U.readStdin)()),e=t.session_id??"default",o=(0,O.sessionDir)(n,e),i=`${o}/actions.jsonl`,d=`${o}/verdict.json`;(0,m.setLogFile)(`${o}/session.log`);const a=t.tool_input;let r;if(typeof a=="string")r=a;else if(typeof a=="object"&&a!==null){const c=a;r=c.command??c.input}if(typeof r=="string"){const c=(0,f.loadConfig)(n),u=(0,f.getCaptureFileChangeset)(c),y=(0,f.getMaxChangesetBytes)(c),_=B(r),$=(0,M.getActiveActivityId)(o),b=t.tool_use_id??"",E=u&&b?(0,k.consumeToolUseData)(e,j(b)):null;for(let F=0;F<_.length;F++){const s=_[F];if(!(0,f.requiresVerification)(s.path,c))continue;const R=await(0,I.openFixCycleIfFixing)({sessionDir:o,actionsFile:i}),g={...(0,x.baseFields)(i),type:"file_change",timestamp:Date.now(),file_path:s.path,tool_name:"Edit",operation:s.operation,lines_added:s.operation==="delete"?null:s.linesAdded,lines_removed:s.operation==="delete"?null:s.operation==="create"?0:s.linesRemoved};if($&&(g.activity_id=$),R&&(g.fix_id=R),b.length>0&&(g.id=(0,S.deriveFileChangeEventId)(e,`${b}:${F}:${s.path}`)),u){let h;E&&Object.prototype.hasOwnProperty.call(E.prior_contents,s.path)&&(h=Y(n,s,E.prior_contents[s.path],y)),h===void 0&&s.body.length>0&&(h=N(s.body,y)),h!==void 0&&(g.changeset=h)}await(0,x.appendAction)(i,g)}}(0,P.runClearVerdict)({verdictFile:d,sessionDir:o}),m.logger.debug(`clear-verdict: cleared for ${e}`),(0,D.writeAndExit)(JSON.stringify({}),0)}l(z,"run");0&&(module.exports={applyPatchStashKey,parseApplyPatchBody,run});
@@ -1,6 +1,6 @@
1
- "use strict";var d=Object.defineProperty;var v=Object.getOwnPropertyDescriptor;var x=Object.getOwnPropertyNames;var _=Object.prototype.hasOwnProperty;var a=(o,t)=>d(o,"name",{value:t,configurable:!0});var w=(o,t)=>{for(var n in t)d(o,n,{get:t[n],enumerable:!0})},P=(o,t,n,e)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of x(t))!_.call(o,s)&&s!==n&&d(o,s,{get:()=>t[s],enumerable:!(e=v(t,s))||e.enumerable});return o};var R=o=>P(d({},"__esModule",{value:!0}),o);var U={};w(U,{run:()=>A});module.exports=R(U);var f=require("fs"),g=require("path"),y=require("../../../hooks/core/actions"),b=require("../../../hooks/core/activity"),C=require("../../../hooks/core/tool-use-stash"),h=require("../../../lib/config"),l=require("../../../lib/logger"),S=require("../../../lib/stdin"),k=require("../util"),m=require("./clear-verdict");function T(o){const t=o.tool_input;if(typeof t=="string")return t;if(typeof t=="object"&&t!==null){const n=t,e=n.command??n.input;if(typeof e=="string")return e}return null}a(T,"readPatchBody");function F(o,t){return(0,g.isAbsolute)(t)?t:(0,g.join)(o,t)}a(F,"resolveFilePath");function $(o,t,n,e){const s=(0,m.parseApplyPatchBody)(e),i={};for(const r of s){if(r.operation==="create"){i[r.path]=null;continue}const c=F(o,r.path);try{i[r.path]=(0,f.existsSync)(c)?(0,f.readFileSync)(c,"utf-8"):null}catch(u){l.logger.debug(`failed to read prior content of ${c} for changeset stash: ${u}`),i[r.path]=null}}const p={prior_contents:i};(0,C.stashToolUseData)(t,(0,m.applyPatchStashKey)(n),p)}a($,"stashPriorContents");async function A(o,t){const n=t?.soft===!0,e=(0,k.parseCodexHookStdin)((0,S.readStdin)()),s=e.session_id??"default",i=`${o}/.ironbee/sessions/${s}`,p=`${i}/actions.jsonl`;if((0,l.setLogFile)(`${i}/session.log`),!n&&(0,y.hasToolCallsSinceLastVerdict)(p)){const c=`BLOCKED: You used devtools tools but haven't submitted a verdict yet.
1
+ "use strict";var d=Object.defineProperty;var x=Object.getOwnPropertyDescriptor;var _=Object.getOwnPropertyNames;var w=Object.prototype.hasOwnProperty;var a=(o,t)=>d(o,"name",{value:t,configurable:!0});var P=(o,t)=>{for(var e in t)d(o,e,{get:t[e],enumerable:!0})},R=(o,t,e,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of _(t))!w.call(o,s)&&s!==e&&d(o,s,{get:()=>t[s],enumerable:!(n=x(t,s))||n.enumerable});return o};var T=o=>R(d({},"__esModule",{value:!0}),o);var $={};P($,{run:()=>U});module.exports=T($);var f=require("fs"),g=require("path"),y=require("../../../hooks/core/actions"),S=require("../../../hooks/core/activity"),b=require("../../../hooks/core/tool-use-stash"),m=require("../../../lib/config"),l=require("../../../lib/logger"),C=require("../../../lib/stdin"),k=require("../util"),h=require("./clear-verdict"),v=require("../../../lib/runtime-paths");function F(o){const t=o.tool_input;if(typeof t=="string")return t;if(typeof t=="object"&&t!==null){const e=t,n=e.command??e.input;if(typeof n=="string")return n}return null}a(F,"readPatchBody");function A(o,t){return(0,g.isAbsolute)(t)?t:(0,g.join)(o,t)}a(A,"resolveFilePath");function D(o,t,e,n){const s=(0,h.parseApplyPatchBody)(n),i={};for(const r of s){if(r.operation==="create"){i[r.path]=null;continue}const c=A(o,r.path);try{i[r.path]=(0,f.existsSync)(c)?(0,f.readFileSync)(c,"utf-8"):null}catch(u){l.logger.debug(`failed to read prior content of ${c} for changeset stash: ${u}`),i[r.path]=null}}const p={prior_contents:i};(0,b.stashToolUseData)(t,(0,h.applyPatchStashKey)(e),p)}a(D,"stashPriorContents");async function U(o,t){const e=t?.soft===!0,n=(0,k.parseCodexHookStdin)((0,C.readStdin)()),s=n.session_id??"default",i=(0,v.sessionDir)(o,s),p=`${i}/actions.jsonl`;if((0,l.setLogFile)(`${i}/session.log`),!e&&(0,y.hasToolCallsSinceLastVerdict)(p)){const c=`BLOCKED: You used devtools tools but haven't submitted a verdict yet.
2
2
 
3
3
  You MUST submit a verdict before editing more code. Use:
4
4
  echo '{"session_id":"${s}","status":"fail","checks":["..."],"issues":["..."]}' | ironbee hook submit-verdict
5
5
 
6
- Then proceed with the edit.`;process.stdout.write(JSON.stringify({hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:c}})),process.exit(0);return}const r=e.tool_use_id??"";if(r){const c=(0,h.loadConfig)(o);if((0,h.getCaptureFileChangeset)(c)){const u=T(e);u!==null&&$(o,s,r,u)}}await(0,b.startActivity)({sessionDir:i,actionsFile:p,source:"pre_tool_use"}),l.logger.debug("require-verdict: allowed apply_patch"),process.exit(0)}a(A,"run");0&&(module.exports={run});
6
+ Then proceed with the edit.`;process.stdout.write(JSON.stringify({hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:c}})),process.exit(0);return}const r=n.tool_use_id??"";if(r){const c=(0,m.loadConfig)(o);if((0,m.getCaptureFileChangeset)(c)){const u=F(n);u!==null&&D(o,s,r,u)}}await(0,S.startActivity)({sessionDir:i,actionsFile:p,source:"pre_tool_use"}),l.logger.debug("require-verdict: allowed apply_patch"),process.exit(0)}a(U,"run");0&&(module.exports={run});
@@ -1,9 +1,9 @@
1
- "use strict";var u=Object.defineProperty;var K=Object.getOwnPropertyDescriptor;var q=Object.getOwnPropertyNames;var B=Object.prototype.hasOwnProperty;var C=(o,e)=>u(o,"name",{value:e,configurable:!0});var J=(o,e)=>{for(var s in e)u(o,s,{get:e[s],enumerable:!0})},L=(o,e,s,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let t of q(e))!B.call(o,t)&&t!==s&&u(o,t,{get:()=>e[t],enumerable:!(n=K(e,t))||n.enumerable});return o};var H=o=>L(u({},"__esModule",{value:!0}),o);var M={};J(M,{run:()=>z});module.exports=H(M);var $=require("crypto"),x=require("../../../hooks/core/activity"),i=require("../../../hooks/core/session-state"),N=require("../../../hooks/core/actions"),E=require("../../../hooks/core/verification-lifecycle"),O=require("../../../hooks/core/verification-context"),U=require("../../../lib/config"),m=require("../../../lib/logger"),A=require("../../../lib/recording-tools"),D=require("../../../hooks/core/scenario-tools"),V=require("../../../lib/stdin"),f=require("../util");async function z(o,e){const s=e?.soft===!0,n=(0,f.parseCodexHookStdin)((0,V.readStdin)()),t=n.session_id??"default",r=`${o}/.ironbee/sessions/${t}`,y=`${r}/actions.jsonl`;(0,m.setLogFile)(`${r}/session.log`);const g=(0,D.isScenarioTool)(n.tool_name),h=(0,i.getActiveVerificationId)(r);if(!h&&!s&&!g){const p=`BLOCKED: You must start a verification cycle before using devtools tools.
1
+ "use strict";var u=Object.defineProperty;var q=Object.getOwnPropertyDescriptor;var B=Object.getOwnPropertyNames;var J=Object.prototype.hasOwnProperty;var C=(o,e)=>u(o,"name",{value:e,configurable:!0});var L=(o,e)=>{for(var s in e)u(o,s,{get:e[s],enumerable:!0})},H=(o,e,s,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let t of B(e))!J.call(o,t)&&t!==s&&u(o,t,{get:()=>e[t],enumerable:!(r=q(e,t))||r.enumerable});return o};var z=o=>H(u({},"__esModule",{value:!0}),o);var W={};L(W,{run:()=>M});module.exports=z(W);var x=require("crypto"),N=require("../../../hooks/core/activity"),$=require("../../../lib/runtime-paths"),i=require("../../../hooks/core/session-state"),E=require("../../../hooks/core/actions"),O=require("../../../hooks/core/verification-lifecycle"),U=require("../../../hooks/core/verification-context"),D=require("../../../lib/config"),m=require("../../../lib/logger"),A=require("../../../lib/recording-tools"),V=require("../../../hooks/core/scenario-tools"),P=require("../../../lib/stdin"),f=require("../util");async function M(o,e){const s=e?.soft===!0,r=(0,f.parseCodexHookStdin)((0,P.readStdin)()),t=r.session_id??"default",n=(0,$.sessionDir)(o,t),b=`${n}/actions.jsonl`;(0,m.setLogFile)(`${n}/session.log`);const g=(0,V.isScenarioTool)(r.tool_name),h=(0,i.getActiveVerificationId)(n);if(!h&&!s&&!g){const p=`BLOCKED: You must start a verification cycle before using devtools tools.
2
2
 
3
3
  Start verification first:
4
4
  echo '{"session_id":"${t}"}' | ironbee hook verification-start
5
5
 
6
- Then use the verification tools for the active cycle(s) \u2014 mcp__browser-devtools__bdt_* for browser, mcp__node-devtools__ndt_* for node, mcp__backend-devtools__bedt_* for backend, mcp__android-devtools__adt_* for android.`;process.stdout.write(JSON.stringify({hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:p}})),process.exit(0);return}const _=n.tool_name??"",S=(0,f.extractCodexMcpServer)(_),c=(0,A.recordingToolsForServer)(S),P=c!==null?(0,f.canonicalizeCodexToolName)(_.split("__").pop()??""):"";if(!s&&!g&&c!==null&&(0,i.isRecordingRequired)(r)&&!(0,i.isRecordingActive)(r)&&P!==c.startTool){const p=`BLOCKED: Recording is required but not started.
6
+ Then use the verification tools for the active cycle(s) \u2014 mcp__browser-devtools__bdt_* for browser, mcp__node-devtools__ndt_* for node, mcp__backend-devtools__bedt_* for backend, mcp__android-devtools__adt_* for android, mcp__terminal-devtools__tdt_* for terminal.`;process.stdout.write(JSON.stringify({hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:p}})),process.exit(0);return}const _=r.tool_name??"",S=(0,f.extractCodexMcpServer)(_),c=(0,A.recordingToolsForServer)(S),j=c!==null?(0,f.canonicalizeCodexToolName)(_.split("__").pop()??""):"";if(!s&&!g&&c!==null&&(0,i.isRecordingRequired)(n)&&!(0,i.isRecordingActive)(n)&&j!==c.startTool){const p=`BLOCKED: Recording is required but not started.
7
7
 
8
8
  1. Start recording NOW:
9
9
  Use mcp__${c.server}__${c.startTool}
@@ -12,4 +12,4 @@ Then use the verification tools for the active cycle(s) \u2014 mcp__browser-devt
12
12
 
13
13
  3. **Stop recording BEFORE submitting verdict:**
14
14
  Use mcp__${c.server}__${c.stopTool}
15
- submit-verdict will reject with "recording is still active" if you skip this.`;process.stdout.write(JSON.stringify({hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:p}})),process.exit(0);return}await(0,x.startActivity)({sessionDir:r,actionsFile:y,source:"pre_tool_use"});let d=h;s&&!d&&!g&&(d=(await(0,E.startVerification)({sessionId:t,sessionDir:r,actionsFile:y,recordingEnabled:!1})).verificationId);const j=(0,i.getActiveTraceId)(r),v=(0,i.getActiveActivityId)(r),k=(0,N.resolveProjectName)(o),b=[`prj:${k}`,`sid:${t}`];v&&b.push(`aid:${v}`),d&&b.push(`vid:${d}`);const F=`ironbee=${b.join(";")}`,a=(0,U.loadConfig)(o),T={...n.tool_input&&typeof n.tool_input=="object"?n.tool_input:{}},l={projectName:k,sessionId:t,activityId:v,verificationId:d,traceId:j,traceState:F,toolCallId:(0,$.randomUUID)()};n.tool_use_id&&(l.toolUseId=n.tool_use_id),l.mcpServer=S??"browser-devtools";const w=(0,i.getUserEmail)(r);w&&(l.userEmail=w),a.collector?.url&&(l.collectorUrl=a.collector.url),a.collector?.oauthToken?l.collectorOAuthToken=a.collector.oauthToken:a.collector?.apiKey&&(l.collectorApiKey=a.collector.apiKey),T._metadata=l;const I={hookEventName:"PreToolUse",permissionDecision:"allow",updatedInput:T},R=(0,O.buildVerificationContextOnceForCycle)({projectDir:o,sessionId:t,sessionDir:r,activeVerificationId:d,config:a});R.length>0&&(I.additionalContext=R),process.stdout.write(JSON.stringify({hookSpecificOutput:I})),m.logger.debug(`require-verification: allowed ${_} with _metadata`),process.exit(0)}C(z,"run");0&&(module.exports={run});
15
+ submit-verdict will reject with "recording is still active" if you skip this.`;process.stdout.write(JSON.stringify({hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:p}})),process.exit(0);return}await(0,N.startActivity)({sessionDir:n,actionsFile:b,source:"pre_tool_use"});let d=h;s&&!d&&!g&&(d=(await(0,O.startVerification)({sessionId:t,sessionDir:n,actionsFile:b,recordingEnabled:!1})).verificationId);const F=(0,i.getActiveTraceId)(n),v=(0,i.getActiveActivityId)(n),k=(0,E.resolveProjectName)(o),y=[`prj:${k}`,`sid:${t}`];v&&y.push(`aid:${v}`),d&&y.push(`vid:${d}`);const K=`ironbee=${y.join(";")}`,a=(0,D.loadConfig)(o),T={...r.tool_input&&typeof r.tool_input=="object"?r.tool_input:{}},l={projectName:k,sessionId:t,activityId:v,verificationId:d,traceId:F,traceState:K,toolCallId:(0,x.randomUUID)()};r.tool_use_id&&(l.toolUseId=r.tool_use_id),l.mcpServer=S??"browser-devtools";const w=(0,i.getUserEmail)(n);w&&(l.userEmail=w),a.collector?.url&&(l.collectorUrl=a.collector.url),a.collector?.oauthToken?l.collectorOAuthToken=a.collector.oauthToken:a.collector?.apiKey&&(l.collectorApiKey=a.collector.apiKey),T._metadata=l;const I={hookEventName:"PreToolUse",permissionDecision:"allow",updatedInput:T},R=(0,U.buildVerificationContextOnceForCycle)({projectDir:o,sessionId:t,sessionDir:n,activeVerificationId:d,config:a});R.length>0&&(I.additionalContext=R),process.stdout.write(JSON.stringify({hookSpecificOutput:I})),m.logger.debug(`require-verification: allowed ${_} with _metadata`),process.exit(0)}C(M,"run");0&&(module.exports={run});
@@ -1,4 +1,4 @@
1
- "use strict";var c=Object.defineProperty;var x=Object.getOwnPropertyDescriptor;var k=Object.getOwnPropertyNames;var O=Object.prototype.hasOwnProperty;var g=(i,s)=>c(i,"name",{value:s,configurable:!0});var I=(i,s)=>{for(var e in s)c(i,e,{get:s[e],enumerable:!0})},w=(i,s,e,o)=>{if(s&&typeof s=="object"||typeof s=="function")for(let t of k(s))!O.call(i,t)&&t!==e&&c(i,t,{get:()=>s[t],enumerable:!(o=x(s,t))||o.enumerable});return i};var C=i=>w(c({},"__esModule",{value:!0}),i);var T={};I(T,{run:()=>N});module.exports=C(T);var r=require("../../../hooks/core/actions"),p=require("../../../import/ids"),n=require("../../../hooks/core/session-state"),d=require("../../../lib/config"),u=require("../../../lib/logger"),l=require("../../../lib/output"),S=require("../../../lib/stdin"),b=require("../../../lib/telemetry"),f=require("../util");async function N(i){const s=(0,f.parseCodexHookStdin)((0,S.readStdin)()),e=s.session_id??"default",o=`${i}/.ironbee/sessions/${e}`,t=`${o}/actions.jsonl`;(0,u.setLogFile)(`${o}/session.log`);const a=(0,f.resolveCodexUsage)();a.userEmail&&(0,n.setUserEmail)(o,a.userEmail),(0,n.setUsage)(o,{usage_type:a.usageType??null,usage_plan:a.usagePlan??null});const y={...(0,r.baseFields)(t),id:(0,p.deriveSessionStartEventId)(e),type:"session_start",timestamp:Date.now(),session_id:e,client:"codex",source:s.source??"startup"};await(0,r.appendAction)(t,y),await(0,n.reconcileSessionState)(o,t,r.appendAction);const m=(0,d.getVerificationEnabled)((0,d.loadConfig)(i));if(await(0,b.trackSessionStart)("codex",e,m,i),u.logger.debug(`session-start: ${e}`),!m){(0,l.writeAndExit)(JSON.stringify({}),0);return}const h=JSON.stringify({session_id:e,status:"pass",checks:["form submits successfully","new item appears in list"]}),v=JSON.stringify({session_id:e,status:"fail",checks:["form renders","submit button unresponsive"],issues:["button click handler not firing","TypeError in console"]}),E=`IRONBEE VERIFICATION \u2014 SESSION ACTIVE
1
+ "use strict";var c=Object.defineProperty;var k=Object.getOwnPropertyDescriptor;var O=Object.getOwnPropertyNames;var I=Object.prototype.hasOwnProperty;var g=(s,i)=>c(s,"name",{value:i,configurable:!0});var w=(s,i)=>{for(var e in i)c(s,e,{get:i[e],enumerable:!0})},C=(s,i,e,t)=>{if(i&&typeof i=="object"||typeof i=="function")for(let o of O(i))!I.call(s,o)&&o!==e&&c(s,o,{get:()=>i[o],enumerable:!(t=k(i,o))||t.enumerable});return s};var N=s=>C(c({},"__esModule",{value:!0}),s);var _={};w(_,{run:()=>T});module.exports=N(_);var r=require("../../../hooks/core/actions"),p=require("../../../import/ids"),n=require("../../../hooks/core/session-state"),d=require("../../../lib/config"),u=require("../../../lib/logger"),l=require("../../../lib/output"),S=require("../../../lib/stdin"),b=require("../../../lib/telemetry"),f=require("../util"),v=require("../../../lib/runtime-paths");async function T(s){const i=(0,f.parseCodexHookStdin)((0,S.readStdin)()),e=i.session_id??"default",t=(0,v.sessionDir)(s,e),o=`${t}/actions.jsonl`;(0,u.setLogFile)(`${t}/session.log`),(0,n.setProjectDir)(t,s);const a=(0,f.resolveCodexUsage)();a.userEmail&&(0,n.setUserEmail)(t,a.userEmail),(0,n.setUsage)(t,{usage_type:a.usageType??null,usage_plan:a.usagePlan??null});const y={...(0,r.baseFields)(o),id:(0,p.deriveSessionStartEventId)(e),type:"session_start",timestamp:Date.now(),session_id:e,client:"codex",source:i.source??"startup"};await(0,r.appendAction)(o,y),await(0,n.reconcileSessionState)(t,o,r.appendAction);const m=(0,d.getVerificationEnabled)((0,d.loadConfig)(s));if(await(0,b.trackSessionStart)("codex",e,m,s),u.logger.debug(`session-start: ${e}`),!m){(0,l.writeAndExit)(JSON.stringify({}),0);return}const h=JSON.stringify({session_id:e,status:"pass",checks:["form submits successfully","new item appears in list"]}),E=JSON.stringify({session_id:e,status:"fail",checks:["form renders","submit button unresponsive"],issues:["button click handler not firing","TypeError in console"]}),x=`IRONBEE VERIFICATION \u2014 SESSION ACTIVE
2
2
  Session ID: ${e}
3
3
 
4
4
  You MUST verify all code changes in the browser before completing any task.
@@ -10,8 +10,8 @@ Submit via shell:
10
10
  echo '${h}' | ironbee hook submit-verdict
11
11
 
12
12
  On fail (issues is required):
13
- echo '${v}' | ironbee hook submit-verdict
13
+ echo '${E}' | ironbee hook submit-verdict
14
14
 
15
15
  Required fields: session_id, status, checks
16
16
  On fail, include: issues (array of strings describing what failed)
17
- On pass after a previous fail, include: fixes (array of strings describing what was fixed)`;(0,l.writeAndExit)(JSON.stringify({hookSpecificOutput:{hookEventName:"SessionStart",additionalContext:E}}),0)}g(N,"run");0&&(module.exports={run});
17
+ On pass after a previous fail, include: fixes (array of strings describing what was fixed)`;(0,l.writeAndExit)(JSON.stringify({hookSpecificOutput:{hookEventName:"SessionStart",additionalContext:x}}),0)}g(T,"run");0&&(module.exports={run});
@@ -1 +1 @@
1
- "use strict";var n=Object.defineProperty;var m=Object.getOwnPropertyDescriptor;var c=Object.getOwnPropertyNames;var f=Object.prototype.hasOwnProperty;var d=(e,o)=>n(e,"name",{value:o,configurable:!0});var u=(e,o)=>{for(var s in o)n(e,s,{get:o[s],enumerable:!0})},x=(e,o,s,t)=>{if(o&&typeof o=="object"||typeof o=="function")for(let i of c(o))!f.call(e,i)&&i!==s&&n(e,i,{get:()=>o[i],enumerable:!(t=m(o,i))||t.enumerable});return e};var $=e=>x(n({},"__esModule",{value:!0}),e);var _={};u(_,{run:()=>S});module.exports=$(_);var r=require("../../../lib/logger"),g=require("../../../lib/stdin"),p=require("../util"),a=require("../thread-map");async function S(e){const o=(0,p.parseCodexHookStdin)((0,g.readStdin)()),s=o.session_id??"",t=o.agent_id??"";if(!s||!t){process.exit(0);return}(0,r.setLogFile)(`${e}/.ironbee/sessions/${s}/session.log`),(0,a.writeThreadMapping)(e,t,s),r.logger.debug(`subagent-start: mapped thread ${t} \u2192 session ${s} (agent_type=${o.agent_type??"?"})`),process.exit(0)}d(S,"run");0&&(module.exports={run});
1
+ "use strict";var n=Object.defineProperty;var f=Object.getOwnPropertyDescriptor;var c=Object.getOwnPropertyNames;var u=Object.prototype.hasOwnProperty;var d=(t,o)=>n(t,"name",{value:o,configurable:!0});var x=(t,o)=>{for(var e in o)n(t,e,{get:o[e],enumerable:!0})},S=(t,o,e,i)=>{if(o&&typeof o=="object"||typeof o=="function")for(let s of c(o))!u.call(t,s)&&s!==e&&n(t,s,{get:()=>o[s],enumerable:!(i=f(o,s))||i.enumerable});return t};var _=t=>S(n({},"__esModule",{value:!0}),t);var l={};x(l,{run:()=>k});module.exports=_(l);var r=require("../../../lib/logger"),p=require("../../../lib/stdin"),g=require("../util"),a=require("../thread-map"),m=require("../../../lib/runtime-paths");async function k(t){const o=(0,g.parseCodexHookStdin)((0,p.readStdin)()),e=o.session_id??"",i=o.agent_id??"";if(!e||!i){process.exit(0);return}(0,r.setLogFile)((0,m.sessionLogFile)(t,e)),(0,a.writeThreadMapping)(t,i,e),r.logger.debug(`subagent-start: mapped thread ${i} \u2192 session ${e} (agent_type=${o.agent_type??"?"})`),process.exit(0)}d(k,"run");0&&(module.exports={run});