agentsclimarketplace

Ooda setup

Skill mataeil/OODA-loop/skills/ooda-setup

Autonomous operations layer for Claude Code — opens small reviewable PRs for your live side project and re-orients from which ones you merge or reject. HALT file + hard cost cap; you stay in command.

Install
npx -y skills add mataeil/OODA-loop --skill ooda-setup

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 5 stars5 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

3-step project setup wizard. Auto-detects language, test framework, CI, and endpoints. Creates config.json from config.example.json.

SKILL.md

14.2 KB, as published. Nobody here has run it

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:

IndicatorLanguage
package.jsonTypeScript (if *.ts files exist) or JavaScript
go.modGo
Cargo.tomlRust
pyproject.toml / requirements.txtPython
GemfileRuby
pom.xml / build.gradleJava

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):

  1. package.json has scripts.testnpm test
  2. 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>)
  3. go.mod present → go test ./...
  4. Cargo.toml present → cargo test
  5. Gemfile contains rspecbundle exec rspec
  6. 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:

  1. Check package.json scripts.start for a port, or docker-compose.yml port mappings.
  2. 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.
  3. Framework defaults: Next.js/Express → http://localhost:3000, FastAPI/Django → http://localhost:8000, Go → http://localhost:8080.
  4. 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):

  1. ${CLAUDE_PLUGIN_ROOT}/config.example.json (plugin installation)
  2. ~/.ooda-loop/config.example.json (global git clone installation)
  3. ./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_level0
  • 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:

  1. 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

  2. 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

  3. 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.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.