@mirnoorata/codexa 0.2.1 → 0.3.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 +93 -29
- package/dist/cache-lock.js +72 -12
- package/dist/cache-lock.js.map +1 -1
- 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/graph.js +4 -2
- package/dist/graph.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 +124 -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 +1 -0
- package/dist/mcp/tool-registry.js +5 -0
- package/dist/mcp/tool-registry.js.map +1 -1
- package/dist/mcp/tools.d.ts +1 -0
- package/dist/mcp/tools.js +6 -0
- 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 +31 -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 +10 -2
- package/dist/query/post-edit.js.map +1 -1
- package/dist/query/verification/masking.d.ts +8 -0
- package/dist/query/verification/masking.js +82 -0
- package/dist/query/verification/masking.js.map +1 -0
- package/dist/query/verification/script-credit.d.ts +14 -0
- package/dist/query/verification/script-credit.js +232 -0
- package/dist/query/verification/script-credit.js.map +1 -0
- package/dist/query/verification/shell.d.ts +5 -1
- package/dist/query/verification/shell.js +288 -9
- package/dist/query/verification/shell.js.map +1 -1
- package/dist/query/verification.js +187 -21
- package/dist/query/verification.js.map +1 -1
- package/dist/query/workflow.js +2 -2
- package/dist/query/workflow.js.map +1 -1
- package/dist/repo-files.js +74 -14
- package/dist/repo-files.js.map +1 -1
- package/dist/resolver.js +20 -2
- package/dist/resolver.js.map +1 -1
- package/dist/retrieval.js +5 -5
- package/dist/retrieval.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/dist/util.d.ts +1 -0
- package/dist/util.js +7 -0
- package/dist/util.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 +4 -2
- package/plugins/codexa/.codex-plugin/plugin.json +1 -1
|
@@ -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
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// MCP launcher for the Codexa Claude Code plugin. Claude Code starts plugin
|
|
3
|
+
// MCP servers with the session's project directory as cwd; this script
|
|
4
|
+
// resolves the repository from there, resolves the Codexa CLI (explicit
|
|
5
|
+
// override, walked-up package/checkout dist, global install), and execs
|
|
6
|
+
// `codexa serve` over stdio. Self-contained on purpose: when the plugin is
|
|
7
|
+
// installed from a marketplace only this directory is copied, so it cannot
|
|
8
|
+
// reference files outside the plugin root.
|
|
9
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
10
|
+
import { existsSync } from "node:fs";
|
|
11
|
+
import path from "node:path";
|
|
12
|
+
import { fileURLToPath } from "node:url";
|
|
13
|
+
|
|
14
|
+
const explicitRepo = process.argv[2];
|
|
15
|
+
const autoRefresh = process.env.CODEXA_PLUGIN_AUTO_REFRESH !== "0";
|
|
16
|
+
const repoRoot = resolveRepoRoot(explicitRepo);
|
|
17
|
+
if (!repoRoot) {
|
|
18
|
+
console.error(
|
|
19
|
+
"codexa plugin MCP could not find a git repository. Set CODEXA_REPO to the repository root or start Claude Code inside the repository."
|
|
20
|
+
);
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const launch = resolveCodexaLaunch(repoRoot, autoRefresh);
|
|
25
|
+
const child = spawn(launch.command, launch.args, {
|
|
26
|
+
cwd: repoRoot,
|
|
27
|
+
stdio: ["inherit", "inherit", "inherit"],
|
|
28
|
+
env: process.env
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
child.on("exit", (code, signal) => {
|
|
32
|
+
if (signal) {
|
|
33
|
+
process.kill(process.pid, signal);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
process.exit(code ?? 1);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
child.on("error", (error) => {
|
|
40
|
+
console.error(`codexa plugin MCP failed to start ${launch.command}: ${error.message}`);
|
|
41
|
+
process.exit(1);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
function resolveCodexaLaunch(repoRoot, autoRefresh) {
|
|
45
|
+
// Claude Code has no client-side tool allowlist, so the profile is applied
|
|
46
|
+
// server-side. Default core matches `codexa init`'s default and the
|
|
47
|
+
// plugin's token-discipline pitch; CODEXA_PLUGIN_TOOLS=full widens it.
|
|
48
|
+
const toolProfile = process.env.CODEXA_PLUGIN_TOOLS === "full" ? "full" : "core";
|
|
49
|
+
const serveArgs = ["serve", repoRoot, autoRefresh ? "--auto-refresh" : "--no-auto-refresh", "--tools", toolProfile];
|
|
50
|
+
const overrideCli = process.env.CODEXA_CLI;
|
|
51
|
+
if (overrideCli && existsSync(overrideCli)) {
|
|
52
|
+
return { command: process.execPath, args: [overrideCli, ...serveArgs] };
|
|
53
|
+
}
|
|
54
|
+
// npm package layout: integrations/claude-code/scripts -> package root.
|
|
55
|
+
const scriptDir = path.dirname(fileURLToPath(import.meta.url));
|
|
56
|
+
const bundledCli = path.resolve(scriptDir, "../../../dist/cli.js");
|
|
57
|
+
if (existsSync(bundledCli)) {
|
|
58
|
+
return { command: process.execPath, args: [bundledCli, ...serveArgs] };
|
|
59
|
+
}
|
|
60
|
+
if (commandExists("codexa")) {
|
|
61
|
+
return { command: "codexa", args: serveArgs };
|
|
62
|
+
}
|
|
63
|
+
if (process.env.CODEXA_PLUGIN_ALLOW_NPX_FALLBACK === "1") {
|
|
64
|
+
return { command: "npx", args: ["-y", "@mirnoorata/codexa", ...serveArgs] };
|
|
65
|
+
}
|
|
66
|
+
console.error(
|
|
67
|
+
"codexa plugin MCP could not find the Codexa CLI. Install it with `npm install -g @mirnoorata/codexa`, set CODEXA_CLI to a dist/cli.js path, or set CODEXA_PLUGIN_ALLOW_NPX_FALLBACK=1 to allow npm registry fallback."
|
|
68
|
+
);
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function commandExists(command) {
|
|
73
|
+
const probe = process.platform === "win32" ? "where" : "which";
|
|
74
|
+
try {
|
|
75
|
+
return spawnSync(probe, [command], { stdio: "ignore" }).status === 0;
|
|
76
|
+
} catch {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function resolveRepoRoot(explicit) {
|
|
82
|
+
// Claude Code starts plugin MCP servers with the session's project
|
|
83
|
+
// directory as cwd; that is the authoritative signal. $PWD is deliberately
|
|
84
|
+
// NOT consulted — it is inherited shell state and can point at an
|
|
85
|
+
// unrelated repository.
|
|
86
|
+
const candidates = [explicit, process.env.CODEXA_REPO, process.env.CLAUDE_PROJECT_DIR, process.cwd()].filter(
|
|
87
|
+
(value) => typeof value === "string" && value.trim().length > 0
|
|
88
|
+
);
|
|
89
|
+
for (const candidate of candidates) {
|
|
90
|
+
const root = gitRoot(path.resolve(candidate));
|
|
91
|
+
if (root) {
|
|
92
|
+
return root;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function gitRoot(candidate) {
|
|
99
|
+
if (!existsSync(candidate)) {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
const result = spawnSync("git", ["-C", candidate, "rev-parse", "--show-toplevel"], {
|
|
103
|
+
encoding: "utf8",
|
|
104
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
105
|
+
});
|
|
106
|
+
if (result.status !== 0) {
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
const root = result.stdout.trim();
|
|
110
|
+
return root ? path.resolve(root) : null;
|
|
111
|
+
}
|