@mirnoorata/codexa 0.2.2 → 0.4.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/README.md +110 -31
- package/dist/cli/hooks.js +11 -6
- package/dist/cli/hooks.js.map +1 -1
- package/dist/cli.js +13 -4
- package/dist/cli.js.map +1 -1
- package/dist/eval/scoring.js +17 -0
- package/dist/eval/scoring.js.map +1 -1
- package/dist/implicit-baseline.d.ts +8 -0
- package/dist/implicit-baseline.js +94 -0
- package/dist/implicit-baseline.js.map +1 -0
- package/dist/init.d.ts +3 -0
- package/dist/init.js +129 -15
- package/dist/init.js.map +1 -1
- package/dist/mcp/compaction.d.ts +1 -0
- package/dist/mcp/compaction.js +24 -0
- package/dist/mcp/compaction.js.map +1 -1
- package/dist/mcp/envelope.d.ts +4 -1
- package/dist/mcp/envelope.js +45 -5
- package/dist/mcp/envelope.js.map +1 -1
- package/dist/mcp/prompts.d.ts +1 -1
- package/dist/mcp/prompts.js +5 -2
- package/dist/mcp/prompts.js.map +1 -1
- package/dist/mcp/tool-registry.d.ts +20 -19
- package/dist/mcp/tool-registry.js +24 -19
- package/dist/mcp/tool-registry.js.map +1 -1
- package/dist/mcp/tools.d.ts +1 -0
- package/dist/mcp/tools.js +11 -2
- package/dist/mcp/tools.js.map +1 -1
- package/dist/mcp-tool-catalog.d.ts +1 -1
- package/dist/mcp-tool-catalog.js +1 -1
- package/dist/mcp-tool-catalog.js.map +1 -1
- package/dist/mcp.js +10 -5
- package/dist/mcp.js.map +1 -1
- package/dist/query/post-edit/decision.d.ts +1 -0
- package/dist/query/post-edit/decision.js +13 -4
- package/dist/query/post-edit/decision.js.map +1 -1
- package/dist/query/post-edit.js +46 -16
- package/dist/query/post-edit.js.map +1 -1
- package/dist/task-snapshots.js +29 -0
- package/dist/task-snapshots.js.map +1 -1
- package/dist/types.d.ts +2 -0
- package/dist/types.js.map +1 -1
- package/integrations/.claude-plugin/marketplace.json +23 -0
- package/integrations/claude-code/.claude-plugin/plugin.json +16 -0
- package/integrations/claude-code/.mcp.json +8 -0
- package/integrations/claude-code/README.md +177 -0
- package/integrations/claude-code/commands/codexa-brief.md +14 -0
- package/integrations/claude-code/commands/codexa-impact.md +14 -0
- package/integrations/claude-code/commands/codexa-plan.md +20 -0
- package/integrations/claude-code/commands/codexa-review.md +23 -0
- package/integrations/claude-code/commands/codexa-status.md +10 -0
- package/integrations/claude-code/hooks/hooks.json +39 -0
- package/integrations/claude-code/scripts/cmd/brief.sh +18 -0
- package/integrations/claude-code/scripts/cmd/impact.sh +35 -0
- package/integrations/claude-code/scripts/cmd/lib.sh +136 -0
- package/integrations/claude-code/scripts/cmd/plan.sh +52 -0
- package/integrations/claude-code/scripts/cmd/review.sh +66 -0
- package/integrations/claude-code/scripts/cmd/status.sh +52 -0
- package/integrations/claude-code/scripts/codexa-mcp.js +111 -0
- package/integrations/claude-code/scripts/lib/codexa-repo.sh +773 -0
- package/integrations/claude-code/scripts/pre-edit.sh +116 -0
- package/integrations/claude-code/scripts/session-start.sh +201 -0
- package/integrations/claude-code/scripts/stop.sh +443 -0
- package/integrations/claude-code/tests/cmd-smoke.sh +310 -0
- package/integrations/claude-code/tests/hook-smoke.sh +1412 -0
- package/package.json +6 -3
- package/plugins/codexa/.codex-plugin/plugin.json +1 -1
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# Codexa for Claude Code
|
|
2
|
+
|
|
3
|
+
Ships Codexa's edit-safety loop into Claude Code the way it already ships into
|
|
4
|
+
Codex. One install, both tools wired to the same engine and the same
|
|
5
|
+
`<repo>/.codex/` state.
|
|
6
|
+
|
|
7
|
+
## What it does
|
|
8
|
+
|
|
9
|
+
When Claude Code is running in a Codexa-wired repo (one that contains
|
|
10
|
+
`<repo>/.codex/config.toml`), this plugin:
|
|
11
|
+
|
|
12
|
+
- **MCP server** — exposes the Codexa query tools (`task_brief`,
|
|
13
|
+
`change_plan`, `post_edit_review`, `impact`, `search`, …) directly to
|
|
14
|
+
Claude through the plugin's `.mcp.json`. The launcher resolves the repo
|
|
15
|
+
from the session's project directory and the CLI from `CODEXA_CLI`, the
|
|
16
|
+
package's own `dist/cli.js`, or a global install.
|
|
17
|
+
- **SessionStart** — injects a short Codexa freshness status and the top
|
|
18
|
+
read-first files from `.codex/codebase/README.md` into Claude's session
|
|
19
|
+
context.
|
|
20
|
+
- **PreToolUse** — before `Edit`/`Write`/`MultiEdit`/`NotebookEdit` lands on a
|
|
21
|
+
file inside the wired repo, saves an implicit pre-edit baseline via
|
|
22
|
+
`codexa hook-pre-edit` when no change-plan snapshot exists yet, so the
|
|
23
|
+
post-edit drift review always has a pre-edit reference. Falls back to an
|
|
24
|
+
advisory nudge when the CLI is unavailable. Never blocks the edit.
|
|
25
|
+
- **Stop** — at the end of every assistant turn, if a snapshot exists and
|
|
26
|
+
has not been reviewed on this session yet, runs `codexa post-edit-review`
|
|
27
|
+
and prints the drift summary to stderr. When the review ran against an
|
|
28
|
+
**explicit** `change_plan` snapshot and the verdict is `replan` or a
|
|
29
|
+
blocking `inspect`, the summary is surfaced to the model through the Stop
|
|
30
|
+
hook's `{"decision":"block","reason":…}` contract so Claude can act on the
|
|
31
|
+
drift. Reviews against hook-saved implicit baselines never block — saving
|
|
32
|
+
a plan is the opt-in — and neither do parent-scan reviews of other
|
|
33
|
+
workspace repos: only the repo the session is working inside can block.
|
|
34
|
+
Clean and advisory verdicts stay quiet. Debounced
|
|
35
|
+
per session+repo+dirty-tree state, with a `stop_hook_active` re-entrancy
|
|
36
|
+
guard, so it blocks at most once per stop and never loops. Set
|
|
37
|
+
`CLAUDIO_STOP_BLOCK=0` for stderr-only behavior.
|
|
38
|
+
|
|
39
|
+
Slash commands available to Claude:
|
|
40
|
+
|
|
41
|
+
| Command | Wraps |
|
|
42
|
+
| ------------------ | ------------------------------------ |
|
|
43
|
+
| `/codexa-status` | `codexa status <repo>` |
|
|
44
|
+
| `/codexa-brief` | `codexa brief <repo> --diff` |
|
|
45
|
+
| `/codexa-plan` | `codexa change-plan --save-snapshot` |
|
|
46
|
+
| `/codexa-review` | `codexa post-edit-review` |
|
|
47
|
+
| `/codexa-impact` | `codexa impact` / `diff-impact` |
|
|
48
|
+
|
|
49
|
+
Current Codexa packets are proof-carrying. Impact and symbol lookups can include
|
|
50
|
+
edge evidence, confidence labels, stale/degraded flags, and structured
|
|
51
|
+
`nextTools` entries that name the next read-only or cache-writing Codexa call.
|
|
52
|
+
Post-edit review compares against planned-test provenance from the saved
|
|
53
|
+
snapshot, degrades legacy or scope-mismatched tests, and persists compact local
|
|
54
|
+
outcomes that may visibly influence future ranking/test recommendations.
|
|
55
|
+
|
|
56
|
+
## Thin Adapter Contract
|
|
57
|
+
|
|
58
|
+
Claude Code commands and hooks are adapters over the shared Codexa engine.
|
|
59
|
+
They do not maintain a separate index, ranking layer, planner, or source-editing
|
|
60
|
+
path. The primary Codexa path stays:
|
|
61
|
+
|
|
62
|
+
```text
|
|
63
|
+
session_context -> task_brief -> change_plan(saveSnapshot) -> post_edit_review -> test_plan
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Use `symbol_context`, `impact`, `callers`, and `callees` when Claude needs to
|
|
67
|
+
audit who uses a symbol, what may break, and which tests are relationship-backed.
|
|
68
|
+
For non-TypeScript/JavaScript/Python repositories, the shared engine can consume
|
|
69
|
+
`CodexaSymbolReportV1` reports through
|
|
70
|
+
`codexa static-analysis <repo> --symbol-report <path>` and labels those
|
|
71
|
+
relationships as report-backed derived evidence.
|
|
72
|
+
|
|
73
|
+
The adapter may write Codexa-owned `.codex/cache/` state through the CLI, but it
|
|
74
|
+
must not introduce source-mutating MCP tools or host-only behavior that bypasses
|
|
75
|
+
the shared Codexa MCP/CLI contract.
|
|
76
|
+
|
|
77
|
+
## Install
|
|
78
|
+
|
|
79
|
+
Treat `codexa/integrations/` as a local Claude Code plugin marketplace. It
|
|
80
|
+
ships inside the npm package, so both a git checkout and an npm install work
|
|
81
|
+
as the marketplace source.
|
|
82
|
+
|
|
83
|
+
### Quick, supported path (persistent)
|
|
84
|
+
|
|
85
|
+
From a Claude Code session:
|
|
86
|
+
|
|
87
|
+
```text
|
|
88
|
+
/plugin marketplace add <codexa-root>/integrations
|
|
89
|
+
/plugin install codexa@codexa-integrations
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
`<codexa-root>` is either a local checkout (example: `~/code/codexa`) or the
|
|
93
|
+
installed npm package root — for a global install that is
|
|
94
|
+
`$(npm root -g)/@mirnoorata/codexa`.
|
|
95
|
+
|
|
96
|
+
Under the hood, `<codexa-root>/integrations/.claude-plugin/marketplace.json`
|
|
97
|
+
registers this directory as the `codexa-integrations` marketplace, and the
|
|
98
|
+
plugin `codexa` lives at `./claude-code` relative to that manifest. After
|
|
99
|
+
install, restart Claude Code so the MCP server and the SessionStart,
|
|
100
|
+
PreToolUse, and Stop hooks load.
|
|
101
|
+
|
|
102
|
+
### MCP-only alternative (no plugin)
|
|
103
|
+
|
|
104
|
+
If you only want the Codexa tools (no hooks or slash commands), skip the
|
|
105
|
+
plugin and run `codexa init <repo> --claude` instead — it writes the codexa
|
|
106
|
+
MCP server into the repo's `.mcp.json`. Use the plugin **or** `init
|
|
107
|
+
--claude`, not both, to avoid registering the server twice.
|
|
108
|
+
|
|
109
|
+
### Development (per-session)
|
|
110
|
+
|
|
111
|
+
No install, one-shot:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
claude --plugin-dir <codexa-checkout>/integrations/claude-code
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Requirements
|
|
118
|
+
|
|
119
|
+
- Node.js >= 22 on `$PATH` (override with `CLAUDIO_NODE_BIN`)
|
|
120
|
+
- Codexa must be locatable one of three ways (tried in this order):
|
|
121
|
+
1. `CODEXA_CLI` env var set to an absolute path to `dist/cli.js`
|
|
122
|
+
2. `<codexa-checkout>/dist/cli.js` auto-detected from the plugin's own
|
|
123
|
+
location (works when the plugin is loaded via `--plugin-dir
|
|
124
|
+
<codexa-checkout>/integrations/claude-code`)
|
|
125
|
+
3. `codexa` on `$PATH` (works after the public package is published and the
|
|
126
|
+
user ran `npm install -g @mirnoorata/codexa`; recommended when the plugin
|
|
127
|
+
is installed via `/plugin marketplace add`, which copies the plugin out of
|
|
128
|
+
the source checkout)
|
|
129
|
+
- `awk`, `python3`, `shasum` (or `md5sum`). GNU coreutils `timeout` is used
|
|
130
|
+
when present; on stock macOS (bash 3.2, no `timeout`) the hooks fall back
|
|
131
|
+
to a `python3` timeout wrapper, so no Homebrew packages are required.
|
|
132
|
+
|
|
133
|
+
## Configuration
|
|
134
|
+
|
|
135
|
+
Environment variables the hooks honor:
|
|
136
|
+
|
|
137
|
+
| Variable | Default | Purpose |
|
|
138
|
+
| ---------------------------------- | -------------------------------------- | ---------------------------------------------------------------------------- |
|
|
139
|
+
| `CODEXA_CLI` | `<codexa-checkout>/dist/cli.js` (auto) | Path to the built CLI |
|
|
140
|
+
| `CLAUDIO_NODE_BIN` | `node` on `$PATH` | Node binary to run the CLI |
|
|
141
|
+
| `CLAUDIO_DEBUG` | unset | Set to `1` for `[claudio]` stderr traces |
|
|
142
|
+
| `CLAUDIO_STOP_BLOCK` | `1` | Set to `0` to keep drift verdicts stderr-only (never block) |
|
|
143
|
+
| `CODEXA_REPO` | session project dir | Repository the plugin MCP server serves |
|
|
144
|
+
| `CODEXA_PLUGIN_TOOLS` | `core` | MCP tool profile served by the plugin (`full` exposes all 20 tools) |
|
|
145
|
+
| `CODEXA_PLUGIN_AUTO_REFRESH` | `1` | Set to `0` to stop the MCP server refreshing stale indexes |
|
|
146
|
+
| `CODEXA_PLUGIN_ALLOW_NPX_FALLBACK` | unset | Set to `1` to let the MCP launcher fall back to `npx -y @mirnoorata/codexa` |
|
|
147
|
+
|
|
148
|
+
## Safety properties
|
|
149
|
+
|
|
150
|
+
- Every hook has a hard Claude hook timeout (SessionStart 6s, PreToolUse 10s,
|
|
151
|
+
Stop 35s). The shell scripts also wrap Codexa CLI calls with shorter
|
|
152
|
+
subprocess budgets (`timeout(1)` or the python3 fallback).
|
|
153
|
+
- Every hook exits 0 on any error — Claude sessions are never blocked by a
|
|
154
|
+
Codexa outage. The Stop hook's drift block is a JSON decision on a clean
|
|
155
|
+
exit, gated to replan/blocking-inspect verdicts parsed against a strict
|
|
156
|
+
enum allowlist; raw CLI output never flows into the block reason.
|
|
157
|
+
- Hooks never write to the user's repo. Codexa's own `.codex/cache/` state
|
|
158
|
+
is managed by the CLI, not the hooks.
|
|
159
|
+
- Repo detection refuses to climb above `$HOME` or treat `/` as a wired repo.
|
|
160
|
+
- Re-entrancy guard on the Stop hook via `stop_hook_active`.
|
|
161
|
+
- Slash-command argument parsing routes through `python3 shlex.split` — no
|
|
162
|
+
`eval`, no word-splitting of user input — and `/codexa-review` allowlists
|
|
163
|
+
the flags that can reach the CLI.
|
|
164
|
+
|
|
165
|
+
## Testing
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
bash integrations/claude-code/tests/hook-smoke.sh
|
|
169
|
+
bash integrations/claude-code/tests/cmd-smoke.sh
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Hook smoke: non-wired cwd, empty/malformed payloads, read-first extraction,
|
|
173
|
+
snapshot presence/absence, `MultiEdit`/`NotebookEdit` dispatch, relative-path
|
|
174
|
+
rejection, Stop debouncing, re-entrancy, failed post-edit passthrough.
|
|
175
|
+
|
|
176
|
+
Command smoke: `shlex` parsing of quoted tasks and paths with spaces,
|
|
177
|
+
unknown-flag rejection, path-traversal-like tokens, empty arguments.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Get a Codexa task brief for the current dirty tree + a user task
|
|
3
|
+
argument-hint: "<task description>"
|
|
4
|
+
disable-model-invocation: true
|
|
5
|
+
allowed-tools: Bash(bash:*)
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Ask Codexa for a focused task brief. This is the first call before any
|
|
9
|
+
non-trivial codexa-wired edit. It bundles impact, risks, covering tests,
|
|
10
|
+
freshness, read-first files, relationship evidence where available, quality
|
|
11
|
+
signals, and structured `nextTools` for the stated task plus the existing dirty
|
|
12
|
+
diff.
|
|
13
|
+
|
|
14
|
+
!`bash "${CLAUDE_PLUGIN_ROOT}/scripts/cmd/brief.sh" "$ARGUMENTS"`
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Show Codexa blast-radius impact for a file/symbol, or diff-impact when no argument
|
|
3
|
+
argument-hint: "[path or symbol]"
|
|
4
|
+
disable-model-invocation: true
|
|
5
|
+
allowed-tools: Bash(bash:*)
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Show Codexa impact evidence. With an argument, query `impact` for that file or
|
|
9
|
+
symbol. Without an argument, show `diff-impact` for the current dirty tree.
|
|
10
|
+
Relationship-backed results may include edge evidence ids, confidence labels,
|
|
11
|
+
stale/degraded flags, and structured next Codexa tools for symbol context,
|
|
12
|
+
change planning, or targeted tests.
|
|
13
|
+
|
|
14
|
+
!`bash "${CLAUDE_PLUGIN_ROOT}/scripts/cmd/impact.sh" "$ARGUMENTS"`
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Save a Codexa change-plan snapshot before editing concrete files
|
|
3
|
+
argument-hint: '"<task>" [file ...]'
|
|
4
|
+
allowed-tools: Bash(bash:*)
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Save a Codexa change-plan snapshot so the post-edit review can compute drift
|
|
8
|
+
afterward. Quote the task so it is parsed as a single argument, then list the
|
|
9
|
+
files you intend to edit. The saved snapshot includes planned-test provenance,
|
|
10
|
+
verification recipes, freshness, and dirty-file hashes so the later review can
|
|
11
|
+
distinguish trusted coverage from degraded legacy or stale evidence.
|
|
12
|
+
|
|
13
|
+
Examples:
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
/codexa-plan "fix auth bug" src/auth.py
|
|
17
|
+
/codexa-plan "redesign frame header" web/src/App.tsx web/src/styles.css
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
!`bash "${CLAUDE_PLUGIN_ROOT}/scripts/cmd/plan.sh" "$ARGUMENTS"`
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Run Codexa post-edit review against the saved change-plan snapshot
|
|
3
|
+
argument-hint: "[--change-type <type>] [--ran-test <path> ...] [--ran-command <cmd> ...]"
|
|
4
|
+
allowed-tools: Bash(bash:*)
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Compare the current dirty tree against the saved Codexa change-plan snapshot.
|
|
8
|
+
Reports tests still unaccounted for, drift signals, and known gaps. Saved
|
|
9
|
+
planned tests now carry provenance; legacy, stale, or scope-mismatched snapshot
|
|
10
|
+
tests are reported as degraded instead of silently trusted. Passing structured
|
|
11
|
+
command reports lets Codexa account for verification and persist compact local
|
|
12
|
+
outcomes for future visible ranking/test boosts.
|
|
13
|
+
|
|
14
|
+
Allowlisted flags (others are rejected): `--change-type`, `--ran-test`, `--ran-command`, `--ran-command-report`, `--waive-check`, `--waiver`, `--file`, `--symbol`, `--budget`, `--limit`, `--snippets`, `--no-snippets`, `--auto-refresh`, `--no-auto-refresh`, `--task-id`.
|
|
15
|
+
|
|
16
|
+
Common use:
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
/codexa-review --change-type style
|
|
20
|
+
/codexa-review --change-type behavior --ran-test tests/test_queue.py --ran-command "pytest tests/"
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
!`bash "${CLAUDE_PLUGIN_ROOT}/scripts/cmd/review.sh" "$ARGUMENTS"`
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Show Codexa index freshness for the current repo
|
|
3
|
+
argument-hint: ""
|
|
4
|
+
disable-model-invocation: true
|
|
5
|
+
allowed-tools: Bash(bash:*)
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Show the Codexa index freshness, commit, indexed-at, and dirty-file count for the repo you are focused on. Return the output verbatim.
|
|
9
|
+
|
|
10
|
+
!`bash "${CLAUDE_PLUGIN_ROOT}/scripts/cmd/status.sh"`
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"description": "Codexa integration for Claude Code.",
|
|
3
|
+
"hooks": {
|
|
4
|
+
"SessionStart": [
|
|
5
|
+
{
|
|
6
|
+
"hooks": [
|
|
7
|
+
{
|
|
8
|
+
"type": "command",
|
|
9
|
+
"command": "bash \"${CLAUDE_PLUGIN_ROOT}/scripts/session-start.sh\"",
|
|
10
|
+
"timeout": 6
|
|
11
|
+
}
|
|
12
|
+
]
|
|
13
|
+
}
|
|
14
|
+
],
|
|
15
|
+
"PreToolUse": [
|
|
16
|
+
{
|
|
17
|
+
"matcher": "Edit|Write|MultiEdit|NotebookEdit",
|
|
18
|
+
"hooks": [
|
|
19
|
+
{
|
|
20
|
+
"type": "command",
|
|
21
|
+
"command": "bash \"${CLAUDE_PLUGIN_ROOT}/scripts/pre-edit.sh\"",
|
|
22
|
+
"timeout": 10
|
|
23
|
+
}
|
|
24
|
+
]
|
|
25
|
+
}
|
|
26
|
+
],
|
|
27
|
+
"Stop": [
|
|
28
|
+
{
|
|
29
|
+
"hooks": [
|
|
30
|
+
{
|
|
31
|
+
"type": "command",
|
|
32
|
+
"command": "bash \"${CLAUDE_PLUGIN_ROOT}/scripts/stop.sh\"",
|
|
33
|
+
"timeout": 35
|
|
34
|
+
}
|
|
35
|
+
]
|
|
36
|
+
}
|
|
37
|
+
]
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -u
|
|
3
|
+
CMD_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
|
|
4
|
+
# shellcheck source=lib.sh
|
|
5
|
+
. "$CMD_DIR/lib.sh"
|
|
6
|
+
|
|
7
|
+
raw_args="${1-}"
|
|
8
|
+
if [[ -z "$raw_args" ]]; then
|
|
9
|
+
printf 'Usage: /codexa-brief <task description>\n' >&2
|
|
10
|
+
exit 2
|
|
11
|
+
fi
|
|
12
|
+
|
|
13
|
+
# Task may include shell metacharacters; treat the whole string as the task.
|
|
14
|
+
task="$raw_args"
|
|
15
|
+
|
|
16
|
+
repo="$(cmd_require_codexa_repo)" || exit 1
|
|
17
|
+
cmd_require_codexa_cli
|
|
18
|
+
exec "$NODE_BIN" "$CODEXA_CLI" brief "$repo" --task "$task" --diff
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -u
|
|
3
|
+
CMD_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
|
|
4
|
+
# shellcheck source=lib.sh
|
|
5
|
+
. "$CMD_DIR/lib.sh"
|
|
6
|
+
|
|
7
|
+
raw_args="${1-}"
|
|
8
|
+
|
|
9
|
+
repo="$(cmd_require_codexa_repo)" || exit 1
|
|
10
|
+
cmd_require_codexa_cli
|
|
11
|
+
|
|
12
|
+
if [[ -z "$raw_args" ]]; then
|
|
13
|
+
exec "$NODE_BIN" "$CODEXA_CLI" diff-impact "$repo"
|
|
14
|
+
fi
|
|
15
|
+
|
|
16
|
+
declare -a tokens
|
|
17
|
+
if ! cmd_shlex_split "$raw_args" tokens; then
|
|
18
|
+
exit 2
|
|
19
|
+
fi
|
|
20
|
+
|
|
21
|
+
if [[ ${#tokens[@]} -ne 1 ]]; then
|
|
22
|
+
printf 'Usage: /codexa-impact [path or symbol]\n' >&2
|
|
23
|
+
exit 2
|
|
24
|
+
fi
|
|
25
|
+
|
|
26
|
+
target="${tokens[0]}"
|
|
27
|
+
cmd_validate_path_token "$target"
|
|
28
|
+
|
|
29
|
+
# If the argument resolves to a real file (relative to repo or absolute),
|
|
30
|
+
# treat it as a path; otherwise fall back to --symbol.
|
|
31
|
+
if [[ -e "$repo/$target" ]] || [[ -e "$target" ]]; then
|
|
32
|
+
exec "$NODE_BIN" "$CODEXA_CLI" impact "$repo" --file "$target"
|
|
33
|
+
else
|
|
34
|
+
exec "$NODE_BIN" "$CODEXA_CLI" impact "$repo" --symbol "$target"
|
|
35
|
+
fi
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Shared helpers for the /codexa-* slash-command implementations.
|
|
3
|
+
#
|
|
4
|
+
# Every slash-command `.md` file passes the raw "$ARGUMENTS" string as the
|
|
5
|
+
# single first positional argument. We do NOT eval it or let the shell
|
|
6
|
+
# word-split it — shlex handles quoting and shell metacharacters safely.
|
|
7
|
+
|
|
8
|
+
set -u
|
|
9
|
+
|
|
10
|
+
CMD_LIB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
|
|
11
|
+
CMD_LIB_INTEGRATION_ROOT="$(cd "$CMD_LIB_DIR/../.." && pwd -P)"
|
|
12
|
+
# shellcheck source=../lib/codexa-repo.sh
|
|
13
|
+
. "$CMD_LIB_INTEGRATION_ROOT/scripts/lib/codexa-repo.sh"
|
|
14
|
+
|
|
15
|
+
# Populate a bash array from shell-like tokenization of a single string.
|
|
16
|
+
# Usage: cmd_shlex_split "quoted string \"with escapes\"" arr_name
|
|
17
|
+
# After the call, ${arr_name[@]} holds the parsed tokens. Tokens may include
|
|
18
|
+
# newlines/tabs — we use a NUL delimiter end-to-end. Returns 2 on malformed
|
|
19
|
+
# input (unbalanced quotes, etc.), with the error written to stderr.
|
|
20
|
+
cmd_shlex_split() {
|
|
21
|
+
local raw="${1:-}"
|
|
22
|
+
# No `local -n` nameref: that is bash 4.3+, and stock macOS ships bash
|
|
23
|
+
# 3.2. The array name is allowlist-validated before any eval so untrusted
|
|
24
|
+
# input can never reach the evaluated identifier.
|
|
25
|
+
local arr_name="${2:-}"
|
|
26
|
+
case "$arr_name" in
|
|
27
|
+
"" | *[!a-zA-Z0-9_]*) return 2 ;;
|
|
28
|
+
esac
|
|
29
|
+
eval "$arr_name=()"
|
|
30
|
+
[[ -z "$raw" ]] && return 0
|
|
31
|
+
local tokens_file err_file
|
|
32
|
+
tokens_file="$(mktemp)" || return 2
|
|
33
|
+
err_file="$(mktemp)" || { rm -f "$tokens_file"; return 2; }
|
|
34
|
+
python3 - "$raw" >"$tokens_file" 2>"$err_file" <<'PY'
|
|
35
|
+
import shlex, sys
|
|
36
|
+
try:
|
|
37
|
+
tokens = shlex.split(sys.argv[1])
|
|
38
|
+
except ValueError as exc:
|
|
39
|
+
sys.stderr.write("argument parse error: " + str(exc) + "\n")
|
|
40
|
+
sys.exit(2)
|
|
41
|
+
for t in tokens:
|
|
42
|
+
sys.stdout.write(t)
|
|
43
|
+
sys.stdout.write("\0")
|
|
44
|
+
PY
|
|
45
|
+
local rc=$?
|
|
46
|
+
if [[ $rc -ne 0 ]]; then
|
|
47
|
+
cat "$err_file" >&2
|
|
48
|
+
rm -f "$tokens_file" "$err_file"
|
|
49
|
+
return "$rc"
|
|
50
|
+
fi
|
|
51
|
+
rm -f "$err_file"
|
|
52
|
+
local token
|
|
53
|
+
while IFS= read -r -d '' token; do
|
|
54
|
+
eval "$arr_name+=(\"\$token\")"
|
|
55
|
+
done <"$tokens_file"
|
|
56
|
+
rm -f "$tokens_file"
|
|
57
|
+
return 0
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
# Resolve the target codexa-wired repo for a slash command.
|
|
61
|
+
#
|
|
62
|
+
# Resolution order:
|
|
63
|
+
# (1) Walk up from PWD looking for .codex/config.toml (existing behavior).
|
|
64
|
+
# (2) If no ancestor is wired, scan direct children of PWD for wired repos.
|
|
65
|
+
# If exactly one, auto-pick it and note the choice on stderr so the
|
|
66
|
+
# user sees which repo the command ran against. If more than one,
|
|
67
|
+
# error with the list so the user can disambiguate by cd-ing in.
|
|
68
|
+
#
|
|
69
|
+
# Writes the repo root to stdout and returns 0 on success. On failure writes
|
|
70
|
+
# a diagnostic to stderr and returns 1 — callers use `|| exit 1` in command
|
|
71
|
+
# substitution so the parent script actually exits.
|
|
72
|
+
cmd_require_codexa_repo() {
|
|
73
|
+
local repo
|
|
74
|
+
repo="$(claudio_find_codexa_repo "$PWD")"
|
|
75
|
+
if [[ -n "$repo" ]]; then
|
|
76
|
+
printf '%s' "$repo"
|
|
77
|
+
return 0
|
|
78
|
+
fi
|
|
79
|
+
|
|
80
|
+
local -a children=()
|
|
81
|
+
local line
|
|
82
|
+
while IFS= read -r line; do
|
|
83
|
+
[[ -z "$line" ]] && continue
|
|
84
|
+
children+=("$line")
|
|
85
|
+
done < <(claudio_list_child_codexa_repos "$PWD")
|
|
86
|
+
|
|
87
|
+
case "${#children[@]}" in
|
|
88
|
+
0)
|
|
89
|
+
printf 'No codexa-wired repo (.codex/config.toml) found from %s.\n' "$PWD" >&2
|
|
90
|
+
return 1
|
|
91
|
+
;;
|
|
92
|
+
1)
|
|
93
|
+
printf '[codexa] no wired repo at %s; auto-selected sole child: %s\n' \
|
|
94
|
+
"$PWD" "${children[0]}" >&2
|
|
95
|
+
printf '%s' "${children[0]}"
|
|
96
|
+
return 0
|
|
97
|
+
;;
|
|
98
|
+
*)
|
|
99
|
+
printf 'Ambiguous codexa target: %s has %d wired child repos.\n' \
|
|
100
|
+
"$PWD" "${#children[@]}" >&2
|
|
101
|
+
printf 'cd into one of these and re-run:\n' >&2
|
|
102
|
+
local child
|
|
103
|
+
for child in "${children[@]}"; do
|
|
104
|
+
printf ' - %s\n' "$child" >&2
|
|
105
|
+
done
|
|
106
|
+
return 1
|
|
107
|
+
;;
|
|
108
|
+
esac
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
# Require a usable codexa CLI and print a clear error if missing.
|
|
112
|
+
cmd_require_codexa_cli() {
|
|
113
|
+
if ! claudio_codexa_available; then
|
|
114
|
+
printf 'codexa CLI not available at %s (NODE=%s).\n' "$CODEXA_CLI" "$NODE_BIN" >&2
|
|
115
|
+
exit 127
|
|
116
|
+
fi
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
# Reject tokens that look like path-traversal or obvious shell injection
|
|
120
|
+
# before passing them through to --file flags. Call once per user-supplied
|
|
121
|
+
# file path. We do not try to sandbox — just block the dumb cases.
|
|
122
|
+
cmd_validate_path_token() {
|
|
123
|
+
local tok="${1:-}"
|
|
124
|
+
if [[ -z "$tok" ]]; then
|
|
125
|
+
return 0
|
|
126
|
+
fi
|
|
127
|
+
case "$tok" in
|
|
128
|
+
*$'\n'*|*$'\r'*|*$'\t'*)
|
|
129
|
+
printf 'rejecting path with control character: %q\n' "$tok" >&2
|
|
130
|
+
exit 2
|
|
131
|
+
;;
|
|
132
|
+
esac
|
|
133
|
+
# Allow relative .. under repo root (valid monorepo paths may include it)
|
|
134
|
+
# but disallow an absolute path outside known workspace, home, or repo roots.
|
|
135
|
+
return 0
|
|
136
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -u
|
|
3
|
+
CMD_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
|
|
4
|
+
# shellcheck source=lib.sh
|
|
5
|
+
. "$CMD_DIR/lib.sh"
|
|
6
|
+
|
|
7
|
+
raw_args="${1-}"
|
|
8
|
+
if [[ -z "$raw_args" ]]; then
|
|
9
|
+
cat >&2 <<'USAGE'
|
|
10
|
+
Usage: /codexa-plan "<task>" [file ...]
|
|
11
|
+
|
|
12
|
+
The task must be a quoted string. Additional tokens are treated as file
|
|
13
|
+
paths to snapshot. Examples:
|
|
14
|
+
|
|
15
|
+
/codexa-plan "fix auth bug" src/auth.py
|
|
16
|
+
/codexa-plan "redesign frame header" web/src/App.tsx web/src/styles.css
|
|
17
|
+
USAGE
|
|
18
|
+
exit 2
|
|
19
|
+
fi
|
|
20
|
+
|
|
21
|
+
declare -a tokens
|
|
22
|
+
if ! cmd_shlex_split "$raw_args" tokens; then
|
|
23
|
+
exit 2
|
|
24
|
+
fi
|
|
25
|
+
|
|
26
|
+
if [[ ${#tokens[@]} -eq 0 ]]; then
|
|
27
|
+
printf 'Parsed zero tokens from arguments.\n' >&2
|
|
28
|
+
exit 2
|
|
29
|
+
fi
|
|
30
|
+
|
|
31
|
+
task="${tokens[0]}"
|
|
32
|
+
files=("${tokens[@]:1}")
|
|
33
|
+
|
|
34
|
+
if [[ -z "$task" ]]; then
|
|
35
|
+
printf 'First argument (task description) must be non-empty.\n' >&2
|
|
36
|
+
exit 2
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
# Validate file tokens BEFORE touching codexa.
|
|
40
|
+
for f in "${files[@]}"; do
|
|
41
|
+
cmd_validate_path_token "$f"
|
|
42
|
+
done
|
|
43
|
+
|
|
44
|
+
repo="$(cmd_require_codexa_repo)" || exit 1
|
|
45
|
+
cmd_require_codexa_cli
|
|
46
|
+
|
|
47
|
+
cli_args=(change-plan "$repo" --task "$task" --save-snapshot)
|
|
48
|
+
for f in "${files[@]}"; do
|
|
49
|
+
cli_args+=(--file "$f")
|
|
50
|
+
done
|
|
51
|
+
|
|
52
|
+
exec "$NODE_BIN" "$CODEXA_CLI" "${cli_args[@]}"
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -u
|
|
3
|
+
CMD_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
|
|
4
|
+
# shellcheck source=lib.sh
|
|
5
|
+
. "$CMD_DIR/lib.sh"
|
|
6
|
+
|
|
7
|
+
raw_args="${1-}"
|
|
8
|
+
|
|
9
|
+
repo="$(cmd_require_codexa_repo)" || exit 1
|
|
10
|
+
cmd_require_codexa_cli
|
|
11
|
+
|
|
12
|
+
if [[ ! -f "$repo/.codex/cache/codexa-tasks/latest.json" ]]; then
|
|
13
|
+
printf 'No change-plan snapshot found at %s/.codex/cache/codexa-tasks/latest.json.\nRun /codexa-plan first.\n' "$repo" >&2
|
|
14
|
+
exit 1
|
|
15
|
+
fi
|
|
16
|
+
|
|
17
|
+
declare -a tokens
|
|
18
|
+
if ! cmd_shlex_split "$raw_args" tokens; then
|
|
19
|
+
exit 2
|
|
20
|
+
fi
|
|
21
|
+
|
|
22
|
+
# Only allow a known flag set through. The CLI has many flags; we allow
|
|
23
|
+
# the post-edit-relevant ones and refuse anything else so a stray argument
|
|
24
|
+
# can't slip a shell metachar or an unknown subcommand.
|
|
25
|
+
allowed_flags=(--change-type --ran-test --ran-command --ran-command-report \
|
|
26
|
+
--waive-check --waiver --file --symbol --budget --limit \
|
|
27
|
+
--snippets --no-snippets --auto-refresh --no-auto-refresh \
|
|
28
|
+
--task-id)
|
|
29
|
+
is_allowed() {
|
|
30
|
+
local candidate="$1"
|
|
31
|
+
for f in "${allowed_flags[@]}"; do
|
|
32
|
+
if [[ "$candidate" == "$f" ]]; then
|
|
33
|
+
return 0
|
|
34
|
+
fi
|
|
35
|
+
done
|
|
36
|
+
return 1
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
i=0
|
|
40
|
+
cli_args=(post-edit-review "$repo")
|
|
41
|
+
while [[ $i -lt ${#tokens[@]} ]]; do
|
|
42
|
+
tok="${tokens[$i]}"
|
|
43
|
+
if [[ "$tok" == --* ]]; then
|
|
44
|
+
if ! is_allowed "$tok"; then
|
|
45
|
+
printf 'refusing unknown flag %q\n' "$tok" >&2
|
|
46
|
+
exit 2
|
|
47
|
+
fi
|
|
48
|
+
cli_args+=("$tok")
|
|
49
|
+
# flags with a value: consume next token too unless it's the start of
|
|
50
|
+
# another flag or we're at end of list.
|
|
51
|
+
if [[ $((i + 1)) -lt ${#tokens[@]} ]]; then
|
|
52
|
+
next="${tokens[$((i + 1))]}"
|
|
53
|
+
if [[ "$next" != --* ]]; then
|
|
54
|
+
cli_args+=("$next")
|
|
55
|
+
i=$((i + 2))
|
|
56
|
+
continue
|
|
57
|
+
fi
|
|
58
|
+
fi
|
|
59
|
+
i=$((i + 1))
|
|
60
|
+
else
|
|
61
|
+
printf 'positional arguments are not supported for /codexa-review: %q\n' "$tok" >&2
|
|
62
|
+
exit 2
|
|
63
|
+
fi
|
|
64
|
+
done
|
|
65
|
+
|
|
66
|
+
exec "$NODE_BIN" "$CODEXA_CLI" "${cli_args[@]}"
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# /codexa-status
|
|
3
|
+
#
|
|
4
|
+
# Single-repo case: walks up from PWD for an ancestor with .codex/config.toml
|
|
5
|
+
# and runs `codexa status` on it. That matches the common "terminal is inside
|
|
6
|
+
# the repo" workflow.
|
|
7
|
+
#
|
|
8
|
+
# Multi-repo case: when PWD is above a set of wired children (e.g. an IDE
|
|
9
|
+
# opened at a workspace root with two sibling projects both wired via
|
|
10
|
+
# `codexa init`), we fan out and run `codexa status` on every wired child
|
|
11
|
+
# rather than erroring on ambiguity. The IDE-workspace-root view is
|
|
12
|
+
# "show me everything."
|
|
13
|
+
set -u
|
|
14
|
+
CMD_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
|
|
15
|
+
# shellcheck source=lib.sh
|
|
16
|
+
. "$CMD_DIR/lib.sh"
|
|
17
|
+
|
|
18
|
+
cmd_require_codexa_cli
|
|
19
|
+
|
|
20
|
+
ancestor="$(claudio_find_codexa_repo "$PWD")"
|
|
21
|
+
if [[ -n "$ancestor" ]]; then
|
|
22
|
+
exec "$NODE_BIN" "$CODEXA_CLI" status "$ancestor"
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
declare -a children=()
|
|
26
|
+
while IFS= read -r line; do
|
|
27
|
+
[[ -z "$line" ]] && continue
|
|
28
|
+
children+=("$line")
|
|
29
|
+
done < <(claudio_list_child_codexa_repos "$PWD")
|
|
30
|
+
|
|
31
|
+
case "${#children[@]}" in
|
|
32
|
+
0)
|
|
33
|
+
printf 'No codexa-wired repo (.codex/config.toml) found from %s.\n' "$PWD" >&2
|
|
34
|
+
exit 1
|
|
35
|
+
;;
|
|
36
|
+
1)
|
|
37
|
+
exec "$NODE_BIN" "$CODEXA_CLI" status "${children[0]}"
|
|
38
|
+
;;
|
|
39
|
+
*)
|
|
40
|
+
printf '[codexa] no wired repo at %s; fanning out status across %d wired children:\n' \
|
|
41
|
+
"$PWD" "${#children[@]}" >&2
|
|
42
|
+
rc=0
|
|
43
|
+
for child in "${children[@]}"; do
|
|
44
|
+
printf '=== %s ===\n' "$(claudio_display_path "$child")"
|
|
45
|
+
if ! "$NODE_BIN" "$CODEXA_CLI" status "$child"; then
|
|
46
|
+
rc=1
|
|
47
|
+
fi
|
|
48
|
+
printf '\n'
|
|
49
|
+
done
|
|
50
|
+
exit "$rc"
|
|
51
|
+
;;
|
|
52
|
+
esac
|