claudekit-codex-sync 0.1.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/AGENTS.md +45 -0
- package/README.md +131 -0
- package/bin/ck-codex-sync +12 -0
- package/bin/ck-codex-sync.js +9 -0
- package/docs/code-standards.md +62 -0
- package/docs/codebase-summary.md +83 -0
- package/docs/codex-vs-claude-agents.md +74 -0
- package/docs/installation-guide.md +64 -0
- package/docs/project-overview-pdr.md +44 -0
- package/docs/project-roadmap.md +51 -0
- package/docs/system-architecture.md +106 -0
- package/package.json +16 -0
- package/plans/260222-2051-claudekit-codex-community-sync/phase-01-productization.md +36 -0
- package/plans/260222-2051-claudekit-codex-community-sync/phase-02-core-refactor.md +32 -0
- package/plans/260222-2051-claudekit-codex-community-sync/phase-03-agent-transpiler.md +33 -0
- package/plans/260222-2051-claudekit-codex-community-sync/phase-04-parity-harness.md +43 -0
- package/plans/260222-2051-claudekit-codex-community-sync/phase-05-distribution-npm.md +35 -0
- package/plans/260222-2051-claudekit-codex-community-sync/phase-06-git-clone-docs.md +28 -0
- package/plans/260222-2051-claudekit-codex-community-sync/phase-07-qa-release.md +35 -0
- package/plans/260222-2051-claudekit-codex-community-sync/plan.md +99 -0
- package/plans/260223-0951-refactor-and-upgrade/phase-01-project-structure.md +79 -0
- package/plans/260223-0951-refactor-and-upgrade/phase-02-extract-templates.md +36 -0
- package/plans/260223-0951-refactor-and-upgrade/phase-03-modularize-python.md +107 -0
- package/plans/260223-0951-refactor-and-upgrade/phase-04-live-source-detection.md +76 -0
- package/plans/260223-0951-refactor-and-upgrade/phase-05-agent-toml-config.md +88 -0
- package/plans/260223-0951-refactor-and-upgrade/phase-06-backup-registry.md +58 -0
- package/plans/260223-0951-refactor-and-upgrade/phase-07-tests-docs-push.md +54 -0
- package/plans/260223-0951-refactor-and-upgrade/plan.md +72 -0
- package/reports/brainstorm-260222-2051-claudekit-codex-community-sync.md +113 -0
- package/scripts/bootstrap-claudekit-skill-scripts.sh +150 -0
- package/scripts/claudekit-sync-all.py +1150 -0
- package/scripts/export-claudekit-prompts.sh +221 -0
- package/scripts/normalize-claudekit-for-codex.sh +261 -0
- package/src/claudekit_codex_sync/__init__.py +0 -0
- package/src/claudekit_codex_sync/asset_sync_dir.py +125 -0
- package/src/claudekit_codex_sync/asset_sync_zip.py +140 -0
- package/src/claudekit_codex_sync/bridge_generator.py +33 -0
- package/src/claudekit_codex_sync/cli.py +199 -0
- package/src/claudekit_codex_sync/config_enforcer.py +140 -0
- package/src/claudekit_codex_sync/constants.py +104 -0
- package/src/claudekit_codex_sync/dep_bootstrapper.py +73 -0
- package/src/claudekit_codex_sync/path_normalizer.py +248 -0
- package/src/claudekit_codex_sync/prompt_exporter.py +89 -0
- package/src/claudekit_codex_sync/runtime_verifier.py +32 -0
- package/src/claudekit_codex_sync/source_resolver.py +78 -0
- package/src/claudekit_codex_sync/sync_registry.py +77 -0
- package/src/claudekit_codex_sync/utils.py +130 -0
- package/templates/agents-md.md +45 -0
- package/templates/bridge-docs-init.sh +25 -0
- package/templates/bridge-project-status.sh +49 -0
- package/templates/bridge-resolve-command.py +52 -0
- package/templates/bridge-skill.md +63 -0
- package/templates/command-map.md +44 -0
- package/tests/__init__.py +1 -0
- package/tests/test_config_enforcer.py +44 -0
- package/tests/test_path_normalizer.py +61 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"""Utility functions for ClaudeKit Codex Sync."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import hashlib
|
|
6
|
+
import json
|
|
7
|
+
import os
|
|
8
|
+
import shutil
|
|
9
|
+
import subprocess
|
|
10
|
+
import sys
|
|
11
|
+
from datetime import datetime
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from typing import Dict, Iterable, List, Optional, Sequence, Set, Tuple
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class SyncError(RuntimeError):
|
|
17
|
+
"""Custom error for sync operations."""
|
|
18
|
+
pass
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def eprint(msg: str) -> None:
|
|
22
|
+
"""Print to stderr."""
|
|
23
|
+
print(msg, file=sys.stderr)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def run_cmd(
|
|
27
|
+
cmd: Sequence[str],
|
|
28
|
+
*,
|
|
29
|
+
cwd: Optional[Path] = None,
|
|
30
|
+
dry_run: bool = False,
|
|
31
|
+
check: bool = True,
|
|
32
|
+
capture: bool = False,
|
|
33
|
+
) -> subprocess.CompletedProcess:
|
|
34
|
+
"""Run a shell command with optional dry-run mode."""
|
|
35
|
+
pretty = " ".join(cmd)
|
|
36
|
+
if dry_run:
|
|
37
|
+
print(f"[dry-run] {pretty}")
|
|
38
|
+
return subprocess.CompletedProcess(cmd, 0, "", "")
|
|
39
|
+
return subprocess.run(
|
|
40
|
+
list(cmd),
|
|
41
|
+
cwd=str(cwd) if cwd else None,
|
|
42
|
+
check=check,
|
|
43
|
+
text=True,
|
|
44
|
+
capture_output=capture,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def ensure_parent(path: Path, dry_run: bool) -> None:
|
|
49
|
+
"""Ensure parent directory exists."""
|
|
50
|
+
if dry_run:
|
|
51
|
+
return
|
|
52
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def write_bytes_if_changed(
|
|
56
|
+
path: Path, data: bytes, *, mode: Optional[int], dry_run: bool
|
|
57
|
+
) -> Tuple[bool, bool]:
|
|
58
|
+
"""Write bytes to file if content changed. Returns (changed, is_new)."""
|
|
59
|
+
exists = path.exists()
|
|
60
|
+
if exists and path.read_bytes() == data:
|
|
61
|
+
if mode is not None and not dry_run:
|
|
62
|
+
os.chmod(path, mode)
|
|
63
|
+
return False, False
|
|
64
|
+
if dry_run:
|
|
65
|
+
return True, not exists
|
|
66
|
+
ensure_parent(path, dry_run=False)
|
|
67
|
+
path.write_bytes(data)
|
|
68
|
+
if mode is not None:
|
|
69
|
+
os.chmod(path, mode)
|
|
70
|
+
return True, not exists
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def write_text_if_changed(
|
|
74
|
+
path: Path, text: str, *, executable: bool = False, dry_run: bool = False
|
|
75
|
+
) -> bool:
|
|
76
|
+
"""Write text to file if content changed."""
|
|
77
|
+
mode = None
|
|
78
|
+
if executable:
|
|
79
|
+
mode = 0o755
|
|
80
|
+
data = text.encode("utf-8")
|
|
81
|
+
changed, _ = write_bytes_if_changed(path, data, mode=mode, dry_run=dry_run)
|
|
82
|
+
return changed
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def load_manifest(path: Path) -> Set[str]:
|
|
86
|
+
"""Load manifest file as set of paths."""
|
|
87
|
+
if not path.exists():
|
|
88
|
+
return set()
|
|
89
|
+
return {line.strip() for line in path.read_text(encoding="utf-8").splitlines() if line.strip()}
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def save_manifest(path: Path, values: Iterable[str], dry_run: bool) -> None:
|
|
93
|
+
"""Save manifest file from set of paths."""
|
|
94
|
+
data = "\n".join(sorted(set(values)))
|
|
95
|
+
if data:
|
|
96
|
+
data += "\n"
|
|
97
|
+
write_text_if_changed(path, data, dry_run=dry_run)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def apply_replacements(text: str, rules: Sequence[Tuple[str, str]]) -> str:
|
|
101
|
+
"""Apply string replacements in sequence."""
|
|
102
|
+
out = text
|
|
103
|
+
for old, new in rules:
|
|
104
|
+
out = out.replace(old, new)
|
|
105
|
+
return out
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def is_excluded_path(parts: Sequence[str]) -> bool:
|
|
109
|
+
"""Check if path contains excluded directories."""
|
|
110
|
+
blocked = {".system", "node_modules", ".venv", "dist", "build", "__pycache__", ".pytest_cache"}
|
|
111
|
+
return any(p in blocked for p in parts)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def compute_hash(path: Path) -> str:
|
|
115
|
+
"""Compute SHA-256 hash of file contents."""
|
|
116
|
+
return hashlib.sha256(path.read_bytes()).hexdigest()
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def create_backup(path: Path) -> Path:
|
|
120
|
+
"""Create a timestamped backup of a file."""
|
|
121
|
+
ts = datetime.now().strftime("%Y%m%d-%H%M%S")
|
|
122
|
+
backup = path.with_suffix(f".ck-backup-{ts}{path.suffix}")
|
|
123
|
+
shutil.copy2(path, backup)
|
|
124
|
+
return backup
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def load_template(name: str) -> str:
|
|
128
|
+
"""Load a template file from the templates directory."""
|
|
129
|
+
templates_dir = Path(__file__).parent.parent.parent / "templates"
|
|
130
|
+
return (templates_dir / name).read_text(encoding="utf-8")
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
Codex working profile for this workspace, adapted from ClaudeKit rules and workflows.
|
|
4
|
+
|
|
5
|
+
## Operating Principles
|
|
6
|
+
|
|
7
|
+
- Follow `YAGNI`, `KISS`, `DRY`.
|
|
8
|
+
- Prefer direct, maintainable solutions over speculative abstraction.
|
|
9
|
+
- Do not claim completion without evidence (tests, checks, or concrete validation).
|
|
10
|
+
- Never use fake implementations just to make tests/build pass.
|
|
11
|
+
|
|
12
|
+
## Default Workflow
|
|
13
|
+
|
|
14
|
+
1. Read context first: `README.md` and relevant docs under `./docs/`.
|
|
15
|
+
2. For non-trivial work, create/update a plan in `./plans/` before coding.
|
|
16
|
+
3. Implement in existing files unless new files are clearly needed.
|
|
17
|
+
4. Validate with project compile/lint/test commands.
|
|
18
|
+
5. Run code-review mindset before finalizing (bugs, regressions, missing tests first).
|
|
19
|
+
6. Update docs when behavior, architecture, contracts, or operations change.
|
|
20
|
+
|
|
21
|
+
## Quality Gates
|
|
22
|
+
|
|
23
|
+
- Handle edge cases and error paths explicitly.
|
|
24
|
+
- Keep security and performance implications visible in design decisions.
|
|
25
|
+
- Keep code readable and intention-revealing; add comments only when needed for non-obvious logic.
|
|
26
|
+
|
|
27
|
+
## Documentation Rules
|
|
28
|
+
|
|
29
|
+
- `./docs` is the source of truth for project docs.
|
|
30
|
+
- Keep docs synchronized with code and implementation decisions.
|
|
31
|
+
- When summarizing/reporting, be concise and list unresolved questions at the end.
|
|
32
|
+
|
|
33
|
+
## Skill Usage
|
|
34
|
+
|
|
35
|
+
- Activate relevant skills intentionally per task.
|
|
36
|
+
- For legacy ClaudeKit command intents (`/ck-help`, `/coding-level`, `/ask`, `/docs/*`, `/journal`, `/watzup`), use `$claudekit-command-bridge`.
|
|
37
|
+
|
|
38
|
+
## Reference Material (Imported from ClaudeKit)
|
|
39
|
+
|
|
40
|
+
- `~/.codex/claudekit/CLAUDE.md`
|
|
41
|
+
- `~/.codex/claudekit/rules/development-rules.md`
|
|
42
|
+
- `~/.codex/claudekit/rules/primary-workflow.md`
|
|
43
|
+
- `~/.codex/claudekit/rules/orchestration-protocol.md`
|
|
44
|
+
- `~/.codex/claudekit/rules/documentation-management.md`
|
|
45
|
+
- `~/.codex/claudekit/rules/team-coordination-rules.md`
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
DOCS_DIR="${1:-docs}"
|
|
5
|
+
mkdir -p "$DOCS_DIR"
|
|
6
|
+
mkdir -p "$DOCS_DIR/journals"
|
|
7
|
+
|
|
8
|
+
create_if_missing() {
|
|
9
|
+
local file="$1"
|
|
10
|
+
local content="$2"
|
|
11
|
+
if [[ ! -f "$file" ]]; then
|
|
12
|
+
printf "%s\\n" "$content" > "$file"
|
|
13
|
+
echo "created: $file"
|
|
14
|
+
else
|
|
15
|
+
echo "exists: $file"
|
|
16
|
+
fi
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
create_if_missing "$DOCS_DIR/project-overview-pdr.md" "# Project Overview / PDR"
|
|
20
|
+
create_if_missing "$DOCS_DIR/code-standards.md" "# Code Standards"
|
|
21
|
+
create_if_missing "$DOCS_DIR/codebase-summary.md" "# Codebase Summary"
|
|
22
|
+
create_if_missing "$DOCS_DIR/design-guidelines.md" "# Design Guidelines"
|
|
23
|
+
create_if_missing "$DOCS_DIR/deployment-guide.md" "# Deployment Guide"
|
|
24
|
+
create_if_missing "$DOCS_DIR/system-architecture.md" "# System Architecture"
|
|
25
|
+
create_if_missing "$DOCS_DIR/project-roadmap.md" "# Project Roadmap"
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
PLANS_DIR="${1:-plans}"
|
|
5
|
+
|
|
6
|
+
echo "## Project Status"
|
|
7
|
+
echo
|
|
8
|
+
|
|
9
|
+
if [[ -d "$PLANS_DIR" ]]; then
|
|
10
|
+
total_plans=0
|
|
11
|
+
completed=0
|
|
12
|
+
in_progress=0
|
|
13
|
+
pending=0
|
|
14
|
+
|
|
15
|
+
while IFS= read -r plan; do
|
|
16
|
+
total_plans=$((total_plans + 1))
|
|
17
|
+
status="$(grep -E '^status:' "$plan" | head -n1 | awk '{print $2}')"
|
|
18
|
+
case "$status" in
|
|
19
|
+
completed) completed=$((completed + 1)) ;;
|
|
20
|
+
in-progress|in_progress) in_progress=$((in_progress + 1)) ;;
|
|
21
|
+
pending|"") pending=$((pending + 1)) ;;
|
|
22
|
+
*) ;;
|
|
23
|
+
esac
|
|
24
|
+
done < <(find "$PLANS_DIR" -type f -name plan.md | sort)
|
|
25
|
+
|
|
26
|
+
echo "- Plans: $total_plans"
|
|
27
|
+
echo "- Completed: $completed"
|
|
28
|
+
echo "- In progress: $in_progress"
|
|
29
|
+
echo "- Pending/unknown: $pending"
|
|
30
|
+
echo
|
|
31
|
+
else
|
|
32
|
+
echo "- Plans directory not found: $PLANS_DIR"
|
|
33
|
+
echo
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
|
37
|
+
echo "## Git"
|
|
38
|
+
echo
|
|
39
|
+
echo "- Branch: $(git rev-parse --abbrev-ref HEAD)"
|
|
40
|
+
if [[ -n "$(git status --porcelain)" ]]; then
|
|
41
|
+
echo "- Working tree: dirty"
|
|
42
|
+
else
|
|
43
|
+
echo "- Working tree: clean"
|
|
44
|
+
fi
|
|
45
|
+
else
|
|
46
|
+
echo "## Git"
|
|
47
|
+
echo
|
|
48
|
+
echo "- Not in a git repository"
|
|
49
|
+
fi
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
MAP = {
|
|
5
|
+
"/preview": "markdown-novel-viewer",
|
|
6
|
+
"/kanban": "plans-kanban",
|
|
7
|
+
"/review/codebase": "code-review",
|
|
8
|
+
"/test": "web-testing",
|
|
9
|
+
"/test/ui": "web-testing",
|
|
10
|
+
"/worktree": "git",
|
|
11
|
+
"/plan": "plan",
|
|
12
|
+
"/plan/validate": "plan",
|
|
13
|
+
"/plan/archive": "project-management",
|
|
14
|
+
"/plan/red-team": "plan",
|
|
15
|
+
"/docs/init": "claudekit-command-bridge (docs-init.sh)",
|
|
16
|
+
"/docs/update": "claudekit-command-bridge",
|
|
17
|
+
"/docs/summarize": "claudekit-command-bridge",
|
|
18
|
+
"/journal": "claudekit-command-bridge",
|
|
19
|
+
"/watzup": "claudekit-command-bridge",
|
|
20
|
+
"/ask": "claudekit-command-bridge (architecture mode)",
|
|
21
|
+
"/coding-level": "claudekit-command-bridge (explanation depth)",
|
|
22
|
+
"/ck-help": "claudekit-command-bridge (this resolver)",
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def main() -> int:
|
|
27
|
+
raw = " ".join(sys.argv[1:]).strip()
|
|
28
|
+
if not raw:
|
|
29
|
+
print('Usage: resolve-command.py "<legacy-command-or-intent>"')
|
|
30
|
+
return 1
|
|
31
|
+
|
|
32
|
+
cmd = raw.split()[0]
|
|
33
|
+
if cmd in MAP:
|
|
34
|
+
print(f"{cmd} -> {MAP[cmd]}")
|
|
35
|
+
return 0
|
|
36
|
+
|
|
37
|
+
for prefix, target in [
|
|
38
|
+
("/docs/", "claudekit-command-bridge"),
|
|
39
|
+
("/plan/", "plan"),
|
|
40
|
+
("/review/", "code-review"),
|
|
41
|
+
("/test", "web-testing"),
|
|
42
|
+
]:
|
|
43
|
+
if cmd.startswith(prefix):
|
|
44
|
+
print(f"{cmd} -> {target}")
|
|
45
|
+
return 0
|
|
46
|
+
|
|
47
|
+
print(f"{cmd} -> no direct map; use find-skills + claudekit-command-bridge")
|
|
48
|
+
return 0
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
if __name__ == "__main__":
|
|
52
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: claudekit-command-bridge
|
|
3
|
+
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.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# ClaudeKit Command Bridge
|
|
7
|
+
|
|
8
|
+
Translate ClaudeKit command intent into Codex skills/workflows.
|
|
9
|
+
|
|
10
|
+
## Quick Mapping
|
|
11
|
+
|
|
12
|
+
| Legacy command | Codex target |
|
|
13
|
+
|---|---|
|
|
14
|
+
| `/preview` | `markdown-novel-viewer` skill |
|
|
15
|
+
| `/kanban` | `plans-kanban` skill |
|
|
16
|
+
| `/review/codebase` | `code-review` skill |
|
|
17
|
+
| `/test` or `/test/ui` | `web-testing` skill |
|
|
18
|
+
| `/worktree` | `git` skill + git worktree commands |
|
|
19
|
+
| `/plan/*` | `plan` skill |
|
|
20
|
+
| `/docs/init` | Run `scripts/docs-init.sh` then review docs |
|
|
21
|
+
| `/docs/update` | Update docs from latest code changes |
|
|
22
|
+
| `/docs/summarize` | Summarize codebase into `docs/codebase-summary.md` |
|
|
23
|
+
| `/journal` | Write concise entry under `docs/journals/` |
|
|
24
|
+
| `/watzup` | Produce status report from plans + git state |
|
|
25
|
+
| `/ask` | Architecture consultation mode (no implementation) |
|
|
26
|
+
| `/coding-level` | Adjust explanation depth (levels 0-5 rubric below) |
|
|
27
|
+
| `/ck-help` | Run `scripts/resolve-command.py "<request>"` |
|
|
28
|
+
|
|
29
|
+
## Commands Converted Here
|
|
30
|
+
|
|
31
|
+
### `/ask` -> Architecture mode
|
|
32
|
+
|
|
33
|
+
- Provide architecture analysis, tradeoffs, risks, and phased strategy.
|
|
34
|
+
- Do not start implementation unless user explicitly asks.
|
|
35
|
+
|
|
36
|
+
### `/coding-level` -> Explanation depth policy
|
|
37
|
+
|
|
38
|
+
Use requested level when explaining:
|
|
39
|
+
|
|
40
|
+
- `0`: ELI5, minimal jargon, analogies.
|
|
41
|
+
- `1`: Junior, explain why and common mistakes.
|
|
42
|
+
- `2`: Mid, include patterns and tradeoffs.
|
|
43
|
+
- `3`: Senior, architecture and constraints focus.
|
|
44
|
+
- `4`: Lead, risk/business impact and strategy.
|
|
45
|
+
- `5`: Expert, concise implementation-first.
|
|
46
|
+
|
|
47
|
+
### `/docs/init`, `/docs/update`, `/docs/summarize`
|
|
48
|
+
|
|
49
|
+
- Initialize docs structure with `scripts/docs-init.sh`.
|
|
50
|
+
- Keep docs source of truth under `./docs`.
|
|
51
|
+
|
|
52
|
+
### `/journal`, `/watzup`
|
|
53
|
+
|
|
54
|
+
- Write concise journal entries in `docs/journals/`.
|
|
55
|
+
- For status, summarize plans and git state.
|
|
56
|
+
|
|
57
|
+
## Helper Scripts
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
python3 ${CODEX_HOME:-$HOME/.codex}/skills/claudekit-command-bridge/scripts/resolve-command.py "/docs/update"
|
|
61
|
+
${CODEX_HOME:-$HOME/.codex}/skills/claudekit-command-bridge/scripts/docs-init.sh
|
|
62
|
+
${CODEX_HOME:-$HOME/.codex}/skills/claudekit-command-bridge/scripts/project-status.sh
|
|
63
|
+
```
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# ClaudeKit -> Codex Command Map
|
|
2
|
+
|
|
3
|
+
## Covered by existing skills
|
|
4
|
+
|
|
5
|
+
- `/preview` -> `markdown-novel-viewer`
|
|
6
|
+
- `/kanban` -> `plans-kanban`
|
|
7
|
+
- `/review/codebase` -> `code-review`
|
|
8
|
+
- `/test`, `/test/ui` -> `web-testing`
|
|
9
|
+
- `/worktree` -> `git`
|
|
10
|
+
- `/plan/*` -> `plan`
|
|
11
|
+
|
|
12
|
+
## Converted into bridge workflows
|
|
13
|
+
|
|
14
|
+
- `/ck-help` -> `claudekit-command-bridge` (`resolve-command.py`)
|
|
15
|
+
- `/coding-level` -> `claudekit-command-bridge` (depth rubric + output styles)
|
|
16
|
+
- `/ask` -> `claudekit-command-bridge` (architecture mode)
|
|
17
|
+
- `/docs/init`, `/docs/update`, `/docs/summarize` -> `claudekit-command-bridge`
|
|
18
|
+
- `/journal`, `/watzup` -> `claudekit-command-bridge`
|
|
19
|
+
|
|
20
|
+
## Explicitly excluded in this sync
|
|
21
|
+
|
|
22
|
+
- `/use-mcp` (excluded when `--include-mcp` is not set)
|
|
23
|
+
- Hooks (excluded when `--include-hooks` is not set)
|
|
24
|
+
|
|
25
|
+
## Custom Prompt Aliases (`/prompts:<name>`)
|
|
26
|
+
|
|
27
|
+
- `/ask` -> `/prompts:ask`
|
|
28
|
+
- `/ck-help` -> `/prompts:ck-help`
|
|
29
|
+
- `/coding-level` -> `/prompts:coding-level`
|
|
30
|
+
- `/docs/init` -> `/prompts:docs-init`
|
|
31
|
+
- `/docs/summarize` -> `/prompts:docs-summarize`
|
|
32
|
+
- `/docs/update` -> `/prompts:docs-update`
|
|
33
|
+
- `/journal` -> `/prompts:journal`
|
|
34
|
+
- `/kanban` -> `/prompts:kanban`
|
|
35
|
+
- `/plan/archive` -> `/prompts:plan-archive`
|
|
36
|
+
- `/plan/red-team` -> `/prompts:plan-red-team`
|
|
37
|
+
- `/plan/validate` -> `/prompts:plan-validate`
|
|
38
|
+
- `/preview` -> `/prompts:preview`
|
|
39
|
+
- `/review/codebase` -> `/prompts:review-codebase`
|
|
40
|
+
- `/review/codebase/parallel` -> `/prompts:review-codebase-parallel`
|
|
41
|
+
- `/test` -> `/prompts:test`
|
|
42
|
+
- `/test/ui` -> `/prompts:test-ui`
|
|
43
|
+
- `/watzup` -> `/prompts:watzup`
|
|
44
|
+
- `/worktree` -> `/prompts:worktree`
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Tests package
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""Tests for config_enforcer module."""
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
from claudekit_codex_sync.config_enforcer import enforce_multi_agent_flag
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def test_adds_multi_agent_to_existing_features(tmp_path):
|
|
8
|
+
"""Test adding multi_agent to existing [features] section."""
|
|
9
|
+
config = tmp_path / "config.toml"
|
|
10
|
+
config.write_text("[features]\n")
|
|
11
|
+
enforce_multi_agent_flag(config, dry_run=False)
|
|
12
|
+
content = config.read_text()
|
|
13
|
+
assert "multi_agent = true" in content
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def test_creates_features_section_if_missing(tmp_path):
|
|
17
|
+
"""Test creating [features] section if it doesn't exist."""
|
|
18
|
+
config = tmp_path / "config.toml"
|
|
19
|
+
config.write_text("some_other_setting = 1\n")
|
|
20
|
+
enforce_multi_agent_flag(config, dry_run=False)
|
|
21
|
+
content = config.read_text()
|
|
22
|
+
assert "[features]" in content
|
|
23
|
+
assert "multi_agent = true" in content
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def test_preserves_existing_config(tmp_path):
|
|
27
|
+
"""Test that existing config is preserved when adding multi_agent."""
|
|
28
|
+
config = tmp_path / "config.toml"
|
|
29
|
+
original = 'project_doc_max_bytes = 65536\n'
|
|
30
|
+
config.write_text(original)
|
|
31
|
+
enforce_multi_agent_flag(config, dry_run=False)
|
|
32
|
+
content = config.read_text()
|
|
33
|
+
assert "project_doc_max_bytes = 65536" in content
|
|
34
|
+
assert "multi_agent = true" in content
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def test_creates_file_if_not_exists(tmp_path):
|
|
38
|
+
"""Test creating config file if it doesn't exist."""
|
|
39
|
+
config = tmp_path / "config.toml"
|
|
40
|
+
enforce_multi_agent_flag(config, dry_run=False)
|
|
41
|
+
assert config.exists()
|
|
42
|
+
content = config.read_text()
|
|
43
|
+
assert "[features]" in content
|
|
44
|
+
assert "multi_agent = true" in content
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"""Tests for path_normalizer module."""
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
from claudekit_codex_sync.constants import SKILL_MD_REPLACEMENTS
|
|
5
|
+
from claudekit_codex_sync.path_normalizer import apply_replacements
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def test_claude_to_codex_path():
|
|
9
|
+
"""Test that .claude paths are normalized to .codex paths."""
|
|
10
|
+
text = "$HOME/.claude/skills/brainstorm/SKILL.md"
|
|
11
|
+
result = apply_replacements(text, SKILL_MD_REPLACEMENTS)
|
|
12
|
+
assert ".claude" not in result
|
|
13
|
+
assert ".codex" in result or "CODEX_HOME" in result
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def test_home_claude_replacement():
|
|
17
|
+
"""Test $HOME/.claude/skills/ replacement."""
|
|
18
|
+
text = "$HOME/.claude/skills/test/skill.py"
|
|
19
|
+
result = apply_replacements(text, SKILL_MD_REPLACEMENTS)
|
|
20
|
+
assert result == "${CODEX_HOME:-$HOME/.codex}/skills/test/skill.py"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def test_dot_slash_claude_replacement():
|
|
24
|
+
"""Test ./.claude/skills/ replacement."""
|
|
25
|
+
text = "./.claude/skills/test/skill.py"
|
|
26
|
+
result = apply_replacements(text, SKILL_MD_REPLACEMENTS)
|
|
27
|
+
assert result == "${CODEX_HOME:-$HOME/.codex}/skills/test/skill.py"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def test_tilde_claude_replacement():
|
|
31
|
+
"""Test ~/.claude/ replacement."""
|
|
32
|
+
text = "~/.claude/config.json"
|
|
33
|
+
result = apply_replacements(text, SKILL_MD_REPLACEMENTS)
|
|
34
|
+
assert result == "~/.codex/config.json"
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def test_ck_json_replacement():
|
|
38
|
+
"""Test .ck.json path replacement."""
|
|
39
|
+
text = "~/.claude/.ck.json"
|
|
40
|
+
result = apply_replacements(text, SKILL_MD_REPLACEMENTS)
|
|
41
|
+
assert result == "~/.codex/claudekit/.ck.json"
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def test_no_change_for_non_claude():
|
|
45
|
+
"""Test that non-claude paths are not changed."""
|
|
46
|
+
text = "/some/other/path/file.txt"
|
|
47
|
+
result = apply_replacements(text, SKILL_MD_REPLACEMENTS)
|
|
48
|
+
assert result == text
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def test_multiple_replacements():
|
|
52
|
+
"""Test multiple paths in same text."""
|
|
53
|
+
text = """
|
|
54
|
+
Use skills from $HOME/.claude/skills/
|
|
55
|
+
And scripts from ./.claude/scripts/
|
|
56
|
+
Config at ~/.claude/.ck.json
|
|
57
|
+
"""
|
|
58
|
+
result = apply_replacements(text, SKILL_MD_REPLACEMENTS)
|
|
59
|
+
assert "$HOME/.claude/skills/" not in result
|
|
60
|
+
assert "./.claude/scripts/" not in result
|
|
61
|
+
assert "~/.claude/.ck.json" not in result
|