@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.
Files changed (91) hide show
  1. package/README.md +93 -29
  2. package/dist/cache-lock.js +72 -12
  3. package/dist/cache-lock.js.map +1 -1
  4. package/dist/cli/hooks.js +11 -6
  5. package/dist/cli/hooks.js.map +1 -1
  6. package/dist/cli.js +13 -4
  7. package/dist/cli.js.map +1 -1
  8. package/dist/graph.js +4 -2
  9. package/dist/graph.js.map +1 -1
  10. package/dist/implicit-baseline.d.ts +8 -0
  11. package/dist/implicit-baseline.js +94 -0
  12. package/dist/implicit-baseline.js.map +1 -0
  13. package/dist/init.d.ts +3 -0
  14. package/dist/init.js +124 -15
  15. package/dist/init.js.map +1 -1
  16. package/dist/mcp/compaction.d.ts +1 -0
  17. package/dist/mcp/compaction.js +24 -0
  18. package/dist/mcp/compaction.js.map +1 -1
  19. package/dist/mcp/envelope.d.ts +4 -1
  20. package/dist/mcp/envelope.js +45 -5
  21. package/dist/mcp/envelope.js.map +1 -1
  22. package/dist/mcp/prompts.d.ts +1 -1
  23. package/dist/mcp/prompts.js +5 -2
  24. package/dist/mcp/prompts.js.map +1 -1
  25. package/dist/mcp/tool-registry.d.ts +1 -0
  26. package/dist/mcp/tool-registry.js +5 -0
  27. package/dist/mcp/tool-registry.js.map +1 -1
  28. package/dist/mcp/tools.d.ts +1 -0
  29. package/dist/mcp/tools.js +6 -0
  30. package/dist/mcp/tools.js.map +1 -1
  31. package/dist/mcp-tool-catalog.d.ts +1 -1
  32. package/dist/mcp-tool-catalog.js +1 -1
  33. package/dist/mcp-tool-catalog.js.map +1 -1
  34. package/dist/mcp.js +31 -5
  35. package/dist/mcp.js.map +1 -1
  36. package/dist/query/post-edit/decision.d.ts +1 -0
  37. package/dist/query/post-edit/decision.js +13 -4
  38. package/dist/query/post-edit/decision.js.map +1 -1
  39. package/dist/query/post-edit.js +10 -2
  40. package/dist/query/post-edit.js.map +1 -1
  41. package/dist/query/verification/masking.d.ts +8 -0
  42. package/dist/query/verification/masking.js +82 -0
  43. package/dist/query/verification/masking.js.map +1 -0
  44. package/dist/query/verification/script-credit.d.ts +14 -0
  45. package/dist/query/verification/script-credit.js +232 -0
  46. package/dist/query/verification/script-credit.js.map +1 -0
  47. package/dist/query/verification/shell.d.ts +5 -1
  48. package/dist/query/verification/shell.js +288 -9
  49. package/dist/query/verification/shell.js.map +1 -1
  50. package/dist/query/verification.js +187 -21
  51. package/dist/query/verification.js.map +1 -1
  52. package/dist/query/workflow.js +2 -2
  53. package/dist/query/workflow.js.map +1 -1
  54. package/dist/repo-files.js +74 -14
  55. package/dist/repo-files.js.map +1 -1
  56. package/dist/resolver.js +20 -2
  57. package/dist/resolver.js.map +1 -1
  58. package/dist/retrieval.js +5 -5
  59. package/dist/retrieval.js.map +1 -1
  60. package/dist/task-snapshots.js +29 -0
  61. package/dist/task-snapshots.js.map +1 -1
  62. package/dist/types.d.ts +2 -0
  63. package/dist/types.js.map +1 -1
  64. package/dist/util.d.ts +1 -0
  65. package/dist/util.js +7 -0
  66. package/dist/util.js.map +1 -1
  67. package/integrations/.claude-plugin/marketplace.json +23 -0
  68. package/integrations/claude-code/.claude-plugin/plugin.json +16 -0
  69. package/integrations/claude-code/.mcp.json +8 -0
  70. package/integrations/claude-code/README.md +177 -0
  71. package/integrations/claude-code/commands/codexa-brief.md +14 -0
  72. package/integrations/claude-code/commands/codexa-impact.md +14 -0
  73. package/integrations/claude-code/commands/codexa-plan.md +20 -0
  74. package/integrations/claude-code/commands/codexa-review.md +23 -0
  75. package/integrations/claude-code/commands/codexa-status.md +10 -0
  76. package/integrations/claude-code/hooks/hooks.json +39 -0
  77. package/integrations/claude-code/scripts/cmd/brief.sh +18 -0
  78. package/integrations/claude-code/scripts/cmd/impact.sh +35 -0
  79. package/integrations/claude-code/scripts/cmd/lib.sh +136 -0
  80. package/integrations/claude-code/scripts/cmd/plan.sh +52 -0
  81. package/integrations/claude-code/scripts/cmd/review.sh +66 -0
  82. package/integrations/claude-code/scripts/cmd/status.sh +52 -0
  83. package/integrations/claude-code/scripts/codexa-mcp.js +111 -0
  84. package/integrations/claude-code/scripts/lib/codexa-repo.sh +773 -0
  85. package/integrations/claude-code/scripts/pre-edit.sh +116 -0
  86. package/integrations/claude-code/scripts/session-start.sh +201 -0
  87. package/integrations/claude-code/scripts/stop.sh +443 -0
  88. package/integrations/claude-code/tests/cmd-smoke.sh +310 -0
  89. package/integrations/claude-code/tests/hook-smoke.sh +1412 -0
  90. package/package.json +4 -2
  91. 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
+ }