refacil-sdd-ai 5.1.1 → 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 +22 -14
- package/agents/debugger.md +12 -5
- package/agents/implementer.md +11 -4
- package/bin/cli.js +122 -28
- package/lib/bus/presenter.js +173 -97
- package/lib/global-paths.js +11 -0
- package/lib/hooks.js +94 -1
- package/lib/ide-detection.js +1 -0
- package/lib/installer.js +53 -1
- package/lib/opencode-plugin/index.js +59 -24
- package/lib/session-repo-sync.js +88 -0
- package/lib/testing-policy-sync.js +113 -0
- package/lib/toml-converter.js +170 -0
- package/package.json +5 -3
- package/skills/apply/SKILL.md +17 -3
- package/skills/bug/SKILL.md +5 -2
- package/skills/join/SKILL.md +1 -1
- package/skills/prereqs/METHODOLOGY-CONTRACT.md +6 -2
- package/skills/setup/SKILL.md +49 -9
- package/skills/setup/troubleshooting.md +1 -1
- package/skills/update/SKILL.md +3 -2
- package/templates/compact-guidance.md +3 -3
- package/templates/testing-policy.md +7 -0
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 **
|
|
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 **
|
|
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
|
|
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
|
|
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`.
|
|
@@ -190,7 +190,7 @@ refacil-sdd-ai sdd config --json
|
|
|
190
190
|
|
|
191
191
|
## Available IDE Skills
|
|
192
192
|
|
|
193
|
-
All invoked as `/refacil:<name>` in Claude Code, Cursor, or
|
|
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
|
-
**
|
|
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
|
|
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/hooks.json` | `session.created` handler in the global OpenCode plugin |
|
|
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
|
|---|---|
|
|
@@ -334,6 +334,8 @@ Installed during `init` / `update` for each selected IDE. The same four behavior
|
|
|
334
334
|
|
|
335
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
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.
|
|
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
|
|
|
339
341
|
### Review gate on push
|
|
@@ -493,6 +495,11 @@ Skills, sub-agents, and hooks are installed into the user's global IDE directori
|
|
|
493
495
|
~/.config/opencode/agents/refacil-*.md # OpenCode sub-agents (permission block + mode:subagent)
|
|
494
496
|
~/.config/opencode/plugins/refacil-hooks.js # Plugin: session.created + tui.prompt.append + tool.execute.before
|
|
495
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
|
+
|
|
496
503
|
# refacil-sdd-ai state
|
|
497
504
|
~/.refacil-sdd-ai/
|
|
498
505
|
selected-ides.json # IDE selection saved on init, reused by update
|
|
@@ -532,6 +539,7 @@ refacil-sdd/ # SDD artifacts store
|
|
|
532
539
|
- [Claude Code](https://claude.ai/code) — Anthropic CLI
|
|
533
540
|
- [Cursor](https://cursor.sh) — AI IDE
|
|
534
541
|
- [OpenCode](https://opencode.ai) — open-source AI development agent
|
|
542
|
+
- [Codex](https://github.com/openai/codex) — OpenAI CLI agent
|
|
535
543
|
|
|
536
544
|
## License
|
|
537
545
|
|
package/agents/debugger.md
CHANGED
|
@@ -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
|
|
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. **
|
|
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:
|
|
168
|
+
### Step 4: Verify tests (`METHODOLOGY-CONTRACT.md` §3.1)
|
|
168
169
|
|
|
169
|
-
|
|
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.
|
package/agents/implementer.md
CHANGED
|
@@ -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
|
-
- `
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
10
|
-
|
|
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
|
|
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
|
-
|
|
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]
|
|
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
|
|
687
|
-
if (
|
|
688
|
-
console.
|
|
689
|
-
} else
|
|
690
|
-
|
|
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:
|
|
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
|
|
810
|
-
if (
|
|
811
|
-
console.
|
|
812
|
-
} else
|
|
813
|
-
|
|
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:
|
|
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
|
|
892
|
-
Use --all to install for all
|
|
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
|
|
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
|
|
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
|