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