From 511076e0597490256f772e60db622cfd001f5002 Mon Sep 17 00:00:00 2001 From: Bryan Ramos Date: Thu, 2 Apr 2026 13:25:34 -0400 Subject: [PATCH] refactor(build): drive generation from shared configs --- .gitignore | 2 + generate.sh | 492 ++++++++++++++++++++++++++++++++++++++++++++------ install.sh | 76 +++++++- settings.json | 54 ------ 4 files changed, 507 insertions(+), 117 deletions(-) delete mode 100644 settings.json diff --git a/.gitignore b/.gitignore index 466ff5b..72c3fc4 100644 --- a/.gitignore +++ b/.gitignore @@ -10,7 +10,9 @@ settings.local.json Thumbs.db # Generated output (derived from source templates via generate.sh) +settings.json claude/ codex/ .claude .codex +.direnv diff --git a/generate.sh b/generate.sh index 60e28e3..abd2123 100755 --- a/generate.sh +++ b/generate.sh @@ -2,8 +2,9 @@ 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. +# shared agent source files plus a vendor-neutral runtime config. +# 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) @@ -17,6 +18,8 @@ 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_SHARED_YAML="$SCRIPT_DIR/SETTINGS.yaml" +TEAM_YAML="$SCRIPT_DIR/TEAM.yaml" SETTINGS_JSON="$SCRIPT_DIR/settings.json" CLAUDE_DIR="$SCRIPT_DIR/claude" @@ -48,14 +51,6 @@ extract_body() { 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 @@ -75,6 +70,301 @@ expand_body() { done } +# --------------------------------------------------------------------------- +# yaml_escape_single_quoted — escapes text for YAML single-quoted scalars +# --------------------------------------------------------------------------- +yaml_escape_single_quoted() { + printf '%s' "$1" | sed "s/'/''/g" +} + +# --------------------------------------------------------------------------- +# csv_from_yaml_array — joins YAML array values from stdin with ", " +# --------------------------------------------------------------------------- +csv_from_yaml_array() { + local first=1 + local item + while IFS= read -r item; do + [ -n "$item" ] || continue + if [ "$first" -eq 0 ]; then + printf ', ' + fi + printf '%s' "$item" + first=0 + done +} + +# --------------------------------------------------------------------------- +# validate_team_protocol — validates TEAM protocol fields and referenced files +# --------------------------------------------------------------------------- +validate_team_protocol() { + [ -f "$TEAM_YAML" ] || { + echo "Error: missing $TEAM_YAML" + exit 1 + } + + yq -e '.version == 1' "$TEAM_YAML" > /dev/null + yq -e '.agents.order and .agents.items and .skills.order and .skills.items and .rules.order and .rules.items' "$TEAM_YAML" > /dev/null + + local section id ids_in_order + for section in agents skills rules; do + while IFS= read -r id; do + [ -n "$id" ] || continue + yq -e ".${section}.items.${id}" "$TEAM_YAML" > /dev/null + [ "$(yq -r ".${section}.items.${id}.id" "$TEAM_YAML")" = "$id" ] || { + echo "Error: TEAM ${section} item '${id}' has mismatched id field" + exit 1 + } + done < <(yq -r ".${section}.order[]" "$TEAM_YAML") + + ids_in_order="$(yq -r ".${section}.order[]" "$TEAM_YAML")" + while IFS= read -r id; do + [ -n "$id" ] || continue + printf '%s\n' "$ids_in_order" | grep -qx "$id" || { + echo "Error: TEAM ${section} item '${id}' missing from order list" + exit 1 + } + done < <(yq -r ".${section}.items | keys | .[]" "$TEAM_YAML") + done + + while IFS= read -r id; do + [ -n "$id" ] || continue + local path + path="$SCRIPT_DIR/$(yq -r ".agents.items.${id}.instruction_file" "$TEAM_YAML")" + [ -f "$path" ] || { + echo "Error: missing agent instruction file for '${id}': $path" + exit 1 + } + done < <(yq -r '.agents.order[]' "$TEAM_YAML") + + while IFS= read -r id; do + [ -n "$id" ] || continue + local path + path="$SCRIPT_DIR/$(yq -r ".skills.items.${id}.instruction_file" "$TEAM_YAML")" + [ -f "$path" ] || { + echo "Error: missing skill instruction file for '${id}': $path" + exit 1 + } + done < <(yq -r '.skills.order[]' "$TEAM_YAML") + + while IFS= read -r id; do + [ -n "$id" ] || continue + local path + path="$SCRIPT_DIR/$(yq -r ".rules.items.${id}.source_file" "$TEAM_YAML")" + [ -f "$path" ] || { + echo "Error: missing rule source file for '${id}': $path" + exit 1 + } + done < <(yq -r '.rules.order[]' "$TEAM_YAML") +} + +# --------------------------------------------------------------------------- +# validate_shared_settings — validates the shared protocol fields we rely on +# --------------------------------------------------------------------------- +validate_shared_settings() { + [ -f "$SETTINGS_SHARED_YAML" ] || { + echo "Error: missing $SETTINGS_SHARED_YAML" + exit 1 + } + + yq -e '.version == 1' "$SETTINGS_SHARED_YAML" > /dev/null + yq -e '.model.class == "fast" or .model.class == "balanced" or .model.class == "powerful"' "$SETTINGS_SHARED_YAML" > /dev/null + yq -e '.model.reasoning == "low" or .model.reasoning == "medium" or .model.reasoning == "high" or .model.reasoning == "max"' "$SETTINGS_SHARED_YAML" > /dev/null + yq -e '.runtime.filesystem == "read-only" or .runtime.filesystem == "workspace-write"' "$SETTINGS_SHARED_YAML" > /dev/null + yq -e '.runtime.approval == "manual" or .runtime.approval == "guarded-auto" or .runtime.approval == "full-auto"' "$SETTINGS_SHARED_YAML" > /dev/null + yq -e '(.runtime.network_access | type) == "!!bool"' "$SETTINGS_SHARED_YAML" > /dev/null + yq -e ' + (.runtime.tools // []) as $tools | + ( + $tools | + map( + select( + . == "shell" or + . == "read" or + . == "edit" or + . == "write" or + . == "glob" or + . == "grep" or + . == "web_fetch" or + . == "web_search" + ) + ) | + length + ) == ($tools | length) + ' "$SETTINGS_SHARED_YAML" > /dev/null +} + +# --------------------------------------------------------------------------- +# map_model_class_to_claude — maps shared model.class to Claude model value +# --------------------------------------------------------------------------- +map_model_class_to_claude() { + local model_class="$1" + case "$model_class" in + fast) echo "haiku" ;; + powerful) echo "opus" ;; + balanced) echo "sonnet" ;; + *) echo "sonnet" ;; + esac +} + +# --------------------------------------------------------------------------- +# map_approval_intent_to_codex_policy — shared approval intent to Codex value +# --------------------------------------------------------------------------- +map_approval_intent_to_codex_policy() { + local approval_intent="$1" + case "$approval_intent" in + manual) echo "on-request" ;; + full-auto) echo "never" ;; + guarded-auto) echo "untrusted" ;; + *) echo "untrusted" ;; + esac +} + +# --------------------------------------------------------------------------- +# map_filesystem_intent_to_claude_mode — shared filesystem to Claude mode +# --------------------------------------------------------------------------- +map_filesystem_intent_to_claude_mode() { + local filesystem="$1" + case "$filesystem" in + read-only) echo "plan" ;; + workspace-write) echo "acceptEdits" ;; + *) echo "acceptEdits" ;; + esac +} + +# --------------------------------------------------------------------------- +# map_portable_tool_to_claude — shared runtime tool to Claude allow-list name +# --------------------------------------------------------------------------- +map_portable_tool_to_claude() { + local tool="$1" + case "$tool" in + shell) echo "Bash" ;; + read) echo "Read" ;; + edit) echo "Edit" ;; + write) echo "Write" ;; + glob) echo "Glob" ;; + grep) echo "Grep" ;; + web_fetch) echo "WebFetch" ;; + web_search) echo "WebSearch" ;; + *) echo "$tool" ;; + esac +} + +# --------------------------------------------------------------------------- +# json_escape — escapes a string for JSON string literal output +# --------------------------------------------------------------------------- +json_escape() { + printf '%s' "$1" | sed 's/\\/\\\\/g; s/"/\\"/g' +} + +# --------------------------------------------------------------------------- +# json_array_from_lines — renders stdin as a compact JSON string array +# --------------------------------------------------------------------------- +json_array_from_lines() { + local first=1 + local item + + printf '[' + while IFS= read -r item; do + [ -n "$item" ] || continue + if [ "$first" -eq 0 ]; then + printf ', ' + fi + printf '"%s"' "$(json_escape "$item")" + first=0 + done + printf ']' +} + +# --------------------------------------------------------------------------- +# generate_legacy_settings_json — emits Claude-compatible settings.json +# from SETTINGS.yaml so downstream generation stays backward-compatible +# --------------------------------------------------------------------------- +generate_legacy_settings_json() { + local model_class model_reasoning runtime_filesystem runtime_approval + local claude_model claude_default_mode codex_approval_policy codex_network_access + local allow_json deny_json ask_json claude_md_excludes_json + + model_class="$(yq -r '.model.class' "$SETTINGS_SHARED_YAML")" + model_reasoning="$(yq -r '.model.reasoning' "$SETTINGS_SHARED_YAML")" + runtime_filesystem="$(yq -r '.runtime.filesystem' "$SETTINGS_SHARED_YAML")" + runtime_approval="$(yq -r '.runtime.approval' "$SETTINGS_SHARED_YAML")" + + claude_model="$(map_model_class_to_claude "$model_class")" + claude_default_mode="$(map_filesystem_intent_to_claude_mode "$runtime_filesystem")" + codex_approval_policy="$(yq -r '.targets.codex.approval_policy // ""' "$SETTINGS_SHARED_YAML")" + codex_network_access="$(yq -r '.targets.codex.network_access // .runtime.network_access // false' "$SETTINGS_SHARED_YAML")" + + if [ -z "$codex_approval_policy" ] || [ "$codex_approval_policy" = "null" ]; then + codex_approval_policy="$(map_approval_intent_to_codex_policy "$runtime_approval")" + fi + + allow_json="$( + yq -r '.runtime.tools[]' "$SETTINGS_SHARED_YAML" \ + | while IFS= read -r tool; do + map_portable_tool_to_claude "$tool" + done \ + | json_array_from_lines + )" + + deny_json="$( + { + yq -r '.safety.protected_paths[]' "$SETTINGS_SHARED_YAML" | while IFS= read -r path; do + printf 'Read(%s)\n' "$path" + printf 'Write(%s)\n' "$path" + printf 'Edit(%s)\n' "$path" + done + } | json_array_from_lines + )" + + ask_json="$( + yq -r '.safety.dangerous_shell_commands.ask[]' "$SETTINGS_SHARED_YAML" \ + | while IFS= read -r cmd; do + printf 'Bash(%s)\n' "$cmd" + done \ + | json_array_from_lines + )" + + claude_md_excludes_json="$( + yq -r '(.targets.claude.claude_md_excludes // [".claude/agent-memory/**"])[]' "$SETTINGS_SHARED_YAML" \ + | json_array_from_lines + )" + + cat > "$SETTINGS_JSON" < ../skills" - # Generate agent .md files with expanded template variables - for agent_file in "$AGENTS_SRC"/*.md; do - [ -f "$agent_file" ] || continue + # Generate agent .md files from TEAM metadata + markdown instruction body + local agent_id + while IFS= read -r agent_id; do + [ -n "$agent_id" ] || continue - local agent_basename - agent_basename="$(basename "$agent_file")" - local dst_file="$CLAUDE_AGENTS_DIR/$agent_basename" + local name description model effort permission_mode + local src_file dst_file body expanded_body + local max_turns background memory isolation + local tools_csv disallowed_tools_csv - # Extract frontmatter and body separately - local frontmatter body expanded_body - frontmatter="$(extract_frontmatter_block "$agent_file")" - body="$(extract_body "$agent_file")" + name="$(yq -r ".agents.items.${agent_id}.name" "$TEAM_YAML")" + description="$(yq -r ".agents.items.${agent_id}.description" "$TEAM_YAML")" + model="$(yq -r ".agents.items.${agent_id}.model" "$TEAM_YAML")" + effort="$(yq -r ".agents.items.${agent_id}.effort // \"\"" "$TEAM_YAML")" + permission_mode="$(yq -r ".agents.items.${agent_id}.permission_mode // \"\"" "$TEAM_YAML")" + tools_csv="$(yq -r ".agents.items.${agent_id}.tools[]" "$TEAM_YAML" | csv_from_yaml_array)" + disallowed_tools_csv="$(yq -r ".agents.items.${agent_id}.disallowed_tools // [] | .[]" "$TEAM_YAML" | csv_from_yaml_array)" + max_turns="$(yq -r ".agents.items.${agent_id}.max_turns // \"\"" "$TEAM_YAML")" + background="$(yq -r ".agents.items.${agent_id}.background // \"\"" "$TEAM_YAML")" + memory="$(yq -r ".agents.items.${agent_id}.memory // \"\"" "$TEAM_YAML")" + isolation="$(yq -r ".agents.items.${agent_id}.isolation // \"\"" "$TEAM_YAML")" + + src_file="$SCRIPT_DIR/$(yq -r ".agents.items.${agent_id}.instruction_file" "$TEAM_YAML")" + dst_file="$CLAUDE_AGENTS_DIR/${name}.md" + + body="$(extract_body "$src_file")" expanded_body="$(expand_body "$body" "${CLAUDE_VARS[@]}")" - # Reassemble: frontmatter + expanded body { - echo "$frontmatter" + echo "---" + echo "name: '$(yaml_escape_single_quoted "$name")'" + echo "description: '$(yaml_escape_single_quoted "$description")'" + echo "model: '$(yaml_escape_single_quoted "$model")'" + if [ -n "$effort" ] && [ "$effort" != "null" ]; then + echo "effort: '$(yaml_escape_single_quoted "$effort")'" + fi + if [ -n "$permission_mode" ] && [ "$permission_mode" != "null" ]; then + echo "permissionMode: '$(yaml_escape_single_quoted "$permission_mode")'" + fi + echo "tools: '$(yaml_escape_single_quoted "$tools_csv")'" + if [ -n "$disallowed_tools_csv" ] && [ "$disallowed_tools_csv" != "null" ]; then + echo "disallowedTools: '$(yaml_escape_single_quoted "$disallowed_tools_csv")'" + fi + if [ "$background" = "true" ]; then + echo "background: true" + fi + if [ -n "$memory" ] && [ "$memory" != "null" ]; then + echo "memory: '$(yaml_escape_single_quoted "$memory")'" + fi + if [ -n "$isolation" ] && [ "$isolation" != "null" ]; then + echo "isolation: '$(yaml_escape_single_quoted "$isolation")'" + fi + if [ -n "$max_turns" ] && [ "$max_turns" != "null" ]; then + echo "maxTurns: $max_turns" + fi + echo "skills:" + yq -r ".agents.items.${agent_id}.skills[]" "$TEAM_YAML" | while IFS= read -r skill; do + echo " - $(yaml_escape_single_quoted "$skill")" + done + echo "---" + echo "" echo "$expanded_body" } > "$dst_file" echo "Generated: $dst_file" - done + done < <(yq -r '.agents.order[]' "$TEAM_YAML") } # --------------------------------------------------------------------------- @@ -189,28 +561,23 @@ generate_codex() { rm -rf "$CODEX_DIR" mkdir -p "$CODEX_AGENTS_DIR" - # Generate agent .toml files + # Generate agent .toml files from TEAM metadata + markdown instruction body echo "Generating Codex agent definitions..." - for agent_file in "$AGENTS_SRC"/*.md; do - [ -f "$agent_file" ] || continue + local agent_id + while IFS= read -r agent_id; do + [ -n "$agent_id" ] || 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 // ""')" + local src_file dst_file + name="$(yq -r ".agents.items.${agent_id}.name" "$TEAM_YAML")" + description="$(yq -r ".agents.items.${agent_id}.description" "$TEAM_YAML")" + model="$(yq -r ".agents.items.${agent_id}.model" "$TEAM_YAML")" + effort="$(yq -r ".agents.items.${agent_id}.effort // \"\"" "$TEAM_YAML")" + permission_mode="$(yq -r ".agents.items.${agent_id}.permission_mode // \"\"" "$TEAM_YAML")" + tools="$(yq -r ".agents.items.${agent_id}.tools[]" "$TEAM_YAML" | csv_from_yaml_array)" + disallowed_tools="$(yq -r ".agents.items.${agent_id}.disallowed_tools // [] | .[]" "$TEAM_YAML" | csv_from_yaml_array)" + src_file="$SCRIPT_DIR/$(yq -r ".agents.items.${agent_id}.instruction_file" "$TEAM_YAML")" + dst_file="$CODEX_AGENTS_DIR/${name}.toml" # Map to Codex equivalents local codex_model codex_effort codex_sandbox @@ -220,7 +587,7 @@ generate_codex() { # Extract and expand body with Codex variable values local body expanded_body - body="$(extract_body "$agent_file")" + body="$(extract_body "$src_file")" expanded_body="$(expand_body "$body" "${CODEX_VARS[@]}")" # Build developer_instructions: append disallowedTools note if present @@ -245,42 +612,48 @@ ${developer_instructions} TOML echo "Generated: $dst_file" - done + done < <(yq -r '.agents.order[]' "$TEAM_YAML") - # Generate AGENTS.md — concatenate rules/*.md with tool-agnostic header + # Generate AGENTS.md — concatenate TEAM-ordered rules 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 "$RULES_DIR"/*.md; do + local rule_id rules_file + while IFS= read -r rule_id; do + [ -n "$rule_id" ] || continue + yq -r ".rules.items.${rule_id}.applies_to[]" "$TEAM_YAML" | grep -qx "codex" || continue + rules_file="$SCRIPT_DIR/$(yq -r ".rules.items.${rule_id}.source_file" "$TEAM_YAML")" echo "" cat "$rules_file" - done + done < <(yq -r '.rules.order[]' "$TEAM_YAML") } > "$CODEX_DIR/AGENTS.md" echo "Generated: $CODEX_DIR/AGENTS.md" - # Generate config.toml — derive sandbox_mode from settings.json defaultMode + # Generate config.toml — derive sandbox/approval defaults from shared config echo "" echo "Generating codex/config.toml..." - local default_mode - default_mode="$(yq -r '.permissions.defaultMode // "acceptEdits"' "$SETTINGS_JSON")" + local default_mode codex_approval_override codex_network_access + default_mode="$(map_filesystem_intent_to_claude_mode "$(yq -r '.runtime.filesystem' "$SETTINGS_SHARED_YAML")")" + codex_approval_override="$(yq -r '.targets.codex.approval_policy // ""' "$SETTINGS_SHARED_YAML")" + codex_network_access="$(yq -r '.targets.codex.network_access // .runtime.network_access // false' "$SETTINGS_SHARED_YAML")" - # 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 + local config_sandbox config_approval + config_sandbox="$(map_default_sandbox_mode "$default_mode")" + config_approval="$(map_approval_policy "$default_mode" "$codex_approval_override")" cat > "$CODEX_DIR/config.toml" < $src" } +# Return one skill id per line for a target platform from TEAM.yaml. +# Falls back to empty output when TEAM.yaml is unavailable. +list_team_skills_for_target() { + local target="$1" + + if [ ! -f "$TEAM_YAML" ]; then + return 0 + fi + + # Validate TEAM parseability before resolving inventory. + yq -e '.version == 1 and has("skills") and (.skills | has("order")) and (.skills | has("items"))' "$TEAM_YAML" > /dev/null + + local skill_id applies + while IFS= read -r skill_id; do + [ -n "$skill_id" ] || continue + applies="$(yq -r ".skills.items.\"$skill_id\".applies_to[]? // \"\"" "$TEAM_YAML")" + if printf '%s\n' "$applies" | grep -Fxq "$target"; then + printf '%s\n' "$skill_id" + fi + done < <(yq -r '.skills.order[]' "$TEAM_YAML") +} + +# Resolve a TEAM skill id to its source directory using instruction_file. +resolve_skill_dir_from_team() { + local skill_id="$1" + + if [ ! -f "$TEAM_YAML" ]; then + return 1 + fi + + local instruction_file skill_dir + instruction_file="$( + yq -r ".skills.items.\"$skill_id\".instruction_file // \"\"" "$TEAM_YAML" + )" + [ -n "$instruction_file" ] || return 1 + + skill_dir="$(dirname "$SCRIPT_DIR/$instruction_file")" + [ -d "$skill_dir" ] || return 1 + + printf '%s\n' "$skill_dir" +} + create_symlink "$AGENTS_SRC" "$AGENTS_DST" "agents" create_symlink "$SKILLS_SRC" "$SKILLS_DST" "skills" create_symlink "$RULES_SRC" "$RULES_DST" "rules" @@ -139,10 +182,35 @@ if [ -d "$SCRIPT_DIR/codex" ]; then # Skills: symlink each skill directory into ~/.codex/skills/ # (Can't replace the whole directory — .system/ must remain intact) mkdir -p "$CODEX_DIR/skills" - for skill_dir in "$SKILLS_SRC"/*/; do - skill_name="$(basename "$skill_dir")" - create_symlink "$skill_dir" "$CODEX_DIR/skills/$skill_name" "codex skill: $skill_name" - done + if [ -f "$TEAM_YAML" ]; then + team_codex_skills_tmp="$(mktemp)" + if ! list_team_skills_for_target "codex" > "$team_codex_skills_tmp" 2>/dev/null; then + echo "Warning: TEAM.yaml exists but could not be parsed; falling back to directory-based Codex skill install." + for skill_dir in "$SKILLS_SRC"/*/; do + skill_name="$(basename "$skill_dir")" + create_symlink "$skill_dir" "$CODEX_DIR/skills/$skill_name" "codex skill: $skill_name" + done + rm -f "$team_codex_skills_tmp" + else + # TEAM is authoritative when present, including the explicit zero-skills case. + while IFS= read -r skill_id; do + [ -n "$skill_id" ] || continue + skill_dir="$(resolve_skill_dir_from_team "$skill_id" || true)" + if [ -z "$skill_dir" ]; then + echo "Warning: TEAM.yaml skill '$skill_id' has no valid instruction_file directory; skipping." + continue + fi + create_symlink "$skill_dir" "$CODEX_DIR/skills/$skill_id" "codex skill: $skill_id" + done < "$team_codex_skills_tmp" + rm -f "$team_codex_skills_tmp" + fi + else + # Legacy fallback only when TEAM.yaml is absent. + for skill_dir in "$SKILLS_SRC"/*/; do + skill_name="$(basename "$skill_dir")" + create_symlink "$skill_dir" "$CODEX_DIR/skills/$skill_name" "codex skill: $skill_name" + done + fi # Generated agents if [ -d "$SCRIPT_DIR/codex/agents" ]; then diff --git a/settings.json b/settings.json deleted file mode 100644 index 582b9a2..0000000 --- a/settings.json +++ /dev/null @@ -1,54 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/claude-code-settings.json", - "attribution": { - "commit": "", - "pr": "" - }, - "permissions": { - "allow": [ - "Bash", - "Read", - "Edit", - "Write", - "Glob", - "Grep", - "WebFetch", - "WebSearch" - ], - "deny": [ - "Read(~/.ssh/**)", - "Read(~/.aws/**)", - "Read(~/.gnupg/**)", - "Read(**/.env*)", - "Write(~/.ssh/**)", - "Write(~/.aws/**)", - "Write(~/.gnupg/**)", - "Write(**/.env*)", - "Edit(~/.ssh/**)", - "Edit(~/.aws/**)", - "Edit(~/.gnupg/**)", - "Edit(**/.env*)" - ], - "ask": [ - "Bash(rm *)", - "Bash(rmdir *)", - "Bash(git push --force*)", - "Bash(git push -f*)", - "Bash(git reset --hard*)", - "Bash(git clean *)", - "Bash(chmod *)", - "Bash(dd *)", - "Bash(mkfs*)", - "Bash(shred *)", - "Bash(kill *)", - "Bash(killall *)", - "Bash(sudo *)" - ], - "defaultMode": "acceptEdits" - }, - "model": "sonnet", - "effortLevel": "medium", - "claudeMdExcludes": [ - ".claude/agent-memory/**" - ] -}