references/mode-clone.md
# team-setup reference: Mode 1 — Clone from GitHub
Part of the `team-setup` skill. Load this file only when the user chose Mode 1 (clone an existing team-ai-directives repository from GitHub).
Shared context lives in `SKILL.md` (Goal, Input Validation, Mode Selection Flow, Post-Setup checklist).
---
### Mode 1: Clone from GitHub
Clone an existing team-ai-directives repository from GitHub.
**Explore**:
1. Ask the user for the GitHub repository URL (default: `https://github.com/tikalk/agentic-sdlc-team-ai-directives`)
2. Validate the URL starts with `https://` (reject `file://`, `ssh://`, and other schemes — see Input Validation). Only clone repositories you trust; the cloned content is read by agents later.
3. Ask where to clone it (default: `./team-ai-directives`)
4. Check that the destination does not already exist
**Present**:
Show the user:
- Source URL
- Destination path
- Estimated size (from remote repo info if available)
**Confirm**:
```
Clone team-ai-directives from {URL} to {DEST}?
[Y/n]
```
**Write/Execute**:
```bash
git clone "{URL}" "{DEST}"
```
After clone, verify the team AI directives structure exists:
- `{DEST}/context_modules/constitution.md`
- `{DEST}/context_modules/rules/`
- `{DEST}/context_modules/personas/`
- `{DEST}/context_modules/examples/`
- `{DEST}/CDR.md`
- `{DEST}/.skills.json`
Verify the `adlc` orphan branch exists (for CDR drafts and usage reports), create if missing:
```bash
git -C "{DEST}" show-ref --verify --quiet refs/heads/adlc || {
cd "{DEST}"
git checkout --orphan adlc
mkdir -p drafts/cdr reports/sessions reports/projects
echo '{}' > reports/confidence-scores.json
touch drafts/cdr/.gitkeep reports/sessions/.gitkeep reports/projects/.gitkeep
git add -A && git commit -m "Initialize adlc orphan branch (drafts + reports)"
git checkout main
}
```
*On success: complete Post-Setup Configuration — write `team_ai_directives` to `.adlc/init-options.json`, run the health check (`SKILL.md`), check the `.gitignore` convention (Gitignore Convention Check, `SKILL.md`), inject `AGENTS.md` (`references/post-setup-agents.md`), install MCP config (`references/post-setup-mcp.md`).*
references/mode-configured.md
# team-setup reference: Mode 4 — Already Configured
Part of the `team-setup` skill. Load this file only when the project is already wired (verify and report status, then run the MCP config install).
Shared context lives in `SKILL.md` (Goal, Input Validation, Mode Selection Flow, Post-Setup checklist).
---
### Mode 4: Already Configured
The team AI directives is already configured. Verify and report status.
**Explore**:
1. Check `.adlc/init-options.json` for `team_ai_directives` field
2. If found, resolve the path and validate the team AI directives structure
3. Check `TEAM_AI_DIRECTIVES` env var as fallback
4. Check default path `team-ai-directives` as final fallback
**Present**:
Show the user the resolved team AI directives path and validation results.
**Write/Execute**:
No writes needed — the team AI directives is already configured. Then:
1. Run the **Gitignore Convention Check** (Post-Setup Configuration step 3 in
`SKILL.md`): verify `.gitignore` carries the layout allowlist and not a
wholesale `.adlc/` ignore; add missing rules when absent.
2. Run the **MCP config install** (Post-Setup Configuration step 5): copy the
team `.mcp.json` into the project only if absent — an existing project file
wins and is never merged into; report the gap instead.
*On success: complete Post-Setup Configuration — write `team_ai_directives` to `.adlc/init-options.json`, run the health check (`SKILL.md`), check the `.gitignore` convention (Gitignore Convention Check, `SKILL.md`), inject `AGENTS.md` (`references/post-setup-agents.md`), install MCP config (`references/post-setup-mcp.md`).*
references/mode-local.md
# team-setup reference: Mode 2 — Point to Existing Local Path
Part of the `team-setup` skill. Load this file only when the user chose Mode 2 (wire an existing local team-ai-directives directory).
Shared context lives in `SKILL.md` (Goal, Input Validation, Mode Selection Flow, Post-Setup checklist).
---
### Mode 2: Point to Existing Local Path
Wire an existing local team-ai-directives directory into the project.
**Explore**:
1. Ask the user for the path to their existing team AI directives directory
2. Validate the path exists
3. Validate the team AI directives structure (same checks as Mode 1 post-clone)
4. If validation fails, explain what's missing and ask the user to fix it or choose a different mode
**Present**:
Show the user:
- Resolved absolute path
- Validation results (which required files/dirs exist and which are missing)
**Confirm**:
```
Use existing team-ai-directives at {ABSOLUTE_PATH}?
[Y/n]
```
**Write/Execute**:
Update the project's `.adlc/init-options.json` to set the `team_ai_directives` field to the resolved path. Uses `jq` for safe JSON manipulation — never interpolate user input into shell source.
```bash
# Resolve to an absolute path and validate (see Input Validation)
ABSOLUTE_PATH="$(realpath "$USER_PATH")"
# Write config using jq (merge into existing or create new)
if [ -f ".adlc/init-options.json" ]; then
jq --arg p "$ABSOLUTE_PATH" '. + {team_ai_directives: $p}' ".adlc/init-options.json" > ".adlc/init-options.json.tmp" && mv ".adlc/init-options.json.tmp" ".adlc/init-options.json"
else
jq -n --arg p "$ABSOLUTE_PATH" '{team_ai_directives: $p}' > ".adlc/init-options.json"
fi
```
Ensure the `adlc` orphan branch exists (create if missing):
```bash
git -C "$ABSOLUTE_PATH" show-ref --verify --quiet refs/heads/adlc || {
cd "$ABSOLUTE_PATH"
git checkout --orphan adlc
mkdir -p drafts/cdr reports/sessions reports/projects
echo '{}' > reports/confidence-scores.json
touch drafts/cdr/.gitkeep reports/sessions/.gitkeep reports/projects/.gitkeep
git add -A && git commit -m "Initialize adlc orphan branch (drafts + reports)"
git checkout main
}
```
*On success: complete Post-Setup Configuration — write `team_ai_directives` to `.adlc/init-options.json`, run the health check (`SKILL.md`), check the `.gitignore` convention (Gitignore Convention Check, `SKILL.md`), inject `AGENTS.md` (`references/post-setup-agents.md`), install MCP config (`references/post-setup-mcp.md`).*
references/mode-scaffold.md
# team-setup reference: Mode 3 — Scaffold New Empty Directives
Part of the `team-setup` skill. Load this file only when the user chose Mode 3 (create a fresh, neutral team-ai-directives).
Shared context lives in `SKILL.md` (Goal, Input Validation, Mode Selection Flow, Post-Setup checklist).
---
### Mode 3: Scaffold New Empty team AI directives
Create a fresh, neutral team AI directives at a specified path.
**Explore**:
1. Ask the user where to create the team AI directives (default: `./team-ai-directives`)
2. Ask for the team name
3. Check the destination does not already exist or is empty
**Present**:
Show the user the 10 files that will be created:
| # | File | Purpose |
|---|------|---------|
| 1 | `README.md` | Getting started documentation |
| 2 | `AGENTS.md` | Agent instructions (loading order, rules, skills) |
| 3 | `CDR.md` | Derived CDR index stub (auto-generated by `/team-repair`) |
| 4 | `.skills.json` | Empty skills manifest (schema v2.0.0: `default`/`external`/`blocked`/`policy`) |
| 5 | `.mcp.json.example` | Empty MCP servers config example |
| 6 | `context_modules/constitution.md` | Placeholder constitution (OKF v0.2 frontmatter) — fill via `/team-constitution` |
| 7 | `context_modules/index.md` | OKF v0.2 root index (`okf_version: "0.2"`) linking sub-directories |
| 8 | `context_modules/log.md` | OKF §9 aggregate update log |
| 9 | `context_modules/rules/index.md` | OKF §8 progressive disclosure (rules) |
| 10 | `context_modules/rules/log.md` | OKF §9 rules audit trail |
| 11 | `context_modules/rules/.gitkeep` | Rules directory placeholder |
| 12 | `context_modules/personas/index.md` | OKF §8 progressive disclosure (personas) |
| 13 | `context_modules/personas/log.md` | OKF §9 personas audit trail |
| 14 | `context_modules/personas/.gitkeep` | Personas directory placeholder |
| 15 | `context_modules/examples/index.md` | OKF §8 progressive disclosure (examples) |
| 16 | `context_modules/examples/log.md` | OKF §9 examples audit trail |
| 17 | `context_modules/examples/.gitkeep` | Examples directory placeholder |
| 18 | `skills/.gitkeep` | Skills directory placeholder |
**Confirm**:
```
Scaffold empty team-ai-directives at {DEST} with team name "{TEAM_NAME}"?
[Y/n]
```
**Write/Execute**:
Create directory structure:
```bash
mkdir -p "{DEST}/context_modules/rules"
mkdir -p "{DEST}/context_modules/personas"
mkdir -p "{DEST}/context_modules/examples"
mkdir -p "{DEST}/skills"
```
Create `{DEST}/README.md`:
```markdown
# {TEAM_NAME} Team AI Directives
Team AI directives repository for {TEAM_NAME}.
## Getting Started
1. Wire this directives repository into a project:
```
/team-setup
```
Choose "Point to existing local path" and select this directory.
2. Add context modules to `context_modules/` (rules, personas, examples).
3. Add skills to `skills/` and register them in `.skills.json`.
4. Update `CDR.md` as context modules are approved.
See [ADLC Team Skills](https://github.com/tikalk/adlc-team-skills) for full documentation.
```
Create `{DEST}/AGENTS.md`:
```markdown
# Agent Instructions
## Structure
- `context_modules/constitution.md` — Team constitution
- `context_modules/rules/` — Team rules and workflows
- `context_modules/personas/` — Team personas
- `context_modules/examples/` — Team examples
- `skills/` — Team skills
- `CDR.md` — Context Directive Records
## Loading Order
1. Load constitution.md first
2. Load relevant rules for the current task
3. Load relevant personas for the current task
4. Load relevant examples for the current task
## Using Skills
Skills are located in the `skills/` directory. Browse available skills using `team-skills` and install them as needed.
## CDR.md
CDR.md is a derived flat table, auto-generated by `/team-repair` from per-directory `index.md` files. It is used by `team-boot` for system prompt injection. Do not edit manually — run `/team-repair` to regenerate.
```
Create `{DEST}/CDR.md`:
```markdown
# Context Directive Records (Derived Index)
> ⚠️ Auto-generated by `/team-repair`. Do not edit manually.
> Source of truth: `context_modules/*/index.md` + module frontmatter.
> Decision lifecycle (Accepted/Rejected) lives in the `adlc` orphan branch `drafts/cdr/`.
## CDR Index
| ID | Path | Type | Description | Generated | Verified | Age | Status |
|----|------|------|-------------|-----------|----------|-----|--------|
**Stats**: 0 entries | Last Updated: {TODAY}
```
Create `{DEST}/.skills.json`:
```json
{
"version": "2.0.0",
"source": "team-ai-directives",
"description": "Team skills manifest. The `default` list contains skill names that are auto-installed during project setup. The `external` map contains on-demand skills fetched by URL. The `blocked` list contains skills that must never be installed.",
"default": [],
"external": {},
"blocked": [],
"policy": {
"auto_install_default": true,
"enforce_blocked": true,
"allow_project_override": true
}
}
```
Create `{DEST}/.mcp.json.example`:
```json
{
"mcpServers": {}
}
```
Create `{DEST}/context_modules/constitution.md`:
```markdown
---
type: Constitution
title: "{TEAM_NAME} Constitution"
description: "Team-wide principles and governance"
resource: ./context_modules/constitution.md
tags: [constitution]
generated: { by: agent:team-setup, at: {TODAY}T00:00:00Z }
id: constitution
cdr_ref: null
created: {TODAY}
verified:
- { by: agent:team-setup, at: {TODAY}T00:00:00Z }
status: stable
stale_after: 180d
---
# {TEAM_NAME} Constitution
No team-wide principles defined yet. Add principles as they are established.
```
Create OKF v0.2-compliant `index.md` and `log.md` files for progressive disclosure:
Create `{DEST}/context_modules/index.md`:
```markdown
---
okf_version: "0.2"
---
# Context Modules
* [Rules](rules/index.md) - Team rules and workflows
* [Personas](personas/index.md) - Team personas
* [Examples](examples/index.md) - Team examples
```
Create `{DEST}/context_modules/log.md`:
```markdown
# Context Modules Update Log
```
Create `{DEST}/context_modules/rules/index.md`:
```markdown
# Rules
No rules defined yet. Use `/team-levelup` to create rules via CDRs.
```
Create `{DEST}/context_modules/rules/log.md`:
```markdown
# Rules Update Log
```
Create `{DEST}/context_modules/personas/index.md`:
```markdown
# Personas
No personas defined yet. Use `/team-levelup` to create personas via CDRs.
```
Create `{DEST}/context_modules/personas/log.md`:
```markdown
# Personas Update Log
```
Create `{DEST}/context_modules/examples/index.md`:
```markdown
# Examples
No examples defined yet. Use `/team-levelup` to create examples via CDRs.
```
Create `{DEST}/context_modules/examples/log.md`:
```markdown
# Examples Update Log
```
Create gitkeep files:
```bash
touch "{DEST}/context_modules/rules/.gitkeep"
touch "{DEST}/context_modules/personas/.gitkeep"
touch "{DEST}/context_modules/examples/.gitkeep"
touch "{DEST}/skills/.gitkeep"
```
Initialize git (required for `/team-levelup` branch/commit/PR flow):
```bash
cd "{DEST}" && git init && git add -A && git commit -m "Initial team-ai-directives scaffold"
```
Create the `adlc` orphan branch for CDR drafts and usage reports:
```bash
cd "{DEST}"
git checkout --orphan adlc
mkdir -p drafts/cdr reports/sessions reports/projects
echo '{}' > reports/confidence-scores.json
touch drafts/cdr/.gitkeep reports/sessions/.gitkeep reports/projects/.gitkeep
git add -A
git commit -m "Initialize adlc orphan branch (drafts + reports)"
git checkout main # back to main branch
```
**Follow-up**: The scaffolded `context_modules/constitution.md` is a placeholder ("No team-wide principles defined yet"). Tell the user:
```text
Scaffold complete. Run /team-constitution next to establish your team's
principles interactively — it detects the placeholder and walks you through
creating the real constitution.
```
After scaffold, run the Post-Setup Configuration checklist in `SKILL.md` (details in `references/post-setup-agents.md` and `references/post-setup-mcp.md`).
*On success: complete Post-Setup Configuration — write `team_ai_directives` to `.adlc/init-options.json`, run the health check (`SKILL.md`), check the `.gitignore` convention (Gitignore Convention Check, `SKILL.md`), inject `AGENTS.md` (`references/post-setup-agents.md`), install MCP config (`references/post-setup-mcp.md`).*
references/post-setup-agents.md
# team-setup reference: Post-Setup — Project AGENTS.md Injection
Part of the `team-setup` skill. Load this file only after any setup mode completes, to inject the project-level `AGENTS.md` directive.
Shared context lives in `SKILL.md` (Goal, Input Validation, Mode Selection Flow, Post-Setup checklist).
---
3. Inject the project-level `AGENTS.md` directive so agents auto-invoke `team-boot` at session start:
```bash
# Bash
bash "$(dirname "$0")/scripts/bash/team-helpers.sh" --inject-agents "{PROJECT_ROOT}"
# PowerShell
pwsh "$(Split-Path $PSCommandPath -Parent)/scripts/powershell/team-helpers.ps1" -InjectAgents "{PROJECT_ROOT}"
```
This creates or updates the project's `AGENTS.md` with a managed section (between `<!-- TEAM_AI_DIRECTIVES START -->` and `<!-- TEAM_AI_DIRECTIVES END -->` markers) containing:
- **Event-hook awareness**: notes that `team-boot` runs automatically at session start via the event hook (for agents with event support), injecting a lean orientation into the first user message.
- **Fallback invocation**: "If the team AI directives context is NOT in your system prompt or first user message (agent without event support), invoke the `team-boot` skill before responding to any task or question."
- **Unconfigured handling**: "If team AI directives are unconfigured, invoke the `team-setup` skill."
- **Class-boot invocation**: invoke the matching class boot at the START of a matching task — before planning the todo list and before implementation — so the class context informs planning (never deferred); workspace-root fallback when the CWD has no local memory (`.gitmodules` marker → workspace root `docs/adlc/memory/`).
- **Team Context & Decisions contract**: "Every response MUST include a Team Context & Decisions section before the task answer" — one merged 6-column table (`ID | Name | Type | Rel | Status | Clarify`): Status `in use` + Clarify `—` rows list genuinely matched accepted records; Status `pending`/`captured`/`clarified`/`handed off` + Clarify skill rows track emerging decisions (no draft row may carry `in use`), followed by `_Scope: N CDRs · A ADRs · P PDRs · C ChDRs · E evals · M skills — J rows shown · Unrecorded: P pending · Unclarified: D drafts._` (J = all rows shown). 0 rows matched → heading + counts line only, no table, never a table header. One row per line; before sending, if the section contains `|` but no newline, rewrite it.
- **Render rule**: heading, table (when rows exist), and counts line each on their own lines, never collapsed
- **Todo surfacing**: each detected decision is mirrored as a task-list todo (capture skill suggested — the boot never writes drafts; the capture skill writes to `.adlc/drafts/{type}/` when the user runs it — then the matching clarify skill at session end); after code-modifying tasks, a trailing todo sweeps Team Context & Decisions until _Unrecorded: 0 pending · Unclarified: 0 drafts_ (a draft leaves Unclarified only via its clarify skill or an explicit user handoff to a named clarify or execute skill); before closing, deliver the clarify prompt naming each uncaptured decision (ID + capture skill) and each captured draft (ID + skill); if the user defers clarify, mark those rows handed off
Without this section, an agent without event support has no session-start instruction to load team context, and the team AI directives repository remains invisible until manually loaded. The section is idempotent: re-running `team-setup` or `team-repair` updates the section in place without duplicating content.
references/post-setup-mcp.md
# team-setup reference: Post-Setup — MCP Config Install
Part of the `team-setup` skill. Load this file only after any setup mode completes, to seed the project's MCP config from the team declaration — copy-once, never a merge.
Shared context lives in `SKILL.md` (Goal, Input Validation, Mode Selection Flow, Post-Setup checklist).
---
4. **Install MCP config**: Read `{TEAM_AI_DIRECTIVES}/.mcp.json` if it exists, then follow the copy-once contract — the project's `.mcp.json` is project source (human-edited, never rewritten):
- **The project has no `.mcp.json`** — copy the team file in as-is (a one-time seed, not a generation) and report which servers were seeded.
- **The project already has a `.mcp.json`** — it wins and is **never merged into or rewritten**. Diff it against the team's and report the gap — *"your `.mcp.json` lacks N servers the team declares: X, Y"* — without writing a byte. A missing or unreadable team `.mcp.json` is itself reported; never degrade silently to "no gap".
- Per-agent configs (`.opencode/mcp.json`, `.cursor/`, `.codex/`, `.claude/`) are the derived side of the asymmetry: they are materialized *from* the project's `.mcp.json`, never the other way round. Writing them is fine; writing the project's `.mcp.json` is not.
- No secret values in `.mcp.json` — variable *names* only, never literal secrets; `env` indirection is the guard, not `.gitignore`.
- In every case, highlight any unresolved environment variables needed by the servers.
scripts/bash/team-helpers.sh
#!/usr/bin/env bash
# team-helpers.sh — Shared utilities for team-* skills
#
# Flags:
# --json Output path info as JSON (default: key=value)
# --scaffold [DIR] Create a fresh 11-file team AI directives scaffold at DIR
# --validate [DIR] Validate the team AI directives structure at DIR (exit 1 if incomplete)
# --wire [DIR] Validate DIR, then write team_ai_directives (absolute path) into
# the project's .adlc/init-options.json — the deterministic Mode 2
# wiring used headless (adlc-cli team setup --directives)
# --agents-only DIR Create only AGENTS.md at DIR (for repair use)
# --inject-agents [DIR] Inject team-boot directive into project-level AGENTS.md at DIR
# --agents-status [DIR] Report managed-section status in project-level AGENTS.md at DIR
# --agents-uninstall [DIR] Remove managed section from project-level AGENTS.md at DIR
# --name NAME Team name for scaffold (default: "My Team")
set -euo pipefail
###############################################################################
# 1. PATH RESOLUTION
###############################################################################
# JSON output always goes through jq -n (no manual string building).
resolve_paths() {
PROJECT_ROOT="${PROJECT_ROOT:-$([[ -d ".adlc" ]] && pwd || git rev-parse --show-toplevel 2>/dev/null || pwd)}"
BRANCH="${BRANCH:-$(git branch --show-current 2>/dev/null || echo 'unknown')}"
TEAM_AI_DIRECTIVES=""
# 1. Check TEAM_AI_DIRECTIVES env var (highest priority)
if [[ -n "${TEAM_AI_DIRECTIVES:-}" ]]; then
TEAM_AI_DIRECTIVES="$TEAM_AI_DIRECTIVES"
fi
# 2. Check .adlc/init-options.json
if [[ -z "$TEAM_AI_DIRECTIVES" ]]; then
INIT_OPTIONS="${PROJECT_ROOT}/.adlc/init-options.json"
if [[ -f "$INIT_OPTIONS" ]]; then
require_jq || return 1
TEAM_AI_DIRECTIVES=$(jq -r '.team_ai_directives // empty' "$INIT_OPTIONS" 2>/dev/null || true)
fi
fi
# 3. Fallback to default path
if [[ -z "$TEAM_AI_DIRECTIVES" ]]; then
TEAM_AI_DIRECTIVES="${PROJECT_ROOT}/team-ai-directives"
fi
echo "PROJECT_ROOT=$PROJECT_ROOT"
echo "TEAM_AI_DIRECTIVES=$TEAM_AI_DIRECTIVES"
echo "BRANCH=$BRANCH"
}
output_json() {
require_jq || return 1
jq -n \
--arg repo_root "$PROJECT_ROOT" \
--arg team_ai_directives "$TEAM_AI_DIRECTIVES" \
--arg branch "$BRANCH" \
'{"REPO_ROOT":$repo_root,"TEAM_AI_DIRECTIVES":$team_ai_directives,"BRANCH":$branch}'
}
# All JSON reads/writes below go through jq (no silent fallback when it is
# missing: an empty parse must not resolve the wrong directives path).
require_jq() {
if ! command -v jq >/dev/null 2>&1; then
echo "ERROR: jq is required but not installed (https://jqlang.github.io/jq/download/)" >&2
return 1
fi
}
###############################################################################
# 2. TEAM AI DIRECTIVES STRUCTURE VALIDATION
###############################################################################
validate_team_ai_directives() {
local dir="$1"
local missing=0
for required in \
"context_modules/constitution.md" \
"context_modules/rules" \
"context_modules/personas" \
"context_modules/examples" \
"CDR.md" \
".skills.json"; do
if [[ ! -e "${dir}/${required}" ]]; then
echo "MISSING: ${required}"
missing=$((missing + 1))
fi
done
return "$missing"
}
# JSON-parse check shared by --validate and --wire: the structure check is
# existence-only, but the post-setup health contract requires .skills.json to
# parse (`jq empty` tests parsing only — `jq -e .` would reject a valid `null`).
# Prints MISSING and returns 1 when present-but-malformed, 0 otherwise.
check_skills_json_parses() {
local dir="$1"
if [[ -e "${dir}/.skills.json" ]] \
&& ! jq empty "${dir}/.skills.json" >/dev/null 2>&1; then
echo "MISSING: .skills.json is not valid JSON"
return 1
fi
return 0
}
# --validate [DIR] — exit 0 when the structure is complete, exit 1 with the
# MISSING lines when it is not. Used by headless callers to gate wiring.
validate_structure_cli() {
local dir="$1"
local missing=0
validate_team_ai_directives "$dir" || missing=$?
check_skills_json_parses "$dir" || missing=$((missing + 1))
if [[ "$missing" -gt 0 ]]; then
echo "INVALID: team-ai-directives at ${dir} is missing ${missing} required path(s)" >&2
return 1
fi
echo "VALID: team-ai-directives at ${dir}"
return 0
}
# --wire [DIR] — deterministic Mode 2 (point to existing local path): validate
# the structure, then merge the absolute path into .adlc/init-options.json.
# Never interpolates the path into shell/Python source — it travels as argv.
wire_team_ai_directives() {
local dir="$1"
if [[ -z "$dir" ]]; then
echo "ERROR: --wire requires a team-ai-directives directory argument" >&2
exit 1
fi
if [[ ! -d "$dir" ]]; then
echo "ERROR: team-ai-directives directory not found: ${dir}" >&2
exit 1
fi
local abs
abs="$(cd "$dir" && pwd -P)"
local missing=0
validate_team_ai_directives "$abs" || missing=$?
# .skills.json must also parse (post-setup health check contract)
check_skills_json_parses "$abs" || missing=$((missing + 1))
if [[ "$missing" -gt 0 ]]; then
echo "ERROR: team-ai-directives at ${abs} failed validation (${missing} issue(s)) — not wired" >&2
return 1
fi
# PROJECT_ROOT may be unset in a fresh headless shell (set -u is on) — fall
# back exactly like resolve_paths, and refuse an explicit empty value that
# would resolve init-options.json to /.adlc.
local project_root="${PROJECT_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"
if [[ -z "$project_root" ]]; then
echo "ERROR: PROJECT_ROOT is empty — cannot locate .adlc/init-options.json" >&2
return 1
fi
require_jq || return 1
local init_dir="${project_root}/.adlc"
local init_path="${init_dir}/init-options.json"
mkdir -p "$init_dir"
# Missing file starts as an empty object (same as the old python reader's
# `cfg = {}`); existing file must parse or wiring refuses.
if [[ ! -e "$init_path" ]]; then
jq -n '{}' > "$init_path"
fi
if ! jq -e --arg td "$abs" 'select(type == "object") | .team_ai_directives = $td' "$init_path" > "${init_path}.tmp" 2>/dev/null; then
rm -f "${init_path}.tmp" 2>/dev/null
echo "ERROR: ${init_path} is not valid JSON or not an object — not wired" >&2
return 1
fi
# Write back through the existing path (keeps 0600 mode / symlinks).
cat "${init_path}.tmp" > "$init_path"
rm -f "${init_path}.tmp"
echo "WIRED team_ai_directives=${abs}"
return 0
}
###############################################################################
# 3. SCAFFOLD
###############################################################################
scaffold_team_ai_directives() {
local dest="$1"
local team_name="${2:-My Team}"
local today
today=$(date +%Y-%m-%d)
if [[ -d "$dest" && -n "$(ls -A "$dest" 2>/dev/null)" ]]; then
echo "ERROR: Destination '$dest' already exists and is not empty." >&2
exit 1
fi
mkdir -p "${dest}/context_modules/rules"
mkdir -p "${dest}/context_modules/personas"
mkdir -p "${dest}/context_modules/examples"
mkdir -p "${dest}/skills"
cat > "${dest}/README.md" << README
# ${team_name} Team AI Directives
Team AI directives repository for ${team_name}.
## Getting Started
1. Wire this directives repository into a project:
\`\`\`
/team-setup
\`\`\`
Choose "Point to existing local path" and select this directory.
2. Add context modules to \`context_modules/\` (rules, personas, examples).
3. Add skills to \`skills/\` and register them in \`.skills.json\`.
4. Update \`CDR.md\` as context modules are approved.
See [ADLC Team Skills](https://github.com/tikalk/adlc-team-skills) for full documentation.
README
cat > "${dest}/AGENTS.md" << 'AGENTS'
# Agent Instructions
## Structure
- `context_modules/constitution.md` — Team constitution
- `context_modules/rules/` — Team rules and workflows
- `context_modules/personas/` — Team personas
- `context_modules/examples/` — Team examples
- `skills/` — Team skills
- `evals/` — Directive compliance goldensets (pass/fail cases)
- `CDR.md` — Context Directive Records
## Loading Order
1. Load constitution.md first
2. Load relevant rules for the current task
3. Load relevant personas for the current task
4. Load relevant examples for the current task
## Using Skills
Skills are located in the `skills/` directory. Browse available skills using team-skills and install them as needed.
## CDR.md
The CDR.md file tracks approved context contributions. Update it when adding new context modules.
AGENTS
cat > "${dest}/CDR.md" << CDR
# Context Directive Records
Context Directive Records (CDRs) track decisions about contributing context modules (rules, personas, examples, skills) to team-ai-directives.
## CDR Index
| ID | Target Module | Type | Status | Created | Verified | Age | Descriptor |
|----|---------------|------|--------|---------|----------|-----|------------|
**Stats**: 0 entries | Last Updated: ${today}
CDR
cat > "${dest}/.skills.json" << 'SKILLSJSON'
{
"version": "2.0.0",
"source": "team-ai-directives",
"description": "Team skills manifest. The `default` list contains skill names that are auto-installed during project setup. The `external` map contains on-demand skills fetched by URL. The `blocked` list contains skills that must never be installed.",
"default": [],
"external": {},
"blocked": [],
"policy": {
"auto_install_default": true,
"enforce_blocked": true,
"allow_project_override": true
}
}
SKILLSJSON
cat > "${dest}/.mcp.json.example" << 'MCPJSON'
{
"mcpServers": {}
}
MCPJSON
cat > "${dest}/context_modules/constitution.md" << CONSTITUTION
---
type: Constitution
title: "${team_name} Constitution"
description: "Team-wide principles and governance"
resource: ./context_modules/constitution.md
tags: [constitution]
generated: { by: agent:team-setup, at: ${today}T00:00:00Z }
id: constitution
cdr_ref: null
created: ${today}
verified:
- { by: agent:team-setup, at: ${today}T00:00:00Z }
status: stable
stale_after: 180d
---
# ${team_name} Constitution
No team-wide principles defined yet. Add principles as they are established.
CONSTITUTION
cat > "${dest}/context_modules/index.md" << 'INDEXTOP'
---
okf_version: "0.2"
---
# Context Modules
* [Rules](rules/index.md) - Team rules and workflows
* [Personas](personas/index.md) - Team personas
* [Examples](examples/index.md) - Team examples
INDEXTOP
cat > "${dest}/context_modules/log.md" << 'LOGTOP'
# Context Modules Update Log
LOGTOP
cat > "${dest}/context_modules/rules/index.md" << 'INDEXRULES'
# Rules
No rules defined yet. Use /team-levelup to create rules via CDRs.
INDEXRULES
cat > "${dest}/context_modules/rules/log.md" << 'LOGRULES'
# Rules Update Log
LOGRULES
cat > "${dest}/context_modules/personas/index.md" << 'INDEXPERS'
# Personas
No personas defined yet. Use /team-levelup to create personas via CDRs.
INDEXPERS
cat > "${dest}/context_modules/personas/log.md" << 'LOGPERS'
# Personas Update Log
LOGPERS
cat > "${dest}/context_modules/examples/index.md" << 'INDEXEX'
# Examples
No examples defined yet. Use /team-levelup to create examples via CDRs.
INDEXEX
cat > "${dest}/context_modules/examples/log.md" << 'LOGEX'
# Examples Update Log
LOGEX
cat > "${dest}/CDR.md" << 'CDRDERIVED'
# Context Directive Records (Derived Index)
> ⚠️ Auto-generated by `/team-repair`. Do not edit manually.
> Source of truth: `context_modules/*/index.md` + module frontmatter.
> Decision lifecycle (Accepted/Rejected) lives in the `adlc` orphan branch `drafts/cdr/`.
## CDR Index
| ID | Path | Type | Description | Generated | Verified | Age | Status |
|----|------|------|-------------|-----------|----------|-----|--------|
**Stats**: 0 entries | Last Updated: PLACEHOLDER
CDRDERIVED
touch "${dest}/context_modules/rules/.gitkeep"
touch "${dest}/context_modules/personas/.gitkeep"
touch "${dest}/context_modules/examples/.gitkeep"
touch "${dest}/skills/.gitkeep"
echo "Scaffolded team-ai-directives at ${dest}"
echo "Team name: ${team_name}"
echo "Files created: 14"
validate_team_ai_directives "$dest" || true
}
scaffold_agents_only() {
local dest="$1"
mkdir -p "$dest"
cat > "${dest}/AGENTS.md" << 'AGENTS'
# Agent Instructions
## Structure
- `context_modules/constitution.md` — Team constitution
- `context_modules/rules/` — Team rules and workflows
- `context_modules/personas/` — Team personas
- `context_modules/examples/` — Team examples
- `skills/` — Team skills
- `reports/sessions/` — Published session summaries (from `/team-levelup`, on `adlc` branch)
- `CDR.md` — Context Directive Records
## Loading Order
1. Load constitution.md first
2. Load relevant rules for the current task
3. Load relevant personas for the current task
4. Load relevant examples for the current task
## Using Skills
Skills are located in the `skills/` directory. Browse available skills using team-skills and install them as needed.
## CDR.md
The CDR.md file tracks approved context contributions. Update it when adding new context modules.
AGENTS
echo "Created AGENTS.md at ${dest}"
}
###############################################################################
# 4. INJECT PROJECT-LEVEL AGENTS.md DIRECTIVE
###############################################################################
inject_project_agents() {
local project_root="${1:-.}"
local team_directive="${2:-}"
# Resolve team_ai_directives if not provided
if [[ -z "$team_directive" ]]; then
local init_options="${project_root}/.adlc/init-options.json"
if [[ -f "$init_options" ]]; then
require_jq || return 1
team_directive=$(jq -r '.team_ai_directives // empty' "$init_options" 2>/dev/null || true)
fi
fi
# Fallback to env var
if [[ -z "$team_directive" ]] && [[ -n "${TEAM_AI_DIRECTIVES:-}" ]]; then
team_directive="$TEAM_AI_DIRECTIVES"
fi
# Fallback to default path
if [[ -z "$team_directive" ]]; then
team_directive="${project_root}/team-ai-directives"
fi
local agents_file="${project_root}/AGENTS.md"
local marker_start="<!-- TEAM_AI_DIRECTIVES START -->"
local marker_end="<!-- TEAM_AI_DIRECTIVES END -->"
# Build the managed section
local section
section=$(cat <<SECTION
${marker_start}
# Team AI Directives
This project is bound by the team AI directives repository at \`${team_directive}\`.
team-boot runs automatically at session start via the event hook (for agents with event support), injecting a lean orientation into the first user message with the team constitution, CDR index, Class Boots catalog, skills registry, and MCP servers.
If the team AI directives context is NOT in your system prompt or first user message (agent without event support), invoke the \`team-boot\` skill before responding to any task or question.
If team AI directives are unconfigured, invoke the \`team-setup\` skill.
Invoke the matching class boot when a task or decision matches a row — invoke it
at the START of the matching task, before planning the todo list and before
implementation, so the class context informs planning; never defer to session end
or post-hoc. When no local memory index exists in the current working directory
and the directory sits inside a workspace (detected via a \`.gitmodules\` marker
in an ancestor), the boot reads the workspace root's \`docs/adlc/memory/\` index
instead (the dual-read order applies: \`docs/adlc/memory\` first, legacy
\`.adlc/memory\` fallback).
## Class Boots
| Boot | Injects | Invoke When | Capture Via |
|--|--|--|--|
| architect-boot | ADR index (docs/adlc/memory/adr/ + legacy .adlc/memory/adr/) | architecture work; tech-stack/pattern choice | /architect-specify |
| product-boot | PDR index (docs/adlc/memory/pdr/ + legacy .adlc/memory/pdr/) | product/feature scope, personas, monetization | /product-specify |
| change-boot | ChDR index (docs/adlc/memory/chdr/chdr.md + legacy .adlc/memory/chdr.md) | change-history rationale, reverts, issue-linked commits, git commands w/ human-authored messages, authored PRs, CHANGELOG edits | /change-specify (mining: /change-init) |
| team-levelup | CDR module bodies (team-ai-directives) | CDR descriptor match; reusable team pattern | /team-levelup |
| tech-radar-boot | Tikal Tech Radar context | choosing/evaluating technology | radar context + /architect-specify |
Each class boot emits its class context section and its own searched line (_Searched N records, K matched._).
**Every response MUST include** a Team Context & Decisions section before the task answer (Status \`in use\` rows ground the session in accepted records; Status \`pending\` rows capture emerging decisions in the same table):
## Team Context & Decisions
| ID | Name | Type | Rel | Status | Clarify |
|--|--|--|--|--|--|
| CDR-YYYY-NNN | <name> | <type> | <relevance> | in use | — |
| — | <decision> | <ADR/PDR/CDR/ChDR/Eval> | <trigger> | pending | <clarify skill> |
Plus: \`_Scope: N CDRs · A ADRs · P PDRs · C ChDRs · E evals · M skills — J rows shown · Unrecorded: N pending · Unclarified: M captured drafts._\` — **J MUST equal the number of rows (any indexed type). Status \`in use\` + Clarify \`—\` = grounding context (accepted records only); Status \`pending\`/\`captured\`/\`clarified\`/\`handed off\` + Clarify skill = owed decisions. No draft row may carry Status \`in use\` — drafts appear only as pending decision rows. 0 rows matched → emit the section heading + the counts line only — no table. Zero rows means heading plus counts line only — never a table header. Emit one table row per line. (do not copy a hard-coded row or inflate the count).** Scope names the always-available layer and class indexes (counts only, never content) plus the session's open decisions; class boots append their own scope line when fired.
Render the section as markdown blocks — heading, table (when rows exist), and counts line each on their own lines. Before sending, if the section contains a pipe character but no newline, rewrite it.
## Decision Capture
Detect decisions as they emerge and suggest the matching capture skill — never write drafts or touch \`.adlc/drafts/\`; the capture skill writes the draft when the user runs it. Full detection and capture guidance lives in the matching class boot:
- Tech stack / pattern choice → ADR → /architect-specify (pull tech-radar-boot context first for tech selection)
- Feature scope / persona / monetization → PDR → /product-specify
- Reusable team rule / pattern → CDR → /team-levelup
- Revert/hotfix rationale / issue-linked commit → ChDR → /change-specify (mined history: /change-init)
- Eval criterion discovered → Eval → /evals-specify
Maintain the decision rows in the same table above (Status pending/captured/clarified/handed off + Clarify skill); with no pending decisions, emit the heading + the counts line only — no table.
At session end, deliver the clarify prompt naming each uncaptured decision (ID + capture skill) and each captured draft (ID + clarify skill); if the user defers clarify, mark those rows handed off. Only suggest capture when genuinely warranted.
Surface each detected decision as a task-list todo (suggest the capture skill — never write the draft yourself; the capture skill writes to \`.adlc/drafts/{type}/\` when the user runs it — then run the matching clarify skill at session end). After code-modifying tasks, add a trailing todo to sweep Team Context & Decisions until _Unrecorded: 0 pending · Unclarified: 0 drafts_ (a draft leaves Unclarified only via its clarify skill or an explicit user handoff to a named clarify or execute skill).
${marker_end}
SECTION
)
# Create AGENTS.md if it doesn't exist, or update the managed section.
# Pure-awk rewrite: the section content may contain quotes, backslashes,
# and backticks, so it travels via temp files, never the command line.
local section_file tmp_new had_section=false
section_file=$(mktemp) tmp_new=$(mktemp)
if [[ -f "$agents_file" ]] && grep -qF "$marker_start" "$agents_file" && grep -qF "$marker_end" "$agents_file"; then
had_section=true
fi
printf '%s' "$section" > "$section_file"
if [[ -f "$agents_file" ]]; then
awk -v start="$marker_start" -v end="$marker_end" -v secfile="$section_file" '
BEGIN { sec = ""; while ((getline line < secfile) > 0) sec = sec line "\n"; close(secfile); found_s = 0; found_e = 0 }
{ lines[NR] = $0 "\n" }
index($0, start) && !found_s { s_line = NR; found_s = 1 }
index($0, end) && found_s && !found_e { e_line = NR; found_e = 1 }
END {
if (found_s && found_e && s_line < e_line) {
for (i = 1; i < s_line; i++) printf "%s", lines[i]
printf "%s", sec
for (i = e_line + 1; i <= NR; i++) printf "%s", lines[i]
} else {
for (i = 1; i <= NR; i++) printf "%s", lines[i]
print ""
printf "%s", sec
}
}
' "$agents_file" > "$tmp_new"
else
printf '%s\n' "$section" > "$tmp_new"
fi
# Write back through the existing path (keeps file mode and symlinks);
# a fresh file then gets the normal umask default.
cat "$tmp_new" > "$agents_file"
rm -f "$tmp_new" "$section_file"
if grep -qF "$marker_start" "$agents_file"; then
if $had_section; then
echo "Updated team AI directives section in ${agents_file}"
else
echo "Injected team AI directives section into ${agents_file}"
fi
fi
}
###############################################################################
# 5. MANAGED SECTION STATUS + UNINSTALL
###############################################################################
agents_section_status() {
local project_root="${1:-.}"
local agents_file="${project_root}/AGENTS.md"
local marker_start="<!-- TEAM_AI_DIRECTIVES START -->"
local marker_end="<!-- TEAM_AI_DIRECTIVES END -->"
if [[ ! -f "$agents_file" ]]; then
echo "STATUS: missing (no AGENTS.md at ${project_root})"
return 1
fi
if grep -qF "$marker_start" "$agents_file" && grep -qF "$marker_end" "$agents_file"; then
echo "STATUS: installed (${agents_file} contains the managed section)"
else
echo "STATUS: not-installed (${agents_file} lacks the managed section)"
return 1
fi
}
agents_section_uninstall() {
local project_root="${1:-.}"
local agents_file="${project_root}/AGENTS.md"
local marker_start="<!-- TEAM_AI_DIRECTIVES START -->"
local marker_end="<!-- TEAM_AI_DIRECTIVES END -->"
if [[ ! -f "$agents_file" ]]; then
echo "Nothing to remove: no AGENTS.md at ${project_root}"
return 0
fi
if ! grep -qF "$marker_start" "$agents_file" || ! grep -qF "$marker_end" "$agents_file"; then
echo "No managed section in ${agents_file} (nothing removed)"
return 0
fi
local s_line e_line
s_line=$(grep -nF -m1 "$marker_start" "$agents_file" | cut -d: -f1)
e_line=$(grep -nF -m1 "$marker_end" "$agents_file" | cut -d: -f1)
if [[ "$s_line" -ge "$e_line" ]]; then
echo "No managed section in ${agents_file} (nothing removed)"
return 0
fi
# Cut the managed section, collapse 3+ newlines to 2, strip leading and
# trailing blank lines, keep exactly one trailing newline when non-empty.
local tmp_un
tmp_un=$(mktemp)
# s_line == 1 must skip head: BSD head rejects `-n 0` (macOS inject-then-uninstall).
{ if (( s_line > 1 )); then head -n $((s_line - 1)) "$agents_file"; fi; tail -n +$((e_line + 1)) "$agents_file"; } > "$tmp_un"
awk '/^$/ { pending++; next } { if (out > 0 && pending > 0) print ""; print; pending = 0; out++ }' \
"$tmp_un" > "${tmp_un}.2"
# Same write-back rule as inject: keep mode and symlinks.
cat "${tmp_un}.2" > "$agents_file"
rm -f "$tmp_un" "${tmp_un}.2"
echo "Removed team AI directives section from ${agents_file}"
}
###############################################################################
# MAIN
###############################################################################
main() {
if [[ "$#" -eq 0 ]]; then
resolve_paths
return
fi
local has_json=false
local has_scaffold=false
local has_validate=false
local has_wire=false
local has_agents_only=false
local has_inject_agents=false
local has_agents_status=false
local has_agents_uninstall=false
local scaffold_dest=""
local validate_dest=""
local wire_dest=""
local agents_only_dest=""
local inject_dest=""
local agents_status_dest=""
local agents_uninstall_dest=""
local team_name="My Team"
local parsing_scaffold=false
local parsing_validate=false
local parsing_wire=false
local parsing_agents=false
local parsing_name=false
local parsing_inject=false
local parsing_status=false
local parsing_uninstall=false
for arg in "$@"; do
if [[ "$arg" == "--json" || "$arg" == "-Json" ]]; then
has_json=true
continue
fi
if [[ "$arg" == "--help" || "$arg" == "-h" ]]; then
echo "Usage: team-helpers.sh [--json] [--scaffold DIR] [--validate DIR] [--wire DIR] [--agents-only DIR] [--inject-agents [DIR]] [--agents-status [DIR]] [--agents-uninstall [DIR]] [--name NAME]"
exit 0
fi
if [[ "$arg" == "--scaffold" ]]; then
has_scaffold=true
parsing_scaffold=true
parsing_validate=false
parsing_wire=false
parsing_agents=false
parsing_name=false
parsing_inject=false
parsing_status=false
parsing_uninstall=false
continue
fi
if [[ "$arg" == "--validate" ]]; then
has_validate=true
parsing_validate=true
parsing_scaffold=false
parsing_wire=false
parsing_agents=false
parsing_name=false
parsing_inject=false
parsing_status=false
parsing_uninstall=false
continue
fi
if [[ "$arg" == "--wire" ]]; then
has_wire=true
parsing_wire=true
parsing_scaffold=false
parsing_validate=false
parsing_agents=false
parsing_name=false
parsing_inject=false
parsing_status=false
parsing_uninstall=false
continue
fi
if [[ "$arg" == "--agents-only" ]]; then
has_agents_only=true
parsing_agents=true
parsing_scaffold=false
parsing_validate=false
parsing_wire=false
parsing_name=false
parsing_inject=false
parsing_status=false
parsing_uninstall=false
continue
fi
if [[ "$arg" == "--inject-agents" ]]; then
has_inject_agents=true
parsing_inject=true
parsing_scaffold=false
parsing_validate=false
parsing_wire=false
parsing_agents=false
parsing_name=false
parsing_status=false
parsing_uninstall=false
continue
fi
if [[ "$arg" == "--agents-status" ]]; then
has_agents_status=true
parsing_status=true
parsing_scaffold=false
parsing_validate=false
parsing_wire=false
parsing_agents=false
parsing_name=false
parsing_inject=false
parsing_uninstall=false
continue
fi
if [[ "$arg" == "--agents-uninstall" ]]; then
has_agents_uninstall=true
parsing_uninstall=true
parsing_scaffold=false
parsing_validate=false
parsing_wire=false
parsing_agents=false
parsing_name=false
parsing_inject=false
parsing_status=false
continue
fi
if [[ "$arg" == "--name" ]]; then
parsing_name=true
parsing_scaffold=false
parsing_validate=false
parsing_wire=false
parsing_agents=false
parsing_inject=false
parsing_status=false
parsing_uninstall=false
continue
fi
if $parsing_scaffold && [[ -n "$arg" ]]; then
scaffold_dest="$arg"
parsing_scaffold=false
continue
fi
if $parsing_validate && [[ -n "$arg" ]]; then
validate_dest="$arg"
parsing_validate=false
continue
fi
if $parsing_wire && [[ -n "$arg" ]]; then
wire_dest="$arg"
parsing_wire=false
continue
fi
if $parsing_agents && [[ -n "$arg" ]]; then
agents_only_dest="$arg"
parsing_agents=false
continue
fi
if $parsing_inject && [[ -n "$arg" ]]; then
inject_dest="$arg"
parsing_inject=false
continue
fi
if $parsing_status && [[ -n "$arg" ]]; then
agents_status_dest="$arg"
parsing_status=false
continue
fi
if $parsing_uninstall && [[ -n "$arg" ]]; then
agents_uninstall_dest="$arg"
parsing_uninstall=false
continue
fi
if $parsing_name && [[ -n "$arg" ]]; then
team_name="$arg"
parsing_name=false
continue
fi
done
if $has_scaffold; then
if [[ -z "$scaffold_dest" ]]; then
echo "ERROR: --scaffold requires a destination directory argument" >&2
exit 1
fi
scaffold_team_ai_directives "$scaffold_dest" "$team_name"
return
fi
if $has_agents_only; then
if [[ -z "$agents_only_dest" ]]; then
echo "ERROR: --agents-only requires a destination directory argument" >&2
exit 1
fi
scaffold_agents_only "$agents_only_dest"
return
fi
if $has_validate; then
if [[ -z "$validate_dest" ]]; then
echo "ERROR: --validate requires a team-ai-directives directory argument" >&2
exit 1
fi
validate_structure_cli "$validate_dest"
return
fi
if $has_wire; then
if [[ -z "$wire_dest" ]]; then
echo "ERROR: --wire requires a team-ai-directives directory argument" >&2
exit 1
fi
wire_team_ai_directives "$wire_dest"
return
fi
if $has_inject_agents; then
local project_root="${inject_dest:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"
inject_project_agents "$project_root"
return
fi
if $has_agents_status; then
local project_root="${agents_status_dest:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"
agents_section_status "$project_root"
return
fi
if $has_agents_uninstall; then
local project_root="${agents_uninstall_dest:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"
agents_section_uninstall "$project_root"
return
fi
# Default: resolve paths
resolve_paths > /dev/null
if $has_json; then
output_json
else
resolve_paths
fi
}
main "$@"
scripts/powershell/team-helpers.ps1
#!/usr/bin/env pwsh
# team-helpers.ps1 — Shared utilities for team-* skills (PowerShell)
#
# Flags:
# -Json Output path info as JSON (default: key=value)
# -Scaffold DIR Create a fresh 11-file team AI directives scaffold at DIR
# -Validate DIR Validate the team AI directives structure at DIR (exit 1 if incomplete)
# -Wire DIR Validate DIR, then write team_ai_directives (absolute path) into
# the project's .adlc/init-options.json — the deterministic Mode 2
# wiring used headless (adlc-cli team setup --directives)
# -AgentsOnly DIR Create only AGENTS.md at DIR (for repair use)
# -InjectAgents DIR Inject team-boot directive into project-level AGENTS.md at DIR
# -AgentsStatus DIR Report managed-section status in project-level AGENTS.md at DIR
# -AgentsUninstall DIR Remove managed section from project-level AGENTS.md at DIR
# -Name NAME Team name for scaffold (default: "My Team")
param(
[switch]$Json,
[string]$Scaffold = "",
[string]$Validate = "",
[string]$Wire = "",
[string]$AgentsOnly = "",
[string]$InjectAgents = "",
[string]$AgentsStatus = "",
[string]$AgentsUninstall = "",
[string]$Name = "My Team"
)
$ErrorActionPreference = "Stop"
# 1. PATH RESOLUTION
$ProjectRoot = if ($env:PROJECT_ROOT) { $env:PROJECT_ROOT } else {
$gitRoot = git rev-parse --show-toplevel 2>$null
if ($gitRoot) { $gitRoot } else { (Get-Location).Path }
}
$Branch = if ($env:BRANCH) { $env:BRANCH } else {
$b = git branch --show-current 2>$null
if ($b) { $b } else { "unknown" }
}
$TeamAiDirectives = ""
# 1. Check TEAM_AI_DIRECTIVES env var (highest priority)
if ($env:TEAM_AI_DIRECTIVES) {
$TeamAiDirectives = $env:TEAM_AI_DIRECTIVES
}
# 2. Check .adlc/init-options.json
if (-not $TeamAiDirectives) {
$InitOptions = Join-Path $ProjectRoot ".adlc" "init-options.json"
if (Test-Path $InitOptions) {
try {
$Config = Get-Content $InitOptions -Raw | ConvertFrom-Json
if ($Config.team_ai_directives) {
$TeamAiDirectives = $Config.team_ai_directives
}
} catch {}
}
}
# 3. Fallback to default path
if (-not $TeamAiDirectives) {
$TeamAiDirectives = Join-Path $ProjectRoot "team-ai-directives"
}
function Write-OutputKeyValue {
Write-Output "PROJECT_ROOT=$ProjectRoot"
Write-Output "TEAM_AI_DIRECTIVES=$TeamAiDirectives"
Write-Output "BRANCH=$Branch"
}
function Write-OutputJson {
$output = @{
REPO_ROOT = $ProjectRoot
TEAM_AI_DIRECTIVES = $TeamAiDirectives
BRANCH = $Branch
}
return $output | ConvertTo-Json
}
# 2. TEAM AI DIRECTIVES STRUCTURE VALIDATION
function Test-TeamAiDirectivesStructure {
param([string]$Dir)
$missing = 0
$required = @(
"context_modules/constitution.md",
"context_modules/rules",
"context_modules/personas",
"context_modules/examples",
"CDR.md",
".skills.json"
)
foreach ($item in $required) {
$path = Join-Path $Dir $item
if (-not (Test-Path $path)) {
Write-Output "MISSING: $item"
$missing++
}
}
return $missing
}
# JSON-parse check shared by -Validate and -Wire: the structure check is
# existence-only, but the post-setup health contract requires .skills.json to
# parse. Emits MISSING and returns 1 when present-but-malformed, 0 otherwise.
function Test-SkillsJsonParses {
param([string]$Dir)
$skillsJson = Join-Path $Dir ".skills.json"
if ((Test-Path $skillsJson)) {
try {
$null = Get-Content $skillsJson -Raw | ConvertFrom-Json
} catch {
Write-Output "MISSING: .skills.json is not valid JSON"
return 1
}
}
return 0
}
# -Validate DIR — exit 0 when the structure is complete, exit 1 with the
# MISSING lines when it is not. Used by headless callers to gate wiring.
# NOTE: Test-TeamAiDirectivesStructure emits MISSING lines to the output
# stream AND returns the count, so the call captures an array whose last
# element is the count — never compare or increment it directly.
function Invoke-TeamAiDirectivesValidation {
param([string]$Dir)
$check = @(Test-TeamAiDirectivesStructure -Dir $Dir)
$missing = [int]$check[-1]
if ($check.Count -gt 1) { $check[0..($check.Count - 2)] | Write-Output }
$skillsOk = Test-SkillsJsonParses -Dir $Dir
if ($skillsOk -ne 0) {
$skillsOk | Where-Object { $_ -is [string] } | Write-Output
$missing++
}
if ($missing -gt 0) {
Write-Error "INVALID: team-ai-directives at ${Dir} is missing ${missing} required path(s)"
exit 1
}
Write-Output "VALID: team-ai-directives at ${Dir}"
}
# -Wire DIR — deterministic Mode 2 (point to existing local path): validate
# the structure, then merge the absolute path into .adlc/init-options.json.
function Invoke-TeamAiDirectivesWire {
param([string]$Dir)
if (-not $Dir) {
Write-Error "-Wire requires a team-ai-directives directory argument"
exit 1
}
if (-not (Test-Path $Dir -PathType Container)) {
Write-Error "team-ai-directives directory not found: ${Dir}"
exit 1
}
$abs = (Resolve-Path $Dir).Path
# NOTE: Test-TeamAiDirectivesStructure emits MISSING lines to the output
# stream AND returns the count, so the call captures an array whose last
# element is the count — never compare or increment it directly.
$check = @(Test-TeamAiDirectivesStructure -Dir $abs)
$missing = [int]$check[-1]
if ($check.Count -gt 1) { $check[0..($check.Count - 2)] | Write-Output }
# .skills.json must also parse (post-setup health check contract)
$skillsOk = Test-SkillsJsonParses -Dir $abs
if ($skillsOk -ne 0) {
$skillsOk | Where-Object { $_ -is [string] } | Write-Output
$missing++
}
if ($missing -gt 0) {
Write-Error "team-ai-directives at ${abs} failed validation (${missing} issue(s)) — not wired"
exit 1
}
$InitOptions = Join-Path $ProjectRoot ".adlc" "init-options.json"
$Config = @{}
if (Test-Path $InitOptions) {
try {
$raw = Get-Content $InitOptions -Raw
if ([string]::IsNullOrWhiteSpace($raw)) { throw "empty" }
$parsed = $raw | ConvertFrom-Json -AsHashtable
if ($parsed -isnot [hashtable]) { throw "not an object" }
$Config = $parsed
} catch {
Write-Error "init-options.json is not valid JSON or not an object — not wired"
exit 1
}
}
$Config["team_ai_directives"] = $abs
$null = New-Item -ItemType Directory -Force -Path (Split-Path $InitOptions)
$Config | ConvertTo-Json -Depth 10 | Set-Content -Path $InitOptions -Encoding UTF8
Write-Output "WIRED team_ai_directives=${abs}"
}
# 3. SCAFFOLD
function New-TeamAiDirectivesScaffold {
param([string]$Dest, [string]$TeamName = "My Team")
$today = Get-Date -Format "yyyy-MM-dd"
if ((Test-Path $Dest) -and (Get-ChildItem $Dest -Force).Count -gt 0) {
Write-Error "Destination '$Dest' already exists and is not empty."
exit 1
}
$null = New-Item -ItemType Directory -Force -Path (Join-Path $Dest "context_modules/rules")
$null = New-Item -ItemType Directory -Force -Path (Join-Path $Dest "context_modules/personas")
$null = New-Item -ItemType Directory -Force -Path (Join-Path $Dest "context_modules/examples")
$null = New-Item -ItemType Directory -Force -Path (Join-Path $Dest "skills")
$readme = @"
# ${TeamName} Team AI Directives
Team AI directives repository for ${TeamName}.
## Getting Started
1. Wire this directives repository into a project:
```
/team-setup
```
Choose "Point to existing local path" and select this directory.
2. Add context modules to `context_modules/` (rules, personas, examples).
3. Add skills to `skills/` and register them in `.skills.json`.
4. Update `CDR.md` as context modules are approved.
See [ADLC Team Skills](https://github.com/tikalk/adlc-team-skills) for full documentation.
"@
Set-Content -Path (Join-Path $Dest "README.md") -Value $readme
$agents = @'
# Agent Instructions
## Structure
- `context_modules/constitution.md` — Team constitution
- `context_modules/rules/` — Team rules and workflows
- `context_modules/personas/` — Team personas
- `context_modules/examples/` — Team examples
- `skills/` — Team skills
- `evals/` — Directive compliance goldensets (pass/fail cases)
- `CDR.md` — Context Directive Records
## Loading Order
1. Load constitution.md first
2. Load relevant rules for the current task
3. Load relevant personas for the current task
4. Load relevant examples for the current task
## Using Skills
Skills are located in the `skills/` directory. Browse available skills using team-skills and install them as needed.
## CDR.md
The CDR.md file tracks approved context contributions. Update it when adding new context modules.
'@
Set-Content -Path (Join-Path $Dest "AGENTS.md") -Value $agents
$cdr = @"
# Context Directive Records
Context Directive Records (CDRs) track decisions about contributing context modules (rules, personas, examples, skills) to team-ai-directives.
## CDR Index
| ID | Target Module | Type | Status | Created | Verified | Age | Descriptor |
|----|---------------|------|--------|---------|----------|-----|------------|
**Stats**: 0 entries | Last Updated: ${today}
"@
Set-Content -Path (Join-Path $Dest "CDR.md") -Value $cdr
$skillsJson = @'
{
"version": "2.0.0",
"source": "team-ai-directives",
"description": "Team skills manifest. The `default` list contains skill names that are auto-installed during project setup. The `external` map contains on-demand skills fetched by URL. The `blocked` list contains skills that must never be installed.",
"default": [],
"external": {},
"blocked": [],
"policy": {
"auto_install_default": true,
"enforce_blocked": true,
"allow_project_override": true
}
}
'@
Set-Content -Path (Join-Path $Dest ".skills.json") -Value $skillsJson
$mcpJson = @'
{
"mcpServers": {}
}
'@
Set-Content -Path (Join-Path $Dest ".mcp.json.example") -Value $mcpJson
$constitution = @"
---
type: Constitution
title: "${TeamName} Constitution"
description: "Team-wide principles and governance"
resource: ./context_modules/constitution.md
tags: [constitution]
generated: { by: agent:team-setup, at: ${today}T00:00:00Z }
id: constitution
cdr_ref: null
created: ${today}
verified:
- { by: agent:team-setup, at: ${today}T00:00:00Z }
status: stable
stale_after: 180d
---
# ${TeamName} Constitution
No team-wide principles defined yet. Add principles as they are established.
"@
Set-Content -Path (Join-Path $Dest "context_modules/constitution.md") -Value $constitution
$indexTop = @"
---
okf_version: "0.2"
---
# Context Modules
* [Rules](rules/index.md) - Team rules and workflows
* [Personas](personas/index.md) - Team personas
* [Examples](examples/index.md) - Team examples
"@
Set-Content -Path (Join-Path $Dest "context_modules/index.md") -Value $indexTop
Set-Content -Path (Join-Path $Dest "context_modules/log.md") -Value "# Context Modules Update Log`n"
Set-Content -Path (Join-Path $Dest "context_modules/rules/index.md") -Value "# Rules`n`nNo rules defined yet. Use /team-levelup to create rules via CDRs."
Set-Content -Path (Join-Path $Dest "context_modules/rules/log.md") -Value "# Rules Update Log`n"
Set-Content -Path (Join-Path $Dest "context_modules/personas/index.md") -Value "# Personas`n`nNo personas defined yet. Use /team-levelup to create personas via CDRs."
Set-Content -Path (Join-Path $Dest "context_modules/personas/log.md") -Value "# Personas Update Log`n"
Set-Content -Path (Join-Path $Dest "context_modules/examples/index.md") -Value "# Examples`n`nNo examples defined yet. Use /team-levelup to create examples via CDRs."
Set-Content -Path (Join-Path $Dest "context_modules/examples/log.md") -Value "# Examples Update Log`n"
$cdrDerived = @"
# Context Directive Records (Derived Index)
> ⚠️ Auto-generated by `/team-repair`. Do not edit manually.
> Source of truth: `context_modules/*/index.md` + module frontmatter.
> Decision lifecycle (Accepted/Rejected) lives in the `adlc` orphan branch `drafts/cdr/`.
## CDR Index
| ID | Path | Type | Description | Generated | Verified | Age | Status |
|----|------|------|-------------|-----------|----------|-----|--------|
**Stats**: 0 entries | Last Updated: ${today}
"@
Set-Content -Path (Join-Path $Dest "CDR.md") -Value $cdrDerived
$null = New-Item -ItemType File -Force -Path (Join-Path $Dest "context_modules/rules/.gitkeep")
$null = New-Item -ItemType File -Force -Path (Join-Path $Dest "context_modules/personas/.gitkeep")
$null = New-Item -ItemType File -Force -Path (Join-Path $Dest "context_modules/examples/.gitkeep")
$null = New-Item -ItemType File -Force -Path (Join-Path $Dest "skills/.gitkeep")
Write-Output "Scaffolded team-ai-directives at ${Dest}"
Write-Output "Team name: ${TeamName}"
Write-Output "Files created: 14"
$missing = Test-TeamAiDirectivesStructure -Dir $Dest
if ($missing -gt 0) {
Write-Output "WARNING: $missing required items missing"
}
}
function New-AgentsOnly {
param([string]$Dest)
$null = New-Item -ItemType Directory -Force -Path $Dest
$agents = @'
# Agent Instructions
## Structure
- `context_modules/constitution.md` — Team constitution
- `context_modules/rules/` — Team rules and workflows
- `context_modules/personas/` — Team personas
- `context_modules/examples/` — Team examples
- `skills/` — Team skills
- `evals/` — Directive compliance goldensets (pass/fail cases)
- `CDR.md` — Context Directive Records
## Loading Order
1. Load constitution.md first
2. Load relevant rules for the current task
3. Load relevant personas for the current task
4. Load relevant examples for the current task
## Using Skills
Skills are located in the `skills/` directory. Browse available skills using team-skills and install them as needed.
## CDR.md
The CDR.md file tracks approved context contributions. Update it when adding new context modules.
'@
Set-Content -Path (Join-Path $Dest "AGENTS.md") -Value $agents
Write-Output "Created AGENTS.md at ${Dest}"
}
# 4. INJECT PROJECT-LEVEL AGENTS.md DIRECTIVE
function Invoke-ProjectAgentsInjection {
param([string]$ProjectRoot = ".")
$TeamDirective = ""
# Resolve team_ai_directives from .adlc/init-options.json
$InitOptions = Join-Path $ProjectRoot ".adlc" "init-options.json"
if (Test-Path $InitOptions) {
try {
$Config = Get-Content $InitOptions -Raw | ConvertFrom-Json
if ($Config.team_ai_directives) {
$TeamDirective = $Config.team_ai_directives
}
} catch {}
}
# Fallback to env var
if (-not $TeamDirective -and $env:TEAM_AI_DIRECTIVES) {
$TeamDirective = $env:TEAM_AI_DIRECTIVES
}
# Fallback to default path
if (-not $TeamDirective) {
$TeamDirective = Join-Path $ProjectRoot "team-ai-directives"
}
$AgentsFile = Join-Path $ProjectRoot "AGENTS.md"
$MarkerStart = "<!-- TEAM_AI_DIRECTIVES START -->"
$MarkerEnd = "<!-- TEAM_AI_DIRECTIVES END -->"
$Section = @"
$MarkerStart
# Team AI Directives
This project is bound by the team AI directives repository at ``$TeamDirective``.
team-boot runs automatically at session start via the event hook (for agents with event support), injecting a lean orientation into the first user message with the team constitution, CDR index, Class Boots catalog, skills registry, and MCP servers.
If the team AI directives context is NOT in your system prompt or first user message (agent without event support), invoke the ``team-boot`` skill before responding to any task or question.
If team AI directives are unconfigured, invoke the ``team-setup`` skill.
Invoke the matching class boot when a task or decision matches a row — invoke it
at the START of the matching task, before planning the todo list and before
implementation, so the class context informs planning; never defer to session end
or post-hoc. When no local memory index exists in the current working directory
and the directory sits inside a workspace (detected via a ``.gitmodules`` marker
in an ancestor), the boot reads the workspace root's ``docs/adlc/memory/`` index
instead (the dual-read order applies: ``docs/adlc/memory`` first, legacy
``.adlc/memory`` fallback).
## Class Boots
| Boot | Injects | Invoke When | Capture Via |
|--|--|--|--|
| architect-boot | ADR index (docs/adlc/memory/adr/ + legacy .adlc/memory/adr/) | architecture work; tech-stack/pattern choice | /architect-specify |
| product-boot | PDR index (docs/adlc/memory/pdr/ + legacy .adlc/memory/pdr/) | product/feature scope, personas, monetization | /product-specify |
| change-boot | ChDR index (docs/adlc/memory/chdr/chdr.md + legacy .adlc/memory/chdr.md) | change-history rationale, reverts, issue-linked commits, git commands w/ human-authored messages, authored PRs, CHANGELOG edits | /change-specify (mining: /change-init) |
| team-levelup | CDR module bodies (team-ai-directives) | CDR descriptor match; reusable team pattern | /team-levelup |
| tech-radar-boot | Tikal Tech Radar context | choosing/evaluating technology | radar context + /architect-specify |
Each class boot emits its class context section and its own searched line (_Searched N records, K matched._).
**Every response MUST include** a Team Context & Decisions section before the task answer (Status ``in use`` rows ground the session in accepted records; Status ``pending`` rows capture emerging decisions in the same table):
## Team Context & Decisions
| ID | Name | Type | Rel | Status | Clarify |
|--|--|--|--|--|--|
| CDR-YYYY-NNN | <name> | <type> | <relevance> | in use | — |
| — | <decision> | <ADR/PDR/CDR/ChDR/Eval> | <trigger> | pending | <clarify skill> |
Plus: ``_Scope: N CDRs · A ADRs · P PDRs · C ChDRs · E evals · M skills — J rows shown · Unrecorded: N pending · Unclarified: M captured drafts._`` — **J MUST equal the number of rows (any indexed type). Status ``in use`` + Clarify ``—`` = grounding context (accepted records only); Status ``pending``/``captured``/``clarified``/``handed off`` + Clarify skill = owed decisions. No draft row may carry Status ``in use`` — drafts appear only as pending decision rows. 0 rows matched → emit the section heading + the counts line only — no table. Zero rows means heading plus counts line only — never a table header. Emit one table row per line. (do not copy a hard-coded row or inflate the count).** Scope names the always-available layer and class indexes (counts only, never content) plus the session's open decisions; class boots append their own scope line when fired.
Render the section as markdown blocks — heading, table (when rows exist), and counts line each on their own lines. Before sending, if the section contains a pipe character but no newline, rewrite it.
## Decision Capture
Detect decisions as they emerge and suggest the matching capture skill — never write drafts or touch ``.adlc/drafts/``; the capture skill writes the draft when the user runs it. Full detection and capture guidance lives in the matching class boot:
- Tech stack / pattern choice → ADR → /architect-specify (pull tech-radar-boot context first for tech selection)
- Feature scope / persona / monetization → PDR → /product-specify
- Reusable team rule / pattern → CDR → /team-levelup
- Revert/hotfix rationale / issue-linked commit → ChDR → /change-specify (mined history: /change-init)
- Eval criterion discovered → Eval → /evals-specify
Maintain the decision rows in the same table above (Status pending/captured/clarified/handed off + Clarify skill); with no pending decisions, emit the heading + the counts line only — no table.
At session end, deliver the clarify prompt naming each uncaptured decision (ID + capture skill) and each captured draft (ID + clarify skill); if the user defers clarify, mark those rows handed off. Only suggest capture when genuinely warranted.
Surface each detected decision as a task-list todo (suggest the capture skill — never write the draft yourself; the capture skill writes to ``.adlc/drafts/{type}/`` when the user runs it — then run the matching clarify skill at session end). After code-modifying tasks, add a trailing todo to sweep Team Context & Decisions until _Unrecorded: 0 pending · Unclarified: 0 drafts_ (a draft leaves Unclarified only via its clarify skill or an explicit user handoff to a named clarify or execute skill).
$MarkerEnd
"@
if (Test-Path $AgentsFile) {
$Content = Get-Content $AgentsFile -Raw -Encoding UTF8
$StartIdx = $Content.IndexOf($MarkerStart)
$EndIdx = $Content.IndexOf($MarkerEnd)
if ($StartIdx -ge 0 -and $EndIdx -gt $StartIdx) {
# Replace existing managed section
$NewContent = $Content.Substring(0, $StartIdx) + $Section + $Content.Substring($EndIdx + $MarkerEnd.Length)
if (-not $NewContent.EndsWith("`n")) { $NewContent += "`n" }
elseif (-not $NewContent.EndsWith("`n`n")) { $NewContent += "`n" }
Set-Content -Path $AgentsFile -Value $NewContent -Encoding UTF8
Write-Output "Updated team AI directives section in $AgentsFile"
} else {
# Append managed section
if ($Content -and -not $Content.EndsWith("`n")) { $Content += "`n" }
if ($Content -and -not $Content.EndsWith("`n`n")) { $Content += "`n" }
$Content += $Section + "`n"
Set-Content -Path $AgentsFile -Value $Content -Encoding UTF8
Write-Output "Injected team AI directives section into $AgentsFile"
}
} else {
# Create new AGENTS.md
Set-Content -Path $AgentsFile -Value ($Section + "`n") -Encoding UTF8
Write-Output "Created $AgentsFile with team AI directives section"
}
}
function Get-ProjectAgentsSectionStatus {
param([string]$ProjectRoot = ".")
$AgentsFile = Join-Path $ProjectRoot "AGENTS.md"
$MarkerStart = "<!-- TEAM_AI_DIRECTIVES START -->"
$MarkerEnd = "<!-- TEAM_AI_DIRECTIVES END -->"
if (-not (Test-Path $AgentsFile)) {
Write-Output "STATUS: missing (no AGENTS.md at $ProjectRoot)"
return
}
$Content = Get-Content $AgentsFile -Raw -Encoding UTF8
if ($Content.Contains($MarkerStart) -and $Content.Contains($MarkerEnd)) {
Write-Output "STATUS: installed ($AgentsFile contains the managed section)"
} else {
Write-Output "STATUS: not-installed ($AgentsFile lacks the managed section)"
}
}
function Remove-ProjectAgentsSection {
param([string]$ProjectRoot = ".")
$AgentsFile = Join-Path $ProjectRoot "AGENTS.md"
$MarkerStart = "<!-- TEAM_AI_DIRECTIVES START -->"
$MarkerEnd = "<!-- TEAM_AI_DIRECTIVES END -->"
if (-not (Test-Path $AgentsFile)) {
Write-Output "Nothing to remove: no AGENTS.md at $ProjectRoot"
return
}
$Content = Get-Content $AgentsFile -Raw -Encoding UTF8
$StartIdx = $Content.IndexOf($MarkerStart)
$EndIdx = $Content.IndexOf($MarkerEnd)
if ($StartIdx -lt 0 -or $EndIdx -le $StartIdx) {
Write-Output "No managed section in $AgentsFile (nothing removed)"
return
}
$NewContent = $Content.Substring(0, $StartIdx) + $Content.Substring($EndIdx + $MarkerEnd.Length)
while ($NewContent.Contains("`n`n`n")) { $NewContent = $NewContent.Replace("`n`n`n", "`n`n") }
$NewContent = $NewContent.Trim("`r", "`n")
if ($NewContent) { $NewContent += "`n" }
Set-Content -Path $AgentsFile -Value $NewContent -Encoding UTF8
Write-Output "Removed team AI directives section from $AgentsFile"
}
# MAIN
if ($Scaffold) {
New-TeamAiDirectivesScaffold -Dest $Scaffold -TeamName $Name
} elseif ($Validate) {
Invoke-TeamAiDirectivesValidation -Dir $Validate
} elseif ($Wire) {
Invoke-TeamAiDirectivesWire -Dir $Wire
} elseif ($AgentsOnly) {
New-AgentsOnly -Dest $AgentsOnly
} elseif ($InjectAgents -ne "") {
Invoke-ProjectAgentsInjection -ProjectRoot $InjectAgents
} elseif ($AgentsStatus -ne "") {
Get-ProjectAgentsSectionStatus -ProjectRoot $AgentsStatus
} elseif ($AgentsUninstall -ne "") {
Remove-ProjectAgentsSection -ProjectRoot $AgentsUninstall
} elseif ($Json) {
Write-OutputJson
} else {
Write-OutputKeyValue
}
SKILL.md
---
name: team-setup
description: Use when bootstrapping a team directives repository from scratch, cloning an existing one, pointing to a local path, or checking an existing configuration.
---
# team-setup
## Overview
`team-setup` is an interactive skill that guides you through setting up the team AI directives. It presents four modes, explains each option, confirms your choice, and executes the setup.
It is invoked in two ways:
- **User-invoked** (`/team-setup`) — anytime, to configure or check a project.
- **Model-invoked by `team-boot`** — automatically at session start when a project has no `.adlc/init-options.json` configuration (self-install), so an unconfigured project wires itself without the user knowing the command.
The skill is non-destructive: it never overwrites existing files or directories. If the target path already contains a configured team AI directives, it detects this and offers the "Already configured" mode instead.
## When to Use
- Starting a new team from scratch and need a neutral team AI directives scaffold to fill in later.
- Your team already has a directives repo on GitHub and you want to clone it locally.
- You have a local team AI directives directory already (e.g., from a previous project) and want to wire it up.
- You're unsure whether the team AI directives is already configured and want a quick check.
- When the project isn't yet wired to a team AI directives (no `.adlc/init-options.json` `team_ai_directives` field).
- Automatically via `team-boot` when it detects an unconfigured project at session start (self-install).
## Decline Handling (when model-invoked by team-boot)
When `team-boot` invokes this skill because the project is unconfigured, the
user may choose not to set up team AI directives right now. Handle decline
explicitly to avoid a re-prompt loop:
- If the user declines at mode selection, do **not** run any mode. Exit
cleanly and tell `team-boot` the user declined.
- Offer a persistent opt-out: *"Don't ask again for this project?"* On yes
(build mode only), write `.adlc/init-options.json` with
`team_ai_directives: null`:
```bash
echo '{"team_ai_directives": null}' > ".adlc/init-options.json"
```
This marker makes `team-boot` skip setup silently on every future prompt.
- In plan/read-only mode, a persistent opt-out cannot be written — the
decline is session-scoped only; tell `team-boot` to defer.
- Never force a mode; the setup is user-consented at every step.
## Core Process
### Goal
Set up a team AI directives using one of four modes.
### Security: Input Validation (all modes)
Before executing any mode, validate every user-supplied value (paths, URLs, team
names). These values are interpolated into shell commands; unvalidated input is
a command-injection vector.
- **Paths** (`{DEST}`, `{ABSOLUTE_PATH}`): reject if they contain any of
`` ` ``, `$`, `;`, `|`, `&`, `(`, `)`, `<`, `>`, newline, or backslash.
Resolve to an absolute path with `realpath`/`Resolve-Path` before use.
- **Team name**: must match `^[A-Za-z0-9 ._-]+$`. Reject anything else.
- **Clone URL** (Mode 1): must start with `https://`. Reject `file://`, `ssh://`,
and any non-`https` scheme unless the user explicitly confirms the risk.
Cloning runs no code from the repo, but the cloned content is read by agents
later — only clone repositories you trust.
If any value fails validation, report which value and why, and re-ask. Never
interpolate a user value into a Python/eval source string — pass it through the
environment (see Mode 2).
**Fast path:** if `.adlc/init-options.json` already contains a valid `team_ai_directives` path, skip to Mode 4 (Already Configured) — do not re-clone, re-point, or re-scaffold.
### Modes at a Glance
| # | Mode | When | Details in |
|---|------|------|-----------|
| 1 | Clone from GitHub | team already has a directives repo | `references/mode-clone.md` |
| 2 | Point to existing local path | directives dir already exists locally | `references/mode-local.md` |
| 3 | Scaffold new empty directives | starting a team from scratch | `references/mode-scaffold.md` |
| 4 | Already configured | check / verify existing wiring | `references/mode-configured.md` |
### Mode 1: Clone from GitHub
Full walkthrough in `references/mode-clone.md`.
### Mode 2: Point to Existing Local Path
Full walkthrough in `references/mode-local.md`.
### Mode 3: Scaffold New Empty team AI directives
Full walkthrough in `references/mode-scaffold.md`.
### Mode 4: Already Configured
Full walkthrough in `references/mode-configured.md`.
### Mode Selection Flow
1. **Explore**: Present the user with four options:
```
How would you like to set up team-ai-directives?
1) Clone from GitHub — Clone an existing repository
2) Point to existing local path — Use a team AI directives you already have
3) Scaffold new empty team AI directives — Create a fresh neutral team AI directives
4) Already configured — Check existing configuration
```
2. **Present**: For the chosen mode, explain what will happen and show details.
3. **Confirm**: Ask the user to confirm before executing.
4. **Write/Execute**: Perform the setup for the chosen mode.
### Script Entry Points (deterministic, headless use)
The Mode 1/2 validation and wiring logic has ONE home — the shared helper
scripts — and is exposed as flags so headless callers (e.g. `adlc-cli team
setup --directives <path>`) run the same checks without an LLM session:
- `scripts/bash/team-helpers.sh [--validate DIR] [--wire DIR]`
- `scripts/powershell/team-helpers.ps1 [-Validate DIR] [-Wire DIR]`
`--validate DIR` runs the Verification structure check (exit 0 when complete,
exit 1 with `MISSING:` lines when not). `--wire DIR` validates, then merges
the absolute path into the project's `.adlc/init-options.json` as
`team_ai_directives` (existing keys preserved; refuses to wire an invalid
structure). Both twins behave identically. Interactive sessions still go
through Mode Selection above; the in-session path and the headless path share
the same implementation.
### Post-Setup Configuration
After any mode completes successfully, update the project configuration:
1. Write `team_ai_directives` to `.adlc/init-options.json`
2. Verify the team AI directives is accessible by running a quick health check:
- `{TEAM_AI_DIRECTIVES}/context_modules/constitution.md` exists
- `{TEAM_AI_DIRECTIVES}/.skills.json` exists and is valid JSON
3. Check the `.gitignore` convention (layout allowlist — see the Gitignore Convention Check step below).
4. Inject the project-level `AGENTS.md` directive — full command and managed-section contract in `references/post-setup-agents.md`.
5. Install MCP config — copy-once contract (seed the project's `.mcp.json`
only when absent; an existing project file wins and is never merged into)
in `references/post-setup-mcp.md`.
#### Gitignore Convention Check
Verify `.gitignore` follows the ADLC allowlist — a fresh `team setup` must
never leave a wholesale `.adlc/` ignore in place (it would hide the tracked
`.adlc/` artifacts the team model relies on):
1. If `.gitignore` contains a bare `.adlc/` rule, replace it (and any
`.adlc/*` + `!.adlc/...` lines that conflict) with the R7 allowlist:
ignore `.adlc/*`, `.adlc/evals/results/`, `.adlc/memory/*`,
`.adlc/team-learn-report.md`, `.adlc/team-levelup-report.md`, and
`graphify-out/`, while re-including `!.adlc/init-options.json`,
`!.adlc/workspace.yml`, `!.adlc/drafts/`, `!.adlc/evals/`,
`!.adlc/memory/`, `!.adlc/memory/evals/`, and
`!.adlc/memory/evals/holdout.json`. The allowlist above is canonical for
this skill; keep `.gitignore` in sync with it.
2. Also verify the agent-install surface rules are ignored (`.agents/`,
`.opencode/`, `.claude/`, `.cursor/`, `.codex/`, `.gemini/`, `.qwen/`,
`.devin/`, `.tabnine/`, `skills-lock.json`, `.skills.json`,
`.events.json`, `.pytest_cache/`, `.ruff_cache/`). Note that `.mcp.json`
is deliberately **not** in this list: it is committed project source —
human-edited, never generated — so a `.gitignore` entry for it is a defect
to report, not a missing rule to add; its guardrail is variable *names*
only, never literal secrets — `env` indirection is the guard, not
`.gitignore`.
3. Never remove unrelated (non-ADLC) rules; only add missing allowlist rules.
4. Tracked legacy files keep working — the allowlist gates only untracked
files, so no migration is required before the switch.
## Common Rationalizations
| Rationalization | Why it's wrong | What to do instead |
|---|---|---|
| "I'll just clone it manually." | Manual cloning skips the `.adlc/init-options.json` wiring, so agents won't find the team AI directives. | Use Mode 1 — it clones AND configures. |
| "I already have a team AI directives directory, I'll just use it." | The directory may be incomplete (missing required files) or not wired in config. | Use Mode 2 — it validates the structure and creates the config entry. |
| "I'll just create a few files by hand." | An incomplete scaffold breaks health checks and agent discovery. | Use Mode 3 — it creates all 10 required files with valid structure. |
| "I'm sure it's already configured." | The path may be stale, moved, or the env var may point to a deleted dir. | Use Mode 4 — it validates the existing configuration. |
| "Scaffolding without a team name is fine." | The team name is used in `README.md` — a blank name makes the team AI directives anonymous and harder to audit. | Always provide a team name in Mode 3. |
## Red Flags
- **Cloning over an existing directory** — Mode 1 refuses if the destination already exists to prevent overwrites.
- **Pointing to a non-existent path** — Mode 2 validates the path exists before proceeding.
- **Scaffolding without required dirs being writable** — Mode 3 creates directories with `mkdir -p` but will fail on permission errors; check permissions first.
- **Skipping the `team_ai_directives` config write** — without this field in `init-options.json`, agents cannot discover the team AI directives.
- **Using a relative path in `init-options.json`** — always resolve to an absolute path so the config is portable across working directories.
- **Skipping `git init` in Mode 3** — a scaffolded team AI directives without git cannot be used by `/team-levelup` (branch/commit/PR flow). Mode 3 runs `git init` automatically; if you skip it, run `git init` manually before `/team-levelup`.
- **Skipping the project-level AGENTS.md injection** — without the `<!-- TEAM_AI_DIRECTIVES START -->` managed section in the project's `AGENTS.md`, agents without event support have no session-start instruction to load team context. The `.adlc/init-options.json` config alone is insufficient — it tells skills where the team AI directives is, but nothing tells the agent to check. (For agents with event support, the session-start hook injects the orientation regardless, but AGENTS.md remains the fallback and the source of the Team Context in Use output contract.)
- **Interpolating user input into Python/shell source strings** — pass paths through the environment (`os.environ`) instead; string interpolation of `$ABSOLUTE_PATH` into a Python one-liner is a command-injection vector.
- **Cloning a non-`https://` URL in Mode 1** — reject `file://`/`ssh://`/other schemes; cloned content is read by agents later, so only clone trusted repos.
- **Skipping the MCP config install** — a project with no `.mcp.json` of its own is never seeded and loses access to team-declared MCP servers; a project that has one only loses the gap report (its file is never written either way).
- **Accepting shell metacharacters in paths or team names** — validate before interpolating into `mkdir`/`git commit`/heredocs (see Input Validation).
- **Treating user decline as an error** — declining setup is a valid outcome; exit cleanly, tell `team-boot` the user declined, and offer the `team_ai_directives: null` opt-out marker (build mode only).
- **Writing the opt-out marker in plan/read-only mode** — a persistent opt-out requires a write; in plan mode the decline is session-scoped and setup defers instead.
## Verification
- [ ] The team AI directives directory exists at the configured path.
- [ ] `{TEAM_AI_DIRECTIVES}/context_modules/constitution.md` exists.
- [ ] `{TEAM_AI_DIRECTIVES}/context_modules/rules/` exists.
- [ ] `{TEAM_AI_DIRECTIVES}/context_modules/personas/` exists.
- [ ] `{TEAM_AI_DIRECTIVES}/context_modules/examples/` exists.
- [ ] `{TEAM_AI_DIRECTIVES}/CDR.md` exists.
- [ ] `{TEAM_AI_DIRECTIVES}/.skills.json` exists and is valid JSON.
- [ ] `.adlc/init-options.json` contains a `team_ai_directives` field with the absolute path.
- [ ] `.gitignore` follows the layout allowlist (no wholesale `.adlc/` ignore; tracked exceptions `!.adlc/init-options.json`, `!.adlc/workspace.yml`, `!.adlc/drafts/`, `!.adlc/evals/`, `!.adlc/memory/evals/holdout.json` present).
- [ ] Project-level `AGENTS.md` exists and contains the `<!-- TEAM_AI_DIRECTIVES START -->` managed section with the event-hook awareness note, fallback `team-boot` invocation, and the Team Context in Use output contract.
- [ ] (Mode 3 only) `git rev-parse --is-inside-work-tree` succeeds inside `{TEAM_AI_DIRECTIVES}`.
- [ ] Running `team-verify` (Phase 0 of team-repair) passes all 7 checks.
- [ ] All user-supplied paths/URLs/team names passed Input Validation (no shell metacharacters; clone URL is `https://`).
- [ ] Mode 2 wrote `team_ai_directives` via the environment (no `$ABSOLUTE_PATH` interpolation into Python source).
- [ ] If `{TEAM_AI_DIRECTIVES}/.mcp.json` exists: the project's `.mcp.json` was seeded from it only when the project had none — an existing project file was left untouched (never merged into or rewritten), any gap between the two was reported rather than closed, and unresolved env vars were highlighted.
- [ ] (Model-invoked by `team-boot`) a user decline exited cleanly without running any mode; the persistent opt-out was offered, and `team_ai_directives: null` was written only in build mode.
## Configuration
- `TEAM_AI_DIRECTIVES` — Path to the team AI directives (overrides `.adlc/init-options.json`).
- `.adlc/init-options.json` — Project-level config file with `team_ai_directives` field.
- Default fallback: `team-ai-directives/` relative to project root.
- `team-helpers.sh` / `team-helpers.ps1` — Shared scripts used for scaffolding and path resolution.
## 12-Factor Alignment
Factor XI (Directives as Code) — establishes a version-controlled team directives repository.