mirror of
https://github.com/itme-brain/agent-team.git
synced 2026-05-08 10:40:12 -04:00
fix
This commit is contained in:
parent
28ab10c58f
commit
7381316e28
12 changed files with 230 additions and 75 deletions
|
|
@ -1,4 +1,4 @@
|
||||||
# Global Claude Code Instructions
|
# Global Claude Code Instructions
|
||||||
|
|
||||||
Rules are modularized in `rules/` and loaded automatically by the generated Claude config.
|
Rules are modularized in `rules/` and loaded automatically by the generated Claude config.
|
||||||
Agent-team specific protocols live in skills (orchestrate, conventions, worker-protocol, qa-checklist, message-schema, project).
|
Agent-team specific protocols live in skills (orchestrate, conventions, worker-protocol, qa-checklist, message-schema).
|
||||||
|
|
|
||||||
16
README.md
16
README.md
|
|
@ -65,7 +65,6 @@ just clean # removes generated artifacts: settings.json + claude/ + cod
|
||||||
| `worker-protocol` | Output format, feedback handling, and operational procedures for worker agents |
|
| `worker-protocol` | Output format, feedback handling, and operational procedures for worker agents |
|
||||||
| `qa-checklist` | Self-validation checklist workers run before returning results |
|
| `qa-checklist` | Self-validation checklist workers run before returning results |
|
||||||
| `message-schema` | Typed YAML frontmatter envelopes for all inter-agent communication |
|
| `message-schema` | Typed YAML frontmatter envelopes for all inter-agent communication |
|
||||||
| `project` | Instructs agents to check for and ingest a project-specific skill file before starting work |
|
|
||||||
|
|
||||||
## Rules
|
## Rules
|
||||||
|
|
||||||
|
|
@ -120,6 +119,7 @@ Runtime policy is documented in [spec/agent-runtime-v1.md](spec/agent-runtime-v1
|
||||||
| `SETTINGS.yaml` | `claude/settings.json` | Claude adapter output |
|
| `SETTINGS.yaml` | `claude/settings.json` | Claude adapter output |
|
||||||
| `SETTINGS.yaml` | `codex/config.toml` | Codex adapter output |
|
| `SETTINGS.yaml` | `codex/config.toml` | Codex adapter output |
|
||||||
| `TEAM.yaml` + `rules/*.md` | `codex/AGENTS.md` | Codex adapter output |
|
| `TEAM.yaml` + `rules/*.md` | `codex/AGENTS.md` | Codex adapter output |
|
||||||
|
| `TEAM.yaml` + `skills/*/SKILL.md` | `codex/skills -> ../skills` | Codex adapter output |
|
||||||
| `TEAM.yaml` + `skills/*/SKILL.md` | installed skill dirs | target install output |
|
| `TEAM.yaml` + `skills/*/SKILL.md` | installed skill dirs | target install output |
|
||||||
|
|
||||||
All final config files are generated artifacts. The authored protocol sources are `SETTINGS.yaml`, `TEAM.yaml`, and Markdown instruction content. The primary workflows are `nix run .#build` / `nix run .#install` or the equivalent `just` commands.
|
All final config files are generated artifacts. The authored protocol sources are `SETTINGS.yaml`, `TEAM.yaml`, and Markdown instruction content. The primary workflows are `nix run .#build` / `nix run .#install` or the equivalent `just` commands.
|
||||||
|
|
@ -129,6 +129,7 @@ Narrow compatibility caveats:
|
||||||
- TEAM schema is intentionally rigid/repo-specific in v1. Inventory changes require schema updates in lockstep.
|
- TEAM schema is intentionally rigid/repo-specific in v1. Inventory changes require schema updates in lockstep.
|
||||||
- Claude generated agent frontmatter is normalized by generator serialization (field order/quoting), which may produce non-semantic diffs.
|
- Claude generated agent frontmatter is normalized by generator serialization (field order/quoting), which may produce non-semantic diffs.
|
||||||
- Codex skill installation is TEAM-authoritative when `TEAM.yaml` is present. Legacy directory fallback is used only when TEAM is absent or unparseable.
|
- Codex skill installation is TEAM-authoritative when `TEAM.yaml` is present. Legacy directory fallback is used only when TEAM is absent or unparseable.
|
||||||
|
- Codex custom-agent files do not preserve every TEAM agent field. `background`, `memory`, and `isolation` have no documented per-agent equivalents in current Codex docs. TEAM `skills` are mapped into per-agent Codex `skills.config` entries.
|
||||||
|
|
||||||
Shared runtime intent is generated conservatively across tools:
|
Shared runtime intent is generated conservatively across tools:
|
||||||
|
|
||||||
|
|
@ -140,7 +141,13 @@ Shared runtime intent is generated conservatively across tools:
|
||||||
| `runtime.approval = guarded-auto` | partially represented | `approval_policy = "untrusted"` |
|
| `runtime.approval = guarded-auto` | partially represented | `approval_policy = "untrusted"` |
|
||||||
| `runtime.approval = full-auto` | partially represented | `approval_policy = "never"` |
|
| `runtime.approval = full-auto` | partially represented | `approval_policy = "never"` |
|
||||||
|
|
||||||
The adapters do not expose identical config surfaces. For example, Codex does not support Claude-style per-tool `allow` / `deny` / `ask` patterns directly. The shared protocol keeps the intent portable, then adapters derive the closest target behavior. Use target-specific fields only where there is no shared equivalent:
|
The adapters do not expose identical config surfaces. For example, Codex does not support Claude-style per-tool `allow` / `deny` / `ask` patterns directly. The shared protocol keeps the intent portable, then adapters derive the closest target behavior.
|
||||||
|
|
||||||
|
`runtime.approval` and `runtime.network_access` are the primary source of truth. `targets.codex.approval_policy` and `targets.codex.network_access` are compatibility overrides for exceptional cases only. When set, they override the Codex-derived value.
|
||||||
|
|
||||||
|
This repo intentionally sets those Codex overrides to `approval_policy: never` and `network_access: true`. The reason is not that Codex has no approval controls at all, but that it lacks Claude-equivalent pattern-level permission controls for tool/path `allow` / `deny` / `ask`. In this repo, Codex therefore runs with a deliberately more permissive top-level policy than the portable runtime defaults.
|
||||||
|
|
||||||
|
Use target-specific fields only when you intentionally need a target-only override:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
targets:
|
targets:
|
||||||
|
|
@ -234,7 +241,6 @@ Each project repo can extend the team with local config in `.claude/`:
|
||||||
|
|
||||||
- `.claude/CLAUDE.md` — project-specific instructions (architecture notes, domain conventions, stack details)
|
- `.claude/CLAUDE.md` — project-specific instructions (architecture notes, domain conventions, stack details)
|
||||||
- `.claude/agents/` — project-local agent overrides or additions
|
- `.claude/agents/` — project-local agent overrides or additions
|
||||||
- `.claude/skills/project.md` — skill file that agents automatically ingest before starting work (see the `project` skill)
|
|
||||||
|
|
||||||
Commit `.claude/` with the project so the team has context wherever it runs.
|
Commit `.claude/` with the project so the team has context wherever it runs.
|
||||||
|
|
||||||
|
|
@ -242,7 +248,7 @@ Commit `.claude/` with the project so the team has context wherever it runs.
|
||||||
|
|
||||||
Two memory systems coexist:
|
Two memory systems coexist:
|
||||||
|
|
||||||
- **Manual memory** (`.claude/memory/`) — curated context files with YAML frontmatter, indexed by `MEMORY.md`. Loaded as part of the CLAUDE.md hierarchy on every session. Use this for project decisions, user preferences, and reference pointers.
|
- **Project memory** (`memory/`) — curated context files with YAML frontmatter, indexed by `MEMORY.md`. This is the portable, instruction-level memory source shared across targets.
|
||||||
- **Agent memory** (`.claude/agent-memory/`) — Claude Code's built-in runtime memory, written automatically by agents with `memory: project` scope. Excluded from CLAUDE.md context via `claudeMdExcludes` to avoid polluting the context window.
|
- **Agent memory** (`.claude/agent-memory/`) — Claude Code's built-in runtime memory, written automatically by agents with `memory: project` scope. Excluded from CLAUDE.md context via `claudeMdExcludes` to avoid polluting the context window.
|
||||||
|
|
||||||
Commit both directories with the repo so memory persists across machines and sessions.
|
Commit both directories when used so memory persists across machines and sessions.
|
||||||
|
|
|
||||||
|
|
@ -47,5 +47,8 @@ targets:
|
||||||
claude_md_excludes:
|
claude_md_excludes:
|
||||||
- .claude/agent-memory/**
|
- .claude/agent-memory/**
|
||||||
codex:
|
codex:
|
||||||
approval_policy: untrusted
|
# Intentional target override: Codex does not expose Claude-equivalent
|
||||||
network_access: false
|
# per-tool/path allow/deny/ask controls, so this repo runs Codex in
|
||||||
|
# full-auto with network enabled by default.
|
||||||
|
approval_policy: never
|
||||||
|
network_access: true
|
||||||
|
|
|
||||||
13
TEAM.yaml
13
TEAM.yaml
|
|
@ -25,9 +25,9 @@ agents:
|
||||||
- Grep
|
- Grep
|
||||||
- WebFetch
|
- WebFetch
|
||||||
- WebSearch
|
- WebSearch
|
||||||
- Bash
|
|
||||||
- Write
|
- Write
|
||||||
disallowed_tools: []
|
disallowed_tools:
|
||||||
|
- Edit
|
||||||
max_turns: 35
|
max_turns: 35
|
||||||
skills:
|
skills:
|
||||||
- conventions
|
- conventions
|
||||||
|
|
@ -39,7 +39,7 @@ agents:
|
||||||
description: Use after implementation — audits for security vulnerabilities and validates runtime behavior. Builds, tests, and probes acceptance criteria. Never modifies code.
|
description: Use after implementation — audits for security vulnerabilities and validates runtime behavior. Builds, tests, and probes acceptance criteria. Never modifies code.
|
||||||
model: sonnet
|
model: sonnet
|
||||||
effort: ""
|
effort: ""
|
||||||
permission_mode: ""
|
permission_mode: acceptEdits
|
||||||
tools:
|
tools:
|
||||||
- Read
|
- Read
|
||||||
- Glob
|
- Glob
|
||||||
|
|
@ -82,17 +82,16 @@ agents:
|
||||||
documenter:
|
documenter:
|
||||||
id: documenter
|
id: documenter
|
||||||
name: documenter
|
name: documenter
|
||||||
description: Use when asked to write or update documentation — READMEs, API references, architecture overviews, inline doc comments, or changelogs. Reads code first, writes accurate docs. Never modifies source code.
|
description: Use when asked to write or update documentation — READMEs, API references, architecture overviews, inline doc comments, or changelogs. Reads code first and updates documentation artifacts only.
|
||||||
model: sonnet
|
model: sonnet
|
||||||
effort: high
|
effort: high
|
||||||
permission_mode: ""
|
permission_mode: acceptEdits
|
||||||
tools:
|
tools:
|
||||||
- Read
|
- Read
|
||||||
- Write
|
- Write
|
||||||
- Edit
|
- Edit
|
||||||
- Glob
|
- Glob
|
||||||
- Grep
|
- Grep
|
||||||
- Bash
|
|
||||||
disallowed_tools: []
|
disallowed_tools: []
|
||||||
max_turns: 20
|
max_turns: 20
|
||||||
skills:
|
skills:
|
||||||
|
|
@ -136,7 +135,6 @@ agents:
|
||||||
- Read
|
- Read
|
||||||
- Glob
|
- Glob
|
||||||
- Grep
|
- Grep
|
||||||
- Bash
|
|
||||||
- WebFetch
|
- WebFetch
|
||||||
- WebSearch
|
- WebSearch
|
||||||
disallowed_tools:
|
disallowed_tools:
|
||||||
|
|
@ -157,7 +155,6 @@ agents:
|
||||||
- Read
|
- Read
|
||||||
- Glob
|
- Glob
|
||||||
- Grep
|
- Grep
|
||||||
- Bash
|
|
||||||
- WebFetch
|
- WebFetch
|
||||||
- WebSearch
|
- WebSearch
|
||||||
disallowed_tools:
|
disallowed_tools:
|
||||||
|
|
|
||||||
33
flake.nix
33
flake.nix
|
|
@ -60,6 +60,22 @@
|
||||||
|
|
||||||
validate(instance=settings_data, schema=settings_schema)
|
validate(instance=settings_data, schema=settings_schema)
|
||||||
validate(instance=team_data, schema=team_schema)
|
validate(instance=team_data, schema=team_schema)
|
||||||
|
|
||||||
|
# TEAM referenced files must exist on disk.
|
||||||
|
for agent_id in team_data["agents"]["order"]:
|
||||||
|
instruction_file = team_data["agents"]["items"][agent_id]["instruction_file"]
|
||||||
|
if not (root / instruction_file).is_file():
|
||||||
|
raise FileNotFoundError(f"Missing agent instruction file: {instruction_file}")
|
||||||
|
|
||||||
|
for skill_id in team_data["skills"]["order"]:
|
||||||
|
instruction_file = team_data["skills"]["items"][skill_id]["instruction_file"]
|
||||||
|
if not (root / instruction_file).is_file():
|
||||||
|
raise FileNotFoundError(f"Missing skill instruction file: {instruction_file}")
|
||||||
|
|
||||||
|
for rule_id in team_data["rules"]["order"]:
|
||||||
|
source_file = team_data["rules"]["items"][rule_id]["source_file"]
|
||||||
|
if not (root / source_file).is_file():
|
||||||
|
raise FileNotFoundError(f"Missing rule source file: {source_file}")
|
||||||
PY
|
PY
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
|
@ -104,6 +120,7 @@
|
||||||
program = "${mkAppScript "install" ''
|
program = "${mkAppScript "install" ''
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
test -f ./install.sh || { echo "Run this command from the repository root."; exit 1; }
|
test -f ./install.sh || { echo "Run this command from the repository root."; exit 1; }
|
||||||
|
${validateCmd}
|
||||||
${bashBin} ./install.sh
|
${bashBin} ./install.sh
|
||||||
''}/bin/install";
|
''}/bin/install";
|
||||||
meta.description = "Install generated artifacts into Claude and Codex config directories.";
|
meta.description = "Install generated artifacts into Claude and Codex config directories.";
|
||||||
|
|
@ -148,6 +165,22 @@
|
||||||
|
|
||||||
validate(instance=settings_data, schema=settings_schema)
|
validate(instance=settings_data, schema=settings_schema)
|
||||||
validate(instance=team_data, schema=team_schema)
|
validate(instance=team_data, schema=team_schema)
|
||||||
|
|
||||||
|
# TEAM referenced files must exist on disk.
|
||||||
|
for agent_id in team_data["agents"]["order"]:
|
||||||
|
instruction_file = team_data["agents"]["items"][agent_id]["instruction_file"]
|
||||||
|
if not (root / instruction_file).is_file():
|
||||||
|
raise FileNotFoundError(f"Missing agent instruction file: {instruction_file}")
|
||||||
|
|
||||||
|
for skill_id in team_data["skills"]["order"]:
|
||||||
|
instruction_file = team_data["skills"]["items"][skill_id]["instruction_file"]
|
||||||
|
if not (root / instruction_file).is_file():
|
||||||
|
raise FileNotFoundError(f"Missing skill instruction file: {instruction_file}")
|
||||||
|
|
||||||
|
for rule_id in team_data["rules"]["order"]:
|
||||||
|
source_file = team_data["rules"]["items"][rule_id]["source_file"]
|
||||||
|
if not (root / source_file).is_file():
|
||||||
|
raise FileNotFoundError(f"Missing rule source file: {source_file}")
|
||||||
PY
|
PY
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
|
|
||||||
55
generate.sh
55
generate.sh
|
|
@ -435,11 +435,11 @@ map_default_sandbox_mode() {
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# map_approval_policy — determines Codex approval_policy from shared config
|
# map_approval_policy — determines Codex approval_policy from shared config
|
||||||
# $1 = Claude permissions.defaultMode value
|
# $1 = runtime.approval value (manual / guarded-auto / full-auto)
|
||||||
# $2 = optional Codex approval override from shared config
|
# $2 = optional Codex approval override from shared config
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
map_approval_policy() {
|
map_approval_policy() {
|
||||||
local default_mode="$1"
|
local runtime_approval="$1"
|
||||||
local override="$2"
|
local override="$2"
|
||||||
|
|
||||||
if [ -n "$override" ] && [ "$override" != "null" ]; then
|
if [ -n "$override" ] && [ "$override" != "null" ]; then
|
||||||
|
|
@ -447,14 +447,7 @@ map_approval_policy() {
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Closest intent mapping:
|
map_approval_intent_to_codex_policy "$runtime_approval"
|
||||||
# - plan remains guarded
|
|
||||||
# - acceptEdits becomes Codex's closest "edit without constant prompts" mode
|
|
||||||
case "$default_mode" in
|
|
||||||
plan) echo "on-request" ;;
|
|
||||||
acceptEdits) echo "untrusted" ;;
|
|
||||||
*) echo "untrusted" ;;
|
|
||||||
esac
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
@ -560,6 +553,8 @@ generate_codex() {
|
||||||
# Clean and recreate output directories
|
# Clean and recreate output directories
|
||||||
rm -rf "$CODEX_DIR"
|
rm -rf "$CODEX_DIR"
|
||||||
mkdir -p "$CODEX_AGENTS_DIR"
|
mkdir -p "$CODEX_AGENTS_DIR"
|
||||||
|
ln -s ../skills "$CODEX_DIR/skills"
|
||||||
|
echo "Symlinked: $CODEX_DIR/skills -> ../skills"
|
||||||
|
|
||||||
# Generate agent .toml files from TEAM metadata + markdown instruction body
|
# Generate agent .toml files from TEAM metadata + markdown instruction body
|
||||||
echo "Generating Codex agent definitions..."
|
echo "Generating Codex agent definitions..."
|
||||||
|
|
@ -568,6 +563,7 @@ generate_codex() {
|
||||||
[ -n "$agent_id" ] || continue
|
[ -n "$agent_id" ] || continue
|
||||||
|
|
||||||
local name description model effort permission_mode tools disallowed_tools
|
local name description model effort permission_mode tools disallowed_tools
|
||||||
|
local agent_skills
|
||||||
local src_file dst_file
|
local src_file dst_file
|
||||||
name="$(yq -r ".agents.items.${agent_id}.name" "$TEAM_YAML")"
|
name="$(yq -r ".agents.items.${agent_id}.name" "$TEAM_YAML")"
|
||||||
description="$(yq -r ".agents.items.${agent_id}.description" "$TEAM_YAML")"
|
description="$(yq -r ".agents.items.${agent_id}.description" "$TEAM_YAML")"
|
||||||
|
|
@ -576,6 +572,7 @@ generate_codex() {
|
||||||
permission_mode="$(yq -r ".agents.items.${agent_id}.permission_mode // \"\"" "$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)"
|
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)"
|
disallowed_tools="$(yq -r ".agents.items.${agent_id}.disallowed_tools // [] | .[]" "$TEAM_YAML" | csv_from_yaml_array)"
|
||||||
|
agent_skills="$(yq -r ".agents.items.${agent_id}.skills[]" "$TEAM_YAML")"
|
||||||
src_file="$SCRIPT_DIR/$(yq -r ".agents.items.${agent_id}.instruction_file" "$TEAM_YAML")"
|
src_file="$SCRIPT_DIR/$(yq -r ".agents.items.${agent_id}.instruction_file" "$TEAM_YAML")"
|
||||||
dst_file="$CODEX_AGENTS_DIR/${name}.toml"
|
dst_file="$CODEX_AGENTS_DIR/${name}.toml"
|
||||||
|
|
||||||
|
|
@ -599,6 +596,13 @@ generate_codex() {
|
||||||
You do NOT have access to these tools: ${disallowed_tools}"
|
You do NOT have access to these tools: ${disallowed_tools}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# TOML multiline basic strings use """ delimiters; reject raw delimiter
|
||||||
|
# sequences in instruction bodies so generated TOML remains parseable.
|
||||||
|
if printf '%s' "$developer_instructions" | grep -q '"""'; then
|
||||||
|
echo "Error: agent instruction contains raw triple quotes (\"\"\") which break TOML in $src_file"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
# Write TOML output
|
# Write TOML output
|
||||||
cat > "$dst_file" <<TOML
|
cat > "$dst_file" <<TOML
|
||||||
name = "${name}"
|
name = "${name}"
|
||||||
|
|
@ -606,11 +610,35 @@ description = "${description}"
|
||||||
model = "${codex_model}"
|
model = "${codex_model}"
|
||||||
model_reasoning_effort = "${codex_effort}"
|
model_reasoning_effort = "${codex_effort}"
|
||||||
sandbox_mode = "${codex_sandbox}"
|
sandbox_mode = "${codex_sandbox}"
|
||||||
|
TOML
|
||||||
|
|
||||||
|
cat >> "$dst_file" <<TOML
|
||||||
developer_instructions = """
|
developer_instructions = """
|
||||||
${developer_instructions}
|
${developer_instructions}
|
||||||
"""
|
"""
|
||||||
TOML
|
TOML
|
||||||
|
|
||||||
|
local skill_id skill_applies enabled
|
||||||
|
while IFS= read -r skill_id; do
|
||||||
|
[ -n "$skill_id" ] || continue
|
||||||
|
skill_applies="$(yq -r ".skills.items.${skill_id}.applies_to[]" "$TEAM_YAML")"
|
||||||
|
if ! printf '%s\n' "$skill_applies" | grep -qx "codex"; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
enabled=false
|
||||||
|
if printf '%s\n' "$agent_skills" | grep -qx "$skill_id"; then
|
||||||
|
enabled=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
cat >> "$dst_file" <<TOML
|
||||||
|
[[skills.config]]
|
||||||
|
path = "../skills/${skill_id}/SKILL.md"
|
||||||
|
enabled = ${enabled}
|
||||||
|
|
||||||
|
TOML
|
||||||
|
done < <(yq -r '.skills.order[]' "$TEAM_YAML")
|
||||||
|
|
||||||
echo "Generated: $dst_file"
|
echo "Generated: $dst_file"
|
||||||
done < <(yq -r '.agents.order[]' "$TEAM_YAML")
|
done < <(yq -r '.agents.order[]' "$TEAM_YAML")
|
||||||
|
|
||||||
|
|
@ -620,7 +648,7 @@ TOML
|
||||||
{
|
{
|
||||||
echo "# Agent Team Instructions"
|
echo "# Agent Team Instructions"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Agent-team specific protocols live in skills (orchestrate, conventions, worker-protocol, qa-checklist, message-schema, project)."
|
echo "Agent-team specific protocols live in skills (orchestrate, conventions, worker-protocol, qa-checklist, message-schema)."
|
||||||
local rule_id rules_file
|
local rule_id rules_file
|
||||||
while IFS= read -r rule_id; do
|
while IFS= read -r rule_id; do
|
||||||
[ -n "$rule_id" ] || continue
|
[ -n "$rule_id" ] || continue
|
||||||
|
|
@ -636,14 +664,15 @@ TOML
|
||||||
echo ""
|
echo ""
|
||||||
echo "Generating codex/config.toml..."
|
echo "Generating codex/config.toml..."
|
||||||
|
|
||||||
local default_mode codex_approval_override codex_network_access
|
local default_mode runtime_approval codex_approval_override codex_network_access
|
||||||
default_mode="$(map_filesystem_intent_to_claude_mode "$(yq -r '.runtime.filesystem' "$SETTINGS_SHARED_YAML")")"
|
default_mode="$(map_filesystem_intent_to_claude_mode "$(yq -r '.runtime.filesystem' "$SETTINGS_SHARED_YAML")")"
|
||||||
|
runtime_approval="$(yq -r '.runtime.approval' "$SETTINGS_SHARED_YAML")"
|
||||||
codex_approval_override="$(yq -r '.targets.codex.approval_policy // ""' "$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")"
|
codex_network_access="$(yq -r '.targets.codex.network_access // .runtime.network_access // false' "$SETTINGS_SHARED_YAML")"
|
||||||
|
|
||||||
local config_sandbox config_approval
|
local config_sandbox config_approval
|
||||||
config_sandbox="$(map_default_sandbox_mode "$default_mode")"
|
config_sandbox="$(map_default_sandbox_mode "$default_mode")"
|
||||||
config_approval="$(map_approval_policy "$default_mode" "$codex_approval_override")"
|
config_approval="$(map_approval_policy "$runtime_approval" "$codex_approval_override")"
|
||||||
|
|
||||||
cat > "$CODEX_DIR/config.toml" <<TOML
|
cat > "$CODEX_DIR/config.toml" <<TOML
|
||||||
#:schema https://developers.openai.com/codex/config-schema.json
|
#:schema https://developers.openai.com/codex/config-schema.json
|
||||||
|
|
|
||||||
143
install.sh
143
install.sh
|
|
@ -11,7 +11,6 @@ SKILLS_SRC="$SCRIPT_DIR/skills"
|
||||||
RULES_SRC="$SCRIPT_DIR/rules"
|
RULES_SRC="$SCRIPT_DIR/rules"
|
||||||
TEAM_YAML="$SCRIPT_DIR/TEAM.yaml"
|
TEAM_YAML="$SCRIPT_DIR/TEAM.yaml"
|
||||||
AGENTS_DST="$CLAUDE_DIR/agents"
|
AGENTS_DST="$CLAUDE_DIR/agents"
|
||||||
SKILLS_DST="$CLAUDE_DIR/skills"
|
|
||||||
RULES_DST="$CLAUDE_DIR/rules"
|
RULES_DST="$CLAUDE_DIR/rules"
|
||||||
CLAUDE_MD_SRC="$SCRIPT_DIR/claude/CLAUDE.md"
|
CLAUDE_MD_SRC="$SCRIPT_DIR/claude/CLAUDE.md"
|
||||||
CLAUDE_MD_DST="$CLAUDE_DIR/CLAUDE.md"
|
CLAUDE_MD_DST="$CLAUDE_DIR/CLAUDE.md"
|
||||||
|
|
@ -31,12 +30,15 @@ echo "Source: $SCRIPT_DIR"
|
||||||
echo "Target: $CLAUDE_DIR"
|
echo "Target: $CLAUDE_DIR"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# Pre-flight: require generated claude/ output before proceeding
|
# Pre-flight: build fresh generated outputs before proceeding.
|
||||||
if [ ! -d "$SCRIPT_DIR/claude" ]; then
|
if [ ! -f "$SCRIPT_DIR/generate.sh" ]; then
|
||||||
echo "Error: claude/ not found. Run ./generate.sh first."
|
echo "Error: generate.sh not found."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
echo "Generating fresh artifacts before install..."
|
||||||
|
bash "$SCRIPT_DIR/generate.sh"
|
||||||
|
|
||||||
# Ensure ~/.claude exists
|
# Ensure ~/.claude exists
|
||||||
mkdir -p "$CLAUDE_DIR"
|
mkdir -p "$CLAUDE_DIR"
|
||||||
|
|
||||||
|
|
@ -82,6 +84,22 @@ create_symlink() {
|
||||||
echo "Linked: $dst -> $src"
|
echo "Linked: $dst -> $src"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ensure_directory() {
|
||||||
|
local dst="$1"
|
||||||
|
local name="$2"
|
||||||
|
|
||||||
|
if [ -L "$dst" ]; then
|
||||||
|
echo "Removing existing symlink: $dst"
|
||||||
|
rm "$dst"
|
||||||
|
elif [ -f "$dst" ]; then
|
||||||
|
local backup="${dst}.backup.$(date +%Y%m%d%H%M%S)"
|
||||||
|
echo "Backing up existing $name file to: $backup"
|
||||||
|
mv "$dst" "$backup"
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$dst"
|
||||||
|
}
|
||||||
|
|
||||||
# Symlink a single file
|
# Symlink a single file
|
||||||
create_file_symlink() {
|
create_file_symlink() {
|
||||||
local src="$1"
|
local src="$1"
|
||||||
|
|
@ -165,11 +183,95 @@ resolve_skill_dir_from_team() {
|
||||||
printf '%s\n' "$skill_dir"
|
printf '%s\n' "$skill_dir"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
install_team_skills_for_target() {
|
||||||
|
local target="$1"
|
||||||
|
local dst_root="$2"
|
||||||
|
local label_prefix="$3"
|
||||||
|
|
||||||
|
ensure_directory "$dst_root" "$label_prefix skills"
|
||||||
|
|
||||||
|
local skill_dir skill_name skill_dst skill_id skill_dir_path
|
||||||
|
local expected_skills_tmp
|
||||||
|
expected_skills_tmp="$(mktemp)"
|
||||||
|
|
||||||
|
cleanup_skill_symlinks() {
|
||||||
|
local expected_file="$1"
|
||||||
|
local existing_path existing_name
|
||||||
|
|
||||||
|
for existing_path in "$dst_root"/*; do
|
||||||
|
[ -e "$existing_path" ] || [ -L "$existing_path" ] || continue
|
||||||
|
[ -L "$existing_path" ] || continue
|
||||||
|
|
||||||
|
existing_name="$(basename "$existing_path")"
|
||||||
|
if [ -s "$expected_file" ] && grep -Fxq "$existing_name" "$expected_file"; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Removing stale symlink: $existing_path"
|
||||||
|
rm "$existing_path"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ -f "$TEAM_YAML" ]; then
|
||||||
|
local team_skills_tmp
|
||||||
|
team_skills_tmp="$(mktemp)"
|
||||||
|
if ! list_team_skills_for_target "$target" > "$team_skills_tmp" 2>/dev/null; then
|
||||||
|
echo "Warning: TEAM.yaml exists but could not be parsed; falling back to directory-based ${label_prefix} skill install."
|
||||||
|
for skill_dir in "$SKILLS_SRC"/*/; do
|
||||||
|
skill_name="$(basename "$skill_dir")"
|
||||||
|
printf '%s\n' "$skill_name" >> "$expected_skills_tmp"
|
||||||
|
done
|
||||||
|
cleanup_skill_symlinks "$expected_skills_tmp"
|
||||||
|
for skill_dir in "$SKILLS_SRC"/*/; do
|
||||||
|
skill_name="$(basename "$skill_dir")"
|
||||||
|
create_symlink "$skill_dir" "$dst_root/$skill_name" "${label_prefix} skill: $skill_name"
|
||||||
|
done
|
||||||
|
rm -f "$team_skills_tmp"
|
||||||
|
rm -f "$expected_skills_tmp"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
while IFS= read -r skill_id; do
|
||||||
|
[ -n "$skill_id" ] || continue
|
||||||
|
printf '%s\n' "$skill_id" >> "$expected_skills_tmp"
|
||||||
|
done < "$team_skills_tmp"
|
||||||
|
|
||||||
|
cleanup_skill_symlinks "$expected_skills_tmp"
|
||||||
|
|
||||||
|
while IFS= read -r skill_id; do
|
||||||
|
[ -n "$skill_id" ] || continue
|
||||||
|
skill_dir_path="$(resolve_skill_dir_from_team "$skill_id" || true)"
|
||||||
|
if [ -z "$skill_dir_path" ]; then
|
||||||
|
echo "Warning: TEAM.yaml skill '$skill_id' has no valid instruction_file directory; skipping."
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
create_symlink "$skill_dir_path" "$dst_root/$skill_id" "${label_prefix} skill: $skill_id"
|
||||||
|
done < "$team_skills_tmp"
|
||||||
|
rm -f "$team_skills_tmp"
|
||||||
|
rm -f "$expected_skills_tmp"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
for skill_dir in "$SKILLS_SRC"/*/; do
|
||||||
|
skill_name="$(basename "$skill_dir")"
|
||||||
|
printf '%s\n' "$skill_name" >> "$expected_skills_tmp"
|
||||||
|
done
|
||||||
|
|
||||||
|
cleanup_skill_symlinks "$expected_skills_tmp"
|
||||||
|
|
||||||
|
for skill_dir in "$SKILLS_SRC"/*/; do
|
||||||
|
skill_name="$(basename "$skill_dir")"
|
||||||
|
create_symlink "$skill_dir" "$dst_root/$skill_name" "${label_prefix} skill: $skill_name"
|
||||||
|
done
|
||||||
|
|
||||||
|
rm -f "$expected_skills_tmp"
|
||||||
|
}
|
||||||
|
|
||||||
create_symlink "$AGENTS_SRC" "$AGENTS_DST" "agents"
|
create_symlink "$AGENTS_SRC" "$AGENTS_DST" "agents"
|
||||||
create_symlink "$SKILLS_SRC" "$SKILLS_DST" "skills"
|
|
||||||
create_symlink "$RULES_SRC" "$RULES_DST" "rules"
|
create_symlink "$RULES_SRC" "$RULES_DST" "rules"
|
||||||
create_file_symlink "$CLAUDE_MD_SRC" "$CLAUDE_MD_DST" "CLAUDE.md"
|
create_file_symlink "$CLAUDE_MD_SRC" "$CLAUDE_MD_DST" "CLAUDE.md"
|
||||||
create_file_symlink "$SETTINGS_SRC" "$SETTINGS_DST" "settings.json"
|
create_file_symlink "$SETTINGS_SRC" "$SETTINGS_DST" "settings.json"
|
||||||
|
install_team_skills_for_target "claude" "$CLAUDE_DIR/skills" "claude"
|
||||||
|
|
||||||
# Codex CLI integration (optional — only if codex/ output exists)
|
# Codex CLI integration (optional — only if codex/ output exists)
|
||||||
CODEX_DIR="$HOME/.codex"
|
CODEX_DIR="$HOME/.codex"
|
||||||
|
|
@ -181,36 +283,7 @@ if [ -d "$SCRIPT_DIR/codex" ]; then
|
||||||
|
|
||||||
# Skills: symlink each skill directory into ~/.codex/skills/
|
# Skills: symlink each skill directory into ~/.codex/skills/
|
||||||
# (Can't replace the whole directory — .system/ must remain intact)
|
# (Can't replace the whole directory — .system/ must remain intact)
|
||||||
mkdir -p "$CODEX_DIR/skills"
|
install_team_skills_for_target "codex" "$CODEX_DIR/skills" "codex"
|
||||||
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
|
# Generated agents
|
||||||
if [ -d "$SCRIPT_DIR/codex/agents" ]; then
|
if [ -d "$SCRIPT_DIR/codex/agents" ]; then
|
||||||
|
|
|
||||||
|
|
@ -10,3 +10,4 @@
|
||||||
- Use `MEMORY.md` in that directory as the index (one line per entry pointing to a file)
|
- 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)
|
- Memory files use frontmatter: `name`, `description`, `type` (user/feedback/project/reference)
|
||||||
- Commit `memory/` with the repo so memory persists across machines and sessions
|
- Commit `memory/` with the repo so memory persists across machines and sessions
|
||||||
|
- Tool-specific runtime memory (for example `.claude/agent-memory/`) is optional and does not replace `memory/` as the project source of truth
|
||||||
|
|
|
||||||
|
|
@ -149,10 +149,12 @@
|
||||||
"on-request",
|
"on-request",
|
||||||
"untrusted",
|
"untrusted",
|
||||||
"never"
|
"never"
|
||||||
]
|
],
|
||||||
|
"description": "Codex-only compatibility override. Prefer runtime.approval as the portable source of truth."
|
||||||
},
|
},
|
||||||
"network_access": {
|
"network_access": {
|
||||||
"type": "boolean"
|
"type": "boolean",
|
||||||
|
"description": "Codex-only compatibility override. Prefer runtime.network_access as the portable source of truth."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -367,7 +367,7 @@
|
||||||
"inventory_skills": {
|
"inventory_skills": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"description": "Skill inventory for protocol v1. This schema enforces exact order, exact keys, and key/id equality for the current repository inventory.",
|
"description": "Skill inventory for protocol v1. This schema enforces exact order, exact keys, and key/id equality for the current repository inventory. The fixed v1 inventory does not include a separate 'project' skill entry.",
|
||||||
"required": [
|
"required": [
|
||||||
"order",
|
"order",
|
||||||
"items"
|
"items"
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ Version 1 standardizes:
|
||||||
- portable tool classes
|
- portable tool classes
|
||||||
- protected path rules
|
- protected path rules
|
||||||
- dangerous shell command prompts
|
- dangerous shell command prompts
|
||||||
- target-specific escape hatches only when the target exposes settings with no shared equivalent
|
- a narrow set of target-specific escape hatches for compatibility overrides
|
||||||
|
|
||||||
Version 1 does not attempt to standardize:
|
Version 1 does not attempt to standardize:
|
||||||
|
|
||||||
|
|
@ -50,13 +50,20 @@ Version 1 does not attempt to standardize:
|
||||||
|
|
||||||
### `targets`
|
### `targets`
|
||||||
|
|
||||||
Target blocks are escape hatches, not the main schema. Use them only where a runtime exposes a knob with no shared equivalent.
|
Target blocks are escape hatches, not the main schema.
|
||||||
|
|
||||||
Current target-specific fields:
|
Current target-specific fields:
|
||||||
|
|
||||||
- `targets.claude.claude_md_excludes`
|
- `targets.claude.claude_md_excludes`
|
||||||
- `targets.codex.approval_policy`
|
- `targets.codex.approval_policy` (optional override of derived approval)
|
||||||
- `targets.codex.network_access`
|
- `targets.codex.network_access` (optional override of derived network access)
|
||||||
|
|
||||||
|
Authority rules:
|
||||||
|
|
||||||
|
- `runtime.approval` and `runtime.network_access` are the portable source of truth.
|
||||||
|
- Codex target fields exist for explicit compatibility overrides and should normally be omitted.
|
||||||
|
- When Codex target fields are set, they intentionally override the derived Codex value.
|
||||||
|
- In this repo, `targets.codex.approval_policy` and `targets.codex.network_access` are intentionally set so Codex runs with `approval_policy = "never"` and network enabled by default. This is a deliberate target-specific compatibility choice, not an accidental divergence.
|
||||||
|
|
||||||
## Adapter rules
|
## Adapter rules
|
||||||
|
|
||||||
|
|
@ -81,15 +88,16 @@ Lossiness:
|
||||||
|
|
||||||
- `runtime.filesystem = read-only` -> `sandbox_mode = "read-only"`
|
- `runtime.filesystem = read-only` -> `sandbox_mode = "read-only"`
|
||||||
- `runtime.filesystem = workspace-write` -> `sandbox_mode = "workspace-write"`
|
- `runtime.filesystem = workspace-write` -> `sandbox_mode = "workspace-write"`
|
||||||
- `runtime.approval = manual` -> `approval_policy = "on-request"`
|
- `runtime.approval = manual` -> `approval_policy = "on-request"` (unless overridden)
|
||||||
- `runtime.approval = guarded-auto` -> `approval_policy = "untrusted"`
|
- `runtime.approval = guarded-auto` -> `approval_policy = "untrusted"` (unless overridden)
|
||||||
- `runtime.approval = full-auto` -> `approval_policy = "never"`
|
- `runtime.approval = full-auto` -> `approval_policy = "never"` (unless overridden)
|
||||||
- `runtime.network_access` -> `[sandbox_workspace_write].network_access`
|
- `runtime.network_access` -> `[sandbox_workspace_write].network_access`
|
||||||
|
|
||||||
Lossiness:
|
Lossiness:
|
||||||
|
|
||||||
- Codex does not expose Claude-style per-tool `allow` / `deny` / `ask` pattern controls in `config.toml`.
|
- Codex does not expose Claude-style per-tool `allow` / `deny` / `ask` pattern controls in `config.toml`.
|
||||||
- Protected paths and dangerous command prompts are therefore only partially representable in Codex config today.
|
- Protected paths and dangerous command prompts are therefore only partially representable in Codex config today.
|
||||||
|
- Codex does expose coarse approval controls, including `approval_policy` and documented granular approval categories, but not the same pattern-level permission model Claude exposes.
|
||||||
|
|
||||||
## Compatibility contract
|
## Compatibility contract
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -79,9 +79,10 @@ Each agent entry includes metadata required for adapter generation:
|
||||||
Each skill entry includes lightweight metadata and content reference:
|
Each skill entry includes lightweight metadata and content reference:
|
||||||
|
|
||||||
- `id`
|
- `id`
|
||||||
|
- `name`
|
||||||
- `description`
|
- `description`
|
||||||
- `instruction_file`
|
- `instruction_file`
|
||||||
- optional target/install metadata
|
- target/install metadata (`applies_to`, `install_mode`)
|
||||||
|
|
||||||
Skill prose remains in `skills/*/SKILL.md`.
|
Skill prose remains in `skills/*/SKILL.md`.
|
||||||
|
|
||||||
|
|
@ -110,6 +111,7 @@ Current target behavior:
|
||||||
- `codex/config.toml`
|
- `codex/config.toml`
|
||||||
- `codex/AGENTS.md`
|
- `codex/AGENTS.md`
|
||||||
- `codex/agents/*.toml`
|
- `codex/agents/*.toml`
|
||||||
|
- `codex/skills` symlinked to the shared skill directories for relative `skills.config` references
|
||||||
|
|
||||||
## Validation Requirements
|
## Validation Requirements
|
||||||
|
|
||||||
|
|
@ -128,6 +130,7 @@ TEAM validation enforces schema + runtime checks for:
|
||||||
- Existing YAML frontmatter in `agents/*.md` may remain for editorial continuity, but generation does not use it for team metadata.
|
- Existing YAML frontmatter in `agents/*.md` may remain for editorial continuity, but generation does not use it for team metadata.
|
||||||
- Output diffs that are purely formatting-related are acceptable; semantic behavior changes are not unless explicitly documented.
|
- Output diffs that are purely formatting-related are acceptable; semantic behavior changes are not unless explicitly documented.
|
||||||
- TEAM schema is intentionally rigid/repo-specific in v1; inventory additions/removals require schema updates in lockstep.
|
- TEAM schema is intentionally rigid/repo-specific in v1; inventory additions/removals require schema updates in lockstep.
|
||||||
|
- Agent metadata is not fully portable across targets. Current Codex custom-agent docs cover session-style fields such as `model`, `model_reasoning_effort`, `sandbox_mode`, `mcp_servers`, and `skills.config`, but do not document per-agent equivalents for TEAM's `background`, `memory`, or `isolation` fields.
|
||||||
|
|
||||||
## Out of Scope
|
## Out of Scope
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue