feat: template-based dual-target generator for Claude + Codex

Replace generate-codex.sh with unified generate.sh that produces both
claude/ and codex/ output from template source files.

Agent bodies use ${PLANS_DIR}, ${WEB_SEARCH}, ${SEARCH_TOOLS} placeholders
expanded per-target via envsubst. Skills and rules made tool-agnostic
(no Claude tool names or .claude/ paths). Orchestrate skill stays
Claude-only.

install.sh now symlinks from claude/agents/ instead of agents/ directly.
flake.nix adds gettext (envsubst) to devShell.
This commit is contained in:
Bryan Ramos 2026-04-02 08:51:00 -04:00
parent 37ec0fd7ec
commit b9d8b03895
17 changed files with 687 additions and 211 deletions

View file

@ -0,0 +1,338 @@
---
date: 2026-04-02
task: template-based dual-target generator
tier: 2
status: active
---
## Plan: Template-based dual-target generator
## Summary
Refactor agent-team from "Claude source of truth with a Codex converter" to "tool-agnostic templates with a generator that produces both Claude and Codex output." Agent bodies gain `${VAR}` placeholders for tool-specific references. Skills and rules are made tool-agnostic by replacing Claude-specific tool names and paths with generic language. A new `generate.sh` replaces `generate-codex.sh` and produces both `claude/` and `codex/` output directories. `install.sh` changes to symlink from the generated `claude/` directory. The orchestrate skill stays Claude-only since it is deeply tied to Claude's Agent tool dispatch model.
## Out of Scope
- Changing agent frontmatter schema (YAML fields stay as-is; the generator handles YAML-to-TOML conversion)
- Adding new agents or skills
- Changing the orchestrate skill's content (it stays Claude-only, not templated)
- Changing conventions skill content (already tool-agnostic)
- Modifying the message-schema envelope format
- Codex config.toml generation (the current `generate-codex.sh` logic for mapping model/effort/sandbox is preserved, not redesigned)
- README content updates beyond what's needed to reflect the new structure
## Research Findings
**envsubst scoping:** `envsubst '${PLANS_DIR} ${WEB_SEARCH}'` substitutes only the listed variables, leaving other `$` references untouched. Safe for use in files with YAML frontmatter and bash-like content.
**Codex path conventions:** Codex has no `.claude/plans/` equivalent -- plans are regular files at `plans/` (project-relative). No `.claude/memory/` -- use `memory/` or omit. Skills are discovered from `~/.agents/skills/` via `SKILL.md` auto-matching or `skills.config` in agent TOML. Skills are NOT inlined into `developer_instructions`.
**Skill strategy:** Option B (make skills tool-agnostic) for most skills. Orchestrate stays Claude-only. Tool name references like "Use Read/Glob/Grep" add marginal value and can be replaced with generic language like "Search the codebase."
## Codebase Analysis
### Files to modify
| File | Change |
|---|---|
| `agents/architect.md` | Replace `.claude/plans/` (lines 19, 106) with `${PLANS_DIR}` |
| `agents/reviewer.md` | Replace `via WebFetch/WebSearch` (line 33) with `${WEB_SEARCH}` |
| `agents/debugger.md` | Replace `Use Grep` (line 25) with `${SEARCH_TOOLS}` |
| `agents/documenter.md` | Replace `Use Read/Glob/Grep` (line 29) with `${SEARCH_TOOLS}` |
| `skills/message-schema/SKILL.md` | Replace `.claude/plans/` (lines 155, 205) with `${PLANS_DIR}` |
| `skills/project/SKILL.md` | Replace `.claude/skills/project.md` (lines 7, 9) with `${PROJECT_SKILL_PATH}` |
| `skills/worker-protocol/SKILL.md` | Replace `Read/Glob/Grep` (line 50) with generic language |
| `skills/qa-checklist/SKILL.md` | Replace `Read/Grep` (line 13) with generic language |
| `rules/01-session.md` | Replace `.claude/memory/` (lines 3, 9, 12) with `${MEMORY_DIR}` |
| `rules/04-tools.md` | Replace `suggest /clear` (line 17) with `${CLEAR_CMD}` |
| `generate-codex.sh` | **Delete** -- replaced by `generate.sh` |
| `generate.sh` | **New** -- produces both `claude/` and `codex/` |
| `install.sh` | Rewire Claude symlinks to point at `claude/` output directory |
| `.gitignore` | Add `claude/` to exclusions |
| `flake.nix` | Add `envsubst` (from `gettext`) to devShell packages |
### Files for context (read-only)
| File | Why |
|---|---|
| `agents/worker.md` | Confirm no Claude-specific references in body (clean) |
| `agents/auditor.md` | Confirm no Claude-specific references in body (clean) |
| `agents/researcher.md` | Confirm no Claude-specific references in body (clean) |
| `skills/orchestrate/SKILL.md` | Confirm Claude-only decision (deeply coupled) |
| `skills/conventions/SKILL.md` | Confirm already tool-agnostic (clean) |
| `rules/02-responses.md` through `rules/07-research.md` | Confirm no Claude-specific references (clean, except 04-tools.md) |
| `codex/config.toml` | Understand current generated output (reference only) |
### Current patterns
- **Shell scripts** use `set -euo pipefail`, `SCRIPT_DIR` idiom, echo-based progress reporting
- **yq** (yq-go) is the YAML/JSON processor -- used for frontmatter extraction and settings parsing
- **Generated output** is committed to `codex/` but gitignored; `claude/` will follow the same pattern
- **Symlink strategy** in install.sh: directory symlinks for agents/skills/rules, file symlinks for individual config files; backup-on-conflict pattern
- **Agent markdown** uses YAML frontmatter with specific schema fields (`name`, `description`, `model`, `effort`, `permissionMode`, `tools`, `disallowedTools`, `maxTurns`, `skills`, `isolation`, `background`, `memory`)
## Interface Contracts
### Module ownership
- `generate.sh`: owned by Step 5 (Wave 3), responsible for template expansion + output generation
- `agents/*.md` templates: owned by Steps 1-2 (Wave 1), responsible for adding `${VAR}` placeholders
- `skills/` tool-agnostic edits: owned by Step 3 (Wave 1), responsible for removing tool-specific language
- `rules/` tool-agnostic edits: owned by Step 4 (Wave 1), responsible for removing tool-specific paths
- `install.sh`: owned by Step 6 (Wave 3), responsible for rewiring symlink sources
- `.gitignore` + `flake.nix`: owned by Step 7 (Wave 2), support infrastructure
### Shared interfaces
**Template variable contract** -- all workers must use exactly these variable names and nothing else:
```bash
# Variable definitions used by generate.sh for envsubst
# Claude target
PLANS_DIR=".claude/plans"
WEB_SEARCH="via WebFetch/WebSearch"
SEARCH_TOOLS="Use Grep/Glob/Read"
CLEAR_CMD="suggest /clear"
MEMORY_DIR=".claude/memory"
PROJECT_SKILL_PATH=".claude/skills/project.md"
# Codex target
PLANS_DIR="plans"
WEB_SEARCH="via web search"
SEARCH_TOOLS="Search the codebase"
CLEAR_CMD="suggest starting a new session"
MEMORY_DIR="memory"
PROJECT_SKILL_PATH=".agents/skills/project/SKILL.md"
```
**Agent body extraction pattern** (preserved from `generate-codex.sh`):
```bash
# Extract body after closing --- of frontmatter
awk 'BEGIN{fm=0} /^---$/{if(fm==0){fm=1;next} if(fm==1){fm=2;next}} fm==2{print}' "$src_file"
```
**Claude output directory structure:**
```
claude/
├── agents/ # Expanded .md files (frontmatter preserved, body substituted)
├── CLAUDE.md # Copied from source
├── settings.json # Copied from source
├── rules -> ../rules # Symlink to shared rules (already tool-agnostic after Wave 1)
└── skills -> ../skills # Symlink to shared skills (already tool-agnostic after Wave 1)
```
**Codex output directory structure:**
```
codex/
├── agents/ # Generated .toml files (body substituted with Codex values)
├── AGENTS.md # Generated from CLAUDE.md + expanded rules
└── config.toml # Generated from settings.json
```
### Conventions for this task
- Error handling: `set -euo pipefail` in all shell scripts. Echo progress for each major operation. Non-zero exit on failure.
- Naming: `${UPPER_SNAKE_CASE}` for template variables. `kebab-case` for file names.
- Template markers: Use `${VAR}` syntax only. Never use `$VAR` (ambiguous with shell) or `{{VAR}}` (not envsubst-compatible).
- Skill/rule edits: Replace tool-specific language with generic equivalents. Do NOT add `${VAR}` placeholders to skills or rules -- they are made tool-agnostic directly, not templated. Only agent bodies and message-schema (which references plan paths in examples) use template variables.
**Correction on skill/rule strategy:** Skills and rules become tool-agnostic by direct edit (hardcoded generic language). They are then shared as-is between both targets via symlinks from the output directories. Template variables (`${VAR}`) are used ONLY in agent body markdown and in message-schema's example paths. This keeps the template surface minimal.
However, `rules/01-session.md` and `rules/04-tools.md` present a problem: their content differs between Claude and Codex (`.claude/memory/` vs `memory/`, `/clear` vs "new session"). Two approaches:
**Approach A -- Template the rules too:** Add `${VAR}` placeholders to rules and expand them per-target. This means rules can't be symlinked; they must be copied into each output directory.
**Approach B -- Make rules fully generic:** Use tool-agnostic language ("the project memory directory", "suggest clearing context"). No placeholders needed; rules stay shared.
**Decision: Approach B.** The rules are guidance for agent behavior, not config. Generic language ("the project memory directory at the project root") communicates the intent without coupling to a specific path. The exact path is already established by the agent body templates and skills. This keeps the template surface to just agent bodies + message-schema examples.
**Revised skill/rule edit strategy:**
- `rules/01-session.md`: Replace `.claude/memory/` with `memory/` (tool-agnostic path). This works for both targets because the rules describe conceptual behavior ("persist in the memory directory"), and the actual path resolution happens in agent instructions.
- `rules/04-tools.md`: Replace `suggest /clear` with `suggest clearing context or starting a new session` (tool-agnostic).
- `skills/worker-protocol/SKILL.md`: Replace `use Read/Glob/Grep directly` with `verify by reading the relevant files directly` (tool-agnostic).
- `skills/qa-checklist/SKILL.md`: Replace `Verify with Read/Grep if uncertain` with `Verify by reading the code if uncertain` (tool-agnostic).
- `skills/project/SKILL.md`: This one references a concrete file path (`.claude/skills/project.md`). Two options: (a) make it generic ("check for a project-specific skill file in the standard location"), or (b) template it. Since the path genuinely differs between tools and is specific enough to matter, **template it** with `${PROJECT_SKILL_PATH}`. This means the project skill gets expanded per-target, not symlinked. But since skills are directory-symlinked as a whole, we need a different approach: generate only this one skill per-target, or restructure.
**Revised approach for project skill:** The simplest solution is to make the path generic. The project skill says "check for a project-specific skill file" -- the agent already knows where to look because each tool has its own conventions. Change the instruction to: "Check for a project-specific skill file in the current working directory's configuration. For Claude Code, this is `.claude/skills/project.md`. For Codex, this is discovered via the standard skill path." Actually this leaks tool awareness into the shared file.
**Final decision for project skill:** Make it fully generic: "Before starting any work, check for a project-specific skill file in the current working directory. The location depends on the tool configuration." The concrete path is not needed -- each tool resolves its own skill paths. The skill's purpose is behavioral ("check for project context before starting"), not path-specific.
Similarly for message-schema `plan_file` examples: these show `.claude/plans/kebab-case-title.md` as an example value. For Codex, this would be `plans/kebab-case-title.md`. Since message-schema is loaded as a skill (shared), we should either template it or make the example generic. **Decision:** Use `plans/kebab-case-title.md` as the example (dropping the `.claude/` prefix). This is the tool-agnostic path. Claude's architect agent body already specifies the full `.claude/plans/` path, so the schema example doesn't need to repeat the tool-specific prefix.
**Final template variable list (reduced):**
Only agent body markdown files use `${VAR}` placeholders. Everything else is made tool-agnostic by direct edit.
| Variable | Claude value | Codex value | Used in |
|---|---|---|---|
| `${PLANS_DIR}` | `.claude/plans` | `plans` | architect.md body |
| `${WEB_SEARCH}` | `via WebFetch/WebSearch` | `via web search` | reviewer.md body |
| `${SEARCH_TOOLS}` | `Use Grep/Glob/Read` | `Search the codebase` | debugger.md body, documenter.md body |
Skills, rules, and message-schema are made tool-agnostic by direct edit (no placeholders).
## Approach
**Strategy:** Two-layer approach.
1. **Layer 1 -- Tool-agnostic shared content.** Skills and rules are edited to remove Claude-specific tool names and paths. They become shared infrastructure, symlinked from both output directories.
2. **Layer 2 -- Templated agent bodies.** Agent markdown files in `agents/` gain `${VAR}` placeholders in their body text (not frontmatter). `generate.sh` expands these with tool-specific values and writes the results to `claude/` and `codex/`.
The generator (`generate.sh`) replaces `generate-codex.sh` and handles both targets:
- **Claude target:** Expand templates with Claude values, copy frontmatter-intact agent .md files to `claude/agents/`, copy CLAUDE.md and settings.json, symlink to shared skills/rules.
- **Codex target:** Expand templates with Codex values, convert YAML frontmatter to TOML (preserving existing model/effort/sandbox mapping logic), generate AGENTS.md from CLAUDE.md + expanded rules, generate config.toml from settings.json.
**Alternative considered: Jinja/m4 templating.** Rejected -- envsubst is simpler, already available via gettext in Nix, and sufficient for the ~3 variable substitutions in agent bodies. The complexity of Jinja (conditional blocks, filters) is not needed.
**Alternative considered: Keep Claude agents as source, derive Codex only.** This is the current approach. Rejected because it means the "source" files contain Claude-specific references that leak into Codex output (the current bug this refactor fixes). Making the source tool-agnostic eliminates the class of bugs where a Codex agent says "Use Grep" or references `.claude/plans/`.
## Risks & Gotchas
1. **envsubst touching unintended `$` in agent bodies.** Mitigated by using the scoped form: `envsubst '${PLANS_DIR} ${WEB_SEARCH} ${SEARCH_TOOLS}'`. Only listed variables are substituted. The architect body contains `$` in example YAML blocks, which must NOT be substituted.
2. **YAML frontmatter containing `$`.** The frontmatter is not passed through envsubst -- only the body. The generator extracts frontmatter and body separately, expands only the body, then reassembles.
3. **Skills/rules shared as symlinks -- edit affects both targets immediately.** This is intentional. The skills and rules are tool-agnostic after Wave 1, so sharing is correct. But if someone adds a Claude-specific reference to a shared skill later, it leaks to Codex. The README should document this constraint.
4. **Codex config.toml gets overwritten.** The user's `codex/config.toml` has been manually edited (different content than what generate-codex.sh produces). `generate.sh` will overwrite it. Mitigation: document that `codex/config.toml` is generated and should not be hand-edited. User customizations should go in the source `settings.json`.
5. **install.sh behavior change.** Currently installs directly from `agents/` source. After this change, it installs from `claude/agents/` (generated). Users must run `generate.sh` before `install.sh`. The install script should check for this and error with guidance.
6. **orchestrate skill references `.claude/plans/` paths.** This is acceptable -- orchestrate is Claude-only (not used by Codex). The skill is still shared via the skills directory symlink, but Codex agents don't load it (it's not in their skills list).
## Risk Tags
breaking-change (install.sh workflow changes: generate.sh must run first), data-mutation (generates files to claude/ and codex/ directories)
## Implementation Waves
### Wave 1 -- Make skills and rules tool-agnostic (4 parallel tasks)
These are independent edits to different files. No task depends on another.
- [ ] **Step 1: Template agent bodies** -- Add `${VAR}` placeholders to agent markdown files.
- `agents/architect.md`: Replace `.claude/plans/<kebab-case-title>.md` with `${PLANS_DIR}/<kebab-case-title>.md` (line 19) and `.claude/plans/kebab-case-title.md` with `${PLANS_DIR}/kebab-case-title.md` (line 106). There is also a `.claude/plans/` reference on line 69 inside the orchestrator's resume instruction -- replace that too. Verify no other `.claude/` references exist in the body.
- `agents/reviewer.md`: Replace `via WebFetch/WebSearch` with `${WEB_SEARCH}` (line 33).
- `agents/debugger.md`: Replace `Use Grep to find the relevant code` with `${SEARCH_TOOLS} to find the relevant code` (line 25). Verify the surrounding sentence reads naturally.
- `agents/documenter.md`: Replace `Use Read/Glob/Grep to understand the actual behavior` with `${SEARCH_TOOLS} to understand the actual behavior` (line 29).
- [ ] **Step 2: Make message-schema tool-agnostic** -- Edit `skills/message-schema/SKILL.md`.
- Replace `plan_file: .claude/plans/kebab-case-title.md` with `plan_file: plans/kebab-case-title.md` (lines 155, 205). This is an example value in the schema, not a literal config -- using the generic path is correct.
- Do NOT change the envelope structure or field names.
- [ ] **Step 3: Make skills tool-agnostic** -- Edit skills that contain Claude-specific tool names.
- `skills/worker-protocol/SKILL.md` line 50: Replace `use Read/Glob/Grep directly. Don't guess at file contents — verify.` with `verify by reading the relevant files. Don't guess at file contents.`
- `skills/qa-checklist/SKILL.md` line 13: Replace `Verify with Read/Grep if uncertain.` with `Verify by reading the code if uncertain.`
- `skills/project/SKILL.md` lines 7, 9: Replace `.claude/skills/project.md` with `a project-specific skill file`. Rewrite the two sentences:
- Line 7: "Before starting any work, check for a project-specific skill file in the current working directory's tool configuration."
- Line 9: "If one exists, read it and treat its contents as additional instructions..."
- Do NOT edit `skills/orchestrate/SKILL.md` (stays Claude-only) or `skills/conventions/SKILL.md` (already clean).
- [ ] **Step 4: Make rules tool-agnostic** -- Edit rules with Claude-specific references.
- `rules/01-session.md`: Replace all three occurrences of `.claude/memory/` with `memory/`. Update surrounding prose if needed for clarity. The CLAUDE.md hierarchy reference on line 3 is fine -- it's a generic concept name, not a file path (each tool has its own hierarchy).
- `rules/04-tools.md` line 17: Replace `suggest /clear` with `suggest clearing context or starting a new session`.
### Wave 2 -- Infrastructure (depends on Wave 1 for knowing the final variable list)
- [ ] **Step 5: Update .gitignore and flake.nix** -- Support infrastructure for the new generator.
- `.gitignore`: Add `claude/` line (generated output, same treatment as `codex/`). Keep the existing `codex/` line.
- `flake.nix`: Add `pkgs.gettext` to the devShell packages list (provides `envsubst`). Keep existing `pkgs.yq-go` and `pkgs.codex`.
### Wave 3 -- Generator and installer (depends on Wave 1 for templates, Wave 2 for infrastructure)
- [ ] **Step 6: Write generate.sh** -- New unified generator replacing `generate-codex.sh`.
- Location: `generate.sh` (project root, same level as old `generate-codex.sh`)
- Delete `generate-codex.sh` (or rename -- but deleting is cleaner since the new script fully replaces it)
- **Claude target generation:**
1. Create `claude/agents/` directory
2. For each `agents/*.md`: extract frontmatter and body separately. Run body through `envsubst '${PLANS_DIR} ${WEB_SEARCH} ${SEARCH_TOOLS}'` with Claude variable values. Reassemble frontmatter + expanded body. Write to `claude/agents/<name>.md`.
3. Copy `CLAUDE.md` to `claude/CLAUDE.md`
4. Copy `settings.json` to `claude/settings.json`
5. Create relative symlinks: `claude/rules -> ../rules`, `claude/skills -> ../skills`
- **Codex target generation** (preserve all existing logic from `generate-codex.sh`):
1. Create `codex/agents/` directory
2. For each `agents/*.md`: extract frontmatter and body. Run body through `envsubst '${PLANS_DIR} ${WEB_SEARCH} ${SEARCH_TOOLS}'` with Codex variable values. Convert frontmatter to TOML format (same mapping functions: `map_model`, `map_effort`, `map_sandbox_mode`). Write to `codex/agents/<name>.toml`.
3. Generate `codex/AGENTS.md` from `CLAUDE.md` + rules/*.md (same logic as current `generate-codex.sh`)
4. Generate `codex/config.toml` from `settings.json` (same logic as current)
- **Script structure:**
```bash
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# --- Variable definitions ---
# Claude target
declare -A CLAUDE_VARS=(
[PLANS_DIR]=".claude/plans"
[WEB_SEARCH]="via WebFetch/WebSearch"
[SEARCH_TOOLS]="Use Grep/Glob/Read"
)
# Codex target
declare -A CODEX_VARS=(
[PLANS_DIR]="plans"
[WEB_SEARCH]="via web search"
[SEARCH_TOOLS]="Search the codebase"
)
# --- Shared functions ---
extract_frontmatter() { ... } # yq --front-matter=extract
extract_body() { ... } # awk pattern from current script
expand_body() { ... } # envsubst with scoped variable list
# --- Claude generation ---
generate_claude() { ... }
# --- Codex generation (ported from generate-codex.sh) ---
generate_codex() { ... }
generate_claude
generate_codex
echo "Done. Run ./install.sh to link into tool directories."
```
- **Key detail for envsubst scoping:** The `expand_body` function must export only the target's variables, run envsubst with the explicit variable list, then unset them. This prevents cross-contamination and protects other `$` references in the body.
```bash
expand_body() {
local body="$1"
shift
# Remaining args are KEY=VALUE pairs
local var_list=""
for pair in "$@"; do
local key="${pair%%=*}"
local val="${pair#*=}"
export "$key=$val"
var_list+=" \${$key}"
done
echo "$body" | envsubst "$var_list"
for pair in "$@"; do
unset "${pair%%=*}"
done
}
```
- [ ] **Step 7: Update install.sh** -- Rewire to use generated output.
- **Claude installation:** Change `AGENTS_SRC` from `$SCRIPT_DIR/agents` to `$SCRIPT_DIR/claude/agents`. Change `CLAUDE_MD_SRC` from `$SCRIPT_DIR/CLAUDE.md` to `$SCRIPT_DIR/claude/CLAUDE.md`. Change `SETTINGS_SRC` from `$SCRIPT_DIR/settings.json` to `$SCRIPT_DIR/claude/settings.json`. Skills and rules still symlink from source (they're tool-agnostic and shared): `SKILLS_SRC="$SCRIPT_DIR/skills"`, `RULES_SRC="$SCRIPT_DIR/rules"` (unchanged).
- **Pre-flight check:** At the top of install.sh, verify `claude/` directory exists. If not, print: `"Error: claude/ not found. Run ./generate.sh first."` and exit 1.
- **Codex installation:** Change `codex/agents` source to `$SCRIPT_DIR/codex/agents` (already correct). Keep all other Codex paths the same.
- Preserve the entire symlink helper infrastructure (create_symlink, create_file_symlink, OS detection, backup logic).
### Wave 4 -- Documentation and cleanup (depends on Wave 3)
- [ ] **Step 8: Update README.md** -- Reflect the new workflow.
- Quick install section: add `./generate.sh` before `./install.sh`
- Codex CLI compatibility section: update to reflect `generate.sh` replaces `generate-codex.sh` and now generates both targets
- "What gets generated" table: add Claude row showing `agents/*.md` -> `claude/agents/*.md`
- Add a note about template variables and the tool-agnostic constraint on shared skills/rules
- Remove references to `generate-codex.sh`
## Acceptance Criteria
1. `./generate.sh` produces `claude/agents/*.md` with Claude-specific values expanded (`.claude/plans/`, `Use Grep/Glob/Read`, etc.) -- verified by: grep the generated files for expanded values, confirm no `${` remains
2. `./generate.sh` produces `codex/agents/*.toml` with Codex-specific values expanded (`plans/`, `Search the codebase`, etc.) -- verified by: grep the generated files for expanded values, confirm no `${` remains
3. No Claude-specific tool names (`Read`, `Glob`, `Grep`, `WebFetch`, `WebSearch`, `Edit`, `Write`) appear in skills (except orchestrate) or rules -- verified by: grep shared skills and rules for these tool names
4. No `.claude/` paths appear in skills (except orchestrate) or rules -- verified by: grep shared skills and rules for `.claude/`
5. `./install.sh` errors with a helpful message if `claude/` does not exist -- verified by: manual test
6. `./install.sh` successfully symlinks from `claude/` when it exists -- verified by: manual test
7. Codex output is functionally identical to what `generate-codex.sh` produced (same TOML structure, same model/effort/sandbox mappings) except with Codex-specific values substituted -- verified by: diff old and new codex/ output
8. The `envsubst` expansion does NOT touch `$` characters in YAML frontmatter or example code blocks -- verified by: inspect architect.md generated output for intact `$` in YAML examples
9. `flake.nix` devShell includes `gettext` (provides envsubst) -- verified by: `nix develop -c which envsubst`

3
.gitignore vendored
View file

@ -9,5 +9,6 @@ settings.local.json
.DS_Store
Thumbs.db
# Generated Codex CLI config (derived from Claude source files via generate-codex.sh)
# Generated output (derived from source templates via generate.sh)
claude/
codex/

View file

@ -7,10 +7,12 @@ A portable Claude Code agent team configuration. Clone it, run `install.sh`, and
```bash
git clone <repo-url> ~/agent-team
cd ~/agent-team
./install.sh
nix develop # enter devShell with yq + envsubst
./generate.sh # generate Claude + Codex config from templates
./install.sh # symlinks into ~/.claude/ and ~/.codex/ (if present)
```
The script symlinks `agents/`, `skills/`, `rules/`, `CLAUDE.md`, and `settings.json` into `~/.claude/`. Works on Linux, macOS, and Windows (Git Bash).
The scripts generate configuration for both Claude Code and Codex CLI (if `~/.codex/` exists), then symlink agents, skills, rules, CLAUDE.md, and settings.json into `~/.claude/`. Works on Linux, macOS, and Windows (Git Bash).
## Maintenance
@ -66,19 +68,20 @@ This project also generates configuration for [OpenAI Codex CLI](https://github.
### Setup
```bash
nix develop # enter devShell with yq
./generate-codex.sh # generate Codex config from Claude source files
nix develop # enter devShell with yq + envsubst
./generate.sh # generate Claude + Codex config from templates
./install.sh # installs both Claude and Codex (if ~/.codex exists)
```
### What gets generated
| Source | Generated | Codex location |
| Source | Generated | Location |
|---|---|---|
| `agents/*.md` | `codex/agents/*.toml` | `~/.codex/agents/` |
| `agents/*.md` (templates) | `claude/agents/*.md` | `~/.claude/agents/` |
| `agents/*.md` (templates) | `codex/agents/*.toml` | `~/.codex/agents/` |
| `CLAUDE.md` + `rules/*.md` | `codex/AGENTS.md` | `~/.codex/AGENTS.md` |
| `settings.json` | `codex/config.toml` | `~/.codex/config.toml` |
| `skills/` | (shared as-is) | `~/.agents/skills/` |
| `skills/` | (shared as-is) | `~/.claude/skills/` + `~/.agents/skills/` |
### Model mapping
@ -88,6 +91,18 @@ nix develop # enter devShell with yq
| `sonnet` | `o4-mini` |
| `haiku` | `o4-mini` |
### Template variables
Agent body text uses `${VAR}` placeholders that are expanded per-target by `generate.sh`:
| Variable | Claude | Codex |
|---|---|---|
| `${PLANS_DIR}` | `.claude/plans` | `plans` |
| `${WEB_SEARCH}` | `via WebFetch/WebSearch` | `via web search` |
| `${SEARCH_TOOLS}` | `Use Grep/Glob/Read` | `Search the codebase` |
Skills and rules are tool-agnostic and shared as-is — do not add tool-specific references to them.
## Project-specific config
Each project repo can extend the team with local config in `.claude/`:

View file

@ -16,7 +16,7 @@ You are an architect. You handle the full planning pipeline: triage, architectur
Never implement anything. Never modify source files. Analyze, evaluate, plan.
**Plan persistence:** Always write the approved plan to `.claude/plans/<kebab-case-title>.md`. Never return the plan inline without writing it first. Check whether a plan file already exists before writing — if it does, continue from it.
**Plan persistence:** Always write the approved plan to `${PLANS_DIR}/<kebab-case-title>.md`. Never return the plan inline without writing it first. Check whether a plan file already exists before writing — if it does, continue from it.
Frontmatter format:
```
@ -103,7 +103,7 @@ After writing the plan file, return a `plan_result` envelope:
---
type: plan_result
signal: plan_complete | blocked
plan_file: .claude/plans/kebab-case-title.md
plan_file: ${PLANS_DIR}/kebab-case-title.md
wave_count: 3
step_count: 7
risk_tags:

View file

@ -21,7 +21,7 @@ You are a debugger. Your job is to find the root cause of a bug and apply the mi
Confirm the bug is reproducible before doing anything else. Run the failing test, command, or request. If you cannot reproduce it, say so immediately — do not guess at a fix.
### 2. Isolate
Narrow down where the failure originates. Read the stack trace or error message carefully. Use Grep to find the relevant code. Read the actual code — do not assume you know what it does.
Narrow down where the failure originates. Read the stack trace or error message carefully. ${SEARCH_TOOLS} to find the relevant code. Read the actual code — do not assume you know what it does.
### 3. Hypothesize
Form a specific hypothesis: "The bug is caused by X because Y." State it explicitly before writing any fix. If you have multiple hypotheses, rank them by likelihood.

View file

@ -26,7 +26,7 @@ You are a documentation specialist. Your job is to read code and produce accurat
## How you operate
1. **Read the code first.** Never document what you haven't read. Use Read/Glob/Grep to understand the actual behavior before writing a word.
1. **Read the code first.** Never document what you haven't read. ${SEARCH_TOOLS} to understand the actual behavior before writing a word.
2. **Match existing conventions.** Check for existing docs in the repo — tone, structure, format — and match them. Check `skills/conventions` for project-specific rules.
3. **Be accurate, not aspirational.** Document what the code does, not what it should do. If behavior is unclear, say so — don't invent.
4. **Link, don't duplicate.** Where a concept is already documented elsewhere (official docs, another file), link to it rather than re-explaining.

View file

@ -30,7 +30,7 @@ You are a reviewer. You do two things in one pass: quality review and claim veri
## Claim verification
- **Acceptance criteria** — walk each criterion explicitly by number. Clean code that doesn't do what was asked is a FAIL.
- **API and library usage** — verify against official docs via WebFetch/WebSearch when the implementation uses external APIs, libraries, or non-obvious patterns
- **API and library usage** — verify against official docs ${WEB_SEARCH} when the implementation uses external APIs, libraries, or non-obvious patterns
- **File and path claims** — do they exist?
- **Logic correctness** — does the implementation actually solve the problem?
- **Contradictions** — between worker output and source code, between claims and evidence

View file

@ -7,7 +7,7 @@
in {
devShells = forAllSystems (pkgs: {
default = pkgs.mkShell {
packages = [ pkgs.yq-go pkgs.codex ];
packages = [ pkgs.yq-go pkgs.gettext pkgs.codex ];
};
});
};

View file

@ -1,184 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
# generate-codex.sh — generates Codex CLI config from Claude source files.
# Claude source files are the source of truth; this script derives Codex equivalents.
# Idempotent: safe to run multiple times.
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
CODEX_DIR="$SCRIPT_DIR/codex"
CODEX_AGENTS_DIR="$CODEX_DIR/agents"
AGENTS_SRC="$SCRIPT_DIR/agents"
RULES_DIR="$SCRIPT_DIR/rules"
CLAUDE_MD="$SCRIPT_DIR/CLAUDE.md"
SETTINGS_JSON="$SCRIPT_DIR/settings.json"
# Create output directories
mkdir -p "$CODEX_DIR"
mkdir -p "$CODEX_AGENTS_DIR"
# Clean existing generated agent TOMLs
rm -f "$CODEX_AGENTS_DIR"/*.toml
# ---------------------------------------------------------------------------
# map_model — maps Claude model name to Codex model name
# ---------------------------------------------------------------------------
map_model() {
local model="$1"
case "$model" in
opus) echo "o3" ;;
sonnet) echo "o4-mini" ;;
haiku) echo "o4-mini" ;;
*) echo "o4-mini" ;;
esac
}
# ---------------------------------------------------------------------------
# map_effort — maps Claude effort level to Codex model_reasoning_effort
# ---------------------------------------------------------------------------
map_effort() {
local effort="$1"
case "$effort" in
low) echo "low" ;;
medium) echo "medium" ;;
high) echo "high" ;;
max) echo "xhigh" ;;
*) echo "medium" ;;
esac
}
# ---------------------------------------------------------------------------
# map_sandbox_mode — determines Codex sandbox_mode from agent frontmatter
# $1 = permissionMode value (plan / acceptEdits / "")
# $2 = tools list (comma-separated)
# ---------------------------------------------------------------------------
map_sandbox_mode() {
local permission_mode="$1"
local tools="$2"
# plan mode is read-only
if [ "$permission_mode" = "plan" ]; then
echo "read-only"
return
fi
# acceptEdits with Write or Edit tool → workspace-write
if [ "$permission_mode" = "acceptEdits" ]; then
if echo "$tools" | grep -qE '\b(Write|Edit)\b'; then
echo "workspace-write"
return
fi
fi
# Default: read-only
echo "read-only"
}
# ---------------------------------------------------------------------------
# generate_agent_toml — converts a single agent .md file to Codex .toml
# ---------------------------------------------------------------------------
generate_agent_toml() {
local src_file="$1"
local agent_basename
agent_basename="$(basename "$src_file" .md)"
local dst_file="$CODEX_AGENTS_DIR/${agent_basename}.toml"
# Extract YAML frontmatter using yq
local frontmatter
frontmatter="$(yq --front-matter=extract '.' "$src_file")"
# Extract individual fields from frontmatter
local name description model effort permission_mode tools disallowed_tools
name="$(echo "$frontmatter" | yq '.name // ""')"
description="$(echo "$frontmatter" | yq '.description // ""')"
model="$(echo "$frontmatter" | yq '.model // ""')"
effort="$(echo "$frontmatter" | yq '.effort // ""')"
permission_mode="$(echo "$frontmatter" | yq '.permissionMode // ""')"
tools="$(echo "$frontmatter" | yq '.tools // ""')"
disallowed_tools="$(echo "$frontmatter" | yq '.disallowedTools // ""')"
# Map to Codex equivalents
local codex_model codex_effort codex_sandbox
codex_model="$(map_model "$model")"
codex_effort="$(map_effort "${effort:-medium}")"
codex_sandbox="$(map_sandbox_mode "$permission_mode" "$tools")"
# Extract markdown body (everything after the closing frontmatter ---)
# The frontmatter block starts at line 1 with --- and ends at the second ---
local body
body="$(awk 'BEGIN{fm=0} /^---$/{if(fm==0){fm=1;next} if(fm==1){fm=2;next}} fm==2{print}' "$src_file")"
# Build developer_instructions: append disallowedTools note if present
local developer_instructions
developer_instructions="$body"
if [ -n "$disallowed_tools" ] && [ "$disallowed_tools" != "null" ]; then
developer_instructions="${developer_instructions}
You do NOT have access to these tools: ${disallowed_tools}"
fi
# Write TOML output
cat > "$dst_file" <<TOML
name = "${name}"
description = "${description}"
model = "${codex_model}"
model_reasoning_effort = "${codex_effort}"
sandbox_mode = "${codex_sandbox}"
developer_instructions = """
${developer_instructions}
"""
TOML
echo "Generated: $dst_file"
}
# ---------------------------------------------------------------------------
# Generate agents
# ---------------------------------------------------------------------------
echo "Generating Codex agent definitions..."
for agent_file in "$AGENTS_SRC"/*.md; do
[ -f "$agent_file" ] || continue
generate_agent_toml "$agent_file"
done
# ---------------------------------------------------------------------------
# Generate AGENTS.md — concatenate CLAUDE.md and rules/*.md (sorted)
# ---------------------------------------------------------------------------
echo ""
echo "Generating codex/AGENTS.md..."
{
echo "# Agent Team Instructions"
echo ""
echo "Agent-team specific protocols live in skills (orchestrate, conventions, worker-protocol, qa-checklist, message-schema, project)."
for rules_file in $(ls "$RULES_DIR"/*.md | sort); do
echo ""
cat "$rules_file"
done
} > "$CODEX_DIR/AGENTS.md"
echo "Generated: $CODEX_DIR/AGENTS.md"
# ---------------------------------------------------------------------------
# Generate config.toml — derive sandbox_mode from settings.json defaultMode
# ---------------------------------------------------------------------------
echo ""
echo "Generating codex/config.toml..."
default_mode="$(yq -r '.permissions.defaultMode // "acceptEdits"' "$SETTINGS_JSON")"
# Map Claude defaultMode to Codex sandbox_mode
case "$default_mode" in
plan) config_sandbox="read-only" ;;
acceptEdits) config_sandbox="workspace-write" ;;
*) config_sandbox="workspace-write" ;;
esac
cat > "$CODEX_DIR/config.toml" <<TOML
model = "o4-mini"
model_reasoning_effort = "medium"
sandbox_mode = "${config_sandbox}"
approval_policy = "on-request"
TOML
echo "Generated: $CODEX_DIR/config.toml"
echo ""
echo "Done. Run ./install.sh to link generated files into ~/.codex/"

295
generate.sh Executable file
View file

@ -0,0 +1,295 @@
#!/usr/bin/env bash
set -euo pipefail
# generate.sh — generates both Claude and Codex output directories from
# shared agent source files. Agent source files (agents/*.md) are the
# single source of truth; this script derives tool-specific equivalents.
#
# Template variables in agent bodies are expanded per-target:
# ${PLANS_DIR} — where plans live (.claude/plans vs plans)
# ${WEB_SEARCH} — how web search is referenced
# ${SEARCH_TOOLS} — how codebase search tools are referenced
#
# Idempotent: safe to run multiple times.
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
AGENTS_SRC="$SCRIPT_DIR/agents"
RULES_DIR="$SCRIPT_DIR/rules"
CLAUDE_MD="$SCRIPT_DIR/CLAUDE.md"
SETTINGS_JSON="$SCRIPT_DIR/settings.json"
CLAUDE_DIR="$SCRIPT_DIR/claude"
CLAUDE_AGENTS_DIR="$CLAUDE_DIR/agents"
CODEX_DIR="$SCRIPT_DIR/codex"
CODEX_AGENTS_DIR="$CODEX_DIR/agents"
# ---------------------------------------------------------------------------
# Template variable values per target (KEY=VALUE pairs)
# ---------------------------------------------------------------------------
CLAUDE_VARS=(
"PLANS_DIR=.claude/plans"
"WEB_SEARCH=via WebFetch/WebSearch"
"SEARCH_TOOLS=Use Grep/Glob/Read"
)
CODEX_VARS=(
"PLANS_DIR=plans"
"WEB_SEARCH=via web search"
"SEARCH_TOOLS=Search the codebase"
)
# ---------------------------------------------------------------------------
# extract_body — extracts everything after the second --- (YAML frontmatter)
# ---------------------------------------------------------------------------
extract_body() {
local file="$1"
awk 'BEGIN{fm=0} /^---$/{if(fm==0){fm=1;next} if(fm==1){fm=2;next}} fm==2{print}' "$file"
}
# ---------------------------------------------------------------------------
# extract_frontmatter_block — extracts the raw frontmatter including delimiters
# ---------------------------------------------------------------------------
extract_frontmatter_block() {
local file="$1"
awk 'BEGIN{fm=0} /^---$/{if(fm==0){fm=1;print;next} if(fm==1){print;exit}} fm==1{print}' "$file"
}
# ---------------------------------------------------------------------------
# expand_body — runs envsubst on body text, substituting only our 3 variables
# $1 = body text
# $2.. = KEY=VALUE pairs to export
# ---------------------------------------------------------------------------
expand_body() {
local body="$1"
shift
# Export only the specified variables
for pair in "$@"; do
export "${pair%%=*}=${pair#*=}"
done
echo "$body" | envsubst '${PLANS_DIR} ${WEB_SEARCH} ${SEARCH_TOOLS}'
# Clean up exported variables
for pair in "$@"; do
unset "${pair%%=*}"
done
}
# ---------------------------------------------------------------------------
# map_model — maps Claude model name to Codex model name
# ---------------------------------------------------------------------------
map_model() {
local model="$1"
case "$model" in
opus) echo "o3" ;;
sonnet) echo "o4-mini" ;;
haiku) echo "o4-mini" ;;
*) echo "o4-mini" ;;
esac
}
# ---------------------------------------------------------------------------
# map_effort — maps Claude effort level to Codex model_reasoning_effort
# ---------------------------------------------------------------------------
map_effort() {
local effort="$1"
case "$effort" in
low) echo "low" ;;
medium) echo "medium" ;;
high) echo "high" ;;
max) echo "xhigh" ;;
*) echo "medium" ;;
esac
}
# ---------------------------------------------------------------------------
# map_sandbox_mode — determines Codex sandbox_mode from agent frontmatter
# $1 = permissionMode value (plan / acceptEdits / "")
# $2 = tools list (comma-separated)
# ---------------------------------------------------------------------------
map_sandbox_mode() {
local permission_mode="$1"
local tools="$2"
# plan mode is read-only
if [ "$permission_mode" = "plan" ]; then
echo "read-only"
return
fi
# acceptEdits with Write or Edit tool → workspace-write
if [ "$permission_mode" = "acceptEdits" ]; then
if echo "$tools" | grep -qE '\b(Write|Edit)\b'; then
echo "workspace-write"
return
fi
fi
# Default: read-only
echo "read-only"
}
# ---------------------------------------------------------------------------
# generate_claude — produces claude/ output directory
# ---------------------------------------------------------------------------
generate_claude() {
echo "=== Generating Claude output ==="
# Clean and recreate output directories
rm -rf "$CLAUDE_DIR"
mkdir -p "$CLAUDE_AGENTS_DIR"
# Copy CLAUDE.md
cp "$CLAUDE_MD" "$CLAUDE_DIR/CLAUDE.md"
echo "Copied: $CLAUDE_DIR/CLAUDE.md"
# Copy settings.json
cp "$SETTINGS_JSON" "$CLAUDE_DIR/settings.json"
echo "Copied: $CLAUDE_DIR/settings.json"
# Create relative symlinks for rules and skills
ln -s ../rules "$CLAUDE_DIR/rules"
echo "Symlinked: $CLAUDE_DIR/rules -> ../rules"
ln -s ../skills "$CLAUDE_DIR/skills"
echo "Symlinked: $CLAUDE_DIR/skills -> ../skills"
# Generate agent .md files with expanded template variables
for agent_file in "$AGENTS_SRC"/*.md; do
[ -f "$agent_file" ] || continue
local agent_basename
agent_basename="$(basename "$agent_file")"
local dst_file="$CLAUDE_AGENTS_DIR/$agent_basename"
# Extract frontmatter and body separately
local frontmatter body expanded_body
frontmatter="$(extract_frontmatter_block "$agent_file")"
body="$(extract_body "$agent_file")"
expanded_body="$(expand_body "$body" "${CLAUDE_VARS[@]}")"
# Reassemble: frontmatter + expanded body
{
echo "$frontmatter"
echo "$expanded_body"
} > "$dst_file"
echo "Generated: $dst_file"
done
}
# ---------------------------------------------------------------------------
# generate_codex — produces codex/ output directory
# ---------------------------------------------------------------------------
generate_codex() {
echo ""
echo "=== Generating Codex output ==="
# Clean and recreate output directories
rm -rf "$CODEX_DIR"
mkdir -p "$CODEX_AGENTS_DIR"
# Generate agent .toml files
echo "Generating Codex agent definitions..."
for agent_file in "$AGENTS_SRC"/*.md; do
[ -f "$agent_file" ] || continue
local agent_basename
agent_basename="$(basename "$agent_file" .md)"
local dst_file="$CODEX_AGENTS_DIR/${agent_basename}.toml"
# Extract YAML frontmatter using yq
local frontmatter
frontmatter="$(yq --front-matter=extract '.' "$agent_file")"
# Extract individual fields from frontmatter
local name description model effort permission_mode tools disallowed_tools
name="$(echo "$frontmatter" | yq '.name // ""')"
description="$(echo "$frontmatter" | yq '.description // ""')"
model="$(echo "$frontmatter" | yq '.model // ""')"
effort="$(echo "$frontmatter" | yq '.effort // ""')"
permission_mode="$(echo "$frontmatter" | yq '.permissionMode // ""')"
tools="$(echo "$frontmatter" | yq '.tools // ""')"
disallowed_tools="$(echo "$frontmatter" | yq '.disallowedTools // ""')"
# Map to Codex equivalents
local codex_model codex_effort codex_sandbox
codex_model="$(map_model "$model")"
codex_effort="$(map_effort "${effort:-medium}")"
codex_sandbox="$(map_sandbox_mode "$permission_mode" "$tools")"
# Extract and expand body with Codex variable values
local body expanded_body
body="$(extract_body "$agent_file")"
expanded_body="$(expand_body "$body" "${CODEX_VARS[@]}")"
# Build developer_instructions: append disallowedTools note if present
local developer_instructions
developer_instructions="$expanded_body"
if [ -n "$disallowed_tools" ] && [ "$disallowed_tools" != "null" ]; then
developer_instructions="${developer_instructions}
You do NOT have access to these tools: ${disallowed_tools}"
fi
# Write TOML output
cat > "$dst_file" <<TOML
name = "${name}"
description = "${description}"
model = "${codex_model}"
model_reasoning_effort = "${codex_effort}"
sandbox_mode = "${codex_sandbox}"
developer_instructions = """
${developer_instructions}
"""
TOML
echo "Generated: $dst_file"
done
# Generate AGENTS.md — concatenate rules/*.md with tool-agnostic header
echo ""
echo "Generating codex/AGENTS.md..."
{
echo "# Agent Team Instructions"
echo ""
echo "Agent-team specific protocols live in skills (orchestrate, conventions, worker-protocol, qa-checklist, message-schema, project)."
for rules_file in $(ls "$RULES_DIR"/*.md | sort); do
echo ""
cat "$rules_file"
done
} > "$CODEX_DIR/AGENTS.md"
echo "Generated: $CODEX_DIR/AGENTS.md"
# Generate config.toml — derive sandbox_mode from settings.json defaultMode
echo ""
echo "Generating codex/config.toml..."
local default_mode
default_mode="$(yq -r '.permissions.defaultMode // "acceptEdits"' "$SETTINGS_JSON")"
# Map Claude defaultMode to Codex sandbox_mode
local config_sandbox
case "$default_mode" in
plan) config_sandbox="read-only" ;;
acceptEdits) config_sandbox="workspace-write" ;;
*) config_sandbox="workspace-write" ;;
esac
cat > "$CODEX_DIR/config.toml" <<TOML
model = "o4-mini"
model_reasoning_effort = "medium"
sandbox_mode = "${config_sandbox}"
approval_policy = "on-request"
TOML
echo "Generated: $CODEX_DIR/config.toml"
}
# ---------------------------------------------------------------------------
# Main
# ---------------------------------------------------------------------------
generate_claude
generate_codex
echo ""
echo "Done."

View file

@ -6,15 +6,15 @@ set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
CLAUDE_DIR="$HOME/.claude"
AGENTS_SRC="$SCRIPT_DIR/agents"
AGENTS_SRC="$SCRIPT_DIR/claude/agents"
SKILLS_SRC="$SCRIPT_DIR/skills"
RULES_SRC="$SCRIPT_DIR/rules"
AGENTS_DST="$CLAUDE_DIR/agents"
SKILLS_DST="$CLAUDE_DIR/skills"
RULES_DST="$CLAUDE_DIR/rules"
CLAUDE_MD_SRC="$SCRIPT_DIR/CLAUDE.md"
CLAUDE_MD_SRC="$SCRIPT_DIR/claude/CLAUDE.md"
CLAUDE_MD_DST="$CLAUDE_DIR/CLAUDE.md"
SETTINGS_SRC="$SCRIPT_DIR/settings.json"
SETTINGS_SRC="$SCRIPT_DIR/claude/settings.json"
SETTINGS_DST="$CLAUDE_DIR/settings.json"
# Detect OS
@ -30,6 +30,12 @@ echo "Source: $SCRIPT_DIR"
echo "Target: $CLAUDE_DIR"
echo ""
# Pre-flight: require generated claude/ output before proceeding
if [ ! -d "$SCRIPT_DIR/claude" ]; then
echo "Error: claude/ not found. Run ./generate.sh first."
exit 1
fi
# Ensure ~/.claude exists
mkdir -p "$CLAUDE_DIR"
@ -130,6 +136,11 @@ if [ -d "$CODEX_DIR" ]; then
echo ""
echo "Codex CLI detected at $CODEX_DIR"
# Warn if generated codex/ output is missing
if [ ! -d "$SCRIPT_DIR/codex" ]; then
echo "Warning: codex/ not found. Run ./generate.sh first to generate Codex output."
fi
# Skills shared via ~/.agents/skills/ (Codex discovery path)
mkdir -p "$CODEX_AGENTS_DIR"
create_symlink "$SKILLS_SRC" "$CODEX_AGENTS_DIR/skills" "codex skills"
@ -138,7 +149,7 @@ if [ -d "$CODEX_DIR" ]; then
if [ -d "$SCRIPT_DIR/codex/agents" ]; then
create_symlink "$SCRIPT_DIR/codex/agents" "$CODEX_DIR/agents" "codex agents"
else
echo "Run ./generate-codex.sh first to generate Codex agent definitions"
echo "Run ./generate.sh first to generate Codex agent definitions"
fi
# Generated AGENTS.md (symlink to project root for Codex discovery)

View file

@ -1,12 +1,12 @@
# Session Behavior
- Treat each session as stateless — do not assume context from prior sessions
- The CLAUDE.md hierarchy and `.claude/memory/` are the only sources of persistent context
- The instruction hierarchy and `memory/` are the only sources of persistent context
- If something needs to carry forward across sessions, persist it in the appropriate file — not in session memory
# Project Memory
- Project-specific memory lives in `.claude/memory/` at the project root
- Project-specific memory lives in `memory/` at the project root
- Use `MEMORY.md` in that directory as the index (one line per entry pointing to a file)
- Memory files use frontmatter: `name`, `description`, `type` (user/feedback/project/reference)
- Commit `.claude/memory/` with the repo so memory persists across machines and sessions
- Commit `memory/` with the repo so memory persists across machines and sessions

View file

@ -14,4 +14,4 @@
- Use subagents for exploratory reads and investigations to keep the main context clean
- Prefer scoped file reads (offset/limit) over reading entire large files
- When a task is complete or the topic shifts significantly, suggest /clear
- When a task is complete or the topic shifts significantly, suggest clearing context or starting a new session

View file

@ -151,7 +151,7 @@ Emitted by: architect (Phase 2)
---
type: plan_result
signal: plan_complete | blocked
plan_file: .claude/plans/kebab-case-title.md
plan_file: plans/kebab-case-title.md
wave_count: 3
step_count: 7
risk_tags:
@ -202,7 +202,7 @@ Sent to: worker, debugger, documenter
type: task_assignment
signal: execute
task: "short task title"
plan_file: .claude/plans/kebab-case-title.md
plan_file: plans/kebab-case-title.md
wave: 1
step: 2
---

View file

@ -4,7 +4,7 @@ description: Instructs agents to check for and ingest a project-specific skill f
when_to_use: Loaded by all agents. Checks for .claude/skills/project.md in the working directory and ingests project-local conventions.
---
Before starting any work, check for a project-specific skill file at `.claude/skills/project.md` in the current working directory.
Before starting any work, check for a project-specific skill file in the current working directory's configuration.
If it exists, read it and treat its contents as additional instructions — project conventions, architecture notes, domain context, or anything else the project maintainer has defined. These instructions take precedence over general defaults where they conflict.

View file

@ -9,7 +9,7 @@ when_to_use: Loaded by all agents that produce output envelopes. Run before retu
Before returning your output, validate against every item below. If you find a violation, fix it — don't just note it.
### Factual accuracy
- Every file path, function name, class name, and line number you reference — does it actually exist? Verify with Read/Grep if uncertain. Never guess paths or signatures.
- Every file path, function name, class name, and line number you reference — does it actually exist? Verify by reading the code if uncertain. Never guess paths or signatures.
- Every version number, API endpoint, or external reference — is it correct? If you can't verify, say "unverified" explicitly.
- No invented specifics. If you don't know something, say so.

View file

@ -47,7 +47,7 @@ Before returning your output, run the `qa-checklist` skill against your work. Fi
## Cost sensitivity
- Keep responses tight. Result only.
- Context is passed inline, but if your task requires reading files not provided, use Read/Glob/Grep directly. Don't guess at file contents — verify. Keep it targeted.
- Context is passed inline, but if your task requires reading files not provided, verify by reading the relevant files. Don't guess at file contents. Keep it targeted.
## Commits