@ironbee-ai/cli 0.29.0 → 0.30.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 +6 -0
- package/dist/clients/claude/agents/ironbee-scenario.md +191 -0
- package/dist/clients/claude/agents/ironbee-verifier.md +22 -5
- package/dist/clients/claude/commands/ironbee-manage-scenario.md +36 -0
- package/dist/clients/claude/commands/ironbee-search-scenario.md +22 -0
- package/dist/clients/claude/commands/ironbee-sync-scenario.md +31 -0
- package/dist/clients/claude/commands/ironbee-verify.md +13 -12
- package/dist/clients/claude/hooks/require-verification.js +3 -3
- package/dist/clients/claude/hooks/track-action.js +1 -1
- package/dist/clients/claude/index.js +4 -4
- package/dist/clients/claude/platforms/scenario.android.md +31 -0
- package/dist/clients/claude/platforms/scenario.backend.md +26 -0
- package/dist/clients/claude/platforms/scenario.browser.md +41 -0
- package/dist/clients/claude/platforms/scenario.node.md +27 -0
- package/dist/clients/claude/trust.js +1 -0
- package/dist/clients/codex/agents/ironbee-scenario.md +179 -0
- package/dist/clients/codex/agents/ironbee-verifier.md +22 -5
- package/dist/clients/codex/commands/ironbee-manage-scenario/SKILL.main.md +102 -0
- package/dist/clients/codex/commands/ironbee-manage-scenario/SKILL.md +38 -0
- package/dist/clients/codex/commands/ironbee-search-scenario/SKILL.main.md +37 -0
- package/dist/clients/codex/commands/ironbee-search-scenario/SKILL.md +23 -0
- package/dist/clients/codex/commands/ironbee-sync-scenario/SKILL.main.md +55 -0
- package/dist/clients/codex/commands/ironbee-sync-scenario/SKILL.md +33 -0
- package/dist/clients/codex/commands/ironbee-verify/SKILL.main.md +12 -3
- package/dist/clients/codex/commands/ironbee-verify/SKILL.md +4 -3
- package/dist/clients/codex/hooks/require-verification.js +3 -3
- package/dist/clients/codex/hooks/track-action.js +1 -1
- package/dist/clients/codex/index.js +2 -2
- package/dist/clients/codex/platforms/scenario.android.md +31 -0
- package/dist/clients/codex/platforms/scenario.backend.md +26 -0
- package/dist/clients/codex/platforms/scenario.browser.md +40 -0
- package/dist/clients/codex/platforms/scenario.node.md +27 -0
- package/dist/clients/codex/util.js +6 -6
- package/dist/clients/cursor/commands/ironbee-manage-scenario/SKILL.md +100 -0
- package/dist/clients/cursor/commands/ironbee-search-scenario/SKILL.md +34 -0
- package/dist/clients/cursor/commands/ironbee-sync-scenario/SKILL.md +54 -0
- package/dist/clients/cursor/commands/ironbee-verify/SKILL.md +2 -1
- package/dist/clients/cursor/hooks/require-verification.js +3 -3
- package/dist/clients/cursor/hooks/track-action.js +1 -1
- package/dist/clients/cursor/index.js +1 -1
- package/dist/clients/cursor/platforms/scenario.android.md +31 -0
- package/dist/clients/cursor/platforms/scenario.backend.md +26 -0
- package/dist/clients/cursor/platforms/scenario.browser.md +40 -0
- package/dist/clients/cursor/platforms/scenario.node.md +27 -0
- package/dist/commands/scenario.js +1 -0
- package/dist/hooks/core/actions.js +7 -7
- package/dist/hooks/core/nested-tools.js +1 -1
- package/dist/hooks/core/scenario-tools.js +1 -0
- package/dist/index.js +1 -1
- package/dist/lib/config.js +1 -1
- package/dist/lib/git.js +1 -1
- package/dist/lib/install-version.js +1 -1
- package/dist/lib/platform-section.js +3 -3
- package/dist/lib/scenario-staleness.js +1 -0
- package/dist/tui/config/schema.js +1 -1
- package/dist/tui/scenarios/area.js +2 -0
- package/dist/tui/shell/registry.js +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ironbee-sync-scenario
|
|
3
|
+
description: "Re-validate saved IronBee verification scenarios against the current code and repair MECHANICAL drift, using the scenario-* MCP tools directly. A leading `check` token = dry-run (report drift, no repair). Never auto-changes what a scenario verifies."
|
|
4
|
+
disable-model-invocation: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# IronBee — Sync scenario(s)
|
|
8
|
+
|
|
9
|
+
Re-validate + repair saved verification **scenarios** using the devtools `*_scenario-*` MCP tools
|
|
10
|
+
directly. This is NOT a verification cycle — no verdict, no gate.
|
|
11
|
+
|
|
12
|
+
> Argument: `$ARGUMENTS`
|
|
13
|
+
|
|
14
|
+
## Steps
|
|
15
|
+
1. **Resolve mode + target**: strip a leading `check` token (→ dry-run) and a leading `force` token
|
|
16
|
+
(→ include ALL scenarios, not just stale); remainder = `all` (stale ones; with `force`, every one)
|
|
17
|
+
or a name / description (one). Empty → `all`. **Print the target list + count before running.**
|
|
18
|
+
Run targets that share an `ironbee.group` in ascending `ironbee.order` (a flow split across platforms).
|
|
19
|
+
2. **For each target scenario** (resolve via `*_scenario-search` / `*_scenario-list`; `all` = the stale
|
|
20
|
+
ones — covered files changed since their `ironbee.commit`, or authored as drafts) **run it**
|
|
21
|
+
(`*_scenario-run`, against the live app — start it if needed, tear down what you started) and classify:
|
|
22
|
+
- **passes** → still current; (non-check) `*_scenario-update` to stamp `ironbee.commit` → HEAD
|
|
23
|
+
(read via `git rev-parse HEAD`) + `ironbee.liveValidated: true`. `*_scenario-update`
|
|
24
|
+
shallow-replaces metadata — read current metadata and re-send it MERGED with these two keys
|
|
25
|
+
(don't drop `coveredPaths` / `group` / `argsSchema`).
|
|
26
|
+
- **mechanical DRIFT** (the way to reach / drive the flow changed, not the expected outcome) →
|
|
27
|
+
repair the SCRIPT mechanics only, `*_scenario-update`, re-run until green, then stamp.
|
|
28
|
+
- **real DEFECT** (the expected outcome is unreachable — the app broke) → **STOP, report, do NOT
|
|
29
|
+
touch the scenario.**
|
|
30
|
+
- **expectation CHANGED** (a deliberate behavior / spec change) → do NOT auto-edit the assertion;
|
|
31
|
+
ask the user.
|
|
32
|
+
- **`check` mode** → only run + report drift; never repair / update.
|
|
33
|
+
- **Classify safely** (repair is the only branch that edits a scenario, so a defect mistaken for
|
|
34
|
+
drift masks a regression): before repairing, self-check whether the fix changes *how* the flow
|
|
35
|
+
is driven (drift — OK to repair) or *what* it asserts (never drift — a defect → STOP, or a
|
|
36
|
+
deliberate change → ask). A failure while reaching / driving the flow leans drift; a failure at
|
|
37
|
+
the terminal assertion leans defect. **Uncertain → treat as a defect and STOP.**
|
|
38
|
+
3. **Report** per scenario: repaired / still-fresh / defect-reported / needs decision.
|
|
39
|
+
|
|
40
|
+
**Hard rule: repair MECHANICS, never the ASSERTION / expected outcome** — silently relaxing an
|
|
41
|
+
assertion to make a stale scenario pass would mask a regression. (To just *detect* staleness without
|
|
42
|
+
running anything, use `ironbee scenario status`.)
|
|
43
|
+
|
|
44
|
+
<!--IRONBEE:PLATFORM:browser-->
|
|
45
|
+
<!--/IRONBEE:PLATFORM:browser-->
|
|
46
|
+
|
|
47
|
+
<!--IRONBEE:PLATFORM:node-->
|
|
48
|
+
<!--/IRONBEE:PLATFORM:node-->
|
|
49
|
+
|
|
50
|
+
<!--IRONBEE:PLATFORM:backend-->
|
|
51
|
+
<!--/IRONBEE:PLATFORM:backend-->
|
|
52
|
+
|
|
53
|
+
<!--IRONBEE:PLATFORM:android-->
|
|
54
|
+
<!--/IRONBEE:PLATFORM:android-->
|
|
@@ -22,7 +22,8 @@ The FIRST whitespace-delimited token of whatever the user provided alongside thi
|
|
|
22
22
|
|
|
23
23
|
A custom verification scenario may be supplied when this command is invoked — either as **inline text** or as a **path to a file** (any location, any format; it is read at run time). The scenario is whatever the user provided alongside invoking this command, after stripping a leading `fix` / `report` mode token (see **Mode**).
|
|
24
24
|
|
|
25
|
-
- **If
|
|
25
|
+
- **If the scenario part starts with `scenario:`** (after the mode token), everything after `scenario:` (to the end) is a **SAVED scenario reference** — an exact name OR a semantic description. Resolve it across enabled platforms (`*_scenario-search` for the description + an exact-name `*_scenario-list` match), pick the single strong match (ambiguous → ask which; none → say so and fall back to the default flow), then **run it in ONE `*_scenario-run` call** (no manual re-discovery) and **judge its result (functional) + any returned visual evidence (e.g. screenshots)**. The scenario's nested tool calls satisfy each active cycle's required tools (as long as it exercises them). No exact name needed — e.g. `scenario: the full purchase flow`. **On PASS, keep it fresh:** `*_scenario-update` its `ironbee.commit` → HEAD (`git rev-parse HEAD`) + `liveValidated: true` (re-send the full metadata merged); on FAIL / defect, don't stamp.
|
|
26
|
+
- **If a scenario is supplied (free text), it is authoritative**: verify exactly what it describes. Drive each active cycle's tools to exercise precisely the flows, states, and endpoints it names — this **replaces** the default "exercise the changed pages/endpoints" guidance.
|
|
26
27
|
- **If the scenario is (or points to) a file path**, read that file with your file-read tool and treat its contents as the scenario. Do not assume a fixed location or format — read whatever path was given.
|
|
27
28
|
- **If the path does not resolve to an existing file**, stop and report `scenario file not found: <path>`, then ask how to proceed — do not verify the literal path string or guess a target.
|
|
28
29
|
- **If no scenario is supplied**, fall back to the default flow: exercise the changed pages/endpoints per the active platform sections below.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
"use strict";var f=Object.defineProperty;var
|
|
1
|
+
"use strict";var f=Object.defineProperty;var B=Object.getOwnPropertyDescriptor;var F=Object.getOwnPropertyNames;var K=Object.prototype.hasOwnProperty;var R=(o,t)=>f(o,"name",{value:t,configurable:!0});var j=(o,t)=>{for(var c in t)f(o,c,{get:t[c],enumerable:!0})},J=(o,t,c,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of F(t))!K.call(o,r)&&r!==c&&f(o,r,{get:()=>t[r],enumerable:!(i=B(t,r))||i.enumerable});return o};var L=o=>J(f({},"__esModule",{value:!0}),o);var D={};j(D,{run:()=>q});module.exports=L(D);var I=require("crypto"),e=require("../../../hooks/core/session-state"),S=require("../../../hooks/core/actions"),k=require("../../../hooks/core/activity"),O=require("../../../hooks/core/verification-lifecycle"),U=require("../../../lib/config"),E=require("../../../lib/recording-tools"),M=require("../../../hooks/core/scenario-tools"),g=require("../../../lib/logger"),$=require("../../../lib/stdin");const h={"MCP:bdt_":"browser-devtools","MCP:ndt_":"node-devtools","MCP:bedt_":"backend-devtools","MCP:adt_":"android-devtools"},W="browser-devtools";async function q(o,t){const c=t?.soft===!0;let i;try{i=JSON.parse((0,$.readStdin)())}catch(n){g.logger.debug(`failed to parse stdin: ${n}`);const x={permission:"allow"};process.stdout.write(JSON.stringify(x)),process.exit(0);return}const r=i.conversation_id??"default",s=`${o}/.ironbee/sessions/${r}`;(0,g.setLogFile)(`${s}/session.log`);const b=`${s}/actions.jsonl`,p=(0,M.isScenarioTool)(i.tool_name),P=(0,e.getActiveVerificationId)(s);if(!P&&!c&&!p){const n={permission:"deny",agent_message:`BLOCKED: You must start a verification cycle before using devtools tools (browser-devtools / node-devtools / backend-devtools / android-devtools).
|
|
2
2
|
|
|
3
3
|
Start verification first:
|
|
4
4
|
echo '{"session_id":"${r}"}' | ironbee hook verification-start
|
|
5
5
|
|
|
6
|
-
Then use the verification tools for the active cycle(s) \u2014 MCP:bdt_* for browser, MCP:ndt_* for node, MCP:bedt_* for backend, MCP:adt_* for android.`};process.stdout.write(JSON.stringify(n)),process.exit(2);return}const
|
|
6
|
+
Then use the verification tools for the active cycle(s) \u2014 MCP:bdt_* for browser, MCP:ndt_* for node, MCP:bedt_* for backend, MCP:adt_* for android.`};process.stdout.write(JSON.stringify(n)),process.exit(2);return}const m=i.tool_name??"",v=m.startsWith("MCP:")?m.slice(4):"",u=v?(0,E.recordingToolsForBareTool)(v):null;if(!c&&!p&&u!==null&&(0,e.isRecordingRequired)(s)&&!(0,e.isRecordingActive)(s)&&v!==u.startTool){const n={permission:"deny",agent_message:`BLOCKED: Recording is required but not started.
|
|
7
7
|
|
|
8
8
|
1. Start recording NOW:
|
|
9
9
|
Use MCP:${u.startTool}
|
|
@@ -12,4 +12,4 @@ Then use the verification tools for the active cycle(s) \u2014 MCP:bdt_* for bro
|
|
|
12
12
|
|
|
13
13
|
3. **Stop recording BEFORE submitting verdict:**
|
|
14
14
|
Use MCP:${u.stopTool}
|
|
15
|
-
submit-verdict will reject with "recording is still active" if you skip this.`};process.stdout.write(JSON.stringify(n)),process.exit(2);return}await(0,k.startActivity)({sessionDir:
|
|
15
|
+
submit-verdict will reject with "recording is still active" if you skip this.`};process.stdout.write(JSON.stringify(n)),process.exit(2);return}await(0,k.startActivity)({sessionDir:s,actionsFile:b,source:"pre_tool_use"});let d=P;c&&!d&&!p&&(d=(await(0,O.startVerification)({sessionId:r,sessionDir:s,actionsFile:b,recordingEnabled:!1})).verificationId);const A=(0,e.getActiveTraceId)(s),_=(0,e.getActiveActivityId)(s),y=(0,S.resolveProjectName)(o),C=[`prj:${y}`,`sid:${r}`];_&&C.push(`aid:${_}`),d&&C.push(`vid:${d}`);const N=`ironbee=${C.join(";")}`,l=(0,U.loadConfig)(o),T={...i.tool_input??{}},a={projectName:y,sessionId:r,activityId:_,verificationId:d,traceId:A,traceState:N,toolCallId:(0,I.randomUUID)()};i.tool_use_id&&(a.toolUseId=i.tool_use_id),a.mcpServer=(()=>{for(const n of Object.keys(h))if(m.startsWith(n))return h[n];return W})();const w=(0,e.getUserEmail)(s);w&&(a.userEmail=w),l.collector?.url&&(a.collectorUrl=l.collector.url),l.collector?.oauthToken?a.collectorOAuthToken=l.collector.oauthToken:l.collector?.apiKey&&(a.collectorApiKey=l.collector.apiKey),T._metadata=a;const V={permission:"allow",updated_input:T};process.stdout.write(JSON.stringify(V)),process.exit(0)}R(q,"run");0&&(module.exports={run});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var y=Object.defineProperty;var U=Object.getOwnPropertyDescriptor;var K=Object.getOwnPropertyNames;var j=Object.prototype.hasOwnProperty;var g=(o,t)=>y(o,"name",{value:t,configurable:!0});var Q=(o,t)=>{for(var l in t)y(o,l,{get:t[l],enumerable:!0})},Y=(o,t,l,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of K(t))!j.call(o,r)&&r!==l&&y(o,r,{get:()=>t[r],enumerable:!(i=U(t,r))||i.enumerable});return o};var q=o=>Y(y({},"__esModule",{value:!0}),o);var it={};Q(it,{run:()=>nt});module.exports=q(it);var
|
|
1
|
+
"use strict";var y=Object.defineProperty;var U=Object.getOwnPropertyDescriptor;var K=Object.getOwnPropertyNames;var j=Object.prototype.hasOwnProperty;var g=(o,t)=>y(o,"name",{value:t,configurable:!0});var Q=(o,t)=>{for(var l in t)y(o,l,{get:t[l],enumerable:!0})},Y=(o,t,l,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of K(t))!j.call(o,r)&&r!==l&&y(o,r,{get:()=>t[r],enumerable:!(i=U(t,r))||i.enumerable});return o};var q=o=>Y(y({},"__esModule",{value:!0}),o);var it={};Q(it,{run:()=>nt});module.exports=q(it);var p=require("../../../hooks/core/actions"),T=require("../../../hooks/core/nested-tools"),c=require("../../../hooks/core/session-state"),L=require("../../../hooks/core/verification-context"),E=require("../../../lib/config"),s=require("../../../lib/logger"),k=require("../../../lib/recording-tools"),F=require("../../../lib/output"),P=require("../../../lib/stdin"),f=require("../../../queue"),b=require("../util");const S="bdt_",$="ndt_",x="bedt_",D="adt_",G="browser-devtools",H="node-devtools",Z="backend-devtools",tt="android-devtools";function ot(o){return o.startsWith(x)?Z:o.startsWith(D)?tt:o.startsWith(S)?G:o.startsWith($)?H:null}g(ot,"resolveServerByPrefix");async function nt(o){let t;try{t=JSON.parse((0,P.readStdin)())}catch(n){s.logger.debug(`failed to parse stdin: ${n}`),process.stdout.write(JSON.stringify({})),process.exit(0);return}const l=t.conversation_id??"default",i=`${o}/.ironbee/sessions/${l}`,r=`${i}/actions.jsonl`;(0,s.setLogFile)(`${i}/session.log`);const v=t.tool_name??"unknown",R=Date.now(),w=t.tool_input&&typeof t.tool_input=="object"&&!Array.isArray(t.tool_input)?{...t.tool_input,_metadata:void 0}:t.tool_input,A=(0,c.getActiveActivityId)(i),O=(0,c.getActiveVerificationId)(i),C=(0,c.getActiveTraceId)(i),e=(0,b.classifyTool)(v,t.tool_input),W=e.tool_type==="mcp"&&e.tool_name.startsWith(S),B=e.tool_type==="mcp"&&e.tool_name.startsWith($),J=e.tool_type==="mcp"&&e.tool_name.startsWith(x),M=e.tool_type==="mcp"&&e.tool_name.startsWith(D),m=W||B||J||M,a=e.tool_type==="mcp"?ot(e.tool_name)??e.mcp_server:e.mcp_server,z=m?w:(0,b.extractCursorToolInput)(v,w),I=typeof t.error_message=="string"&&t.error_message.length>0?t.error_message:void 0;let u;if(I){const n=[];t.failure_type&&n.push(t.failure_type),t.is_interrupt&&n.push("interrupted"),u=`${n.length>0?`${n.join(",")}: `:""}${I}`}const h={...(0,p.baseFields)(r),type:"tool_call",timestamp:R,tool_name:e.tool_name,tool_type:e.tool_type,tool_use_id:t.tool_use_id,tool_input:z,tool_input_size:V(t.tool_input),tool_response:u?void 0:t.tool_output,tool_response_size:V(u?void 0:t.tool_output),activity_id:A,verification_id:O,trace_id:C,duration:typeof t.duration=="number"?t.duration:null,mcp_server:a,error:u};if(m){await(0,p.appendAction)(r,h);const n=(0,k.recordingToolsForServer)(a);n!==null&&(e.tool_name===n.startTool?((0,c.setRecordingActive)(i,!0),s.logger.debug(`track-action: recording started (${n.cycle})`)):e.tool_name===n.stopTool&&((0,c.setRecordingActive)(i,!1),s.logger.debug(`track-action: recording stopped (${n.cycle})`)))}else et(o,l,h);if(s.logger.debug(`track-action: ${v}${u?" (failed)":""}`),m&&a!==null&&e.tool_name===(0,T.executeToolBareName)(a)&&!u){const n=(0,T.extractNestedToolCalls)(t.tool_input,a),_=(0,k.recordingToolsForServer)(a);for(const d of n){_!==null&&(d.name===_.startTool?((0,c.setRecordingActive)(i,!0),s.logger.debug(`track-action (nested): recording started (${_.cycle})`)):d.name===_.stopTool&&((0,c.setRecordingActive)(i,!1),s.logger.debug(`track-action (nested): recording stopped (${_.cycle})`)));const X={...(0,p.baseFields)(r),type:"tool_call",timestamp:d.startTime??R,tool_name:d.name,tool_type:"mcp",tool_input:d.args,activity_id:A,verification_id:O,trace_id:C,duration:d.duration??null,mcp_server:a,nested:!0,parent_tool_use_id:t.tool_use_id};await(0,p.appendAction)(r,X),s.logger.debug(`track-action (nested): ${d.name}`)}}const N={};if(m)try{const n=(0,L.buildVerificationContextOnceForCycle)({projectDir:o,sessionId:l,sessionDir:i,activeVerificationId:O,config:(0,E.loadConfig)(o)});n.length>0&&(N.additional_context=n)}catch(n){s.logger.debug(`track-action: verification-context injection skipped: ${n instanceof Error?n.message:n}`)}(0,F.writeAndExit)(JSON.stringify(N),0)}g(nt,"run");function et(o,t,l){if(!(0,E.isJobQueueEnabled)(o))return;const i={...l};delete i.tool_response;try{(0,f.submit)(o,t,f.SEND_EVENT_TYPE,i)}catch(r){if(r instanceof f.JobTooLargeError){s.logger.debug(`track-action: wire event too large for ${l.tool_name}; dropping`);return}s.logger.debug(`track-action: failed to submit ${l.tool_name}: ${r instanceof Error?r.message:r}`)}}g(et,"submitEvent");function V(o){if(o==null)return 0;try{const t=typeof o=="string"?o:JSON.stringify(o);return t===void 0?0:Buffer.byteLength(t,"utf-8")}catch{return 0}}g(V,"byteSize");0&&(module.exports={run});
|
|
@@ -1 +1 @@
|
|
|
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});
|
|
1
|
+
"use strict";var $=Object.defineProperty;var x=Object.getOwnPropertyDescriptor;var z=Object.getOwnPropertyNames;var Q=Object.prototype.hasOwnProperty;var v=(c,o)=>$(c,"name",{value:o,configurable:!0});var X=(c,o)=>{for(var e in o)$(c,e,{get:o[e],enumerable:!0})},Y=(c,o,e,s)=>{if(o&&typeof o=="object"||typeof o=="function")for(let t of z(o))!Q.call(c,t)&&t!==e&&$(c,t,{get:()=>o[t],enumerable:!(s=x(o,t))||s.enumerable});return c};var Z=c=>Y($({},"__esModule",{value:!0}),c);var no={};X(no,{CursorClient:()=>io});module.exports=Z(no);var i=require("fs"),l=require("path"),p=require("../../lib/logger"),n=require("../../lib/output"),T=require("../../lib/fs-prune"),N=require("./hooks/verify-gate"),P=require("./hooks/clear-verdict"),L=require("./hooks/track-action"),B=require("./hooks/track-action-monitor"),V=require("./hooks/session-start"),J=require("./hooks/require-verdict"),D=require("./hooks/require-verification"),F=require("./hooks/activity-start"),U=require("./hooks/activity-end"),K=require("./hooks/session-end"),a=require("../../lib/config"),q=require("../../lib/platform-section"),j=require("../../lib/gitignore");const y="browser-devtools",b="node-devtools",h="backend-devtools",S="android-devtools",oo="ironbee",O=["ironbee-manage-scenario","ironbee-search-scenario","ironbee-sync-scenario"];function eo(c){return(0,l.join)(__dirname,"..",c,"platforms")}v(eo,"platformsDirFor");function _(c){const o=Object.keys(c);if(o.length===0)return!0;if(o.length===1&&o[0]==="mcpServers"){const e=c.mcpServers;return e===void 0||Object.keys(e).length===0}return!1}v(_,"isMcpConfigEmpty");const E="ironbee",I=[`${y}:*`,`${b}:*`,`${h}:*`,`${S}:*`];function ro(c){const o=new Set(["mcpAllowlist","terminalAllowlist"]);for(const t of Object.keys(c))if(!o.has(t))return!1;const e=c.mcpAllowlist??[],s=c.terminalAllowlist??[];return e.length===0&&s.length===0}v(ro,"isPermissionsEmpty");function so(c){const o=new Set(["version","hooks"]);for(const e of Object.keys(c))if(!o.has(e))return!1;return Object.keys(c.hooks??{}).length===0}v(so,"isCursorHooksEmpty");function d(c){return n.pc.dim(c)}v(d,"cursorColor");class io{constructor(){this.name="cursor"}static{v(this,"CursorClient")}detect(o){return(0,i.existsSync)((0,l.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,j.ensureIronBeeGitignored)(o);const u=(0,l.join)(o,".cursor"),g=(0,l.join)(u,"rules"),f=(0,l.join)(u,"skills");(0,i.mkdirSync)(g,{recursive:!0}),(0,i.mkdirSync)(f,{recursive:!0});const m=(0,l.join)(u,"hooks.json");if(this.mergeHooksConfig(m,t),r){if(t==="enforce"){const C=(0,l.join)(f,"ironbee-verification.md"),w=(0,i.readFileSync)((0,l.join)(__dirname,"skills","ironbee-verification.md"),"utf-8");(0,i.writeFileSync)(C,w);const H=(0,l.join)(g,"ironbee-verification.mdc"),W=(0,i.readFileSync)((0,l.join)(__dirname,"rules","ironbee-verification.mdc"),"utf-8");(0,i.writeFileSync)(H,W),console.log(` ${n.pc.dim("\u2192")} ${d("[cursor]")} skill ${n.pc.dim("\u2192")} ${n.pc.dim(C)}`),console.log(` ${n.pc.dim("\u2192")} ${d("[cursor]")} rule ${n.pc.dim("\u2192")} ${n.pc.dim(H)}`)}const k=(0,l.join)(f,"ironbee-verify");(0,i.mkdirSync)(k,{recursive:!0});const A=(0,l.join)(k,"SKILL.md"),G=(0,i.readFileSync)((0,l.join)(__dirname,"commands","ironbee-verify","SKILL.md"),"utf-8");(0,i.writeFileSync)(A,G);for(const C of O){const w=(0,l.join)(f,C);(0,i.mkdirSync)(w,{recursive:!0}),(0,i.writeFileSync)((0,l.join)(w,"SKILL.md"),(0,i.readFileSync)((0,l.join)(__dirname,"commands",C,"SKILL.md"),"utf-8"))}const M=(0,l.join)(u,"mcp.json");this.writeMcpConfig(M,o);const R=(0,l.join)(u,"permissions.json");this.writePermissionsConfig(R,o),(0,q.syncPlatformSectionsToConfig)(o,eo),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(A)}`),console.log(` ${n.pc.dim("\u2192")} ${d("[cursor]")} mcp ${n.pc.dim("\u2192")} ${n.pc.dim(M)}`),console.log(` ${n.pc.dim("\u2192")} ${d("[cursor]")} perms ${n.pc.dim("\u2192")} ${n.pc.dim(R)}`),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,l.join)(o,".cursor"),s=(0,l.join)(e,"skills","ironbee-verification.md"),t=(0,l.join)(e,"rules","ironbee-verification.mdc"),r=(0,l.join)(e,"skills","ironbee-analyze","SKILL.md"),u=(0,l.join)(e,"skills","ironbee-verify","SKILL.md");this.removeFile(s),this.removeFile(t),this.removeFile(r),this.removeFile(u);for(const k of O)this.removeFile((0,l.join)(e,"skills",k,"SKILL.md"));this.removeFile((0,l.join)(e,"skills","ironbee-run-scenario","SKILL.md"));const g=(0,l.join)(e,"hooks.json");this.removeIronBeeHooks(g),this.maybeDeleteEmptyHooks(g);const f=(0,l.join)(e,"mcp.json");this.removeMcpServer(f);const m=(0,l.join)(e,"permissions.json");this.removePermissionsEntries(m),(0,T.pruneEmptyDirs)(e)}maybeDeleteEmptyHooks(o){if((0,i.existsSync)(o))try{const e=JSON.parse((0,i.readFileSync)(o,"utf-8"));so(e)&&(0,i.unlinkSync)(o)}catch(e){p.logger.debug(`failed to inspect ${o} for emptiness: ${e}`)}}async runVerifyGate(o){await(0,N.run)(o)}async runClearVerdict(o){await(0,P.run)(o)}async runTrackAction(o){await(0,L.run)(o)}async runSessionStart(o){await(0,V.run)(o)}async runRequireVerdict(o,e){await(0,J.run)(o,e)}async runRequireVerification(o,e){await(0,D.run)(o,e)}async runActivityStart(o){await(0,F.run)(o)}async runActivityEnd(o){await(0,U.run)(o)}async runTrackActionMonitor(o){await(0,B.run)(o)}async runSessionEnd(o){await(0,K.run)(o)}async runTrackActionPre(o){}isIronBeeHook(o){return o.command.includes(oo)}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(k=>!this.isIronBeeHook(k));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[y]&&(delete e.mcpServers[y],s=!0),e.mcpServers&&e.mcpServers[b]&&(delete e.mcpServers[b],s=!0),e.mcpServers&&e.mcpServers[h]&&(delete e.mcpServers[h],s=!0),e.mcpServers&&e.mcpServers[S]&&(delete e.mcpServers[S],s=!0),_(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[y]=(0,a.getMcpServerEntry)(e):delete s.mcpServers[y],(0,a.isCycleEnabled)(t,"node")?s.mcpServers[b]=(0,a.getNodeDevToolsMcpEntry)(e):delete s.mcpServers[b],(0,a.isCycleEnabled)(t,"backend")?s.mcpServers[h]=(0,a.getBackendDevToolsMcpEntry)(e):delete s.mcpServers[h],(0,a.isCycleEnabled)(t,"android")?s.mcpServers[S]=(0,a.getAndroidDevToolsMcpEntry)(e):delete s.mcpServers[S],_(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(`${y}:*`),(0,a.isCycleEnabled)(t,"node")&&r.push(`${b}:*`),(0,a.isCycleEnabled)(t,"backend")&&r.push(`${h}:*`),(0,a.isCycleEnabled)(t,"android")&&r.push(`${S}:*`);const u=new Set(I),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!==E);f.push(E),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(I);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!==E),e.terminalAllowlist.length===0&&delete e.terminalAllowlist),ro(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,31 @@
|
|
|
1
|
+
### android platform (enabled)
|
|
2
|
+
- **Use for**: Android app scenarios on a real device / emulator.
|
|
3
|
+
- **Server**: `android-devtools` · **scenario tools**: the `adt_scenario-*` tools
|
|
4
|
+
(`adt_scenario-add` / `-update` / `-delete` / `-list` / `-search` / `-run`).
|
|
5
|
+
- **Store**: project → `.ironbee/scenarios/adt`, global → `~/.ironbee/scenarios/adt` (the
|
|
6
|
+
server's `SCENARIOS_DIR`; you pass `scope`, the server resolves the path).
|
|
7
|
+
- Scenario **scripts** call this platform's tools via `callTool('<bare-tool>', {...})` — discover
|
|
8
|
+
the available `adt_*` tool names from your connected MCP tool 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 android cycle collects). In the script:
|
|
12
|
+
1. **Connect + launch** — `adt_device_connect` (list targets with `adt_device_list-targets`; an
|
|
13
|
+
emulator is usually `emulator-5554`), then `adt_device_launch-app` with the package name.
|
|
14
|
+
2. Pick an **evidence path** for the changed code area:
|
|
15
|
+
- **Device-evidence path** — drive the UI to exercise the change (`adt_interaction_tap` /
|
|
16
|
+
`adt_interaction_input-text` / `adt_interaction_swipe` / `adt_interaction_scroll`; locate elements
|
|
17
|
+
with `adt_a11y_find-element` / the UI-snapshot's element refs — do NOT hand-parse the snapshot
|
|
18
|
+
TEXT with regex), then capture **BOTH**: a screenshot (`adt_content_take-screenshot`
|
|
19
|
+
**with `returnOutput: true`** — put the returned `filePath` in your result; the verifier `Read`s
|
|
20
|
+
that file to judge the pixels. **Do NOT set `includeBase64`** — a nested scenario screenshot isn't
|
|
21
|
+
surfaced as an inline image and base64 only bloats the result) **AND** a UI snapshot
|
|
22
|
+
(`adt_a11y_take-ui-snapshot`, `returnOutput: true` — its TEXT view hierarchy / labels is what the
|
|
23
|
+
verifier reads). Both are MANDATORY (visual + structural, like the browser screenshot + aria pair).
|
|
24
|
+
- **Log-evidence path** — `adt_o11y_log-read` / `adt_o11y_log-follow` (with `returnOutput: true`)
|
|
25
|
+
for the tag(s) relevant to the change; confirm expected lines appear AND no FATAL / crash (E/
|
|
26
|
+
entries) for the app package.
|
|
27
|
+
|
|
28
|
+
`return` the evidence — UI-snapshot text, log lines, the screenshot `filePath`s — **plus explicit
|
|
29
|
+
pass/fail assertions**. That returned result is what `/ironbee-verify scenario:<name>` reads to judge
|
|
30
|
+
functional + structural (from the text) and **visual** (by `Read`ing the returned screenshot files).
|
|
31
|
+
**`android-devtools` is Android-only.**
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
### backend platform (enabled)
|
|
2
|
+
- **Use for**: backend protocol scenarios (HTTP / gRPC / GraphQL / WebSocket / DB).
|
|
3
|
+
- **Server**: `backend-devtools` · **scenario tools**: the `bedt_scenario-*` tools
|
|
4
|
+
(`bedt_scenario-add` / `-update` / `-delete` / `-list` / `-search` / `-run`).
|
|
5
|
+
- **Store**: project → `.ironbee/scenarios/bedt`, global → `~/.ironbee/scenarios/bedt` (the
|
|
6
|
+
server's `SCENARIOS_DIR`; you pass `scope`, the server resolves the path).
|
|
7
|
+
- Scenario **scripts** call this platform's tools via `callTool('<bare-tool>', {...})` — discover
|
|
8
|
+
the available `bedt_*` tool names from your connected MCP tool 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 backend cycle collects). At least ONE evidence path
|
|
12
|
+
is required — in the script, exercise one+:
|
|
13
|
+
- **Protocol-call** — `bedt_request_http` / `bedt_request_grpc` / `bedt_request_graphql` /
|
|
14
|
+
`bedt_request_websocket-open…` / `bedt_request_replay`; inspect the response `status` / body /
|
|
15
|
+
headers (4xx/5xx and gRPC non-OK are NORMAL results, not transport errors — decide pass/fail by what
|
|
16
|
+
the task requires). Chain POST→GET to confirm side effects.
|
|
17
|
+
- **Log-evidence** — `bedt_log_register-source` then `bedt_log_read` / `bedt_log_read-multi` /
|
|
18
|
+
`bedt_log_follow` (filter by level / pattern / trace-id) when an external driver hits the endpoint.
|
|
19
|
+
- **DB-evidence** — `bedt_db_connect` (read-only by default) then `bedt_db_query` /
|
|
20
|
+
`bedt_db_describe-table` / `bedt_db_snapshot` + `bedt_db_diff` to inspect state after a migration /
|
|
21
|
+
write.
|
|
22
|
+
|
|
23
|
+
`return` the responses / log lines / rows (capture each read with `returnOutput: true` so the data
|
|
24
|
+
reaches the script's `return`) **plus explicit pass/fail assertions** so a later verify run can judge
|
|
25
|
+
them. Runtime-agnostic —
|
|
26
|
+
works for any backend language (Node, Java, Python, Go, Rust, Ruby, .NET, …).
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
### browser platform (enabled)
|
|
2
|
+
- **Use for**: UI / frontend scenarios driven through a real browser.
|
|
3
|
+
- **Server**: `browser-devtools` · **scenario tools**: the `bdt_scenario-*` tools
|
|
4
|
+
(`bdt_scenario-add` / `-update` / `-delete` / `-list` / `-search` / `-run`).
|
|
5
|
+
- **Store**: project → `.ironbee/scenarios/bdt`, global → `~/.ironbee/scenarios/bdt` (the
|
|
6
|
+
server's `SCENARIOS_DIR`; you pass `scope`, the server resolves the path).
|
|
7
|
+
- Scenario **scripts** call this platform's tools via `callTool('<bare-tool>', {...})` — discover
|
|
8
|
+
the available `bdt_*` tool names from your connected MCP tool 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 browser cycle collects). In the script:
|
|
12
|
+
1. **Navigate** — `bdt_navigation_go-to` to the affected page(s), then **actually interact** (click
|
|
13
|
+
buttons, fill forms, submit data, trigger the workflow that changed). A click-through that asserts
|
|
14
|
+
nothing verifies nothing — the interaction is what makes the evidence meaningful. **Target elements
|
|
15
|
+
with the `selector`/`ref` the aria-snapshot returns for each** (e.g. `getByRole(...)` or `@e12`) —
|
|
16
|
+
do NOT hand-parse the snapshot TEXT with regex/string-matching: embedded quotes or special chars in
|
|
17
|
+
labels make that brittle (it silently misses elements). This includes deriving a positional
|
|
18
|
+
**`.nth(i)`** index by parsing the snapshot — a quote or special char in any earlier label shifts
|
|
19
|
+
every index, so the click lands on the wrong element (or none). Pick each element by its own
|
|
20
|
+
`getByRole(...)`/`ref`, or scope it to the matching card/row with a CSS `:has()` selector (e.g.
|
|
21
|
+
`.product-card:has(h4:has-text('Widget')) button:has-text('Add to cart')`). NOTE: the
|
|
22
|
+
browser-devtools resolver accepts only a flat `getByXYZ(...)` expression OR a CSS string — Playwright
|
|
23
|
+
locator chaining like `.filter({ hasText })` does NOT parse. Never compute element positions from
|
|
24
|
+
snapshot text.
|
|
25
|
+
2. **Screenshot** — `bdt_content_take-screenshot` (or `includeScreenshot: true` on a nav/interaction
|
|
26
|
+
call) **with `returnOutput: true`, and put the returned `filePath` (absolute path to the saved PNG)
|
|
27
|
+
in your result**. The later verifier opens that file with its `Read` tool to judge the pixels
|
|
28
|
+
(readability, layout, cut-off content, expected render). **Do NOT set `includeBase64`** — a nested
|
|
29
|
+
scenario screenshot is NOT surfaced as an inline MCP image (`scenario-run` strips nested image data)
|
|
30
|
+
and base64 only bloats the result; the returned `filePath` is how visual judging works.
|
|
31
|
+
3. **Accessibility** — `bdt_a11y_take-aria-snapshot` (or `includeSnapshot: true`), called with
|
|
32
|
+
`returnOutput: true` — the snapshot TEXT is what the verifier reads to judge page structure.
|
|
33
|
+
4. **Console** — `bdt_o11y_get-console-messages` with `returnOutput: true` to surface errors.
|
|
34
|
+
|
|
35
|
+
`return` the evidence — aria-snapshot text, page text (`bdt_content_get-as-text`), console errors, the
|
|
36
|
+
screenshot `filePath`s — **plus explicit pass/fail assertions**. That returned result is what
|
|
37
|
+
`/ironbee-verify scenario:<name>` reads to judge the run: functional + structural from the text, and
|
|
38
|
+
**visual by `Read`ing the returned screenshot files**. Capture the evidence AFTER the interactions
|
|
39
|
+
whose state you want to assert; for an intermediate state (a modal that opens then closes) capture at
|
|
40
|
+
that point too.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
### node platform (enabled)
|
|
2
|
+
- **Use for**: Node.js runtime-debug scenarios (V8 inspector probes / logs).
|
|
3
|
+
- **Server**: `node-devtools` · **scenario tools**: the `ndt_scenario-*` tools
|
|
4
|
+
(`ndt_scenario-add` / `-update` / `-delete` / `-list` / `-search` / `-run`).
|
|
5
|
+
- **Store**: project → `.ironbee/scenarios/ndt`, global → `~/.ironbee/scenarios/ndt` (the
|
|
6
|
+
server's `SCENARIOS_DIR`; you pass `scope`, the server resolves the path).
|
|
7
|
+
- Scenario **scripts** call this platform's tools via `callTool('<bare-tool>', {...})` — discover
|
|
8
|
+
the available `ndt_*` tool names from your connected MCP tool 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 node cycle collects). In the script:
|
|
12
|
+
1. **Connect** — `ndt_debug_connect` (one of `pid` / `processName` / `containerName` /
|
|
13
|
+
`inspectorPort` / `wsUrl`).
|
|
14
|
+
2. Pick an **evidence path** for the changed code path:
|
|
15
|
+
- **Probe path** (proves the code path executed) — set a probe at the changed location
|
|
16
|
+
(`ndt_debug_put-tracepoint` / `ndt_debug_put-logpoint` / `ndt_debug_put-exceptionpoint`),
|
|
17
|
+
**exercise the path** (drive it via a request / CLI / another platform's call — without this the
|
|
18
|
+
probe never fires), then read `ndt_debug_get-probe-snapshots`; at least one probe must come back
|
|
19
|
+
`triggered: true`.
|
|
20
|
+
- **Log path** (proves no errors during execution) — exercise the path, then `ndt_debug_get-logs`
|
|
21
|
+
filtered to the error level (no ERROR-level entries = pass).
|
|
22
|
+
|
|
23
|
+
`return` the probe snapshots / logs (read them with `returnOutput: true` so their data reaches the
|
|
24
|
+
script's `return`) **plus explicit pass/fail assertions** so a later verify run can judge them.
|
|
25
|
+
**`node-devtools` is
|
|
26
|
+
Node.js ONLY** — never author `ndt_*` scenarios for Java / Python / Go / Rust / Ruby / .NET / PHP
|
|
27
|
+
backends; use the **backend** platform for those.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";var S=Object.create;var g=Object.defineProperty;var C=Object.getOwnPropertyDescriptor;var $=Object.getOwnPropertyNames;var y=Object.getPrototypeOf,j=Object.prototype.hasOwnProperty;var h=(e,t)=>g(e,"name",{value:t,configurable:!0});var R=(e,t)=>{for(var r in t)g(e,r,{get:t[r],enumerable:!0})},p=(e,t,r,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of $(t))!j.call(e,i)&&i!==r&&g(e,i,{get:()=>t[i],enumerable:!(s=C(t,i))||s.enumerable});return e};var N=(e,t,r)=>(r=e!=null?S(y(e)):{},p(t||!e||!e.__esModule?g(r,"default",{value:e,enumerable:!0}):r,e)),O=e=>p(g({},"__esModule",{value:!0}),e);var x={};R(x,{scenarioCommand:()=>m});module.exports=O(x);var b=require("commander"),u=require("path"),n=N(require("picocolors")),d=require("../lib/scenario-staleness"),w=require("../lib/git"),c=require("../lib/config");function F(e){return e==="fresh"?n.default.green("fresh "):e==="stale"?n.default.yellow("stale "):n.default.dim("unknown")}h(F,"stateLabel");function k(e){const t=(0,u.resolve)(e.project??process.cwd()),r=(0,d.checkScenarioFreshness)(t);if(e.json===!0){const o=e.stale===!0?r.filter(l=>l.state==="stale"):r;console.log(JSON.stringify(o,null,2)),e.stale===!0&&o.length>0&&(process.exitCode=1);return}if(r.length===0){console.log(n.default.dim("No saved scenarios found (.ironbee/scenarios/)."));return}const s=r.filter(o=>o.state==="stale");if(e.stale===!0){if(s.length===0){console.log(n.default.green("\u2713 No stale scenarios."));return}for(const o of s)v(o);console.log(""),console.log(n.default.yellow(`${s.length} stale scenario(s) \u2014 re-validate with /ironbee-sync-scenario or /ironbee-verify scenario:<name>.`)),process.exitCode=1;return}for(const o of r)v(o);const i=r.filter(o=>o.state==="fresh").length,f=r.filter(o=>o.state==="unknown").length;console.log(""),console.log(`${r.length} scenario(s): ${n.default.green(`${i} fresh`)}, ${n.default.yellow(`${s.length} stale`)}, ${n.default.dim(`${f} unknown`)}.`),s.length>0&&console.log(n.default.dim("Re-validate stale ones with /ironbee-sync-scenario or /ironbee-verify scenario:<name>."))}h(k,"runStatus");function v(e){if(console.log(`${F(e.state)} ${n.default.bold(e.name)} ${n.default.dim(`[${e.platform}]`)} \u2014 ${e.reason}`),e.changedCoveredPaths.length>0){const t=e.changedCoveredPaths.slice(0,5);for(const r of t)console.log(` ${n.default.dim("\xB7")} ${r}`);e.changedCoveredPaths.length>t.length&&console.log(` ${n.default.dim(`\xB7 +${e.changedCoveredPaths.length-t.length} more`)}`)}}h(v,"printRow");function D(e){const t=(0,u.resolve)(e.project??process.cwd()),r=(0,c.loadConfig)(t);let s=(0,c.getVerificationContextCommitDepth)(r);if(e.commitDepth!==void 0){const a=Number.parseInt(e.commitDepth,10);Number.isFinite(a)&&a>=0&&(s=a)}const i=(0,w.getChangedPathsRelative)(t,s);if(i===null){if(e.json===!0){console.log(JSON.stringify({available:!1,reason:"not a git repository",scenarioCount:0,covered:[],gaps:[],changed:[]},null,2));return}console.log(n.default.dim("Not a git repository \u2014 cannot compute the changed set."));return}const f=i.filter(a=>(0,c.requiresVerification)(a,r)),o=(0,d.coverageGaps)(t,f);if(e.json===!0){console.log(JSON.stringify(o,null,2));return}if(o.changed.length===0){console.log(n.default.dim("No verification-relevant changed files in the current window (working tree + recent commits)."));return}if(o.scenarioCount===0){console.log(n.default.yellow(`No saved scenarios \u2014 all ${o.changed.length} verification-relevant changed file(s) are uncovered.`)),console.log(n.default.dim("Author one with /ironbee-manage-scenario."));return}if(o.gaps.length===0){console.log(n.default.green(`\u2713 All ${o.changed.length} verification-relevant changed file(s) are covered by a saved scenario.`));return}console.log(n.default.yellow(`${o.gaps.length} of ${o.changed.length} verification-relevant changed file(s) covered by NO scenario:`));const l=o.gaps.slice(0,30);for(const a of l)console.log(` ${n.default.dim("\xB7")} ${a}`);o.gaps.length>l.length&&console.log(` ${n.default.dim(`\xB7 +${o.gaps.length-l.length} more`)}`),console.log(""),console.log(n.default.dim("Author a scenario for an uncovered area with /ironbee-manage-scenario. (Advisory \u2014 coveredPaths is author-declared, so a gap can be a false positive.)"))}h(D,"runCoverage");const m=new b.Command("scenario").description("Inspect saved verification scenarios (.ironbee/scenarios/)");m.command("status").description("Show freshness (fresh / stale / unknown) of saved scenarios vs the current code").option("-p, --project <dir>","project directory (default: cwd)").option("--stale","show only stale scenarios; exit non-zero when any exist (CI gate)").option("--json","machine-readable JSON output").action(e=>{k(e)}),m.command("coverage").description("Show changed files (working tree + recent commits) covered by NO saved scenario (the inverse of status)").option("-p, --project <dir>","project directory (default: cwd)").option("--commit-depth <n>","commits back to include beyond the working tree (default: verificationContext.commitDepth)").option("--json","machine-readable JSON output").action(e=>{D(e)});0&&(module.exports={scenarioCommand});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
"use strict";var v=Object.defineProperty;var F=Object.getOwnPropertyDescriptor;var
|
|
2
|
-
`)}catch(
|
|
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
|
|
4
|
-
`).filter(
|
|
5
|
-
`).filter(o=>o.length>0);let r=-1;const s=[];for(let o=0;o<
|
|
6
|
-
`).filter(o=>o.length>0).map(o=>{try{return JSON.parse(o)}catch{return null}});let r=-1;for(let o=0;o<
|
|
7
|
-
`).filter(
|
|
1
|
+
"use strict";var v=Object.defineProperty;var F=Object.getOwnPropertyDescriptor;var R=Object.getOwnPropertyNames;var N=Object.prototype.hasOwnProperty;var c=(t,e)=>v(t,"name",{value:e,configurable:!0});var j=(t,e)=>{for(var i in e)v(t,i,{get:e[i],enumerable:!0})},P=(t,e,i,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of R(e))!N.call(t,r)&&r!==i&&v(t,r,{get:()=>e[r],enumerable:!(n=F(e,r))||n.enumerable});return t};var L=t=>P(v({},"__esModule",{value:!0}),t);var te={};j(te,{ActivityAwareEvent:()=>d.ActivityAwareEvent,Event:()=>d.Event,EventType:()=>d.EventType,EventTypeValue:()=>d.EventTypeValue,FixAwareEvent:()=>d.FixAwareEvent,VerificationAwareEvent:()=>d.VerificationAwareEvent,appendAction:()=>I,baseFields:()=>T,deterministicSessionEndId:()=>D,findDurationSinceLastAction:()=>G,findLastActionTimestamp:()=>W,getFileChangesSinceLastFailVerdict:()=>X,getFileChangesSinceLastVerification:()=>H,getToolCallsSinceLastFileChange:()=>K,getToolCallsSinceLastVerification:()=>z,hasFileChangesSinceLastVerification:()=>Q,hasToolCallsSinceLastVerdict:()=>B,hasVerifierEverEngaged:()=>Z,readActionsSinceLastMarker:()=>$,resolveProjectName:()=>V,summarizeFixFileChanges:()=>ee});module.exports=L(te);var l=require("fs"),y=require("crypto"),a=require("path"),f=require("../../lib/logger"),b=require("../../lib/collector"),w=require("../../lib/config"),k=require("../../queue/submit"),C=require("../../queue/types"),S=require("../../queue/register-handlers"),d=require("../../lib/event");function V(t){let e=t,i;for(;;){const n=(0,a.join)(e,".git");if((0,l.existsSync)(n)){const s=M(n);if(s==="directory"){const o=E(n);return o||(0,a.basename)(e)}if(s==="linked-pointer"){const o=_(n),u=o!==void 0?E(o):void 0;return u||(0,a.basename)(e)}s==="worktree-pointer"&&i===void 0&&(i=U(n))}const r=(0,a.dirname)(e);if(r===e)break;e=r}return i??(0,a.basename)(t)}c(V,"resolveProjectName");function M(t){try{const e=(0,l.statSync)(t);if(e.isDirectory())return"directory";if(!e.isFile())return"other";const i=_(t);return i===void 0?"other":(0,l.existsSync)((0,a.join)(i,"commondir"))?"worktree-pointer":"linked-pointer"}catch(e){return f.logger.debug(`resolveProjectName: stat failed for ${t}: ${e instanceof Error?e.message:e}`),"other"}}c(M,"classifyGitEntry");function _(t){try{const i=(0,l.readFileSync)(t,"utf-8").trim().match(/^gitdir:\s*(.+)$/m);if(!i)return;const n=i[1].trim();return(0,a.isAbsolute)(n)?n:(0,a.resolve)((0,a.dirname)(t),n)}catch(e){f.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,l.existsSync)(e))return;const i=q((0,l.readFileSync)(e,"utf-8"));return i?O(i):void 0}catch(e){f.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 i=(0,a.join)(e,"commondir");let n;try{if((0,l.existsSync)(i)){const o=(0,l.readFileSync)(i,"utf-8").trim();n=(0,a.isAbsolute)(o)?o:(0,a.resolve)(e,o)}else n=e}catch(o){f.logger.debug(`resolveProjectName: commondir at ${i} unreadable: ${o instanceof Error?o.message:o}`);return}const r=E(n);if(r)return r;const s=(0,a.basename)(n)===".git"?(0,a.basename)((0,a.dirname)(n)):(0,a.basename)(n);return s.length>0?s:void 0}c(U,"repoNameFromWorktreePointer");function q(t){const e=t.split(/\r?\n/);let i=null,n=null,r,s;for(const o of e){const u=o.trim();if(u.length===0||u.startsWith("#")||u.startsWith(";"))continue;const p=u.match(/^\[([^\s\]]+)(?:\s+"([^"]*)")?\]$/);if(p){i=p[1].toLowerCase(),n=p[2]??null;continue}if(i!=="remote"||n===null)continue;const m=u.match(/^url\s*=\s*(.+?)\s*$/i);if(!m)continue;const g=m[1];n==="origin"&&(r=g),s===void 0&&(s=g)}return r??s}c(q,"readFirstRemoteUrl");function O(t){let e=t.trim();if(e.length===0)return;const i=e.match(/^[^/@]+@[^/:]+:(.+)$/);if(i?e=i[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(O,"repoNameFromRemoteUrl");function T(t){const e=(0,a.basename)((0,a.dirname)(t)),i=(0,a.dirname)((0,a.dirname)((0,a.dirname)((0,a.dirname)(t)))),n=(0,a.dirname)(t),{getUserEmail:r,getUsageType:s,getUsagePlan:o}=require("./session-state"),u=r(n),p=s(n),m=o(n),g={id:(0,y.randomUUID)(),session_id:e,project_name:V(i)};return u&&(g.user_email=u),p&&(g.usage_type=p),m&&(g.usage_plan=m),g}c(T,"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 i=T(t);e.id||(e.id=i.id),e.session_id||(e.session_id=i.session_id),e.project_name||(e.project_name=i.project_name);try{(0,l.mkdirSync)((0,a.dirname)(t),{recursive:!0}),(0,l.appendFileSync)(t,JSON.stringify(e)+`
|
|
2
|
+
`)}catch(n){f.logger.debug(`failed to append action to ${t}: ${n}`)}if(e.type!=="tool_call"){const n=(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,n,r)}catch(s){s instanceof b.RetriableCollectorError?J(r,n,e,s):f.logger.debug(`failed to send action to collector: ${s}`)}}}c(I,"appendAction");function J(t,e,i,n){if(!(0,w.isJobQueueEnabled)(t)){f.logger.debug(`collector fallback: jobQueue disabled, dropping type=${i.type} id=${i.id} (cause: ${n.message})`);return}try{(0,k.submit)(t,e,S.SEND_EVENT_TYPE,i),f.logger.debug(`collector fallback: enqueued type=${i.type} id=${i.id} as send_event (cause: ${n.message})`)}catch(r){if(r instanceof C.JobTooLargeError){f.logger.debug(`collector fallback: event too large for queue (${r.sizeBytes} bytes), dropping type=${i.type} id=${i.id}`);return}f.logger.debug(`collector fallback: queue submit failed for type=${i.type} id=${i.id}: ${r}`)}}c(J,"enqueueCollectorFallback");function G(t,e,i){if((0,l.existsSync)(t))try{const r=(0,l.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 i-o.timestamp}catch{}}catch(n){f.logger.debug(`failed to find duration for ${e}: ${n}`)}}c(G,"findDurationSinceLastAction");function $(t,e){return h(t,i=>i.type===e)}c($,"readActionsSinceLastMarker");function W(t){if((0,l.existsSync)(t))try{const i=(0,l.readFileSync)(t,"utf-8").trim().split(`
|
|
4
|
+
`).filter(n=>n.length>0);for(let n=i.length-1;n>=0;n--)try{const r=JSON.parse(i[n]);if(typeof r.timestamp=="number")return r.timestamp}catch{}}catch(e){f.logger.debug(`failed to read last action timestamp from ${t}: ${e}`)}}c(W,"findLastActionTimestamp");function h(t,e){if(!(0,l.existsSync)(t))return[];try{const n=(0,l.readFileSync)(t,"utf-8").trim().split(`
|
|
5
|
+
`).filter(o=>o.length>0);let r=-1;const s=[];for(let o=0;o<n.length;o++)try{const u=JSON.parse(n[o]);s.push(u),e(u)&&(r=o)}catch{}return s.slice(r+1)}catch(i){return f.logger.debug(`failed to read actions from ${t}: ${i}`),[]}}c(h,"readActionsSinceMatch");function A(t){return t.type!=="verification_requested"?!1:t.action==="allow"}c(A,"isAllowVerificationRequested");function x(t){if(t.type!=="tool_call")return!1;const e=t.verification_id;return typeof e=="string"&&e.length>0}c(x,"isVerificationScopedToolCall");function z(t){return h(t,A).filter(x)}c(z,"getToolCallsSinceLastVerification");function K(t){return $(t,"file_change").filter(x)}c(K,"getToolCallsSinceLastFileChange");function B(t){return $(t,"verdict_write").some(x)}c(B,"hasToolCallsSinceLastVerdict");function Q(t){return h(t,A).some(i=>i.type==="file_change")}c(Q,"hasFileChangesSinceLastVerification");function H(t){return h(t,A).filter(i=>i.type==="file_change")}c(H,"getFileChangesSinceLastVerification");function Y(t){return t.type!=="verdict_write"?!1:t.verdict?.status==="fail"}c(Y,"isFailVerdictWrite");function X(t){if(!(0,l.existsSync)(t))return[];try{const n=(0,l.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<n.length;o++){const u=n[o];u!==null&&Y(u)&&(r=o)}if(r<0)return[];const s=[];for(let o=r+1;o<n.length;o++){const u=n[o];u!==null&&u.type==="file_change"&&s.push(u)}return s}catch(e){return f.logger.debug(`failed to read file changes since last fail verdict: ${e}`),[]}}c(X,"getFileChangesSinceLastFailVerdict");function Z(t){if(!(0,l.existsSync)(t))return!1;try{const i=(0,l.readFileSync)(t,"utf-8").trim().split(`
|
|
7
|
+
`).filter(n=>n.length>0);for(const n of i){let r;try{r=JSON.parse(n)}catch{continue}if(r.type==="verification_start")return!0;if(r.type==="tool_call"){const s=r;if(s.tool_type==="mcp"&&typeof s.mcp_server=="string"&&s.mcp_server.endsWith("-devtools"))return!0}}return!1}catch(e){return f.logger.debug(`failed to scan verifier engagement in ${t}: ${e}`),!1}}c(Z,"hasVerifierEverEngaged");function ee(t){if(t.length===0)return[];const e=new Map,i=[];for(const n of t){const r=n.file_path;let s=e.get(r);s||(s={op:n.operation,added:0,removed:0},e.set(r,s),i.push(r)),s.op=s.op==="create"||n.operation==="create"?"create":n.operation,s.added+=typeof n.lines_added=="number"?n.lines_added:0,s.removed+=typeof n.lines_removed=="number"?n.lines_removed:0}return i.map(n=>{const r=e.get(n);return`${r.op} ${n} (+${r.added}/-${r.removed})`})}c(ee,"summarizeFixFileChanges");0&&(module.exports={ActivityAwareEvent,Event,EventType,EventTypeValue,FixAwareEvent,VerificationAwareEvent,appendAction,baseFields,deterministicSessionEndId,findDurationSinceLastAction,findLastActionTimestamp,getFileChangesSinceLastFailVerdict,getFileChangesSinceLastVerification,getToolCallsSinceLastFileChange,getToolCallsSinceLastVerification,hasFileChangesSinceLastVerification,hasToolCallsSinceLastVerdict,hasVerifierEverEngaged,readActionsSinceLastMarker,resolveProjectName,summarizeFixFileChanges});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var a=Object.defineProperty;var b=Object.getOwnPropertyDescriptor;var p=Object.getOwnPropertyNames;var E=Object.prototype.hasOwnProperty;var l=(n,t)=>a(n,"name",{value:t,configurable:!0});var h=(n,t)=>{for(var r in t)a(n,r,{get:t[r],enumerable:!0})},S=(n,t,r,e)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of p(t))!E.call(n,i)&&i!==r&&a(n,i,{get:()=>t[i],enumerable:!(e=b(t,i))||e.enumerable});return n};var A=n=>S(a({},"__esModule",{value:!0}),n);var L={};h(L,{executeToolBareName:()=>O,extractNestedToolCalls:()=>j,extractNestedToolCallsFromResponse:()=>P,isNestedToolContainer:()=>R});module.exports=A(L);var g=require("../../lib/config");const d=Object.fromEntries(Object.entries(g.CYCLE_TO_SERVER).map(([n,t])=>[t,g.CYCLE_TOOL_PREFIXES[n]])),w=/callTool\s*\(\s*\\?[`'"]([^`'"\\]+)\\?[`'"]/g;function O(n){if(!n)return null;const t=d[n];return t!==void 0?`${t}execute`:null}l(O,"executeToolBareName");function R(n,t){if(!n||!t)return!1;const r=d[t];return r===void 0?!1:n===`${r}execute`||n===`${r}scenario-run`}l(R,"isNestedToolContainer");const k=16;function x(n){try{return JSON.parse(n)}catch{return}}l(x,"tryJsonParse");function c(n,t){if(t>k||n===null||n===void 0)return null;if(typeof n=="string"){const r=x(n);return r===void 0?null:c(r,t+1)}if(Array.isArray(n)){for(const r of n)if(r!==null&&typeof r=="object"){const e=r.text;if(typeof e=="string"){const i=x(e);if(i!==void 0){const s=c(i,t+1);if(s!==null)return s}}}return null}if(typeof n=="object"){const r=n;if(Array.isArray(r.toolCalls))return r.toolCalls;if(r.structuredContent!==void 0){const e=c(r.structuredContent,t+1);if(e!==null)return e}if(r.content!==void 0){const e=c(r.content,t+1);if(e!==null)return e}}return null}l(c,"locateToolCallsArray");function y(n,t,r,e,i){if(!(i>k))for(const s of n){if(s===null||typeof s!="object")continue;const u=s,o=u.name;if(typeof o=="string"&&o.length>0){const f={name:N(o,t,r),args:void 0};typeof u.startTime=="number"&&(f.startTime=u.startTime),typeof u.duration=="number"&&(f.duration=u.duration),e.push(f)}Array.isArray(u.children)&&y(u.children,t,r,e,i+1)}}l(y,"flattenResponseToolCalls");function P(n,t){const r=d[t];if(r===void 0)return null;const e=c(n,0);if(e===null)return null;const i=[];return y(e,t,r,i,0),i}l(P,"extractNestedToolCallsFromResponse");function N(n,t,r){let e=n;const i=`mcp__${t}__`,s=`mcp__${t.replace(/-/g,"_")}__`;return e.startsWith(i)?e=e.slice(i.length):e.startsWith(s)&&(e=e.slice(s.length)),e.startsWith("MCP:")&&(e=e.slice(4)),e.startsWith(r)||(e=`${r}${e}`),e}l(N,"normalizeNestedName");function $(n,t){if(n[t]!=="{")return;let r=0;for(let e=t;e<n.length;e++)if(n[e]==="{")r++;else if(n[e]==="}"&&(r--,r===0))return n.slice(t,e+1)}l($,"extractBalancedBraces");function j(n,t){const r=d[t];if(r===void 0)return[];const e=typeof n=="string"?n:JSON.stringify(n??""),i=[],s=new Set;w.lastIndex=0;let u=w.exec(e);for(;u!==null;){const o=N(u[1],t,r);if(!s.has(o)){s.add(o);let f;const _=u.index+u[0].length,C=e.slice(_).trimStart();if(C.startsWith(",")){const T=C.slice(1).trimStart();if(T.startsWith("{")){const m=$(T,0);if(m)try{f=JSON.parse(m)}catch{f=m}}}i.push({name:o,args:f})}u=w.exec(e)}return i}l(j,"extractNestedToolCalls");0&&(module.exports={executeToolBareName,extractNestedToolCalls,extractNestedToolCallsFromResponse,isNestedToolContainer});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";var i=Object.defineProperty;var c=Object.getOwnPropertyDescriptor;var f=Object.getOwnPropertyNames;var g=Object.prototype.hasOwnProperty;var s=(n,e)=>i(n,"name",{value:e,configurable:!0});var p=(n,e)=>{for(var t in e)i(n,t,{get:e[t],enumerable:!0})},u=(n,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of f(e))!g.call(n,o)&&o!==t&&i(n,o,{get:()=>e[o],enumerable:!(r=c(e,o))||r.enumerable});return n};var E=n=>u(i({},"__esModule",{value:!0}),n);var _={};p(_,{isScenarioTool:()=>R});module.exports=E(_);const O=/scenario[-_]/;function R(n){return typeof n=="string"&&O.test(n)}s(R,"isScenarioTool");0&&(module.exports={isScenarioTool});
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
"use strict";var
|
|
2
|
+
"use strict";var N=Object.defineProperty;var t=(r,n)=>N(r,"name",{value:n,configurable:!0});var i=require("commander"),c=require("fs"),l=require("path"),m=require("./commands/install"),g=require("./commands/uninstall"),p=require("./commands/update"),f=require("./commands/hook"),C=require("./commands/status"),u=require("./commands/verify"),b=require("./commands/queue"),v=require("./commands/process-job-file"),y=require("./clients/codex/cli"),h=require("./commands/browser"),$=require("./commands/node"),j=require("./commands/backend"),w=require("./commands/android"),I=require("./commands/verification"),k=require("./commands/telemetry"),B=require("./commands/privacy"),F=require("./commands/config"),T=require("./tui"),q=require("./commands/register"),A=require("./commands/unregister"),L=require("./commands/import"),R=require("./clients/claude/cli"),S=require("./commands/login"),U=require("./commands/scenario"),x=require("./queue"),P=require("./lib/icon"),s=require("./lib/version"),e=require("./lib/output");const d=JSON.parse((0,c.readFileSync)((0,l.join)(__dirname,"../package.json"),"utf-8"));(0,x.registerQueueHandlers)();const o=new i.Command;o.name("ironbee").description(d.description).version(d.version),o.addCommand(S.loginCommand),o.addCommand(m.installCommand),o.addCommand(g.uninstallCommand),o.addCommand(p.updateCommand),o.addCommand(C.statusCommand),o.addCommand(u.verifyCommand),o.addCommand(b.queueCommand),o.addCommand(h.browserCommand),o.addCommand($.nodeCommand),o.addCommand(j.backendCommand),o.addCommand(w.androidCommand),o.addCommand(I.verificationCommand),o.addCommand(k.telemetryCommand),o.addCommand(B.privacyCommand),o.addCommand(R.claudeCommand),o.addCommand(y.codexCommand),o.addCommand(F.configCommand),o.addCommand(T.tuiCommand),o.addCommand(q.registerCommand),o.addCommand(A.unregisterCommand),o.addCommand(L.importCommand),o.addCommand(U.scenarioCommand),o.addCommand(v.processJobFileCommand),o.addCommand(f.hookCommand);const O=process.argv[2]==="hook"||process.argv[2]==="process-job-file"||process.argv[2]==="claude"&&process.argv[3]==="process-analytics"||process.argv[2]==="codex"&&process.argv[3]==="process-analytics"||process.argv[2]==="claude"&&process.argv[3]==="otel"&&process.argv[4]==="run";if(process.argv.length===2){(0,P.printBanner)(),console.log(),console.log(` ${e.pc.bold("IronBee CLI")} ${e.pc.dim(`v${(0,s.getLocalVersion)()}`)}`),console.log(` ${e.pc.dim(d.description)}`),console.log(),console.log(` ${e.pc.bold(e.pc.cyan("Usage:"))} ${e.pc.green("ironbee")} ${e.pc.dim("<command> [options]")}`),console.log(),console.log(` ${e.pc.bold(e.pc.cyan("Commands:"))}`);const r=[{name:"install",args:"[project-dir]",desc:"Install hooks + guidance files into a project (--all: every registered project)"},{name:"uninstall",args:"[project-dir]",desc:"Remove IronBee from a project (--all: every registered project)"},{name:"update",args:"",desc:"Update IronBee CLI to the latest version"},{name:"tui",args:"[area]",desc:"Interactive full-screen UI (config \xB7 platforms \xB7 projects \xB7 sessions \xB7 queue \xB7 import)"},{name:"status",args:"[project-dir]",desc:"Show verification status for active sessions"},{name:"verify",args:"[session-id]",desc:"Dry-run verdict validation"},{name:"browser",args:"<enable|disable>",desc:"Toggle the browser verification cycle (default-on)"},{name:"node",args:"<enable|disable>",desc:"Toggle the Node.js runtime debug cycle (opt-in)"},{name:"backend",args:"<enable|disable>",desc:"Toggle the backend protocol cycle (opt-in)"},{name:"android",args:"<enable|disable>",desc:"Toggle the Android mobile verification cycle (opt-in)"},{name:"verification",args:"<enable|disable>",desc:"Master toggle (enable = enforce; disable = monitoring-only)"},{name:"telemetry",args:"<enable|disable>",desc:"Anonymous PostHog telemetry toggle"},{name:"privacy",args:"<enable|disable>",desc:"Privacy mode \u2014 redact devtools tool detail / screenshots / recordings from collector"},{name:"claude statusline",args:"<enable|disable>",desc:"Claude statusline integration (Claude-only)"},{name:"claude otel",args:"<run|status|stop|retry>",desc:"Claude OTEL collector daemon (Claude-only)"},{name:"config",args:"<get|set|unset|list>",desc:"Read or write project / global / local config"},{name:"register",args:"[-p <dir>]",desc:"Add a project to the user-home inventory (no artifact writes)"},{name:"unregister",args:"[-p <dir>]",desc:"Remove a project from the inventory (no artifact writes)"},{name:"queue",args:"<status|drain|\u2026>",desc:"Inspect / drain the background job queue"},{name:"import",args:"",desc:"Import historical Claude sessions to the collector"}],n=t(a=>a.args.length>0?`${a.name} ${a.args}`:a.name,"term"),H=Math.max(...r.map(a=>n(a).length));for(const a of r){const J=a.args.length>0?`${e.pc.green(a.name)} ${e.pc.dim(a.args)}`:e.pc.green(a.name),M=" ".repeat(H-n(a).length);console.log(` ${J}${M} ${a.desc}`)}console.log(),console.log(` ${e.pc.dim("Run")} ${e.pc.cyan("ironbee <command> --help")} ${e.pc.dim("for more info on a command.")}`),console.log()}else O?o.parse(process.argv):(async()=>{await o.parseAsync(process.argv),(0,s.checkForUpdates)().catch(()=>{});const r=process.argv[2];r==="install"||r==="uninstall"||r==="update"||await(0,m.syncSchemaIfChanged)()})();
|