refacil-sdd-ai 5.1.0 → 5.2.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 CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  **SDD-AI** (Specification-Driven Development with AI) packaged as a CLI.
4
4
 
5
- Installs **skills** and **sub-agents** for **Claude Code**, **Cursor**, and **OpenCode** that guide the developer through a structured AI-assisted development workflow, using **`refacil-sdd/`** as the specification store, plus a **local bus** so agents across different repos can communicate with each other.
5
+ Installs **skills** and **sub-agents** for **Claude Code**, **Cursor**, **OpenCode**, and **Codex** that guide the developer through a structured AI-assisted development workflow, using **`refacil-sdd/`** as the specification store, plus a **local bus** so agents across different repos can communicate with each other.
6
6
 
7
7
  ---
8
8
 
@@ -11,9 +11,9 @@ Installs **skills** and **sub-agents** for **Claude Code**, **Cursor**, and **Op
11
11
  ## Requirements
12
12
 
13
13
  - **Node.js >= 20.0.0**
14
- - One or more supported IDEs: **Claude Code >= 2.1.89**, **Cursor**, or **OpenCode**
14
+ - One or more supported IDEs: **Claude Code >= 2.1.89**, **Cursor**, **OpenCode**, or **Codex**
15
15
 
16
- `refacil-sdd-ai init` checks the Claude Code version and warns if it is below 2.1.89. With an older version the rest of the methodology works, but `compact-bash` will have no effect (Claude Code only — OpenCode and Cursor have their own hook delivery mechanisms).
16
+ `refacil-sdd-ai init` checks the Claude Code version and warns if it is below 2.1.89. With an older version the rest of the methodology works, but `compact-bash` will have no effect (Claude Code only — Cursor, OpenCode, and Codex have their own hook delivery mechanisms).
17
17
 
18
18
  ---
19
19
 
@@ -33,8 +33,8 @@ refacil-sdd-ai init
33
33
 
34
34
  `init` installs skills, sub-agents, and hooks into your IDE's **global user directories** (`~/.claude/`, `~/.cursor/`, `~/.config/opencode/`). Skills are available in all your repos from this point — no need to re-run `init` when you open a new repo.
35
35
 
36
- - Interactive IDE selector (Claude Code / Cursor / OpenCode) — pre-selects installed IDEs.
37
- Use `--all` to install for all three without prompting.
36
+ - Interactive IDE selector (Claude Code / Cursor / OpenCode / Codex) — pre-selects installed IDEs.
37
+ Use `--all` to install for all four without prompting.
38
38
  - Your IDE selection is saved to `~/.refacil-sdd-ai/selected-ides.json` and reused on every `update`.
39
39
  - Also prompts for global branch config (`baseBranch`, `protectedBranches`, `artifactLanguage`)
40
40
  stored in `~/.refacil-sdd-ai/config.yaml`. Skip with `--yes` or `--defaults`.
@@ -89,10 +89,10 @@ npm uninstall -g refacil-sdd-ai
89
89
 
90
90
  | Command | Description |
91
91
  |---|---|
92
- | `refacil-sdd-ai init` | Install skills and hooks in the current repo |
93
- | `refacil-sdd-ai update` | Re-copy skills and hooks to the latest version |
92
+ | `refacil-sdd-ai init` | Install skills and hooks into global IDE user directories |
93
+ | `refacil-sdd-ai update` | Re-copy skills and hooks to the latest version (global) |
94
94
  | `refacil-sdd-ai migration-pending [--json]` | Same detection as hooks/`notify-update`; exit 1 if migration is pending; on exit 0 also deletes obsolete `.refacil-pending-update` (same as at the start of `check-update`) |
95
- | `refacil-sdd-ai clean` | Remove SDD-AI skills and hooks from the repo |
95
+ | `refacil-sdd-ai clean` | Remove SDD-AI skills and hooks from global IDE user directories |
96
96
  | `refacil-sdd-ai help` | Show help |
97
97
 
98
98
  ### Internal hooks (invoked automatically — not for manual use)
@@ -184,13 +184,13 @@ refacil-sdd-ai sdd config --json
184
184
 
185
185
  > The `join/leave/say/ask/reply/attend/inbox` subcommands also exist as **IDE skills** (`/refacil:join`, etc.). In most cases use the skills; the CLI commands are for scripting or debugging.
186
186
  >
187
- > **Cross-repo coordination** (ask requests, room agreements, `/refacil:propose`, closing to the requester): after `init`, the file **`BUS-CROSS-REPO.md`** is available in `.claude/skills/refacil-prereqs/` and `.cursor/skills/refacil-prereqs/`.
187
+ > **Cross-repo coordination** (ask requests, room agreements, `/refacil:propose`, closing to the requester): after `init`, the file **`BUS-CROSS-REPO.md`** is available in `~/.claude/skills/refacil-prereqs/` and `~/.cursor/skills/refacil-prereqs/`.
188
188
 
189
189
  ---
190
190
 
191
191
  ## Available IDE Skills
192
192
 
193
- All invoked as `/refacil:<name>` in Claude Code, Cursor, or OpenCode.
193
+ All invoked as `/refacil:<name>` in Claude Code, Cursor, OpenCode, or Codex.
194
194
 
195
195
  ### SDD cycle
196
196
 
@@ -227,7 +227,7 @@ Some skills delegate their heavy work to **sub-agents** that run in isolated con
227
227
 
228
228
  **Model**: `refacil-proposer` runs with `model: opusplan` (uses Opus during plan mode for highest-stakes planning, then switches to Sonnet for execution). Other sub-agents use `model: sonnet` by default for Claude Code, others use inherit model.
229
229
 
230
- **Triple-platform**: `.claude/agents/refacil-*.md` uses `tools:` (granular allowlist). `.cursor/agents/refacil-*.md` is auto-generated: `readonly: true` for agents without `Edit`/`Write`, `readonly: false` for those that have them; always `model: inherit`. `.opencode/agents/refacil-*.md` is auto-generated via `transformFrontmatterForOpenCode()`: converts `tools:` to a `permission:` block (`edit: allow/deny`, `bash: allow/deny`, `webfetch: deny`), adds `mode: subagent`, adds `hidden: true` for internal agents, and removes `model:`. The installer transforms the frontmatter automatically for all three IDEs.
230
+ **Multi-platform**: `.claude/agents/refacil-*.md` uses `tools:` (granular allowlist). `.cursor/agents/refacil-*.md` is auto-generated: `readonly: true` for agents without `Edit`/`Write`, `readonly: false` for those that have them; always `model: inherit`. `.opencode/agents/refacil-*.md` is auto-generated via `transformFrontmatterForOpenCode()`: converts `tools:` to a `permission:` block (`edit: allow/deny`, `bash: allow/deny`, `webfetch: deny`), adds `mode: subagent`, adds `hidden: true` for internal agents, and removes `model:`. `.codex/agents/refacil-*.toml` is auto-generated via `convertAgentToToml()`: extracts `name` and `description` from the YAML frontmatter and places the Markdown body in `developer_instructions = """..."""`. The installer transforms the frontmatter automatically for all four IDEs.
231
231
 
232
232
  **Two-pass `refacil:bug` flow**: the wrapper first invokes the sub-agent in `investigation` mode (writes nothing) → the user confirms the hypothesis and approves the fix → the wrapper validates the working branch → invokes the sub-agent in `fix` mode to implement.
233
233
 
@@ -316,14 +316,14 @@ From there, the full cycle is:
316
316
 
317
317
  ## Automatic Hooks
318
318
 
319
- Installed during `init` / `update` for each selected IDE. The same four behaviors are active in Claude Code, Cursor, and OpenCode — each through its own delivery mechanism.
319
+ Installed during `init` / `update` for each selected IDE. The same four behaviors are active in Claude Code, Cursor, OpenCode, and Codex — each through its own delivery mechanism.
320
320
 
321
- | Behavior | Claude Code | Cursor | OpenCode |
322
- |---|---|---|---|
323
- | **check-update** | `SessionStart` hook in `.claude/settings.json` | `SessionStart` hook in `.cursor/settings.json` | `session.created` handler in `.opencode/plugins/refacil-hooks.js` |
324
- | **notify-update** | `UserPromptSubmit` hook | `beforeSubmitPrompt` hook | `tui.prompt.append` handler |
325
- | **compact-bash** | `PreToolUse` (Bash) hook | `PreToolUse` (Bash) hook | `tool.execute.before` handler for bash tool |
326
- | **check-review** | `PreToolUse` (Bash) hook | `PreToolUse` (Bash) hook | `tool.execute.before` handler for bash tool |
321
+ | Behavior | Claude Code | Cursor | OpenCode | Codex |
322
+ |---|---|---|---|---|
323
+ | **check-update** | `SessionStart` hook in `~/.claude/settings.json` | `SessionStart` hook in `~/.cursor/hooks.json` | `session.created` handler in the global OpenCode plugin | `sessionStart` hook in `~/.codex/config.toml` |
324
+ | **notify-update** | `UserPromptSubmit` hook | `beforeSubmitPrompt` hook | `tui.prompt.append` handler | `userPromptSubmit` hook in `~/.codex/config.toml` |
325
+ | **compact-bash** | `PreToolUse` (Bash) hook | `PreToolUse` (Bash) hook | `tool.execute.before` handler for bash tool | `preToolUse` hook (Bash matcher) in `~/.codex/config.toml` |
326
+ | **check-review** | `PreToolUse` (Bash) hook | `PreToolUse` (Bash) hook | `tool.execute.before` handler for bash tool | `preToolUse` hook (Bash matcher) in `~/.codex/config.toml` |
327
327
 
328
328
  | Behavior | What it does |
329
329
  |---|---|
@@ -332,7 +332,9 @@ Installed during `init` / `update` for each selected IDE. The same four behavior
332
332
  | `compact-bash` | Silently rewrites bare Bash commands. No extra turns, the IDE does not see the change. Requires Claude Code >= 2.1.89 for the `updatedInput` path. |
333
333
  | `check-review` | Intercepts `git push` and blocks if `.review-passed` is missing in any active change. |
334
334
 
335
- > **OpenCode plugin**: a single file (`.opencode/plugins/refacil-hooks.js`) implements all four behaviors. It loads `lib/compact/rules.js` from the package to reuse the same rewrite rules — no duplicated logic. If the rules file is not resolvable, compact-bash is disabled gracefully with a warning to stderr; the plugin never crashes the session.
335
+ > **OpenCode plugin**: a single file installed in the global OpenCode plugins directory implements all four behaviors. It loads `lib/compact/rules.js` from the package to reuse the same rewrite rules — no duplicated logic. If the rules file is not resolvable, compact-bash is disabled gracefully with a warning to stderr; the plugin never crashes the session.
336
+
337
+ > **Codex hooks**: injected into `~/.codex/config.toml` under `[hooks]` with `[features] codex_hooks = true`. Each SDD-AI hook entry carries a boolean marker (`_sdd`, `_sdd_compact`, `_sdd_review`, `_sdd_notify`) for clean removal on `clean`. User-defined hooks outside these entries are preserved.
336
338
 
337
339
  > **Why two hooks for updates?** `SessionStart` does the silent sync when opening the session without user interaction. `notify-update` on `UserPromptSubmit` / `beforeSubmitPrompt` injects the instruction just before the agent processes the next user message, ensuring it is not ignored.
338
340
 
@@ -470,31 +472,46 @@ Local bus (WebSocket over `127.0.0.1`) so agents across different repos can comm
470
472
 
471
473
  ---
472
474
 
473
- ## What Gets Installed in Your Repo
475
+ ## What Gets Installed
474
476
 
475
- Only the IDEs selected during `init` (or detected during `update`) receive files. The three IDE targets are independent — selecting only `.opencode` does not create `.claude/` or `.cursor/` directories.
477
+ ### Global user directories (once, shared across all repos)
478
+
479
+ Skills, sub-agents, and hooks are installed into the user's global IDE directories — not into any project repo. Only the IDEs selected during `init` receive files.
476
480
 
477
481
  ```
478
482
  # Claude Code (if selected)
479
- .claude/skills/refacil-*/ # Skills (includes refacil-prereqs: METHODOLOGY-CONTRACT.md, BUS-CROSS-REPO.md, …)
480
- .claude/agents/refacil-*.md # Read-only sub-agents: auditor, investigator, validator
481
- # Write sub-agents: tester, implementer, debugger, proposer
482
- .claude/settings.json # Hooks: check-update + notify-update + check-review + compact-bash
483
- .claude/.sdd-version # Installed methodology version (used by check-update)
483
+ ~/.claude/skills/refacil-*/ # Skills (includes refacil-prereqs: METHODOLOGY-CONTRACT.md, BUS-CROSS-REPO.md, …)
484
+ ~/.claude/agents/refacil-*.md # Read-only sub-agents: auditor, investigator, validator
485
+ # Write sub-agents: tester, implementer, debugger, proposer
486
+ ~/.claude/settings.json # SDD hooks merged in: check-update, notify-update, check-review, compact-bash
484
487
 
485
488
  # Cursor (if selected)
486
- .cursor/skills/refacil-*/ # Cursor skills (equivalent)
487
- .cursor/agents/refacil-*.md # Cursor sub-agents (readonly:true/false + model:inherit, auto-generated)
488
- .cursor/settings.json # Hooks: check-update + notify-update + check-review + compact-bash
489
- .cursor/.sdd-version # Installed methodology version
490
-
491
- # OpenCode (if selected)
492
- .opencode/skills/refacil-*/ # OpenCode skills (byte-for-byte copy same spec as Claude Code)
493
- .opencode/agents/refacil-*.md # OpenCode sub-agents (permission block + mode:subagent, auto-generated)
494
- .opencode/plugins/refacil-hooks.js # Embedded plugin: session.created + tui.prompt.append + tool.execute.before
495
- .opencode/opencode.json # Created/merged with $schema (user keys preserved)
496
- .opencode/.sdd-version # Installed methodology version
489
+ ~/.cursor/skills/refacil-*/ # Cursor skills (auto-transformed frontmatter: readonly + model:inherit)
490
+ ~/.cursor/agents/refacil-*.md # Cursor sub-agents (readonly:true/false + model:inherit, auto-generated)
491
+ ~/.cursor/hooks.json # SDD hooks merged in (same four behaviors)
492
+
493
+ # OpenCode (if selected) — macOS/Linux: ~/.config/opencode/ Windows: %APPDATA%\opencode
494
+ ~/.config/opencode/skills/refacil-*/ # OpenCode skills
495
+ ~/.config/opencode/agents/refacil-*.md # OpenCode sub-agents (permission block + mode:subagent)
496
+ ~/.config/opencode/plugins/refacil-hooks.js # Plugin: session.created + tui.prompt.append + tool.execute.before
497
+
498
+ # Codex (if selected)
499
+ ~/.codex/skills/refacil-*/ # Codex skills (same content as Claude Code)
500
+ ~/.codex/agents/refacil-*.toml # Codex sub-agents (TOML: name + description + developer_instructions)
501
+ ~/.codex/config.toml # SDD hooks merged in under [hooks] with [features] codex_hooks = true
502
+
503
+ # refacil-sdd-ai state
504
+ ~/.refacil-sdd-ai/
505
+ selected-ides.json # IDE selection saved on init, reused by update
506
+ config.yaml # Global config: baseBranch, protectedBranches, artifactLanguage
507
+ sdd-version # Installed methodology version (used by check-update)
508
+ ```
497
509
 
510
+ ### Per repo (generated by `/refacil:setup`)
511
+
512
+ The only per-repo step is running `/refacil:setup` once per project. It generates the project index — no IDE skills or hooks are written to the repo.
513
+
514
+ ```
498
515
  # Shared (IDE-agnostic)
499
516
  CLAUDE.md # Minimal index → points to AGENTS.md
500
517
  .cursorrules # Cursor format equivalent of CLAUDE.md
@@ -512,6 +529,8 @@ refacil-sdd/ # SDD artifacts store
512
529
  specs/ # Persistent specifications synced from archived changes
513
530
  ```
514
531
 
532
+ > **Migration from project-level installs**: the `check-update` hook (SessionStart) automatically detects and removes any leftover project-level `refacil-*` skills, agents, hooks, and empty IDE directories from older versions.
533
+
515
534
  ---
516
535
 
517
536
  ## Technologies
@@ -520,6 +539,7 @@ refacil-sdd/ # SDD artifacts store
520
539
  - [Claude Code](https://claude.ai/code) — Anthropic CLI
521
540
  - [Cursor](https://cursor.sh) — AI IDE
522
541
  - [OpenCode](https://opencode.ai) — open-source AI development agent
542
+ - [Codex](https://github.com/openai/codex) — OpenAI CLI agent
523
543
 
524
544
  ## License
525
545
 
@@ -11,7 +11,7 @@ You are a debugging agent operating in two modes. In investigation mode you rece
11
11
 
12
12
  Reject weak hypotheses. If the evidence does not support a root cause, say so. Do not propose a fix until the cause is clear.
13
13
 
14
- **Prerequisites**: `agents` profile from `refacil-prereqs/SKILL.md` + rules from `METHODOLOGY-CONTRACT.md`.
14
+ **Prerequisites**: `agents` profile from `refacil-prereqs/SKILL.md` + rules from **`METHODOLOGY-CONTRACT.md` (§3, §3.1 — verification defaults to scoped in fix mode)**.
15
15
 
16
16
  ## Guardrail: direct invocation detection
17
17
 
@@ -29,6 +29,7 @@ If you prefer to continue here, provide:
29
29
  - mode: investigation (only analyze and propose hypotheses) or fix (implement with already-confirmed hypothesis)
30
30
  - description: <full bug description>
31
31
  - hypothesis: <confirmed root cause> (only for mode=fix)
32
+ - testScope: scoped \| full (only for mode=fix; default scoped)
32
33
  ```
33
34
 
34
35
  **Do not proceed with reads or implementation until the scope is clear.**
@@ -124,7 +125,7 @@ Proposed fix for hypothesis #1:
124
125
 
125
126
  ## Fix mode
126
127
 
127
- The main agent passes you: `mode: fix` + `description` + `hypothesis` (root cause confirmed by the user).
128
+ The main agent passes you: `mode: fix` + `description` + `hypothesis` (root cause confirmed by the user) + optional **`testScope`** (`scoped` \| `full`, default **`scoped`**).
128
129
 
129
130
  ### Step 1: Implement the fix
130
131
 
@@ -139,7 +140,7 @@ Detect the project's testing stack and framework: read `METHODOLOGY-CONTRACT.md
139
140
  Generate tests that:
140
141
  1. **Reproduce the bug**: a test that fails WITHOUT the fix (verifies the test is valid).
141
142
  2. **Verify the fix**: the same test passes WITH the fix.
142
- 3. **Verify no regression**: normal flow tests still pass.
143
+ 3. **Guardrails**: extend with normal/control-path assertions when they fit the bug surface (Step 4 **scoped** run targets those files/packages — **not** the entire repo suite).
143
144
 
144
145
  Each test must cover:
145
146
  - `should [correct behavior] when [condition that previously failed]`
@@ -164,9 +165,14 @@ Create `refacil-sdd/changes/<fix-name>/summary.md`:
164
165
 
165
166
  This file is mandatory for traceability and allows the `check-review` hook to detect the active change. The `.review-passed` will be created by `/refacil:review` upon approval.
166
167
 
167
- ### Step 4: Run all tests
168
+ ### Step 4: Verify tests (`METHODOLOGY-CONTRACT.md` §3.1)
168
169
 
169
- Resolve and run the test command according to `METHODOLOGY-CONTRACT.md §3`. All tests must pass.
170
+ 1. Read **`testScope`** from wrapper (default **`scoped`** if omitted).
171
+ 2. **`testScope: full`**: Resolve baseline from **`METHODOLOGY-CONTRACT.md §3`**, run **once unparsed** — **all tests** emitted by that command must pass.
172
+ 3. **`testScope: scoped`** (default): Collect **`verificationTargets`** — every production/test file **you edited or added** during fix mode (**including** regression tests created this session).
173
+ - Build **`scopedCommand`** by narrowing baseline §3 to cover only those roots (directories, `-p`/`-pl`, `--`/path suffixes — follow stack docs + **`AGENTS.md` / `.agents/testing.md`** when present — see §3.1 **Scoped command patterns**).
174
+ - Run **`scopedCommand`**; everything it selects must pass. **Do not** upgrade to repo-wide invocation while `scoped` unless §3.1 says narrowing is unreliable — then run baseline **once**, prepend report line **WARN: scoped narrowing unavailable → full-suite fallback (heavy)**.
175
+ 4. **`testsResult.command`** in JSON must quote the **literal** executed shell string (`scopedCommand` or baseline).
170
176
 
171
177
  ### Report + JSON block (fix)
172
178
 
@@ -208,4 +214,5 @@ Resolve and run the test command according to `METHODOLOGY-CONTRACT.md §3`. All
208
214
  - In mode=investigation: follow diagnose loop discipline (reproduce, minimize, hypothesize, validate evidence) before proposing a fix.
209
215
  - In mode=fix: the fix must be MINIMAL. Never over-refactor.
210
216
  - Regression tests are MANDATORY in mode=fix.
217
+ - **Scoped verification**: default **`testScope: scoped`** from wrapper — narrowed command in Step 4, not wholesale “run entire repo suite” unless `full`.
211
218
  - Use **concise** output mode by default.
@@ -11,7 +11,7 @@ You are an implementation agent. You receive a structured briefing (objective, s
11
11
 
12
12
  If the briefing is ambiguous or a task cannot be completed safely, report it — do not silently skip or guess.
13
13
 
14
- **Prerequisites**: rules from `refacil-prereqs/METHODOLOGY-CONTRACT.md`.
14
+ **Prerequisites**: rules from `refacil-prereqs/METHODOLOGY-CONTRACT.md` (**§3**, **§3.1** — default verification is **scoped**).
15
15
 
16
16
  ## Guardrail: direct invocation detection
17
17
 
@@ -72,7 +72,9 @@ Read from the prompt the `BRIEFING:` sections passed by the wrapper:
72
72
  - `scope.modify` — existing files to modify
73
73
  - `scope.doNotTouch` — files out of scope
74
74
  - `tasks` — numbered task list
75
- - `testCommand` — verification command
75
+ - `testScope` — `scoped` \| `full` (default **`scoped`** if absent — treat missing as scoped)
76
+ - `testCommand` — **exact shell command** to execute for verification (narrowed when `scoped`)
77
+ - `verificationWarning` — optional hint from wrapper (often explains fallback-to-baseline)
76
78
  - `architectureContext` — already-extracted architecture context
77
79
  - `specsNote` — if there are specs, where they are and whether there are possible contradictions
78
80
 
@@ -82,7 +84,7 @@ If the briefing is **not present** (direct invocation without briefing):
82
84
  3. Read `refacil-sdd/changes/<changeName>/tasks.md` (tasks)
83
85
  4. Read `AGENTS.md` (architecture)
84
86
  5. Read the change specs
85
- 6. Read `METHODOLOGY-CONTRACT.md §3` (test command)
87
+ 6. Read `METHODOLOGY-CONTRACT.md` §3 and §3.1 (narrow **before** invoking the runner unless you explicitly widen)
86
88
 
87
89
  ### Step 2: Read existing interfaces (scope.modify only)
88
90
 
@@ -103,7 +105,12 @@ If a task requires touching a file outside the scope: note it in `issues` as pot
103
105
 
104
106
  ### Step 4: Verify
105
107
 
106
- Run the `testCommand` from the briefing (or from `METHODOLOGY-CONTRACT.md §3` if not in the briefing).
108
+ Follow **`METHODOLOGY-CONTRACT.md §3.1`**:
109
+
110
+ 1. Run **exactly** the **`testCommand`** supplied in the briefing.
111
+ 2. If **`testCommand` is missing**, resolve baseline from **`METHODOLOGY-CONTRACT.md §3`** and **narrow** it yourself using `scope.create` ∪ `scope.modify` plus the §3.1 **Scoped command patterns**. If narrowing is unsafe, run the baseline **once**, add **`issues`** entry severity **MEDIUM** explaining full-suite fallback, and cite `verificationWarning` pattern if analogous.
112
+ 3. **Do not** broaden the briefing’s `testCommand` into a fuller suite when `testScope` is **`scoped`** (or omitted). Repo-wide regression belongs in CI or an explicit **`/refacil:test … full`**.
113
+ 4. If `verificationWarning` is present in the briefing, mirror a short note in **`issues`** (severity **LOW**) so the wrapper/user sees CPU/RAM risk was intentional.
107
114
 
108
115
  ### Step 5: Report + JSON block
109
116
 
package/bin/cli.js CHANGED
@@ -1,14 +1,13 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
 
3
3
  'use strict';
4
4
 
5
5
  const fs = require('fs');
6
6
  const path = require('path');
7
7
  const os = require('os');
8
- const {
9
- syncCompactGuidance,
10
- removeCompactGuidance,
11
- } = require('../lib/compact-guidance');
8
+ const { removeCompactGuidance } = require('../lib/compact-guidance');
9
+ const { removeTestingPolicyBlock } = require('../lib/testing-policy-sync');
10
+ const { syncRepoSessionMarkers } = require('../lib/session-repo-sync');
12
11
  const compactBash = require('../lib/compact/bash');
13
12
  const {
14
13
  installSkills,
@@ -17,6 +16,7 @@ const {
17
16
  removeSkills,
18
17
  removeGlobalSkills,
19
18
  removeOpenCodeArtifacts,
19
+ removeCodexArtifacts,
20
20
  removeOpenspecLegacyAssets,
21
21
  removeProjectLevelArtifacts,
22
22
  createClaudeMd,
@@ -30,7 +30,7 @@ const {
30
30
  checkClaudeCodeVersion,
31
31
  } = require('../lib/installer');
32
32
  const { installHooks, uninstallHooks, cleanLegacySettingsHooks, installOpenCodePlugin, uninstallOpenCodePlugin, removeProjectLevelHooks } = require('../lib/hooks');
33
- const { globalClaudeDir, globalCursorDir, globalOpenCodeDir, readSelectedIDEs, writeSelectedIDEs } = require('../lib/global-paths');
33
+ const { globalClaudeDir, globalCursorDir, globalOpenCodeDir, globalCodexDir, readSelectedIDEs, writeSelectedIDEs } = require('../lib/global-paths');
34
34
  const { detectInstalledIDEs } = require('../lib/ide-detection');
35
35
  const { handleCompact } = require('../lib/commands/compact');
36
36
  const { handleBus } = require('../lib/commands/bus');
@@ -131,7 +131,8 @@ function repoIsInitialized() {
131
131
  if (
132
132
  fs.existsSync(path.join(globalClaudeDir(home), 'skills')) ||
133
133
  fs.existsSync(path.join(globalCursorDir(home), 'skills')) ||
134
- fs.existsSync(path.join(globalOpenCodeDir(home), 'skills'))
134
+ fs.existsSync(path.join(globalOpenCodeDir(home), 'skills')) ||
135
+ fs.existsSync(path.join(globalCodexDir(home), 'skills'))
135
136
  ) {
136
137
  return true;
137
138
  }
@@ -204,9 +205,9 @@ function readlineMultiSelect(options) {
204
205
  */
205
206
  async function selectIDEs() {
206
207
  const allFlag = process.argv.includes('--all');
207
- const allIDEs = ['.claude', '.cursor', '.opencode'];
208
+ const allIDEs = ['.claude', '.cursor', '.opencode', '.codex'];
208
209
 
209
- // --all or non-TTY: install all three
210
+ // --all or non-TTY: install all four
210
211
  if (allFlag || !process.stdout.isTTY) {
211
212
  return allIDEs;
212
213
  }
@@ -226,11 +227,15 @@ async function selectIDEs() {
226
227
  const openCodeSelected = hasSaved
227
228
  ? savedSelection.includes('.opencode')
228
229
  : detectedIds.includes('opencode') || fs.existsSync(path.join(projectRoot, '.opencode'));
230
+ const codexSelected = hasSaved
231
+ ? savedSelection.includes('.codex')
232
+ : detectedIds.includes('codex');
229
233
 
230
234
  const options = [
231
235
  { label: 'Claude Code (~/.claude/)', value: '.claude', selected: claudeSelected },
232
236
  { label: 'Cursor (~/.cursor/)', value: '.cursor', selected: cursorSelected },
233
237
  { label: 'OpenCode (global config dir)', value: '.opencode', selected: openCodeSelected },
238
+ { label: 'Codex (~/.codex/)', value: '.codex', selected: codexSelected },
234
239
  ];
235
240
 
236
241
  // Try @clack/prompts first, fall back to inline readline
@@ -337,7 +342,8 @@ function checkUpdate() {
337
342
  const globalActive =
338
343
  fs.existsSync(path.join(globalClaudeDir(home), 'skills')) ||
339
344
  fs.existsSync(path.join(globalCursorDir(home), 'skills')) ||
340
- fs.existsSync(path.join(globalOpenCodeDir(home), 'skills'));
345
+ fs.existsSync(path.join(globalOpenCodeDir(home), 'skills')) ||
346
+ fs.existsSync(path.join(globalCodexDir(home), 'skills'));
341
347
 
342
348
  if (globalActive) {
343
349
  const cleaned = removeProjectLevelArtifacts(projectRoot);
@@ -354,9 +360,23 @@ function checkUpdate() {
354
360
  let localVersion = getPackageVersion(packageRoot);
355
361
 
356
362
  try {
357
- syncCompactGuidance(projectRoot, packageRoot);
363
+ const syncOut = syncRepoSessionMarkers(projectRoot, packageRoot);
364
+ if (!syncOut.ok) {
365
+ process.stderr.write(`[refacil-sdd-ai] session repo sync: ${syncOut.reason}\n`);
366
+ } else {
367
+ if (syncOut.compact.status === 'error') {
368
+ process.stderr.write(`[refacil-sdd-ai] Could not sync compact-guidance: ${syncOut.compact.message}\n`);
369
+ }
370
+ if (syncOut.testing.status === 'error') {
371
+ process.stderr.write(`[refacil-sdd-ai] Could not sync testing-policy block: ${syncOut.testing.message}\n`);
372
+ } else if (
373
+ ['created-file', 'appended', 'replaced', 'written-empty'].includes(syncOut.testing.status)
374
+ ) {
375
+ process.stdout.write(`[refacil-sdd-ai] testing-policy: ${syncOut.testing.status} (.agents/testing.md)\n`);
376
+ }
377
+ }
358
378
  } catch (err) {
359
- process.stderr.write(`[refacil-sdd-ai] Could not sync compact-guidance: ${err.message}\n`);
379
+ process.stderr.write(`[refacil-sdd-ai] session repo sync: ${err.message}\n`);
360
380
  }
361
381
 
362
382
  cleanLegacySettingsHooks(projectRoot);
@@ -616,6 +636,7 @@ async function init() {
616
636
  const installClaude = selectedIDEs.includes('.claude');
617
637
  const installCursor = selectedIDEs.includes('.cursor');
618
638
  const installOpenCode = selectedIDEs.includes('.opencode');
639
+ const installCodex = selectedIDEs.includes('.codex');
619
640
  const homeDir = os.homedir();
620
641
 
621
642
  // Migration step: remove project-level artifacts (now global) — silent, non-destructive
@@ -630,6 +651,7 @@ async function init() {
630
651
  const ideList = selectedIDEs.map((d) => {
631
652
  if (d === '.claude') return `~/.claude/skills/`;
632
653
  if (d === '.cursor') return `~/.cursor/skills/`;
654
+ if (d === '.codex') return `~/.codex/skills/`;
633
655
  return `(opencode-global)/skills/`;
634
656
  }).join(', ');
635
657
  console.log(` ${count} skills installed in ${ideList}`);
@@ -639,6 +661,7 @@ async function init() {
639
661
  const agentList = selectedIDEs.map((d) => {
640
662
  if (d === '.claude') return `~/.claude/agents/`;
641
663
  if (d === '.cursor') return `~/.cursor/agents/`;
664
+ if (d === '.codex') return `~/.codex/agents/`;
642
665
  return `(opencode-global)/agents/`;
643
666
  }).join(', ');
644
667
  console.log(` ${agentsCount} sub-agents installed in ${agentList}`);
@@ -666,6 +689,12 @@ async function init() {
666
689
  }
667
690
  }
668
691
 
692
+ if (installCodex) {
693
+ if (installHooks('.codex', homeDir)) {
694
+ console.log(' Hook check-update added to ~/.codex/config.toml');
695
+ }
696
+ }
697
+
669
698
  try {
670
699
  const IDE_TO_IGNORE = { '.claude': '.claudeignore', '.cursor': '.cursorignore', '.opencode': '.opencodeignore' };
671
700
  const ignoreResult = syncIgnoreFiles(projectRoot, selectedIDEs);
@@ -683,14 +712,29 @@ async function init() {
683
712
  }
684
713
 
685
714
  try {
686
- const result = syncCompactGuidance(projectRoot, packageRoot);
687
- if (result.status === 'appended') {
688
- console.log(' compact-guidance block added to AGENTS.md');
689
- } else if (result.status === 'replaced') {
690
- console.log(' compact-guidance block updated in AGENTS.md');
715
+ const syncOut = syncRepoSessionMarkers(projectRoot, packageRoot);
716
+ if (!syncOut.ok) {
717
+ console.error(` Warning: session repo sync: ${syncOut.reason}`);
718
+ } else {
719
+ if (syncOut.compact.status === 'error') {
720
+ console.error(` Warning: could not sync compact-guidance: ${syncOut.compact.message}`);
721
+ } else if (syncOut.compact.status === 'appended') {
722
+ console.log(' compact-guidance block added to AGENTS.md');
723
+ } else if (syncOut.compact.status === 'replaced') {
724
+ console.log(' compact-guidance block updated in AGENTS.md');
725
+ }
726
+ if (syncOut.testing.status === 'error') {
727
+ console.error(` Warning: could not sync testing-policy: ${syncOut.testing.message}`);
728
+ } else if (syncOut.testing.status === 'created-file') {
729
+ console.log(' testing-policy: created .agents/testing.md');
730
+ } else if (syncOut.testing.status === 'appended' || syncOut.testing.status === 'written-empty') {
731
+ console.log(' testing-policy block added to .agents/testing.md');
732
+ } else if (syncOut.testing.status === 'replaced') {
733
+ console.log(' testing-policy block updated in .agents/testing.md');
734
+ }
691
735
  }
692
736
  } catch (err) {
693
- console.error(` Warning: could not sync compact-guidance: ${err.message}`);
737
+ console.error(` Warning: session repo sync: ${err.message}`);
694
738
  }
695
739
 
696
740
  console.log('\n Next steps:\n');
@@ -721,10 +765,13 @@ function update() {
721
765
  const hasOpenCodeDir = detectedIds.includes('opencode') ||
722
766
  fs.existsSync(path.join(globalOpenCodeDir(homeDir), 'skills')) ||
723
767
  fs.existsSync(path.join(projectRoot, '.opencode'));
768
+ const hasCodexFallback = detectedIds.includes('codex') ||
769
+ fs.existsSync(path.join(globalCodexDir(homeDir), 'skills'));
724
770
  selectedIDEs = [
725
771
  hasClaudeDir && '.claude',
726
772
  hasCursorDir && '.cursor',
727
773
  hasOpenCodeDir && '.opencode',
774
+ hasCodexFallback && '.codex',
728
775
  ].filter(Boolean);
729
776
  // Persist for future runs so detection only happens once
730
777
  if (selectedIDEs.length > 0) writeSelectedIDEs(selectedIDEs);
@@ -733,6 +780,7 @@ function update() {
733
780
  const hasClaudeDir = selectedIDEs.includes('.claude');
734
781
  const hasCursorDir = selectedIDEs.includes('.cursor');
735
782
  const hasOpenCodeDir = selectedIDEs.includes('.opencode');
783
+ const hasCodexDir = selectedIDEs.includes('.codex');
736
784
  const detectedIDEs = selectedIDEs;
737
785
 
738
786
  // Migration step: remove project-level artifacts — silent, non-destructive
@@ -745,6 +793,7 @@ function update() {
745
793
  const installedDirs = detectedIDEs.map((d) => {
746
794
  if (d === '.claude') return '~/.claude/skills/';
747
795
  if (d === '.cursor') return '~/.cursor/skills/';
796
+ if (d === '.codex') return '~/.codex/skills/';
748
797
  return '(opencode-global)/skills/';
749
798
  });
750
799
  console.log(` ${count} skills updated in ${installedDirs.join(', ') || '(none detected)'}`);
@@ -755,6 +804,7 @@ function update() {
755
804
  hasClaudeDir && '~/.claude/agents/',
756
805
  hasCursorDir && '~/.cursor/agents/',
757
806
  hasOpenCodeDir && '(opencode-global)/agents/',
807
+ hasCodexDir && '~/.codex/agents/',
758
808
  ].filter(Boolean);
759
809
  console.log(` ${agentsCount} sub-agents updated in ${agentDirs.join(', ')}`);
760
810
  }
@@ -789,6 +839,12 @@ function update() {
789
839
  }
790
840
  }
791
841
 
842
+ if (hasCodexDir) {
843
+ if (installHooks('.codex', homeDir)) {
844
+ console.log(' Hook check-update updated in ~/.codex/config.toml');
845
+ }
846
+ }
847
+
792
848
  try {
793
849
  const IDE_TO_IGNORE = { '.claude': '.claudeignore', '.cursor': '.cursorignore', '.opencode': '.opencodeignore' };
794
850
  const ignoreResult = syncIgnoreFiles(projectRoot, detectedIDEs);
@@ -806,14 +862,29 @@ function update() {
806
862
  }
807
863
 
808
864
  try {
809
- const result = syncCompactGuidance(projectRoot, packageRoot);
810
- if (result.status === 'appended') {
811
- console.log(' compact-guidance block added to AGENTS.md');
812
- } else if (result.status === 'replaced') {
813
- console.log(' compact-guidance block updated in AGENTS.md');
865
+ const syncOut = syncRepoSessionMarkers(projectRoot, packageRoot);
866
+ if (!syncOut.ok) {
867
+ console.error(` Warning: session repo sync: ${syncOut.reason}`);
868
+ } else {
869
+ if (syncOut.compact.status === 'error') {
870
+ console.error(` Warning: could not sync compact-guidance: ${syncOut.compact.message}`);
871
+ } else if (syncOut.compact.status === 'appended') {
872
+ console.log(' compact-guidance block added to AGENTS.md');
873
+ } else if (syncOut.compact.status === 'replaced') {
874
+ console.log(' compact-guidance block updated in AGENTS.md');
875
+ }
876
+ if (syncOut.testing.status === 'error') {
877
+ console.error(` Warning: could not sync testing-policy: ${syncOut.testing.message}`);
878
+ } else if (syncOut.testing.status === 'created-file') {
879
+ console.log(' testing-policy: created .agents/testing.md');
880
+ } else if (syncOut.testing.status === 'appended' || syncOut.testing.status === 'written-empty') {
881
+ console.log(' testing-policy block added to .agents/testing.md');
882
+ } else if (syncOut.testing.status === 'replaced') {
883
+ console.log(' testing-policy block updated in .agents/testing.md');
884
+ }
814
885
  }
815
886
  } catch (err) {
816
- console.error(` Warning: could not sync compact-guidance: ${err.message}`);
887
+ console.error(` Warning: session repo sync: ${err.message}`);
817
888
  }
818
889
 
819
890
  console.log('\n RESTART your IDE session to apply the changes.\n');
@@ -853,6 +924,17 @@ function clean() {
853
924
  console.error(` Warning: could not remove OpenCode plugin: ${err.message}`);
854
925
  }
855
926
 
927
+ // Remove Codex global artifacts (skills + agents) and hooks
928
+ try {
929
+ removeCodexArtifacts(homeDir);
930
+ console.log(' Codex skills and agents removed from ~/.codex/');
931
+ } catch (err) {
932
+ console.error(` Warning: could not remove Codex artifacts: ${err.message}`);
933
+ }
934
+ if (uninstallHooks('.codex', homeDir)) {
935
+ console.log(' SDD-AI hooks removed from ~/.codex/config.toml');
936
+ }
937
+
856
938
  // Clean project-level OpenCode artifacts if .opencode/ directory is present
857
939
  if (fs.existsSync(path.join(projectRoot, '.opencode'))) {
858
940
  try {
@@ -872,6 +954,15 @@ function clean() {
872
954
  console.error(` Warning: could not clean compact-guidance: ${err.message}`);
873
955
  }
874
956
 
957
+ try {
958
+ const tp = removeTestingPolicyBlock(projectRoot);
959
+ if (tp.status === 'removed') {
960
+ console.log(' testing-policy block removed from .agents/testing.md');
961
+ }
962
+ } catch (err) {
963
+ console.error(` Warning: could not clean testing-policy: ${err.message}`);
964
+ }
965
+
875
966
  console.log(' AGENTS.md, CLAUDE.md and .cursorrules were not removed.');
876
967
  console.log('\n Note: if you have openspec/ in the repo, migrate first with: refacil-sdd-ai sdd status');
877
968
  console.log(' (the openspec/ → refacil-sdd/ migration is automatic on any sdd subcommand)');
@@ -883,19 +974,20 @@ function help() {
883
974
  const claudePath = globalClaudeDir(home);
884
975
  const cursorPath = globalCursorDir(home);
885
976
  const opencodePath = globalOpenCodeDir(home);
977
+ const codexPath = globalCodexDir(home);
886
978
 
887
979
  console.log(`
888
980
  refacil-sdd-ai — SDD-AI Methodology
889
981
 
890
982
  Commands:
891
- init Install skills globally for Claude Code, Cursor and/or OpenCode (interactive IDE selector).
892
- Use --all to install for all three IDEs without prompting.
983
+ init Install skills globally for Claude Code, Cursor, OpenCode and/or Codex (interactive IDE selector).
984
+ Use --all to install for all four IDEs without prompting.
893
985
  Use --yes or --defaults to skip interactive branch config prompts.
894
986
  Creates CLAUDE.md, .cursorrules and .opencode/opencode.json as appropriate.
895
987
  Migrates any project-level artifacts to global dirs automatically.
896
988
  update Re-copy skills for all detected IDEs to global user dirs
897
989
  migration-pending Same validation as hooks/notify-update: list migrations (exit 1 if any; --json)
898
- check-update Sync skills and compact-guidance at session start (SessionStart hook)
990
+ check-update Sync skills, compact-guidance (AGENTS.md), and testing-policy block (.agents/testing.md) at session start
899
991
  notify-update Notify methodology migration only if applicable (UserPromptSubmit hook)
900
992
  check-review Verify that review has been completed (used by PreToolUse hook)
901
993
  compact-bash Rewrite bare Bash commands to reduce tokens (used by PreToolUse hook)
@@ -940,7 +1032,7 @@ function help() {
940
1032
  Full flow:
941
1033
  1. npm install -g refacil-sdd-ai
942
1034
  2. refacil-sdd-ai init
943
- 3. RESTART your IDE session (Claude Code, Cursor, or OpenCode)
1035
+ 3. RESTART your IDE session (Claude Code, Cursor, OpenCode, or Codex)
944
1036
  4. Run: /refacil:setup (generates AGENTS.md for your project)
945
1037
 
946
1038
  Global installation paths (this machine):
@@ -950,6 +1042,8 @@ function help() {
950
1042
  ${cursorPath}/hooks.json (hooks)
951
1043
  - OpenCode: ${opencodePath}/skills/, ${opencodePath}/agents/
952
1044
  ${opencodePath}/plugins/refacil-hooks.js
1045
+ - Codex: ${codexPath}/skills/, ${codexPath}/agents/
1046
+ ${codexPath}/config.toml (hooks)
953
1047
 
954
1048
  Requirements:
955
1049
  - Node.js >= 20.0.0