@ironbee-ai/cli 0.31.0 → 0.33.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 +8 -0
- package/dist/clients/base.js +1 -1
- package/dist/clients/claude/agents/ironbee-scenario.md +40 -11
- package/dist/clients/claude/agents/ironbee-verifier.md +40 -4
- package/dist/clients/claude/commands/ironbee-manage-scenario.md +2 -1
- package/dist/clients/claude/hooks/require-verdict.js +2 -2
- package/dist/clients/claude/hooks/require-verification.js +3 -3
- 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/index.js +4 -4
- package/dist/clients/claude/platforms/scenario.terminal.md +26 -0
- package/dist/clients/claude/platforms/skill.browser.md +1 -1
- package/dist/clients/claude/platforms/skill.terminal.md +62 -0
- package/dist/clients/codex/agents/ironbee-scenario.md +39 -10
- package/dist/clients/codex/agents/ironbee-verifier.md +39 -3
- package/dist/clients/codex/commands/ironbee-manage-scenario/SKILL.main.md +21 -6
- package/dist/clients/codex/commands/ironbee-manage-scenario/SKILL.md +2 -1
- package/dist/clients/codex/commands/ironbee-search-scenario/SKILL.main.md +3 -0
- package/dist/clients/codex/commands/ironbee-sync-scenario/SKILL.main.md +4 -1
- package/dist/clients/codex/commands/ironbee-verify/SKILL.main.md +4 -0
- package/dist/clients/codex/hooks/require-verification.js +1 -1
- package/dist/clients/codex/hooks/track-action.js +1 -1
- package/dist/clients/codex/index.js +2 -2
- package/dist/clients/codex/platforms/command-verify.terminal.md +61 -0
- package/dist/clients/codex/platforms/rule.terminal.md +31 -0
- package/dist/clients/codex/platforms/scenario.terminal.md +36 -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/rules/ironbee-verification.main.md +3 -0
- package/dist/clients/codex/skills/ironbee-verification.main.md +14 -0
- package/dist/clients/codex/util.js +1 -1
- package/dist/clients/cursor/commands/ironbee-manage-scenario/SKILL.md +21 -6
- package/dist/clients/cursor/commands/ironbee-search-scenario/SKILL.md +3 -0
- package/dist/clients/cursor/commands/ironbee-sync-scenario/SKILL.md +4 -1
- package/dist/clients/cursor/commands/ironbee-verify/SKILL.md +4 -0
- 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/track-action-monitor.js +1 -1
- package/dist/clients/cursor/hooks/track-action.js +1 -1
- package/dist/clients/cursor/index.js +1 -1
- package/dist/clients/cursor/platforms/command-verify.terminal.md +61 -0
- package/dist/clients/cursor/platforms/rule.terminal.md +31 -0
- package/dist/clients/cursor/platforms/scenario.terminal.md +29 -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 +14 -0
- package/dist/clients/registry.js +1 -1
- package/dist/commands/config.js +2 -2
- package/dist/commands/hook.js +22 -19
- package/dist/commands/install.js +1 -1
- package/dist/commands/platform-suggest.js +2 -0
- package/dist/commands/scenario.js +1 -1
- package/dist/commands/terminal.js +1 -0
- package/dist/hooks/core/actions.js +9 -7
- package/dist/hooks/core/run-checks.js +7 -0
- package/dist/hooks/core/verification-context.js +19 -15
- package/dist/hooks/core/verify-gate.js +35 -21
- package/dist/import/claude/events/tool-call.js +1 -1
- package/dist/import/codex/events/tool-call.js +1 -1
- package/dist/index.js +1 -1
- package/dist/lib/config.js +1 -1
- package/dist/lib/event.js +1 -1
- package/dist/lib/headless.js +1 -0
- package/dist/lib/install-version.js +1 -1
- package/dist/lib/platform-section.js +5 -4
- package/dist/lib/prompt.js +6 -5
- package/dist/lib/scenario-staleness.js +1 -1
- package/dist/tui/config/schema.js +1 -1
- package/dist/tui/platforms/area.js +2 -2
- package/dist/tui/projects/area.js +4 -4
- package/dist/tui/shell/session.js +5 -5
- package/package.json +1 -1
|
@@ -0,0 +1,54 @@
|
|
|
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 you run in a terminal** — CLIs, REPLs, shells, and full-screen TUIs. It spawns the program attached to a PTY (like tmux) so you can drive it and observe its output. Do **NOT** call `MCP:tdt_*` tools for changes with no terminal-observable behavior.
|
|
8
|
+
|
|
9
|
+
**How to tell whether the change is terminal-observable:**
|
|
10
|
+
- A CLI / command entrypoint (`bin/`, a `main()` that parses argv, a `package.json` `bin` field, a Cobra / Click / argparse / Commander command)
|
|
11
|
+
- A REPL or interactive shell the change affects
|
|
12
|
+
- A full-screen TUI (curses / blessed / ink / bubbletea / ratatui)
|
|
13
|
+
|
|
14
|
+
If the change is web-only UI with no terminal surface, that belongs to the **browser cycle** — do NOT call any `MCP:tdt_*` tools for it.
|
|
15
|
+
|
|
16
|
+
**Misconfiguration recovery.** If this cycle activated but the change has no terminal-observable behavior, the operator enabled the terminal cycle by mistake. The stop hook will keep blocking with `incomplete_tools` until `maxRetries` is exhausted. Don't fabricate a PTY session. Stop and tell the user clearly: the change has no terminal surface; ask them to run `ironbee terminal disable` to unblock the gate.
|
|
17
|
+
|
|
18
|
+
## Terminal flow
|
|
19
|
+
|
|
20
|
+
1. **Pick an evidence path** per changed code area:
|
|
21
|
+
- **Run-evidence path** (a one-shot command confirms the change works):
|
|
22
|
+
- Run the command in a PTY: `MCP:tdt_pty_run` with the command line — it returns the full output AND the exit code in one call.
|
|
23
|
+
- Confirm the output is what the change should produce AND the exit code is expected (`0` for success, or the specific non-zero code a flag/error path should return). The exit code is as important as the text — a command that prints the right thing but exits non-zero is a fail.
|
|
24
|
+
- **Interactive-evidence path** (driving a REPL / shell / full-screen TUI confirms the change is live):
|
|
25
|
+
- Spawn the program attached to a PTY: `MCP:tdt_pty_start` — it returns a `paneId` you use for the rest of the flow.
|
|
26
|
+
- Drive it with input: `MCP:tdt_interaction_send-keys` (tmux key syntax — `Enter`, `C-c`, `Up`, `Tab`, …) for control keys / navigation, or `MCP:tdt_interaction_send-text` to type a literal string.
|
|
27
|
+
- **Synchronize before reading** — call `MCP:tdt_sync_wait-for` to block until the expected output appears, rather than guessing a delay.
|
|
28
|
+
- Capture the output: `MCP:tdt_content_capture` — use `mode: stream` for line-oriented programs (REPLs, shells; pass an incremental `since` cursor to read only new lines) and `mode: screen` for full-screen TUIs (the rendered screen buffer).
|
|
29
|
+
- Confirm the captured output shows the expected result.
|
|
30
|
+
- Stop the pane when done: `MCP:tdt_pty_stop`.
|
|
31
|
+
- **Auxiliary (NOT evidence — sync/setup/correlation only):** `MCP:tdt_sync_wait-for-idle` (wait until output stops changing), `MCP:tdt_content_get-cursor` (current cursor position), `MCP:tdt_pty_resize` / `MCP:tdt_pty_signal` / `MCP:tdt_pty_list` (manage panes). None of these count toward the gate — they help you drive the test, they don't inspect it.
|
|
32
|
+
|
|
33
|
+
**Batch (speed):** on the run-evidence path each `MCP:tdt_pty_run` is one round-trip already. On the interactive path, prefer `MCP:tdt_sync_wait-for` over fixed delays so you read exactly when the output is ready; capture incrementally with `mode: stream` + a `since` cursor instead of re-reading the whole buffer.
|
|
34
|
+
|
|
35
|
+
### Verdict fields
|
|
36
|
+
The verdict is platform-agnostic — submit only semantic judgment:
|
|
37
|
+
|
|
38
|
+
```json
|
|
39
|
+
{
|
|
40
|
+
"session_id": "<sid>",
|
|
41
|
+
"status": "pass",
|
|
42
|
+
"checks": ["`mycli --json` emits valid JSON and exits 0", "REPL `:help` lists the new command"]
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
On fail, include `issues`. On pass after a previous fail, include `fixes`.
|
|
47
|
+
|
|
48
|
+
Terminal-cycle pass criteria:
|
|
49
|
+
- **Run-evidence**: the command was run via `MCP:tdt_pty_run` AND its output and exit code confirm the change behaved correctly.
|
|
50
|
+
- **Interactive-evidence**: a pane was spawned (`MCP:tdt_pty_start`) AND input was driven (`MCP:tdt_interaction_send-keys` / `MCP:tdt_interaction_send-text`) AND output was captured (`MCP:tdt_content_capture`) AND it shows the expected result.
|
|
51
|
+
|
|
52
|
+
## Multi-cycle (browser + terminal simultaneously)
|
|
53
|
+
|
|
54
|
+
Both cycles can be active simultaneously. One `verification-start` covers all active cycles; one platform-agnostic verdict covers them all; one retry counter applies globally.
|
|
@@ -35,6 +35,12 @@ If already running, skip start. If the build fails, fix it before proceeding.
|
|
|
35
35
|
|
|
36
36
|
**Don't guess ports.** After starting, check the actual port via `docker compose ps`, process output, or config files.
|
|
37
37
|
|
|
38
|
+
## Verify end-to-end — trace the blast radius (don't stop at the edited file)
|
|
39
|
+
|
|
40
|
+
A change's defect most often surfaces not on the edited file's own surface but in a **downstream consumer** of what the change produces — wherever its output is read back, stored, rendered, or acted on. Before driving tools, spend ONE quick pass reading/searching the code to map the blast radius: identify what the change produces and which other surfaces consume it, then exercise the FULL flow from where the change is produced through to where its effect is observable — not only the surface the edited file owns. A feature that works at its source but breaks in a downstream consumer is a **FAIL**.
|
|
41
|
+
|
|
42
|
+
This holds even when the consumer was not itself edited: the place you should have updated but didn't never appears in the changed-files list, so don't let that list bound your verification — **follow the data, not the diff.** Keep the mapping quick (a focused scan, not a full audit) so it doesn't eat the speed budget.
|
|
43
|
+
|
|
38
44
|
## Universal flow
|
|
39
45
|
|
|
40
46
|
1. Implement your changes (write/edit code).
|
|
@@ -43,6 +49,11 @@ If already running, skip start. If the build fails, fix it before proceeding.
|
|
|
43
49
|
echo '{"session_id":"<your-session-id>"}' | ironbee hook verification-start
|
|
44
50
|
```
|
|
45
51
|
Devtools tools are blocked without this.
|
|
52
|
+
2.5. **Run the project checks FIRST (lint/test/…)** — the deterministic first step of verification:
|
|
53
|
+
```
|
|
54
|
+
echo '{"session_id":"<your-session-id>"}' | ironbee hook run-checks
|
|
55
|
+
```
|
|
56
|
+
Use a generous shell timeout (they may take minutes). This runs the project's configured `verification.checks` and records the results the gate reads. 🛑 **IF ANY REQUIRED CHECK FAILS, THE VERIFICATION HAS ALREADY FAILED — STOP.** It is **NOT your call** whether the failure is "just a fixture", "unrelated", or "pre-existing" — a required non-zero exit **IS** a failure. Do **NOT** drive the devtools tools, do **NOT** submit a pass; submit a **fail** verdict whose `issues` are the failing checks (the gate enforces the fix). Only when **every** required check PASSES do you continue. (If it reports "no checks configured", just continue.)
|
|
46
57
|
3. Build and start the application if not already running.
|
|
47
58
|
4. **Run the per-cycle flows for every active cycle.** See the platform sections near the bottom of this file — each enabled cycle's section has its own flow steps and mandatory tools. All active cycles must be exercised within this one verification cycle.
|
|
48
59
|
5. Stop the dev server when verification is complete (every cycle — including the final one).
|
|
@@ -92,6 +103,9 @@ Each tool call is a separate LLM round-trip, and that round-trip — not the too
|
|
|
92
103
|
<!--IRONBEE:PLATFORM:android-->
|
|
93
104
|
<!--/IRONBEE:PLATFORM:android-->
|
|
94
105
|
|
|
106
|
+
<!--IRONBEE:PLATFORM:terminal-->
|
|
107
|
+
<!--/IRONBEE:PLATFORM:terminal-->
|
|
108
|
+
|
|
95
109
|
## Important
|
|
96
110
|
- **Always submit a verdict after every verification attempt** — both pass AND fail. Fail verdicts are tracked for analytics.
|
|
97
111
|
- The stop hook checks that the required tools were used for every active cycle and that the verdict carries non-empty `checks`.
|
package/dist/clients/registry.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var s=Object.defineProperty;var h=Object.getOwnPropertyDescriptor;var E=Object.getOwnPropertyNames;var y=Object.prototype.hasOwnProperty;var n=(e,t)=>s(e,"name",{value:t,configurable:!0});var j=(e,t)=>{for(var i in t)s(e,i,{get:t[i],enumerable:!0})},P=(e,t,i,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of E(t))!y.call(e,o)&&o!==i&&s(e,o,{get:()=>t[o],enumerable:!(r=h(t,o))||r.enumerable});return e};var S=e=>P(s({},"__esModule",{value:!0}),e);var A={};j(A,{REGISTERED_CLIENTS:()=>l,SUGGESTION_PRIORITY:()=>c,clientNames:()=>b,detectClients:()=>g,findClient:()=>p,isProjectActive:()=>x,listActiveProjects:()=>T,resolveSuggestionClient:()=>w,resolveTargetClients:()=>v});module.exports=S(A);var u=require("fs"),a=require("path"),C=require("./claude"),d=require("./cursor"),I=require("./codex"),m=require("../lib/projects-registry");const l=[new C.ClaudeClient,new d.CursorClient,new I.CodexClient];function p(e){return l.find(t=>t.name===e)}n(p,"findClient");const c=["claude","codex","cursor"];function w(e){const t=e.filter(r=>typeof r.runHeadlessPrompt=="function");if(t.length===0)return;const i=n(r=>{const o=c.indexOf(r.name);return o===-1?c.length:o},"rank");return t.slice().sort((r,o)=>i(r)-i(o))[0]}n(w,"resolveSuggestionClient");function g(e){return l.filter(t=>t.detect(e))}n(g,"detectClients");function b(){return l.map(e=>e.name).join(", ")}n(b,"clientNames");function v(e,t){if(t==="all")return l;if(t!==void 0){const r=p(t);if(!r)throw new Error(`Unknown client: '${t}'. Available: ${b()}.`);return[r]}const i=g(e);return i.length>0?i:[l[0]]}n(v,"resolveTargetClients");function x(e){return!f(e.path)||!R(e.path)?!1:l.some(t=>D(t,e.path))?!0:f((0,a.join)(e.path,".ironbee"))}n(x,"isProjectActive");function T(){return(0,m.listProjects)().filter(x)}n(T,"listActiveProjects");function D(e,t){try{return e.detect(t)}catch{return!1}}n(D,"safeDetect");function f(e){try{return(0,u.existsSync)(e)}catch{return!1}}n(f,"safeExists");function R(e){try{return(0,u.statSync)(e).isDirectory()}catch{return!1}}n(R,"safeIsDir");0&&(module.exports={REGISTERED_CLIENTS,SUGGESTION_PRIORITY,clientNames,detectClients,findClient,isProjectActive,listActiveProjects,resolveSuggestionClient,resolveTargetClients});
|
package/dist/commands/config.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var m=Object.defineProperty;var
|
|
2
|
-
`)}l(j,"writeConfigFile");function H(e){const o=e.indexOf(".");return o===-1?e:e.slice(0,o)}l(H,"topKey");function $(e){return z.has(H(e))}l($,"affectsArtifacts");function Q(e,o){if(o.length===0)return e;const n=o.split(".");let r=e;for(const i of n){if(r==null||typeof r!="object"||Array.isArray(r))return;r=r[i]}return r}l(Q,"getAtPath");function X(e,o,n){if(o.length===0)throw new Error("Cannot set the root config \u2014 pass a dotted key (e.g. `collector.url`).");const r=o.split(".");let i=e;for(let a=0;a<r.length-1;a++){const s=r[a],c=i[s];(c==null||typeof c!="object"||Array.isArray(c))&&(i[s]={}),i=i[s]}i[r[r.length-1]]=n}l(X,"setAtPath");function Z(e,o){if(o.length===0)return!1;const n=o.split(".");let r=e;for(let a=0;a<n.length-1;a++){const s=n[a],c=r[s];if(c==null||typeof c!="object"||Array.isArray(c))return!1;r=c}const i=n[n.length-1];return Object.prototype.hasOwnProperty.call(r,i)?(delete r[i],!0):!1}l(Z,"unsetAtPath");function ee(e,o){if(o)try{return JSON.parse(e)}catch(n){throw new Error(`--json was set but value is not valid JSON: ${n instanceof Error?n.message:n}`)}try{return JSON.parse(e)}catch{return e}}l(ee,"parseValue");function v(e){if(e.global===!0&&e.local===!0)throw new Error("Pass at most one of --global / --local.");if(e.global===!0)return{path:O(),label:"global",useGlobal:!0};const o=e.projectDir??process.cwd();return e.local===!0?{path:S(o),label:"local",useGlobal:!1}:{path:C(o),label:"project",useGlobal:!1}}l(v,"resolveWriteTarget");function A(e){if((e.global===!0?1:0)+(e.project===!0?1:0)+(e.local===!0?1:0)>1)throw new Error("Pass at most one of --global / --project / --local.");if(e.global===!0)return{label:"global",path:O()};const n=e.projectDir??process.cwd();return e.project===!0?{label:"project",path:C(n)}:e.local===!0?{label:"local",path:S(n)}:{label:"merged",path:n}}l(A,"resolveReadTarget");function P(e){return e.label==="merged"?(0,b.loadConfig)(e.path):w(e.path)}l(P,"readForGet");function oe(e){return e===void 0?"(unset)":typeof e=="string"?e:JSON.stringify(e,null,2)}l(oe,"formatValue");function ne(e,o){const n=(0,f.detectClients)(e);if(o===void 0&&n.length===0)return console.log(` ${t.pc.dim("\xB7")} ${t.pc.dim("no clients detected in")} ${t.pc.dim(e)} ${t.pc.dim("\u2014 skipping artifact rerender")}`),[];const r=(0,b.loadConfig)(e),i=(0,f.resolveTargetClients)(e,o);for(const a of i)a.install(e,r);return(0,G.ensureIronBeeGitignored)(e),i.map(a=>a.name)}l(ne,"rerenderArtifacts");function E(e,o,n,r){const i=(0,g.existsSync)(e)?(0,g.readFileSync)(e,"utf-8"):null;j(e,o);try{return ne(n,r)}catch(a){try{i===null?(0,g.existsSync)(e)&&(0,g.unlinkSync)(e):(0,g.writeFileSync)(e,i)}catch(s){k.logger.debug(`config rollback failed: ${s}`)}throw a}}l(E,"writeAndRerender");const R=10;function te(e){const o=(0,f.listActiveProjects)(),n=(0,J.canonicalizePath)(e);return o.filter(r=>r.path!==n)}l(te,"listOtherProjects");function re(e){const o=e.length===1?"other project":"other projects";console.log(` ${t.pc.yellow("\u26A0")} ${t.pc.bold(String(e.length))} ${o} registered in the inventory still on the prior state:`);const n=e.slice(0,R);for(const r of n)console.log(` ${t.pc.dim("\xB7")} ${t.pc.dim(r.path)}`);e.length>R&&console.log(` ${t.pc.dim("\u2026and")} ${t.pc.bold(String(e.length-R))} ${t.pc.dim("more")}`)}l(re,"printOtherProjectsBanner");async function T(e,o,n){if(!o)return;const r=te(e);if(r.length===0)return;re(r);let i;if(n===!0)i=!0;else if(n===!1){console.log(` ${t.pc.dim("Run")} ${t.pc.cyan("ironbee install --all")} ${t.pc.dim("later to apply, or")} ${t.pc.cyan("ironbee install")} ${t.pc.dim("in selected projects.")}`);return}else if((0,h.isInteractive)()){const a=r.length===1?"this project":`these ${r.length} projects`;i=await(0,h.promptYesNo)(` Apply this change to ${a} now?`,!0)}else{console.log(` ${t.pc.dim("Run")} ${t.pc.cyan("ironbee install --all")} ${t.pc.dim("to apply everywhere, or pass")} ${t.pc.cyan("--apply-all")} ${t.pc.dim("to skip this prompt.")}`);return}if(!i){console.log(` ${t.pc.dim("Skipped. Run")} ${t.pc.cyan("ironbee install --all")} ${t.pc.dim("later to apply.")}`);return}console.log(),await(0,I.runInstallAll)({})}l(T,"maybeApplyToOtherProjects");async function F(e,o,n){if(e.length===0)throw new Error("Key is required (dotted path, e.g. `collector.url`).");const r=v(n),i=ee(o,n.json===!0),a=w(r.path),s=JSON.parse(JSON.stringify(a));X(s,e,i);const c=n.projectDir??process.cwd(),p=n.rerender!==!1&&$(e);let y=[];p?y=E(r.path,s,c,n.client):j(r.path,s),console.log(`${t.pc.green("\u2713")} Set ${t.pc.cyan(e)} = ${t.pc.bold(oe(i))} in ${r.label} config (${t.pc.dim(r.path)}).`),p?y.length>0&&(console.log(` ${t.pc.dim("Re-rendered artifacts for:")} ${t.pc.bold(y.join(", "))}`),console.log(` ${t.pc.yellow("\u26A0")} Restart your editor / agent session for the change to take effect.`)):n.rerender===!1&&$(e)&&console.log(` ${t.pc.yellow("\u26A0")} --no-rerender set: artifacts may now be out of sync with config. Run ${t.pc.cyan("ironbee install")} to resync.`),x(e),r.useGlobal&&await T(c,p,n.applyAll)}l(F,"runSet");function x(e){const o=(0,b.findActiveEnvOverride)(e);o!==void 0&&console.log(` ${t.pc.yellow("\u26A0")} ${t.pc.cyan(o.envVar)} is set in this shell \u2014 env overrides file config, so ${t.pc.cyan(e)} reads will return the env value until ${t.pc.cyan(o.envVar)} is unset.`)}l(x,"warnIfEnvShadowed");async function B(e,o){if(e.length===0)throw new Error("Key is required (dotted path, e.g. `collector.url`).");const n=v(o),r=w(n.path),i=JSON.parse(JSON.stringify(r));if(!Z(i,e)){console.log(`${t.pc.dim("\xB7")} ${t.pc.cyan(e)} not present in ${n.label} config (${t.pc.dim(n.path)}). No-op.`);return}const s=o.projectDir??process.cwd(),c=o.rerender!==!1&&$(e);let p=[];c?p=E(n.path,i,s,o.client):j(n.path,i),console.log(`${t.pc.green("\u2713")} Unset ${t.pc.cyan(e)} in ${n.label} config (${t.pc.dim(n.path)}).`),c&&p.length>0&&(console.log(` ${t.pc.dim("Re-rendered artifacts for:")} ${t.pc.bold(p.join(", "))}`),console.log(` ${t.pc.yellow("\u26A0")} Restart your editor / agent session for the change to take effect.`)),x(e),n.useGlobal&&await T(s,c,o.applyAll)}l(B,"runUnset");function U(e,o){const n=A(o),r=P(n),i=Q(r,e);i===void 0&&(console.error(`${t.pc.dim("\xB7")} ${t.pc.cyan(e)} ${t.pc.dim("not set in")} ${n.label} ${t.pc.dim("config")}`),process.exit(1)),console.log(typeof i=="string"?i:JSON.stringify(i,null,2))}l(U,"runGet");function W(e){const o=A(e),n=P(o);console.log(JSON.stringify(n,null,2))}l(W,"runList");function _(e){const o=v(e);console.log(o.path)}l(_,"runPath");const u=new D.Command("config").description("Read or write IronBee configuration values (project or global). Smart re-render: artifact-affecting keys auto-update installed client files.");u.command("get <key>").description("Print the value at a dotted path. Default: merged effective value (global + project + local). --global / --project / --local narrow to one source.").option("-p, --project-dir <dir>","Project directory (default: cwd).").option("-g, --global","Read from global config only (~/.ironbee/config.json).").option("--project","Read from project config only (<project>/.ironbee/config.json).").option("--local","Read from project-local config only (<project>/.ironbee/config.local.json \u2014 gitignored).").action((e,o)=>{try{U(e,o)}catch(n){console.error(`${t.pc.red("\u2717")} ${n instanceof Error?n.message:n}`),process.exit(1)}}),u.command("set <key> <value>").description("Set a config value. Type-coerces (true/42/[\u2026]/{\u2026}) unless --json forces strict parsing. Re-renders client artifacts when the top-level key affects them. After a global write (`-g`) on an artifact-affecting key, prompts whether to apply the change to every other registered project. Use --local to write to the gitignored personal-override layer instead of the committed project config.").option("-p, --project-dir <dir>","Project directory (default: cwd).").option("-g, --global","Write to global config (~/.ironbee/config.json).").option("--local","Write to the gitignored project-local override (<project>/.ironbee/config.local.json) instead of the committed project config. Mutually exclusive with --global.").option("--client <name>",`Filter clients for artifact rerender (${(0,f.clientNames)()}), or "all". Default: detected clients.`).option("--no-rerender","Skip artifact rerender even when the key normally triggers it.").option("--json","Require value to parse as strict JSON (no string fallback).").option("--apply-all","After a global write on an artifact-affecting key, apply the change to every registered project without prompting.").option("--no-apply-all","After a global write on an artifact-affecting key, do NOT apply to other registered projects (suppress the prompt).").action(async(e,o,n)=>{try{await F(e,o,n)}catch(r){console.error(`${t.pc.red("\u2717")} ${r instanceof Error?r.message:r}`),process.exit(1)}}),u.command("unset <key>").description("Remove a config key. Idempotent \u2014 no-op when the key is absent. Re-renders client artifacts when the top-level key affects them. After a global unset (`-g`) on an artifact-affecting key, prompts whether to apply the change to every other registered project. Use --local to remove from the gitignored personal-override layer instead of the committed project config.").option("-p, --project-dir <dir>","Project directory (default: cwd).").option("-g, --global","Write to global config (~/.ironbee/config.json).").option("--local","Write to the gitignored project-local override (<project>/.ironbee/config.local.json) instead of the committed project config. Mutually exclusive with --global.").option("--client <name>",`Filter clients for artifact rerender (${(0,f.clientNames)()}), or "all". Default: detected clients.`).option("--no-rerender","Skip artifact rerender even when the key normally triggers it.").option("--apply-all","After a global unset on an artifact-affecting key, apply the change to every registered project without prompting.").option("--no-apply-all","After a global unset on an artifact-affecting key, do NOT apply to other registered projects (suppress the prompt).").action(async(e,o)=>{try{await B(e,o)}catch(n){console.error(`${t.pc.red("\u2717")} ${n instanceof Error?n.message:n}`),process.exit(1)}}),u.command("list").description("Print the entire config. Default: merged effective config; --global / --project / --local narrow to one source.").option("-p, --project-dir <dir>","Project directory (default: cwd).").option("-g, --global","Read from global config only.").option("--project","Read from project config only.").option("--local","Read from project-local config only (<project>/.ironbee/config.local.json \u2014 gitignored).").action(e=>{try{W(e)}catch(o){console.error(`${t.pc.red("\u2717")} ${o instanceof Error?o.message:o}`),process.exit(1)}}),u.command("path").description("Print the on-disk path of the targeted config file (project by default; --global for global, --local for the gitignored personal-override layer).").option("-p, --project-dir <dir>","Project directory (default: cwd).").option("-g, --global","Print global config path (~/.ironbee/config.json).").option("--local","Print project-local config path (<project>/.ironbee/config.local.json).").action(e=>{try{_(e)}catch(o){console.error(`${t.pc.red("\u2717")} ${o instanceof Error?o.message:o}`),process.exit(1)}});0&&(module.exports={configCommand,runGet,runList,runPath,runSet,runUnset});
|
|
1
|
+
"use strict";var m=Object.defineProperty;var V=Object.getOwnPropertyDescriptor;var K=Object.getOwnPropertyNames;var L=Object.prototype.hasOwnProperty;var l=(e,o)=>m(e,"name",{value:o,configurable:!0});var q=(e,o)=>{for(var n in o)m(e,n,{get:o[n],enumerable:!0})},M=(e,o,n,r)=>{if(o&&typeof o=="object"||typeof o=="function")for(let i of K(o))!L.call(e,i)&&i!==n&&m(e,i,{get:()=>o[i],enumerable:!(r=V(o,i))||r.enumerable});return e};var Y=e=>M(m({},"__esModule",{value:!0}),e);var le={};q(le,{configCommand:()=>u,runGet:()=>B,runList:()=>U,runPath:()=>W,runSet:()=>F,runUnset:()=>_});module.exports=Y(le);var D=require("commander"),g=require("fs"),N=require("os"),d=require("path"),f=require("../clients/registry"),I=require("./install"),b=require("../lib/config"),G=require("../lib/gitignore"),k=require("../lib/logger"),t=require("../lib/output"),h=require("../lib/prompt"),J=require("../lib/projects-registry");const z=new Set(["verification","collector","browser","node","backend","android","terminal","browserDevTools","nodeDevTools","backendDevTools","androidDevTools","terminalDevTools","telemetry","privacy","statusLine","otel","codex","runtime"]);function S(e){return(0,d.join)((0,d.resolve)(e),".ironbee","config.json")}l(S,"projectConfigPath");function C(e){return(0,d.join)((0,d.resolve)(e),".ironbee","config.local.json")}l(C,"projectLocalConfigPath");function O(){return(0,d.join)((0,N.homedir)(),".ironbee","config.json")}l(O,"globalConfigPath");function w(e){if(!(0,g.existsSync)(e))return{};try{const o=(0,g.readFileSync)(e,"utf-8");return o.trim().length===0?{}:JSON.parse(o)}catch(o){throw k.logger.debug(`failed to read ${e}: ${o}`),new Error(`Config at ${e} is not valid JSON: ${o instanceof Error?o.message:o}`)}}l(w,"readConfigFile");function j(e,o){(0,g.mkdirSync)((0,d.join)(e,".."),{recursive:!0}),(0,g.writeFileSync)(e,JSON.stringify(o,null,2)+`
|
|
2
|
+
`)}l(j,"writeConfigFile");function H(e){const o=e.indexOf(".");return o===-1?e:e.slice(0,o)}l(H,"topKey");const X=["verification.context","verification.checks"];function v(e){for(const o of X)if(e===o||e.startsWith(o+"."))return!1;return z.has(H(e))}l(v,"affectsArtifacts");function Q(e,o){if(o.length===0)return e;const n=o.split(".");let r=e;for(const i of n){if(r==null||typeof r!="object"||Array.isArray(r))return;r=r[i]}return r}l(Q,"getAtPath");function Z(e,o,n){if(o.length===0)throw new Error("Cannot set the root config \u2014 pass a dotted key (e.g. `collector.url`).");const r=o.split(".");let i=e;for(let a=0;a<r.length-1;a++){const s=r[a],c=i[s];(c==null||typeof c!="object"||Array.isArray(c))&&(i[s]={}),i=i[s]}i[r[r.length-1]]=n}l(Z,"setAtPath");function ee(e,o){if(o.length===0)return!1;const n=o.split(".");let r=e;for(let a=0;a<n.length-1;a++){const s=n[a],c=r[s];if(c==null||typeof c!="object"||Array.isArray(c))return!1;r=c}const i=n[n.length-1];return Object.prototype.hasOwnProperty.call(r,i)?(delete r[i],!0):!1}l(ee,"unsetAtPath");function oe(e,o){if(o)try{return JSON.parse(e)}catch(n){throw new Error(`--json was set but value is not valid JSON: ${n instanceof Error?n.message:n}`)}try{return JSON.parse(e)}catch{return e}}l(oe,"parseValue");function $(e){if(e.global===!0&&e.local===!0)throw new Error("Pass at most one of --global / --local.");if(e.global===!0)return{path:O(),label:"global",useGlobal:!0};const o=e.projectDir??process.cwd();return e.local===!0?{path:C(o),label:"local",useGlobal:!1}:{path:S(o),label:"project",useGlobal:!1}}l($,"resolveWriteTarget");function E(e){if((e.global===!0?1:0)+(e.project===!0?1:0)+(e.local===!0?1:0)>1)throw new Error("Pass at most one of --global / --project / --local.");if(e.global===!0)return{label:"global",path:O()};const n=e.projectDir??process.cwd();return e.project===!0?{label:"project",path:S(n)}:e.local===!0?{label:"local",path:C(n)}:{label:"merged",path:n}}l(E,"resolveReadTarget");function A(e){return e.label==="merged"?(0,b.loadConfig)(e.path):w(e.path)}l(A,"readForGet");function ne(e){return e===void 0?"(unset)":typeof e=="string"?e:JSON.stringify(e,null,2)}l(ne,"formatValue");function te(e,o){const n=(0,f.detectClients)(e);if(o===void 0&&n.length===0)return console.log(` ${t.pc.dim("\xB7")} ${t.pc.dim("no clients detected in")} ${t.pc.dim(e)} ${t.pc.dim("\u2014 skipping artifact rerender")}`),[];const r=(0,b.loadConfig)(e),i=(0,f.resolveTargetClients)(e,o);for(const a of i)a.install(e,r);return(0,G.ensureIronBeeGitignored)(e),i.map(a=>a.name)}l(te,"rerenderArtifacts");function P(e,o,n,r){const i=(0,g.existsSync)(e)?(0,g.readFileSync)(e,"utf-8"):null;j(e,o);try{return te(n,r)}catch(a){try{i===null?(0,g.existsSync)(e)&&(0,g.unlinkSync)(e):(0,g.writeFileSync)(e,i)}catch(s){k.logger.debug(`config rollback failed: ${s}`)}throw a}}l(P,"writeAndRerender");const R=10;function re(e){const o=(0,f.listActiveProjects)(),n=(0,J.canonicalizePath)(e);return o.filter(r=>r.path!==n)}l(re,"listOtherProjects");function ie(e){const o=e.length===1?"other project":"other projects";console.log(` ${t.pc.yellow("\u26A0")} ${t.pc.bold(String(e.length))} ${o} registered in the inventory still on the prior state:`);const n=e.slice(0,R);for(const r of n)console.log(` ${t.pc.dim("\xB7")} ${t.pc.dim(r.path)}`);e.length>R&&console.log(` ${t.pc.dim("\u2026and")} ${t.pc.bold(String(e.length-R))} ${t.pc.dim("more")}`)}l(ie,"printOtherProjectsBanner");async function T(e,o,n){if(!o)return;const r=re(e);if(r.length===0)return;ie(r);let i;if(n===!0)i=!0;else if(n===!1){console.log(` ${t.pc.dim("Run")} ${t.pc.cyan("ironbee install --all")} ${t.pc.dim("later to apply, or")} ${t.pc.cyan("ironbee install")} ${t.pc.dim("in selected projects.")}`);return}else if((0,h.isInteractive)()){const a=r.length===1?"this project":`these ${r.length} projects`;i=await(0,h.promptYesNo)(` Apply this change to ${a} now?`,!0)}else{console.log(` ${t.pc.dim("Run")} ${t.pc.cyan("ironbee install --all")} ${t.pc.dim("to apply everywhere, or pass")} ${t.pc.cyan("--apply-all")} ${t.pc.dim("to skip this prompt.")}`);return}if(!i){console.log(` ${t.pc.dim("Skipped. Run")} ${t.pc.cyan("ironbee install --all")} ${t.pc.dim("later to apply.")}`);return}console.log(),await(0,I.runInstallAll)({})}l(T,"maybeApplyToOtherProjects");async function F(e,o,n){if(e.length===0)throw new Error("Key is required (dotted path, e.g. `collector.url`).");const r=$(n),i=oe(o,n.json===!0),a=w(r.path),s=JSON.parse(JSON.stringify(a));Z(s,e,i);const c=n.projectDir??process.cwd(),p=n.rerender!==!1&&v(e);let y=[];p?y=P(r.path,s,c,n.client):j(r.path,s),console.log(`${t.pc.green("\u2713")} Set ${t.pc.cyan(e)} = ${t.pc.bold(ne(i))} in ${r.label} config (${t.pc.dim(r.path)}).`),p?y.length>0&&(console.log(` ${t.pc.dim("Re-rendered artifacts for:")} ${t.pc.bold(y.join(", "))}`),console.log(` ${t.pc.yellow("\u26A0")} Restart your editor / agent session for the change to take effect.`)):n.rerender===!1&&v(e)&&console.log(` ${t.pc.yellow("\u26A0")} --no-rerender set: artifacts may now be out of sync with config. Run ${t.pc.cyan("ironbee install")} to resync.`),x(e),r.useGlobal&&await T(c,p,n.applyAll)}l(F,"runSet");function x(e){const o=(0,b.findActiveEnvOverride)(e);o!==void 0&&console.log(` ${t.pc.yellow("\u26A0")} ${t.pc.cyan(o.envVar)} is set in this shell \u2014 env overrides file config, so ${t.pc.cyan(e)} reads will return the env value until ${t.pc.cyan(o.envVar)} is unset.`)}l(x,"warnIfEnvShadowed");async function _(e,o){if(e.length===0)throw new Error("Key is required (dotted path, e.g. `collector.url`).");const n=$(o),r=w(n.path),i=JSON.parse(JSON.stringify(r));if(!ee(i,e)){console.log(`${t.pc.dim("\xB7")} ${t.pc.cyan(e)} not present in ${n.label} config (${t.pc.dim(n.path)}). No-op.`);return}const s=o.projectDir??process.cwd(),c=o.rerender!==!1&&v(e);let p=[];c?p=P(n.path,i,s,o.client):j(n.path,i),console.log(`${t.pc.green("\u2713")} Unset ${t.pc.cyan(e)} in ${n.label} config (${t.pc.dim(n.path)}).`),c&&p.length>0&&(console.log(` ${t.pc.dim("Re-rendered artifacts for:")} ${t.pc.bold(p.join(", "))}`),console.log(` ${t.pc.yellow("\u26A0")} Restart your editor / agent session for the change to take effect.`)),x(e),n.useGlobal&&await T(s,c,o.applyAll)}l(_,"runUnset");function B(e,o){const n=E(o),r=A(n),i=Q(r,e);i===void 0&&(console.error(`${t.pc.dim("\xB7")} ${t.pc.cyan(e)} ${t.pc.dim("not set in")} ${n.label} ${t.pc.dim("config")}`),process.exit(1)),console.log(typeof i=="string"?i:JSON.stringify(i,null,2))}l(B,"runGet");function U(e){const o=E(e),n=A(o);console.log(JSON.stringify(n,null,2))}l(U,"runList");function W(e){const o=$(e);console.log(o.path)}l(W,"runPath");const u=new D.Command("config").description("Read or write IronBee configuration values (project or global). Smart re-render: artifact-affecting keys auto-update installed client files.");u.command("get <key>").description("Print the value at a dotted path. Default: merged effective value (global + project + local). --global / --project / --local narrow to one source.").option("-p, --project-dir <dir>","Project directory (default: cwd).").option("-g, --global","Read from global config only (~/.ironbee/config.json).").option("--project","Read from project config only (<project>/.ironbee/config.json).").option("--local","Read from project-local config only (<project>/.ironbee/config.local.json \u2014 gitignored).").action((e,o)=>{try{B(e,o)}catch(n){console.error(`${t.pc.red("\u2717")} ${n instanceof Error?n.message:n}`),process.exit(1)}}),u.command("set <key> <value>").description("Set a config value. Type-coerces (true/42/[\u2026]/{\u2026}) unless --json forces strict parsing. Re-renders client artifacts when the top-level key affects them. After a global write (`-g`) on an artifact-affecting key, prompts whether to apply the change to every other registered project. Use --local to write to the gitignored personal-override layer instead of the committed project config.").option("-p, --project-dir <dir>","Project directory (default: cwd).").option("-g, --global","Write to global config (~/.ironbee/config.json).").option("--local","Write to the gitignored project-local override (<project>/.ironbee/config.local.json) instead of the committed project config. Mutually exclusive with --global.").option("--client <name>",`Filter clients for artifact rerender (${(0,f.clientNames)()}), or "all". Default: detected clients.`).option("--no-rerender","Skip artifact rerender even when the key normally triggers it.").option("--json","Require value to parse as strict JSON (no string fallback).").option("--apply-all","After a global write on an artifact-affecting key, apply the change to every registered project without prompting.").option("--no-apply-all","After a global write on an artifact-affecting key, do NOT apply to other registered projects (suppress the prompt).").action(async(e,o,n)=>{try{await F(e,o,n)}catch(r){console.error(`${t.pc.red("\u2717")} ${r instanceof Error?r.message:r}`),process.exit(1)}}),u.command("unset <key>").description("Remove a config key. Idempotent \u2014 no-op when the key is absent. Re-renders client artifacts when the top-level key affects them. After a global unset (`-g`) on an artifact-affecting key, prompts whether to apply the change to every other registered project. Use --local to remove from the gitignored personal-override layer instead of the committed project config.").option("-p, --project-dir <dir>","Project directory (default: cwd).").option("-g, --global","Write to global config (~/.ironbee/config.json).").option("--local","Write to the gitignored project-local override (<project>/.ironbee/config.local.json) instead of the committed project config. Mutually exclusive with --global.").option("--client <name>",`Filter clients for artifact rerender (${(0,f.clientNames)()}), or "all". Default: detected clients.`).option("--no-rerender","Skip artifact rerender even when the key normally triggers it.").option("--apply-all","After a global unset on an artifact-affecting key, apply the change to every registered project without prompting.").option("--no-apply-all","After a global unset on an artifact-affecting key, do NOT apply to other registered projects (suppress the prompt).").action(async(e,o)=>{try{await _(e,o)}catch(n){console.error(`${t.pc.red("\u2717")} ${n instanceof Error?n.message:n}`),process.exit(1)}}),u.command("list").description("Print the entire config. Default: merged effective config; --global / --project / --local narrow to one source.").option("-p, --project-dir <dir>","Project directory (default: cwd).").option("-g, --global","Read from global config only.").option("--project","Read from project config only.").option("--local","Read from project-local config only (<project>/.ironbee/config.local.json \u2014 gitignored).").action(e=>{try{U(e)}catch(o){console.error(`${t.pc.red("\u2717")} ${o instanceof Error?o.message:o}`),process.exit(1)}}),u.command("path").description("Print the on-disk path of the targeted config file (project by default; --global for global, --local for the gitignored personal-override layer).").option("-p, --project-dir <dir>","Project directory (default: cwd).").option("-g, --global","Print global config path (~/.ironbee/config.json).").option("--local","Print project-local config path (<project>/.ironbee/config.local.json).").action(e=>{try{W(e)}catch(o){console.error(`${t.pc.red("\u2717")} ${o instanceof Error?o.message:o}`),process.exit(1)}});0&&(module.exports={configCommand,runGet,runList,runPath,runSet,runUnset});
|
package/dist/commands/hook.js
CHANGED
|
@@ -1,24 +1,27 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var h=Object.defineProperty;var C=Object.getOwnPropertyDescriptor;var R=Object.getOwnPropertyNames;var P=Object.prototype.hasOwnProperty;var E=(i,e)=>h(i,"name",{value:e,configurable:!0});var x=(i,e)=>{for(var t in e)h(i,t,{get:e[t],enumerable:!0})},O=(i,e,t,o)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of R(e))!P.call(i,n)&&n!==t&&h(i,n,{get:()=>e[n],enumerable:!(o=C(e,n))||o.enumerable});return i};var D=i=>O(h({},"__esModule",{value:!0}),i);var _={};x(_,{hookCommand:()=>r});module.exports=D(_);var S=require("commander"),w=require("../clients/registry"),I=require("../hooks/core/submit-verdict"),y=require("../hooks/core/verification-lifecycle"),N=require("../hooks/core/run-checks"),p=require("../lib/config"),l=require("../lib/logger"),a=require("../lib/output"),u=require("../lib/stdin"),j=require("../hooks/core/session-state"),m=require("../clients/session-id"),f=require("../clients/agent-project-dir"),v=require("../lib/runtime-paths");function g(i,e){return(0,p.getVerificationEnabled)((0,p.loadConfig)(i))?!1:(l.logger.debug(`${e}: verification is disabled \u2014 silent no-op`),!0)}E(g,"isVerificationDisabled");function s(i){const e=i??process.env.IRONBEE_CLIENT;e||(process.stderr.write(`Error: client not specified. Use --client <name> or set IRONBEE_CLIENT env var.
|
|
2
2
|
`),process.exit(1));const t=(0,w.findClient)(e);return t||(process.stderr.write(`Error: unknown client "${e}". Run \`ironbee install\` to set up.
|
|
3
|
-
`),process.exit(1)),t}E(s,"resolveClient");const r=new S.Command("hook").description("Internal hook runners (invoked by the AI coding client)");r.command("verify-gate").description("Stop hook \u2014 gates task completion until browser verification passes").option("--client <name>","client name (overrides IRONBEE_CLIENT env var)").action(async i=>{const e=s(i.client);if(!e)return;const t=e.resolveProjectDir();await e.runVerifyGate(t)}),r.command("clear-verdict").description("PostToolUse hook \u2014 clears stale verdict after code edits").option("--client <name>","client name (overrides IRONBEE_CLIENT env var)").action(async i=>{const e=s(i.client);if(!e)return;const t=e.resolveProjectDir();await e.runClearVerdict(t)}),r.command("track-action").description("PostToolUse hook \u2014 tracks browser-devtools tool calls in actions.jsonl").option("--client <name>","client name (overrides IRONBEE_CLIENT env var)").action(async i=>{const e=s(i.client);if(!e)return;const t=e.resolveProjectDir();await e.runTrackAction(t)}),r.command("track-action-monitor").description("PostToolUse hook (monitoring-only mode) \u2014 submits send_event jobs for non-devtools tools and falls back to starting an activity if needed").option("--client <name>","client name (overrides IRONBEE_CLIENT env var)").action(async i=>{const e=s(i.client);if(!e)return;const t=e.resolveProjectDir();await e.runTrackActionMonitor(t)}),r.command("track-action-pre").description("Codex PreToolUse hook \u2014 stashes a hrtime timestamp keyed by tool_use_id so the matching PostToolUse can derive tool_call.duration (Codex hook stdin does not carry duration_ms). No-op on Claude/Cursor \u2014 their hosts provide duration natively.").option("--client <name>","client name (overrides IRONBEE_CLIENT env var)").action(async i=>{const e=s(i.client);if(!e)return;const t=e.resolveProjectDir();await e.runTrackActionPre(t)}),r.command("subagent-start").description("SubagentStart hook \u2014 dispatched per client via IClient.runSubagentStart. Codex: writes the agent_id \u2192 parent session_id bridge (codex-threads.json). Claude: joins the sub-agent as an activity participant so the activity closes only when the last participant (main + all sub-agents) leaves.").option("--client <name>","client name (overrides IRONBEE_CLIENT env var)").action(async i=>{const e=s(i.client);if(!e)return;const t=e.resolveProjectDir();await e.runSubagentStart?.(t)}),r.command("subagent-stop").description("SubagentStop hook \u2014 dispatched per client via IClient.runSubagentStop. Codex: prune the thread map + record the sub-agent's agent_transcript_path for the analytics fold. Claude: backstop that closes a verifier-owned activity/cycle the sub-agent left open without a verdict.").option("--client <name>","client name (overrides IRONBEE_CLIENT env var)").action(async i=>{const e=s(i.client);if(!e)return;const t=e.resolveProjectDir();await e.runSubagentStop?.(t)}),r.command("activity-end").description("Stop hook (monitoring-only mode) \u2014 closes the active activity and triggers a background queue flush").option("--client <name>","client name (overrides IRONBEE_CLIENT env var)").action(async i=>{const e=s(i.client);if(!e)return;const t=e.resolveProjectDir();await e.runActivityEnd(t)}),r.command("session-start").description("SessionStart hook \u2014 records session start in actions.jsonl").option("--client <name>","client name (overrides IRONBEE_CLIENT env var)").action(async i=>{const e=s(i.client);if(!e)return;const t=e.resolveProjectDir();await e.runSessionStart(t)}),r.command("session-status").description("Statusline command (Claude) \u2014 emits a session_status event and chains the user's original statusline").action(async()=>{await(0,w.findClient)("claude")?.runSessionStatus?.()}),r.command("require-verdict").description("PreToolUse hook \u2014 blocks file edits until verdict is submitted after browser tool usage").option("--client <name>","client name (overrides IRONBEE_CLIENT env var)").option("--soft","non-blocking assist-mode variant: stash file_change state but never block the edit").action(async i=>{const e=s(i.client);if(!e)return;const t=e.resolveProjectDir();await e.runRequireVerdict(t,{soft:i.soft===!0})}),r.command("session-end").description("SessionEnd hook \u2014 records session end in actions.jsonl").option("--client <name>","client name (overrides IRONBEE_CLIENT env var)").action(async i=>{const e=s(i.client);if(!e)return;const t=e.resolveProjectDir();await e.runSessionEnd(t)}),r.command("activity-start").description("UserPromptSubmit/beforeSubmitPrompt hook \u2014 starts activity tracking on each agent turn").option("--client <name>","client name (overrides IRONBEE_CLIENT env var)").action(async i=>{const e=s(i.client);if(!e)return;const t=e.resolveProjectDir();await e.runActivityStart(t)}),r.command("require-verification").description("PreToolUse hook \u2014 blocks browser tools until verification-start is called").option("--client <name>","client name (overrides IRONBEE_CLIENT env var)").option("--soft","non-blocking assist-mode variant: inject _metadata but never block the devtools call").action(async i=>{const e=s(i.client);if(!e)return;const t=e.resolveProjectDir();await e.runRequireVerification(t,{soft:i.soft===!0})}),r.command("verification-start").description(`Start a verification cycle (called by agent via Bash). Optional --intent flag: "fix" arms the verify-gate's fix-until-pass backstop (a fail verdict keeps blocking even in a zero-edit window); "report" or omitted = verify-only run (clears any stale intent \u2014 "report" is a tolerated alias of omitting the flag, mirroring the command's mode token).`).option("--intent <mode>",'declared intent of this verification run: "fix" or "report"').action(async i=>{const e=(0,f.resolveAgentProjectDir)();if(
|
|
3
|
+
`),process.exit(1)),t}E(s,"resolveClient");const r=new S.Command("hook").description("Internal hook runners (invoked by the AI coding client)");r.command("verify-gate").description("Stop hook \u2014 gates task completion until browser verification passes").option("--client <name>","client name (overrides IRONBEE_CLIENT env var)").action(async i=>{const e=s(i.client);if(!e)return;const t=e.resolveProjectDir();await e.runVerifyGate(t)}),r.command("clear-verdict").description("PostToolUse hook \u2014 clears stale verdict after code edits").option("--client <name>","client name (overrides IRONBEE_CLIENT env var)").action(async i=>{const e=s(i.client);if(!e)return;const t=e.resolveProjectDir();await e.runClearVerdict(t)}),r.command("track-action").description("PostToolUse hook \u2014 tracks browser-devtools tool calls in actions.jsonl").option("--client <name>","client name (overrides IRONBEE_CLIENT env var)").action(async i=>{const e=s(i.client);if(!e)return;const t=e.resolveProjectDir();await e.runTrackAction(t)}),r.command("track-action-monitor").description("PostToolUse hook (monitoring-only mode) \u2014 submits send_event jobs for non-devtools tools and falls back to starting an activity if needed").option("--client <name>","client name (overrides IRONBEE_CLIENT env var)").action(async i=>{const e=s(i.client);if(!e)return;const t=e.resolveProjectDir();await e.runTrackActionMonitor(t)}),r.command("track-action-pre").description("Codex PreToolUse hook \u2014 stashes a hrtime timestamp keyed by tool_use_id so the matching PostToolUse can derive tool_call.duration (Codex hook stdin does not carry duration_ms). No-op on Claude/Cursor \u2014 their hosts provide duration natively.").option("--client <name>","client name (overrides IRONBEE_CLIENT env var)").action(async i=>{const e=s(i.client);if(!e)return;const t=e.resolveProjectDir();await e.runTrackActionPre(t)}),r.command("subagent-start").description("SubagentStart hook \u2014 dispatched per client via IClient.runSubagentStart. Codex: writes the agent_id \u2192 parent session_id bridge (codex-threads.json). Claude: joins the sub-agent as an activity participant so the activity closes only when the last participant (main + all sub-agents) leaves.").option("--client <name>","client name (overrides IRONBEE_CLIENT env var)").action(async i=>{const e=s(i.client);if(!e)return;const t=e.resolveProjectDir();await e.runSubagentStart?.(t)}),r.command("subagent-stop").description("SubagentStop hook \u2014 dispatched per client via IClient.runSubagentStop. Codex: prune the thread map + record the sub-agent's agent_transcript_path for the analytics fold. Claude: backstop that closes a verifier-owned activity/cycle the sub-agent left open without a verdict.").option("--client <name>","client name (overrides IRONBEE_CLIENT env var)").action(async i=>{const e=s(i.client);if(!e)return;const t=e.resolveProjectDir();await e.runSubagentStop?.(t)}),r.command("activity-end").description("Stop hook (monitoring-only mode) \u2014 closes the active activity and triggers a background queue flush").option("--client <name>","client name (overrides IRONBEE_CLIENT env var)").action(async i=>{const e=s(i.client);if(!e)return;const t=e.resolveProjectDir();await e.runActivityEnd(t)}),r.command("session-start").description("SessionStart hook \u2014 records session start in actions.jsonl").option("--client <name>","client name (overrides IRONBEE_CLIENT env var)").action(async i=>{const e=s(i.client);if(!e)return;const t=e.resolveProjectDir();await e.runSessionStart(t)}),r.command("session-status").description("Statusline command (Claude) \u2014 emits a session_status event and chains the user's original statusline").action(async()=>{await(0,w.findClient)("claude")?.runSessionStatus?.()}),r.command("require-verdict").description("PreToolUse hook \u2014 blocks file edits until verdict is submitted after browser tool usage").option("--client <name>","client name (overrides IRONBEE_CLIENT env var)").option("--soft","non-blocking assist-mode variant: stash file_change state but never block the edit").action(async i=>{const e=s(i.client);if(!e)return;const t=e.resolveProjectDir();await e.runRequireVerdict(t,{soft:i.soft===!0})}),r.command("session-end").description("SessionEnd hook \u2014 records session end in actions.jsonl").option("--client <name>","client name (overrides IRONBEE_CLIENT env var)").action(async i=>{const e=s(i.client);if(!e)return;const t=e.resolveProjectDir();await e.runSessionEnd(t)}),r.command("activity-start").description("UserPromptSubmit/beforeSubmitPrompt hook \u2014 starts activity tracking on each agent turn").option("--client <name>","client name (overrides IRONBEE_CLIENT env var)").action(async i=>{const e=s(i.client);if(!e)return;const t=e.resolveProjectDir();await e.runActivityStart(t)}),r.command("require-verification").description("PreToolUse hook \u2014 blocks browser tools until verification-start is called").option("--client <name>","client name (overrides IRONBEE_CLIENT env var)").option("--soft","non-blocking assist-mode variant: inject _metadata but never block the devtools call").action(async i=>{const e=s(i.client);if(!e)return;const t=e.resolveProjectDir();await e.runRequireVerification(t,{soft:i.soft===!0})}),r.command("verification-start").description(`Start a verification cycle (called by agent via Bash). Optional --intent flag: "fix" arms the verify-gate's fix-until-pass backstop (a fail verdict keeps blocking even in a zero-edit window); "report" or omitted = verify-only run (clears any stale intent \u2014 "report" is a tolerated alias of omitting the flag, mirroring the command's mode token).`).option("--intent <mode>",'declared intent of this verification run: "fix" or "report"').action(async i=>{const e=(0,f.resolveAgentProjectDir)();if(g(e,"verification-start")){(0,a.writeAndExit)(JSON.stringify({message:"verification is disabled in this project; ignoring"})+`
|
|
4
4
|
`,0);return}i.intent!==void 0&&i.intent!=="fix"&&i.intent!=="report"&&(process.stderr.write(`Error: --intent must be "fix" or "report".
|
|
5
|
-
`),process.exit(1));const t=i.intent;let o;try{o=JSON.parse((0,
|
|
6
|
-
`),process.exit(1)}const n=(0,
|
|
7
|
-
`),process.exit(1));const c=(0,
|
|
8
|
-
`,0)}),r.command("
|
|
9
|
-
`,0);return}let e;try{e=JSON.parse((0,
|
|
10
|
-
`),process.exit(1)
|
|
11
|
-
|
|
5
|
+
`),process.exit(1));const t=i.intent;let o;try{o=JSON.parse((0,u.readStdin)())}catch{process.stderr.write(`Error: no JSON provided via stdin.
|
|
6
|
+
`),process.exit(1)}const n=(0,m.resolveAgentSessionId)(o,e);n||(process.stderr.write(`Error: JSON must include a "session_id" field, or run as a delegated sub-agent (your client resolves the session automatically).
|
|
7
|
+
`),process.exit(1));const c=(0,v.sessionDir)(e,n);(0,l.setLogFile)(`${c}/session.log`);const d=(0,p.isRecordingEnabled)(e),k=await(0,y.startVerification)({sessionId:n,sessionDir:c,actionsFile:`${c}/actions.jsonl`,recordingEnabled:d,intent:t}),b={verification_id:k.verificationId,trace_id:k.traceId};d&&(b.recording_required=!0,b.message="Recording is required. Call bdt_content_start-recording BEFORE using any other browser tools."),(0,a.writeAndExit)(JSON.stringify(b)+`
|
|
8
|
+
`,0)}),r.command("run-checks").description("Run the configured verification.checks (lint/test/\u2026) at the START of verification and record one check_result per check (called by the verifier via Bash; session_id from stdin JSON OR the client env fallback). Records to actions.jsonl ONLY; the verify-gate reads them to enforce. Always exits 0.").action(async()=>{const i=(0,f.resolveAgentProjectDir)();if(g(i,"run-checks")){(0,a.writeAndExit)(JSON.stringify({message:"verification is disabled in this project; ignoring"})+`
|
|
9
|
+
`,0);return}let e;try{e=JSON.parse((0,u.readStdin)())}catch{e={}}const t=(0,m.resolveAgentSessionId)(e,i);t||(process.stderr.write(`Error: JSON must include a "session_id" field, or run as a delegated sub-agent (your client resolves the session automatically).
|
|
10
|
+
`),process.exit(1));const o=(0,v.sessionDir)(i,t);(0,l.setLogFile)(`${o}/session.log`);const n=await(0,N.runChecks)(i,t);(0,a.writeAndExit)(`${n.summary}
|
|
11
|
+
`,0)}),r.command("verification-end").description("End a verification cycle (called by agent via Bash)").action(async()=>{const i=(0,f.resolveAgentProjectDir)();if(g(i,"verification-end")){(0,a.writeAndExit)(JSON.stringify({message:"verification is disabled in this project; ignoring"})+`
|
|
12
|
+
`,0);return}let e;try{e=JSON.parse((0,u.readStdin)())}catch{process.stderr.write(`Error: no JSON provided via stdin.
|
|
13
|
+
`),process.exit(1)}const t=(0,m.resolveAgentSessionId)(e,i);t||(process.stderr.write(`Error: JSON must include a "session_id" field, or run as a delegated sub-agent (your client resolves the session automatically).
|
|
14
|
+
`),process.exit(1));const o=(0,v.sessionDir)(i,t);(0,l.setLogFile)(`${o}/session.log`);const n=await(0,y.endVerification)({sessionId:t,sessionDir:o,actionsFile:`${o}/actions.jsonl`});n.success?(0,a.writeAndExit)(JSON.stringify({verification_id:n.verificationId,trace_id:n.traceId})+`
|
|
12
15
|
`,0):(process.stderr.write(n.message+`
|
|
13
|
-
`),process.exit(1))}),r.command("submit-verdict").description("Submit verification verdict (called by agent via Bash)").option("--project-dir <dir>","project directory (overrides env vars)").action(async i=>{const e=(0,f.resolveAgentProjectDir)(i.projectDir);if(
|
|
14
|
-
`,0);return}let t;try{t=(0,
|
|
16
|
+
`),process.exit(1))}),r.command("submit-verdict").description("Submit verification verdict (called by agent via Bash)").option("--project-dir <dir>","project directory (overrides env vars)").action(async i=>{const e=(0,f.resolveAgentProjectDir)(i.projectDir);if(g(e,"submit-verdict")){(0,a.writeAndExit)(`verification is disabled in this project; verdict ignored
|
|
17
|
+
`,0);return}let t;try{t=(0,u.readStdin)()}catch{process.stderr.write(`Error: no verdict JSON provided via stdin.
|
|
15
18
|
`),process.exit(1)}let o;try{o=JSON.parse(t)}catch{process.stderr.write(`Error: verdict is not valid JSON.
|
|
16
|
-
`),process.exit(1)}const n=(0,
|
|
17
|
-
`),process.exit(1));const c=(0,
|
|
18
|
-
`,0):(process.stderr.write(
|
|
19
|
-
`),process.exit(1))}),r.command("record-fix").description("Record what was fixed after a fail verdict (called by agent via Bash). Stashed locally in state.json; merged into the next pass verdict's fixes. Emits no collector event.").action(async()=>{const i=(0,f.resolveAgentProjectDir)();if(
|
|
20
|
-
`,0);return}let e;try{e=JSON.parse((0,
|
|
21
|
-
`),process.exit(1)}const t=(0,
|
|
22
|
-
`),process.exit(1));const o=e.fixes,n=Array.isArray(o)?o.filter(
|
|
23
|
-
`),process.exit(1));const c=(0,
|
|
19
|
+
`),process.exit(1)}const n=(0,m.resolveAgentSessionId)(o,e);n||(process.stderr.write(`Error: verdict JSON must include a "session_id" field, or run as a delegated sub-agent (your client resolves the session automatically).
|
|
20
|
+
`),process.exit(1));const c=(0,v.sessionDir)(e,n);(0,l.setLogFile)(`${c}/session.log`);const d=await(0,I.runSubmitVerdict)({sessionId:n,sessionDir:c,verdictFile:`${c}/verdict.json`,actionsFile:`${c}/actions.jsonl`,verdictJson:t,projectDir:e});d.success?(0,a.writeAndExit)(d.message+`
|
|
21
|
+
`,0):(process.stderr.write(d.message+`
|
|
22
|
+
`),process.exit(1))}),r.command("record-fix").description("Record what was fixed after a fail verdict (called by agent via Bash). Stashed locally in state.json; merged into the next pass verdict's fixes. Emits no collector event.").action(async()=>{const i=(0,f.resolveAgentProjectDir)();if(g(i,"record-fix")){(0,a.writeAndExit)(JSON.stringify({message:"verification is disabled in this project; ignoring"})+`
|
|
23
|
+
`,0);return}let e;try{e=JSON.parse((0,u.readStdin)())}catch{process.stderr.write(`Error: no JSON provided via stdin.
|
|
24
|
+
`),process.exit(1)}const t=(0,m.resolveAgentSessionId)(e,i);t||(process.stderr.write(`Error: JSON must include a "session_id" field, or run as a delegated sub-agent (your client resolves the session automatically).
|
|
25
|
+
`),process.exit(1));const o=e.fixes,n=Array.isArray(o)?o.filter(d=>typeof d=="string"&&d.length>0):[];n.length===0&&(process.stderr.write(`Error: JSON must include a non-empty "fixes" array of strings.
|
|
26
|
+
`),process.exit(1));const c=(0,v.sessionDir)(i,t);(0,l.setLogFile)(`${c}/session.log`),(0,j.addPendingFixes)(c,n),l.logger.debug(`record-fix: session=${t} recorded ${n.length} fix note(s)`),(0,a.writeAndExit)(JSON.stringify({recorded:n.length})+`
|
|
24
27
|
`,0)});0&&(module.exports={hookCommand});
|
package/dist/commands/install.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var C=Object.defineProperty;var R=Object.getOwnPropertyDescriptor;var _=Object.getOwnPropertyNames;var O=Object.prototype.hasOwnProperty;var h=(n,e)=>C(n,"name",{value:e,configurable:!0});var V=(n,e)=>{for(var r in e)C(n,r,{get:e[r],enumerable:!0})},B=(n,e,r,c)=>{if(e&&typeof e=="object"||typeof e=="function")for(let l of _(e))!O.call(n,l)&&l!==r&&C(n,l,{get:()=>e[l],enumerable:!(c=R(e,l))||c.enumerable});return n};var H=n=>B(C({},"__esModule",{value:!0}),n);var q={};V(q,{CYCLE_HINTS:()=>D,SUGGEST_TIMEOUT_MS:()=>N,installCommand:()=>Y,runInstallAll:()=>E,syncSchemaIfChanged:()=>F});module.exports=H(q);var T=require("commander"),L=require("fs"),x=require("path"),d=require("../clients/registry"),v=require("../clients/claude"),p=require("../lib/config"),t=require("../lib/output"),k=require("../lib/projects-registry"),u=require("../lib/prompt"),j=require("../lib/telemetry"),w=require("../lib/install-version"),y=require("../lib/schema-sync"),M=require("./cycle-toggle"),A=require("./platform-suggest"),o=require("./mode-select");const D={browser:"web UI \xB7 DOM \xB7 console \xB7 a11y \xB7 screenshots \xB7 recording",node:"Node.js runtime \xB7 tracepoints \xB7 logpoints \xB7 exceptions \xB7 variables \xB7 logs",backend:"HTTP \xB7 gRPC \xB7 GraphQL \xB7 WebSocket \xB7 DB \xB7 logs",android:"Android device / emulator \xB7 UI interactions \xB7 screenshots \xB7 Logcat \xB7 HTTP capture",terminal:"CLI \xB7 REPL \xB7 TUI \xB7 PTY drive \xB7 output capture"},N=6e4;async function U(n,e,r){if(e!==void 0)return r&&e!=="enforce"&&t.log.warn(`--strict ignored: it only applies to --mode enforce (strict is inert in ${e}).`),{mode:e,strict:e==="enforce"&&r?!0:void 0};if(r&&t.log.warn("--strict ignored: pass it together with --mode enforce."),!(0,u.isInteractive)())return;const c=Math.max(0,o.ALL_MODES.indexOf((0,o.resolveInstallDefaultMode)(n))),l=o.ALL_MODES.map(f=>({label:o.MODE_LABELS[f],hint:o.MODE_HINTS[f]})),i=(0,o.resolveInstallDefaultStrict)(n)?1:0,a=o.STRICT_OPTIONS.map(f=>({label:o.STRICT_LABELS[f?"true":"false"],hint:o.STRICT_HINTS[f?"true":"false"]}));for(;;){const f=await(0,u.promptSelect)("Which verification mode?",l,c),g=o.ALL_MODES[f];if(g!=="enforce")return{mode:g};const s=await(0,u.promptSelect)(`How strict should verification be? (${t.pc.cyan("Esc")} = back)`,a,i,{allowBack:!0});if(s!==u.PROMPT_BACK)return{mode:g,strict:o.STRICT_OPTIONS[s]}}}h(U,"resolveModeSelection");function P(n,e){if(e===void 0)return;const{mode:r,strict:c}=e,i=(0,o.applyModeToLayer)(n,"project",r,c)?"":t.pc.dim(" (unchanged)"),a=r==="enforce"&&c!==void 0?t.pc.dim(` \xB7 ${o.STRICT_LABELS[c?"true":"false"]}`):"";t.log.info(`Mode: ${t.pc.bold(o.MODE_LABELS[r])}${a}${i}`)}h(P,"applyModeSelection");async function G(n,e,r,c){if(e!==void 0)return e;const l=(0,p.loadConfig)(n);if((r??(0,p.getVerificationMode)(l))==="monitor"||!(0,u.isInteractive)())return;const a=p.ALL_CYCLES.map(m=>({label:m,hint:D[m]})),f=p.ALL_CYCLES.map((m,S)=>(0,p.isCyclePatternsActive)(l,m)?S:-1).filter(m=>m>=0),g=(0,d.resolveSuggestionClient)(c),s=g?{suggestLabel:g.name,onSuggest:h(async m=>{const S=await(0,A.suggestPlatforms)(g,{projectDir:n,timeoutMs:N,signal:m});return S===null?null:S.map(b=>p.ALL_CYCLES.indexOf(b)).filter(b=>b>=0)},"onSuggest")}:void 0;return(await(0,u.promptMultiSelect)("Which platforms should require verification?",a,f,s)).map(m=>p.ALL_CYCLES[m])}h(G,"resolvePlatformSelection");function $(n,e){if(e===void 0)return;const r=(0,M.reconcileCyclesInLayer)(n,"project",e,!0),c=e.length>0?t.pc.bold(e.join(", ")):t.pc.dim("none (monitoring tools only)"),l=r.length>0?t.pc.dim(` \u2014 updated: ${r.join(", ")}`):"";t.log.info(`Platforms: ${c}${l}`)}h($,"applyPlatformSelection");function W(n,e){return e!==void 0?(0,d.resolveTargetClients)(n.path,e):(0,d.detectClients)(n.path)}h(W,"clientsForBatchEntry");async function E(n){const e=(0,d.listActiveProjects)();if(e.length===0)return t.log.info("No registered projects to install. Run `ironbee install` somewhere first."),{failures:0,total:0};t.log.info(`Installing across ${t.pc.bold(String(e.length))} registered project(s)\u2026`),t.log.blank();let r=0;const c=[];for(const i of e)try{const a=W(i,n.client);if(a.length===0){t.log.warn(`Skipping ${i.path} \u2014 no resolvable clients`);continue}const f=a.map(g=>g.name);t.log.label("project",t.pc.dim(i.path)),t.log.step(`installing ${t.pc.bold(f.join(", "))}`),(0,v.prepareIronBeeDir)(i.path),P(i.path,n.mode!==void 0?{mode:n.mode,strict:n.mode==="enforce"&&n.strict?!0:void 0}:void 0),$(i.path,n.platforms);for(const g of a)g.install(i.path);(0,k.upsertProject)(i.path),c.push(...f)}catch(a){r++,t.log.error(` ${i.path}: ${a instanceof Error?a.message:String(a)}`)}t.log.blank(),r===0?t.log.success(`Installed across ${e.length} project(s). ${t.pc.dim("Restart your AI coding client(s) to apply.")}`):t.log.warn(`Completed with ${r} failure(s) \u2014 see above.`);const l=Array.from(new Set(c));return l.length>0&&await(0,j.trackInstall)(l),{failures:r,total:e.length}}h(E,"runInstallAll");async function F(){try{if((0,y.isAutoRerenderDisabled)()||(0,y.readSyncedSchemaVersion)()>=w.INSTALL_SCHEMA_VERSION)return!1;const e=(0,d.listActiveProjects)();if(e.length===0)return(0,y.writeSyncedSchemaVersion)(w.INSTALL_SCHEMA_VERSION),!1;if(!(0,u.isInteractive)())return!1;t.log.blank(),t.log.warn(`IronBee's installed setup structure changed in this version \u2014 re-rendering ${t.pc.bold(String(e.length))} registered project(s):`);for(const r of e.slice(0,10))t.log.label("project",t.pc.dim(r.path));return e.length>10&&t.log.info(t.pc.dim(` \u2026and ${e.length-10} more`)),await(0,u.promptAcknowledge)(t.pc.dim(" Press Enter to update them now\u2026 ")),await E({}),(0,y.writeSyncedSchemaVersion)(w.INSTALL_SCHEMA_VERSION),!0}catch(n){return t.log.blank(),t.log.warn(`IronBee schema sync skipped: ${n instanceof Error?n.message:String(n)}`),!1}}h(F,"syncSchemaIfChanged");const Y=new T.Command("install").description("Install IronBee hooks and guidance files into a project. Use --all to install across every registered project (e.g. after a global config change).").argument("[project-dir]","target project directory",".").option("--client <name>",`client to install for (${(0,d.clientNames)()}), or "all"`).option("--all","Install across every project in the user-home inventory (~/.ironbee/projects.json) instead of just one. The [project-dir] argument is ignored when --all is set.").option("--platforms <list>",`comma-separated platforms to enable (${p.ALL_CYCLES.join(", ")}); skips the interactive picker. Empty ("") disables all. Default: interactive picker (pre-checked from current config), browser-only on a fresh non-interactive install.`).option("--mode <mode>",`verification mode (${o.ALL_MODES.join(", ")}); skips the interactive picker. enforce = full enforcement, assist = tools installed but not enforced (default), monitor = monitoring-only. Default: interactive picker (pre-selecting the current mode), untouched on a non-interactive install.`).option("--strict","with --mode enforce: always make the agent actually try every change in the app \u2014 it can't skip any. Enforce-only (ignored for assist/monitor or with no --mode). Default: the agent may skip changes with nothing to try (refactors, type-only edits, docs). Interactively, picking enforce opens this as a sub-choice.").action(async(n,e)=>{let r;if(e.platforms!==void 0)try{r=(0,M.parsePlatformsFlag)(e.platforms)}catch(s){t.log.error(s instanceof Error?s.message:String(s)),process.exit(1)}let c;if(e.mode!==void 0)try{c=(0,o.parseModeFlag)(e.mode)}catch(s){t.log.error(s instanceof Error?s.message:String(s)),process.exit(1)}const l=e.strict===!0;if(e.all===!0){(await E({client:e.client,platforms:r,mode:c,strict:l})).failures>0&&process.exit(1);return}const i=(0,x.resolve)(n);(0,L.existsSync)(i)||(t.log.error(`Directory not found: ${i}`),process.exit(1));let a;if(e.client!==void 0)try{a=(0,d.resolveTargetClients)(i,e.client)}catch(s){t.log.error(s instanceof Error?s.message:String(s)),process.exit(1)}else{const s=(0,d.detectClients)(i);if(s.length>0){a=s;const I=s.map(m=>m.name).join(", ");t.log.info(`Detected client(s): ${t.pc.bold(I)}`)}else if((0,u.isInteractive)()){const I=[...d.REGISTERED_CLIENTS.map(b=>b.name),"all"],m=I.map(b=>({label:b,hint:b==="all"?"every registered client":void 0}));t.log.warn(`No client detected in ${t.pc.dim(i)}.`);const S=await(0,u.promptSelect)("Which client(s) to install for?",m,0);a=(0,d.resolveTargetClients)(i,I[S])}else t.log.warn(`No client detected. Defaulting to: ${d.REGISTERED_CLIENTS[0].name}`),a=[d.REGISTERED_CLIENTS[0]]}const f=await U(i,c,l),g=await G(i,r,f?.mode,a);t.log.blank(),(0,v.prepareIronBeeDir)(i),P(i,f),$(i,g);for(const s of a)s.install(i);(0,k.upsertProject)(i),t.log.blank(),t.log.label("project",t.pc.dim(i)),t.log.blank(),t.log.success(`IronBee installed. ${t.pc.dim("Restart your AI coding client to activate the hooks.")}`),await(0,j.trackInstall)(a.map(s=>s.name),i)});0&&(module.exports={CYCLE_HINTS,SUGGEST_TIMEOUT_MS,installCommand,runInstallAll,syncSchemaIfChanged});
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var u=Object.defineProperty;var m=Object.getOwnPropertyDescriptor;var h=Object.getOwnPropertyNames;var b=Object.prototype.hasOwnProperty;var l=(e,n)=>u(e,"name",{value:n,configurable:!0});var w=(e,n)=>{for(var r in n)u(e,r,{get:n[r],enumerable:!0})},y=(e,n,r,t)=>{if(n&&typeof n=="object"||typeof n=="function")for(let o of h(n))!b.call(e,o)&&o!==r&&u(e,o,{get:()=>n[o],enumerable:!(t=m(n,o))||t.enumerable});return e};var j=e=>y(u({},"__esModule",{value:!0}),e);var C={};w(C,{buildPlatformSuggestionPrompt:()=>p,parsePlatformSuggestion:()=>d,suggestPlatforms:()=>x});module.exports=j(C);var f=require("../lib/config"),g=require("../lib/logger");const S={browser:"the project has a web/browser UI \u2014 frontend pages, components, or styles rendered in a browser",node:"the project has a Node.js runtime whose server-side behavior is worth debugging (Node/Next/Express servers, API routes)",backend:"the project exposes backend protocols to call and verify (HTTP/REST, gRPC, GraphQL, or WebSocket endpoints) in ANY language",android:"the project is an Android app (Kotlin/Java + Gradle, or a React Native Android target)",terminal:"the project is a CLI, REPL, or TUI driven from a terminal"};function p(e){return["You are helping configure IronBee verification platforms for a software project.","Inspect the project in the current working directory (read files as needed) and decide which","of the following verification platforms apply. Enable a platform ONLY when the project","actually has that surface; omit anything that doesn't clearly apply.","","Platforms:",...e.map(r=>{const t=S[r]??`the project uses the ${r} surface`;return`- ${r}: ${t}`}),"","Respond with ONLY a JSON object, no prose, no markdown, in EXACTLY this shape:",'{"platforms": ["browser", "backend"]}',"","Use only platform names from the list above. Returning several is fine. If unsure about one, omit it."].join(`
|
|
2
|
+
`)}l(p,"buildPlatformSuggestionPrompt");function c(e){let n=0,r=-1,t=!1,o=!1,s=null;for(let i=0;i<e.length;i++){const a=e[i];if(t){o?o=!1:a==="\\"?o=!0:a==='"'&&(t=!1);continue}if(a==='"'){t=!0;continue}a==="{"||a==="["?(n===0&&(r=i),n++):(a==="}"||a==="]")&&n>0&&(n--,n===0&&r>=0&&(s=e.slice(r,i+1),r=-1))}return s}l(c,"extractLastBalancedJson");function k(e){const n=[],r=/```(?:json)?\s*([\s\S]*?)```/gi,t=[];let o=r.exec(e);for(;o!==null;)t.push(o[1]),o=r.exec(e);for(const i of t.reverse())n.push(c(i)??i.trim());const s=c(e);return s!==null&&n.push(s),n}l(k,"jsonCandidates");function P(e,n){const r=new Set(e.filter(t=>typeof t=="string").map(t=>t.trim().toLowerCase()));return n.filter(t=>r.has(t.toLowerCase()))}l(P,"normalizePlatforms");function d(e,n){if(typeof e!="string"||e.trim().length===0)return null;for(const r of k(e)){let t;try{t=JSON.parse(r)}catch{continue}let o=null;if(Array.isArray(t)?o=t:t!==null&&typeof t=="object"&&Array.isArray(t.platforms)&&(o=t.platforms),o===null)continue;const s=P(o,n);if(s.length>0)return s}return null}l(d,"parsePlatformSuggestion");async function x(e,n){if(typeof e.runHeadlessPrompt!="function")return null;try{const r=p(f.ALL_CYCLES),t=await e.runHeadlessPrompt(r,n);return d(t,f.ALL_CYCLES)}catch(r){return g.logger.debug(`platform suggestion failed: ${r instanceof Error?r.message:String(r)}`),null}}l(x,"suggestPlatforms");0&&(module.exports={buildPlatformSuggestionPrompt,parsePlatformSuggestion,suggestPlatforms});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var S=Object.create;var g=Object.defineProperty;var
|
|
1
|
+
"use strict";var S=Object.create;var g=Object.defineProperty;var $=Object.getOwnPropertyDescriptor;var C=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 C(t))!j.call(e,i)&&i!==r&&g(e,i,{get:()=>t[i],enumerable:!(s=$(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: verification.context.commitDepth)").option("--json","machine-readable JSON output").action(e=>{D(e)});0&&(module.exports={scenarioCommand});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";var n=Object.defineProperty;var y=Object.getOwnPropertyDescriptor;var b=Object.getOwnPropertyNames;var h=Object.prototype.hasOwnProperty;var m=(o,e)=>n(o,"name",{value:e,configurable:!0});var C=(o,e)=>{for(var t in e)n(o,t,{get:e[t],enumerable:!0})},u=(o,e,t,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of b(e))!h.call(o,i)&&i!==t&&n(o,i,{get:()=>e[i],enumerable:!(s=y(e,i))||s.enumerable});return o};var v=o=>u(n({},"__esModule",{value:!0}),o);var j={};C(j,{terminalCommand:()=>w,terminalDisableCommand:()=>f,terminalEnableCommand:()=>p});module.exports=v(j);var r=require("commander"),g=require("../clients/registry"),l=require("../lib/config"),c=require("../lib/output"),a=require("./cycle-toggle");function d(o){return o.option("-p, --project-dir <dir>","Project directory (default: cwd).").option("-g, --global","Write to the global config (~/.ironbee/config.json) instead of the project.").option("--local","Write to the gitignored project-local override (<project>/.ironbee/config.local.json). Mutually exclusive with --global.").option("--client <name>",`Only update guidance md files for this client (${(0,g.clientNames)()}), or "all". Default: every registered client (per-file existsSync gate skips uninstalled ones).`)}m(d,"attachToggleOptions");const p=d(new r.Command("enable")).description('Enable the terminal interaction verification cycle. Writes a minimal `{ "terminal": {} }` block \u2014 code defaults (cli/** / cmd/** / bin/** / shell scripts) flow in at runtime; `config.json` stays minimal. Refuses (warn + no-op) when the cycle is already enabled at this layer.').action(o=>{try{const e=o.projectDir??process.cwd(),t=(0,l.resolveConfigTargetFromFlags)(o);(0,a.applyEnableCycle)("terminal",e,t,o.client)}catch(e){console.error(`${c.pc.red("\u2717")} ${e instanceof Error?e.message:e}`),process.exit(1)}}),f=d(new r.Command("disable")).description("Disable the terminal interaction verification cycle. With no customizations + no lower-layer override, drops the entire `terminal` block. Otherwise writes `verifyPatterns: []` (load-bearing hard kill; preserves `alwaysRequired` / `evidencePaths` / `additionalVerifyPatterns`).").action(o=>{try{const e=o.projectDir??process.cwd(),t=(0,l.resolveConfigTargetFromFlags)(o);(0,a.applyDisableCycle)("terminal",e,t,o.client)}catch(e){console.error(`${c.pc.red("\u2717")} ${e instanceof Error?e.message:e}`),process.exit(1)}}),w=new r.Command("terminal").description("Manage the terminal interaction verification cycle (CLI / REPL / TUI driving via the `terminal-devtools` MCP, `tdt_*` tools).").addCommand(p).addCommand(f);0&&(module.exports={terminalCommand,terminalDisableCommand,terminalEnableCommand});
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
"use strict";var
|
|
2
|
-
`)}catch(n){
|
|
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){
|
|
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){
|
|
5
|
-
`).filter(o=>o.length>0);let r=-1;const s=[];for(let o=0;o<n.length;o++)try{const
|
|
6
|
-
`).filter(
|
|
7
|
-
`).filter(
|
|
1
|
+
"use strict";var v=Object.defineProperty;var F=Object.getOwnPropertyDescriptor;var j=Object.getOwnPropertyNames;var N=Object.prototype.hasOwnProperty;var a=(t,e)=>v(t,"name",{value:e,configurable:!0});var P=(t,e)=>{for(var i in e)v(t,i,{get:e[i],enumerable:!0})},D=(t,e,i,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of j(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=>D(v({},"__esModule",{value:!0}),t);var re={};P(re,{ActivityAwareEvent:()=>f.ActivityAwareEvent,Event:()=>f.Event,EventType:()=>f.EventType,EventTypeValue:()=>f.EventTypeValue,FixAwareEvent:()=>f.FixAwareEvent,VerificationAwareEvent:()=>f.VerificationAwareEvent,appendAction:()=>I,baseFields:()=>T,deterministicSessionEndId:()=>J,findDurationSinceLastAction:()=>W,findLastActionTimestamp:()=>z,getCheckResultsForLatestVerification:()=>X,getFileChangesSinceLastFailVerdict:()=>te,getFileChangesSinceLastVerification:()=>Y,getLatestVerificationId:()=>Z,getToolCallsSinceLastFileChange:()=>B,getToolCallsSinceLastVerification:()=>K,hasFileChangesSinceLastVerification:()=>H,hasToolCallsSinceLastVerdict:()=>Q,hasVerifierEverEngaged:()=>ne,readActionsSinceLastMarker:()=>w,resolveProjectName:()=>R,summarizeFixFileChanges:()=>ie});module.exports=L(re);var u=require("fs"),_=require("crypto"),c=require("path"),d=require("../../lib/logger"),b=require("../../lib/collector"),$=require("../../lib/config"),C=require("../../queue/submit"),S=require("../../queue/types"),V=require("../../queue/register-handlers"),f=require("../../lib/event");function R(t){let e=t,i;for(;;){const n=(0,c.join)(e,".git");if((0,u.existsSync)(n)){const s=q(n);if(s==="directory"){const o=A(n);return o||(0,c.basename)(e)}if(s==="linked-pointer"){const o=E(n),l=o!==void 0?A(o):void 0;return l||(0,c.basename)(e)}s==="worktree-pointer"&&i===void 0&&(i=M(n))}const r=(0,c.dirname)(e);if(r===e)break;e=r}return i??(0,c.basename)(t)}a(R,"resolveProjectName");function q(t){try{const e=(0,u.statSync)(t);if(e.isDirectory())return"directory";if(!e.isFile())return"other";const i=E(t);return i===void 0?"other":(0,u.existsSync)((0,c.join)(i,"commondir"))?"worktree-pointer":"linked-pointer"}catch(e){return d.logger.debug(`resolveProjectName: stat failed for ${t}: ${e instanceof Error?e.message:e}`),"other"}}a(q,"classifyGitEntry");function E(t){try{const i=(0,u.readFileSync)(t,"utf-8").trim().match(/^gitdir:\s*(.+)$/m);if(!i)return;const n=i[1].trim();return(0,c.isAbsolute)(n)?n:(0,c.resolve)((0,c.dirname)(t),n)}catch(e){d.logger.debug(`resolveProjectName: pointer at ${t} unreadable: ${e instanceof Error?e.message:e}`);return}}a(E,"readGitdirPointer");function A(t){try{const e=(0,c.join)(t,"config");if(!(0,u.existsSync)(e))return;const i=U((0,u.readFileSync)(e,"utf-8"));return i?O(i):void 0}catch(e){d.logger.debug(`resolveProjectName: failed to read git config at ${t}: ${e instanceof Error?e.message:e}`);return}}a(A,"repoNameFromGitConfig");function M(t){const e=E(t);if(e===void 0)return;const i=(0,c.join)(e,"commondir");let n;try{if((0,u.existsSync)(i)){const o=(0,u.readFileSync)(i,"utf-8").trim();n=(0,c.isAbsolute)(o)?o:(0,c.resolve)(e,o)}else n=e}catch(o){d.logger.debug(`resolveProjectName: commondir at ${i} unreadable: ${o instanceof Error?o.message:o}`);return}const r=A(n);if(r)return r;const s=(0,c.basename)(n)===".git"?(0,c.basename)((0,c.dirname)(n)):(0,c.basename)(n);return s.length>0?s:void 0}a(M,"repoNameFromWorktreePointer");function U(t){const e=t.split(/\r?\n/);let i=null,n=null,r,s;for(const o of e){const l=o.trim();if(l.length===0||l.startsWith("#")||l.startsWith(";"))continue;const g=l.match(/^\[([^\s\]]+)(?:\s+"([^"]*)")?\]$/);if(g){i=g[1].toLowerCase(),n=g[2]??null;continue}if(i!=="remote"||n===null)continue;const p=l.match(/^url\s*=\s*(.+?)\s*$/i);if(!p)continue;const m=p[1];n==="origin"&&(r=m),s===void 0&&(s=m)}return r??s}a(U,"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}a(O,"repoNameFromRemoteUrl");function T(t){const e=(0,c.basename)((0,c.dirname)(t)),i=(0,c.dirname)(t),{getUserEmail:n,getUsageType:r,getUsagePlan:s,getProjectDir:o}=require("./session-state"),l=o(i)??(0,c.dirname)((0,c.dirname)((0,c.dirname)(i))),g=n(i),p=r(i),m=s(i),h={id:(0,_.randomUUID)(),session_id:e,project_name:R(l)};return g&&(h.user_email=g),p&&(h.usage_type=p),m&&(h.usage_plan=m),h}a(T,"baseFields");function J(t){const e=(0,_.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)}`}a(J,"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,u.mkdirSync)((0,c.dirname)(t),{recursive:!0}),(0,u.appendFileSync)(t,JSON.stringify(e)+`
|
|
2
|
+
`)}catch(n){d.logger.debug(`failed to append action to ${t}: ${n}`)}if(e.type!=="tool_call"&&e.type!=="check_result"){const n=(0,c.dirname)(t),r=(0,c.basename)(n),{getProjectDir:s}=require("./session-state"),o=s(n)??(0,c.dirname)((0,c.dirname)((0,c.dirname)(n)));try{await(0,b.sendToCollector)(e,r,o)}catch(l){l instanceof b.RetriableCollectorError?G(o,r,e,l):d.logger.debug(`failed to send action to collector: ${l}`)}}}a(I,"appendAction");function G(t,e,i,n){if(!(0,$.isJobQueueEnabled)(t)){d.logger.debug(`collector fallback: jobQueue disabled, dropping type=${i.type} id=${i.id} (cause: ${n.message})`);return}try{(0,C.submit)(t,e,V.SEND_EVENT_TYPE,i),d.logger.debug(`collector fallback: enqueued type=${i.type} id=${i.id} as send_event (cause: ${n.message})`)}catch(r){if(r instanceof S.JobTooLargeError){d.logger.debug(`collector fallback: event too large for queue (${r.sizeBytes} bytes), dropping type=${i.type} id=${i.id}`);return}d.logger.debug(`collector fallback: queue submit failed for type=${i.type} id=${i.id}: ${r}`)}}a(G,"enqueueCollectorFallback");function W(t,e,i){if((0,u.existsSync)(t))try{const r=(0,u.readFileSync)(t,"utf-8").trim().split(`
|
|
3
|
+
`).filter(s=>s.length>0);for(let s=r.length-1;s>=0;s--)try{const o=JSON.parse(r[s]);if(o.type===e&&typeof o.timestamp=="number")return i-o.timestamp}catch{}}catch(n){d.logger.debug(`failed to find duration for ${e}: ${n}`)}}a(W,"findDurationSinceLastAction");function w(t,e){return y(t,i=>i.type===e)}a(w,"readActionsSinceLastMarker");function z(t){if((0,u.existsSync)(t))try{const i=(0,u.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){d.logger.debug(`failed to read last action timestamp from ${t}: ${e}`)}}a(z,"findLastActionTimestamp");function y(t,e){if(!(0,u.existsSync)(t))return[];try{const n=(0,u.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 l=JSON.parse(n[o]);s.push(l),e(l)&&(r=o)}catch{}return s.slice(r+1)}catch(i){return d.logger.debug(`failed to read actions from ${t}: ${i}`),[]}}a(y,"readActionsSinceMatch");function x(t){return t.type!=="verification_requested"?!1:t.action==="allow"}a(x,"isAllowVerificationRequested");function k(t){if(t.type!=="tool_call")return!1;const e=t.verification_id;return typeof e=="string"&&e.length>0}a(k,"isVerificationScopedToolCall");function K(t){return y(t,x).filter(k)}a(K,"getToolCallsSinceLastVerification");function B(t){return w(t,"file_change").filter(k)}a(B,"getToolCallsSinceLastFileChange");function Q(t){return w(t,"verdict_write").some(k)}a(Q,"hasToolCallsSinceLastVerdict");function H(t){return y(t,x).some(i=>i.type==="file_change")}a(H,"hasFileChangesSinceLastVerification");function Y(t){return y(t,x).filter(i=>i.type==="file_change")}a(Y,"getFileChangesSinceLastVerification");function X(t){if(!(0,u.existsSync)(t))return[];try{const i=(0,u.readFileSync)(t,"utf-8").trim().split(`
|
|
6
|
+
`).filter(s=>s.length>0);let n;const r=[];for(const s of i){let o;try{o=JSON.parse(s)}catch{continue}if(o.type==="verification_start"){const l=o.verification_id;typeof l=="string"&&l.length>0&&(n=l)}else o.type==="check_result"&&r.push(o)}return n===void 0?[]:r.filter(s=>s.verification_id===n)}catch(e){return d.logger.debug(`failed to read check_results from ${t}: ${e}`),[]}}a(X,"getCheckResultsForLatestVerification");function Z(t){if((0,u.existsSync)(t))try{const i=(0,u.readFileSync)(t,"utf-8").trim().split(`
|
|
7
|
+
`).filter(r=>r.length>0);let n;for(const r of i)try{const s=JSON.parse(r);if(s.type==="verification_start"){const o=s.verification_id;typeof o=="string"&&o.length>0&&(n=o)}}catch{continue}return n}catch(e){d.logger.debug(`failed to read latest verification_id from ${t}: ${e}`);return}}a(Z,"getLatestVerificationId");function ee(t){return t.type!=="verdict_write"?!1:t.verdict?.status==="fail"}a(ee,"isFailVerdictWrite");function te(t){if(!(0,u.existsSync)(t))return[];try{const n=(0,u.readFileSync)(t,"utf-8").trim().split(`
|
|
8
|
+
`).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 l=n[o];l!==null&&ee(l)&&(r=o)}if(r<0)return[];const s=[];for(let o=r+1;o<n.length;o++){const l=n[o];l!==null&&l.type==="file_change"&&s.push(l)}return s}catch(e){return d.logger.debug(`failed to read file changes since last fail verdict: ${e}`),[]}}a(te,"getFileChangesSinceLastFailVerdict");function ne(t){if(!(0,u.existsSync)(t))return!1;try{const i=(0,u.readFileSync)(t,"utf-8").trim().split(`
|
|
9
|
+
`).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 d.logger.debug(`failed to scan verifier engagement in ${t}: ${e}`),!1}}a(ne,"hasVerifierEverEngaged");function ie(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})`})}a(ie,"summarizeFixFileChanges");0&&(module.exports={ActivityAwareEvent,Event,EventType,EventTypeValue,FixAwareEvent,VerificationAwareEvent,appendAction,baseFields,deterministicSessionEndId,findDurationSinceLastAction,findLastActionTimestamp,getCheckResultsForLatestVerification,getFileChangesSinceLastFailVerdict,getFileChangesSinceLastVerification,getLatestVerificationId,getToolCallsSinceLastFileChange,getToolCallsSinceLastVerification,hasFileChangesSinceLastVerification,hasToolCallsSinceLastVerdict,hasVerifierEverEngaged,readActionsSinceLastMarker,resolveProjectName,summarizeFixFileChanges});
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";var h=Object.defineProperty;var M=Object.getOwnPropertyDescriptor;var O=Object.getOwnPropertyNames;var U=Object.prototype.hasOwnProperty;var f=(e,t)=>h(e,"name",{value:t,configurable:!0});var V=(e,t)=>{for(var n in t)h(e,n,{get:t[n],enumerable:!0})},D=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of O(t))!U.call(e,s)&&s!==n&&h(e,s,{get:()=>t[s],enumerable:!(r=M(t,s))||r.enumerable});return e};var L=e=>D(h({},"__esModule",{value:!0}),e);var G={};V(G,{isShellShimName:()=>E,runChecks:()=>Y});module.exports=L(G);var $=require("child_process"),l=require("path"),_=require("../../lib/logger"),a=require("../../lib/config"),p=require("../../lib/runtime-paths"),m=require("./session-state"),k=require("./actions");const P=8*1024,q=16*1024*1024;function N(e,t){const n=Buffer.from(e,"utf-8");if(n.length<=t)return e;let r=n.length-t;for(;r<n.length&&(n[r]&192)===128;)r++;return`\u2026(truncated, last ${n.length-r} of ${n.length} bytes shown)\u2026
|
|
2
|
+
${n.subarray(r).toString("utf-8")}`}f(N,"tailCap");const H=["npm","npx","yarn","pnpm","bun","corepack"];function E(e){const t=(0,l.basename)(e).toLowerCase();if(/\.(cmd|bat)$/.test(t))return!0;const n=t.replace(/\.[^.]+$/,"");return H.includes(n)}f(E,"isShellShimName");function W(e){return process.platform==="win32"&&E(e)}f(W,"needsWindowsShell");function J(e){if(e.length===0)return"no checks ran";const t=e.map(s=>{const d=s.status==="pass"?"PASS":s.status==="fail"?"FAIL":"ERROR",g=s.required?"":" (optional)";return` [${d}] ${s.name}${g}`}),n=e.find(s=>s.status==="fail"),r=n?`
|
|
3
|
+
|
|
4
|
+
--- ${n.name} output ---
|
|
5
|
+
${n.output}`:"";return`Verification checks:
|
|
6
|
+
${t.join(`
|
|
7
|
+
`)}${r}`}f(J,"buildSummary");async function Y(e,t){const n=(0,a.loadConfig)(e);if(!(0,a.getVerificationEnabled)(n))return{ran:!1,reason:"monitor",results:[],summary:"checks skipped (monitoring mode)"};const r=(0,a.getVerificationChecks)(n),s=Array.isArray(n.verification?.checks)?n.verification.checks.length:0;if(s>r.length&&_.logger.debug(`run-checks: ${s-r.length} malformed verification.checks entr(ies) dropped (missing name/command) \u2014 not run, not enforced`),r.length===0)return{ran:!1,reason:"no_checks",results:[],summary:"no checks configured"};const d=(0,p.sessionDir)(e,t),g=(0,m.getActiveVerificationId)(d);if(g===void 0)return{ran:!1,reason:"no_cycle",results:[],summary:"no active verification cycle \u2014 run `ironbee hook verification-start` before `run-checks`"};const I=(0,m.getActiveActivityId)(d),x=(0,m.getActiveTraceId)(d),w=(0,p.sessionActionsFile)(e,t),b=[];for(const o of r){const T=o.cwd?(0,l.isAbsolute)(o.cwd)?o.cwd:(0,l.resolve)(e,o.cwd):e,A=o.timeoutMs??a.DEFAULT_CHECK_TIMEOUT_MS,F={...process.env,...o.env??{}},B=Date.now();let u="pass",C=null,S=!1,y=!1,c="";try{const i=(0,$.spawnSync)(o.command,o.args??[],{cwd:T,env:F,timeout:A,encoding:"utf-8",maxBuffer:q,shell:W(o.command)});if(c=N(`${i.stdout??""}${i.stderr??""}`,P),i.error){const v=i.error.code;v==="ETIMEDOUT"?(u="fail",S=!0,c=c||`timed out after ${A}ms`):(u="error",y=!0,c=`spawn error (${v??"unknown"}): ${i.error.message}`)}else i.signal!==null?(u="fail",S=i.signal==="SIGTERM",C=null,c=c||`killed by ${i.signal}`):(C=i.status,u=i.status===0?"pass":"fail")}catch(i){u="error",y=!0,c=`spawn error: ${i instanceof Error?i.message:String(i)}`}const R={...(0,k.baseFields)(w),type:"check_result",timestamp:Date.now(),verification_id:g,activity_id:I??"",trace_id:x,name:o.name,command:(0,l.basename)(o.command),status:u,exit_code:C,required:o.required!==!1,duration:Date.now()-B,timed_out:S,output:c};if(y&&(R.error="spawn"),await(0,k.appendAction)(w,R),b.push(R),u==="fail")break}return{ran:!0,results:b,summary:J(b)}}f(Y,"runChecks");0&&(module.exports={isShellShimName,runChecks});
|