refactor(generate): port generate.sh to Python

Replace 960-line bash script with ~680-line Python module that leans on
pyyaml and jsonschema instead of shelling to yq/jq/awk/sed/envsubst for every
field. Ecosystem dependencies pinned through the existing flake pythonEnv.

Motivation: the generator had outgrown bash. Recent bugs (awk frontmatter
state machine eating markdown ---, envsubst variable scope, shell quoting in
nested heredocs, multi-section rule surgery) were all classic bash pitfalls
that don't exist in Python.

Design notes:
- Uses pyyaml for TEAM.yaml / SETTINGS.yaml parsing.
- Uses jsonschema to validate both inside the generator (previously only in
  flake.nix's embedded Python block).
- Does NOT use python-frontmatter because its content-stripping drops
  leading blank lines that matter for byte-level parity with bash output.
  Replaced with a 6-line fence-split that preserves whitespace exactly.
- Does NOT use tomli-w because it can't emit multiline-basic-string
  ("\"\"\"...\"\"\"") literals — it would escape every newline in the
  developer_instructions body onto a single line, destroying readability.
  Codex TOML output is hand-built with a documented comment.
- Opencode skill pool now symlinks per-skill based on applies_to instead
  of a blanket symlink, honoring TEAM.yaml's skill filtering.

Verified: snapshotted generated outputs before the port and diffed after.
All of claude/, codex/, opencode/ are byte-identical to baseline except
claude/settings.json, which now uses json.dumps(indent=2) multi-line arrays
instead of hand-built compact arrays — confirmed semantically identical via
json.load comparison.

flake.nix, install.sh, README.md, .gitignore updated to reference
generate.py instead of generate.sh. generate.sh deleted.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Bryan Ramos 2026-04-14 11:12:05 -04:00
parent 26d004fe46
commit 590145c714
11 changed files with 699 additions and 952 deletions

View file

@ -31,13 +31,13 @@ echo "Target: $CLAUDE_DIR"
echo ""
# Pre-flight: build fresh generated outputs before proceeding.
if [ ! -f "$SCRIPT_DIR/generate.sh" ]; then
echo "Error: generate.sh not found."
if [ ! -f "$SCRIPT_DIR/generate.py" ]; then
echo "Error: generate.py not found."
exit 1
fi
echo "Generating fresh artifacts before install..."
bash "$SCRIPT_DIR/generate.sh"
python "$SCRIPT_DIR/generate.py"
# Ensure ~/.claude exists
mkdir -p "$CLAUDE_DIR"
@ -289,7 +289,7 @@ if [ -d "$SCRIPT_DIR/codex" ]; then
if [ -d "$SCRIPT_DIR/codex/agents" ]; then
create_symlink "$SCRIPT_DIR/codex/agents" "$CODEX_DIR/agents" "codex agents"
else
echo "Run ./generate.sh first to generate Codex agent definitions"
echo "Run ./generate.py first to generate Codex agent definitions"
fi
# Generated AGENTS.md (symlink to project root for Codex discovery)
@ -318,7 +318,7 @@ if [ -d "$SCRIPT_DIR/opencode" ]; then
if [ -d "$SCRIPT_DIR/opencode/agents" ]; then
create_symlink "$SCRIPT_DIR/opencode/agents" "$OPENCODE_CONFIG_DIR/agents" "opencode agents"
else
echo "Run ./generate.sh first to generate OpenCode agent definitions"
echo "Run ./generate.py first to generate OpenCode agent definitions"
fi
# Generated AGENTS.md