| name | ooda-setup |
| description | 3-step project setup wizard. Auto-detects language, test framework, CI, and endpoints. Creates config.json from config.example.json. |
| ooda_phase | support |
| version | 1.3.0 |
| input | {"files":["config.example.json"],"config_keys":[]} |
| output | {"files":["config.json"],"prs":"none"} |
| safety | {"halt_check":true,"read_only":true} |
| domains | [] |
| chain_triggers | [] |
ooda-setup — Project Setup Wizard
Entry point for new users. Run /ooda-setup after cloning OODA-loop.
Runs a 3-step interactive wizard: scan → confirm domains → write config.json.
Safety Check (Always First)
Read agent/safety/HALT. If the file exists → print [HALT] Setup blocked. Remove agent/safety/HALT to continue. and stop immediately.
If config.json already exists → back up to config.json.bak and ask:
[WARNING] config.json already exists.
(o) Overwrite — discard current config, start fresh
(m) Merge — keep existing domains and safety settings, update detected values only
(a) Abort — cancel setup
Choice (o/m/a):
o → overwrite entirely (backed up to config.json.bak)
m → read existing config, preserve user-modified fields (domains, safety, progressive_complexity.current_level, cost), only update auto-detected values (project.name, test_command, deploy_workflow, health_endpoints)
a or anything else → abort
Step 1/3: Scan Project
Print: [1/3] Scanning your project...
Detect Monorepo — before language detection, check for monorepo indicators:
package.json has "workspaces" field → npm/yarn workspaces monorepo
pnpm-workspace.yaml exists → pnpm monorepo
lerna.json exists → Lerna monorepo
packages/ or apps/ directory exists with multiple sub-package.json files → probable monorepo
If monorepo detected, scan each workspace root for its own indicator files and aggregate results. Print:
Structure: monorepo ({N} packages detected)
If not a monorepo, print Structure: single-package.
For monorepos, auto-detect values from the root config first, then fall back to the first workspace that has the relevant indicator. Use the root package.json scripts for test/build commands unless a workspace-level override exists.
Detect Language — use Glob to check for indicator files:
| Indicator | Language |
|---|
package.json | TypeScript (if *.ts files exist) or JavaScript |
go.mod | Go |
Cargo.toml | Rust |
pyproject.toml / requirements.txt | Python |
Gemfile | Ruby |
pom.xml / build.gradle | Java |
Check framework hints:
package.json: "next" → Next.js, "react" → React, "express" → Express
requirements.txt / pyproject.toml: fastapi → FastAPI, django → Django, flask → Flask
go.mod: gin → Gin, echo → Echo, fiber → Fiber
If no indicator found → language = null.
For monorepos, report the primary language (most common across packages) and note others: e.g. TypeScript (3 packages), Go (1 package).
Detect Test Command (first match wins):
package.json has scripts.test → npm test
pytest.ini or conftest.py exists → pytest (enhanced: if pytest-cov found in requirements.txt or pyproject.toml dependencies, append --cov; detect main source directory from project structure for --cov=<dir>)
go.mod present → go test ./...
Cargo.toml present → cargo test
Gemfile contains rspec → bundle exec rspec
- No match →
"" (warn user)
Detect CI — Glob .github/workflows/*.yml. If a file contains deploy in its name → deploy_workflow = that filename. If workflows exist but none named deploy → use the first one. None found → null.
Detect Health Endpoints — multi-strategy detection:
- Check
package.json scripts.start for a port, or docker-compose.yml port mappings.
- Source-code scan: grep for health route patterns in source files:
- Python:
@app.get("/health"), @app.route("/health"), path("health/", url(r"^health")
- JS/TS:
app.get('/health'), router.get('/health')
- Go:
HandleFunc("/health", Handle("/health"
If a /health route is found, include it with the detected port.
- Framework defaults: Next.js/Express →
http://localhost:3000, FastAPI/Django → http://localhost:8000, Go → http://localhost:8080.
- None detected →
[].
Detect Project Name — from package.json .name, go.mod module last segment, Cargo.toml [package] name, or current directory name.
Check Git — run git rev-parse --is-inside-work-tree 2>/dev/null. If fails → [WARN] Not a git repo. backlog domain will be disabled. Set is_git_repo = false.
Print summary:
[1/3] Scanning your project...
Language: TypeScript (Next.js)
Tests: jest (npm test)
CI: GitHub Actions (deploy.yml)
Endpoints: http://localhost:3000
Name: my-app
Show (not detected) for any missing item.
Step 2/3: Mission + Domain Configuration
Capture the mission FIRST — it is what the loop self-drives toward. This is
the single most important input: an installed OODA-loop without a mission just
cycles by staleness; with one, every Decide is pulled toward the project's
purpose. Ask:
[2/3] In one sentence — what is this project for, and what does "done well" mean?
(e.g. "Keep the live URL shortener fast and reliable and ship the launch
backlog." This steers every cycle. Press Enter to skip — not recommended.)
Write the answer to config.mission (empty string if skipped — scoring then
falls back to staleness-only, with a printed warning that the loop has no
purpose to drive toward).
Then, for EACH domain enabled below, set mission_alignment in [0.0, 1.0] — how
much working that domain advances the stated mission. Infer a sensible default
from the mission text and the domain's role (a domain the mission names directly
→ ~1.0; a generally-useful domain → ~0.6; an off-mission/curiosity domain like
competitors for an internal tool → ~0.1), then show the inferred values and let
the operator adjust:
service_health alignment 1.0 (mission mentions "reliable")
backlog alignment 1.0 (mission mentions "ship the backlog")
competitors alignment 0.2 (not on the critical path)
Adjust any? (e.g. "competitors 0.0", or Enter to accept)
Print:
[2/3] Recommended domains:
✓ service_health (weight 2.0) — always recommended
✓ test_coverage (weight 0.5) — if test command detected
✓ backlog (weight 0.3) — if git repo detected
? business_strategy (weight 1.0) — optional
? ux_evolution (weight 1.0) — optional
? competitors (weight 0.3) — optional
Which domains to enable? (Enter for recommended, or list names separated by spaces)
Defaults: service_health always; test_coverage if test_command detected; backlog if is_git_repo = true.
Read input: empty → use defaults. Space-separated names → enable exactly those. Validate against known domain names; re-ask once if invalid.
Step 3/3: Create Config
Print: [3/3] Creating config.json...
Locate the config template. Search in order (first found wins):
${CLAUDE_PLUGIN_ROOT}/config.example.json (plugin installation)
~/.ooda-loop/config.example.json (global git clone installation)
./config.example.json (running from the OODA-loop repo itself)
If none found → generate a minimal config with sensible defaults (all fields from the
schema with default values). Print [WARN] config.example.json not found — using built-in defaults.
Apply detected values:
project.name → detected name
test_command → detected command or ""
deploy_workflow → detected filename or null
health_endpoints → detected array or []
progressive_complexity.current_level → 0
- Each domain:
enabled = true if in user-chosen list, false otherwise
Graceful degradation:
- No language detected → ask user:
What language does your project use?
- No test command → set
"", print [WARN] Set test_command in config.json manually.
- No CI → set null, print
[INFO] deploy_workflow set to null.
- Not a git repo → force backlog enabled = false
Validate the JSON structure before writing. Write to config.json. Never write tokens, keys, or passwords — skip any detected value that looks like a secret and warn.
Scaffold project directories — create the OODA runtime directories if they don't exist:
mkdir -p agent/state/evolve
mkdir -p agent/safety
Initialize state files: state.json, confidence.json, metrics.json, action_queue.json,
memos.json (with "score_adjustments": {}, "interventions": [], "history": []),
goals.json, episodes.json, principles.json, skill_gaps.json,
reflections.json ({"schema_version": "1.0.0", "reflections": []}),
outcomes.json ({"schema_version": "1.0.0", "entries": []}),
cost_ledger.json, CHANGELOG.md in agent/state/evolve/. Use canonical schemas.
(cascades.json, cycle_log.jsonl, and per-domain lens.json are created lazily by evolve.)
Auto-derive verifiable goals FROM the mission (v1.4.1, Iteration 6). Don't
make the operator hand-write done-conditions — propose them from the mission
text + the detected stack, then let them confirm. This is the "state your
purpose, the loop figures out how to measure it" install flow. Map mission
phrases to concrete metric_commands, e.g.:
| mission says… | proposed goal + metric_command |
|---|
| "green tests" / "reliable" / "correctness" | g_tests: {test_command} exit 0 (or coverage ≥ N from test_coverage.json) |
| "ship the backlog" / "close issues" | g_backlog: jq '.pending|length==0' agent/state/evolve/action_queue.json |
| "fast" / "p95 latency" / "uptime" | g_health: jq '.status=="healthy"' agent/state/service_health.json |
| (a named feature/MVP) | g_mvp: a milestone the operator pastes a check for |
Present 1–3 derived goals: Proposed done-conditions from your mission: … (Enter to accept, or edit). Write the accepted ones to goals.json as active
goals with progress: 0.0. If the mission was skipped or nothing maps, fall
through to the manual prompt below.
Or seed at least one verifiable goal manually (loop-engineering done-condition).
A loop with no written, machine-checkable goal "runs until the money runs out."
Write goals.json with one active goal whose metric_command is a shell command
that prints a number or boolean the engine can check each cycle (Step 2-C drives
goal.progress from it, and the Loop Scorecard's Goal Progress KPI surfaces it):
{ "schema_version": "1.0.0", "goals": [
{ "id": "g1", "title": "<your done-condition, e.g. drain the action backlog>",
"status": "active", "progress": 0.0, "target": 1.0,
"metric_command": "jq '.pending|length==0' agent/state/evolve/action_queue.json" } ] }
Ask the operator for one goal during setup; if they skip, write an empty
{ "goals": [] } and print a reminder that the loop has no done-condition yet.
Add to .gitignore if not already present:
config.json
agent/safety/HALT
agent/state/**/*.lock
agent/state/evolve/.lock
Do NOT gitignore agent/state/ itself: evolve Step 6-D deliberately commits the
state JSONs (decision history, confidence, episodes — the auditable memory of
the loop). Ignoring the directory silently turns every 6-D commit into a no-op
and the project loses its own learning trail (issue #31). Only the transient
lock files (and the HALT kill-switch) stay untracked.
Print:
[3/3] Setup complete!
Created: config.json
Level: 0 (Just watching)
Domains: service_health, test_coverage, backlog
Next steps:
/evolve — Run first cycle (observe-only)
/ooda-status — Check current state
/ooda-config — Modify configuration
Step 4/4: First Look (Verification Mini-Cycle)
After printing the "Setup complete!" block, run a quick verification pass against
the just-written config.json. This gives the user real data from their project
within 60 seconds of setup, before they ever run /evolve.
Print: [4/4] Taking a first look at your project...
Each domain check runs independently so one failure cannot block the others.
Apply a 15-second overall timeout for all checks combined and a 10-second per-domain timeout:
-
test_coverage: If config.test_command is non-empty, run it with a 10-second
timeout. Parse the output for pass/fail counts and coverage percentage.
Print: test_coverage {passed}/{total} passing ({coverage}% coverage)
If test_command is empty: print test_coverage [Skip] No test_command configured
If command fails or times out: print test_coverage [Error] Test command failed
-
service_health: If config.health_endpoints is non-empty, curl each endpoint
with --max-time 5. Print status code and response time.
Print: service_health {url} → {status} ({time}ms)
If array is empty: print service_health [Skip] No health endpoints configured
If curl fails: print service_health {url} → unreachable
-
backlog: Run gh issue list --state open --limit 100 --json number 2>/dev/null.
Count results.
Print: backlog {count} open issues
If gh is not installed or fails: print backlog [Skip] gh CLI not available
If no issues: print backlog 0 open issues
Print: Ready. Run /evolve for your first full cycle.
This step is informational only — it does not write state files or modify config.json.
If the entire step fails or times out, print [4/4] Verification skipped. and continue.
After the verification mini-cycle, read the domains from the written config.json. For any domain where status: "available" (i.e., not enabled and not explicitly disabled), append:
3 optional skills are available but not yet configured:
/scan-market — market research and strategic analysis
/scan-ux — UX audit and UI analysis
/scan-competitors — competitor monitoring
Create any of these when you're ready:
/ooda-skill create <name>
Or disable ones you don't need:
/ooda-skill disable <name>
Only list domains whose status field equals "available" in the generated config. If no domains have status: "available", omit this block entirely.