@ironbee-ai/cli 0.21.2 → 0.22.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 (56) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/clients/claude/agents/ironbee-verifier.md +12 -2
  3. package/dist/clients/claude/commands/ironbee-verify.md +28 -15
  4. package/dist/clients/claude/hooks/activity-start.js +1 -1
  5. package/dist/clients/claude/hooks/require-verdict.js +2 -2
  6. package/dist/clients/claude/hooks/require-verification.js +7 -7
  7. package/dist/clients/claude/hooks/track-action.js +1 -1
  8. package/dist/clients/claude/index.js +4 -4
  9. package/dist/clients/claude/platforms/skill.android.md +65 -0
  10. package/dist/clients/codex/agents/ironbee-verifier.md +11 -1
  11. package/dist/clients/codex/commands/ironbee-verify/SKILL.md +24 -5
  12. package/dist/clients/codex/hooks/require-verification.js +7 -7
  13. package/dist/clients/codex/hooks/track-action.js +1 -1
  14. package/dist/clients/codex/index.js +2 -2
  15. package/dist/clients/codex/platforms/command-verify.android.md +61 -0
  16. package/dist/clients/codex/platforms/rule.android.md +32 -0
  17. package/dist/clients/codex/platforms/skill.android.md +55 -0
  18. package/dist/clients/codex/skills/ironbee-verification.md +3 -0
  19. package/dist/clients/codex/util.js +11 -11
  20. package/dist/clients/cursor/commands/ironbee-verify/SKILL.md +21 -4
  21. package/dist/clients/cursor/hooks/require-verdict.js +1 -1
  22. package/dist/clients/cursor/hooks/require-verification.js +6 -6
  23. package/dist/clients/cursor/hooks/track-action.js +1 -1
  24. package/dist/clients/cursor/index.js +1 -1
  25. package/dist/clients/cursor/platforms/command-verify.android.md +61 -0
  26. package/dist/clients/cursor/platforms/rule.android.md +32 -0
  27. package/dist/clients/cursor/platforms/skill.android.md +55 -0
  28. package/dist/clients/cursor/rules/ironbee-verification.mdc +3 -0
  29. package/dist/clients/cursor/skills/ironbee-verification.md +3 -0
  30. package/dist/commands/android.js +1 -0
  31. package/dist/commands/config.js +2 -2
  32. package/dist/commands/cycle-toggle.js +4 -4
  33. package/dist/commands/hook.js +16 -15
  34. package/dist/commands/install.js +1 -1
  35. package/dist/commands/mode-select.js +2 -0
  36. package/dist/hooks/core/actions.js +6 -5
  37. package/dist/hooks/core/session-state.js +1 -1
  38. package/dist/hooks/core/submit-verdict.js +4 -4
  39. package/dist/hooks/core/verification-lifecycle.js +1 -1
  40. package/dist/hooks/core/verify-gate.js +29 -23
  41. package/dist/import/claude/events/tool-call.js +1 -1
  42. package/dist/import/codex/events/tool-call.js +1 -1
  43. package/dist/index.js +1 -1
  44. package/dist/lib/config.js +1 -1
  45. package/dist/lib/install-version.js +1 -0
  46. package/dist/lib/platform-section.js +4 -3
  47. package/dist/lib/prompt.js +4 -4
  48. package/dist/lib/recording-tools.js +1 -0
  49. package/dist/lib/schema-sync.js +2 -0
  50. package/dist/lib/version.js +1 -1
  51. package/dist/scripts/postinstall.js +1 -1
  52. package/dist/tui/config/schema.js +1 -1
  53. package/dist/tui/platforms/area.js +2 -2
  54. package/dist/tui/projects/area.js +4 -4
  55. package/dist/tui/sessions/area.js +3 -3
  56. package/package.json +1 -1
@@ -1 +1 @@
1
- "use strict";var C=Object.defineProperty;var G=Object.getOwnPropertyDescriptor;var W=Object.getOwnPropertyNames;var x=Object.prototype.hasOwnProperty;var k=(l,o)=>C(l,"name",{value:o,configurable:!0});var z=(l,o)=>{for(var e in o)C(l,e,{get:o[e],enumerable:!0})},Q=(l,o,e,s)=>{if(o&&typeof o=="object"||typeof o=="function")for(let t of W(o))!x.call(l,t)&&t!==e&&C(l,t,{get:()=>o[t],enumerable:!(s=G(o,t))||s.enumerable});return l};var X=l=>Q(C({},"__esModule",{value:!0}),l);var so={};z(so,{CursorClient:()=>ro});module.exports=X(so);var i=require("fs"),c=require("path"),p=require("../../lib/logger"),n=require("../../lib/output"),O=require("../../lib/fs-prune"),T=require("./hooks/verify-gate"),_=require("./hooks/clear-verdict"),P=require("./hooks/track-action"),I=require("./hooks/track-action-monitor"),N=require("./hooks/session-start"),B=require("./hooks/require-verdict"),J=require("./hooks/require-verification"),V=require("./hooks/activity-start"),U=require("./hooks/activity-end"),L=require("./hooks/session-end"),a=require("../../lib/config"),F=require("../../lib/platform-section"),D=require("../../lib/gitignore");const v="browser-devtools",y="node-devtools",b="backend-devtools",Y="ironbee";function Z(l){return(0,c.join)(__dirname,"..",l,"platforms")}k(Z,"platformsDirFor");function R(l){const o=Object.keys(l);if(o.length===0)return!0;if(o.length===1&&o[0]==="mcpServers"){const e=l.mcpServers;return e===void 0||Object.keys(e).length===0}return!1}k(R,"isMcpConfigEmpty");const S="ironbee",H=[`${v}:*`,`${y}:*`,`${b}:*`];function oo(l){const o=new Set(["mcpAllowlist","terminalAllowlist"]);for(const t of Object.keys(l))if(!o.has(t))return!1;const e=l.mcpAllowlist??[],s=l.terminalAllowlist??[];return e.length===0&&s.length===0}k(oo,"isPermissionsEmpty");function eo(l){const o=new Set(["version","hooks"]);for(const e of Object.keys(l))if(!o.has(e))return!1;return Object.keys(l.hooks??{}).length===0}k(eo,"isCursorHooksEmpty");function d(l){return n.pc.dim(l)}k(d,"cursorColor");class ro{constructor(){this.name="cursor"}static{k(this,"CursorClient")}detect(o){return(0,i.existsSync)((0,c.join)(o,".cursor"))}resolveProjectDir(){return process.env.CURSOR_PROJECT_DIR??process.env.IRONBEE_PROJECT_DIR??process.cwd()}install(o,e){const s=e??(0,a.loadConfig)(o),t=(0,a.getVerificationMode)(s),r=t!=="monitor";this.cleanupArtifacts(o),(0,D.ensureIronBeeGitignored)(o);const u=(0,c.join)(o,".cursor"),g=(0,c.join)(u,"rules"),f=(0,c.join)(u,"skills");(0,i.mkdirSync)(g,{recursive:!0}),(0,i.mkdirSync)(f,{recursive:!0});const m=(0,c.join)(u,"hooks.json");if(this.mergeHooksConfig(m,t),r){if(t==="enforce"){const A=(0,c.join)(f,"ironbee-verification.md"),j=(0,i.readFileSync)((0,c.join)(__dirname,"skills","ironbee-verification.md"),"utf-8");(0,i.writeFileSync)(A,j);const M=(0,c.join)(g,"ironbee-verification.mdc"),q=(0,i.readFileSync)((0,c.join)(__dirname,"rules","ironbee-verification.mdc"),"utf-8");(0,i.writeFileSync)(M,q),console.log(` ${n.pc.dim("\u2192")} ${d("[cursor]")} skill ${n.pc.dim("\u2192")} ${n.pc.dim(A)}`),console.log(` ${n.pc.dim("\u2192")} ${d("[cursor]")} rule ${n.pc.dim("\u2192")} ${n.pc.dim(M)}`)}const h=(0,c.join)(f,"ironbee-verify");(0,i.mkdirSync)(h,{recursive:!0});const w=(0,c.join)(h,"SKILL.md"),K=(0,i.readFileSync)((0,c.join)(__dirname,"commands","ironbee-verify","SKILL.md"),"utf-8");(0,i.writeFileSync)(w,K);const $=(0,c.join)(u,"mcp.json");this.writeMcpConfig($,o);const E=(0,c.join)(u,"permissions.json");this.writePermissionsConfig(E,o),(0,F.syncPlatformSectionsToConfig)(o,Z),console.log(` ${n.pc.dim("\u2192")} ${d("[cursor]")} hooks ${n.pc.dim("\u2192")} ${n.pc.dim(m)}`),t==="assist"&&console.log(` ${n.pc.dim("\u2192")} ${d("[cursor]")} ${n.pc.yellow("assist mode")} (verification.auto: false) \u2014 manual /ironbee-verify only, no enforcement`),console.log(` ${n.pc.dim("\u2192")} ${d("[cursor]")} command ${n.pc.dim("\u2192")} ${n.pc.dim(w)}`),console.log(` ${n.pc.dim("\u2192")} ${d("[cursor]")} mcp ${n.pc.dim("\u2192")} ${n.pc.dim($)}`),console.log(` ${n.pc.dim("\u2192")} ${d("[cursor]")} perms ${n.pc.dim("\u2192")} ${n.pc.dim(E)}`),console.log(),console.log(` ${n.pc.yellow("\u26A0")} ${n.pc.yellow("Cursor requires manual steps:")}`),console.log(` ${n.pc.yellow("1.")} Restart Cursor to load the new hooks and MCP config`),console.log(` ${n.pc.yellow("2.")} Go to ${n.pc.bold("Settings \u2192 Tools & MCP")} and verify ${n.pc.bold("browser-devtools")} is enabled`),console.log(` ${n.pc.yellow("3.")} If the server shows as enabled but tools are unavailable, toggle it off and on`)}else console.log(` ${n.pc.dim("\u2192")} ${d("[cursor]")} ${n.pc.yellow("monitoring-only mode")} (verification.enable: false)`),console.log(` ${n.pc.dim("\u2192")} ${d("[cursor]")} hooks ${n.pc.dim("\u2192")} ${n.pc.dim(m)}`),console.log(),console.log(` ${n.pc.yellow("\u26A0")} Restart Cursor to load the new hook config`)}uninstall(o){this.cleanupArtifacts(o),console.log(` ${n.pc.dim("\u2192")} ${d("[cursor]")} removed hooks, skill, rule, command, and MCP`),console.log(),console.log(` ${n.pc.yellow("\u26A0")} Restart Cursor to apply changes`)}cleanupArtifacts(o){const e=(0,c.join)(o,".cursor"),s=(0,c.join)(e,"skills","ironbee-verification.md"),t=(0,c.join)(e,"rules","ironbee-verification.mdc"),r=(0,c.join)(e,"skills","ironbee-analyze","SKILL.md"),u=(0,c.join)(e,"skills","ironbee-verify","SKILL.md");this.removeFile(s),this.removeFile(t),this.removeFile(r),this.removeFile(u);const g=(0,c.join)(e,"hooks.json");this.removeIronBeeHooks(g),this.maybeDeleteEmptyHooks(g);const f=(0,c.join)(e,"mcp.json");this.removeMcpServer(f);const m=(0,c.join)(e,"permissions.json");this.removePermissionsEntries(m),(0,O.pruneEmptyDirs)(e)}maybeDeleteEmptyHooks(o){if((0,i.existsSync)(o))try{const e=JSON.parse((0,i.readFileSync)(o,"utf-8"));eo(e)&&(0,i.unlinkSync)(o)}catch(e){p.logger.debug(`failed to inspect ${o} for emptiness: ${e}`)}}async runVerifyGate(o){await(0,T.run)(o)}async runClearVerdict(o){await(0,_.run)(o)}async runTrackAction(o){await(0,P.run)(o)}async runSessionStart(o){await(0,N.run)(o)}async runRequireVerdict(o,e){await(0,B.run)(o,e)}async runRequireVerification(o,e){await(0,J.run)(o,e)}async runActivityStart(o){await(0,V.run)(o)}async runActivityEnd(o){await(0,U.run)(o)}async runTrackActionMonitor(o){await(0,I.run)(o)}async runSessionEnd(o){await(0,L.run)(o)}async runTrackActionPre(o){}isIronBeeHook(o){return o.command.includes(Y)}mergeHooksConfig(o,e){const s=e!=="monitor",t=e==="assist"?" --soft":"";let r={version:1,hooks:{}};if((0,i.existsSync)(o))try{r=JSON.parse((0,i.readFileSync)(o,"utf-8")),r.hooks||(r.hooks={})}catch(f){p.logger.debug(`failed to parse ${o}: ${f}`),r={version:1,hooks:{}}}for(const f of Object.keys(r.hooks)){const m=r.hooks[f].filter(h=>!this.isIronBeeHook(h));m.length===0?delete r.hooks[f]:r.hooks[f]=m}r.hooks.sessionStart||(r.hooks.sessionStart=[]),r.hooks.sessionStart.push({command:"ironbee hook session-start --client cursor"}),r.hooks.beforeSubmitPrompt||(r.hooks.beforeSubmitPrompt=[]),r.hooks.beforeSubmitPrompt.push({command:"ironbee hook activity-start --client cursor"}),s&&(r.hooks.preToolUse||(r.hooks.preToolUse=[]),r.hooks.preToolUse.push({command:`ironbee hook require-verification --client cursor${t}`,matcher:"MCP:(bdt|ndt|bedt)_.*",failClosed:e==="enforce"}),r.hooks.preToolUse.push({command:`ironbee hook require-verdict --client cursor${t}`,matcher:"Write|StrReplace|Delete",failClosed:e==="enforce"}),r.hooks.postToolUse||(r.hooks.postToolUse=[]),r.hooks.postToolUse.push({command:"ironbee hook clear-verdict --client cursor",matcher:"Write|StrReplace|Delete"})),r.hooks.postToolUse||(r.hooks.postToolUse=[]);const u=s?"ironbee hook track-action --client cursor":"ironbee hook track-action-monitor --client cursor";r.hooks.postToolUse.push({command:u}),r.hooks.postToolUseFailure||(r.hooks.postToolUseFailure=[]),r.hooks.postToolUseFailure.push({command:u}),r.hooks.stop||(r.hooks.stop=[]);const g=e==="enforce"?"ironbee hook verify-gate --client cursor":"ironbee hook activity-end --client cursor";r.hooks.stop.push({command:g}),r.hooks.sessionEnd||(r.hooks.sessionEnd=[]),r.hooks.sessionEnd.push({command:"ironbee hook session-end --client cursor"}),r.version=1,(0,i.writeFileSync)(o,JSON.stringify(r,null,2))}removeIronBeeHooks(o){if((0,i.existsSync)(o))try{const e=JSON.parse((0,i.readFileSync)(o,"utf-8"));if(!e.hooks)return;for(const s of Object.keys(e.hooks)){const t=e.hooks[s].filter(r=>!this.isIronBeeHook(r));t.length===0?delete e.hooks[s]:e.hooks[s]=t}(0,i.writeFileSync)(o,JSON.stringify(e,null,2))}catch(e){p.logger.debug(`failed to remove hooks from ${o}: ${e}`)}}removeMcpServer(o){if((0,i.existsSync)(o))try{const e=JSON.parse((0,i.readFileSync)(o,"utf-8"));let s=!1;e.mcpServers&&e.mcpServers[v]&&(delete e.mcpServers[v],s=!0),e.mcpServers&&e.mcpServers[y]&&(delete e.mcpServers[y],s=!0),e.mcpServers&&e.mcpServers[b]&&(delete e.mcpServers[b],s=!0),R(e)?(0,i.unlinkSync)(o):s&&(0,i.writeFileSync)(o,JSON.stringify(e,null,2))}catch(e){p.logger.debug(`failed to remove MCP server from ${o}: ${e}`)}}removeFile(o){(0,i.existsSync)(o)&&(0,i.unlinkSync)(o)}writeMcpConfig(o,e){let s={mcpServers:{}};if((0,i.existsSync)(o))try{s=JSON.parse((0,i.readFileSync)(o,"utf-8")),s.mcpServers||(s.mcpServers={})}catch(r){p.logger.debug(`failed to parse ${o}: ${r}`),s={mcpServers:{}}}const t=(0,a.loadConfig)(e);if((0,a.isCycleEnabled)(t,"browser")?s.mcpServers[v]=(0,a.getMcpServerEntry)(e):delete s.mcpServers[v],(0,a.isCycleEnabled)(t,"node")?s.mcpServers[y]=(0,a.getNodeDevToolsMcpEntry)(e):delete s.mcpServers[y],(0,a.isCycleEnabled)(t,"backend")?s.mcpServers[b]=(0,a.getBackendDevToolsMcpEntry)(e):delete s.mcpServers[b],R(s)){try{(0,i.rmSync)(o,{force:!0})}catch(r){p.logger.debug(`failed to remove empty ${o}: ${r}`)}return}(0,i.writeFileSync)(o,JSON.stringify(s,null,2))}writePermissionsConfig(o,e){let s={};if((0,i.existsSync)(o))try{s=JSON.parse((0,i.readFileSync)(o,"utf-8"))}catch(m){p.logger.debug(`failed to parse ${o}: ${m}`),s={}}const t=(0,a.loadConfig)(e),r=[];(0,a.isCycleEnabled)(t,"browser")&&r.push(`${v}:*`),(0,a.isCycleEnabled)(t,"node")&&r.push(`${y}:*`),(0,a.isCycleEnabled)(t,"backend")&&r.push(`${b}:*`);const u=new Set(H),g=(s.mcpAllowlist??[]).filter(m=>!u.has(m));for(const m of r)g.includes(m)||g.push(m);g.length>0?s.mcpAllowlist=g:delete s.mcpAllowlist;const f=(s.terminalAllowlist??[]).filter(m=>m!==S);f.push(S),s.terminalAllowlist=f,(0,i.writeFileSync)(o,JSON.stringify(s,null,2))}removePermissionsEntries(o){if((0,i.existsSync)(o))try{const e=JSON.parse((0,i.readFileSync)(o,"utf-8")),s=new Set(H);Array.isArray(e.mcpAllowlist)&&(e.mcpAllowlist=e.mcpAllowlist.filter(t=>!s.has(t)),e.mcpAllowlist.length===0&&delete e.mcpAllowlist),Array.isArray(e.terminalAllowlist)&&(e.terminalAllowlist=e.terminalAllowlist.filter(t=>t!==S),e.terminalAllowlist.length===0&&delete e.terminalAllowlist),oo(e)?(0,i.unlinkSync)(o):(0,i.writeFileSync)(o,JSON.stringify(e,null,2))}catch(e){p.logger.debug(`failed to remove permissions from ${o}: ${e}`)}}}0&&(module.exports={CursorClient});
1
+ "use strict";var S=Object.defineProperty;var W=Object.getOwnPropertyDescriptor;var x=Object.getOwnPropertyNames;var z=Object.prototype.hasOwnProperty;var k=(l,o)=>S(l,"name",{value:o,configurable:!0});var Q=(l,o)=>{for(var e in o)S(l,e,{get:o[e],enumerable:!0})},X=(l,o,e,s)=>{if(o&&typeof o=="object"||typeof o=="function")for(let t of x(o))!z.call(l,t)&&t!==e&&S(l,t,{get:()=>o[t],enumerable:!(s=W(o,t))||s.enumerable});return l};var Y=l=>X(S({},"__esModule",{value:!0}),l);var io={};Q(io,{CursorClient:()=>so});module.exports=Y(io);var i=require("fs"),c=require("path"),p=require("../../lib/logger"),n=require("../../lib/output"),_=require("../../lib/fs-prune"),T=require("./hooks/verify-gate"),P=require("./hooks/clear-verdict"),I=require("./hooks/track-action"),N=require("./hooks/track-action-monitor"),B=require("./hooks/session-start"),V=require("./hooks/require-verdict"),J=require("./hooks/require-verification"),U=require("./hooks/activity-start"),D=require("./hooks/activity-end"),L=require("./hooks/session-end"),a=require("../../lib/config"),F=require("../../lib/platform-section"),K=require("../../lib/gitignore");const v="browser-devtools",y="node-devtools",h="backend-devtools",b="android-devtools",Z="ironbee";function oo(l){return(0,c.join)(__dirname,"..",l,"platforms")}k(oo,"platformsDirFor");function H(l){const o=Object.keys(l);if(o.length===0)return!0;if(o.length===1&&o[0]==="mcpServers"){const e=l.mcpServers;return e===void 0||Object.keys(e).length===0}return!1}k(H,"isMcpConfigEmpty");const w="ironbee",O=[`${v}:*`,`${y}:*`,`${h}:*`,`${b}:*`];function eo(l){const o=new Set(["mcpAllowlist","terminalAllowlist"]);for(const t of Object.keys(l))if(!o.has(t))return!1;const e=l.mcpAllowlist??[],s=l.terminalAllowlist??[];return e.length===0&&s.length===0}k(eo,"isPermissionsEmpty");function ro(l){const o=new Set(["version","hooks"]);for(const e of Object.keys(l))if(!o.has(e))return!1;return Object.keys(l.hooks??{}).length===0}k(ro,"isCursorHooksEmpty");function d(l){return n.pc.dim(l)}k(d,"cursorColor");class so{constructor(){this.name="cursor"}static{k(this,"CursorClient")}detect(o){return(0,i.existsSync)((0,c.join)(o,".cursor"))}resolveProjectDir(){return process.env.CURSOR_PROJECT_DIR??process.env.IRONBEE_PROJECT_DIR??process.cwd()}install(o,e){const s=e??(0,a.loadConfig)(o),t=(0,a.getVerificationMode)(s),r=t!=="monitor";this.cleanupArtifacts(o),(0,K.ensureIronBeeGitignored)(o);const u=(0,c.join)(o,".cursor"),g=(0,c.join)(u,"rules"),f=(0,c.join)(u,"skills");(0,i.mkdirSync)(g,{recursive:!0}),(0,i.mkdirSync)(f,{recursive:!0});const m=(0,c.join)(u,"hooks.json");if(this.mergeHooksConfig(m,t),r){if(t==="enforce"){const M=(0,c.join)(f,"ironbee-verification.md"),j=(0,i.readFileSync)((0,c.join)(__dirname,"skills","ironbee-verification.md"),"utf-8");(0,i.writeFileSync)(M,j);const R=(0,c.join)(g,"ironbee-verification.mdc"),G=(0,i.readFileSync)((0,c.join)(__dirname,"rules","ironbee-verification.mdc"),"utf-8");(0,i.writeFileSync)(R,G),console.log(` ${n.pc.dim("\u2192")} ${d("[cursor]")} skill ${n.pc.dim("\u2192")} ${n.pc.dim(M)}`),console.log(` ${n.pc.dim("\u2192")} ${d("[cursor]")} rule ${n.pc.dim("\u2192")} ${n.pc.dim(R)}`)}const C=(0,c.join)(f,"ironbee-verify");(0,i.mkdirSync)(C,{recursive:!0});const $=(0,c.join)(C,"SKILL.md"),q=(0,i.readFileSync)((0,c.join)(__dirname,"commands","ironbee-verify","SKILL.md"),"utf-8");(0,i.writeFileSync)($,q);const E=(0,c.join)(u,"mcp.json");this.writeMcpConfig(E,o);const A=(0,c.join)(u,"permissions.json");this.writePermissionsConfig(A,o),(0,F.syncPlatformSectionsToConfig)(o,oo),console.log(` ${n.pc.dim("\u2192")} ${d("[cursor]")} hooks ${n.pc.dim("\u2192")} ${n.pc.dim(m)}`),t==="assist"&&console.log(` ${n.pc.dim("\u2192")} ${d("[cursor]")} ${n.pc.yellow("assist mode")} (verification.auto: false) \u2014 manual /ironbee-verify only, no enforcement`),console.log(` ${n.pc.dim("\u2192")} ${d("[cursor]")} command ${n.pc.dim("\u2192")} ${n.pc.dim($)}`),console.log(` ${n.pc.dim("\u2192")} ${d("[cursor]")} mcp ${n.pc.dim("\u2192")} ${n.pc.dim(E)}`),console.log(` ${n.pc.dim("\u2192")} ${d("[cursor]")} perms ${n.pc.dim("\u2192")} ${n.pc.dim(A)}`),console.log(),console.log(` ${n.pc.yellow("\u26A0")} ${n.pc.yellow("Cursor requires manual steps:")}`),console.log(` ${n.pc.yellow("1.")} Restart Cursor to load the new hooks and MCP config`),console.log(` ${n.pc.yellow("2.")} Go to ${n.pc.bold("Settings \u2192 Tools & MCP")} and verify ${n.pc.bold("browser-devtools")} is enabled`),console.log(` ${n.pc.yellow("3.")} If the server shows as enabled but tools are unavailable, toggle it off and on`)}else console.log(` ${n.pc.dim("\u2192")} ${d("[cursor]")} ${n.pc.yellow("monitoring-only mode")} (verification.enable: false)`),console.log(` ${n.pc.dim("\u2192")} ${d("[cursor]")} hooks ${n.pc.dim("\u2192")} ${n.pc.dim(m)}`),console.log(),console.log(` ${n.pc.yellow("\u26A0")} Restart Cursor to load the new hook config`)}uninstall(o){this.cleanupArtifacts(o),console.log(` ${n.pc.dim("\u2192")} ${d("[cursor]")} removed hooks, skill, rule, command, and MCP`),console.log(),console.log(` ${n.pc.yellow("\u26A0")} Restart Cursor to apply changes`)}cleanupArtifacts(o){const e=(0,c.join)(o,".cursor"),s=(0,c.join)(e,"skills","ironbee-verification.md"),t=(0,c.join)(e,"rules","ironbee-verification.mdc"),r=(0,c.join)(e,"skills","ironbee-analyze","SKILL.md"),u=(0,c.join)(e,"skills","ironbee-verify","SKILL.md");this.removeFile(s),this.removeFile(t),this.removeFile(r),this.removeFile(u);const g=(0,c.join)(e,"hooks.json");this.removeIronBeeHooks(g),this.maybeDeleteEmptyHooks(g);const f=(0,c.join)(e,"mcp.json");this.removeMcpServer(f);const m=(0,c.join)(e,"permissions.json");this.removePermissionsEntries(m),(0,_.pruneEmptyDirs)(e)}maybeDeleteEmptyHooks(o){if((0,i.existsSync)(o))try{const e=JSON.parse((0,i.readFileSync)(o,"utf-8"));ro(e)&&(0,i.unlinkSync)(o)}catch(e){p.logger.debug(`failed to inspect ${o} for emptiness: ${e}`)}}async runVerifyGate(o){await(0,T.run)(o)}async runClearVerdict(o){await(0,P.run)(o)}async runTrackAction(o){await(0,I.run)(o)}async runSessionStart(o){await(0,B.run)(o)}async runRequireVerdict(o,e){await(0,V.run)(o,e)}async runRequireVerification(o,e){await(0,J.run)(o,e)}async runActivityStart(o){await(0,U.run)(o)}async runActivityEnd(o){await(0,D.run)(o)}async runTrackActionMonitor(o){await(0,N.run)(o)}async runSessionEnd(o){await(0,L.run)(o)}async runTrackActionPre(o){}isIronBeeHook(o){return o.command.includes(Z)}mergeHooksConfig(o,e){const s=e!=="monitor",t=e==="assist"?" --soft":"";let r={version:1,hooks:{}};if((0,i.existsSync)(o))try{r=JSON.parse((0,i.readFileSync)(o,"utf-8")),r.hooks||(r.hooks={})}catch(f){p.logger.debug(`failed to parse ${o}: ${f}`),r={version:1,hooks:{}}}for(const f of Object.keys(r.hooks)){const m=r.hooks[f].filter(C=>!this.isIronBeeHook(C));m.length===0?delete r.hooks[f]:r.hooks[f]=m}r.hooks.sessionStart||(r.hooks.sessionStart=[]),r.hooks.sessionStart.push({command:"ironbee hook session-start --client cursor"}),r.hooks.beforeSubmitPrompt||(r.hooks.beforeSubmitPrompt=[]),r.hooks.beforeSubmitPrompt.push({command:"ironbee hook activity-start --client cursor"}),s&&(r.hooks.preToolUse||(r.hooks.preToolUse=[]),r.hooks.preToolUse.push({command:`ironbee hook require-verification --client cursor${t}`,matcher:"MCP:(bdt|ndt|bedt|adt)_.*",failClosed:e==="enforce"}),r.hooks.preToolUse.push({command:`ironbee hook require-verdict --client cursor${t}`,matcher:"Write|StrReplace|Delete",failClosed:e==="enforce"}),r.hooks.postToolUse||(r.hooks.postToolUse=[]),r.hooks.postToolUse.push({command:"ironbee hook clear-verdict --client cursor",matcher:"Write|StrReplace|Delete"})),r.hooks.postToolUse||(r.hooks.postToolUse=[]);const u=s?"ironbee hook track-action --client cursor":"ironbee hook track-action-monitor --client cursor";r.hooks.postToolUse.push({command:u}),r.hooks.postToolUseFailure||(r.hooks.postToolUseFailure=[]),r.hooks.postToolUseFailure.push({command:u}),r.hooks.stop||(r.hooks.stop=[]);const g=e==="enforce"?"ironbee hook verify-gate --client cursor":"ironbee hook activity-end --client cursor";r.hooks.stop.push({command:g}),r.hooks.sessionEnd||(r.hooks.sessionEnd=[]),r.hooks.sessionEnd.push({command:"ironbee hook session-end --client cursor"}),r.version=1,(0,i.writeFileSync)(o,JSON.stringify(r,null,2))}removeIronBeeHooks(o){if((0,i.existsSync)(o))try{const e=JSON.parse((0,i.readFileSync)(o,"utf-8"));if(!e.hooks)return;for(const s of Object.keys(e.hooks)){const t=e.hooks[s].filter(r=>!this.isIronBeeHook(r));t.length===0?delete e.hooks[s]:e.hooks[s]=t}(0,i.writeFileSync)(o,JSON.stringify(e,null,2))}catch(e){p.logger.debug(`failed to remove hooks from ${o}: ${e}`)}}removeMcpServer(o){if((0,i.existsSync)(o))try{const e=JSON.parse((0,i.readFileSync)(o,"utf-8"));let s=!1;e.mcpServers&&e.mcpServers[v]&&(delete e.mcpServers[v],s=!0),e.mcpServers&&e.mcpServers[y]&&(delete e.mcpServers[y],s=!0),e.mcpServers&&e.mcpServers[h]&&(delete e.mcpServers[h],s=!0),e.mcpServers&&e.mcpServers[b]&&(delete e.mcpServers[b],s=!0),H(e)?(0,i.unlinkSync)(o):s&&(0,i.writeFileSync)(o,JSON.stringify(e,null,2))}catch(e){p.logger.debug(`failed to remove MCP server from ${o}: ${e}`)}}removeFile(o){(0,i.existsSync)(o)&&(0,i.unlinkSync)(o)}writeMcpConfig(o,e){let s={mcpServers:{}};if((0,i.existsSync)(o))try{s=JSON.parse((0,i.readFileSync)(o,"utf-8")),s.mcpServers||(s.mcpServers={})}catch(r){p.logger.debug(`failed to parse ${o}: ${r}`),s={mcpServers:{}}}const t=(0,a.loadConfig)(e);if((0,a.isCycleEnabled)(t,"browser")?s.mcpServers[v]=(0,a.getMcpServerEntry)(e):delete s.mcpServers[v],(0,a.isCycleEnabled)(t,"node")?s.mcpServers[y]=(0,a.getNodeDevToolsMcpEntry)(e):delete s.mcpServers[y],(0,a.isCycleEnabled)(t,"backend")?s.mcpServers[h]=(0,a.getBackendDevToolsMcpEntry)(e):delete s.mcpServers[h],(0,a.isCycleEnabled)(t,"android")?s.mcpServers[b]=(0,a.getAndroidDevToolsMcpEntry)(e):delete s.mcpServers[b],H(s)){try{(0,i.rmSync)(o,{force:!0})}catch(r){p.logger.debug(`failed to remove empty ${o}: ${r}`)}return}(0,i.writeFileSync)(o,JSON.stringify(s,null,2))}writePermissionsConfig(o,e){let s={};if((0,i.existsSync)(o))try{s=JSON.parse((0,i.readFileSync)(o,"utf-8"))}catch(m){p.logger.debug(`failed to parse ${o}: ${m}`),s={}}const t=(0,a.loadConfig)(e),r=[];(0,a.isCycleEnabled)(t,"browser")&&r.push(`${v}:*`),(0,a.isCycleEnabled)(t,"node")&&r.push(`${y}:*`),(0,a.isCycleEnabled)(t,"backend")&&r.push(`${h}:*`),(0,a.isCycleEnabled)(t,"android")&&r.push(`${b}:*`);const u=new Set(O),g=(s.mcpAllowlist??[]).filter(m=>!u.has(m));for(const m of r)g.includes(m)||g.push(m);g.length>0?s.mcpAllowlist=g:delete s.mcpAllowlist;const f=(s.terminalAllowlist??[]).filter(m=>m!==w);f.push(w),s.terminalAllowlist=f,(0,i.writeFileSync)(o,JSON.stringify(s,null,2))}removePermissionsEntries(o){if((0,i.existsSync)(o))try{const e=JSON.parse((0,i.readFileSync)(o,"utf-8")),s=new Set(O);Array.isArray(e.mcpAllowlist)&&(e.mcpAllowlist=e.mcpAllowlist.filter(t=>!s.has(t)),e.mcpAllowlist.length===0&&delete e.mcpAllowlist),Array.isArray(e.terminalAllowlist)&&(e.terminalAllowlist=e.terminalAllowlist.filter(t=>t!==w),e.terminalAllowlist.length===0&&delete e.terminalAllowlist),eo(e)?(0,i.unlinkSync)(o):(0,i.writeFileSync)(o,JSON.stringify(e,null,2))}catch(e){p.logger.debug(`failed to remove permissions from ${o}: ${e}`)}}}0&&(module.exports={CursorClient});
@@ -0,0 +1,61 @@
1
+ <!-- Android mobile verification is ENABLED for this project. -->
2
+
3
+ ## Android Mode (when `android.verifyPatterns` matches an edited file)
4
+
5
+ > **Precondition: the project must actually target Android.** If there is no `android/` directory or `AndroidManifest.xml`, this section does NOT apply — `MCP:adt_*` tools require a connected Android device or emulator. Just do browser verification.
6
+
7
+ If the project has android verification enabled (`ironbee android enable` once at setup) and your edits touch matching paths, the stop hook also enforces an Android cycle. The same `verification-start` covers both cycles; one platform-agnostic verdict covers both.
8
+
9
+ ### Mode behavior (android cycle)
10
+ - **default** (no arg or `default`): exercise only the UI flows / code paths your diff touched.
11
+ - **full**: exercise every Android code path reachable from files matching `android.verifyPatterns`.
12
+ - `visual` / `functional`: browser-only modes; android cycle behaves as `default` when they are passed.
13
+
14
+ ### Steps (run within step 3 of the Universal steps above)
15
+ 1. **If `recording.enable` is on, start a screen recording first**: `MCP:adt_content_start-recording` — the gate blocks every other android tool until it runs.
16
+ 2. **Connect to a running device or emulator**: `MCP:adt_device_connect`.
17
+ 3. **Launch the app** (or bring it to the relevant screen): `MCP:adt_device_launch-app`.
18
+ 4. **Pick an evidence path** for the changed code:
19
+ - **Device-evidence** (proves the change is visible / functional): drive UI (`MCP:adt_interaction_tap` / `MCP:adt_interaction_input-text` / `MCP:adt_interaction_swipe`) → screenshot (`MCP:adt_content_take-screenshot`) → UI snapshot (`MCP:adt_a11y_take-ui-snapshot`). **STOP and visually analyze the screenshot** — readability, layout, cut-off content, expected state rendered; the snapshot reports structure, the screenshot shows what the user actually sees. Both are MANDATORY on this path.
20
+ - **Log-evidence** (proves the changed code path executed): `MCP:adt_o11y_log-read` or `MCP:adt_o11y_log-follow`. Confirm expected log lines present AND no FATAL/crash from the app package.
21
+ 5. **If recording was started, stop it now** — `MCP:adt_content_stop-recording`. submit-verdict rejects with `"recording is still active"` when this step is skipped.
22
+ 6. **Submit verdict** — platform-agnostic, just status + checks (+ issues/fixes).
23
+
24
+ ### Verdict (platform-agnostic)
25
+ ```json
26
+ {
27
+ "session_id": "...",
28
+ "status": "pass",
29
+ "checks": ["LoginActivity renders after auth refactor", "no crash in Logcat"]
30
+ }
31
+ ```
32
+
33
+ For a multi-cycle pass, both browser and android pass criteria must hold.
34
+
35
+ ---
36
+
37
+ ## Default Mode (android cycle)
38
+
39
+ Focus on the UI flows or code paths your diff touched — not the entire app.
40
+
41
+ ### 1. Study the changes
42
+ 1. Run `git diff --name-only` and `git diff --name-only HEAD~1`
43
+ 2. **Ignore `.ironbee/`, `.claude/`, `.cursor/`** — tool config, not application code
44
+ 3. **Read the full diff** for every Android file in scope — note new UI elements, changed layouts, new logic branches, changed Logcat tags
45
+ 4. Before connecting, identify: which Activity / Fragment / Composable is affected? Which UI actions exercise it? Which Logcat tags emit on that path?
46
+
47
+ ### 2. Verify against the running device
48
+ - **Connect + launch the app** on the device/emulator
49
+ - **Drive the affected UI flow** — tap/type/swipe to reach the changed screen
50
+ - **Take a screenshot + a UI snapshot** — the screenshot must show the expected UI state after your change; the UI snapshot must show the expected view hierarchy / labels
51
+ - **Check Logcat** for the relevant tag(s) — expected log lines must appear; no FATAL or unhandled exception from the app package
52
+
53
+ ---
54
+
55
+ ## Full Mode (`/ironbee-verify full`, android cycle)
56
+
57
+ Verify every Android code path reachable from files matching `android.verifyPatterns`, not just the changed files. Do NOT run `git diff` or scope to recent changes.
58
+
59
+ - Launch every Activity / Fragment / screen in scope
60
+ - Drive at least one happy-path flow AND one error-path flow per screen
61
+ - Screenshot + UI snapshot each screen; no FATAL entries in Logcat
@@ -0,0 +1,32 @@
1
+ <!-- Android mobile verification is ENABLED for this project. The stop hook
2
+ enforces an android cycle whenever an edited file matches
3
+ `android.verifyPatterns`. -->
4
+
5
+ ## Android cycle
6
+
7
+ Android file changes IF the file matches `android.verifyPatterns` ALSO require verification through the **android-devtools** MCP server (prefix `MCP:adt_*`). Android-cycle verification means connecting to a running device or emulator, driving the app UI to exercise the changed code and taking a screenshot — OR reading Logcat to confirm the changed code path executed without crashes.
8
+
9
+ Both cycles can be active simultaneously (e.g. you edit both a React component and a Kotlin activity in the same task). One `verification-start` covers all active cycles; one platform-agnostic verdict covers them all; one retry counter applies globally.
10
+
11
+ ### ⚠️ `android-devtools` is ONLY for Android apps
12
+
13
+ `android-devtools` works with Android devices and emulators via ADB. It does NOT work for web-only, iOS-only, or desktop projects. If the project has no `android/` directory or `AndroidManifest.xml`, do NOT call `MCP:adt_*` tools.
14
+
15
+ **Misconfiguration recovery.** If this cycle activated but the project isn't an Android project, the operator enabled the android cycle by mistake. The stop hook will keep blocking with `incomplete_tools` for the android cycle. Don't attempt the connection. Instead, stop and clearly report to the user: the project does not target Android; ask them to run `ironbee android disable` to unblock the gate.
16
+
17
+ ### Android-cycle additions to the main flow
18
+
19
+ These attach to the **Required steps** above — they don't replace any step. Numbering follows the main flow:
20
+
21
+ - **Within step 3 (run flow):** also run the android flow: connect (`MCP:adt_device_connect`) → launch app (`MCP:adt_device_launch-app`) → pick ONE evidence path:
22
+ - **Device-evidence**: drive UI (`MCP:adt_interaction_tap` / `MCP:adt_interaction_input-text` / `MCP:adt_interaction_swipe`) + screenshot (`MCP:adt_content_take-screenshot`) + UI snapshot (`MCP:adt_a11y_take-ui-snapshot`)
23
+ - **Log-evidence**: read Logcat (`MCP:adt_o11y_log-read` or `MCP:adt_o11y_log-follow`) confirming expected output and no crashes
24
+ - If `recording.enable` is on, the gate forces `MCP:adt_content_start-recording` BEFORE the android steps above and rejects the verdict if you don't call `MCP:adt_content_stop-recording` AFTER them. Always pair start/stop around the steps above.
25
+ - **Within step 6 (submit verdict):** submit one platform-agnostic verdict with `status` + `checks` (+ `issues`/`fixes` as needed). Android-cycle pass criteria: device connected AND (UI-interaction + screenshot + UI snapshot taken) OR (Logcat read with no crashes).
26
+
27
+ ### Additional BANNED for android cycle
28
+
29
+ - Calling `MCP:adt_*` tools without first opening a verification cycle (`ironbee hook verification-start`).
30
+ - **Calling `MCP:adt_*` tools when the project does NOT target Android.** Use the browser cycle only for web-only projects.
31
+ - Claiming `status: pass` for an android cycle when no evidence path was exercised.
32
+ - **Submitting a verdict while an android recording is still active** — always call `MCP:adt_content_stop-recording` BEFORE `ironbee hook submit-verdict`. The recording start/stop pair brackets the verification flow.
@@ -0,0 +1,55 @@
1
+ <!-- Android mobile verification is ENABLED for this project. The stop hook
2
+ enforces an android cycle whenever an edited file matches
3
+ `android.verifyPatterns`. -->
4
+
5
+ ## ⚠️ CRITICAL: when NOT to use android-devtools
6
+
7
+ **`android-devtools` is ONLY for Android apps** (native Kotlin/Java + React Native / Expo with an Android target). Do **NOT** call `MCP:adt_*` tools for projects that do not have an Android target.
8
+
9
+ **How to tell whether the project targets Android:**
10
+ - `android/` directory at the project root (React Native, Expo)
11
+ - `app/build.gradle` or `build.gradle.kts` with `com.android.application`
12
+ - `AndroidManifest.xml` present under `android/` or `app/src/main/`
13
+
14
+ If you see only `ios/`, `web/`, or no mobile directories — the project does NOT target Android. Do NOT call any `MCP:adt_*` tools.
15
+
16
+ **Misconfiguration recovery.** If this cycle activated but the project isn't an Android project, the operator enabled the android cycle by mistake. The stop hook will keep blocking with `incomplete_tools` until `maxRetries` is exhausted. Don't attempt a device connection. Stop and tell the user clearly: the project does not target Android; ask them to run `ironbee android disable` to unblock the gate.
17
+
18
+ ## Android flow
19
+
20
+ > **Recording (only when `recording.enable` is on in config):** the gate blocks every other android tool until you first call `MCP:adt_content_start-recording`, and `submit-verdict` rejects with `"recording is still active"` unless you call `MCP:adt_content_stop-recording` after the steps below. **Treat start/stop as bookends around steps 1-3.** The recording ships to the collector as verification evidence.
21
+
22
+ 1. **Connect to a running device or emulator**: `MCP:adt_device_connect` (list available targets first with `MCP:adt_device_list-targets` if needed).
23
+ - For an emulator, the device is usually `emulator-5554`.
24
+ - For a physical device with USB debugging on, it will appear in the target list.
25
+ 2. **Ensure the app is running** on the target device: `MCP:adt_device_launch-app` with the package name.
26
+ 3. **Pick an evidence path** per changed code area:
27
+ - **Device-evidence path** (UI interaction confirms the change is live):
28
+ - Drive the app UI to exercise the changed code: `MCP:adt_interaction_tap`, `MCP:adt_interaction_input-text`, `MCP:adt_interaction_swipe`, `MCP:adt_interaction_scroll` as needed (use `MCP:adt_a11y_find-element` to locate elements).
29
+ - Take a screenshot: `MCP:adt_content_take-screenshot` — then STOP and visually analyze it (readability, layout, cut-off content, expected state rendered). The screenshot tells you what the user actually sees.
30
+ - Take a UI snapshot: `MCP:adt_a11y_take-ui-snapshot` — verify the view hierarchy / labels / structure match the change.
31
+ - Screenshot AND UI snapshot are BOTH MANDATORY on this path (the stop hook checks each) — visual + structural evidence, like the browser cycle's screenshot + aria-snapshot pair.
32
+ - **Log-evidence path** (device logs confirm the changed code path executed):
33
+ - Read Logcat output for the tag(s) relevant to the changed code: `MCP:adt_o11y_log-read` or `MCP:adt_o11y_log-follow` (drain a follow with `MCP:adt_o11y_log-get-followed`, stop it with `MCP:adt_o11y_log-stop-follow`).
34
+ - Confirm expected log lines appear AND no unexpected crashes (FATAL / E/ entries for the app package).
35
+
36
+ ### Verdict fields
37
+ The verdict is platform-agnostic — submit only semantic judgment:
38
+
39
+ ```json
40
+ {
41
+ "session_id": "<sid>",
42
+ "status": "pass",
43
+ "checks": ["LoginActivity renders correctly after auth change", "no crash in Logcat"]
44
+ }
45
+ ```
46
+
47
+ On fail, include `issues`. On pass after a previous fail, include `fixes`.
48
+
49
+ Android-cycle pass criteria:
50
+ - **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.
51
+ - **Log-evidence**: Logcat was read AND the expected log lines are present AND no crash (FATAL / unhandled exception) from the app's package.
52
+
53
+ ## Multi-cycle (browser + android simultaneously)
54
+
55
+ Both cycles can be active simultaneously. One `verification-start` covers all active cycles; one platform-agnostic verdict covers them all; one retry counter applies globally.
@@ -51,3 +51,6 @@ Every file edit automatically clears your verdict, requiring re-verification.
51
51
 
52
52
  <!--IRONBEE:PLATFORM:backend-->
53
53
  <!--/IRONBEE:PLATFORM:backend-->
54
+
55
+ <!--IRONBEE:PLATFORM:android-->
56
+ <!--/IRONBEE:PLATFORM:android-->
@@ -67,6 +67,9 @@ If already running, skip start. If the build fails, fix it before proceeding.
67
67
  <!--IRONBEE:PLATFORM:backend-->
68
68
  <!--/IRONBEE:PLATFORM:backend-->
69
69
 
70
+ <!--IRONBEE:PLATFORM:android-->
71
+ <!--/IRONBEE:PLATFORM:android-->
72
+
70
73
  ## Important
71
74
  - **Always submit a verdict after every verification attempt** — both pass AND fail. Fail verdicts are tracked for analytics.
72
75
  - The stop hook checks that the required tools were used for every active cycle and that the verdict carries non-empty `checks`.
@@ -0,0 +1 @@
1
+ "use strict";var r=Object.defineProperty;var b=Object.getOwnPropertyDescriptor;var y=Object.getOwnPropertyNames;var h=Object.prototype.hasOwnProperty;var s=(o,e)=>r(o,"name",{value:e,configurable:!0});var u=(o,e)=>{for(var i in e)r(o,i,{get:e[i],enumerable:!0})},C=(o,e,i,d)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of y(e))!h.call(o,n)&&n!==i&&r(o,n,{get:()=>e[n],enumerable:!(d=b(e,n))||d.enumerable});return o};var v=o=>C(r({},"__esModule",{value:!0}),o);var j={};u(j,{androidCommand:()=>w,androidDisableCommand:()=>f,androidEnableCommand:()=>g});module.exports=v(j);var t=require("commander"),p=require("../clients/registry"),l=require("../lib/config"),c=require("../lib/output"),a=require("./cycle-toggle");function m(o){return o.option("-p, --project-dir <dir>","Project directory (default: cwd).").option("-g, --global","Write to the global config (~/.ironbee/config.json) instead of the project.").option("--local","Write to the gitignored project-local override (<project>/.ironbee/config.local.json). Mutually exclusive with --global.").option("--client <name>",`Only update guidance md files for this client (${(0,p.clientNames)()}), or "all". Default: every registered client (per-file existsSync gate skips uninstalled ones).`)}s(m,"attachToggleOptions");const g=m(new t.Command("enable")).description('Enable the Android mobile verification cycle. Writes a minimal `{ "android": {} }` block \u2014 code defaults (android/** / app/src/** paths) flow in at runtime; `config.json` stays minimal. Refuses (warn + no-op) when the cycle is already enabled at this layer.').action(o=>{try{const e=o.projectDir??process.cwd(),i=(0,l.resolveConfigTargetFromFlags)(o);(0,a.applyEnableCycle)("android",e,i,o.client)}catch(e){console.error(`${c.pc.red("\u2717")} ${e instanceof Error?e.message:e}`),process.exit(1)}}),f=m(new t.Command("disable")).description("Disable the Android mobile verification cycle. With no customizations + no lower-layer override, drops the entire `android` block. Otherwise writes `verifyPatterns: []` (load-bearing hard kill; preserves `alwaysRequired` / `evidencePaths` / `additionalVerifyPatterns`).").action(o=>{try{const e=o.projectDir??process.cwd(),i=(0,l.resolveConfigTargetFromFlags)(o);(0,a.applyDisableCycle)("android",e,i,o.client)}catch(e){console.error(`${c.pc.red("\u2717")} ${e instanceof Error?e.message:e}`),process.exit(1)}}),w=new t.Command("android").description("Manage the Android mobile verification cycle (device / emulator UI interactions + Logcat via the `android-devtools` MCP, `adt_*` tools).").addCommand(g).addCommand(f);0&&(module.exports={androidCommand,androidDisableCommand,androidEnableCommand});
@@ -1,2 +1,2 @@
1
- "use strict";var m=Object.defineProperty;var K=Object.getOwnPropertyDescriptor;var V=Object.getOwnPropertyNames;var q=Object.prototype.hasOwnProperty;var l=(e,o)=>m(e,"name",{value:o,configurable:!0});var L=(e,o)=>{for(var n in o)m(e,n,{get:o[n],enumerable:!0})},M=(e,o,n,r)=>{if(o&&typeof o=="object"||typeof o=="function")for(let i of V(o))!q.call(e,i)&&i!==n&&m(e,i,{get:()=>o[i],enumerable:!(r=K(o,i))||r.enumerable});return e};var Y=e=>M(m({},"__esModule",{value:!0}),e);var ie={};L(ie,{configCommand:()=>u,runGet:()=>U,runList:()=>W,runPath:()=>_,runSet:()=>F,runUnset:()=>B});module.exports=Y(ie);var N=require("commander"),g=require("fs"),D=require("os"),d=require("path"),f=require("../clients/registry"),I=require("./install"),b=require("../lib/config"),G=require("../lib/gitignore"),k=require("../lib/logger"),t=require("../lib/output"),h=require("../lib/prompt"),J=require("../lib/projects-registry");const z=new Set(["verification","collector","browser","node","backend","browserDevTools","nodeDevTools","backendDevTools","telemetry","privacy","statusLine","otel"]);function C(e){return(0,d.join)((0,d.resolve)(e),".ironbee","config.json")}l(C,"projectConfigPath");function S(e){return(0,d.join)((0,d.resolve)(e),".ironbee","config.local.json")}l(S,"projectLocalConfigPath");function O(){return(0,d.join)((0,D.homedir)(),".ironbee","config.json")}l(O,"globalConfigPath");function w(e){if(!(0,g.existsSync)(e))return{};try{const o=(0,g.readFileSync)(e,"utf-8");return o.trim().length===0?{}:JSON.parse(o)}catch(o){throw k.logger.debug(`failed to read ${e}: ${o}`),new Error(`Config at ${e} is not valid JSON: ${o instanceof Error?o.message:o}`)}}l(w,"readConfigFile");function j(e,o){(0,g.mkdirSync)((0,d.join)(e,".."),{recursive:!0}),(0,g.writeFileSync)(e,JSON.stringify(o,null,2)+`
2
- `)}l(j,"writeConfigFile");function H(e){const o=e.indexOf(".");return o===-1?e:e.slice(0,o)}l(H,"topKey");function $(e){return z.has(H(e))}l($,"affectsArtifacts");function Q(e,o){if(o.length===0)return e;const n=o.split(".");let r=e;for(const i of n){if(r==null||typeof r!="object"||Array.isArray(r))return;r=r[i]}return r}l(Q,"getAtPath");function X(e,o,n){if(o.length===0)throw new Error("Cannot set the root config \u2014 pass a dotted key (e.g. `collector.url`).");const r=o.split(".");let i=e;for(let a=0;a<r.length-1;a++){const s=r[a],c=i[s];(c==null||typeof c!="object"||Array.isArray(c))&&(i[s]={}),i=i[s]}i[r[r.length-1]]=n}l(X,"setAtPath");function Z(e,o){if(o.length===0)return!1;const n=o.split(".");let r=e;for(let a=0;a<n.length-1;a++){const s=n[a],c=r[s];if(c==null||typeof c!="object"||Array.isArray(c))return!1;r=c}const i=n[n.length-1];return Object.prototype.hasOwnProperty.call(r,i)?(delete r[i],!0):!1}l(Z,"unsetAtPath");function ee(e,o){if(o)try{return JSON.parse(e)}catch(n){throw new Error(`--json was set but value is not valid JSON: ${n instanceof Error?n.message:n}`)}try{return JSON.parse(e)}catch{return e}}l(ee,"parseValue");function v(e){if(e.global===!0&&e.local===!0)throw new Error("Pass at most one of --global / --local.");if(e.global===!0)return{path:O(),label:"global",useGlobal:!0};const o=e.projectDir??process.cwd();return e.local===!0?{path:S(o),label:"local",useGlobal:!1}:{path:C(o),label:"project",useGlobal:!1}}l(v,"resolveWriteTarget");function A(e){if((e.global===!0?1:0)+(e.project===!0?1:0)+(e.local===!0?1:0)>1)throw new Error("Pass at most one of --global / --project / --local.");if(e.global===!0)return{label:"global",path:O()};const n=e.projectDir??process.cwd();return e.project===!0?{label:"project",path:C(n)}:e.local===!0?{label:"local",path:S(n)}:{label:"merged",path:n}}l(A,"resolveReadTarget");function P(e){return e.label==="merged"?(0,b.loadConfig)(e.path):w(e.path)}l(P,"readForGet");function oe(e){return e===void 0?"(unset)":typeof e=="string"?e:JSON.stringify(e,null,2)}l(oe,"formatValue");function ne(e,o){const n=(0,f.detectClients)(e);if(o===void 0&&n.length===0)return console.log(` ${t.pc.dim("\xB7")} ${t.pc.dim("no clients detected in")} ${t.pc.dim(e)} ${t.pc.dim("\u2014 skipping artifact rerender")}`),[];const r=(0,b.loadConfig)(e),i=(0,f.resolveTargetClients)(e,o);for(const a of i)a.install(e,r);return(0,G.ensureIronBeeGitignored)(e),i.map(a=>a.name)}l(ne,"rerenderArtifacts");function E(e,o,n,r){const i=(0,g.existsSync)(e)?(0,g.readFileSync)(e,"utf-8"):null;j(e,o);try{return ne(n,r)}catch(a){try{i===null?(0,g.existsSync)(e)&&(0,g.unlinkSync)(e):(0,g.writeFileSync)(e,i)}catch(s){k.logger.debug(`config rollback failed: ${s}`)}throw a}}l(E,"writeAndRerender");const R=10;function te(e){const o=(0,f.listActiveProjects)(),n=(0,J.canonicalizePath)(e);return o.filter(r=>r.path!==n)}l(te,"listOtherProjects");function re(e){const o=e.length===1?"other project":"other projects";console.log(` ${t.pc.yellow("\u26A0")} ${t.pc.bold(String(e.length))} ${o} registered in the inventory still on the prior state:`);const n=e.slice(0,R);for(const r of n)console.log(` ${t.pc.dim("\xB7")} ${t.pc.dim(r.path)}`);e.length>R&&console.log(` ${t.pc.dim("\u2026and")} ${t.pc.bold(String(e.length-R))} ${t.pc.dim("more")}`)}l(re,"printOtherProjectsBanner");async function T(e,o,n){if(!o)return;const r=te(e);if(r.length===0)return;re(r);let i;if(n===!0)i=!0;else if(n===!1){console.log(` ${t.pc.dim("Run")} ${t.pc.cyan("ironbee install --all")} ${t.pc.dim("later to apply, or")} ${t.pc.cyan("ironbee install")} ${t.pc.dim("in selected projects.")}`);return}else if((0,h.isInteractive)()){const a=r.length===1?"this project":`these ${r.length} projects`;i=await(0,h.promptYesNo)(` Apply this change to ${a} now?`,!0)}else{console.log(` ${t.pc.dim("Run")} ${t.pc.cyan("ironbee install --all")} ${t.pc.dim("to apply everywhere, or pass")} ${t.pc.cyan("--apply-all")} ${t.pc.dim("to skip this prompt.")}`);return}if(!i){console.log(` ${t.pc.dim("Skipped. Run")} ${t.pc.cyan("ironbee install --all")} ${t.pc.dim("later to apply.")}`);return}console.log(),await(0,I.runInstallAll)({})}l(T,"maybeApplyToOtherProjects");async function F(e,o,n){if(e.length===0)throw new Error("Key is required (dotted path, e.g. `collector.url`).");const r=v(n),i=ee(o,n.json===!0),a=w(r.path),s=JSON.parse(JSON.stringify(a));X(s,e,i);const c=n.projectDir??process.cwd(),p=n.rerender!==!1&&$(e);let y=[];p?y=E(r.path,s,c,n.client):j(r.path,s),console.log(`${t.pc.green("\u2713")} Set ${t.pc.cyan(e)} = ${t.pc.bold(oe(i))} in ${r.label} config (${t.pc.dim(r.path)}).`),p?y.length>0&&(console.log(` ${t.pc.dim("Re-rendered artifacts for:")} ${t.pc.bold(y.join(", "))}`),console.log(` ${t.pc.yellow("\u26A0")} Restart your editor / agent session for the change to take effect.`)):n.rerender===!1&&$(e)&&console.log(` ${t.pc.yellow("\u26A0")} --no-rerender set: artifacts may now be out of sync with config. Run ${t.pc.cyan("ironbee install")} to resync.`),x(e),r.useGlobal&&await T(c,p,n.applyAll)}l(F,"runSet");function x(e){const o=(0,b.findActiveEnvOverride)(e);o!==void 0&&console.log(` ${t.pc.yellow("\u26A0")} ${t.pc.cyan(o.envVar)} is set in this shell \u2014 env overrides file config, so ${t.pc.cyan(e)} reads will return the env value until ${t.pc.cyan(o.envVar)} is unset.`)}l(x,"warnIfEnvShadowed");async function B(e,o){if(e.length===0)throw new Error("Key is required (dotted path, e.g. `collector.url`).");const n=v(o),r=w(n.path),i=JSON.parse(JSON.stringify(r));if(!Z(i,e)){console.log(`${t.pc.dim("\xB7")} ${t.pc.cyan(e)} not present in ${n.label} config (${t.pc.dim(n.path)}). No-op.`);return}const s=o.projectDir??process.cwd(),c=o.rerender!==!1&&$(e);let p=[];c?p=E(n.path,i,s,o.client):j(n.path,i),console.log(`${t.pc.green("\u2713")} Unset ${t.pc.cyan(e)} in ${n.label} config (${t.pc.dim(n.path)}).`),c&&p.length>0&&(console.log(` ${t.pc.dim("Re-rendered artifacts for:")} ${t.pc.bold(p.join(", "))}`),console.log(` ${t.pc.yellow("\u26A0")} Restart your editor / agent session for the change to take effect.`)),x(e),n.useGlobal&&await T(s,c,o.applyAll)}l(B,"runUnset");function U(e,o){const n=A(o),r=P(n),i=Q(r,e);i===void 0&&(console.error(`${t.pc.dim("\xB7")} ${t.pc.cyan(e)} ${t.pc.dim("not set in")} ${n.label} ${t.pc.dim("config")}`),process.exit(1)),console.log(typeof i=="string"?i:JSON.stringify(i,null,2))}l(U,"runGet");function W(e){const o=A(e),n=P(o);console.log(JSON.stringify(n,null,2))}l(W,"runList");function _(e){const o=v(e);console.log(o.path)}l(_,"runPath");const u=new N.Command("config").description("Read or write IronBee configuration values (project or global). Smart re-render: artifact-affecting keys auto-update installed client files.");u.command("get <key>").description("Print the value at a dotted path. Default: merged effective value (global + project + local). --global / --project / --local narrow to one source.").option("-p, --project-dir <dir>","Project directory (default: cwd).").option("-g, --global","Read from global config only (~/.ironbee/config.json).").option("--project","Read from project config only (<project>/.ironbee/config.json).").option("--local","Read from project-local config only (<project>/.ironbee/config.local.json \u2014 gitignored).").action((e,o)=>{try{U(e,o)}catch(n){console.error(`${t.pc.red("\u2717")} ${n instanceof Error?n.message:n}`),process.exit(1)}}),u.command("set <key> <value>").description("Set a config value. Type-coerces (true/42/[\u2026]/{\u2026}) unless --json forces strict parsing. Re-renders client artifacts when the top-level key affects them. After a global write (`-g`) on an artifact-affecting key, prompts whether to apply the change to every other registered project. Use --local to write to the gitignored personal-override layer instead of the committed project config.").option("-p, --project-dir <dir>","Project directory (default: cwd).").option("-g, --global","Write to global config (~/.ironbee/config.json).").option("--local","Write to the gitignored project-local override (<project>/.ironbee/config.local.json) instead of the committed project config. Mutually exclusive with --global.").option("--client <name>",`Filter clients for artifact rerender (${(0,f.clientNames)()}), or "all". Default: detected clients.`).option("--no-rerender","Skip artifact rerender even when the key normally triggers it.").option("--json","Require value to parse as strict JSON (no string fallback).").option("--apply-all","After a global write on an artifact-affecting key, apply the change to every registered project without prompting.").option("--no-apply-all","After a global write on an artifact-affecting key, do NOT apply to other registered projects (suppress the prompt).").action(async(e,o,n)=>{try{await F(e,o,n)}catch(r){console.error(`${t.pc.red("\u2717")} ${r instanceof Error?r.message:r}`),process.exit(1)}}),u.command("unset <key>").description("Remove a config key. Idempotent \u2014 no-op when the key is absent. Re-renders client artifacts when the top-level key affects them. After a global unset (`-g`) on an artifact-affecting key, prompts whether to apply the change to every other registered project. Use --local to remove from the gitignored personal-override layer instead of the committed project config.").option("-p, --project-dir <dir>","Project directory (default: cwd).").option("-g, --global","Write to global config (~/.ironbee/config.json).").option("--local","Write to the gitignored project-local override (<project>/.ironbee/config.local.json) instead of the committed project config. Mutually exclusive with --global.").option("--client <name>",`Filter clients for artifact rerender (${(0,f.clientNames)()}), or "all". Default: detected clients.`).option("--no-rerender","Skip artifact rerender even when the key normally triggers it.").option("--apply-all","After a global unset on an artifact-affecting key, apply the change to every registered project without prompting.").option("--no-apply-all","After a global unset on an artifact-affecting key, do NOT apply to other registered projects (suppress the prompt).").action(async(e,o)=>{try{await B(e,o)}catch(n){console.error(`${t.pc.red("\u2717")} ${n instanceof Error?n.message:n}`),process.exit(1)}}),u.command("list").description("Print the entire config. Default: merged effective config; --global / --project / --local narrow to one source.").option("-p, --project-dir <dir>","Project directory (default: cwd).").option("-g, --global","Read from global config only.").option("--project","Read from project config only.").option("--local","Read from project-local config only (<project>/.ironbee/config.local.json \u2014 gitignored).").action(e=>{try{W(e)}catch(o){console.error(`${t.pc.red("\u2717")} ${o instanceof Error?o.message:o}`),process.exit(1)}}),u.command("path").description("Print the on-disk path of the targeted config file (project by default; --global for global, --local for the gitignored personal-override layer).").option("-p, --project-dir <dir>","Project directory (default: cwd).").option("-g, --global","Print global config path (~/.ironbee/config.json).").option("--local","Print project-local config path (<project>/.ironbee/config.local.json).").action(e=>{try{_(e)}catch(o){console.error(`${t.pc.red("\u2717")} ${o instanceof Error?o.message:o}`),process.exit(1)}});0&&(module.exports={configCommand,runGet,runList,runPath,runSet,runUnset});
1
+ "use strict";var m=Object.defineProperty;var K=Object.getOwnPropertyDescriptor;var V=Object.getOwnPropertyNames;var q=Object.prototype.hasOwnProperty;var l=(e,o)=>m(e,"name",{value:o,configurable:!0});var L=(e,o)=>{for(var n in o)m(e,n,{get:o[n],enumerable:!0})},M=(e,o,n,r)=>{if(o&&typeof o=="object"||typeof o=="function")for(let i of V(o))!q.call(e,i)&&i!==n&&m(e,i,{get:()=>o[i],enumerable:!(r=K(o,i))||r.enumerable});return e};var Y=e=>M(m({},"__esModule",{value:!0}),e);var ie={};L(ie,{configCommand:()=>u,runGet:()=>U,runList:()=>W,runPath:()=>_,runSet:()=>F,runUnset:()=>B});module.exports=Y(ie);var D=require("commander"),g=require("fs"),N=require("os"),d=require("path"),f=require("../clients/registry"),I=require("./install"),b=require("../lib/config"),G=require("../lib/gitignore"),k=require("../lib/logger"),t=require("../lib/output"),h=require("../lib/prompt"),J=require("../lib/projects-registry");const z=new Set(["verification","collector","browser","node","backend","android","browserDevTools","nodeDevTools","backendDevTools","androidDevTools","telemetry","privacy","statusLine","otel"]);function C(e){return(0,d.join)((0,d.resolve)(e),".ironbee","config.json")}l(C,"projectConfigPath");function S(e){return(0,d.join)((0,d.resolve)(e),".ironbee","config.local.json")}l(S,"projectLocalConfigPath");function O(){return(0,d.join)((0,N.homedir)(),".ironbee","config.json")}l(O,"globalConfigPath");function w(e){if(!(0,g.existsSync)(e))return{};try{const o=(0,g.readFileSync)(e,"utf-8");return o.trim().length===0?{}:JSON.parse(o)}catch(o){throw k.logger.debug(`failed to read ${e}: ${o}`),new Error(`Config at ${e} is not valid JSON: ${o instanceof Error?o.message:o}`)}}l(w,"readConfigFile");function j(e,o){(0,g.mkdirSync)((0,d.join)(e,".."),{recursive:!0}),(0,g.writeFileSync)(e,JSON.stringify(o,null,2)+`
2
+ `)}l(j,"writeConfigFile");function H(e){const o=e.indexOf(".");return o===-1?e:e.slice(0,o)}l(H,"topKey");function $(e){return z.has(H(e))}l($,"affectsArtifacts");function Q(e,o){if(o.length===0)return e;const n=o.split(".");let r=e;for(const i of n){if(r==null||typeof r!="object"||Array.isArray(r))return;r=r[i]}return r}l(Q,"getAtPath");function X(e,o,n){if(o.length===0)throw new Error("Cannot set the root config \u2014 pass a dotted key (e.g. `collector.url`).");const r=o.split(".");let i=e;for(let a=0;a<r.length-1;a++){const s=r[a],c=i[s];(c==null||typeof c!="object"||Array.isArray(c))&&(i[s]={}),i=i[s]}i[r[r.length-1]]=n}l(X,"setAtPath");function Z(e,o){if(o.length===0)return!1;const n=o.split(".");let r=e;for(let a=0;a<n.length-1;a++){const s=n[a],c=r[s];if(c==null||typeof c!="object"||Array.isArray(c))return!1;r=c}const i=n[n.length-1];return Object.prototype.hasOwnProperty.call(r,i)?(delete r[i],!0):!1}l(Z,"unsetAtPath");function ee(e,o){if(o)try{return JSON.parse(e)}catch(n){throw new Error(`--json was set but value is not valid JSON: ${n instanceof Error?n.message:n}`)}try{return JSON.parse(e)}catch{return e}}l(ee,"parseValue");function v(e){if(e.global===!0&&e.local===!0)throw new Error("Pass at most one of --global / --local.");if(e.global===!0)return{path:O(),label:"global",useGlobal:!0};const o=e.projectDir??process.cwd();return e.local===!0?{path:S(o),label:"local",useGlobal:!1}:{path:C(o),label:"project",useGlobal:!1}}l(v,"resolveWriteTarget");function A(e){if((e.global===!0?1:0)+(e.project===!0?1:0)+(e.local===!0?1:0)>1)throw new Error("Pass at most one of --global / --project / --local.");if(e.global===!0)return{label:"global",path:O()};const n=e.projectDir??process.cwd();return e.project===!0?{label:"project",path:C(n)}:e.local===!0?{label:"local",path:S(n)}:{label:"merged",path:n}}l(A,"resolveReadTarget");function P(e){return e.label==="merged"?(0,b.loadConfig)(e.path):w(e.path)}l(P,"readForGet");function oe(e){return e===void 0?"(unset)":typeof e=="string"?e:JSON.stringify(e,null,2)}l(oe,"formatValue");function ne(e,o){const n=(0,f.detectClients)(e);if(o===void 0&&n.length===0)return console.log(` ${t.pc.dim("\xB7")} ${t.pc.dim("no clients detected in")} ${t.pc.dim(e)} ${t.pc.dim("\u2014 skipping artifact rerender")}`),[];const r=(0,b.loadConfig)(e),i=(0,f.resolveTargetClients)(e,o);for(const a of i)a.install(e,r);return(0,G.ensureIronBeeGitignored)(e),i.map(a=>a.name)}l(ne,"rerenderArtifacts");function E(e,o,n,r){const i=(0,g.existsSync)(e)?(0,g.readFileSync)(e,"utf-8"):null;j(e,o);try{return ne(n,r)}catch(a){try{i===null?(0,g.existsSync)(e)&&(0,g.unlinkSync)(e):(0,g.writeFileSync)(e,i)}catch(s){k.logger.debug(`config rollback failed: ${s}`)}throw a}}l(E,"writeAndRerender");const R=10;function te(e){const o=(0,f.listActiveProjects)(),n=(0,J.canonicalizePath)(e);return o.filter(r=>r.path!==n)}l(te,"listOtherProjects");function re(e){const o=e.length===1?"other project":"other projects";console.log(` ${t.pc.yellow("\u26A0")} ${t.pc.bold(String(e.length))} ${o} registered in the inventory still on the prior state:`);const n=e.slice(0,R);for(const r of n)console.log(` ${t.pc.dim("\xB7")} ${t.pc.dim(r.path)}`);e.length>R&&console.log(` ${t.pc.dim("\u2026and")} ${t.pc.bold(String(e.length-R))} ${t.pc.dim("more")}`)}l(re,"printOtherProjectsBanner");async function T(e,o,n){if(!o)return;const r=te(e);if(r.length===0)return;re(r);let i;if(n===!0)i=!0;else if(n===!1){console.log(` ${t.pc.dim("Run")} ${t.pc.cyan("ironbee install --all")} ${t.pc.dim("later to apply, or")} ${t.pc.cyan("ironbee install")} ${t.pc.dim("in selected projects.")}`);return}else if((0,h.isInteractive)()){const a=r.length===1?"this project":`these ${r.length} projects`;i=await(0,h.promptYesNo)(` Apply this change to ${a} now?`,!0)}else{console.log(` ${t.pc.dim("Run")} ${t.pc.cyan("ironbee install --all")} ${t.pc.dim("to apply everywhere, or pass")} ${t.pc.cyan("--apply-all")} ${t.pc.dim("to skip this prompt.")}`);return}if(!i){console.log(` ${t.pc.dim("Skipped. Run")} ${t.pc.cyan("ironbee install --all")} ${t.pc.dim("later to apply.")}`);return}console.log(),await(0,I.runInstallAll)({})}l(T,"maybeApplyToOtherProjects");async function F(e,o,n){if(e.length===0)throw new Error("Key is required (dotted path, e.g. `collector.url`).");const r=v(n),i=ee(o,n.json===!0),a=w(r.path),s=JSON.parse(JSON.stringify(a));X(s,e,i);const c=n.projectDir??process.cwd(),p=n.rerender!==!1&&$(e);let y=[];p?y=E(r.path,s,c,n.client):j(r.path,s),console.log(`${t.pc.green("\u2713")} Set ${t.pc.cyan(e)} = ${t.pc.bold(oe(i))} in ${r.label} config (${t.pc.dim(r.path)}).`),p?y.length>0&&(console.log(` ${t.pc.dim("Re-rendered artifacts for:")} ${t.pc.bold(y.join(", "))}`),console.log(` ${t.pc.yellow("\u26A0")} Restart your editor / agent session for the change to take effect.`)):n.rerender===!1&&$(e)&&console.log(` ${t.pc.yellow("\u26A0")} --no-rerender set: artifacts may now be out of sync with config. Run ${t.pc.cyan("ironbee install")} to resync.`),x(e),r.useGlobal&&await T(c,p,n.applyAll)}l(F,"runSet");function x(e){const o=(0,b.findActiveEnvOverride)(e);o!==void 0&&console.log(` ${t.pc.yellow("\u26A0")} ${t.pc.cyan(o.envVar)} is set in this shell \u2014 env overrides file config, so ${t.pc.cyan(e)} reads will return the env value until ${t.pc.cyan(o.envVar)} is unset.`)}l(x,"warnIfEnvShadowed");async function B(e,o){if(e.length===0)throw new Error("Key is required (dotted path, e.g. `collector.url`).");const n=v(o),r=w(n.path),i=JSON.parse(JSON.stringify(r));if(!Z(i,e)){console.log(`${t.pc.dim("\xB7")} ${t.pc.cyan(e)} not present in ${n.label} config (${t.pc.dim(n.path)}). No-op.`);return}const s=o.projectDir??process.cwd(),c=o.rerender!==!1&&$(e);let p=[];c?p=E(n.path,i,s,o.client):j(n.path,i),console.log(`${t.pc.green("\u2713")} Unset ${t.pc.cyan(e)} in ${n.label} config (${t.pc.dim(n.path)}).`),c&&p.length>0&&(console.log(` ${t.pc.dim("Re-rendered artifacts for:")} ${t.pc.bold(p.join(", "))}`),console.log(` ${t.pc.yellow("\u26A0")} Restart your editor / agent session for the change to take effect.`)),x(e),n.useGlobal&&await T(s,c,o.applyAll)}l(B,"runUnset");function U(e,o){const n=A(o),r=P(n),i=Q(r,e);i===void 0&&(console.error(`${t.pc.dim("\xB7")} ${t.pc.cyan(e)} ${t.pc.dim("not set in")} ${n.label} ${t.pc.dim("config")}`),process.exit(1)),console.log(typeof i=="string"?i:JSON.stringify(i,null,2))}l(U,"runGet");function W(e){const o=A(e),n=P(o);console.log(JSON.stringify(n,null,2))}l(W,"runList");function _(e){const o=v(e);console.log(o.path)}l(_,"runPath");const u=new D.Command("config").description("Read or write IronBee configuration values (project or global). Smart re-render: artifact-affecting keys auto-update installed client files.");u.command("get <key>").description("Print the value at a dotted path. Default: merged effective value (global + project + local). --global / --project / --local narrow to one source.").option("-p, --project-dir <dir>","Project directory (default: cwd).").option("-g, --global","Read from global config only (~/.ironbee/config.json).").option("--project","Read from project config only (<project>/.ironbee/config.json).").option("--local","Read from project-local config only (<project>/.ironbee/config.local.json \u2014 gitignored).").action((e,o)=>{try{U(e,o)}catch(n){console.error(`${t.pc.red("\u2717")} ${n instanceof Error?n.message:n}`),process.exit(1)}}),u.command("set <key> <value>").description("Set a config value. Type-coerces (true/42/[\u2026]/{\u2026}) unless --json forces strict parsing. Re-renders client artifacts when the top-level key affects them. After a global write (`-g`) on an artifact-affecting key, prompts whether to apply the change to every other registered project. Use --local to write to the gitignored personal-override layer instead of the committed project config.").option("-p, --project-dir <dir>","Project directory (default: cwd).").option("-g, --global","Write to global config (~/.ironbee/config.json).").option("--local","Write to the gitignored project-local override (<project>/.ironbee/config.local.json) instead of the committed project config. Mutually exclusive with --global.").option("--client <name>",`Filter clients for artifact rerender (${(0,f.clientNames)()}), or "all". Default: detected clients.`).option("--no-rerender","Skip artifact rerender even when the key normally triggers it.").option("--json","Require value to parse as strict JSON (no string fallback).").option("--apply-all","After a global write on an artifact-affecting key, apply the change to every registered project without prompting.").option("--no-apply-all","After a global write on an artifact-affecting key, do NOT apply to other registered projects (suppress the prompt).").action(async(e,o,n)=>{try{await F(e,o,n)}catch(r){console.error(`${t.pc.red("\u2717")} ${r instanceof Error?r.message:r}`),process.exit(1)}}),u.command("unset <key>").description("Remove a config key. Idempotent \u2014 no-op when the key is absent. Re-renders client artifacts when the top-level key affects them. After a global unset (`-g`) on an artifact-affecting key, prompts whether to apply the change to every other registered project. Use --local to remove from the gitignored personal-override layer instead of the committed project config.").option("-p, --project-dir <dir>","Project directory (default: cwd).").option("-g, --global","Write to global config (~/.ironbee/config.json).").option("--local","Write to the gitignored project-local override (<project>/.ironbee/config.local.json) instead of the committed project config. Mutually exclusive with --global.").option("--client <name>",`Filter clients for artifact rerender (${(0,f.clientNames)()}), or "all". Default: detected clients.`).option("--no-rerender","Skip artifact rerender even when the key normally triggers it.").option("--apply-all","After a global unset on an artifact-affecting key, apply the change to every registered project without prompting.").option("--no-apply-all","After a global unset on an artifact-affecting key, do NOT apply to other registered projects (suppress the prompt).").action(async(e,o)=>{try{await B(e,o)}catch(n){console.error(`${t.pc.red("\u2717")} ${n instanceof Error?n.message:n}`),process.exit(1)}}),u.command("list").description("Print the entire config. Default: merged effective config; --global / --project / --local narrow to one source.").option("-p, --project-dir <dir>","Project directory (default: cwd).").option("-g, --global","Read from global config only.").option("--project","Read from project config only.").option("--local","Read from project-local config only (<project>/.ironbee/config.local.json \u2014 gitignored).").action(e=>{try{W(e)}catch(o){console.error(`${t.pc.red("\u2717")} ${o instanceof Error?o.message:o}`),process.exit(1)}}),u.command("path").description("Print the on-disk path of the targeted config file (project by default; --global for global, --local for the gitignored personal-override layer).").option("-p, --project-dir <dir>","Project directory (default: cwd).").option("-g, --global","Print global config path (~/.ironbee/config.json).").option("--local","Print project-local config path (<project>/.ironbee/config.local.json).").action(e=>{try{_(e)}catch(o){console.error(`${t.pc.red("\u2717")} ${o instanceof Error?o.message:o}`),process.exit(1)}});0&&(module.exports={configCommand,runGet,runList,runPath,runSet,runUnset});
@@ -1,4 +1,4 @@
1
- "use strict";var b=Object.defineProperty;var B=Object.getOwnPropertyDescriptor;var L=Object.getOwnPropertyNames;var O=Object.prototype.hasOwnProperty;var c=(e,n)=>b(e,"name",{value:n,configurable:!0});var j=(e,n)=>{for(var r in n)b(e,r,{get:n[r],enumerable:!0})},S=(e,n,r,a)=>{if(n&&typeof n=="object"||typeof n=="function")for(let t of L(n))!O.call(e,t)&&t!==r&&b(e,t,{get:()=>n[t],enumerable:!(a=B(n,t))||a.enumerable});return e};var T=e=>S(b({},"__esModule",{value:!0}),e);var q={};j(q,{applyDisableCycle:()=>N,applyEnableCycle:()=>_,assertKnownCycle:()=>k,knownCycles:()=>P,parsePlatformsFlag:()=>x,reconcileCyclesInLayer:()=>Y,setCycleEnabledInLayer:()=>E});module.exports=T(q);var g=require("fs"),h=require("path"),p=require("../clients/registry"),v=require("../lib/logger"),l=require("../lib/output"),d=require("../lib/config");function P(){return[...d.ALL_CYCLES]}c(P,"knownCycles");function k(e){const n=P();if(!n.includes(e))throw new Error(`Unknown cycle '${e}'. Available: ${n.join(", ")}.`)}c(k,"assertKnownCycle");function x(e){const n=P(),r=e.split(",").map(i=>i.trim().toLowerCase()).filter(i=>i.length>0),a=Array.from(new Set(r)),t=a.filter(i=>!n.includes(i));if(t.length>0)throw new Error(`Unknown platform(s): ${t.join(", ")}. Available: ${n.join(", ")}.`);return a}c(x,"parsePlatformsFlag");function m(e){if(!(0,g.existsSync)(e))return{};try{return JSON.parse((0,g.readFileSync)(e,"utf-8"))}catch(n){throw v.logger.debug(`failed to read ${e}: ${n}`),new Error(`Config at ${e} is not valid JSON: ${n instanceof Error?n.message:n}`)}}c(m,"readLayerConfig");function w(e,n){(0,g.mkdirSync)((0,h.join)(e,".."),{recursive:!0}),(0,g.writeFileSync)(e,JSON.stringify(n,null,2)+`
2
- `)}c(w,"writeLayerConfig");function I(e,n){return n===void 0||n==="all"?(0,p.detectClients)(e):(0,p.resolveTargetClients)(e,n)}c(I,"resolveAffectedClients");function A(e,n){const r=I(e,n);if(r.length===0)return v.logger.debug(`cycle-toggle: no clients detected in ${e} \u2014 skipping artifact rerender`),[];const a=(0,d.loadConfig)(e),t=[];for(const i of r)try{i.install(e,a),t.push(i.name)}catch(o){v.logger.debug(`cycle-toggle: failed to rerender ${i.name}: ${o}`)}return t}c(A,"rerenderClientArtifacts");function $(e,n){const r=e[n];if(r&&typeof r=="object"&&!Array.isArray(r))return r}c($,"readCycleBlock");function R(e,n){const r=n==="project"?"":` --${n}`;return`ironbee ${e} disable${r}`}c(R,"disableCommandHint");function _(e,n,r,a){k(e);const t=(0,d.getTargetConfigPath)(r,n),i=m(t),o=$(i,e),f=o!==void 0&&(o.enable===!1||Array.isArray(o.verifyPatterns)&&o.verifyPatterns.length===0),s=d.CYCLES_ENABLED_BY_DEFAULT.has(e);if(!f&&(o!==void 0||s)){console.log(`${l.pc.yellow("\u26A0")} ${e} cycle is already enabled in ${r} config (${l.pc.dim(t)}).
3
- Run ${l.pc.cyan(R(e,r))} first to reset, then re-run enable-${e}.`);return}if(o!==void 0){const C={...o};delete C.enable,Array.isArray(C.verifyPatterns)&&C.verifyPatterns.length===0&&delete C.verifyPatterns,i[e]=C}else s||(i[e]={});w(t,i);const u=A(n,a);console.log(`${l.pc.green("\u2713")} Enabled ${l.pc.bold(e)} cycle verification in ${r} config (${l.pc.dim(t)}).`),console.log(` ${l.pc.dim("Default verifyPatterns from CLI "+e+" cycle apply automatically.")}`),u.length>0&&console.log(` ${l.pc.dim("Re-rendered artifacts (MCP servers, permissions, skill/rule/command md) for: "+u.join(", "))}`),console.log(`
4
- ${l.pc.dim("To customize, set")} ${l.pc.cyan(`${e}.verifyPatterns`)} ${l.pc.dim("(replaces defaults)")} ${l.pc.dim("or")} ${l.pc.cyan(`${e}.additionalVerifyPatterns`)} ${l.pc.dim("(appended).")}`)}c(_,"applyEnableCycle");function N(e,n,r,a){k(e);const t=(0,d.getTargetConfigPath)(r,n),i=m(t),o=$(i,e),f={...o??{}};if(Array.isArray(f.verifyPatterns)&&f.verifyPatterns.length===0&&delete f.verifyPatterns,f.enable=!1,o!==void 0&&o.enable===!1&&Object.keys(o).length===Object.keys(f).length&&Object.keys(f).every(u=>o[u]===f[u])){console.log(`${l.pc.dim("\xB7")} ${e} cycle already disabled in ${r} config (no-op).`);return}i[e]=f,w(t,i);const y=A(n,a);console.log(`${l.pc.green("\u2713")} Disabled ${l.pc.bold(e)} cycle verification in ${r} config (${l.pc.dim(t)}).`),console.log(` ${l.pc.dim("Customizations of alwaysRequired, evidencePaths, additionalVerifyPatterns are preserved.")}`),y.length>0&&console.log(` ${l.pc.dim("Re-rendered artifacts (MCP servers, permissions, skill/rule/command md) for: "+y.join(", "))}`)}c(N,"applyDisableCycle");function E(e,n,r,a=!1){k(n);const t=JSON.stringify(e[n]??null),i=$(e,n),o=d.CYCLES_ENABLED_BY_DEFAULT.has(n);if(r){const s={...i??{}};delete s.enable,Array.isArray(s.verifyPatterns)&&s.verifyPatterns.length===0&&delete s.verifyPatterns,a?(s.enable=!0,e[n]=s):i!==void 0?Object.keys(s).length===0&&o?delete e[n]:e[n]=s:o||(e[n]={})}else{const s={...i??{}};Array.isArray(s.verifyPatterns)&&s.verifyPatterns.length===0&&delete s.verifyPatterns,s.enable=!1,e[n]=s}const f=JSON.stringify(e[n]??null);return t!==f}c(E,"setCycleEnabledInLayer");function F(e,n){const r=$(e,n);return r===void 0?!1:r.enable===!1||Array.isArray(r.verifyPatterns)&&r.verifyPatterns.length===0}c(F,"layerDisablesCycle");function J(e,n,r){const a=d.CONFIG_TARGETS_BY_PRECEDENCE.indexOf(n);for(let t=0;t<a;t++){const i=m((0,d.getTargetConfigPath)(d.CONFIG_TARGETS_BY_PRECEDENCE[t],e));if(F(i,r))return!0}return!1}c(J,"lowerLayerDisablesCycle");function Y(e,n,r){const a=(0,d.getTargetConfigPath)(n,e),t=m(a),i=(0,d.loadConfig)(e),o=[];for(const f of P()){const s=r.includes(f),y=(0,d.isCyclePatternsActive)(i,f);if(s===y)continue;const u=s&&J(e,n,f);E(t,f,s,u)&&o.push(f)}return o.length>0&&w(a,t),o}c(Y,"reconcileCyclesInLayer");0&&(module.exports={applyDisableCycle,applyEnableCycle,assertKnownCycle,knownCycles,parsePlatformsFlag,reconcileCyclesInLayer,setCycleEnabledInLayer});
1
+ "use strict";var b=Object.defineProperty;var B=Object.getOwnPropertyDescriptor;var L=Object.getOwnPropertyNames;var O=Object.prototype.hasOwnProperty;var c=(e,n)=>b(e,"name",{value:n,configurable:!0});var j=(e,n)=>{for(var r in n)b(e,r,{get:n[r],enumerable:!0})},S=(e,n,r,f)=>{if(n&&typeof n=="object"||typeof n=="function")for(let t of L(n))!O.call(e,t)&&t!==r&&b(e,t,{get:()=>n[t],enumerable:!(f=B(n,t))||f.enumerable});return e};var T=e=>S(b({},"__esModule",{value:!0}),e);var q={};j(q,{applyDisableCycle:()=>N,applyEnableCycle:()=>_,assertKnownCycle:()=>k,knownCycles:()=>P,parsePlatformsFlag:()=>x,reconcileCyclesInLayer:()=>Y,setCycleEnabledInLayer:()=>h});module.exports=T(q);var u=require("fs"),E=require("path"),p=require("../clients/registry"),v=require("../lib/logger"),s=require("../lib/output"),a=require("../lib/config");function P(){return[...a.ALL_CYCLES]}c(P,"knownCycles");function k(e){const n=P();if(!n.includes(e))throw new Error(`Unknown cycle '${e}'. Available: ${n.join(", ")}.`)}c(k,"assertKnownCycle");function x(e){const n=P(),r=e.split(",").map(i=>i.trim().toLowerCase()).filter(i=>i.length>0),f=Array.from(new Set(r)),t=f.filter(i=>!n.includes(i));if(t.length>0)throw new Error(`Unknown platform(s): ${t.join(", ")}. Available: ${n.join(", ")}.`);return f}c(x,"parsePlatformsFlag");function m(e){if(!(0,u.existsSync)(e))return{};try{return JSON.parse((0,u.readFileSync)(e,"utf-8"))}catch(n){throw v.logger.debug(`failed to read ${e}: ${n}`),new Error(`Config at ${e} is not valid JSON: ${n instanceof Error?n.message:n}`)}}c(m,"readLayerConfig");function w(e,n){(0,u.mkdirSync)((0,E.join)(e,".."),{recursive:!0}),(0,u.writeFileSync)(e,JSON.stringify(n,null,2)+`
2
+ `)}c(w,"writeLayerConfig");function I(e,n){return n===void 0||n==="all"?(0,p.detectClients)(e):(0,p.resolveTargetClients)(e,n)}c(I,"resolveAffectedClients");function A(e,n){const r=I(e,n);if(r.length===0)return v.logger.debug(`cycle-toggle: no clients detected in ${e} \u2014 skipping artifact rerender`),[];const f=(0,a.loadConfig)(e),t=[];for(const i of r)try{i.install(e,f),t.push(i.name)}catch(l){v.logger.debug(`cycle-toggle: failed to rerender ${i.name}: ${l}`)}return t}c(A,"rerenderClientArtifacts");function $(e,n){const r=e[n];if(r&&typeof r=="object"&&!Array.isArray(r))return r}c($,"readCycleBlock");function R(e,n){const r=n==="project"?"":` --${n}`;return`ironbee ${e} disable${r}`}c(R,"disableCommandHint");function _(e,n,r,f){k(e);const t=(0,a.getTargetConfigPath)(r,n),i=m(t),l=$(i,e),d=l!==void 0&&(l.enable===!1||Array.isArray(l.verifyPatterns)&&l.verifyPatterns.length===0),o=a.CYCLES_ENABLED_BY_DEFAULT.has(e);if(!d&&(l!==void 0||o)){console.log(`${s.pc.yellow("\u26A0")} ${e} cycle is already enabled in ${r} config (${s.pc.dim(t)}).
3
+ Run ${s.pc.cyan(R(e,r))} first to reset, then re-run enable-${e}.`);return}if(l!==void 0){const C={...l};delete C.enable,Array.isArray(C.verifyPatterns)&&C.verifyPatterns.length===0&&delete C.verifyPatterns,i[e]=C}else o||(i[e]={});w(t,i);const y=A(n,f);console.log(`${s.pc.green("\u2713")} Enabled ${s.pc.bold(e)} cycle verification in ${r} config (${s.pc.dim(t)}).`),console.log(` ${s.pc.dim("Default verifyPatterns from CLI "+e+" cycle apply automatically.")}`),y.length>0&&console.log(` ${s.pc.dim("Re-rendered artifacts (MCP servers, permissions, skill/rule/command md) for: "+y.join(", "))}`),console.log(`
4
+ ${s.pc.dim("To customize, set")} ${s.pc.cyan(`${e}.verifyPatterns`)} ${s.pc.dim("(replaces defaults)")} ${s.pc.dim("or")} ${s.pc.cyan(`${e}.additionalVerifyPatterns`)} ${s.pc.dim("(appended).")}`)}c(_,"applyEnableCycle");function N(e,n,r,f){k(e);const t=(0,a.getTargetConfigPath)(r,n),i=m(t),l=$(i,e),d={...l??{}};if(Array.isArray(d.verifyPatterns)&&d.verifyPatterns.length===0&&delete d.verifyPatterns,d.enable=!1,l!==void 0&&l.enable===!1&&Object.keys(l).length===Object.keys(d).length&&Object.keys(d).every(y=>l[y]===d[y])){console.log(`${s.pc.dim("\xB7")} ${e} cycle already disabled in ${r} config (no-op).`);return}i[e]=d,w(t,i);const g=A(n,f);console.log(`${s.pc.green("\u2713")} Disabled ${s.pc.bold(e)} cycle verification in ${r} config (${s.pc.dim(t)}).`),console.log(` ${s.pc.dim("Customizations of alwaysRequired, evidencePaths, additionalVerifyPatterns are preserved.")}`),g.length>0&&console.log(` ${s.pc.dim("Re-rendered artifacts (MCP servers, permissions, skill/rule/command md) for: "+g.join(", "))}`)}c(N,"applyDisableCycle");function h(e,n,r,f=!1){k(n);const t=JSON.stringify(e[n]??null),i=$(e,n),l=a.CYCLES_ENABLED_BY_DEFAULT.has(n);if(r){const o={...i??{}};delete o.enable,Array.isArray(o.verifyPatterns)&&o.verifyPatterns.length===0&&delete o.verifyPatterns,f?(o.enable=!0,e[n]=o):i!==void 0?Object.keys(o).length===0&&l?delete e[n]:e[n]=o:l||(e[n]={})}else{const o={...i??{}};Array.isArray(o.verifyPatterns)&&o.verifyPatterns.length===0&&delete o.verifyPatterns,o.enable=!1,e[n]=o}const d=JSON.stringify(e[n]??null);return t!==d}c(h,"setCycleEnabledInLayer");function F(e,n){const r=$(e,n);return r===void 0?!1:r.enable===!1||Array.isArray(r.verifyPatterns)&&r.verifyPatterns.length===0}c(F,"layerDisablesCycle");function J(e,n,r){const f=a.CONFIG_TARGETS_BY_PRECEDENCE.indexOf(n);for(let t=0;t<f;t++){const i=m((0,a.getTargetConfigPath)(a.CONFIG_TARGETS_BY_PRECEDENCE[t],e));if(F(i,r))return!0}return!1}c(J,"lowerLayerDisablesCycle");function Y(e,n,r,f=!1){const t=(0,a.getTargetConfigPath)(n,e),i=m(t),l=(0,a.loadConfig)(e),d=[];for(const o of P()){const g=r.includes(o);if(f){h(i,o,g,g)&&d.push(o);continue}const y=(0,a.isCyclePatternsActive)(l,o);if(g===y)continue;const C=g&&J(e,n,o);h(i,o,g,C)&&d.push(o)}return d.length>0&&w(t,i),d}c(Y,"reconcileCyclesInLayer");0&&(module.exports={applyDisableCycle,applyEnableCycle,assertKnownCycle,knownCycles,parsePlatformsFlag,reconcileCyclesInLayer,setCycleEnabledInLayer});
@@ -1,23 +1,24 @@
1
- "use strict";var f=Object.defineProperty;var S=Object.getOwnPropertyDescriptor;var N=Object.getOwnPropertyNames;var j=Object.prototype.hasOwnProperty;var y=(i,e)=>f(i,"name",{value:e,configurable:!0});var C=(i,e)=>{for(var t in e)f(i,t,{get:e[t],enumerable:!0})},k=(i,e,t,o)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of N(e))!j.call(i,n)&&n!==t&&f(i,n,{get:()=>e[n],enumerable:!(o=S(e,n))||o.enumerable});return i};var P=i=>k(f({},"__esModule",{value:!0}),i);var D={};C(D,{hookCommand:()=>r});module.exports=P(D);var h=require("commander"),b=require("../clients/registry"),I=require("../hooks/core/submit-verdict"),E=require("../hooks/core/verification-lifecycle"),u=require("../lib/config"),l=require("../lib/logger"),d=require("../lib/output"),m=require("../lib/stdin"),w=require("../hooks/core/session-state"),v=require("../clients/session-id"),g=require("../clients/agent-project-dir");function p(i,e){return(0,u.getVerificationEnabled)((0,u.loadConfig)(i))?!1:(l.logger.debug(`${e}: verification is disabled \u2014 silent no-op`),!0)}y(p,"isVerificationDisabled");function s(i){const e=i??process.env.IRONBEE_CLIENT;e||(process.stderr.write(`Error: client not specified. Use --client <name> or set IRONBEE_CLIENT env var.
2
- `),process.exit(1));const t=(0,b.findClient)(e);return t||(process.stderr.write(`Error: unknown client "${e}". Run \`ironbee install\` to set up.
3
- `),process.exit(1)),t}y(s,"resolveClient");const r=new h.Command("hook").description("Internal hook runners (invoked by the AI coding client)");r.command("verify-gate").description("Stop hook \u2014 gates task completion until browser verification passes").option("--client <name>","client name (overrides IRONBEE_CLIENT env var)").action(async i=>{const e=s(i.client);if(!e)return;const t=e.resolveProjectDir();await e.runVerifyGate(t)}),r.command("clear-verdict").description("PostToolUse hook \u2014 clears stale verdict after code edits").option("--client <name>","client name (overrides IRONBEE_CLIENT env var)").action(async i=>{const e=s(i.client);if(!e)return;const t=e.resolveProjectDir();await e.runClearVerdict(t)}),r.command("track-action").description("PostToolUse hook \u2014 tracks browser-devtools tool calls in actions.jsonl").option("--client <name>","client name (overrides IRONBEE_CLIENT env var)").action(async i=>{const e=s(i.client);if(!e)return;const t=e.resolveProjectDir();await e.runTrackAction(t)}),r.command("track-action-monitor").description("PostToolUse hook (monitoring-only mode) \u2014 submits send_event jobs for non-devtools tools and falls back to starting an activity if needed").option("--client <name>","client name (overrides IRONBEE_CLIENT env var)").action(async i=>{const e=s(i.client);if(!e)return;const t=e.resolveProjectDir();await e.runTrackActionMonitor(t)}),r.command("track-action-pre").description("Codex PreToolUse hook \u2014 stashes a hrtime timestamp keyed by tool_use_id so the matching PostToolUse can derive tool_call.duration (Codex hook stdin does not carry duration_ms). No-op on Claude/Cursor \u2014 their hosts provide duration natively.").option("--client <name>","client name (overrides IRONBEE_CLIENT env var)").action(async i=>{const e=s(i.client);if(!e)return;const t=e.resolveProjectDir();await e.runTrackActionPre(t)}),r.command("subagent-start").description("Codex SubagentStart hook \u2014 writes the agent_id \u2192 parent session_id bridge (codex-threads.json) so a delegated verifier sub-agent's agent-Bash commands resolve the session from CODEX_THREAD_ID. Codex-only.").option("--client <name>","client name (overrides IRONBEE_CLIENT env var)").action(async i=>{const e=s(i.client);if(!e)return;const t=e.resolveProjectDir();await e.runSubagentStart?.(t)}),r.command("subagent-stop").description("Codex SubagentStop hook \u2014 prunes the thread map entry and records the sub-agent's agent_transcript_path for the analytics sub-agent fold. Codex-only.").option("--client <name>","client name (overrides IRONBEE_CLIENT env var)").action(async i=>{const e=s(i.client);if(!e)return;const t=e.resolveProjectDir();await e.runSubagentStop?.(t)}),r.command("activity-end").description("Stop hook (monitoring-only mode) \u2014 closes the active activity and triggers a background queue flush").option("--client <name>","client name (overrides IRONBEE_CLIENT env var)").action(async i=>{const e=s(i.client);if(!e)return;const t=e.resolveProjectDir();await e.runActivityEnd(t)}),r.command("session-start").description("SessionStart hook \u2014 records session start in actions.jsonl").option("--client <name>","client name (overrides IRONBEE_CLIENT env var)").action(async i=>{const e=s(i.client);if(!e)return;const t=e.resolveProjectDir();await e.runSessionStart(t)}),r.command("session-status").description("Statusline command (Claude) \u2014 emits a session_status event and chains the user's original statusline").action(async()=>{await(0,b.findClient)("claude")?.runSessionStatus?.()}),r.command("require-verdict").description("PreToolUse hook \u2014 blocks file edits until verdict is submitted after browser tool usage").option("--client <name>","client name (overrides IRONBEE_CLIENT env var)").option("--soft","non-blocking assist-mode variant: stash file_change state but never block the edit").action(async i=>{const e=s(i.client);if(!e)return;const t=e.resolveProjectDir();await e.runRequireVerdict(t,{soft:i.soft===!0})}),r.command("session-end").description("SessionEnd hook \u2014 records session end in actions.jsonl").option("--client <name>","client name (overrides IRONBEE_CLIENT env var)").action(async i=>{const e=s(i.client);if(!e)return;const t=e.resolveProjectDir();await e.runSessionEnd(t)}),r.command("activity-start").description("UserPromptSubmit/beforeSubmitPrompt hook \u2014 starts activity tracking on each agent turn").option("--client <name>","client name (overrides IRONBEE_CLIENT env var)").action(async i=>{const e=s(i.client);if(!e)return;const t=e.resolveProjectDir();await e.runActivityStart(t)}),r.command("require-verification").description("PreToolUse hook \u2014 blocks browser tools until verification-start is called").option("--client <name>","client name (overrides IRONBEE_CLIENT env var)").option("--soft","non-blocking assist-mode variant: inject _metadata but never block the devtools call").action(async i=>{const e=s(i.client);if(!e)return;const t=e.resolveProjectDir();await e.runRequireVerification(t,{soft:i.soft===!0})}),r.command("verification-start").description("Start a verification cycle (called by agent via Bash)").action(async()=>{const i=(0,g.resolveAgentProjectDir)();if(p(i,"verification-start")){(0,d.writeAndExit)(JSON.stringify({message:"verification is disabled in this project; ignoring"})+`
1
+ "use strict";var g=Object.defineProperty;var N=Object.getOwnPropertyDescriptor;var j=Object.getOwnPropertyNames;var C=Object.prototype.hasOwnProperty;var b=(i,e)=>g(i,"name",{value:e,configurable:!0});var x=(i,e)=>{for(var t in e)g(i,t,{get:e[t],enumerable:!0})},P=(i,e,t,o)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of j(e))!C.call(i,n)&&n!==t&&g(i,n,{get:()=>e[n],enumerable:!(o=N(e,n))||o.enumerable});return i};var D=i=>P(g({},"__esModule",{value:!0}),i);var _={};x(_,{hookCommand:()=>r});module.exports=D(_);var w=require("commander"),h=require("../clients/registry"),k=require("../hooks/core/submit-verdict"),y=require("../hooks/core/verification-lifecycle"),u=require("../lib/config"),l=require("../lib/logger"),d=require("../lib/output"),m=require("../lib/stdin"),S=require("../hooks/core/session-state"),f=require("../clients/session-id"),v=require("../clients/agent-project-dir");function p(i,e){return(0,u.getVerificationEnabled)((0,u.loadConfig)(i))?!1:(l.logger.debug(`${e}: verification is disabled \u2014 silent no-op`),!0)}b(p,"isVerificationDisabled");function s(i){const e=i??process.env.IRONBEE_CLIENT;e||(process.stderr.write(`Error: client not specified. Use --client <name> or set IRONBEE_CLIENT env var.
2
+ `),process.exit(1));const t=(0,h.findClient)(e);return t||(process.stderr.write(`Error: unknown client "${e}". Run \`ironbee install\` to set up.
3
+ `),process.exit(1)),t}b(s,"resolveClient");const r=new w.Command("hook").description("Internal hook runners (invoked by the AI coding client)");r.command("verify-gate").description("Stop hook \u2014 gates task completion until browser verification passes").option("--client <name>","client name (overrides IRONBEE_CLIENT env var)").action(async i=>{const e=s(i.client);if(!e)return;const t=e.resolveProjectDir();await e.runVerifyGate(t)}),r.command("clear-verdict").description("PostToolUse hook \u2014 clears stale verdict after code edits").option("--client <name>","client name (overrides IRONBEE_CLIENT env var)").action(async i=>{const e=s(i.client);if(!e)return;const t=e.resolveProjectDir();await e.runClearVerdict(t)}),r.command("track-action").description("PostToolUse hook \u2014 tracks browser-devtools tool calls in actions.jsonl").option("--client <name>","client name (overrides IRONBEE_CLIENT env var)").action(async i=>{const e=s(i.client);if(!e)return;const t=e.resolveProjectDir();await e.runTrackAction(t)}),r.command("track-action-monitor").description("PostToolUse hook (monitoring-only mode) \u2014 submits send_event jobs for non-devtools tools and falls back to starting an activity if needed").option("--client <name>","client name (overrides IRONBEE_CLIENT env var)").action(async i=>{const e=s(i.client);if(!e)return;const t=e.resolveProjectDir();await e.runTrackActionMonitor(t)}),r.command("track-action-pre").description("Codex PreToolUse hook \u2014 stashes a hrtime timestamp keyed by tool_use_id so the matching PostToolUse can derive tool_call.duration (Codex hook stdin does not carry duration_ms). No-op on Claude/Cursor \u2014 their hosts provide duration natively.").option("--client <name>","client name (overrides IRONBEE_CLIENT env var)").action(async i=>{const e=s(i.client);if(!e)return;const t=e.resolveProjectDir();await e.runTrackActionPre(t)}),r.command("subagent-start").description("Codex SubagentStart hook \u2014 writes the agent_id \u2192 parent session_id bridge (codex-threads.json) so a delegated verifier sub-agent's agent-Bash commands resolve the session from CODEX_THREAD_ID. Codex-only.").option("--client <name>","client name (overrides IRONBEE_CLIENT env var)").action(async i=>{const e=s(i.client);if(!e)return;const t=e.resolveProjectDir();await e.runSubagentStart?.(t)}),r.command("subagent-stop").description("Codex SubagentStop hook \u2014 prunes the thread map entry and records the sub-agent's agent_transcript_path for the analytics sub-agent fold. Codex-only.").option("--client <name>","client name (overrides IRONBEE_CLIENT env var)").action(async i=>{const e=s(i.client);if(!e)return;const t=e.resolveProjectDir();await e.runSubagentStop?.(t)}),r.command("activity-end").description("Stop hook (monitoring-only mode) \u2014 closes the active activity and triggers a background queue flush").option("--client <name>","client name (overrides IRONBEE_CLIENT env var)").action(async i=>{const e=s(i.client);if(!e)return;const t=e.resolveProjectDir();await e.runActivityEnd(t)}),r.command("session-start").description("SessionStart hook \u2014 records session start in actions.jsonl").option("--client <name>","client name (overrides IRONBEE_CLIENT env var)").action(async i=>{const e=s(i.client);if(!e)return;const t=e.resolveProjectDir();await e.runSessionStart(t)}),r.command("session-status").description("Statusline command (Claude) \u2014 emits a session_status event and chains the user's original statusline").action(async()=>{await(0,h.findClient)("claude")?.runSessionStatus?.()}),r.command("require-verdict").description("PreToolUse hook \u2014 blocks file edits until verdict is submitted after browser tool usage").option("--client <name>","client name (overrides IRONBEE_CLIENT env var)").option("--soft","non-blocking assist-mode variant: stash file_change state but never block the edit").action(async i=>{const e=s(i.client);if(!e)return;const t=e.resolveProjectDir();await e.runRequireVerdict(t,{soft:i.soft===!0})}),r.command("session-end").description("SessionEnd hook \u2014 records session end in actions.jsonl").option("--client <name>","client name (overrides IRONBEE_CLIENT env var)").action(async i=>{const e=s(i.client);if(!e)return;const t=e.resolveProjectDir();await e.runSessionEnd(t)}),r.command("activity-start").description("UserPromptSubmit/beforeSubmitPrompt hook \u2014 starts activity tracking on each agent turn").option("--client <name>","client name (overrides IRONBEE_CLIENT env var)").action(async i=>{const e=s(i.client);if(!e)return;const t=e.resolveProjectDir();await e.runActivityStart(t)}),r.command("require-verification").description("PreToolUse hook \u2014 blocks browser tools until verification-start is called").option("--client <name>","client name (overrides IRONBEE_CLIENT env var)").option("--soft","non-blocking assist-mode variant: inject _metadata but never block the devtools call").action(async i=>{const e=s(i.client);if(!e)return;const t=e.resolveProjectDir();await e.runRequireVerification(t,{soft:i.soft===!0})}),r.command("verification-start").description(`Start a verification cycle (called by agent via Bash). Optional --intent flag: "fix" arms the verify-gate's fix-until-pass backstop (a fail verdict keeps blocking even in a zero-edit window); "report" or omitted = verify-only run (clears any stale intent \u2014 "report" is a tolerated alias of omitting the flag, mirroring the command's mode token).`).option("--intent <mode>",'declared intent of this verification run: "fix" or "report"').action(async i=>{const e=(0,v.resolveAgentProjectDir)();if(p(e,"verification-start")){(0,d.writeAndExit)(JSON.stringify({message:"verification is disabled in this project; ignoring"})+`
4
+ `,0);return}i.intent!==void 0&&i.intent!=="fix"&&i.intent!=="report"&&(process.stderr.write(`Error: --intent must be "fix" or "report".
5
+ `),process.exit(1));const t=i.intent;let o;try{o=JSON.parse((0,m.readStdin)())}catch{process.stderr.write(`Error: no JSON provided via stdin.
6
+ `),process.exit(1)}const n=(0,f.resolveAgentSessionId)(o,e);n||(process.stderr.write(`Error: JSON must include a "session_id" field, or run as a delegated sub-agent (your client resolves the session automatically).
7
+ `),process.exit(1));const c=`${e}/.ironbee/sessions/${n}`;(0,l.setLogFile)(`${c}/session.log`);const a=(0,u.isRecordingEnabled)(e),I=await(0,y.startVerification)({sessionId:n,sessionDir:c,actionsFile:`${c}/actions.jsonl`,recordingEnabled:a,intent:t}),E={verification_id:I.verificationId,trace_id:I.traceId};a&&(E.recording_required=!0,E.message="Recording is required. Call bdt_content_start-recording BEFORE using any other browser tools."),(0,d.writeAndExit)(JSON.stringify(E)+`
8
+ `,0)}),r.command("verification-end").description("End a verification cycle (called by agent via Bash)").action(async()=>{const i=(0,v.resolveAgentProjectDir)();if(p(i,"verification-end")){(0,d.writeAndExit)(JSON.stringify({message:"verification is disabled in this project; ignoring"})+`
4
9
  `,0);return}let e;try{e=JSON.parse((0,m.readStdin)())}catch{process.stderr.write(`Error: no JSON provided via stdin.
5
- `),process.exit(1)}const t=(0,v.resolveAgentSessionId)(e,i);t||(process.stderr.write(`Error: JSON must include a "session_id" field, or run as a delegated sub-agent (your client resolves the session automatically).
6
- `),process.exit(1));const o=`${i}/.ironbee/sessions/${t}`;(0,l.setLogFile)(`${o}/session.log`);const n=(0,u.isRecordingEnabled)(i),c=await(0,E.startVerification)({sessionId:t,sessionDir:o,actionsFile:`${o}/actions.jsonl`,recordingEnabled:n}),a={verification_id:c.verificationId,trace_id:c.traceId};n&&(a.recording_required=!0,a.message="Recording is required. Call bdt_content_start-recording BEFORE using any other browser tools."),(0,d.writeAndExit)(JSON.stringify(a)+`
7
- `,0)}),r.command("verification-end").description("End a verification cycle (called by agent via Bash)").action(async()=>{const i=(0,g.resolveAgentProjectDir)();if(p(i,"verification-end")){(0,d.writeAndExit)(JSON.stringify({message:"verification is disabled in this project; ignoring"})+`
8
- `,0);return}let e;try{e=JSON.parse((0,m.readStdin)())}catch{process.stderr.write(`Error: no JSON provided via stdin.
9
- `),process.exit(1)}const t=(0,v.resolveAgentSessionId)(e,i);t||(process.stderr.write(`Error: JSON must include a "session_id" field, or run as a delegated sub-agent (your client resolves the session automatically).
10
- `),process.exit(1));const o=`${i}/.ironbee/sessions/${t}`;(0,l.setLogFile)(`${o}/session.log`);const n=await(0,E.endVerification)({sessionId:t,sessionDir:o,actionsFile:`${o}/actions.jsonl`});n.success?(0,d.writeAndExit)(JSON.stringify({verification_id:n.verificationId,trace_id:n.traceId})+`
10
+ `),process.exit(1)}const t=(0,f.resolveAgentSessionId)(e,i);t||(process.stderr.write(`Error: JSON must include a "session_id" field, or run as a delegated sub-agent (your client resolves the session automatically).
11
+ `),process.exit(1));const o=`${i}/.ironbee/sessions/${t}`;(0,l.setLogFile)(`${o}/session.log`);const n=await(0,y.endVerification)({sessionId:t,sessionDir:o,actionsFile:`${o}/actions.jsonl`});n.success?(0,d.writeAndExit)(JSON.stringify({verification_id:n.verificationId,trace_id:n.traceId})+`
11
12
  `,0):(process.stderr.write(n.message+`
12
- `),process.exit(1))}),r.command("submit-verdict").description("Submit verification verdict (called by agent via Bash)").option("--project-dir <dir>","project directory (overrides env vars)").action(async i=>{const e=(0,g.resolveAgentProjectDir)(i.projectDir);if(p(e,"submit-verdict")){(0,d.writeAndExit)(`verification is disabled in this project; verdict ignored
13
+ `),process.exit(1))}),r.command("submit-verdict").description("Submit verification verdict (called by agent via Bash)").option("--project-dir <dir>","project directory (overrides env vars)").action(async i=>{const e=(0,v.resolveAgentProjectDir)(i.projectDir);if(p(e,"submit-verdict")){(0,d.writeAndExit)(`verification is disabled in this project; verdict ignored
13
14
  `,0);return}let t;try{t=(0,m.readStdin)()}catch{process.stderr.write(`Error: no verdict JSON provided via stdin.
14
15
  `),process.exit(1)}let o;try{o=JSON.parse(t)}catch{process.stderr.write(`Error: verdict is not valid JSON.
15
- `),process.exit(1)}const n=(0,v.resolveAgentSessionId)(o,e);n||(process.stderr.write(`Error: verdict JSON must include a "session_id" field, or run as a delegated sub-agent (your client resolves the session automatically).
16
- `),process.exit(1));const c=`${e}/.ironbee/sessions/${n}`;(0,l.setLogFile)(`${c}/session.log`);const a=await(0,I.runSubmitVerdict)({sessionId:n,sessionDir:c,verdictFile:`${c}/verdict.json`,actionsFile:`${c}/actions.jsonl`,verdictJson:t,projectDir:e});a.success?(0,d.writeAndExit)(a.message+`
16
+ `),process.exit(1)}const n=(0,f.resolveAgentSessionId)(o,e);n||(process.stderr.write(`Error: verdict JSON must include a "session_id" field, or run as a delegated sub-agent (your client resolves the session automatically).
17
+ `),process.exit(1));const c=`${e}/.ironbee/sessions/${n}`;(0,l.setLogFile)(`${c}/session.log`);const a=await(0,k.runSubmitVerdict)({sessionId:n,sessionDir:c,verdictFile:`${c}/verdict.json`,actionsFile:`${c}/actions.jsonl`,verdictJson:t,projectDir:e});a.success?(0,d.writeAndExit)(a.message+`
17
18
  `,0):(process.stderr.write(a.message+`
18
- `),process.exit(1))}),r.command("record-fix").description("Record what was fixed after a fail verdict (called by agent via Bash). Stashed locally in state.json; merged into the next pass verdict's fixes. Emits no collector event.").action(async()=>{const i=(0,g.resolveAgentProjectDir)();if(p(i,"record-fix")){(0,d.writeAndExit)(JSON.stringify({message:"verification is disabled in this project; ignoring"})+`
19
+ `),process.exit(1))}),r.command("record-fix").description("Record what was fixed after a fail verdict (called by agent via Bash). Stashed locally in state.json; merged into the next pass verdict's fixes. Emits no collector event.").action(async()=>{const i=(0,v.resolveAgentProjectDir)();if(p(i,"record-fix")){(0,d.writeAndExit)(JSON.stringify({message:"verification is disabled in this project; ignoring"})+`
19
20
  `,0);return}let e;try{e=JSON.parse((0,m.readStdin)())}catch{process.stderr.write(`Error: no JSON provided via stdin.
20
- `),process.exit(1)}const t=(0,v.resolveAgentSessionId)(e,i);t||(process.stderr.write(`Error: JSON must include a "session_id" field, or run as a delegated sub-agent (your client resolves the session automatically).
21
+ `),process.exit(1)}const t=(0,f.resolveAgentSessionId)(e,i);t||(process.stderr.write(`Error: JSON must include a "session_id" field, or run as a delegated sub-agent (your client resolves the session automatically).
21
22
  `),process.exit(1));const o=e.fixes,n=Array.isArray(o)?o.filter(a=>typeof a=="string"&&a.length>0):[];n.length===0&&(process.stderr.write(`Error: JSON must include a non-empty "fixes" array of strings.
22
- `),process.exit(1));const c=`${i}/.ironbee/sessions/${t}`;(0,l.setLogFile)(`${c}/session.log`),(0,w.addPendingFixes)(c,n),l.logger.debug(`record-fix: session=${t} recorded ${n.length} fix note(s)`),(0,d.writeAndExit)(JSON.stringify({recorded:n.length})+`
23
+ `),process.exit(1));const c=`${i}/.ironbee/sessions/${t}`;(0,l.setLogFile)(`${c}/session.log`),(0,S.addPendingFixes)(c,n),l.logger.debug(`record-fix: session=${t} recorded ${n.length} fix note(s)`),(0,d.writeAndExit)(JSON.stringify({recorded:n.length})+`
23
24
  `,0)});0&&(module.exports={hookCommand});
@@ -1 +1 @@
1
- "use strict";var u=Object.defineProperty;var E=Object.getOwnPropertyDescriptor;var P=Object.getOwnPropertyNames;var x=Object.prototype.hasOwnProperty;var d=(i,e)=>u(i,"name",{value:e,configurable:!0});var D=(i,e)=>{for(var o in e)u(i,o,{get:e[o],enumerable:!0})},N=(i,e,o,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of P(e))!x.call(i,s)&&s!==o&&u(i,s,{get:()=>e[s],enumerable:!(r=E(e,s))||r.enumerable});return i};var L=i=>N(u({},"__esModule",{value:!0}),i);var A={};D(A,{CYCLE_HINTS:()=>k,installCommand:()=>T,runInstallAll:()=>v});module.exports=L(A);var I=require("commander"),w=require("fs"),S=require("path"),l=require("../clients/registry"),b=require("../clients/claude"),c=require("../lib/config"),t=require("../lib/output"),C=require("../lib/projects-registry"),p=require("../lib/prompt"),y=require("../lib/telemetry"),h=require("./cycle-toggle");const k={browser:"web UI \xB7 DOM \xB7 console \xB7 a11y \xB7 screenshots \xB7 recording",node:"Node.js runtime \xB7 tracepoints \xB7 logpoints \xB7 exceptions \xB7 variables \xB7 logs",backend:"HTTP \xB7 gRPC \xB7 GraphQL \xB7 WebSocket \xB7 DB \xB7 logs"};async function M(i,e){if(e!==void 0)return e;const o=(0,c.loadConfig)(i);if((0,c.getVerificationMode)(o)==="monitor"||!(0,p.isInteractive)())return;const r=c.ALL_CYCLES.map(n=>({label:n,hint:k[n]})),s=c.ALL_CYCLES.map((n,m)=>(0,c.isCycleEnabled)(o,n)?m:-1).filter(n=>n>=0);return(await(0,p.promptMultiSelect)("Which platforms should require verification?",r,s)).map(n=>c.ALL_CYCLES[n])}d(M,"resolvePlatformSelection");function j(i,e){if(e===void 0)return;const o=(0,h.reconcileCyclesInLayer)(i,"project",e),r=e.length>0?t.pc.bold(e.join(", ")):t.pc.dim("none (monitoring tools only)"),s=o.length>0?t.pc.dim(` \u2014 updated: ${o.join(", ")}`):"";t.log.info(`Platforms: ${r}${s}`)}d(j,"applyPlatformSelection");function R(i,e){return e!==void 0?(0,l.resolveTargetClients)(i.path,e):(0,l.detectClients)(i.path)}d(R,"clientsForBatchEntry");async function v(i){const e=(0,l.listActiveProjects)();if(e.length===0)return t.log.info("No registered projects to install. Run `ironbee install` somewhere first."),{failures:0,total:0};t.log.info(`Installing across ${t.pc.bold(String(e.length))} registered project(s)\u2026`),t.log.blank();let o=0;const r=[];for(const a of e)try{const n=R(a,i.client);if(n.length===0){t.log.warn(`Skipping ${a.path} \u2014 no resolvable clients`);continue}const m=n.map(f=>f.name);t.log.label("project",t.pc.dim(a.path)),t.log.step(`installing ${t.pc.bold(m.join(", "))}`),(0,b.prepareIronBeeDir)(a.path),j(a.path,i.platforms);for(const f of n)f.install(a.path);(0,C.upsertProject)(a.path),r.push(...m)}catch(n){o++,t.log.error(` ${a.path}: ${n instanceof Error?n.message:String(n)}`)}t.log.blank(),o===0?t.log.success(`Installed across ${e.length} project(s). ${t.pc.dim("Restart your AI coding client(s) to apply.")}`):t.log.warn(`Completed with ${o} failure(s) \u2014 see above.`);const s=Array.from(new Set(r));return s.length>0&&await(0,y.trackInstall)(s),{failures:o,total:e.length}}d(v,"runInstallAll");const T=new I.Command("install").description("Install IronBee hooks and guidance files into a project. Use --all to install across every registered project (e.g. after a global config change).").argument("[project-dir]","target project directory",".").option("--client <name>",`client to install for (${(0,l.clientNames)()}), or "all"`).option("--all","Install across every project in the user-home inventory (~/.ironbee/projects.json) instead of just one. The [project-dir] argument is ignored when --all is set.").option("--platforms <list>",`comma-separated platforms to enable (${c.ALL_CYCLES.join(", ")}); skips the interactive picker. Empty ("") disables all. Default: interactive picker (pre-checked from current config), browser-only on a fresh non-interactive install.`).action(async(i,e)=>{let o;if(e.platforms!==void 0)try{o=(0,h.parsePlatformsFlag)(e.platforms)}catch(n){t.log.error(n instanceof Error?n.message:String(n)),process.exit(1)}if(e.all===!0){(await v({client:e.client,platforms:o})).failures>0&&process.exit(1);return}const r=(0,S.resolve)(i);(0,w.existsSync)(r)||(t.log.error(`Directory not found: ${r}`),process.exit(1));let s;if(e.client!==void 0)try{s=(0,l.resolveTargetClients)(r,e.client)}catch(n){t.log.error(n instanceof Error?n.message:String(n)),process.exit(1)}else{const n=(0,l.detectClients)(r);if(n.length>0){s=n;const m=n.map(f=>f.name).join(", ");t.log.info(`Detected client(s): ${t.pc.bold(m)}`)}else if((0,p.isInteractive)()){const m=[...l.REGISTERED_CLIENTS.map(g=>g.name),"all"],f=m.map(g=>({label:g,hint:g==="all"?"every registered client":void 0}));t.log.warn(`No client detected in ${t.pc.dim(r)}.`);const $=await(0,p.promptSelect)("Which client(s) to install for?",f,0);s=(0,l.resolveTargetClients)(r,m[$])}else t.log.warn(`No client detected. Defaulting to: ${l.REGISTERED_CLIENTS[0].name}`),s=[l.REGISTERED_CLIENTS[0]]}const a=await M(r,o);t.log.blank(),(0,b.prepareIronBeeDir)(r),j(r,a);for(const n of s)n.install(r);(0,C.upsertProject)(r),t.log.blank(),t.log.label("project",t.pc.dim(r)),t.log.blank(),t.log.success(`IronBee installed. ${t.pc.dim("Restart your AI coding client to activate the hooks.")}`),await(0,y.trackInstall)(s.map(n=>n.name),r)});0&&(module.exports={CYCLE_HINTS,installCommand,runInstallAll});
1
+ "use strict";var y=Object.defineProperty;var V=Object.getOwnPropertyDescriptor;var A=Object.getOwnPropertyNames;var N=Object.prototype.hasOwnProperty;var u=(i,e)=>y(i,"name",{value:e,configurable:!0});var T=(i,e)=>{for(var o in e)y(i,o,{get:e[o],enumerable:!0})},R=(i,e,o,a)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of A(e))!N.call(i,r)&&r!==o&&y(i,r,{get:()=>e[r],enumerable:!(a=V(e,r))||a.enumerable});return i};var B=i=>R(y({},"__esModule",{value:!0}),i);var q={};T(q,{CYCLE_HINTS:()=>D,installCommand:()=>F,runInstallAll:()=>v,syncSchemaIfChanged:()=>W});module.exports=B(q);var $=require("commander"),P=require("fs"),x=require("path"),c=require("../clients/registry"),M=require("../clients/claude"),m=require("../lib/config"),t=require("../lib/output"),j=require("../lib/projects-registry"),f=require("../lib/prompt"),w=require("../lib/telemetry"),S=require("../lib/install-version"),p=require("../lib/schema-sync"),C=require("./cycle-toggle"),l=require("./mode-select");const D={browser:"web UI \xB7 DOM \xB7 console \xB7 a11y \xB7 screenshots \xB7 recording",node:"Node.js runtime \xB7 tracepoints \xB7 logpoints \xB7 exceptions \xB7 variables \xB7 logs",backend:"HTTP \xB7 gRPC \xB7 GraphQL \xB7 WebSocket \xB7 DB \xB7 logs"};async function _(i,e){if(e!==void 0)return e;if(!(0,f.isInteractive)())return;const o=(0,l.resolveInstallDefaultMode)(i),a=Math.max(0,l.ALL_MODES.indexOf(o)),r=l.ALL_MODES.map(d=>({label:l.MODE_LABELS[d],hint:l.MODE_HINTS[d]})),s=await(0,f.promptSelect)("Which verification mode?",r,a);return l.ALL_MODES[s]}u(_,"resolveModeSelection");function k(i,e){if(e===void 0)return;const a=(0,l.applyModeToLayer)(i,"project",e)?"":t.pc.dim(" (unchanged)");t.log.info(`Mode: ${t.pc.bold(l.MODE_LABELS[e])}${a}`)}u(k,"applyModeSelection");async function O(i,e,o){if(e!==void 0)return e;const a=(0,m.loadConfig)(i);if((o??(0,m.getVerificationMode)(a))==="monitor"||!(0,f.isInteractive)())return;const s=m.ALL_CYCLES.map(n=>({label:n,hint:D[n]})),d=m.ALL_CYCLES.map((n,g)=>(0,m.isCyclePatternsActive)(a,n)?g:-1).filter(n=>n>=0);return(await(0,f.promptMultiSelect)("Which platforms should require verification?",s,d)).map(n=>m.ALL_CYCLES[n])}u(O,"resolvePlatformSelection");function E(i,e){if(e===void 0)return;const o=(0,C.reconcileCyclesInLayer)(i,"project",e,!0),a=e.length>0?t.pc.bold(e.join(", ")):t.pc.dim("none (monitoring tools only)"),r=o.length>0?t.pc.dim(` \u2014 updated: ${o.join(", ")}`):"";t.log.info(`Platforms: ${a}${r}`)}u(E,"applyPlatformSelection");function H(i,e){return e!==void 0?(0,c.resolveTargetClients)(i.path,e):(0,c.detectClients)(i.path)}u(H,"clientsForBatchEntry");async function v(i){const e=(0,c.listActiveProjects)();if(e.length===0)return t.log.info("No registered projects to install. Run `ironbee install` somewhere first."),{failures:0,total:0};t.log.info(`Installing across ${t.pc.bold(String(e.length))} registered project(s)\u2026`),t.log.blank();let o=0;const a=[];for(const s of e)try{const d=H(s,i.client);if(d.length===0){t.log.warn(`Skipping ${s.path} \u2014 no resolvable clients`);continue}const h=d.map(n=>n.name);t.log.label("project",t.pc.dim(s.path)),t.log.step(`installing ${t.pc.bold(h.join(", "))}`),(0,M.prepareIronBeeDir)(s.path),k(s.path,i.mode),E(s.path,i.platforms);for(const n of d)n.install(s.path);(0,j.upsertProject)(s.path),a.push(...h)}catch(d){o++,t.log.error(` ${s.path}: ${d instanceof Error?d.message:String(d)}`)}t.log.blank(),o===0?t.log.success(`Installed across ${e.length} project(s). ${t.pc.dim("Restart your AI coding client(s) to apply.")}`):t.log.warn(`Completed with ${o} failure(s) \u2014 see above.`);const r=Array.from(new Set(a));return r.length>0&&await(0,w.trackInstall)(r),{failures:o,total:e.length}}u(v,"runInstallAll");async function W(){try{if((0,p.isAutoRerenderDisabled)()||(0,p.readSyncedSchemaVersion)()>=S.INSTALL_SCHEMA_VERSION)return!1;const e=(0,c.listActiveProjects)();if(e.length===0)return(0,p.writeSyncedSchemaVersion)(S.INSTALL_SCHEMA_VERSION),!1;if(!(0,f.isInteractive)())return!1;t.log.blank(),t.log.warn(`IronBee's installed setup structure changed in this version \u2014 re-rendering ${t.pc.bold(String(e.length))} registered project(s):`);for(const o of e.slice(0,10))t.log.label("project",t.pc.dim(o.path));return e.length>10&&t.log.info(t.pc.dim(` \u2026and ${e.length-10} more`)),await(0,f.promptAcknowledge)(t.pc.dim(" Press Enter to update them now\u2026 ")),await v({}),(0,p.writeSyncedSchemaVersion)(S.INSTALL_SCHEMA_VERSION),!0}catch(i){return t.log.blank(),t.log.warn(`IronBee schema sync skipped: ${i instanceof Error?i.message:String(i)}`),!1}}u(W,"syncSchemaIfChanged");const F=new $.Command("install").description("Install IronBee hooks and guidance files into a project. Use --all to install across every registered project (e.g. after a global config change).").argument("[project-dir]","target project directory",".").option("--client <name>",`client to install for (${(0,c.clientNames)()}), or "all"`).option("--all","Install across every project in the user-home inventory (~/.ironbee/projects.json) instead of just one. The [project-dir] argument is ignored when --all is set.").option("--platforms <list>",`comma-separated platforms to enable (${m.ALL_CYCLES.join(", ")}); skips the interactive picker. Empty ("") disables all. Default: interactive picker (pre-checked from current config), browser-only on a fresh non-interactive install.`).option("--mode <mode>",`verification mode (${l.ALL_MODES.join(", ")}); skips the interactive picker. enforce = full enforcement (default), assist = tools installed but not enforced, monitor = monitoring-only. Default: interactive picker (pre-selecting the current mode), untouched on a non-interactive install.`).action(async(i,e)=>{let o;if(e.platforms!==void 0)try{o=(0,C.parsePlatformsFlag)(e.platforms)}catch(n){t.log.error(n instanceof Error?n.message:String(n)),process.exit(1)}let a;if(e.mode!==void 0)try{a=(0,l.parseModeFlag)(e.mode)}catch(n){t.log.error(n instanceof Error?n.message:String(n)),process.exit(1)}if(e.all===!0){(await v({client:e.client,platforms:o,mode:a})).failures>0&&process.exit(1);return}const r=(0,x.resolve)(i);(0,P.existsSync)(r)||(t.log.error(`Directory not found: ${r}`),process.exit(1));let s;if(e.client!==void 0)try{s=(0,c.resolveTargetClients)(r,e.client)}catch(n){t.log.error(n instanceof Error?n.message:String(n)),process.exit(1)}else{const n=(0,c.detectClients)(r);if(n.length>0){s=n;const g=n.map(I=>I.name).join(", ");t.log.info(`Detected client(s): ${t.pc.bold(g)}`)}else if((0,f.isInteractive)()){const g=[...c.REGISTERED_CLIENTS.map(b=>b.name),"all"],I=g.map(b=>({label:b,hint:b==="all"?"every registered client":void 0}));t.log.warn(`No client detected in ${t.pc.dim(r)}.`);const L=await(0,f.promptSelect)("Which client(s) to install for?",I,0);s=(0,c.resolveTargetClients)(r,g[L])}else t.log.warn(`No client detected. Defaulting to: ${c.REGISTERED_CLIENTS[0].name}`),s=[c.REGISTERED_CLIENTS[0]]}const d=await _(r,a),h=await O(r,o,d);t.log.blank(),(0,M.prepareIronBeeDir)(r),k(r,d),E(r,h);for(const n of s)n.install(r);(0,j.upsertProject)(r),t.log.blank(),t.log.label("project",t.pc.dim(r)),t.log.blank(),t.log.success(`IronBee installed. ${t.pc.dim("Restart your AI coding client to activate the hooks.")}`),await(0,w.trackInstall)(s.map(n=>n.name),r)});0&&(module.exports={CYCLE_HINTS,installCommand,runInstallAll,syncSchemaIfChanged});
@@ -0,0 +1,2 @@
1
+ "use strict";var f=Object.defineProperty;var m=Object.getOwnPropertyDescriptor;var p=Object.getOwnPropertyNames;var V=Object.prototype.hasOwnProperty;var r=(o,e)=>f(o,"name",{value:e,configurable:!0});var v=(o,e)=>{for(var n in e)f(o,n,{get:e[n],enumerable:!0})},M=(o,e,n,a)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of p(e))!V.call(o,i)&&i!==n&&f(o,i,{get:()=>e[i],enumerable:!(a=m(e,i))||a.enumerable});return o};var w=o=>M(f({},"__esModule",{value:!0}),o);var E={};v(E,{ALL_MODES:()=>g,MODE_HINTS:()=>C,MODE_LABELS:()=>L,applyModeToLayer:()=>O,buildVerificationBlockForMode:()=>y,parseModeFlag:()=>S,resolveInstallDefaultMode:()=>x});module.exports=w(E);var t=require("fs"),u=require("path"),c=require("../lib/config"),d=require("../lib/logger");const g=["enforce","assist","monitor"],L={enforce:"auto verify (enforce)",assist:"assist",monitor:"monitor only"},C={enforce:"block task completion until changes are verified \u2014 full enforcement (default)",assist:"tools installed but not enforced \u2014 the agent verifies manually via /ironbee-verify",monitor:"no enforcement \u2014 only track sessions / activity / tools for analytics"};function S(o){const e=o.trim().toLowerCase();if(e==="enforce"||e==="assist"||e==="monitor")return e;throw new Error(`Unknown mode '${o}'. Available: ${g.join(", ")}.`)}r(S,"parseModeFlag");function s(o){if(!(0,t.existsSync)(o))return{};try{return JSON.parse((0,t.readFileSync)(o,"utf-8"))}catch(e){throw d.logger.debug(`failed to read ${o}: ${e}`),new Error(`Config at ${o} is not valid JSON: ${e instanceof Error?e.message:e}`)}}r(s,"readLayerConfig");function B(o,e){(0,t.mkdirSync)((0,u.join)(o,".."),{recursive:!0}),(0,t.writeFileSync)(o,JSON.stringify(e,null,2)+`
2
+ `)}r(B,"writeLayerConfig");function l(o){const e=o.verification;if(e&&typeof e=="object"&&!Array.isArray(e))return e}r(l,"readVerificationBlock");function x(o){let e,n;for(const a of["project","local"]){const i=l(s((0,c.getTargetConfigPath)(a,o)));i!==void 0&&(typeof i.enable=="boolean"&&(e=i.enable),typeof i.auto=="boolean"&&(n=i.auto))}return e===!1?"monitor":n===!1?"assist":"enforce"}r(x,"resolveInstallDefaultMode");function y(o,e){const n={...o??{}};switch(e){case"enforce":n.enable=!0,n.auto=!0;break;case"assist":n.enable=!0,n.auto=!1;break;case"monitor":n.enable=!1,delete n.auto;break}return n}r(y,"buildVerificationBlockForMode");function O(o,e,n){const a=(0,c.getTargetConfigPath)(e,o),i=s(a),b=JSON.stringify(i.verification??null);i.verification=y(l(i),n);const k=JSON.stringify(i.verification);return b===k?!1:(B(a,i),!0)}r(O,"applyModeToLayer");0&&(module.exports={ALL_MODES,MODE_HINTS,MODE_LABELS,applyModeToLayer,buildVerificationBlockForMode,parseModeFlag,resolveInstallDefaultMode});
@@ -1,5 +1,6 @@
1
- "use strict";var v=Object.defineProperty;var V=Object.getOwnPropertyDescriptor;var T=Object.getOwnPropertyNames;var R=Object.prototype.hasOwnProperty;var c=(t,e)=>v(t,"name",{value:e,configurable:!0});var j=(t,e)=>{for(var n in e)v(t,n,{get:e[n],enumerable:!0})},N=(t,e,n,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of T(e))!R.call(t,r)&&r!==n&&v(t,r,{get:()=>e[r],enumerable:!(i=V(e,r))||i.enumerable});return t};var P=t=>N(v({},"__esModule",{value:!0}),t);var X={};j(X,{ActivityAwareEvent:()=>d.ActivityAwareEvent,Event:()=>d.Event,EventType:()=>d.EventType,EventTypeValue:()=>d.EventTypeValue,FixAwareEvent:()=>d.FixAwareEvent,VerificationAwareEvent:()=>d.VerificationAwareEvent,appendAction:()=>I,baseFields:()=>F,deterministicSessionEndId:()=>D,findDurationSinceLastAction:()=>O,getFileChangesSinceLastFailVerdict:()=>H,getFileChangesSinceLastVerification:()=>B,getToolCallsSinceLastFileChange:()=>J,getToolCallsSinceLastVerification:()=>z,hasFileChangesSinceLastVerification:()=>K,hasToolCallsSinceLastVerdict:()=>W,readActionsSinceLastMarker:()=>x,resolveProjectName:()=>S,summarizeFixFileChanges:()=>Y});module.exports=P(X);var u=require("fs"),y=require("crypto"),a=require("path"),g=require("../../lib/logger"),b=require("../../lib/collector"),$=require("../../lib/config"),w=require("../../queue/submit"),k=require("../../queue/types"),C=require("../../queue/register-handlers"),d=require("../../lib/event");function S(t){let e=t,n;for(;;){const i=(0,a.join)(e,".git");if((0,u.existsSync)(i)){const s=M(i);if(s==="directory"){const o=E(i);return o||(0,a.basename)(e)}if(s==="linked-pointer"){const o=_(i),l=o!==void 0?E(o):void 0;return l||(0,a.basename)(e)}s==="worktree-pointer"&&n===void 0&&(n=U(i))}const r=(0,a.dirname)(e);if(r===e)break;e=r}return n??(0,a.basename)(t)}c(S,"resolveProjectName");function M(t){try{const e=(0,u.statSync)(t);if(e.isDirectory())return"directory";if(!e.isFile())return"other";const n=_(t);return n===void 0?"other":(0,u.existsSync)((0,a.join)(n,"commondir"))?"worktree-pointer":"linked-pointer"}catch(e){return g.logger.debug(`resolveProjectName: stat failed for ${t}: ${e instanceof Error?e.message:e}`),"other"}}c(M,"classifyGitEntry");function _(t){try{const n=(0,u.readFileSync)(t,"utf-8").trim().match(/^gitdir:\s*(.+)$/m);if(!n)return;const i=n[1].trim();return(0,a.isAbsolute)(i)?i:(0,a.resolve)((0,a.dirname)(t),i)}catch(e){g.logger.debug(`resolveProjectName: pointer at ${t} unreadable: ${e instanceof Error?e.message:e}`);return}}c(_,"readGitdirPointer");function E(t){try{const e=(0,a.join)(t,"config");if(!(0,u.existsSync)(e))return;const n=q((0,u.readFileSync)(e,"utf-8"));return n?L(n):void 0}catch(e){g.logger.debug(`resolveProjectName: failed to read git config at ${t}: ${e instanceof Error?e.message:e}`);return}}c(E,"repoNameFromGitConfig");function U(t){const e=_(t);if(e===void 0)return;const n=(0,a.join)(e,"commondir");let i;try{if((0,u.existsSync)(n)){const o=(0,u.readFileSync)(n,"utf-8").trim();i=(0,a.isAbsolute)(o)?o:(0,a.resolve)(e,o)}else i=e}catch(o){g.logger.debug(`resolveProjectName: commondir at ${n} unreadable: ${o instanceof Error?o.message:o}`);return}const r=E(i);if(r)return r;const s=(0,a.basename)(i)===".git"?(0,a.basename)((0,a.dirname)(i)):(0,a.basename)(i);return s.length>0?s:void 0}c(U,"repoNameFromWorktreePointer");function q(t){const e=t.split(/\r?\n/);let n=null,i=null,r,s;for(const o of e){const l=o.trim();if(l.length===0||l.startsWith("#")||l.startsWith(";"))continue;const p=l.match(/^\[([^\s\]]+)(?:\s+"([^"]*)")?\]$/);if(p){n=p[1].toLowerCase(),i=p[2]??null;continue}if(n!=="remote"||i===null)continue;const m=l.match(/^url\s*=\s*(.+?)\s*$/i);if(!m)continue;const f=m[1];i==="origin"&&(r=f),s===void 0&&(s=f)}return r??s}c(q,"readFirstRemoteUrl");function L(t){let e=t.trim();if(e.length===0)return;const n=e.match(/^[^/@]+@[^/:]+:(.+)$/);if(n?e=n[1]:e=e.replace(/^[a-z][a-z0-9+.-]*:\/\/[^/]*\//i,""),e=e.replace(/[?#].*$/,"").replace(/\/+$/,""),e.length===0)return;const r=(e.split("/").pop()??"").replace(/\.git$/i,"");return r.length>0?r:void 0}c(L,"repoNameFromRemoteUrl");function F(t){const e=(0,a.basename)((0,a.dirname)(t)),n=(0,a.dirname)((0,a.dirname)((0,a.dirname)((0,a.dirname)(t)))),i=(0,a.dirname)(t),{getUserEmail:r,getUsageType:s,getUsagePlan:o}=require("./session-state"),l=r(i),p=s(i),m=o(i),f={id:(0,y.randomUUID)(),session_id:e,project_name:S(n)};return l&&(f.user_email=l),p&&(f.usage_type=p),m&&(f.usage_plan=m),f}c(F,"baseFields");function D(t){const e=(0,y.createHash)("sha256").update("session_end:"+t).digest("hex");return`${e.slice(0,8)}-${e.slice(8,12)}-${e.slice(12,16)}-${e.slice(16,20)}-${e.slice(20,32)}`}c(D,"deterministicSessionEndId");async function I(t,e){const n=F(t);e.id||(e.id=n.id),e.session_id||(e.session_id=n.session_id),e.project_name||(e.project_name=n.project_name);try{(0,u.mkdirSync)((0,a.dirname)(t),{recursive:!0}),(0,u.appendFileSync)(t,JSON.stringify(e)+`
2
- `)}catch(i){g.logger.debug(`failed to append action to ${t}: ${i}`)}if(e.type!=="tool_call"){const i=(0,a.basename)((0,a.dirname)(t)),r=(0,a.dirname)((0,a.dirname)((0,a.dirname)((0,a.dirname)(t))));try{await(0,b.sendToCollector)(e,i,r)}catch(s){s instanceof b.RetriableCollectorError?G(r,i,e,s):g.logger.debug(`failed to send action to collector: ${s}`)}}}c(I,"appendAction");function G(t,e,n,i){if(!(0,$.isJobQueueEnabled)(t)){g.logger.debug(`collector fallback: jobQueue disabled, dropping type=${n.type} id=${n.id} (cause: ${i.message})`);return}try{(0,w.submit)(t,e,C.SEND_EVENT_TYPE,n),g.logger.debug(`collector fallback: enqueued type=${n.type} id=${n.id} as send_event (cause: ${i.message})`)}catch(r){if(r instanceof k.JobTooLargeError){g.logger.debug(`collector fallback: event too large for queue (${r.sizeBytes} bytes), dropping type=${n.type} id=${n.id}`);return}g.logger.debug(`collector fallback: queue submit failed for type=${n.type} id=${n.id}: ${r}`)}}c(G,"enqueueCollectorFallback");function O(t,e,n){if((0,u.existsSync)(t))try{const r=(0,u.readFileSync)(t,"utf-8").trim().split(`
3
- `).filter(s=>s.length>0);for(let s=r.length-1;s>=0;s--)try{const o=JSON.parse(r[s]);if(o.type===e&&typeof o.timestamp=="number")return n-o.timestamp}catch{}}catch(i){g.logger.debug(`failed to find duration for ${e}: ${i}`)}}c(O,"findDurationSinceLastAction");function x(t,e){return h(t,n=>n.type===e)}c(x,"readActionsSinceLastMarker");function h(t,e){if(!(0,u.existsSync)(t))return[];try{const i=(0,u.readFileSync)(t,"utf-8").trim().split(`
4
- `).filter(o=>o.length>0);let r=-1;const s=[];for(let o=0;o<i.length;o++)try{const l=JSON.parse(i[o]);s.push(l),e(l)&&(r=o)}catch{}return s.slice(r+1)}catch(n){return g.logger.debug(`failed to read actions from ${t}: ${n}`),[]}}c(h,"readActionsSinceMatch");function A(t){return t.type!=="verification_requested"?!1:t.action==="allow"}c(A,"isAllowVerificationRequested");function z(t){return h(t,A).filter(n=>n.type==="tool_call")}c(z,"getToolCallsSinceLastVerification");function J(t){return x(t,"file_change").filter(n=>n.type==="tool_call")}c(J,"getToolCallsSinceLastFileChange");function W(t){return x(t,"verdict_write").some(n=>n.type==="tool_call")}c(W,"hasToolCallsSinceLastVerdict");function K(t){return h(t,A).some(n=>n.type==="file_change")}c(K,"hasFileChangesSinceLastVerification");function B(t){return h(t,A).filter(n=>n.type==="file_change")}c(B,"getFileChangesSinceLastVerification");function Q(t){return t.type!=="verdict_write"?!1:t.verdict?.status==="fail"}c(Q,"isFailVerdictWrite");function H(t){if(!(0,u.existsSync)(t))return[];try{const i=(0,u.readFileSync)(t,"utf-8").trim().split(`
5
- `).filter(o=>o.length>0).map(o=>{try{return JSON.parse(o)}catch{return null}});let r=-1;for(let o=0;o<i.length;o++){const l=i[o];l!==null&&Q(l)&&(r=o)}if(r<0)return[];const s=[];for(let o=r+1;o<i.length;o++){const l=i[o];l!==null&&l.type==="file_change"&&s.push(l)}return s}catch(e){return g.logger.debug(`failed to read file changes since last fail verdict: ${e}`),[]}}c(H,"getFileChangesSinceLastFailVerdict");function Y(t){if(t.length===0)return[];const e=new Map,n=[];for(const i of t){const r=i.file_path;let s=e.get(r);s||(s={op:i.operation,added:0,removed:0},e.set(r,s),n.push(r)),s.op=s.op==="create"||i.operation==="create"?"create":i.operation,s.added+=typeof i.lines_added=="number"?i.lines_added:0,s.removed+=typeof i.lines_removed=="number"?i.lines_removed:0}return n.map(i=>{const r=e.get(i);return`${r.op} ${i} (+${r.added}/-${r.removed})`})}c(Y,"summarizeFixFileChanges");0&&(module.exports={ActivityAwareEvent,Event,EventType,EventTypeValue,FixAwareEvent,VerificationAwareEvent,appendAction,baseFields,deterministicSessionEndId,findDurationSinceLastAction,getFileChangesSinceLastFailVerdict,getFileChangesSinceLastVerification,getToolCallsSinceLastFileChange,getToolCallsSinceLastVerification,hasFileChangesSinceLastVerification,hasToolCallsSinceLastVerdict,readActionsSinceLastMarker,resolveProjectName,summarizeFixFileChanges});
1
+ "use strict";var v=Object.defineProperty;var V=Object.getOwnPropertyDescriptor;var T=Object.getOwnPropertyNames;var R=Object.prototype.hasOwnProperty;var c=(t,e)=>v(t,"name",{value:e,configurable:!0});var j=(t,e)=>{for(var n in e)v(t,n,{get:e[n],enumerable:!0})},N=(t,e,n,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of T(e))!R.call(t,r)&&r!==n&&v(t,r,{get:()=>e[r],enumerable:!(i=V(e,r))||i.enumerable});return t};var P=t=>N(v({},"__esModule",{value:!0}),t);var Z={};j(Z,{ActivityAwareEvent:()=>d.ActivityAwareEvent,Event:()=>d.Event,EventType:()=>d.EventType,EventTypeValue:()=>d.EventTypeValue,FixAwareEvent:()=>d.FixAwareEvent,VerificationAwareEvent:()=>d.VerificationAwareEvent,appendAction:()=>I,baseFields:()=>F,deterministicSessionEndId:()=>D,findDurationSinceLastAction:()=>G,findLastActionTimestamp:()=>J,getFileChangesSinceLastFailVerdict:()=>Y,getFileChangesSinceLastVerification:()=>Q,getToolCallsSinceLastFileChange:()=>W,getToolCallsSinceLastVerification:()=>z,hasFileChangesSinceLastVerification:()=>B,hasToolCallsSinceLastVerdict:()=>K,readActionsSinceLastMarker:()=>x,resolveProjectName:()=>C,summarizeFixFileChanges:()=>X});module.exports=P(Z);var u=require("fs"),y=require("crypto"),a=require("path"),f=require("../../lib/logger"),b=require("../../lib/collector"),$=require("../../lib/config"),w=require("../../queue/submit"),k=require("../../queue/types"),S=require("../../queue/register-handlers"),d=require("../../lib/event");function C(t){let e=t,n;for(;;){const i=(0,a.join)(e,".git");if((0,u.existsSync)(i)){const s=L(i);if(s==="directory"){const o=_(i);return o||(0,a.basename)(e)}if(s==="linked-pointer"){const o=E(i),l=o!==void 0?_(o):void 0;return l||(0,a.basename)(e)}s==="worktree-pointer"&&n===void 0&&(n=M(i))}const r=(0,a.dirname)(e);if(r===e)break;e=r}return n??(0,a.basename)(t)}c(C,"resolveProjectName");function L(t){try{const e=(0,u.statSync)(t);if(e.isDirectory())return"directory";if(!e.isFile())return"other";const n=E(t);return n===void 0?"other":(0,u.existsSync)((0,a.join)(n,"commondir"))?"worktree-pointer":"linked-pointer"}catch(e){return f.logger.debug(`resolveProjectName: stat failed for ${t}: ${e instanceof Error?e.message:e}`),"other"}}c(L,"classifyGitEntry");function E(t){try{const n=(0,u.readFileSync)(t,"utf-8").trim().match(/^gitdir:\s*(.+)$/m);if(!n)return;const i=n[1].trim();return(0,a.isAbsolute)(i)?i:(0,a.resolve)((0,a.dirname)(t),i)}catch(e){f.logger.debug(`resolveProjectName: pointer at ${t} unreadable: ${e instanceof Error?e.message:e}`);return}}c(E,"readGitdirPointer");function _(t){try{const e=(0,a.join)(t,"config");if(!(0,u.existsSync)(e))return;const n=U((0,u.readFileSync)(e,"utf-8"));return n?q(n):void 0}catch(e){f.logger.debug(`resolveProjectName: failed to read git config at ${t}: ${e instanceof Error?e.message:e}`);return}}c(_,"repoNameFromGitConfig");function M(t){const e=E(t);if(e===void 0)return;const n=(0,a.join)(e,"commondir");let i;try{if((0,u.existsSync)(n)){const o=(0,u.readFileSync)(n,"utf-8").trim();i=(0,a.isAbsolute)(o)?o:(0,a.resolve)(e,o)}else i=e}catch(o){f.logger.debug(`resolveProjectName: commondir at ${n} unreadable: ${o instanceof Error?o.message:o}`);return}const r=_(i);if(r)return r;const s=(0,a.basename)(i)===".git"?(0,a.basename)((0,a.dirname)(i)):(0,a.basename)(i);return s.length>0?s:void 0}c(M,"repoNameFromWorktreePointer");function U(t){const e=t.split(/\r?\n/);let n=null,i=null,r,s;for(const o of e){const l=o.trim();if(l.length===0||l.startsWith("#")||l.startsWith(";"))continue;const p=l.match(/^\[([^\s\]]+)(?:\s+"([^"]*)")?\]$/);if(p){n=p[1].toLowerCase(),i=p[2]??null;continue}if(n!=="remote"||i===null)continue;const m=l.match(/^url\s*=\s*(.+?)\s*$/i);if(!m)continue;const g=m[1];i==="origin"&&(r=g),s===void 0&&(s=g)}return r??s}c(U,"readFirstRemoteUrl");function q(t){let e=t.trim();if(e.length===0)return;const n=e.match(/^[^/@]+@[^/:]+:(.+)$/);if(n?e=n[1]:e=e.replace(/^[a-z][a-z0-9+.-]*:\/\/[^/]*\//i,""),e=e.replace(/[?#].*$/,"").replace(/\/+$/,""),e.length===0)return;const r=(e.split("/").pop()??"").replace(/\.git$/i,"");return r.length>0?r:void 0}c(q,"repoNameFromRemoteUrl");function F(t){const e=(0,a.basename)((0,a.dirname)(t)),n=(0,a.dirname)((0,a.dirname)((0,a.dirname)((0,a.dirname)(t)))),i=(0,a.dirname)(t),{getUserEmail:r,getUsageType:s,getUsagePlan:o}=require("./session-state"),l=r(i),p=s(i),m=o(i),g={id:(0,y.randomUUID)(),session_id:e,project_name:C(n)};return l&&(g.user_email=l),p&&(g.usage_type=p),m&&(g.usage_plan=m),g}c(F,"baseFields");function D(t){const e=(0,y.createHash)("sha256").update("session_end:"+t).digest("hex");return`${e.slice(0,8)}-${e.slice(8,12)}-${e.slice(12,16)}-${e.slice(16,20)}-${e.slice(20,32)}`}c(D,"deterministicSessionEndId");async function I(t,e){const n=F(t);e.id||(e.id=n.id),e.session_id||(e.session_id=n.session_id),e.project_name||(e.project_name=n.project_name);try{(0,u.mkdirSync)((0,a.dirname)(t),{recursive:!0}),(0,u.appendFileSync)(t,JSON.stringify(e)+`
2
+ `)}catch(i){f.logger.debug(`failed to append action to ${t}: ${i}`)}if(e.type!=="tool_call"){const i=(0,a.basename)((0,a.dirname)(t)),r=(0,a.dirname)((0,a.dirname)((0,a.dirname)((0,a.dirname)(t))));try{await(0,b.sendToCollector)(e,i,r)}catch(s){s instanceof b.RetriableCollectorError?O(r,i,e,s):f.logger.debug(`failed to send action to collector: ${s}`)}}}c(I,"appendAction");function O(t,e,n,i){if(!(0,$.isJobQueueEnabled)(t)){f.logger.debug(`collector fallback: jobQueue disabled, dropping type=${n.type} id=${n.id} (cause: ${i.message})`);return}try{(0,w.submit)(t,e,S.SEND_EVENT_TYPE,n),f.logger.debug(`collector fallback: enqueued type=${n.type} id=${n.id} as send_event (cause: ${i.message})`)}catch(r){if(r instanceof k.JobTooLargeError){f.logger.debug(`collector fallback: event too large for queue (${r.sizeBytes} bytes), dropping type=${n.type} id=${n.id}`);return}f.logger.debug(`collector fallback: queue submit failed for type=${n.type} id=${n.id}: ${r}`)}}c(O,"enqueueCollectorFallback");function G(t,e,n){if((0,u.existsSync)(t))try{const r=(0,u.readFileSync)(t,"utf-8").trim().split(`
3
+ `).filter(s=>s.length>0);for(let s=r.length-1;s>=0;s--)try{const o=JSON.parse(r[s]);if(o.type===e&&typeof o.timestamp=="number")return n-o.timestamp}catch{}}catch(i){f.logger.debug(`failed to find duration for ${e}: ${i}`)}}c(G,"findDurationSinceLastAction");function x(t,e){return h(t,n=>n.type===e)}c(x,"readActionsSinceLastMarker");function J(t){if((0,u.existsSync)(t))try{const n=(0,u.readFileSync)(t,"utf-8").trim().split(`
4
+ `).filter(i=>i.length>0);for(let i=n.length-1;i>=0;i--)try{const r=JSON.parse(n[i]);if(typeof r.timestamp=="number")return r.timestamp}catch{}}catch(e){f.logger.debug(`failed to read last action timestamp from ${t}: ${e}`)}}c(J,"findLastActionTimestamp");function h(t,e){if(!(0,u.existsSync)(t))return[];try{const i=(0,u.readFileSync)(t,"utf-8").trim().split(`
5
+ `).filter(o=>o.length>0);let r=-1;const s=[];for(let o=0;o<i.length;o++)try{const l=JSON.parse(i[o]);s.push(l),e(l)&&(r=o)}catch{}return s.slice(r+1)}catch(n){return f.logger.debug(`failed to read actions from ${t}: ${n}`),[]}}c(h,"readActionsSinceMatch");function A(t){return t.type!=="verification_requested"?!1:t.action==="allow"}c(A,"isAllowVerificationRequested");function z(t){return h(t,A).filter(n=>n.type==="tool_call")}c(z,"getToolCallsSinceLastVerification");function W(t){return x(t,"file_change").filter(n=>n.type==="tool_call")}c(W,"getToolCallsSinceLastFileChange");function K(t){return x(t,"verdict_write").some(n=>n.type==="tool_call")}c(K,"hasToolCallsSinceLastVerdict");function B(t){return h(t,A).some(n=>n.type==="file_change")}c(B,"hasFileChangesSinceLastVerification");function Q(t){return h(t,A).filter(n=>n.type==="file_change")}c(Q,"getFileChangesSinceLastVerification");function H(t){return t.type!=="verdict_write"?!1:t.verdict?.status==="fail"}c(H,"isFailVerdictWrite");function Y(t){if(!(0,u.existsSync)(t))return[];try{const i=(0,u.readFileSync)(t,"utf-8").trim().split(`
6
+ `).filter(o=>o.length>0).map(o=>{try{return JSON.parse(o)}catch{return null}});let r=-1;for(let o=0;o<i.length;o++){const l=i[o];l!==null&&H(l)&&(r=o)}if(r<0)return[];const s=[];for(let o=r+1;o<i.length;o++){const l=i[o];l!==null&&l.type==="file_change"&&s.push(l)}return s}catch(e){return f.logger.debug(`failed to read file changes since last fail verdict: ${e}`),[]}}c(Y,"getFileChangesSinceLastFailVerdict");function X(t){if(t.length===0)return[];const e=new Map,n=[];for(const i of t){const r=i.file_path;let s=e.get(r);s||(s={op:i.operation,added:0,removed:0},e.set(r,s),n.push(r)),s.op=s.op==="create"||i.operation==="create"?"create":i.operation,s.added+=typeof i.lines_added=="number"?i.lines_added:0,s.removed+=typeof i.lines_removed=="number"?i.lines_removed:0}return n.map(i=>{const r=e.get(i);return`${r.op} ${i} (+${r.added}/-${r.removed})`})}c(X,"summarizeFixFileChanges");0&&(module.exports={ActivityAwareEvent,Event,EventType,EventTypeValue,FixAwareEvent,VerificationAwareEvent,appendAction,baseFields,deterministicSessionEndId,findDurationSinceLastAction,findLastActionTimestamp,getFileChangesSinceLastFailVerdict,getFileChangesSinceLastVerification,getToolCallsSinceLastFileChange,getToolCallsSinceLastVerification,hasFileChangesSinceLastVerification,hasToolCallsSinceLastVerdict,readActionsSinceLastMarker,resolveProjectName,summarizeFixFileChanges});