@ironbee-ai/cli 0.31.0 → 0.32.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/dist/clients/claude/agents/ironbee-scenario.md +4 -1
- package/dist/clients/claude/agents/ironbee-verifier.md +21 -3
- package/dist/clients/claude/hooks/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 +3 -0
- package/dist/clients/codex/agents/ironbee-verifier.md +20 -2
- package/dist/clients/codex/commands/ironbee-manage-scenario/SKILL.main.md +3 -0
- package/dist/clients/codex/commands/ironbee-search-scenario/SKILL.main.md +3 -0
- package/dist/clients/codex/commands/ironbee-sync-scenario/SKILL.main.md +3 -0
- package/dist/clients/codex/commands/ironbee-verify/SKILL.main.md +3 -0
- package/dist/clients/codex/hooks/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 +3 -0
- package/dist/clients/codex/util.js +1 -1
- package/dist/clients/cursor/commands/ironbee-manage-scenario/SKILL.md +3 -0
- package/dist/clients/cursor/commands/ironbee-search-scenario/SKILL.md +3 -0
- package/dist/clients/cursor/commands/ironbee-sync-scenario/SKILL.md +3 -0
- package/dist/clients/cursor/commands/ironbee-verify/SKILL.md +3 -0
- package/dist/clients/cursor/hooks/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 +9 -0
- package/dist/commands/config.js +2 -2
- package/dist/commands/install.js +1 -1
- package/dist/commands/scenario.js +1 -1
- package/dist/commands/terminal.js +1 -0
- package/dist/hooks/core/verification-context.js +19 -15
- package/dist/hooks/core/verify-gate.js +25 -20
- package/dist/import/claude/events/tool-call.js +1 -1
- package/dist/import/codex/events/tool-call.js +1 -1
- package/dist/index.js +1 -1
- package/dist/lib/config.js +1 -1
- package/dist/lib/install-version.js +1 -1
- package/dist/lib/platform-section.js +5 -4
- package/dist/lib/scenario-staleness.js +1 -1
- package/dist/tui/config/schema.js +1 -1
- package/dist/tui/platforms/area.js +2 -2
- package/package.json +1 -1
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
<!-- Terminal verification is ENABLED for this project. -->
|
|
2
|
+
|
|
3
|
+
## Terminal Mode (when `terminal.verifyPatterns` matches an edited file)
|
|
4
|
+
|
|
5
|
+
> **Precondition: the change must actually have terminal-observable behavior.** If the edited code is not reachable from a CLI / REPL / shell / full-screen TUI, this section does NOT apply — `MCP:tdt_*` tools drive a program through a PTY. Just do browser verification.
|
|
6
|
+
|
|
7
|
+
If the project has terminal verification enabled (`ironbee terminal enable` once at setup) and your edits touch matching paths, the stop hook also enforces a terminal cycle. The same `verification-start` covers both cycles; one platform-agnostic verdict covers both.
|
|
8
|
+
|
|
9
|
+
### Mode behavior (terminal cycle)
|
|
10
|
+
- **default** (no arg or `default`): exercise only the CLI flows / commands your diff touched.
|
|
11
|
+
- **full**: exercise every CLI command / flow reachable from files matching `terminal.verifyPatterns`.
|
|
12
|
+
- `visual` / `functional`: browser-only modes; terminal cycle behaves as `default` when they are passed.
|
|
13
|
+
|
|
14
|
+
### Steps (run within step 3 of the Universal steps above)
|
|
15
|
+
1. **Pick an evidence path** for the changed code:
|
|
16
|
+
- **Run-evidence** (a one-shot command proves the change works): `MCP:tdt_pty_run` with the command line — it returns the full output AND the exit code in one call. Confirm the output is what the change should produce AND the exit code is expected (`0` for success, or the specific non-zero code an error/flag path returns). A command that prints the right text but exits non-zero is a fail.
|
|
17
|
+
- **Interactive-evidence** (driving a REPL / shell / full-screen TUI proves the change is live):
|
|
18
|
+
- Spawn the program: `MCP:tdt_pty_start` → returns a `paneId`.
|
|
19
|
+
- Drive input: `MCP:tdt_interaction_send-keys` (tmux key syntax — `Enter`, `C-c`, `Up`, `Tab`) or `MCP:tdt_interaction_send-text` (literal text).
|
|
20
|
+
- **Synchronize, don't guess delays**: `MCP:tdt_sync_wait-for` blocks until the expected output appears.
|
|
21
|
+
- Capture: `MCP:tdt_content_capture` — `mode: stream` for line-oriented programs (REPLs, shells; pass an incremental `since` cursor to read only new lines), `mode: screen` for full-screen TUIs (rendered screen buffer). Confirm it shows the expected result.
|
|
22
|
+
- Stop the pane: `MCP:tdt_pty_stop`.
|
|
23
|
+
- Auxiliary (NOT gate evidence): `MCP:tdt_sync_wait-for-idle` waits until output stops changing; `MCP:tdt_content_get-cursor` reports cursor position; `MCP:tdt_pty_resize` / `MCP:tdt_pty_signal` / `MCP:tdt_pty_list` manage panes.
|
|
24
|
+
2. **Submit verdict** — platform-agnostic, just status + checks (+ issues/fixes).
|
|
25
|
+
|
|
26
|
+
### Verdict (platform-agnostic)
|
|
27
|
+
```json
|
|
28
|
+
{
|
|
29
|
+
"session_id": "...",
|
|
30
|
+
"status": "pass",
|
|
31
|
+
"checks": ["`mycli --json` emits valid JSON and exits 0", "REPL `:help` lists the new command"]
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
For a multi-cycle pass, both browser and terminal pass criteria must hold.
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Default Mode (terminal cycle)
|
|
40
|
+
|
|
41
|
+
Focus on the CLI flows or commands your diff touched — not the entire program.
|
|
42
|
+
|
|
43
|
+
### 1. Study the changes
|
|
44
|
+
1. Run `git diff --name-only` and `git diff --name-only HEAD~1`
|
|
45
|
+
2. **Ignore `.ironbee/`, `.claude/`, `.cursor/`** — tool config, not application code
|
|
46
|
+
3. **Read the full diff** for every terminal-facing file in scope — note new commands, changed flags / args, new output formats, changed exit codes, new REPL / TUI states
|
|
47
|
+
4. Before running, identify: which command / subcommand / REPL state is affected? Which invocation exercises it? What output and exit code should it now produce?
|
|
48
|
+
|
|
49
|
+
### 2. Verify in a PTY
|
|
50
|
+
- **Run-evidence**: run the affected command via `MCP:tdt_pty_run` — confirm the output matches the change AND the exit code is correct
|
|
51
|
+
- **Interactive-evidence**: spawn the program, drive the affected flow with `send-keys` / `send-text`, `wait-for` the expected output, then capture it — the captured output must show the expected result after your change
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Full Mode (`/ironbee-verify full`, terminal cycle)
|
|
56
|
+
|
|
57
|
+
Verify every CLI command / flow reachable from files matching `terminal.verifyPatterns`, not just the changed files. Do NOT run `git diff` or scope to recent changes.
|
|
58
|
+
|
|
59
|
+
- Run every command / subcommand in scope
|
|
60
|
+
- Exercise at least one happy-path invocation AND one error-path invocation (bad flag / missing arg) per command, confirming both output and exit code
|
|
61
|
+
- For REPL / TUI surfaces, drive each affected state and capture the rendered output
|
|
@@ -0,0 +1,31 @@
|
|
|
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
|
+
## Terminal cycle
|
|
6
|
+
|
|
7
|
+
Terminal file changes IF the file matches `terminal.verifyPatterns` ALSO require verification through the **terminal-devtools** MCP server (prefix `MCP:tdt_*`). Terminal-cycle verification means spawning the program attached to a PTY (like tmux) and either running a one-shot command to confirm its output and exit code — OR driving a REPL / shell / full-screen TUI with input and capturing the output to confirm the changed code path behaves correctly.
|
|
8
|
+
|
|
9
|
+
Both cycles can be active simultaneously (e.g. you edit both a React component and a CLI command in the same task). One `verification-start` covers all active cycles; one platform-agnostic verdict covers them all; one retry counter applies globally.
|
|
10
|
+
|
|
11
|
+
### ⚠️ `terminal-devtools` is ONLY for terminal programs
|
|
12
|
+
|
|
13
|
+
`terminal-devtools` drives CLIs, REPLs, shells, and full-screen TUIs through a PTY. It does NOT apply to web-only UI, or to changes with no terminal-observable behavior. If the change has no command / REPL / TUI surface, do NOT call `MCP:tdt_*` tools — a web-only change belongs to the browser cycle.
|
|
14
|
+
|
|
15
|
+
**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` for the terminal cycle. Don't fabricate a PTY session. Instead, stop and clearly report to the user: the change has no terminal surface; ask them to run `ironbee terminal disable` to unblock the gate.
|
|
16
|
+
|
|
17
|
+
### Terminal-cycle additions to the main flow
|
|
18
|
+
|
|
19
|
+
These attach to the **Required steps** above — they don't replace any step. Numbering follows the main flow:
|
|
20
|
+
|
|
21
|
+
- **Within step 3 (run flow):** also run the terminal flow — pick ONE evidence path:
|
|
22
|
+
- **Run-evidence**: run a one-shot command (`MCP:tdt_pty_run`) and confirm its output AND exit code are what the change should produce.
|
|
23
|
+
- **Interactive-evidence**: spawn a pane (`MCP:tdt_pty_start`) → drive input (`MCP:tdt_interaction_send-keys` / `MCP:tdt_interaction_send-text`) → synchronize with `MCP:tdt_sync_wait-for` → capture output (`MCP:tdt_content_capture`, `mode: stream` for REPLs/shells with an incremental `since` cursor, `mode: screen` for full-screen TUIs) → stop the pane (`MCP:tdt_pty_stop`). Auxiliary only (NOT evidence): `MCP:tdt_sync_wait-for-idle`, `MCP:tdt_content_get-cursor`, `MCP:tdt_pty_resize` / `MCP:tdt_pty_signal` / `MCP:tdt_pty_list`.
|
|
24
|
+
- **Within step 6 (submit verdict):** submit one platform-agnostic verdict with `status` + `checks` (+ `issues`/`fixes` as needed). Terminal-cycle pass criteria: (a one-shot command was run via `tdt_pty_run` and its output/exit code confirm the change) OR (a pane was spawned AND input was driven AND output was captured and shows the expected result).
|
|
25
|
+
|
|
26
|
+
### Additional BANNED for terminal cycle
|
|
27
|
+
|
|
28
|
+
- Calling `MCP:tdt_*` tools without first opening a verification cycle (`ironbee hook verification-start`).
|
|
29
|
+
- **Calling `MCP:tdt_*` tools when the change has no terminal-observable behavior.** Use the browser cycle for web-only changes.
|
|
30
|
+
- Claiming `status: pass` for a terminal cycle when no evidence path was exercised.
|
|
31
|
+
- Claiming `status: pass` on the run-evidence path while ignoring a non-zero exit code — the exit code is part of the evidence.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
### terminal platform (enabled)
|
|
2
|
+
- **Use for**: CLI / REPL / shell / full-screen TUI scenarios driven through a PTY.
|
|
3
|
+
- **Server**: `terminal-devtools` · **scenario tools**: the `tdt_scenario-*` tools
|
|
4
|
+
(`tdt_scenario-add` / `-update` / `-delete` / `-list` / `-search` / `-run`).
|
|
5
|
+
- **Store**: project → `.ironbee/scenarios/tdt`, global → `~/.ironbee/scenarios/tdt` (the
|
|
6
|
+
server's `SCENARIOS_DIR`; you pass `scope`, the server resolves the path).
|
|
7
|
+
- Scenario **scripts** call this platform's tools via `callTool('<bare-tool>', {...})` — discover
|
|
8
|
+
the available `tdt_*` tool names from your connected MCP tool schemas; don't guess.
|
|
9
|
+
|
|
10
|
+
**What to test & how — capture the SAME evidence the verifier would** (a scenario runs FOR
|
|
11
|
+
verification, so its script must collect what the terminal cycle collects). In the script:
|
|
12
|
+
1. Pick an **evidence path** for the changed code area:
|
|
13
|
+
- **Run-evidence path** — run the one-shot command with `tdt_pty_run` (`returnOutput: true`); put the
|
|
14
|
+
returned output AND exit code in your result. Confirm the output is what the change should produce
|
|
15
|
+
AND the exit code is expected (`0` for success, or the specific non-zero code an error/flag path
|
|
16
|
+
returns) — a command that prints the right text but exits non-zero is a fail.
|
|
17
|
+
- **Interactive-evidence path** — spawn the program with `tdt_pty_start` (returns a `paneId`), drive it
|
|
18
|
+
with `tdt_interaction_send-keys` (tmux key syntax — `Enter`, `C-c`, `Up`, `Tab`) and/or
|
|
19
|
+
`tdt_interaction_send-text` (literal text), **synchronize with `tdt_sync_wait-for`** (block until the
|
|
20
|
+
expected output appears — prefer this over fixed delays), then capture with `tdt_content_capture`
|
|
21
|
+
(`returnOutput: true`; `mode: stream` for line-oriented REPLs/shells with an incremental `since`
|
|
22
|
+
cursor, `mode: screen` for full-screen TUIs) and put the captured output in your result. Stop the
|
|
23
|
+
pane with `tdt_pty_stop` when done. Auxiliary helpers (NOT evidence): `tdt_sync_wait-for-idle`,
|
|
24
|
+
`tdt_content_get-cursor`, `tdt_pty_resize` / `tdt_pty_signal` / `tdt_pty_list`.
|
|
25
|
+
|
|
26
|
+
`return` the evidence — the command output + exit code, or the captured REPL / TUI output — **plus
|
|
27
|
+
explicit pass/fail assertions**. That returned result is what `/ironbee-verify scenario:<name>` reads to
|
|
28
|
+
judge whether the terminal flow behaved correctly.
|
|
29
|
+
**`terminal-devtools` is for terminal programs only (CLI / REPL / shell / TUI).**
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
> **Recording (only when `recording.enable` is on in config):** the gate blocks every other browser tool until you first call `MCP:bdt_content_start-recording`, and `submit-verdict` rejects with `"recording is still active"` unless you call `MCP:bdt_content_stop-recording` after the steps below. **Treat start/stop as bookends around steps 1-5.** The same is enforced as step 6 of the Universal flow.
|
|
8
8
|
|
|
9
|
-
1. **Navigate**: `MCP:bdt_navigation_go-to` — go to the affected page(s)
|
|
9
|
+
1. **Navigate**: `MCP:bdt_navigation_go-to` — go to the affected page(s) **AND any downstream page that renders or consumes what the change produces** — verify the change's effect where it's observed, not only the page the edited file owns
|
|
10
10
|
2. **Interact**: actually exercise what changed — click buttons, fill forms, submit data, trigger workflows. Don't just look at the page.
|
|
11
11
|
3. **Screenshot**: `MCP:bdt_content_take-screenshot` — capture the final visual state
|
|
12
12
|
4. **Accessibility**: `MCP:bdt_a11y_take-aria-snapshot` — verify page structure
|
|
@@ -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).
|
|
@@ -92,6 +98,9 @@ Each tool call is a separate LLM round-trip, and that round-trip — not the too
|
|
|
92
98
|
<!--IRONBEE:PLATFORM:android-->
|
|
93
99
|
<!--/IRONBEE:PLATFORM:android-->
|
|
94
100
|
|
|
101
|
+
<!--IRONBEE:PLATFORM:terminal-->
|
|
102
|
+
<!--/IRONBEE:PLATFORM:terminal-->
|
|
103
|
+
|
|
95
104
|
## Important
|
|
96
105
|
- **Always submit a verdict after every verification attempt** — both pass AND fail. Fail verdicts are tracked for analytics.
|
|
97
106
|
- The stop hook checks that the required tools were used for every active cycle and that the verdict carries non-empty `checks`.
|
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"];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/install.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var S=Object.defineProperty;var
|
|
1
|
+
"use strict";var S=Object.defineProperty;var A=Object.getOwnPropertyDescriptor;var N=Object.getOwnPropertyNames;var R=Object.prototype.hasOwnProperty;var g=(n,e)=>S(n,"name",{value:e,configurable:!0});var V=(n,e)=>{for(var r in e)S(n,r,{get:e[r],enumerable:!0})},_=(n,e,r,c)=>{if(e&&typeof e=="object"||typeof e=="function")for(let d of N(e))!R.call(n,d)&&d!==r&&S(n,d,{get:()=>e[d],enumerable:!(c=A(e,d))||c.enumerable});return n};var B=n=>_(S({},"__esModule",{value:!0}),n);var Y={};V(Y,{CYCLE_HINTS:()=>D,installCommand:()=>F,runInstallAll:()=>j,syncSchemaIfChanged:()=>W});module.exports=B(Y);var P=require("commander"),T=require("fs"),L=require("path"),f=require("../clients/registry"),M=require("../clients/claude"),p=require("../lib/config"),t=require("../lib/output"),k=require("../lib/projects-registry"),m=require("../lib/prompt"),v=require("../lib/telemetry"),I=require("../lib/install-version"),h=require("../lib/schema-sync"),C=require("./cycle-toggle"),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"};async function O(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,m.isInteractive)())return;const c=Math.max(0,o.ALL_MODES.indexOf((0,o.resolveInstallDefaultMode)(n))),d=o.ALL_MODES.map(u=>({label:o.MODE_LABELS[u],hint:o.MODE_HINTS[u]})),i=(0,o.resolveInstallDefaultStrict)(n)?1:0,a=o.STRICT_OPTIONS.map(u=>({label:o.STRICT_LABELS[u?"true":"false"],hint:o.STRICT_HINTS[u?"true":"false"]}));for(;;){const u=await(0,m.promptSelect)("Which verification mode?",d,c),l=o.ALL_MODES[u];if(l!=="enforce")return{mode:l};const s=await(0,m.promptSelect)(`How strict should verification be? (${t.pc.cyan("Esc")} = back)`,a,i,{allowBack:!0});if(s!==m.PROMPT_BACK)return{mode:l,strict:o.STRICT_OPTIONS[s]}}}g(O,"resolveModeSelection");function E(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}`)}g(E,"applyModeSelection");async function H(n,e,r){if(e!==void 0)return e;const c=(0,p.loadConfig)(n);if((r??(0,p.getVerificationMode)(c))==="monitor"||!(0,m.isInteractive)())return;const i=p.ALL_CYCLES.map(l=>({label:l,hint:D[l]})),a=p.ALL_CYCLES.map((l,s)=>(0,p.isCyclePatternsActive)(c,l)?s:-1).filter(l=>l>=0);return(await(0,m.promptMultiSelect)("Which platforms should require verification?",i,a)).map(l=>p.ALL_CYCLES[l])}g(H,"resolvePlatformSelection");function $(n,e){if(e===void 0)return;const r=(0,C.reconcileCyclesInLayer)(n,"project",e,!0),c=e.length>0?t.pc.bold(e.join(", ")):t.pc.dim("none (monitoring tools only)"),d=r.length>0?t.pc.dim(` \u2014 updated: ${r.join(", ")}`):"";t.log.info(`Platforms: ${c}${d}`)}g($,"applyPlatformSelection");function U(n,e){return e!==void 0?(0,f.resolveTargetClients)(n.path,e):(0,f.detectClients)(n.path)}g(U,"clientsForBatchEntry");async function j(n){const e=(0,f.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=U(i,n.client);if(a.length===0){t.log.warn(`Skipping ${i.path} \u2014 no resolvable clients`);continue}const u=a.map(l=>l.name);t.log.label("project",t.pc.dim(i.path)),t.log.step(`installing ${t.pc.bold(u.join(", "))}`),(0,M.prepareIronBeeDir)(i.path),E(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 l of a)l.install(i.path);(0,k.upsertProject)(i.path),c.push(...u)}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 d=Array.from(new Set(c));return d.length>0&&await(0,v.trackInstall)(d),{failures:r,total:e.length}}g(j,"runInstallAll");async function W(){try{if((0,h.isAutoRerenderDisabled)()||(0,h.readSyncedSchemaVersion)()>=I.INSTALL_SCHEMA_VERSION)return!1;const e=(0,f.listActiveProjects)();if(e.length===0)return(0,h.writeSyncedSchemaVersion)(I.INSTALL_SCHEMA_VERSION),!1;if(!(0,m.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,m.promptAcknowledge)(t.pc.dim(" Press Enter to update them now\u2026 ")),await j({}),(0,h.writeSyncedSchemaVersion)(I.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}}g(W,"syncSchemaIfChanged");const F=new P.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,f.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,C.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 d=e.strict===!0;if(e.all===!0){(await j({client:e.client,platforms:r,mode:c,strict:d})).failures>0&&process.exit(1);return}const i=(0,L.resolve)(n);(0,T.existsSync)(i)||(t.log.error(`Directory not found: ${i}`),process.exit(1));let a;if(e.client!==void 0)try{a=(0,f.resolveTargetClients)(i,e.client)}catch(s){t.log.error(s instanceof Error?s.message:String(s)),process.exit(1)}else{const s=(0,f.detectClients)(i);if(s.length>0){a=s;const b=s.map(w=>w.name).join(", ");t.log.info(`Detected client(s): ${t.pc.bold(b)}`)}else if((0,m.isInteractive)()){const b=[...f.REGISTERED_CLIENTS.map(y=>y.name),"all"],w=b.map(y=>({label:y,hint:y==="all"?"every registered client":void 0}));t.log.warn(`No client detected in ${t.pc.dim(i)}.`);const x=await(0,m.promptSelect)("Which client(s) to install for?",w,0);a=(0,f.resolveTargetClients)(i,b[x])}else t.log.warn(`No client detected. Defaulting to: ${f.REGISTERED_CLIENTS[0].name}`),a=[f.REGISTERED_CLIENTS[0]]}const u=await O(i,c,d),l=await H(i,r,u?.mode);t.log.blank(),(0,M.prepareIronBeeDir)(i),E(i,u),$(i,l);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,v.trackInstall)(a.map(s=>s.name),i)});0&&(module.exports={CYCLE_HINTS,installCommand,runInstallAll,syncSchemaIfChanged});
|
|
@@ -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,16 +1,20 @@
|
|
|
1
|
-
"use strict";var y=Object.defineProperty;var
|
|
2
|
-
`,
|
|
3
|
-
`,
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
"use strict";var y=Object.defineProperty;var q=Object.getOwnPropertyDescriptor;var K=Object.getOwnPropertyNames;var Q=Object.prototype.hasOwnProperty;var l=(t,e)=>y(t,"name",{value:e,configurable:!0});var Y=(t,e)=>{for(var r in e)y(t,r,{get:e[r],enumerable:!0})},Z=(t,e,r,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of K(e))!Q.call(t,n)&&n!==r&&y(t,n,{get:()=>e[n],enumerable:!(i=q(e,n))||i.enumerable});return t};var tt=t=>Z(y({},"__esModule",{value:!0}),t);var at={};Y(at,{VERIFICATION_DOC_DIR:()=>x,VERIFICATION_DOC_FILE:()=>I,buildVerificationContext:()=>H,buildVerificationContextForSession:()=>U,buildVerificationContextOnceForCycle:()=>lt,collectChangedPaths:()=>_,renderChangedPathsBlock:()=>L,renderMessageBlock:()=>G,resolveContextFiles:()=>j,resolveVerificationMessage:()=>W});module.exports=tt(at);var b=require("fs"),w=require("os"),s=require("path"),P=require("../../lib/runtime-paths"),u=require("../../lib/config"),N=require("../../lib/git"),m=require("../../lib/logger"),E=require("./session-state"),M=require("./actions");const x=".ironbee",I="VERIFICATION.md",nt=`===== IRONBEE \u2014 PROJECT VERIFICATION INSTRUCTIONS (path-scoped) =====
|
|
2
|
+
`,et=`The following areas changed this cycle. Follow this guidance IN ADDITION to the standard verification flow.
|
|
3
|
+
`,S=`====================================================================
|
|
4
|
+
`,rt=`===== IRONBEE \u2014 PROJECT VERIFICATION INSTRUCTIONS (always-on) =====
|
|
5
|
+
`,O=64*1024;function $(t){return Buffer.byteLength(t,"utf-8")}l($,"byteLen");function it(t){try{return(0,b.realpathSync)(t)}catch{return(0,s.resolve)(t)}}l(it,"canonicalDir");function st(t){const e=(0,s.resolve)(t),r=[];let i=e;for(;;)try{const n=(0,b.realpathSync)(i);return r.length>0?(0,s.join)(n,...r):n}catch{const n=(0,s.dirname)(i);if(n===i)return e;r.unshift((0,s.basename)(i)),i=n}}l(st,"canonicalizePath");function ot(t,e){const r=(0,s.relative)(t,e);return r.length>0&&!r.startsWith("..")&&!(0,s.isAbsolute)(r)}l(ot,"isUnderDir");function T(t){try{return(0,M.getFileChangesSinceLastVerification)(t).map(e=>e.file_path).filter(e=>typeof e=="string"&&e.length>0)}catch(e){return m.logger.debug(`verification-context: actions fallback failed: ${e instanceof Error?e.message:e}`),[]}}l(T,"changedFromActions");function _(t,e,r){let i;if(r.source==="actions")i=T(e);else{const o=(0,N.getChangedPaths)(t,r.commitDepth);i=o===null?T(e):o}const n=new Set;for(const o of i){const g=st((0,s.isAbsolute)(o)?o:(0,s.resolve)(t,o)),c=(0,s.relative)(t,g);c.length===0||c.startsWith("..")||(0,s.isAbsolute)(c)||c.split(s.sep).includes(x)||n.add(g)}return[...n]}l(_,"collectChangedPaths");function j(t,e){const r=new Map;for(const i of e){let n=(0,s.dirname)(i);if(!(n!==t&&!ot(t,n)))for(;;){const o=(0,s.join)(n,x,I);if(!r.has(o)&&(0,b.existsSync)(o))try{const c=(0,b.readFileSync)(o,"utf-8"),f=(0,s.relative)(t,n),h=f.length===0?"":f.split(s.sep).join("/"),d=h.length===0?0:h.split("/").length;r.set(o,{absPath:o,relDir:h,depth:d,content:c})}catch(c){m.logger.debug(`verification-context: failed to read ${o}: ${c instanceof Error?c.message:c}`)}if(n===t)break;const g=(0,s.dirname)(n);if(g===n)break;n=g}}return[...r.values()].sort((i,n)=>i.depth-n.depth||i.relDir.localeCompare(n.relDir))}l(j,"resolveContextFiles");function ct(t){return t.relDir.length===0?{label:"(repo root)",path:`${x}/${I}`}:{label:`${t.relDir}/`,path:`${t.relDir}/${x}/${I}`}}l(ct,"provenance");function V(t){const e=ct(t);return`
|
|
6
|
+
## ${e.label} \u2014 ${e.path}
|
|
6
7
|
${t.content.trimEnd()}
|
|
7
|
-
`}
|
|
8
|
-
- \u2026 +${
|
|
9
|
-
## Changed files this cycle (${
|
|
10
|
-
${
|
|
11
|
-
`)}${
|
|
12
|
-
`}
|
|
13
|
-
|
|
14
|
-
`)
|
|
15
|
-
|
|
16
|
-
|
|
8
|
+
`}l(V,"renderBlock");const F=100;function L(t,e){if(e.length===0)return"";const r=e.map(o=>(0,s.relative)(t,o).split(s.sep).join("/")).sort(),i=r.slice(0,F),n=r.length>F?`
|
|
9
|
+
- \u2026 +${r.length-F} more`:"";return`
|
|
10
|
+
## Changed files this cycle (${r.length})
|
|
11
|
+
${i.map(o=>`- ${o}`).join(`
|
|
12
|
+
`)}${n}
|
|
13
|
+
`}l(L,"renderChangedPathsBlock");function W(t,e){const r=t.trim();if(r.length===0)return"";const i="file:";if(!r.startsWith(i))return r;let n=r.slice(i.length).trim();if(n.length===0)return"";(n==="~"||n.startsWith("~/")||n.startsWith("~\\"))&&(n=(0,s.join)((0,w.homedir)(),n.slice(1).replace(/^[/\\]+/,"")));const o=(0,s.isAbsolute)(n)?n:(0,s.resolve)(e,n);try{const g=(0,b.readFileSync)(o,"utf-8"),c=Buffer.from(g,"utf-8");if(c.length>O){let f=O;for(;f>0&&(c[f]&192)===128;)f--;return`${c.subarray(0,f).toString("utf-8")}
|
|
14
|
+
... (truncated, message file exceeds ${O} bytes)
|
|
15
|
+
`}return g.trimEnd()}catch(g){return m.logger.debug(`verification-context: failed to read message file ${o}: ${g instanceof Error?g.message:g}`),""}}l(W,"resolveVerificationMessage");function G(t){return`${rt}${t.trimEnd()}
|
|
16
|
+
${S}`}l(G,"renderMessageBlock");function H(t,e){const r=e.changedPathsBlock??"";if(t.length===0&&r.length===0)return"";const i=[...t].sort((a,D)=>a.depth-D.depth||a.relDir.localeCompare(D.relDir)),n=nt+et+r,o=Math.max(0,e.maxBytes-$(n)-$(S)),g=[];let c=0;for(let a=i.length-1;a>=0;a--){const D=V(i[a]),C=$(D);if(c+C<=o)g.push(i[a]),c+=C;else if(g.length===0){const v=gt(i[a],o);g.push(v);break}else break}const f=g.reverse(),h=i.length-f.length,d=[n];for(const a of f)d.push(V(a));return h>0&&d.push(`
|
|
17
|
+
[${h} less-specific VERIFICATION.md file(s) omitted to fit the ${e.maxBytes}-byte cap]
|
|
18
|
+
`),d.push(S),d.join("")}l(H,"buildVerificationContext");function gt(t,e){const r=`
|
|
19
|
+
... (truncated)`,i=V({...t,content:""}),n=Math.max(0,e-$(i)-$(r)),o=Buffer.from(t.content,"utf-8").subarray(0,n).toString("utf-8");return{...t,content:o+r}}l(gt,"hardTruncate");function U(t,e,r){if(!(0,u.getVerificationContextEnabled)(r))return"";try{const i=it(t),n=(0,s.join)((0,P.sessionDir)(i,e),"actions.jsonl"),o=(0,u.getVerificationContextSource)(r),g=_(i,n,{source:o,commitDepth:(0,u.getVerificationContextCommitDepth)(r)}),c=g.filter(p=>!(0,u.isIgnoredVerifyPath)(r,p)),f=g.length-c.length,h=new Set;for(const p of c){const R=(0,s.relative)(i,(0,s.dirname)(p)).split(s.sep).join("/");h.add(R.length===0?"(root)":R)}const d=[...h].sort(),a=50,D=d.slice(0,a),C=d.length>a?` [+${d.length-a} more]`:"";m.logger.debug(`verification-context: source=${o} changed in ${d.length} dir(s)`+(f>0?` (${f} ignored)`:"")+`: ${D.join(", ")}${C}`);const v=j(i,c);for(const p of v){const R=p.relDir.length>0?`${p.relDir}/${x}/${I}`:`${x}/${I}`,B=p.content.replace(/\s+/g," ").trim(),X=B.length>100?`${B.slice(0,100)}...`:B;m.logger.debug(`verification-context: \u2022 ${R} \u2014 ${$(p.content)} bytes :: ${X}`)}const k=W((0,u.getVerificationContextMessage)(r),i),A=k.length>0?G(k):"",J=Math.max(0,(0,u.getVerificationContextMaxBytes)(r)-$(A)),z=H(v,{maxBytes:J,changedPathsBlock:L(i,c)});return A+z}catch(i){return m.logger.debug(`verification-context: build failed: ${i instanceof Error?i.message:i}`),""}}l(U,"buildVerificationContextForSession");function lt(t){const{projectDir:e,sessionId:r,sessionDir:i,activeVerificationId:n,config:o}=t;if(!n||!(0,u.getVerificationContextEnabled)(o)||(0,E.getContextInjectedVerificationId)(i)===n)return"";let g="";try{g=U(e,r,o)}catch(c){m.logger.debug(`verification-context: once-for-cycle build failed: ${c instanceof Error?c.message:c}`),g=""}if(g.length>0){const c=g.split(`
|
|
20
|
+
## `).length-1;m.logger.debug(`verification-context: injected ${Buffer.byteLength(g,"utf-8")} bytes, ${c} section(s) for cycle ${n}`)}else m.logger.debug(`verification-context: nothing to inject (no message, no changed paths) for cycle ${n}`);return(0,E.setContextInjectedVerificationId)(i,n),g}l(lt,"buildVerificationContextOnceForCycle");0&&(module.exports={VERIFICATION_DOC_DIR,VERIFICATION_DOC_FILE,buildVerificationContext,buildVerificationContextForSession,buildVerificationContextOnceForCycle,collectChangedPaths,renderChangedPathsBlock,renderMessageBlock,resolveContextFiles,resolveVerificationMessage});
|