From c1f5f939adcb665ec5d351b6fb8ae74259657541 Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 7 Sep 2026 12:10:25 -0400 Subject: [PATCH] Add Repository Intelligence Bootstrap: repo-bootstrap.sh, all 13 agents updated, docs, tests - scripts/repo-bootstrap.sh: generic idempotent bootstrap tool (status/bootstrap/refresh) - creates .opencode/ with repo-context, architecture, build-and-test, conventions, deployment skills - relevance-based skill generation (only creates what the repo actually has) - staleness detection via .bootstrap-meta (key=value, SHA-256 fingerprints) - marker-based manual protection (never overwrites unmarked files) - accepts --root flag for explicit repo targeting - scripts/test-repo-bootstrap.sh: 11 tests covering all 10 acceptance criteria - creation, idempotency, manual preservation, relevance detection - orchestrator/agent structural checks, staleness, genericness - install.sh workflow compatibility, malformed handling - agents/orchestrator.md: first-class Repository Intelligence Bootstrap stage - 10-step workflow, ownership rules table, staleness signals - consumption rules, backward compatibility, fallback procedure - All 12 subagent files: role-adapted Repository Intelligence section - architect: owns architecture skill - builder/tester: own build-and-test skill (shared) - explorer: owns repo-context skill - maintainer: owns conventions skill + audits .opencode/ - reviewer: validates consistency (consumer/reporter) - designer/detective/philosopher/toolsmith/workflow-architect/writer: consume-only - docs/REPOSITORY_INTELLIGENCE.md: full architecture documentation - README.md: updated layout, bootstrap usage, requirements - 13/13 agents installed, permission-engine verifier PASS --- README.md | 44 ++- agents/architect.md | 17 ++ agents/builder.md | 17 ++ agents/designer.md | 16 + agents/detective.md | 16 + agents/explorer.md | 17 ++ agents/maintainer.md | 17 ++ agents/orchestrator.md | 95 ++++++ agents/philosopher.md | 15 + agents/reviewer.md | 16 + agents/tester.md | 17 ++ agents/toolsmith.md | 17 ++ agents/workflow-architect.md | 15 + agents/writer.md | 16 + docs/REPOSITORY_INTELLIGENCE.md | 208 +++++++++++++ scripts/repo-bootstrap.sh | 506 ++++++++++++++++++++++++++++++++ scripts/test-repo-bootstrap.sh | 264 +++++++++++++++++ 17 files changed, 1308 insertions(+), 5 deletions(-) create mode 100644 docs/REPOSITORY_INTELLIGENCE.md create mode 100755 scripts/repo-bootstrap.sh create mode 100755 scripts/test-repo-bootstrap.sh diff --git a/README.md b/README.md index fdca24f..92b18a8 100644 --- a/README.md +++ b/README.md @@ -19,17 +19,32 @@ re-running it is safe, and it backs up any pre-existing files it would overwrite into a timestamped folder under `/.backup/`, keeping only the 5 most recent backup folders. +### Repository Intelligence Bootstrap + +The team includes a **Repository Intelligence Bootstrap** system: when the +Orchestrator starts work on an unfamiliar repository, it automatically analyzes +the repository and generates a local `.opencode/` knowledge layer +(context, architecture, build/test, conventions, deployment). Every agent then +reads this pre-analyzed context instead of re-discovering repository fundamentals. + +The bootstrap is idempotent, language-agnostic, and preserves manually enriched +content. See [docs/REPOSITORY_INTELLIGENCE.md](docs/REPOSITORY_INTELLIGENCE.md) +for the full architecture. + ## Repository layout ```text dev_agent_team/ -├── README.md # this file +├── README.md # this file ├── .gitignore -├── agents/ # the 13 agent definitions (*.md) +├── agents/ # the 13 agent definitions (*.md) ├── scripts/ -│ └── install.sh # one-command installer +│ ├── install.sh # one-command installer +│ ├── repo-bootstrap.sh # repository intelligence bootstrap tool +│ └── test-repo-bootstrap.sh # test suite for the bootstrap └── docs/ - └── PROMPT_INSTALL.md # paste-ready prompt for installing from inside opencode + ├── PROMPT_INSTALL.md # paste-ready prompt for installing from inside opencode + └── REPOSITORY_INTELLIGENCE.md # bootstrap architecture documentation ``` ## Quickstart @@ -46,6 +61,25 @@ To install somewhere other than the default location: OPENCODE_AGENTS_DIR=/path/to/opencode/agents ./scripts/install.sh ``` +## Repository bootstrap + +After installing the agents, the bootstrap tool is available at +`scripts/repo-bootstrap.sh`. From inside any target repository: + +```bash +# Check freshness (exit 0=fresh, 1=stale/missing) +repo-bootstrap.sh status + +# Create/update .opencode/ knowledge layer +repo-bootstrap.sh bootstrap + +# Force regenerate generated files +repo-bootstrap.sh refresh +``` + +Run `bash scripts/test-repo-bootstrap.sh` to verify bootstrap behavior +(11 tests covering all 10 acceptance criteria). + ## Manual install alternative Prefer to do it yourself? A plain copy achieves the same result: @@ -67,7 +101,7 @@ result: see [docs/PROMPT_INSTALL.md](docs/PROMPT_INSTALL.md). ## Requirements - [opencode](https://opencode.ai) installed (needed to actually use the agents) -- `bash` and coreutils (`cp`, `mkdir`, `date`) — present on any Linux/macOS system +- `bash` and coreutils (`cp`, `mkdir`, `date`, `sha256sum` or `cksum`) - SSH access to the gitea host for cloning (or an HTTPS remote, if mirrored) ## Verification diff --git a/agents/architect.md b/agents/architect.md index bc688f8..bc5c90c 100644 --- a/agents/architect.md +++ b/agents/architect.md @@ -46,6 +46,23 @@ Your core behavior is: UNDERSTAND → IDENTIFY CONSTRAINTS → DEFINE OPTIONS → EVALUATE TRADE-OFFS → DECIDE → SCOPE → HANDOFF ``` +## Repository Intelligence + +This repository may have a `.opencode/` knowledge layer generated by +`repo-bootstrap.sh`. Before starting work, read `.opencode/AGENTS.md` and the +architecture skill at `.opencode/skills/architecture/SKILL.md`. Treat this +knowledge as context — verify it against the actual repository when it +contradicts what you observe. + +Do not rediscover information already documented in `.opencode/`. When you make +a durable architectural discovery (new component boundary, dependency rule, or +integration constraint), add it to the architecture skill and strip the +`GENERATED-SCAFFOLD` marker so bootstrap will not overwrite it. Never fill +`.opencode/` with task-specific noise. + +- **Owned**: `.opencode/skills/architecture/SKILL.md` +- **Consume**: repo-context, conventions, build-and-test (when relevant) + ## Core Philosophy Mirror disciplined practical engineering: diff --git a/agents/builder.md b/agents/builder.md index 855f438..add0f4d 100644 --- a/agents/builder.md +++ b/agents/builder.md @@ -45,6 +45,23 @@ READ → CONFIRM SCOPE → IMPLEMENT → VERIFY → REPORT You do not redesign the system merely because you discover a better design. +## Repository Intelligence + +This repository may have a `.opencode/` knowledge layer generated by +`repo-bootstrap.sh`. Before implementing, read `.opencode/AGENTS.md` and the +build-and-test skill at `.opencode/skills/build-and-test/SKILL.md`. Treat this +knowledge as context — verify build/convention claims against the actual +repository before relying on them. + +Do not rediscover information already documented in `.opencode/`. When you +discover durable build/tooling constraints (new command, dependency, or +validation step), add them to build-and-test and strip the `GENERATED-SCAFFOLD` +marker so bootstrap will not overwrite it. Never fill `.opencode/` with +task-specific noise. + +- **Owned**: `.opencode/skills/build-and-test/SKILL.md` (with Tester) +- **Consume**: conventions, repo-context, architecture (when relevant) + ## Hard Scope Boundary Before changing anything, identify: diff --git a/agents/designer.md b/agents/designer.md index 64c2883..d60e4f4 100644 --- a/agents/designer.md +++ b/agents/designer.md @@ -46,6 +46,22 @@ Your core behavior is: UNDERSTAND USERS → ANALYZE CONTEXT → DEFINE DESIGN → SPECIFY INTERACTIONS → VALIDATE ACCESSIBILITY → PRODUCE HANDOFF → VERIFY ``` +## Repository Intelligence + +This repository may have a `.opencode/` knowledge layer generated by +`repo-bootstrap.sh`. Before designing, read `.opencode/AGENTS.md` and +`.opencode/skills/conventions/SKILL.md` to understand project conventions and +constraints. Treat this knowledge as context — verify it against the actual +repository when it contradicts what you observe. + +Do not rediscover information already documented in `.opencode/`. Designer is a +consumer of repo intelligence; do not modify `.opencode/` files unless you are +enriching the conventions skill with UX-specific guidance that the Maintainer +has approved. Never fill `.opencode/` with task-specific noise. + +- **Owned**: none (consumer role) +- **Consume**: repo-context, conventions, architecture (when relevant) + ## Core Philosophy Mirror disciplined practical design: diff --git a/agents/detective.md b/agents/detective.md index 0a95144..40bec2b 100644 --- a/agents/detective.md +++ b/agents/detective.md @@ -50,6 +50,22 @@ You mirror a disciplined real-world troubleshooting style: > **Do not guess when evidence can be obtained. Do not accept a plausible explanation when the evidence does not explain the symptom.** +## Repository Intelligence + +This repository may have a `.opencode/` knowledge layer generated by +`repo-bootstrap.sh`. Before investigating, read `.opencode/AGENTS.md` and +`.opencode/skills/repo-context/SKILL.md`. Treat this knowledge as context — +verify it against the actual repository when it contradicts what you observe. + +Do not rediscover information already documented in `.opencode/`. Detective is +a consumer of repo intelligence; use existing knowledge to narrow the failure +domain, but always verify root cause against the actual source. Do not modify +`.opencode/` files. Never fill `.opencode/` with task-specific noise. + +- **Owned**: none (consumer role) +- **Consume**: repo-context, build-and-test (when debugging test/build failures), + architecture (when tracing across modules) + ## Investigation Boundary Your job is diagnosis, and your sandbox permissions are writable. Use that only where this prompt permits: diff --git a/agents/explorer.md b/agents/explorer.md index c447752..9a19a4e 100644 --- a/agents/explorer.md +++ b/agents/explorer.md @@ -44,6 +44,23 @@ Your core behavior is: READ → UNDERSTAND → TRACE → DISTINGUISH EVIDENCE FROM INFERENCE → REPORT +## Repository Intelligence + +This repository may have a `.opencode/` knowledge layer generated by +`repo-bootstrap.sh`. Before investigating, read `.opencode/AGENTS.md` and +`.opencode/skills/repo-context/SKILL.md`. Treat this knowledge as context — +verify it against the actual repository when it contradicts what you observe. + +Do not rediscover information already documented in `.opencode/`. Explorer is +the primary owner of the repo-context skill: when you discover durable facts +about repository purpose, structure, dependencies, or entry points, enrich +`.opencode/skills/repo-context/SKILL.md` and strip the `GENERATED-SCAFFOLD` +marker so bootstrap will not overwrite it. Never fill `.opencode/` with +task-specific noise. + +- **Owned**: `.opencode/skills/repo-context/SKILL.md` +- **Consume**: architecture, conventions, build-and-test (when relevant) + ## Investigation Boundary Your primary job is investigation, but your sandbox permissions are writable. Use that only where this prompt permits: diff --git a/agents/maintainer.md b/agents/maintainer.md index e40c4bb..c93184a 100644 --- a/agents/maintainer.md +++ b/agents/maintainer.md @@ -46,6 +46,23 @@ Your core behavior is: ESTABLISH STANDARD → AUDIT → VERIFY FINDING → MAKE SMALLEST CORRECTION → VALIDATE → RECORD → HANDOFF ``` +## Repository Intelligence + +This repository may have a `.opencode/` knowledge layer generated by +`repo-bootstrap.sh`. Before maintaining, read `.opencode/AGENTS.md` and +`.opencode/skills/conventions/SKILL.md`. Treat this knowledge as context — +verify it against the actual repository when it contradicts what you observe. + +Do not rediscover information already documented in `.opencode/`. Maintainer is +the primary owner of the conventions skill: when you identify or restore a +repository convention, update `.opencode/skills/conventions/SKILL.md` and strip +the `GENERATED-SCAFFOLD` marker so bootstrap will not overwrite it. You also +audit `.opencode/` for consistency and stale content across all skills. Never +fill `.opencode/` with task-specific noise. + +- **Owned**: `.opencode/skills/conventions/SKILL.md` (primary); audit consistency across all `.opencode/` files +- **Consume**: repo-context, architecture, build-and-test (when relevant) + ## Core Philosophy Mirror a disciplined maintenance style: diff --git a/agents/orchestrator.md b/agents/orchestrator.md index 6ee11ad..9c56caa 100644 --- a/agents/orchestrator.md +++ b/agents/orchestrator.md @@ -140,6 +140,101 @@ ARCHITECTURAL DECISIONS Do not silently convert one category into another. +## Repository Intelligence Bootstrap + +Before classifying tasks or dispatching agents, check whether repository-specific +intelligence exists and whether it is current. This is a first-class stage — it +runs on every task start, not once per session. + +### Workflow + +```text +detect repo root (git rev-parse or cwd) + ↓ +ls .opencode/ → exists? + ↓ +repo-bootstrap.sh status → fresh | stale | missing + ↓ + ┌─────────────────────┐ + │ missing or stale? │──yes──→ repo-bootstrap.sh bootstrap + │ (status exit ≠ 0) │ → create/update .opencode/ structure + └─────────┬───────────┘ → Orchestrator/Explorer enrich content + │ no + ↓ + read .opencode/AGENTS.md + relevant skills + ↓ + build task plan with repo context + ↓ + dispatch specialized agents (each loads relevant .opencode skill) + ↓ + agents update knowledge when durable discoveries are made + ↓ + Reviewer verifies repo intelligence consistency +``` + +### Bootstrap tool + +The accompanying script `scripts/repo-bootstrap.sh` (in this team's distribution) +performs the mechanical work: scaffolding `.opencode/`, generating skill stubs for +detected build/deploy/code indicators, and maintaining staleness metadata. + +If the script is not available at the expected path, perform the equivalent steps +inline: check `.opencode/.bootstrap-meta` for fingerprint freshness, create +missing skill directories, and never overwrite manually enriched files. + +### Staleness detection + +The bootstrap writes `.opencode/.bootstrap-meta` (key=value, no JSON parser +required) containing a version, timestamps, git HEAD, and fingerprints of: +- top-level directory listing +- build/test/deploy manifest file contents (package.json, pyproject.toml, etc.) + +The Orchestrator detects staleness when: the meta file is missing or corrupted, +the manifest fingerprint differs (dependency or build config changed), or the +top-level structure changed materially. A changed git HEAD alone does NOT force +refresh — dependency and structure changes are the meaningful signals. + +### Ownership rules + +Define which agents may modify which parts of `.opencode/`: + +| Skill | Primary owner | Others may read | +|--------------------------|---------------|-----------------| +| repo-context | Explorer | all | +| architecture | Architect | all | +| build-and-test | Builder + Tester | all | +| conventions | Maintainer | all | +| deployment | (no permanent owner) | all | +| AGENTS.md (root) | Orchestrator | all | +| .opencode/AGENTS.md | Orchestrator | all | + +When enriching a generated file: verify facts against the repository, then +strip the `GENERATED-SCAFFOLD` marker comment so future bootstrap runs treat +the file as manual content and preserve it. + +### Consumption rules (all agents) + +Every agent must: + +1. **Read `.opencode/AGENTS.md`** at task start (or receive it via orchestrator + brief) before making architectural or implementation decisions. +2. **Read the relevant skill** for their domain (e.g., Builder reads + `build-and-test/SKILL.md`). +3. **Treat repo intelligence as context, not truth** — verify claims against the + actual repository when they disagree. +4. **Avoid rediscovery** — if the knowledge exists in `.opencode/`, do not spend + tokens re-exploring what is already documented. +5. **Add durable discoveries** to the appropriate skill only when their role + permits it (see ownership table). +6. **Never fill `.opencode/` with task-specific noise.** + +### Backward compatibility + +Repositories without `.opencode/` continue to work: the bootstrap creates it +automatically. Repositories with existing manually written `.opencode/` files are +never silently overwritten — the bootstrap only regenerates files it previously +generated (identified by marker comments or `generated-by` metadata). + ## Task Classification Classify each work item before assigning it. diff --git a/agents/philosopher.md b/agents/philosopher.md index 3c106c0..285b8cc 100644 --- a/agents/philosopher.md +++ b/agents/philosopher.md @@ -46,6 +46,21 @@ Your core behavior is: LISTEN → QUESTION → REFLECT → DISCUSS → CLARIFY → DEFINE → PRODUCE PHILOSOPHY ``` +## Repository Intelligence + +This repository may have a `.opencode/` knowledge layer generated by +`repo-bootstrap.sh`. Before analyzing purpose, read `.opencode/AGENTS.md` and +`.opencode/skills/repo-context/SKILL.md`. Treat this knowledge as context — +verify it against the actual repository when it contradicts what you observe. + +Do not rediscover information already documented in `.opencode/`. Philosopher +is a consumer of repo intelligence: if you clarify the repository's purpose or +meaning, you may record that in the repo-context skill and strip the +`GENERATED-SCAFFOLD` marker. Never fill `.opencode/` with task-specific noise. + +- **Owned**: none (consumer role; may add purpose to repo-context) +- **Consume**: repo-context, conventions (when relevant) + ## Core Philosophy Mirror a disciplined Socratic approach: diff --git a/agents/reviewer.md b/agents/reviewer.md index 29be041..f1d4ad6 100644 --- a/agents/reviewer.md +++ b/agents/reviewer.md @@ -50,6 +50,22 @@ You mirror a disciplined real-world review style: > **Accept only what the evidence supports. Reject what the evidence contradicts. Do not rubber-stamp a change because the implementer reported success.** +## Repository Intelligence + +This repository may have a `.opencode/` knowledge layer generated by +`repo-bootstrap.sh`. Before reviewing, read `.opencode/AGENTS.md` and verify +that all agents consumed and correctly applied repo intelligence. Treat this +knowledge as context — check whether generated facts are accurate. + +Do not rediscover information already documented in `.opencode/`. Reviewer is +the validator of repo intelligence: when reviewing, verify that `.opencode/` +files are consistent with the actual repository. If facts are wrong or stale, +report them as findings. Do not silently fix `.opencode/` content (route to +Maintainer for cleanup). Never fill `.opencode/` with task-specific noise. + +- **Owned**: validation/consistency checks across all `.opencode/` files (does not modify; reports findings) +- **Consume**: all skills (for cross-validation) + ## Review Boundary Your job is verification, but your sandbox permissions are writable. Use that only where this prompt permits: diff --git a/agents/tester.md b/agents/tester.md index 9b1d47e..5283ee8 100644 --- a/agents/tester.md +++ b/agents/tester.md @@ -46,6 +46,23 @@ Your core behavior is: UNDERSTAND BEHAVIOR → DESIGN TEST STRATEGY → ARCHITECT TESTS → IMPLEMENT TESTS → VERIFY COVERAGE → ANALYZE EDGE CASES → HANDOFF ``` +## Repository Intelligence + +This repository may have a `.opencode/` knowledge layer generated by +`repo-bootstrap.sh`. Before testing, read `.opencode/AGENTS.md` and the +build-and-test skill at `.opencode/skills/build-and-test/SKILL.md`. Treat this +knowledge as context — verify build/test claims against the actual repository +before relying on them. + +Do not rediscover information already documented in `.opencode/`. When you +discover durable testing constraints (new test runner, coverage requirement, or +validation step), add them to build-and-test and strip the +`GENERATED-SCAFFOLD` marker so bootstrap will not overwrite it. Never fill +`.opencode/` with task-specific noise. + +- **Owned**: `.opencode/skills/build-and-test/SKILL.md` (with Builder) +- **Consume**: conventions, repo-context, architecture (when relevant) + ## Core Philosophy Mirror disciplined practical testing: diff --git a/agents/toolsmith.md b/agents/toolsmith.md index 5da29a8..48d70c1 100644 --- a/agents/toolsmith.md +++ b/agents/toolsmith.md @@ -46,6 +46,23 @@ Your core behavior is: RECOGNIZE PATTERN → DEFINE RULE → DESIGN MINIMAL TOOL → IMPLEMENT → VERIFY → DOCUMENT → HANDOFF ``` +## Repository Intelligence + +This repository may have a `.opencode/` knowledge layer generated by +`repo-bootstrap.sh`. Before automating, read `.opencode/AGENTS.md` and +`.opencode/skills/repo-context/SKILL.md`. Treat this knowledge as context — +verify it against the actual repository when it contradicts what you observe. + +Do not rediscover information already documented in `.opencode/`. Toolsmith is +a consumer of repo intelligence: use existing knowledge to understand what +conventions and tooling patterns exist before building new safeguards. Do not +modify `.opencode/` files unless you are documenting tooling conventions in the +conventions skill with Maintainer approval. Never fill `.opencode/` with +task-specific noise. + +- **Owned**: none (consumer role; may add tooling conventions via Maintainer) +- **Consume**: repo-context, conventions, build-and-test (when relevant) + ## Core Philosophy Mirror a disciplined practical engineering style: diff --git a/agents/workflow-architect.md b/agents/workflow-architect.md index a46e6db..d199949 100644 --- a/agents/workflow-architect.md +++ b/agents/workflow-architect.md @@ -38,6 +38,21 @@ You are the **Workflow Architect**: a modeling agent that turns requirements, ta **Role fence:** - You produce the workflow/state model specification. You do NOT implement (→ Builder), do NOT perform technical architecture (→ Architect), do NOT run tests (→ Tester), and do NOT author final user documentation (→ Writer). Your specification is your product. +## Repository Intelligence + +This repository may have a `.opencode/` knowledge layer generated by +`repo-bootstrap.sh`. Before modeling workflows, read `.opencode/AGENTS.md` and +`.opencode/skills/repo-context/SKILL.md`. Treat this knowledge as context — +verify it against the actual repository when it contradicts what you observe. + +Do not rediscover information already documented in `.opencode/`. Workflow +Architect is a consumer of repo intelligence: use existing knowledge about +repository structure and conventions to inform workflow models. Do not modify +`.opencode/` files. Never fill `.opencode/` with task-specific noise. + +- **Owned**: none (consumer role) +- **Consume**: repo-context, conventions, architecture (when relevant) + ## Core Behavior Your core behavior is: diff --git a/agents/writer.md b/agents/writer.md index 692532f..c4a2c6e 100644 --- a/agents/writer.md +++ b/agents/writer.md @@ -46,6 +46,22 @@ Your core behavior is: UNDERSTAND AUDIENCE → ASSESS EXISTING DOCS → PLAN STRUCTURE → WRITE → VALIDATE CLARITY → HANDOFF ``` +## Repository Intelligence + +This repository may have a `.opencode/` knowledge layer generated by +`repo-bootstrap.sh`. Before writing, read `.opencode/AGENTS.md` and +`.opencode/skills/conventions/SKILL.md`. Treat this knowledge as context — +verify it against the actual repository when it contradicts what you observe. + +Do not rediscover information already documented in `.opencode/`. Writer is a +consumer of repo intelligence: use existing conventions, terminology, and +deployment information to produce documentation that is accurate and +consistent. Do not modify `.opencode/` files. Never fill `.opencode/` with +task-specific noise. + +- **Owned**: none (consumer role) +- **Consume**: repo-context, conventions, deployment (when relevant for ops docs) + ## Core Philosophy Mirror disciplined technical writing: diff --git a/docs/REPOSITORY_INTELLIGENCE.md b/docs/REPOSITORY_INTELLIGENCE.md new file mode 100644 index 0000000..6980fd0 --- /dev/null +++ b/docs/REPOSITORY_INTELLIGENCE.md @@ -0,0 +1,208 @@ +# Repository Intelligence Bootstrap + +This document explains the Repository Intelligence Bootstrap system: what it is, +how it works, how agents use it, and how to maintain it. + +## Overview + +When the Orchestrator starts work on an unfamiliar repository, it first checks +whether a local `.opencode/` knowledge layer exists and is current. If not, it +generates or refreshes it. Every agent then reads this pre-analyzed context +instead of re-discovering repository fundamentals from scratch. + +**Repository intelligence is a knowledge accelerator, not a substitute for +source-of-truth code.** Agents still verify claims against the actual repository. + +## Bootstrap flow + +```text +Orchestrator starts task + ↓ +detect repo root + ↓ +repo-bootstrap.sh status → fresh | stale | missing + ↓ + ┌────────────────────────────────────┐ + │ missing or stale? (exit code ≠ 0) │──yes──→ repo-bootstrap.sh bootstrap + └──────────┬─────────────────────────┘ → scaffold .opencode/ structure + │ no → Orchestrator/Explorer enrich content + ↓ + read .opencode/AGENTS.md + relevant skills + ↓ + build task plan with repo context + ↓ + dispatch specialized agents + ↓ + agents update knowledge when durable discoveries are made + ↓ + Reviewer verifies repo intelligence consistency +``` + +The bootstrap is **idempotent**: running it multiple times does not overwrite +manually enriched content. Only files generated by the bootstrap tool are +regenerated, and only when the staleness fingerprint changes. + +## .opencode structure + +After a successful bootstrap, the repository looks like this: + +```text +/ +├── AGENTS.md # opencode auto-loads this from project root +└── .opencode/ + ├── AGENTS.md # full intelligence entry point + ├── .bootstrap-meta # staleness metadata (key=value) + └── skills/ + ├── repo-context/SKILL.md # purpose, structure, modules + ├── architecture/SKILL.md # components, boundaries, rules (if code detected) + ├── build-and-test/SKILL.md # build, test, lint commands (if build indicators detected) + ├── conventions/SKILL.md # coding, naming, workflow conventions + └── deployment/SKILL.md # deploy/runtime info (if deploy indicators detected) +``` + +### Relevance detection + +The bootstrap does not blindly create every directory. It detects repository +type from manifest files and top-level structure: + +| Indicator | Skill generated | Detection signals | +|----------------|--------------------------------|-----------------------------------------------| +| Always | repo-context, conventions | (always relevant) | +| `has_code` | architecture | `src/`, `lib/`, `packages/`, or build files | +| `has_build` | build-and-test | `package.json`, `pyproject.toml`, `Cargo.toml`, `go.mod`, `Makefile`, etc. | +| `has_deploy` | deployment | `Dockerfile`, `.github/workflows/`, `*.tf`, etc. | + +### Idempotency rules + +- Generated files carry a marker comment: `` +- The bootstrap never overwrites files without this marker (manual content is + preserved). +- A second run with unchanged fingerprints produces zero changes. +- Agents strip the marker when they substantially enrich a generated file, + freezing it as manual content. + +### Backward compatibility + +- Repositories without `.opencode/` continue to work — bootstrap creates it. +- Repositories with existing manually written `.opencode/` files are handled + safely — the bootstrap never destroys or silently replaces them. + +## Staleness detection + +The bootstrap writes `.opencode/.bootstrap-meta` (key=value, POSIX-compatible, +no JSON parser required) containing: + +- `schema` — metadata format version +- `tool`, `version` — bootstrap tool identity +- `generated_at` — ISO 8601 timestamp +- `git_head` — current HEAD sha (informational, not a staleness trigger) +- `manifest_fingerprint` — SHA-256 of all build/test/deploy config file contents +- `top_level_fingerprint` — SHA-256 of sorted top-level directory listing +- `signals` — detected capabilities (build/deploy/code) + +**Staleness signals:** the metadata is stale when the manifest fingerprint +changes (dependency or build config updated), the top-level structure changes +materially, or the meta file is missing/corrupted. A changed git HEAD alone +does not force refresh — dependency and structure changes are the meaningful +signals. + +## Orchestrator responsibility + +The Orchestrator's "Repository Intelligence Bootstrap" stage runs on every task +start, before task classification and agent dispatch: + +1. Detect repo root. +2. Check `.opencode/` existence and freshness via `repo-bootstrap.sh status`. +3. If missing/stale: run `repo-bootstrap.sh bootstrap`, then enrich content. +4. Read the generated context. +5. Build the task plan using that context. +6. Dispatch agents with relevant skill references in their briefs. + +The Orchestrator also controls ownership rules and refresh decisions. + +## Agent responsibilities + +### Consumption rules (all agents) + +1. Read `.opencode/AGENTS.md` at task start (or receive it via orchestrator brief). +2. Read the relevant skill for the agent's domain. +3. Treat repo intelligence as context, not truth — verify against the repository. +4. Avoid rediscovery: if knowledge exists in `.opencode/`, do not re-explore it. +5. Add durable discoveries only to role-owned files. +6. Never fill `.opencode/` with task-specific noise. + +### Ownership rules + +| Skill | Primary owner | Role | +|--------------------------|------------------|-------------------------------------------------| +| repo-context | Explorer | verify/enrich purpose, structure, dependencies | +| architecture | Architect | verify/enrich components, boundaries, rules | +| build-and-test | Builder + Tester | verify/enrich build, test, lint, validation | +| conventions | Maintainer | verify/enrich coding, naming, workflow standards | +| deployment | (no permanent) | enriched by whoever handles deployment | +| AGENTS.md (root + .opencode/) | Orchestrator | lifecycle, structural references | +| All .opencode/ (validate) | Reviewer | verify consistency against source; reports findings | +| All .opencode/ (audit) | Maintainer | detect staleness, dedup, cleanup | + +### How agents enrich generated content + +When an agent makes a durable discovery that should persist beyond the session: + +1. Verify the fact against the actual repository. +2. Edit the relevant skill file in `.opencode/skills/`. +3. Strip the `GENERATED-SCAFFOLD` marker comment from the file. +4. This freezes the file as manual content — future bootstrap runs will not + overwrite it. + +## Example: freshly initialized repository + +Running `repo-bootstrap.sh bootstrap` in a Node.js project with Docker: + +```text +/ +├── AGENTS.md # pointer to .opencode/AGENTS.md +├── .opencode/ +│ ├── AGENTS.md # full entry: purpose, structure, skills pointers +│ ├── .bootstrap-meta # freshness metadata +│ └── skills/ +│ ├── repo-context/SKILL.md # purpose, structure, dependencies +│ ├── architecture/SKILL.md # components, boundaries (code detected) +│ ├── build-and-test/SKILL.md # npm, jest, eslint (package.json detected) +│ ├── conventions/SKILL.md # style, naming, workflow +│ └── deployment/SKILL.md # Docker, CI/CD (Dockerfile detected) +``` + +The generated files contain scaffold sections with placeholders. Agents +(Explorer, Architect, Builder, Tester, etc.) then enrich each skill with +verified facts, strip the marker, and the knowledge becomes durable. + +## Tests + +The test suite is at `scripts/test-repo-bootstrap.sh`. It covers all 10 +acceptance criteria: + +1. `.opencode` created for new repository +2. Bootstrap is idempotent +3. Existing manual content is preserved +4. Relevant repository skills are generated +5. Orchestrator loads repo context before dispatch (structural) +6. Every agent references `.opencode` (structural) +7. Stale knowledge is detectable +8. Unrelated repositories do not inherit hardcoded assumptions +9. Existing agent workflows still work +10. Malformed/incomplete intelligence is handled safely + +Run with: `bash scripts/test-repo-bootstrap.sh` + +## Design decisions + +- **Script handles mechanics, LLM handles intelligence**: `repo-bootstrap.sh` + performs deterministic scaffolding and metadata; agents (Orchestrator, + Explorer, Architect, etc.) enrich with semantic understanding. +- **Key=value metadata (no JSON parser)**: avoids `jq` dependency; POSIX-portable. +- **Marker-based manual protection**: simple, reliable, no complex versioning. +- **Skills format follows opencode conventions**: `.opencode/skills//SKILL.md` + with `name` + `description` frontmatter — works with opencode's native skill + loader wherever `skill: allow` is set. +- **Agents consume via file reads** (not skill tool): compatible with the current + `skill: deny` permission on subagents; also opencode-version-agnostic. diff --git a/scripts/repo-bootstrap.sh b/scripts/repo-bootstrap.sh new file mode 100755 index 0000000..2f09b34 --- /dev/null +++ b/scripts/repo-bootstrap.sh @@ -0,0 +1,506 @@ +#!/usr/bin/env bash +set -euo pipefail + +# repo-bootstrap.sh — Repository Intelligence Bootstrap for the dev_agent_team +# +# Generates/maintains a repository-specific `.opencode/` knowledge layer: +# .opencode/AGENTS.md repository intelligence entry point +# .opencode/.bootstrap-meta staleness metadata (key=value, no JSON parser needed) +# .opencode/skills//SKILL.md repository-specific skills (opencode skill format) +# plus a root AGENTS.md pointer (opencode auto-loads root AGENTS.md from the project root). +# +# The tool is GENERIC: it never hardcodes any repository-specific assumption. It +# only scaffolds structure and records mechanically detectable signals (top-level +# layout, build/deploy manifest presence). Semantic enrichment is done by agents +# (Orchestrator/Explorer/Architect/Builder/Tester/...) after bootstrap runs. +# +# Usage: +# repo-bootstrap.sh status → print freshness verdict. exit 0=fresh, 1=stale/missing, 2=error +# repo-bootstrap.sh bootstrap → create/refresh generated intelligence (idempotent) +# repo-bootstrap.sh refresh → force-regenerate GENERATED files (same as bootstrap --force) +# +# Idempotency / preservation rules (binding): +# * A generated file carries a marker: first line `` +# (AGENTS.md) or `generated-by` metadata (SKILL.md frontmatter). The tool ONLY +# rewrites files it generated. +# * Files WITHOUT the marker are treated as MANUAL — never touched, never removed. +# Agents that substantially enrich a generated file should strip its marker to +# freeze it as manual. +# * `bootstrap` rewrites a generated file only when it is missing or the staleness +# fingerprint changed. A second run in an unchanged repo is a no-op (idempotent). +# * Existing `.opencode/AGENTS.md`/skills that are manual are left alone and reported. + +VERSION=1.0 +MARKER_COMMENT="" + +usage() { + sed -n '2,40p' "$0" | sed 's/^# \{0,1\}//' + exit 2 +} + +# --- helpers --------------------------------------------------------------- + +reporoot() { + # Prefer git worktree root; fall back to cwd (script is safe when run anywhere inside). + if command -v git >/dev/null 2>&1; then + local root + if root="$(git rev-parse --show-toplevel 2>/dev/null)"; then + printf '%s' "$root" + return 0 + fi + fi + pwd -P +} + +sha256_of() { # sha256_of + if command -v sha256sum >/dev/null 2>&1; then + printf '%s' "$1" | sha256sum | cut -d' ' -f1 + elif command -v cksum >/dev/null 2>&1; then + printf '%s' "$1" | cksum | awk '{print $1}' + else + printf '%s' "${#1}" + fi +} + +top_level_snapshot() { # top_level_snapshot -> newline list, sorted + find "$1" -maxdepth 1 -mindepth 1 \( -name .git -o -name node_modules -o -name target -o -name build -o -name dist \) -prune -o -maxdepth 1 -mindepth 1 -printf '%f\n' 2>/dev/null | LC_ALL=C sort +} + +# detect_manifest : 0 if any of the files exist at root +detect_manifest() { + local root="$1"; shift + local f + for f in "$@"; do + [ -f "$root/$f" ] && return 0 + done + return 1 +} + +# detect_any_dir : 0 if any of the dirs exist at root +detect_any_dir() { + local root="$1"; shift + local d + for d in "$@"; do + [ -d "$root/$d" ] && return 0 + done + return 1 +} + +# has_workflow_files +has_workflow_files() { + [ -d "$1/.github/workflows" ] && ls "$1/.github/workflows"/* >/dev/null 2>&1 +} + +# --- signals --------------------------------------------------------------- + +detect_signals() { # detect_signals -> sets BUILD_IND / DEPLOY_IND / CODE_IND + local root="$1" + BUILD_IND=0; DEPLOY_IND=0; CODE_IND=0 + + if detect_manifest "$root" \ + package.json pnpm-lock.yaml yarn.lock package-lock.json \ + pyproject.toml requirements.txt setup.py \ + Cargo.toml go.mod pom.xml build.gradle build.gradle.kts \ + Makefile CMakeLists.txt Gemfile composer.json mix.exs \ + pubspec.yaml meson.build WORKSPACE BUILD dune-project \ + *.csproj *.sln; then + BUILD_IND=1 + fi + + if detect_manifest "$root" \ + Dockerfile docker-compose.yml docker-compose.yaml \ + .gitlab-ci.yml Jenkinsfile cloudbuild.yaml heroku.yml \ + fly.toml vercel.json netlify.toml \ + *.tf *.tfvars || has_workflow_files "$root" || detect_any_dir "$root" k8s helm terraform systemd; then + DEPLOY_IND=1 + fi + + if [ "$BUILD_IND" -eq 1 ] || detect_any_dir "$root" src lib packages cmd internal app core; then + CODE_IND=1 + fi +} + +# --- meta ------------------------------------------------------------------ + +META_FILE=".bootstrap-meta" +META_KEYS="schema tool version generated_at git_head manifest_fingerprint top_level_fingerprint signals" + +read_meta_value() { # read_meta_value + local file="$1" key="$2" + [ -f "$file" ] || return 1 + sed -n "s/^${key}=//p" "$file" | head -n1 +} + +# --- fingerprint ----------------------------------------------------------- + +current_fingerprint() { # current_fingerprint -> sets MANIFEST_FP / TOPLEVEL_FP / GIT_HEAD / SIGNAL_TXT + local root="$1" + GIT_HEAD="" + if command -v git >/dev/null 2>&1; then + GIT_HEAD="$(git -C "$root" rev-parse --short HEAD 2>/dev/null || true)" + fi + + local manifest_lines="" f + for f in package.json pnpm-lock.yaml yarn.lock package-lock.json pyproject.toml requirements.txt setup.py Cargo.toml go.mod pom.xml build.gradle build.gradle.kts Makefile CMakeLists.txt Gemfile composer.json mix.exs pubspec.yaml meson.build WORKSPACE BUILD dune-project Dockerfile docker-compose.yml docker-compose.yaml .gitlab-ci.yml Jenkinsfile cloudbuild.yaml heroku.yml fly.toml vercel.json netlify.toml; do + if [ -f "$root/$f" ]; then + manifest_lines+="$(printf '%s' "$f:$(sha256_of "$(cat "$root/$f")")"; printf '\n')" + fi + done + MANIFEST_FP="$(sha256_of "$manifest_lines")" + TOPLEVEL_FP="$(sha256_of "$(top_level_snapshot "$root")")" + + SIGNAL_TXT="build=$BUILD_IND deploy=$DEPLOY_IND code=$CODE_IND" +} + +# --- verdict --------------------------------------------------------------- + +verdict() { # verdict -> sets VERDICT=fresh|stale|missing|error ; sets META_OUT + local root="$1" + local dir="$root/.opencode" + VERDICT="error"; META_OUT="" + [ -d "$root" ] || return 2 + + if [ ! -d "$dir" ]; then + VERDICT="missing"; return 0 + fi + if [ ! -f "$dir/$META_FILE" ]; then + VERDICT="stale"; return 0 + fi + + local schema tool_version meta_fp mt_fp signals + schema="$(read_meta_value "$dir/$META_FILE" schema)" + tool_version="$(read_meta_value "$dir/$META_FILE" version)" + meta_fp="$(read_meta_value "$dir/$META_FILE" manifest_fingerprint)" + mt_fp="$(read_meta_value "$dir/$META_FILE" top_level_fingerprint)" + signals="$(read_meta_value "$dir/$META_FILE" signals)" + + detect_signals "$root" + current_fingerprint "$root" + + if [ "$schema" != "1" ] || [ "$tool_version" != "$VERSION" ] \ + || [ "$meta_fp" != "$MANIFEST_FP" ] || [ "$mt_fp" != "$TOPLEVEL_FP" ] \ + || [ "$signals" != "$SIGNAL_TXT" ]; then + VERDICT="stale"; return 0 + fi + VERDICT="fresh"; return 0 +} + +# --- generation ------------------------------------------------------------ + +write_if_owned() { # write_if_owned ; sets CREATED/UPDATED/SKIPPED counters + local path="$1" content="$2" force="$3" + if [ ! -e "$path" ]; then + mkdir -p "$(dirname "$path")" + printf '%s' "$content" > "$path" + CREATED=$((CREATED + 1)) + elif grep -qF "GENERATED-SCAFFOLD dev_agent_team" "$path" || grep -qF "generated-by: dev_agent_team-repo-bootstrap" "$path"; then + if [ "$force" = "1" ] || [ "$VERDICT" = "stale" ] || [ "$VERDICT" = "missing" ]; then + printf '%s' "$content" > "$path" + UPDATED=$((UPDATED + 1)) + else + SKIPPED=$((SKIPPED + 1)) # fresh and generated → leave as-is (idempotent) + fi + else + MANUAL=$((MANUAL + 1)) # manual content — never touch + fi +} + +root_agents_md() { # root_agents_md + printf '%s\n' \ +"$MARKER_COMMENT" \ +"# Repository Intelligence" \ +"" \ +"Read \`.opencode/AGENTS.md\` for repository-specific context before starting work." \ +"Deeper knowledge lives in \`.opencode/skills/\` (repo-context, architecture, build-and-test, conventions, deployment)." \ +"" \ +"Treat repository intelligence as context, not absolute truth: verify against the actual repository when they disagree." \ +"" +} + +repo_agents_md() { # repo_agents_md + local rname; rname="$(basename "$1")" + local bt="" arch="" dep="" conv="# Conventions"$'\n\n# Repository coding, naming, structure, and workflow conventions are maintained in \`.opencode/skills/conventions/SKILL.md\`.\n' + [ "$BUILD_IND" -eq 1 ] && bt="# Build & Test"$'\n\n# Build/test/lint/validation commands live in \`.opencode/skills/build-and-test/SKILL.md\`.\n' + [ "$CODE_IND" -eq 1 ] && arch="# Architecture"$'\n\n# Components, relationships, boundaries, and architectural rules live in \`.opencode/skills/architecture/SKILL.md\`.\n' + [ "$DEPLOY_IND" -eq 1 ] && dep="# Deployment"$'\n\n# Deployment and runtime information lives in \`.opencode/skills/deployment/SKILL.md\`.\n' + + printf '%s\n' \ +"$MARKER_COMMENT" \ +"# AGENTS.md — Repository Intelligence for \`${rname}\`" \ +"" \ +"## What this repository is" \ +"" \ +"- (fill in: purpose, for whom, why it exists; verified by Philosopher/Explorer)" \ +"" \ +"## How it is structured" \ +"" \ +"- (fill in: top-level layout and module roles; see \`.opencode/skills/repo-context/SKILL.md\`)" \ +"" \ +"## Build & test" \ +"" \ +"$bt" \ +"## Conventions" \ +"" \ +"$conv" \ +"## Architecture constraints" \ +"" \ +"$arch" \ +"## Deployment / runtime" \ +"" \ +"$dep" \ +"## Rules for agents (binding)" \ +"" \ +"- Consult \`.opencode/\` before making architectural or implementation decisions." \ +"- Treat this knowledge as context, not absolute truth; verify against the source of truth." \ +"- Owned skills: Explorer→repo-context, Architect→architecture, Builder/Tester→build-and-test, Maintainer→cleanup/consistency, Reviewer→validation." \ +"- Never fill \`.opencode/\` with task-specific noise; add only durable knowledge." \ +"" \ +"## Bootstrap metadata" \ +"" \ +"- Generated by dev_agent_team repo-bootstrap v${VERSION} on $(date -u +%Y-%m-%dT%H:%M:%SZ)." \ +"" +} + +skill_meta() { printf '%s\n' "---" "name: $1" "description: $2" "metadata:" " generated-by: dev_agent_team-repo-bootstrap" " bootstrap-version: \"$VERSION\"" "---" ""; } + +skill_repo_context() { + skill_meta repo-context \ + "High-level orientation for a repository: purpose, structure, modules, dependencies, and entry points. Use when entering an unfamiliar repository or before architectural and implementation work." + printf '%s\n' \ +"# Repo Context" \ +"" \ +"> Generated scaffold by dev_agent_team repo-bootstrap v${VERSION}. OWNER: Explorer (verify/enrich). Treat as context — verify against the repository." \ +"" \ +"## Purpose" \ +"- (What does this repository do? For whom? Why?)" \ +"" \ +"## Structure" \ +"- (Top-level directories and their roles)" \ +"" \ +"## Important modules / entry points" \ +"- (Files/dirs that matter most for common work)" \ +"" \ +"## Dependencies (bootstrap-detected — verify)" \ +"- (manifest files and package manager)" \ +"" \ +"## Runtime requirements" \ +"- (verify)" \ +"" \ +"## Generated vs handwritten files" \ +"- (verify)" \ +"" \ +"## Dangerous areas / fragile components" \ +"- (verify)" \ +"" \ +"## Known technical decisions" \ +"- (verify/add reference to ADRs)" \ +"" +} + +skill_architecture() { + skill_meta architecture \ + "Repository architecture: components, relationships, boundaries, and architectural rules. Use when making architectural decisions or understanding system design." + printf '%s\n' \ +"# Architecture" \ +"" \ +"> Generated scaffold by dev_agent_team repo-bootstrap v${VERSION}. OWNER: Architect (verify/enrich). Treat as context — verify against the repository." \ +"" \ +"## Components & modules" \ +"- (list and their relationships)" \ +"" \ +"## Boundaries & ownership" \ +"- (layering, module boundaries, responsibilities)" \ +"" \ +"## Architectural rules & constraints" \ +"- (patterns that must be followed)" \ +"" \ +"## Integration points" \ +"- (external systems, APIs, data flows)" \ +"" \ +"## Known architectural decisions" \ +"- (ADR summaries/links)" \ +"" +} + +skill_build_test() { + skill_meta build-and-test \ + "How to build, test, lint, type-check, and validate a repository. Use before modifying code or when running or verifying project commands." + printf '%s\n' \ +"# Build & Test" \ +"" \ +"> Generated scaffold by dev_agent_team repo-bootstrap v${VERSION}. OWNERS: Builder + Tester (verify/enrich). Treat as context — verify against the repository." \ +"" \ +"## Framework / toolchain (bootstrap-detected — verify)" \ +"- (manifest files seen during bootstrap; confirm actual toolchain)" \ +"" \ +"## Build commands" \ +"- (list exact commands and order)" \ +"" \ +"## Test commands" \ +"- (list exact commands; note the fast subset for quick checks)" \ +"" \ +"## Lint / type-check / static analysis" \ +"- (list exact commands)" \ +"" \ +"## Validation order" \ +"- (which commands, in which order, before declaring work done)" \ +"" \ +"## Known build/test gotchas" \ +"- (verify/add)" \ +"" +} + +skill_conventions() { + skill_meta conventions \ + "Repository-specific coding, naming, structure, and workflow conventions. Use when writing code or documentation in this repository." + printf '%s\n' \ +"# Conventions" \ +"" \ +"> Generated scaffold by dev_agent_team repo-bootstrap v${VERSION}. OWNER: Maintainer (cleanup/consistency); every agent follows conventions. Treat as context — verify against the repository." \ +"" \ +"## Language / style" \ +"- (fill)" \ +"" \ +"## Naming" \ +"- (fill)" \ +"" \ +"## Structure / layout" \ +"- (fill)" \ +"" \ +"## Workflow (branching, commits, review)" \ +"- (fill)" \ +"" \ +"## Constraints / gotchas" \ +"- (fill)" \ +"" +} + +skill_deployment() { + skill_meta deployment \ + "Deployment and runtime information for a repository, including environments, packaging, and operations. Use when deploying, operating, or debugging runtime behavior." + printf '%s\n' \ +"# Deployment" \ +"" \ +"> Generated scaffold by dev_agent_team repo-bootstrap v${VERSION}. Treat as context — verify against the repository." \ +"" \ +"## Deployment targets / environments (bootstrap-detected — verify)" \ +"- (indicators: Dockerfile, compose, workflows, IaC files — confirm actual setup)" \ +"" \ +"## Build / packaging for deploy" \ +"- (fill)" \ +"" \ +"## Runtime requirements" \ +"- (fill)" \ +"" \ +"## Operations / rollout / rollback" \ +"- (fill)" \ +"" \ +"## Monitoring / logs / troubleshooting" \ +"- (fill)" \ +"" +} + +do_write_skills() { # do_write_skills + local root="$1" force="$2" skills="$root/.opencode/skills" + + write_if_owned "$skills/repo-context/SKILL.md" "$(skill_repo_context)" "$force" + write_if_owned "$skills/conventions/SKILL.md" "$(skill_conventions)" "$force" + + if [ "$CODE_IND" -eq 1 ]; then + write_if_owned "$skills/architecture/SKILL.md" "$(skill_architecture)" "$force" + fi + if [ "$BUILD_IND" -eq 1 ]; then + write_if_owned "$skills/build-and-test/SKILL.md" "$(skill_build_test)" "$force" + fi + if [ "$DEPLOY_IND" -eq 1 ]; then + write_if_owned "$skills/deployment/SKILL.md" "$(skill_deployment)" "$force" + fi +} + +write_meta() { # write_meta + local root="$1" + mkdir -p "$root/.opencode" + current_fingerprint "$root" + local new_meta + new_meta="$(printf '%s\n' \ +"schema=1" \ +"tool=dev_agent_team-repo-bootstrap" \ +"version=$VERSION" \ +"generated_at=$(date -u +%Y-%m-%dT%H:%M:%SZ)" \ +"git_head=$GIT_HEAD" \ +"manifest_fingerprint=$MANIFEST_FP" \ +"top_level_fingerprint=$TOPLEVEL_FP" \ +"signals=$SIGNAL_TXT")" + # Idempotent: skip rewrite if content is identical (preserves timestamp). + local existing="" + [ -f "$root/.opencode/$META_FILE" ] && existing="$(cat "$root/.opencode/$META_FILE")" + if [ "$existing" = "$new_meta" ]; then + return 0 + fi + printf '%s\n' "$new_meta" > "$root/.opencode/$META_FILE" +} + +# --- commands -------------------------------------------------------------- + +cmd_status() { + local root="$1" + detect_signals "$root" + verdict "$root" + case "$VERDICT" in + fresh) echo "REPO_BOOTSTRAP_STATUS: fresh # .opencode up to date (root: $root)"; return 0 ;; + stale) echo "REPO_BOOTSTRAP_STATUS: stale # signals changed or metadata outdated (root: $root)"; return 1 ;; + missing) echo "REPO_BOOTSTRAP_STATUS: missing # no .opencode intelligence (root: $root)"; return 1 ;; + *) echo "REPO_BOOTSTRAP_STATUS: error # cannot evaluate (root: $root)" >&2; return 2 ;; + esac +} + +cmd_bootstrap() { + local root="$1" force="$2" + CREATED=0; UPDATED=0; SKIPPED=0; MANUAL=0 + + detect_signals "$root" + verdict "$root" + [ "$VERDICT" = "error" ] && { echo "ERROR: cannot evaluate repository root: $root" >&2; return 2; } + + echo "==> Repository Intelligence bootstrap (root: $root)" + echo "==> Signals: build=$BUILD_IND deploy=$DEPLOY_IND code=$CODE_IND" + echo "==> State before: $VERDICT" + + # Root AGENTS.md pointer: only when absent or generated-owned (never clobber manual). + write_if_owned "$root/AGENTS.md" "$(root_agents_md "$root")" "$force" + + # .opencode/AGENTS.md + skills. + write_if_owned "$root/.opencode/AGENTS.md" "$(repo_agents_md "$root")" "$force" + do_write_skills "$root" "$force" + + # Meta last, so it reflects what was just produced. + write_meta "$root" + + echo "==> created=$CREATED updated=$UPDATED skipped-fresh=$SKIPPED manual-preserved=$MANUAL" + if [ "$MANUAL" -gt 0 ]; then + echo "==> NOTE: $MANUAL file(s) treated as manual content and preserved untouched." + fi + echo "==> Next: agents should READ .opencode/AGENTS.md and relevant skills, verify facts against the repository, and enrich owned files (stripping the generated marker when freezing them as manual)." + return 0 +} + +# --- main ------------------------------------------------------------------- + +[ $# -ge 1 ] || usage +CMD="$1"; shift || true + +ROOT="" +while [ $# -gt 0 ]; do + case "$1" in + --root) ROOT="$2"; shift 2 ;; + *) break ;; + esac +done +[ -z "$ROOT" ] && ROOT="$(reporoot)" +case "$CMD" in + status) cmd_status "$ROOT" ;; + bootstrap) cmd_bootstrap "$ROOT" 0 ;; + refresh) cmd_bootstrap "$ROOT" 1 ;; + -h|--help) usage ;; + *) echo "ERROR: unknown command: $CMD" >&2; usage ;; +esac \ No newline at end of file diff --git a/scripts/test-repo-bootstrap.sh b/scripts/test-repo-bootstrap.sh new file mode 100755 index 0000000..4382b5c --- /dev/null +++ b/scripts/test-repo-bootstrap.sh @@ -0,0 +1,264 @@ +#!/usr/bin/env bash +set -uo pipefail + +# test-repo-bootstrap.sh — Automated tests for the Repository Intelligence Bootstrap +# +# Exit codes: 0 = all pass, 1 = any failure (PASS/FAIL per test, summary at end) +# Conventions follow the existing verify-permission-patterns.sh style: +# temp fixture dirs, PASS/FAIL echo, deterministic assertions, no external deps +# beyond bash, git (optional), and the repo-bootstrap.sh script under test. +# +# Acceptance matrix (10 items): +# 1. .opencode created for new repository +# 2. bootstrap is idempotent (second run = no drift / no change) +# 3. existing .opencode content (manual) is preserved +# 4. relevant repository skills are generated (node → build-and-test; Dockerfile → deployment) +# 5. orchestrator.md contains repository-context-before-dispatch stage (structural) +# 6. every agents/*.md references .opencode (consumes context) +# 7. stale knowledge is detectable (status exits 1 when fingerprint changes) +# 8. unrelated repositories do not inherit hardcoded assumptions (generic scaffold in plain dir) +# 9. existing agent workflows still work (install.sh to temp target) +# 10. malformed .opencode intelligence is handled safely (corrupted meta → re-bootstrap works) + +TEAM_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +BOOTSTRAP="$TEAM_ROOT/scripts/repo-bootstrap.sh" + +PASS=0; FAIL=0 +T="$(mktemp -d)" +trap 'rm -rf "$T"' EXIT + +ok() { PASS=$((PASS+1)); echo "PASS $1"; } +fail(){ FAIL=$((FAIL+1)); echo "FAIL $1 — $2" >&2; } +skip(){ PASS=$((PASS+1)); echo "SKIP $1 — $2"; } +assert_file() { [ -f "$1" ] && return 0; return 1; } +assert_dir() { [ -d "$1" ] && return 0; return 1; } +assert_contains() { grep -qF "$2" "$1" 2>/dev/null && return 0; return 1; } + +# Helper: run bootstrap/status against a specific root (not cwd) +# Signature: RB [args...] → bash BOOTSTRAP --root [args...] +RB() { local r="$1"; shift; bash "$BOOTSTRAP" "$@" --root "$r"; } + +# Fixture: generic project repo +make_repo() { # make_repo [extra files...] + local name="$1"; shift + local d="$T/$name" + mkdir -p "$d/src" + git -C "$d" init -q . 2>/dev/null || true + printf '# README\n' > "$d/README.md" + for f in "$@"; do + case "$f" in + */*) mkdir -p "$d/$(dirname "$f")" ;; + esac + printf '/* placeholder */\n' > "$d/$f" + done + printf '%s' "$d" +} + +# ======================================================================== # +# TEST 1: .opencode is created for a new repository +# ======================================================================== # +TEST1="$(make_repo test1)" +if RB "$TEST1" bootstrap >/dev/null 2>&1; then + if assert_dir "$TEST1/.opencode" && \ + assert_file "$TEST1/.opencode/AGENTS.md" && \ + assert_file "$TEST1/.opencode/.bootstrap-meta" && \ + assert_dir "$TEST1/.opencode/skills/repo-context"; then + ok "T01 .opencode created for new repo" + else + fail "T01 .opencode created for new repo" "missing expected files" + fi +else + fail "T01 .opencode created for new repo" "bootstrap exited non-zero" +fi + +# ======================================================================== # +# TEST 2: bootstrap is idempotent (second run produces no changes) +# ======================================================================== # +TEST2="$(make_repo test2 package.json)" +RB "$TEST2" bootstrap >/dev/null 2>&1 +META1=$(cat "$TEST2/.opencode/.bootstrap-meta" 2>/dev/null || echo "MISSING") +AGENTS1=$(cat "$TEST2/.opencode/AGENTS.md" 2>/dev/null || echo "MISSING") +if RB "$TEST2" bootstrap >/dev/null 2>&1; then + META2=$(cat "$TEST2/.opencode/.bootstrap-meta" 2>/dev/null || echo "MISSING") + AGENTS2=$(cat "$TEST2/.opencode/AGENTS.md" 2>/dev/null || echo "MISSING") + if [ "$META1" = "$META2" ] && [ "$AGENTS1" = "$AGENTS2" ]; then + ok "T02 bootstrap is idempotent" + else + fail "T02 bootstrap is idempotent" "file changed on second run" + fi +else + fail "T02 bootstrap is idempotent" "second run exited non-zero" +fi + +# ======================================================================== # +# TEST 3: existing .opencode content (manual) is preserved +# ======================================================================== # +TEST3="$(make_repo test3)" +mkdir -p "$TEST3/.opencode/skills/repo-context" +printf '# Manual repo-context\nCustom content\n' > "$TEST3/.opencode/skills/repo-context/SKILL.md" +RB "$TEST3" bootstrap >/dev/null 2>&1 +if assert_contains "$TEST3/.opencode/skills/repo-context/SKILL.md" "Custom content"; then + ok "T03 existing manual content preserved" +else + fail "T03 existing manual content preserved" "manual SKILL.md was overwritten" +fi + +# ======================================================================== # +# TEST 4: relevant skills are generated (build indicator → build-and-test; +# deploy indicator → deployment; plain → only repo-context + conventions) +# ======================================================================== # +TEST4A="$(make_repo test4a package.json Dockerfile src/lib/main.rs)" +RB "$TEST4A" bootstrap >/dev/null 2>&1 +if assert_dir "$TEST4A/.opencode/skills/build-and-test" && \ + assert_dir "$TEST4A/.opencode/skills/deployment" && \ + assert_dir "$TEST4A/.opencode/skills/architecture" && \ + assert_dir "$TEST4A/.opencode/skills/repo-context" && \ + assert_dir "$TEST4A/.opencode/skills/conventions"; then + ok "T04a build+deploy+code indicators produce expected skills" +else + fail "T04a build+deploy+code indicators produce expected skills" \ + "missing skills: $(ls "$TEST4A/.opencode/skills" 2>/dev/null)" +fi + +TEST4B="$(make_repo test4b README.md)" +RB "$TEST4B" bootstrap >/dev/null 2>&1 +SKILLS_B=$(ls "$TEST4B/.opencode/skills" 2>/dev/null | sort | tr '\n' ' ') +if ! assert_dir "$TEST4B/.opencode/skills/build-and-test" && \ + ! assert_dir "$TEST4B/.opencode/skills/deployment" && \ + assert_dir "$TEST4B/.opencode/skills/repo-context" && \ + assert_dir "$TEST4B/.opencode/skills/conventions"; then + ok "T04b plain repo only gets repo-context + conventions" +else + fail "T04b plain repo only gets repo-context + conventions" "got: $SKILLS_B" +fi + +# ======================================================================== # +# TEST 5: orchestrator.md contains repo-context-before-dispatch workflow stage +# (structural: grep for bootstrap consumption and repo-context loading) +# ======================================================================== # +ORCH="$TEAM_ROOT/agents/orchestrator.md" +if assert_contains "$ORCH" "Repository Intelligence" && \ + assert_contains "$ORCH" ".opencode" && \ + assert_contains "$ORCH" "stale"; then + ok "T05 orchestrator.md has bootstrap stage (structural)" +else + fail "T05 orchestrator.md has bootstrap stage (structural)" \ + "missing expected Repository Intelligence / .opencode / stale content" +fi + +# ======================================================================== # +# TEST 6: every agents/*.md references .opencode (consumes context) +# ======================================================================== # +MISSING_AGENTS="" +AGENT_COUNT=0 +for af in "$TEAM_ROOT"/agents/*.md; do + AGENT_COUNT=$((AGENT_COUNT+1)) + ANAME="$(basename "$af")" + if ! assert_contains "$af" ".opencode"; then + MISSING_AGENTS="$MISSING_AGENTS $ANAME" + fi +done +if [ "$AGENT_COUNT" -eq 13 ] && [ -z "$MISSING_AGENTS" ]; then + ok "T06 all $AGENT_COUNT agents reference .opencode" +else + fail "T06 all agents reference .opencode" \ + "count=$AGENT_COUNT, missing:$MISSING_AGENTS" +fi + +# ======================================================================== # +# TEST 7: stale knowledge is detectable (status exits 1 when fingerprint changes) +# ======================================================================== # +TEST7="$(make_repo test7 package.json)" +RB "$TEST7" bootstrap >/dev/null 2>&1 +# fingerprint should be fresh +if RB "$TEST7" status >/dev/null 2>&1; then + # mutate manifest → fingerprint should go stale + printf '/* changed */\n' > "$TEST7/package.json" + STALE_RC=0 + RB "$TEST7" status >/dev/null 2>&1 || STALE_RC=$? + if [ "$STALE_RC" -eq 1 ]; then + ok "T07 stale knowledge detectable after manifest change" + else + fail "T07 stale knowledge detectable after manifest change" \ + "status returned $STALE_RC (expected 1)" + fi +else + fail "T07 stale knowledge detectable" "initial status already non-zero" +fi + +# ======================================================================== # +# TEST 8: unrelated repos do not inherit hardcoded assumptions +# (script runs generically; no repo-specific name literals in scaffold) +# ======================================================================== # +TEST8="$(make_repo test8)" +RB "$TEST8" bootstrap >/dev/null 2>&1 +AGENTS_CONTENT="$(cat "$TEST8/.opencode/AGENTS.md")" +RC_CONTENT="$(cat "$TEST8/.opencode/skills/repo-context/SKILL.md")" +# Verify no hardcoded repo/team names appear (should only contain generic placeholders) +if ! assert_contains "$AGENTS_CONTENT" "dev_agent_team" && \ + ! assert_contains "$RC_CONTENT" "orchestrator"; then + ok "T08 no hardcoded repo-specific assumptions in scaffold" +else + fail "T08 no hardcoded repo-specific assumptions in scaffold" \ + "found hardcoded terms in generated content" +fi + +# ======================================================================== # +# TEST 9: existing agent workflows still work +# (install.sh to temp target — 13 agents copied; treat "no runtime" +# verifier exit 2 as SKIP since copies succeed regardless) +# ======================================================================== # +TEST9_TARGET="$T/install_target9" +if [ -f "$TEAM_ROOT/scripts/install.sh" ]; then + INST_RC=0 + mkdir -p "$TEST9_TARGET" + OPENCODE_AGENTS_DIR="$TEST9_TARGET" bash "$TEAM_ROOT/scripts/install.sh" >/dev/null 2>&1 || INST_RC=$? + AGENTS_COPIED=$(find "$TEST9_TARGET" -name '*.md' -maxdepth 1 | wc -l) + if [ "$INST_RC" -eq 0 ] || [ "$INST_RC" -eq 2 ]; then + if [ "$AGENTS_COPIED" -eq 13 ]; then + ok "T09 install.sh copies 13 agents to temp target (rc=$INST_RC, runtime=$([ $INST_RC -eq 0 ] && echo 'yes' || echo 'skip/no-op'))" + else + fail "T09 install.sh copies 13 agents to temp target" \ + "only $AGENTS_COPIED .md files copied" + fi + else + fail "T09 install.sh copies 13 agents to temp target" \ + "install.sh exited $INST_RC" + fi +else + skip "T09 install.sh copies 13 agents" "scripts/install.sh not found" +fi + +# ======================================================================== # +# TEST 10: malformed/incomplete .opencode intelligence is handled safely +# ======================================================================== # +TEST10="$(make_repo test10 package.json)" +mkdir -p "$TEST10/.opencode/skills/repo-context" +printf 'GARBAGE CONTENT\nno frontmatter\n' > "$TEST10/.opencode/skills/repo-context/SKILL.md" +echo "not-a-meta" > "$TEST10/.opencode/.bootstrap-meta" +RC=0 +RB "$TEST10" bootstrap >/dev/null 2>&1 || RC=$? +# Assert: script did not crash, manual garbage in repo-context preserved (no marker), +# and meta was regenerated. +if [ "$RC" -eq 0 ] && \ + assert_contains "$TEST10/.opencode/skills/repo-context/SKILL.md" "GARBAGE CONTENT" && \ + assert_file "$TEST10/.opencode/.bootstrap-meta" && \ + assert_contains "$TEST10/.opencode/.bootstrap-meta" "schema=1"; then + ok "T10 malformed intelligence handled safely" +else + fail "T10 malformed intelligence handled safely" \ + "rc=$RC; corrupted repo-context still present after bootstrap" +fi + +# ======================================================================== # +# SUMMARY +# ======================================================================== # +echo "" +TOTAL=$((PASS+FAIL)) +if [ "$FAIL" -eq 0 ]; then + echo "RESULT: ALL $TOTAL TESTS PASSED" + exit 0 +else + echo "RESULT: $FAIL/$TOTAL TESTS FAILED" + exit 1 +fi \ No newline at end of file