claudekit-codex-sync 0.2.4 → 0.2.5
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 +2 -2
- package/docs/project-roadmap.md +12 -0
- package/docs/system-architecture.md +4 -6
- package/package.json +1 -1
- package/plans/260223-1737-hooks-to-rules-cleanup/phase-01-hooks-to-rules.md +202 -0
- package/plans/260223-1737-hooks-to-rules-cleanup/phase-02-remove-commands-prompts.md +82 -0
- package/plans/260223-1737-hooks-to-rules-cleanup/phase-03-bridge-and-syntax.md +59 -0
- package/plans/260223-1737-hooks-to-rules-cleanup/phase-04-version-docs-tests.md +135 -0
- package/plans/260223-1737-hooks-to-rules-cleanup/plan.md +49 -0
- package/src/claudekit_codex_sync/cli.py +5 -5
- package/src/claudekit_codex_sync/constants.py +5 -1
- package/src/claudekit_codex_sync/rules_generator.py +31 -0
- package/templates/bridge-skill.md +3 -3
- package/templates/rule-code-quality.md +10 -0
- package/templates/rule-file-naming.md +7 -0
- package/templates/rule-security-privacy.md +15 -0
- package/tests/test_rules_generator.py +28 -0
package/README.md
CHANGED
|
@@ -13,11 +13,11 @@ ClaudeKit uses `~/.claude/` conventions and agent markdown frontmatter. Codex us
|
|
|
13
13
|
| **Scope select** | Sync to project `./.codex/` (default) or global `~/.codex/` (`-g`) |
|
|
14
14
|
| **Fresh clean** | Optional `-f` cleanup of target dirs before sync |
|
|
15
15
|
| **Source resolve** | Uses live `~/.claude/` or `--zip` export |
|
|
16
|
-
| **Asset sync** | Copies agents `.md` → `agents/` (for TOML conversion),
|
|
16
|
+
| **Asset sync** | Copies agents `.md` → `agents/` (for TOML conversion), output-styles/rules/scripts → `codex_home/` directly |
|
|
17
17
|
| **Skill sync** | Copies skills into `codex_home/skills/` |
|
|
18
18
|
| **Path normalize** | Rewrites `.claude` references to `.codex` |
|
|
19
|
+
| **Hook rules** | Generates rules/ from hook behavior (security-privacy, file-naming, code-quality) |
|
|
19
20
|
| **Config enforce** | Ensures `config.toml`, feature flags, and agent registration |
|
|
20
|
-
| **Prompt export** | Generates Codex-compatible prompt files |
|
|
21
21
|
| **Dep bootstrap** | Symlink-first venv strategy, fallback install |
|
|
22
22
|
| **Runtime verify** | Health-checks synced environment |
|
|
23
23
|
|
package/docs/project-roadmap.md
CHANGED
|
@@ -26,6 +26,18 @@
|
|
|
26
26
|
- [x] Documentation refresh for new CLI contract
|
|
27
27
|
- [x] Removed legacy standalone `scripts/` and stale `reports/`
|
|
28
28
|
|
|
29
|
+
## v0.2.5 - Hooks→Rules + Remove Prompts
|
|
30
|
+
|
|
31
|
+
**Status:** Complete
|
|
32
|
+
|
|
33
|
+
- [x] Remove hooks sync (Codex has no hooks API)
|
|
34
|
+
- [x] Generate hook-equivalent rules (security-privacy, file-naming, code-quality)
|
|
35
|
+
- [x] Remove commands sync (deprecated concept)
|
|
36
|
+
- [x] Remove prompt export step (deprecated by OpenAI)
|
|
37
|
+
- [x] Update bridge skill for Codex-native routing
|
|
38
|
+
- [x] Add syntax adaptations for Claude→Codex patterns
|
|
39
|
+
- [x] Add test coverage for rules_generator
|
|
40
|
+
|
|
29
41
|
## v0.3.0 - Coverage + Reliability
|
|
30
42
|
|
|
31
43
|
**Status:** Planned
|
|
@@ -8,12 +8,10 @@
|
|
|
8
8
|
│ ~/.claude/ or zip │───▶│ ./.codex or ~/.codex │
|
|
9
9
|
│ agents/*.md │ │ agents/*.toml (convert) │
|
|
10
10
|
│ skills/* │ │ skills/* │
|
|
11
|
-
│ commands/ │ │ commands/ │
|
|
12
11
|
│ output-styles/ │ │ output-styles/ │
|
|
13
12
|
│ rules/ │ │ rules/ │
|
|
14
13
|
│ scripts/ │ │ scripts/ │
|
|
15
|
-
└─────────────────────┘ │
|
|
16
|
-
│ config.toml │
|
|
14
|
+
└─────────────────────┘ │ config.toml │
|
|
17
15
|
└──────────────────────────┘
|
|
18
16
|
```
|
|
19
17
|
|
|
@@ -28,7 +26,7 @@ Workspace baseline: `./AGENTS.md` is ensured in the current working directory.
|
|
|
28
26
|
|
|
29
27
|
2. **Asset/skill sync**
|
|
30
28
|
- Copy agents `.md` directly to `codex_home/agents/` (for TOML conversion)
|
|
31
|
-
- Copy managed assets (
|
|
29
|
+
- Copy managed assets (output-styles, rules, scripts) to `codex_home/` directly
|
|
32
30
|
- Copy skills to `codex_home/skills/`
|
|
33
31
|
- Apply registry-aware overwrite behavior (`--force`)
|
|
34
32
|
|
|
@@ -43,8 +41,8 @@ Workspace baseline: `./AGENTS.md` is ensured in the current working directory.
|
|
|
43
41
|
- Register agents from `agents/*.toml`
|
|
44
42
|
- Ensure workspace-level `AGENTS.md` baseline file exists
|
|
45
43
|
|
|
46
|
-
5. **
|
|
47
|
-
- Generate
|
|
44
|
+
5. **Hook rules generation**
|
|
45
|
+
- Generate rules/ from hook behavior templates (security-privacy, file-naming, code-quality)
|
|
48
46
|
|
|
49
47
|
6. **Dependency bootstrap**
|
|
50
48
|
- Try symlink reuse of `~/.claude/skills/.venv`
|
package/package.json
CHANGED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
# Phase 1: Remove Hooks, Generate Rules
|
|
2
|
+
|
|
3
|
+
Status: pending
|
|
4
|
+
Priority: critical
|
|
5
|
+
Effort: 0.5d
|
|
6
|
+
|
|
7
|
+
## Goal
|
|
8
|
+
|
|
9
|
+
Stop syncing hooks (dead code). Create rules that replicate hook behavior at startup.
|
|
10
|
+
|
|
11
|
+
## Context
|
|
12
|
+
|
|
13
|
+
- 15 hooks use Claude Code's hook API (SessionStart, PreToolUse, etc.)
|
|
14
|
+
- Codex CLI has NO hooks support — hooks dir is dead code
|
|
15
|
+
- Rules (`~/.codex/rules/*.md`) load at startup = always in context = equivalent to hooks
|
|
16
|
+
|
|
17
|
+
## Steps
|
|
18
|
+
|
|
19
|
+
### 1.1 Remove `"hooks"` from ASSET_DIRS
|
|
20
|
+
|
|
21
|
+
**File:** [constants.py](file:///home/vinhawk/claudekit-codex-sync/src/claudekit_codex_sync/constants.py)
|
|
22
|
+
|
|
23
|
+
```diff
|
|
24
|
+
-ASSET_DIRS = {"commands", "output-styles", "rules", "scripts", "hooks"}
|
|
25
|
+
+ASSET_DIRS = {"output-styles", "rules", "scripts"}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
> Also removes `"commands"` — Phase 2 handles this.
|
|
29
|
+
|
|
30
|
+
### 1.2 Create rule templates
|
|
31
|
+
|
|
32
|
+
**File:** `templates/rule-security-privacy.md` [NEW]
|
|
33
|
+
|
|
34
|
+
```markdown
|
|
35
|
+
# Security & Privacy
|
|
36
|
+
|
|
37
|
+
## Sensitive File Access
|
|
38
|
+
Ask user for explicit approval before reading:
|
|
39
|
+
- `.env`, `.env.*` (API keys, passwords, tokens)
|
|
40
|
+
- `*.key`, `*.pem`, `*.p12`, `credentials.*`, `secrets.*`
|
|
41
|
+
- Files in `~/.ssh/`, `~/.gnupg/`, `~/.aws/`
|
|
42
|
+
|
|
43
|
+
**Flow:** State which file and why → wait for "yes" → read via cat → never output full secrets.
|
|
44
|
+
|
|
45
|
+
## Directory Access Control
|
|
46
|
+
Respect `.ckignore` patterns if present in project root:
|
|
47
|
+
- Read `.ckignore` at session start
|
|
48
|
+
- Do NOT access matching directories
|
|
49
|
+
- Common: `node_modules/`, `dist/`, `build/`, `.git/objects/`
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**File:** `templates/rule-file-naming.md` [NEW]
|
|
53
|
+
|
|
54
|
+
```markdown
|
|
55
|
+
# File Naming
|
|
56
|
+
|
|
57
|
+
Apply before every file creation (skip for markdown/text):
|
|
58
|
+
- **JS/TS/Python/shell**: kebab-case with descriptive names
|
|
59
|
+
- **C#/Java/Kotlin/Swift**: PascalCase
|
|
60
|
+
- **Go/Rust**: snake_case
|
|
61
|
+
- Goal: self-documenting names for LLM tools (Grep, Glob, Search)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**File:** `templates/rule-code-quality.md` [NEW]
|
|
65
|
+
|
|
66
|
+
```markdown
|
|
67
|
+
# Code Quality Reminders
|
|
68
|
+
|
|
69
|
+
## Code Simplification
|
|
70
|
+
After significant file edits (5+), consider running code-simplifier agent.
|
|
71
|
+
|
|
72
|
+
## Post-Planning
|
|
73
|
+
After creating an implementation plan, always activate the cook skill before implementation.
|
|
74
|
+
|
|
75
|
+
## Modularization
|
|
76
|
+
If a code file exceeds 200 lines, consider modularizing it.
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### 1.3 Create rules_generator module
|
|
80
|
+
|
|
81
|
+
**File:** `src/claudekit_codex_sync/rules_generator.py` [NEW]
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
"""Generate hook-equivalent rules from templates."""
|
|
85
|
+
|
|
86
|
+
from __future__ import annotations
|
|
87
|
+
|
|
88
|
+
from pathlib import Path
|
|
89
|
+
|
|
90
|
+
from .utils import load_template, write_text_if_changed
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
RULE_TEMPLATES = {
|
|
94
|
+
"security-privacy.md": "rule-security-privacy.md",
|
|
95
|
+
"file-naming.md": "rule-file-naming.md",
|
|
96
|
+
"code-quality-reminders.md": "rule-code-quality.md",
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def generate_hook_rules(*, codex_home: Path, dry_run: bool) -> int:
|
|
101
|
+
"""Generate rules that replace ClaudeKit hook behavior."""
|
|
102
|
+
rules_dir = codex_home / "rules"
|
|
103
|
+
if not dry_run:
|
|
104
|
+
rules_dir.mkdir(parents=True, exist_ok=True)
|
|
105
|
+
|
|
106
|
+
generated = 0
|
|
107
|
+
for rule_name, template_name in RULE_TEMPLATES.items():
|
|
108
|
+
content = load_template(template_name)
|
|
109
|
+
target = rules_dir / rule_name
|
|
110
|
+
if write_text_if_changed(target, content, dry_run=dry_run):
|
|
111
|
+
generated += 1
|
|
112
|
+
print(f"upsert: rules/{rule_name}")
|
|
113
|
+
|
|
114
|
+
return generated
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### 1.4 Fix `set-active-plan.cjs` hooks/lib dependency
|
|
118
|
+
|
|
119
|
+
> [!WARNING]
|
|
120
|
+
> **Discovered via Codex CLI live testing (`cxp --full-auto`)**:
|
|
121
|
+
> `set-active-plan.cjs` line 16: `require('../hooks/lib/ck-config-utils.cjs')`.
|
|
122
|
+
> Currently works because hooks ARE synced. **Will break after removing hooks.**
|
|
123
|
+
> Only uses 2 functions: `readSessionState` + `writeSessionState` (~20 lines total).
|
|
124
|
+
|
|
125
|
+
**File:** `scripts/set-active-plan.cjs` (source in `~/.claude/scripts/`)
|
|
126
|
+
|
|
127
|
+
Inline the 3 tiny functions to remove hooks/lib dependency:
|
|
128
|
+
|
|
129
|
+
```diff
|
|
130
|
+
-const { writeSessionState, readSessionState } = require('../hooks/lib/ck-config-utils.cjs');
|
|
131
|
+
+// Inlined from hooks/lib/ck-config-utils.cjs (hooks removed in v0.2.5)
|
|
132
|
+
+function getSessionTempPath(sessionId) {
|
|
133
|
+
+ return path.join(os.tmpdir(), `ck-session-${sessionId}.json`);
|
|
134
|
+
+}
|
|
135
|
+
+function readSessionState(sessionId) {
|
|
136
|
+
+ if (!sessionId) return null;
|
|
137
|
+
+ const tempPath = getSessionTempPath(sessionId);
|
|
138
|
+
+ try {
|
|
139
|
+
+ if (!fs.existsSync(tempPath)) return null;
|
|
140
|
+
+ return JSON.parse(fs.readFileSync(tempPath, 'utf8'));
|
|
141
|
+
+ } catch (e) { return null; }
|
|
142
|
+
+}
|
|
143
|
+
+function writeSessionState(sessionId, state) {
|
|
144
|
+
+ if (!sessionId) return false;
|
|
145
|
+
+ const tempPath = getSessionTempPath(sessionId);
|
|
146
|
+
+ const tmpFile = tempPath + '.' + Math.random().toString(36).slice(2);
|
|
147
|
+
+ try {
|
|
148
|
+
+ fs.writeFileSync(tmpFile, JSON.stringify(state, null, 2));
|
|
149
|
+
+ fs.renameSync(tmpFile, tempPath);
|
|
150
|
+
+ return true;
|
|
151
|
+
+ } catch (e) {
|
|
152
|
+
+ try { fs.unlinkSync(tmpFile); } catch (_) {}
|
|
153
|
+
+ return false;
|
|
154
|
+
+ }
|
|
155
|
+
+}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Also need to add `const os = require('os');` if not already present.
|
|
159
|
+
|
|
160
|
+
**Option**: Instead of modifying source, apply this patch during sync in `path_normalizer.py`
|
|
161
|
+
by replacing the `require('../hooks/lib/...')` line with inlined functions.
|
|
162
|
+
|
|
163
|
+
## Codex CLI Live Test Results (cxp --full-auto)
|
|
164
|
+
|
|
165
|
+
| Script | Status | Notes |
|
|
166
|
+
|---|---|---|
|
|
167
|
+
| `ck-help.py` | ✅ OK | |
|
|
168
|
+
| `resolve_env.py` | ✅ OK | |
|
|
169
|
+
| `scan_commands.py` | ✅ OK | |
|
|
170
|
+
| `scan_skills.py` | ✅ OK | |
|
|
171
|
+
| `set-active-plan.cjs` (no args) | ⚠️ FAIL | Expected: usage error |
|
|
172
|
+
| `set-active-plan.cjs` (with plan) | ✅ OK | BUT depends on `../hooks/lib/` |
|
|
173
|
+
| `test-ck-help.py` | ✅ OK | |
|
|
174
|
+
| `test_ck_help.py` | ⚠️ FAIL | Pre-existing: intent detection mismatch |
|
|
175
|
+
| `test_ck_help_integration.py` | ⚠️ FAIL | Pre-existing: 17 pass / 2 fail |
|
|
176
|
+
| `validate-docs.cjs` | ✅ OK | |
|
|
177
|
+
| `win_compat.py` | ✅ OK | |
|
|
178
|
+
| `worktree.cjs` (no args) | ⚠️ FAIL | Expected: usage error |
|
|
179
|
+
| `worktree.test.cjs` | ⚠️ FAIL | Pre-existing test failures |
|
|
180
|
+
|
|
181
|
+
**Summary**: 8 OK / 5 FAIL. Of 5 FAILs: 2 expected (no-args usage), 2 pre-existing test bugs, 1 dependency issue (set-active-plan → hooks/lib).
|
|
182
|
+
|
|
183
|
+
## Todo
|
|
184
|
+
|
|
185
|
+
- [ ] Remove `"hooks"` and `"commands"` from `ASSET_DIRS`
|
|
186
|
+
- [ ] Create `templates/rule-security-privacy.md`
|
|
187
|
+
- [ ] Create `templates/rule-file-naming.md`
|
|
188
|
+
- [ ] Create `templates/rule-code-quality.md`
|
|
189
|
+
- [ ] Create `src/claudekit_codex_sync/rules_generator.py`
|
|
190
|
+
- [ ] Fix `set-active-plan.cjs` hooks/lib dependency (inline or patch during sync)
|
|
191
|
+
- [ ] Compile check: `python3 -m py_compile src/claudekit_codex_sync/rules_generator.py`
|
|
192
|
+
|
|
193
|
+
## Verification
|
|
194
|
+
|
|
195
|
+
```bash
|
|
196
|
+
python3 -m py_compile src/claudekit_codex_sync/rules_generator.py
|
|
197
|
+
python3 -m py_compile src/claudekit_codex_sync/constants.py
|
|
198
|
+
ls templates/rule-*.md # should show 3 files
|
|
199
|
+
# After sync, verify set-active-plan.cjs no longer depends on hooks:
|
|
200
|
+
grep -c "hooks/lib" ~/.codex/scripts/set-active-plan.cjs # should be 0
|
|
201
|
+
node ~/.codex/scripts/set-active-plan.cjs plans/test 2>&1 | head -5 # should not MODULE_NOT_FOUND
|
|
202
|
+
```
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Phase 2: Remove Commands & Prompts Sync
|
|
2
|
+
|
|
3
|
+
Status: pending
|
|
4
|
+
Priority: high
|
|
5
|
+
Effort: 0.5d
|
|
6
|
+
|
|
7
|
+
## Goal
|
|
8
|
+
|
|
9
|
+
Eliminate commands/prompts duplication from pipeline. Commands already removed from `ASSET_DIRS` in Phase 1, now remove prompt generation.
|
|
10
|
+
|
|
11
|
+
## Context
|
|
12
|
+
|
|
13
|
+
- Codex [deprecated custom prompts](https://developers.openai.com/codex/custom-prompts) in favor of skills
|
|
14
|
+
- `prompt_exporter.py` generates duplicates of every command → wasteful
|
|
15
|
+
- Bridge skill already maps legacy `/commands` → Codex skills
|
|
16
|
+
- Both `commands/` and `prompts/` produce identical content
|
|
17
|
+
|
|
18
|
+
## Steps
|
|
19
|
+
|
|
20
|
+
### 2.1 Remove prompt export from CLI
|
|
21
|
+
|
|
22
|
+
**File:** [cli.py](file:///home/vinhawk/claudekit-codex-sync/src/claudekit_codex_sync/cli.py)
|
|
23
|
+
|
|
24
|
+
Remove import and call:
|
|
25
|
+
|
|
26
|
+
```diff
|
|
27
|
+
-from .prompt_exporter import export_prompts
|
|
28
|
+
+from .rules_generator import generate_hook_rules
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Remove prompt export call (~line 188):
|
|
32
|
+
|
|
33
|
+
```diff
|
|
34
|
+
- prompt_stats = export_prompts(codex_home=codex_home, include_mcp=args.mcp, dry_run=args.dry_run)
|
|
35
|
+
- print(f"prompts: added={prompt_stats['added']} total={prompt_stats['total_generated']}")
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Add rules generation call after normalize step (~line 163):
|
|
39
|
+
|
|
40
|
+
```diff
|
|
41
|
+
+ rules_generated = generate_hook_rules(codex_home=codex_home, dry_run=args.dry_run)
|
|
42
|
+
+ print(f"hook_rules_generated={rules_generated}")
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Update summary dict:
|
|
46
|
+
|
|
47
|
+
```diff
|
|
48
|
+
- "prompts": prompt_stats,
|
|
49
|
+
+ "rules_generated": rules_generated,
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### 2.2 Remove prompts manifest from clean_target
|
|
53
|
+
|
|
54
|
+
**File:** [clean_target.py](file:///home/vinhawk/claudekit-codex-sync/src/claudekit_codex_sync/clean_target.py)
|
|
55
|
+
|
|
56
|
+
`".claudekit-generated-prompts.txt"` stays in cleanup list (for cleaning old installs) but `"prompts"` is already in the subdir cleanup loop — no change needed.
|
|
57
|
+
|
|
58
|
+
### 2.3 Remove PROMPT_MANIFEST/PROMPT_REPLACEMENTS usage
|
|
59
|
+
|
|
60
|
+
**File:** [constants.py](file:///home/vinhawk/claudekit-codex-sync/src/claudekit_codex_sync/constants.py)
|
|
61
|
+
|
|
62
|
+
Keep `PROMPT_REPLACEMENTS` and `PROMPT_MANIFEST` constants (no harm, `prompt_exporter.py` still importable). But mark as deprecated with comment:
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
# DEPRECATED: prompt_exporter.py no longer called from CLI pipeline (v0.2.5)
|
|
66
|
+
PROMPT_MANIFEST = ".claudekit-generated-prompts.txt"
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Todo
|
|
70
|
+
|
|
71
|
+
- [ ] Remove `export_prompts` import from `cli.py`
|
|
72
|
+
- [ ] Remove prompt export call from `cli.py`
|
|
73
|
+
- [ ] Add `generate_hook_rules` import and call to `cli.py`
|
|
74
|
+
- [ ] Update summary dict in `cli.py`
|
|
75
|
+
- [ ] Mark `PROMPT_MANIFEST` as deprecated in `constants.py`
|
|
76
|
+
|
|
77
|
+
## Verification
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
python3 -m py_compile src/claudekit_codex_sync/cli.py
|
|
81
|
+
PYTHONPATH=src python3 -m claudekit_codex_sync.cli -g -n # dry-run should not error
|
|
82
|
+
```
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Phase 3: Update Bridge Skill + Syntax Adaptations
|
|
2
|
+
|
|
3
|
+
Status: pending
|
|
4
|
+
Priority: medium
|
|
5
|
+
Effort: 0.25d
|
|
6
|
+
|
|
7
|
+
## Goal
|
|
8
|
+
|
|
9
|
+
Update bridge skill description. Add syntax adaptations for remaining Claude-specific patterns.
|
|
10
|
+
|
|
11
|
+
## Steps
|
|
12
|
+
|
|
13
|
+
### 3.1 Update bridge skill template
|
|
14
|
+
|
|
15
|
+
**File:** [templates/bridge-skill.md](file:///home/vinhawk/claudekit-codex-sync/templates/bridge-skill.md)
|
|
16
|
+
|
|
17
|
+
```diff
|
|
18
|
+
---
|
|
19
|
+
name: claudekit-command-bridge
|
|
20
|
+
-description: Bridge legacy ClaudeKit commands to Codex-native workflows. Use when users mention /ck-help, /coding-level, /ask, /docs/*, /journal, /watzup, or ask for Claude command equivalents.
|
|
21
|
+
+description: Bridge legacy ClaudeKit slash commands to Codex-native skills. Activates when users mention /ck-help, /coding-level, /ask, /docs/*, /journal, /watzup, or ask for Claude command equivalents.
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
-# ClaudeKit Command Bridge
|
|
25
|
+
+# ClaudeKit → Codex Bridge
|
|
26
|
+
|
|
27
|
+
-Translate ClaudeKit command intent into Codex skills/workflows.
|
|
28
|
+
+Translate ClaudeKit command intent into Codex-native skills.
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### 3.2 Add syntax adaptations
|
|
32
|
+
|
|
33
|
+
**File:** [constants.py](file:///home/vinhawk/claudekit-codex-sync/src/claudekit_codex_sync/constants.py)
|
|
34
|
+
|
|
35
|
+
Add entries to `CLAUDE_SYNTAX_ADAPTATIONS`:
|
|
36
|
+
|
|
37
|
+
```diff
|
|
38
|
+
CLAUDE_SYNTAX_ADAPTATIONS: List[Tuple[str, str]] = [
|
|
39
|
+
("Task(Explore)", "the explore agent"),
|
|
40
|
+
("Task(researcher)", "the researcher agent"),
|
|
41
|
+
("Task(", "delegate to "),
|
|
42
|
+
("$HOME/.claude/skills/*", "${CODEX_HOME:-$HOME/.codex}/skills/*"),
|
|
43
|
+
+ ("via Task tool", "via agent delegation"),
|
|
44
|
+
+ ("Claude Code", "Codex CLI"),
|
|
45
|
+
+ ("claude code", "Codex CLI"),
|
|
46
|
+
]
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Todo
|
|
50
|
+
|
|
51
|
+
- [ ] Update `templates/bridge-skill.md` description
|
|
52
|
+
- [ ] Add syntax adaptations to `constants.py`
|
|
53
|
+
|
|
54
|
+
## Verification
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
python3 -m py_compile src/claudekit_codex_sync/constants.py
|
|
58
|
+
grep "Codex-native skills" templates/bridge-skill.md # should match
|
|
59
|
+
```
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# Phase 4: Version Bump, Docs, Tests
|
|
2
|
+
|
|
3
|
+
Status: pending
|
|
4
|
+
Priority: medium
|
|
5
|
+
Effort: 0.5d
|
|
6
|
+
|
|
7
|
+
## Goal
|
|
8
|
+
|
|
9
|
+
Bump to v0.2.5. Update docs. Add tests for `rules_generator`. Run full verification.
|
|
10
|
+
|
|
11
|
+
## Steps
|
|
12
|
+
|
|
13
|
+
### 4.1 Version bump
|
|
14
|
+
|
|
15
|
+
**File:** [package.json](file:///home/vinhawk/claudekit-codex-sync/package.json)
|
|
16
|
+
|
|
17
|
+
```diff
|
|
18
|
+
- "version": "0.2.4",
|
|
19
|
+
+ "version": "0.2.5",
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### 4.2 Add test for rules_generator
|
|
23
|
+
|
|
24
|
+
**File:** `tests/test_rules_generator.py` [NEW]
|
|
25
|
+
|
|
26
|
+
```python
|
|
27
|
+
"""Tests for rules_generator module."""
|
|
28
|
+
|
|
29
|
+
from pathlib import Path
|
|
30
|
+
|
|
31
|
+
from claudekit_codex_sync.rules_generator import generate_hook_rules
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def test_generates_all_rule_files(tmp_path: Path):
|
|
35
|
+
"""Generates 3 rule files."""
|
|
36
|
+
count = generate_hook_rules(codex_home=tmp_path, dry_run=False)
|
|
37
|
+
assert count == 3
|
|
38
|
+
assert (tmp_path / "rules" / "security-privacy.md").exists()
|
|
39
|
+
assert (tmp_path / "rules" / "file-naming.md").exists()
|
|
40
|
+
assert (tmp_path / "rules" / "code-quality-reminders.md").exists()
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def test_idempotent(tmp_path: Path):
|
|
44
|
+
"""Second run returns 0 (no changes)."""
|
|
45
|
+
generate_hook_rules(codex_home=tmp_path, dry_run=False)
|
|
46
|
+
count = generate_hook_rules(codex_home=tmp_path, dry_run=False)
|
|
47
|
+
assert count == 0
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def test_dry_run(tmp_path: Path):
|
|
51
|
+
"""Dry run counts but doesn't create."""
|
|
52
|
+
count = generate_hook_rules(codex_home=tmp_path, dry_run=True)
|
|
53
|
+
assert count == 3
|
|
54
|
+
assert not (tmp_path / "rules").exists()
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### 4.3 Update README
|
|
58
|
+
|
|
59
|
+
**File:** [README.md](file:///home/vinhawk/claudekit-codex-sync/README.md)
|
|
60
|
+
|
|
61
|
+
Update "What It Does" table:
|
|
62
|
+
- Remove `Prompt export` row
|
|
63
|
+
- Remove hooks from `Asset sync` description
|
|
64
|
+
- Add `Hook rules` row: "Generates rules/ from hook behavior (security-privacy, file-naming, code-quality)"
|
|
65
|
+
|
|
66
|
+
### 4.4 Update system-architecture
|
|
67
|
+
|
|
68
|
+
**File:** [docs/system-architecture.md](file:///home/vinhawk/claudekit-codex-sync/docs/system-architecture.md)
|
|
69
|
+
|
|
70
|
+
- Remove Step 5 (Prompt export) from pipeline
|
|
71
|
+
- Add step: "Hook rules generation"
|
|
72
|
+
- Remove `hooks/` from architecture diagram
|
|
73
|
+
- Remove `prompts/* (generated)` from diagram
|
|
74
|
+
|
|
75
|
+
### 4.5 Update project roadmap
|
|
76
|
+
|
|
77
|
+
**File:** [docs/project-roadmap.md](file:///home/vinhawk/claudekit-codex-sync/docs/project-roadmap.md)
|
|
78
|
+
|
|
79
|
+
Add v0.2.5 section:
|
|
80
|
+
|
|
81
|
+
```markdown
|
|
82
|
+
## v0.2.5 - Hooks→Rules + Remove Prompts
|
|
83
|
+
|
|
84
|
+
**Status:** Complete
|
|
85
|
+
|
|
86
|
+
- [x] Remove hooks sync (Codex has no hooks API)
|
|
87
|
+
- [x] Generate hook-equivalent rules (security-privacy, file-naming, code-quality)
|
|
88
|
+
- [x] Remove commands sync (deprecated concept)
|
|
89
|
+
- [x] Remove prompt export step (deprecated by OpenAI)
|
|
90
|
+
- [x] Update bridge skill for Codex-native routing
|
|
91
|
+
- [x] Add syntax adaptations for Claude→Codex patterns
|
|
92
|
+
- [x] Add test coverage for rules_generator
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Todo
|
|
96
|
+
|
|
97
|
+
- [ ] Bump version in `package.json`
|
|
98
|
+
- [ ] Create `tests/test_rules_generator.py`
|
|
99
|
+
- [ ] Update `README.md`
|
|
100
|
+
- [ ] Update `docs/system-architecture.md`
|
|
101
|
+
- [ ] Update `docs/project-roadmap.md`
|
|
102
|
+
|
|
103
|
+
## Final Verification (all 10 steps)
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
# 1. Compile check
|
|
107
|
+
python3 -m py_compile src/claudekit_codex_sync/*.py
|
|
108
|
+
|
|
109
|
+
# 2. Run all tests
|
|
110
|
+
PYTHONPATH=src python3 -m pytest tests/ -v
|
|
111
|
+
|
|
112
|
+
# 3. Dry-run sync
|
|
113
|
+
PYTHONPATH=src python3 -m claudekit_codex_sync.cli -g -n
|
|
114
|
+
|
|
115
|
+
# 4. Actual sync (fresh)
|
|
116
|
+
PYTHONPATH=src python3 -m claudekit_codex_sync.cli -g -f --force
|
|
117
|
+
|
|
118
|
+
# 5. Verify hooks NOT synced
|
|
119
|
+
ls ~/.codex/hooks/ 2>/dev/null && echo "FAIL" || echo "OK: no hooks"
|
|
120
|
+
|
|
121
|
+
# 6. Verify rules generated
|
|
122
|
+
ls ~/.codex/rules/security-privacy.md ~/.codex/rules/file-naming.md ~/.codex/rules/code-quality-reminders.md
|
|
123
|
+
|
|
124
|
+
# 7. Verify no prompts generated
|
|
125
|
+
[ ! -f ~/.codex/prompts/.claudekit-generated-prompts.txt ] && echo "OK" || echo "FAIL"
|
|
126
|
+
|
|
127
|
+
# 8. Verify no commands dir
|
|
128
|
+
ls ~/.codex/commands/ 2>/dev/null && echo "WARN: commands exist" || echo "OK"
|
|
129
|
+
|
|
130
|
+
# 9. Verify existing rules intact
|
|
131
|
+
ls ~/.codex/rules/development-rules.md ~/.codex/rules/primary-workflow.md
|
|
132
|
+
|
|
133
|
+
# 10. Token budget check
|
|
134
|
+
du -b ~/.codex/rules/*.md | awk '{sum+=$1} END {print "Rules: " sum " bytes / 32768 limit"}'
|
|
135
|
+
```
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# v0.2.5 — Hooks→Rules, Remove Commands/Prompts, Harden Pipeline
|
|
2
|
+
|
|
3
|
+
Remove non-functional hooks, eliminate commands/prompts bloat, generate hook-equivalent rules.
|
|
4
|
+
|
|
5
|
+
## Problem
|
|
6
|
+
|
|
7
|
+
- 15+ hooks copied to `~/.codex/hooks/` but Codex CLI has no hooks API → dead code
|
|
8
|
+
- Commands duplicated as both `commands/` and `prompts/` → bloat
|
|
9
|
+
- Custom prompts [deprecated by OpenAI](https://developers.openai.com/codex/custom-prompts)
|
|
10
|
+
- Bridge skill already maps legacy commands → Codex skills
|
|
11
|
+
|
|
12
|
+
## Phases
|
|
13
|
+
|
|
14
|
+
| # | Phase | Status | Key Changes |
|
|
15
|
+
|---|---|---|---|
|
|
16
|
+
| 1 | [Remove hooks, generate rules](phase-01-hooks-to-rules.md) | `[x]` | `constants.py`, 3 templates, `rules_generator.py` |
|
|
17
|
+
| 2 | [Remove commands/prompts](phase-02-remove-commands-prompts.md) | `[x]` | `cli.py`, `clean_target.py` |
|
|
18
|
+
| 3 | [Update bridge skill + syntax](phase-03-bridge-and-syntax.md) | `[x]` | `templates/bridge-skill.md`, `constants.py` |
|
|
19
|
+
| 4 | [Version bump, docs, tests](phase-04-version-docs-tests.md) | `[x]` | `package.json`, `README.md`, docs, tests |
|
|
20
|
+
|
|
21
|
+
## Before → After
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
~/.codex/ ~/.codex/
|
|
25
|
+
hooks/ ❌ NOT WORKING rules/
|
|
26
|
+
15+ .cjs files development-rules.md ← existing
|
|
27
|
+
commands/ ⚠️ deprecated primary-workflow.md ← existing
|
|
28
|
+
18+ .md files security-privacy.md ← NEW from hooks
|
|
29
|
+
prompts/ ⚠️ duplicate file-naming.md ← NEW from hooks
|
|
30
|
+
18+ .md files code-quality-reminders.md ← NEW from hooks
|
|
31
|
+
rules/ ✅ skills/ ✅ unchanged
|
|
32
|
+
skills/ ✅ agents/ ✅ unchanged
|
|
33
|
+
agents/ ✅ scripts/ ✅ unchanged
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Dependencies
|
|
37
|
+
|
|
38
|
+
Phase 1 → Phase 2 → Phase 3 → Phase 4 (strict sequential)
|
|
39
|
+
|
|
40
|
+
## Research Sources
|
|
41
|
+
|
|
42
|
+
- [Codex Skills docs](https://developers.openai.com/codex/skills): Skills at `$HOME/.agents/skills/`
|
|
43
|
+
- [Custom Prompts docs](https://developers.openai.com/codex/custom-prompts): **Deprecated**, use skills
|
|
44
|
+
- Codex rules: `~/.codex/rules/*.md` loaded at startup, always in context
|
|
45
|
+
- Token budget: 37 KiB used / 65 KiB limit → ~28 KiB remaining
|
|
46
|
+
|
|
47
|
+
## Risk
|
|
48
|
+
|
|
49
|
+
Low — removes dead code, generates static rules from templates. No logic change to working features.
|
|
@@ -22,7 +22,7 @@ from .config_enforcer import (
|
|
|
22
22
|
)
|
|
23
23
|
from .dep_bootstrapper import bootstrap_deps
|
|
24
24
|
from .path_normalizer import normalize_agent_tomls, normalize_files
|
|
25
|
-
from .
|
|
25
|
+
from .rules_generator import generate_hook_rules
|
|
26
26
|
from .runtime_verifier import verify_runtime
|
|
27
27
|
from .source_resolver import detect_claude_source, find_latest_zip, validate_source
|
|
28
28
|
from .sync_registry import load_registry, save_registry
|
|
@@ -162,6 +162,9 @@ def main() -> int:
|
|
|
162
162
|
changed = normalize_files(codex_home=codex_home, include_mcp=args.mcp, dry_run=args.dry_run)
|
|
163
163
|
print(f"normalize_changed={changed}")
|
|
164
164
|
|
|
165
|
+
rules_generated = generate_hook_rules(codex_home=codex_home, dry_run=args.dry_run)
|
|
166
|
+
print(f"hook_rules_generated={rules_generated}")
|
|
167
|
+
|
|
165
168
|
# enforce_config BEFORE register_agents — enforce_config rewrites config.toml
|
|
166
169
|
baseline_changed = 0
|
|
167
170
|
if ensure_agents(workspace=workspace, dry_run=args.dry_run):
|
|
@@ -185,9 +188,6 @@ def main() -> int:
|
|
|
185
188
|
agents_registered = register_agents(codex_home=codex_home, dry_run=args.dry_run)
|
|
186
189
|
print(f"agents_registered={agents_registered}")
|
|
187
190
|
|
|
188
|
-
prompt_stats = export_prompts(codex_home=codex_home, include_mcp=args.mcp, dry_run=args.dry_run)
|
|
189
|
-
print(f"prompts: added={prompt_stats['added']} total={prompt_stats['total_generated']}")
|
|
190
|
-
|
|
191
191
|
bootstrap_stats = None
|
|
192
192
|
if not args.no_deps:
|
|
193
193
|
bootstrap_stats = bootstrap_deps(
|
|
@@ -219,7 +219,7 @@ def main() -> int:
|
|
|
219
219
|
"normalize_changed": changed,
|
|
220
220
|
"agent_toml_changed": agent_toml_changed,
|
|
221
221
|
"agents_registered": agents_registered,
|
|
222
|
-
"
|
|
222
|
+
"rules_generated": rules_generated,
|
|
223
223
|
"bootstrap": bootstrap_stats,
|
|
224
224
|
"verify": verify_stats,
|
|
225
225
|
}
|
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
from typing import List, Set, Tuple
|
|
4
4
|
|
|
5
|
-
ASSET_DIRS = {"
|
|
5
|
+
ASSET_DIRS = {"output-styles", "rules", "scripts"}
|
|
6
6
|
ASSET_FILES = {".env.example", ".ck.json"}
|
|
7
7
|
ASSET_MANIFEST = ".sync-manifest-assets.txt"
|
|
8
|
+
# DEPRECATED: prompt_exporter.py no longer called from CLI pipeline (v0.2.5)
|
|
8
9
|
PROMPT_MANIFEST = ".claudekit-generated-prompts.txt"
|
|
9
10
|
REGISTRY_FILE = ".claudekit-sync-registry.json"
|
|
10
11
|
|
|
@@ -67,6 +68,9 @@ CLAUDE_SYNTAX_ADAPTATIONS: List[Tuple[str, str]] = [
|
|
|
67
68
|
("Task(researcher)", "the researcher agent"),
|
|
68
69
|
("Task(", "delegate to "),
|
|
69
70
|
("$HOME/.claude/skills/*", "${CODEX_HOME:-$HOME/.codex}/skills/*"),
|
|
71
|
+
("via Task tool", "via agent delegation"),
|
|
72
|
+
("Claude Code", "Codex CLI"),
|
|
73
|
+
("claude code", "Codex CLI"),
|
|
70
74
|
]
|
|
71
75
|
|
|
72
76
|
# Claude model → Codex model mapping (per developers.openai.com/codex/multi-agent)
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""Generate hook-equivalent rules from templates."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from .utils import load_template, write_text_if_changed
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
RULE_TEMPLATES = {
|
|
11
|
+
"security-privacy.md": "rule-security-privacy.md",
|
|
12
|
+
"file-naming.md": "rule-file-naming.md",
|
|
13
|
+
"code-quality-reminders.md": "rule-code-quality.md",
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def generate_hook_rules(*, codex_home: Path, dry_run: bool) -> int:
|
|
18
|
+
"""Generate rules that replace ClaudeKit hook behavior."""
|
|
19
|
+
rules_dir = codex_home / "rules"
|
|
20
|
+
if not dry_run:
|
|
21
|
+
rules_dir.mkdir(parents=True, exist_ok=True)
|
|
22
|
+
|
|
23
|
+
generated = 0
|
|
24
|
+
for rule_name, template_name in RULE_TEMPLATES.items():
|
|
25
|
+
content = load_template(template_name)
|
|
26
|
+
target = rules_dir / rule_name
|
|
27
|
+
if write_text_if_changed(target, content, dry_run=dry_run):
|
|
28
|
+
generated += 1
|
|
29
|
+
print(f"upsert: rules/{rule_name}")
|
|
30
|
+
|
|
31
|
+
return generated
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: claudekit-command-bridge
|
|
3
|
-
description: Bridge legacy ClaudeKit commands to Codex-native
|
|
3
|
+
description: Bridge legacy ClaudeKit slash commands to Codex-native skills. Activates when users mention /ck-help, /coding-level, /ask, /docs/*, /journal, /watzup, or ask for Claude command equivalents.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# ClaudeKit
|
|
6
|
+
# ClaudeKit → Codex Bridge
|
|
7
7
|
|
|
8
|
-
Translate ClaudeKit command intent into Codex skills
|
|
8
|
+
Translate ClaudeKit command intent into Codex-native skills.
|
|
9
9
|
|
|
10
10
|
## Quick Mapping
|
|
11
11
|
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Code Quality Reminders
|
|
2
|
+
|
|
3
|
+
## Code Simplification
|
|
4
|
+
After significant file edits (5+), consider running code-simplifier agent.
|
|
5
|
+
|
|
6
|
+
## Post-Planning
|
|
7
|
+
After creating an implementation plan, always activate the cook skill before implementation.
|
|
8
|
+
|
|
9
|
+
## Modularization
|
|
10
|
+
If a code file exceeds 200 lines, consider modularizing it.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# File Naming
|
|
2
|
+
|
|
3
|
+
Apply before every file creation (skip for markdown/text):
|
|
4
|
+
- **JS/TS/Python/shell**: kebab-case with descriptive names
|
|
5
|
+
- **C#/Java/Kotlin/Swift**: PascalCase
|
|
6
|
+
- **Go/Rust**: snake_case
|
|
7
|
+
- Goal: self-documenting names for LLM tools (Grep, Glob, Search)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Security & Privacy
|
|
2
|
+
|
|
3
|
+
## Sensitive File Access
|
|
4
|
+
Ask user for explicit approval before reading:
|
|
5
|
+
- `.env`, `.env.*` (API keys, passwords, tokens)
|
|
6
|
+
- `*.key`, `*.pem`, `*.p12`, `credentials.*`, `secrets.*`
|
|
7
|
+
- Files in `~/.ssh/`, `~/.gnupg/`, `~/.aws/`
|
|
8
|
+
|
|
9
|
+
**Flow:** State which file and why → wait for "yes" → read via cat → never output full secrets.
|
|
10
|
+
|
|
11
|
+
## Directory Access Control
|
|
12
|
+
Respect `.ckignore` patterns if present in project root:
|
|
13
|
+
- Read `.ckignore` at session start
|
|
14
|
+
- Do NOT access matching directories
|
|
15
|
+
- Common: `node_modules/`, `dist/`, `build/`, `.git/objects/`
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""Tests for rules_generator module."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from claudekit_codex_sync.rules_generator import generate_hook_rules
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def test_generates_all_rule_files(tmp_path: Path):
|
|
9
|
+
"""Generates 3 rule files."""
|
|
10
|
+
count = generate_hook_rules(codex_home=tmp_path, dry_run=False)
|
|
11
|
+
assert count == 3
|
|
12
|
+
assert (tmp_path / "rules" / "security-privacy.md").exists()
|
|
13
|
+
assert (tmp_path / "rules" / "file-naming.md").exists()
|
|
14
|
+
assert (tmp_path / "rules" / "code-quality-reminders.md").exists()
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def test_idempotent(tmp_path: Path):
|
|
18
|
+
"""Second run returns 0 (no changes)."""
|
|
19
|
+
generate_hook_rules(codex_home=tmp_path, dry_run=False)
|
|
20
|
+
count = generate_hook_rules(codex_home=tmp_path, dry_run=False)
|
|
21
|
+
assert count == 0
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def test_dry_run(tmp_path: Path):
|
|
25
|
+
"""Dry run counts but doesn't create."""
|
|
26
|
+
count = generate_hook_rules(codex_home=tmp_path, dry_run=True)
|
|
27
|
+
assert count == 3
|
|
28
|
+
assert not (tmp_path / "rules").exists()
|