opencode-swarm 7.28.3 → 7.29.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -538,469 +538,69 @@ All tools run locally. No Docker, no network calls.
538
538
 
539
539
  ### Context Budget Guard
540
540
 
541
- Monitors how much context Swarm injects to prevent overflow:
542
-
543
- - **Warning threshold (70%)** — Advisory when context reaches ~2800 tokens
544
- - **Critical threshold (90%)** — Alert at ~3600 tokens with `/swarm handoff` recommendation
545
- - **Non-nagging** — One-time alerts per session
546
-
547
- Disable entirely with `context_budget.enabled: false`.
548
-
549
- ### File Locking for Concurrent Safety
550
-
551
- Hard lock on `plan.json` (serialized writes), advisory lock on `events.jsonl` (append-only log). Stale locks auto-expire via `proper-lockfile`.
552
-
553
- ### Agent Categories
541
+ The Context Budget Guard monitors how much context Swarm is injecting into the conversation. It helps prevent context overflow before it becomes a problem.
554
542
 
555
- Agents are classified into four categories for the monitor server `/metadata` endpoint:
543
+ ### Default Behavior
556
544
 
557
- | Category | Agents |
558
- |----------|--------|
559
- | `orchestrator` | architect |
560
- | `pipeline` | explorer, coder, test_engineer |
561
- | `qa` | reviewer, critic, critic_sounding_board, critic_drift_verifier |
562
- | `support` | sme, docs, designer |
545
+ - **Enabled automatically** No setup required. Swarm starts tracking context usage right away.
546
+ - **What it measures** — Only the context that Swarm injects (plan, context, evidence, retrospectives). It does **not** count your chat history or the model's responses.
547
+ - **Warning threshold (0.7 ratio)** — When swarm-injected context reaches ~2800 tokens (70% of 4000), the architect receives a one-time advisory warning. This is informational — execution continues normally.
548
+ - **Critical threshold (0.9 ratio)** — When context reaches ~3600 tokens (90% of 4000), the architect receives a critical alert with a recommendation to run `/swarm handoff`. This is also one-time only.
549
+ - **Non-nagging** Alerts fire once per session, not repeatedly. You won't be pestered every turn.
550
+ - **Who sees warnings** Only the architect receives these warnings. Other agents are unaware of the budget.
563
551
 
564
- Use `getAgentCategory(agentName)` from `src/config/agent-categories.ts` to resolve an agent's category at runtime.
552
+ To disable entirely, set `context_budget.enabled: false` in your swarm config.
565
553
 
566
554
  ---
567
555
 
568
- <details>
569
- <summary><strong>Full Execution Pipeline (Technical Detail)</strong></summary>
570
-
571
- ### The Pipeline
572
-
573
- Every task goes through this sequence. No exceptions, no overrides.
574
-
575
- ```
576
- MODE: EXECUTE (per task)
577
-
578
- ├── 5a. @coder implements (ONE task only)
579
- ├── 5b. diff + imports (contract + dependency analysis + semantic diff context)
580
- │ └── @system-enhancer injects AST-based semantic diff summary with blast radius
581
- │ into @reviewer context (up to 10 files, conditional on declared scope)
582
- ├── 5c. syntax_check (parse validation)
583
- ├── 5d. placeholder_scan (catches TODOs, stubs, incomplete code)
584
- ├── 5e. lint fix → lint check
585
- ├── 5f. build_check (does it compile?)
586
- ├── 5g. pre_check_batch (4 parallel: lint, secretscan, SAST, quality budget)
587
- ├── 5h. @reviewer (correctness pass)
588
- ├── 5i. @reviewer (security pass, if security-sensitive files changed)
589
- ├── 5j. @test_engineer (verification tests + coverage ≥70%)
590
- ├── 5k. @test_engineer (adversarial tests)
591
- ├── 5l. architect regression sweep (scope:"graph" to find cross-task test regressions)
592
- ├── 5l-ter. test drift detection (conditional — fires when changes involve command behaviour,
593
- │ parsing/routing logic, user-visible output, public contracts, assertion-heavy areas,
594
- │ or helper lifecycle changes; validates tests still align with current behaviour)
595
- ├── 5m. ⛔ Pre-commit checklist (all 4 items required, no override)
596
- └── 5n. Task marked complete, evidence written
597
- ```
598
-
599
- If any step fails, the coder gets structured feedback and retries. After 5 failures on the same task, it escalates to you.
600
-
601
- ### Architect Workflow Modes
602
-
603
- The architect moves through these modes automatically:
604
-
605
- | Mode | What It Means |
606
- |---|---|
607
- | `RESUME` | Existing `.swarm/` state was found, so Swarm continues where it left off |
608
- | `CLARIFY` | Swarm asks for missing information it cannot infer |
609
- | `DISCOVER` | Explorer scans the codebase; co-change dark matter analysis runs automatically to detect hidden file couplings (v6.41) |
610
- | `CONSULT` | SME agents provide domain guidance |
611
- | `PLAN` | Architect writes or updates the phased plan (includes CODEBASE REALITY CHECK on brownfield projects) |
612
- | `CRITIC-GATE` | Critic reviews the plan before execution |
613
- | `EXECUTE` | Tasks are implemented one at a time through the QA pipeline |
614
- | `PHASE-WRAP` | A phase closes out, including: explorer rescan, docs update, `context.md` update, `write_retro`, evidence check, `sbom_generate`, **`@critic_drift_verifier` delegation** (drift check — blocking gate), `write_drift_evidence` call with verdict, mandatory gate evidence verification (`completion-verify.json` + `drift-verifier.json` both required), then `phase_complete` |
615
-
616
- > **CODEBASE REALITY CHECK (v6.29.2):** Before any planning, the Architect dispatches Explorer to verify the current state of every referenced item. Produces a CODEBASE REALITY REPORT with statuses: NOT STARTED, PARTIALLY DONE, ALREADY COMPLETE, or ASSUMPTION INCORRECT. This prevents planning against stale assumptions. Skipped for greenfield projects with no existing codebase references.
617
-
618
- > **Phase Completion Gates (v6.33.4):** Before a phase can be marked complete, two mandatory gates are enforced: (1) completion-verify — deterministic check that plan task identifiers exist in source files, and (2) critic_drift_verifier evidence — verification that the drift verifier approved the implementation. Both gates are automatically bypassed when turbo mode is active.
619
-
620
- ### Important
621
-
622
- A second or later run does **not** necessarily look like a first run.
623
-
624
- If `.swarm/plan.md` already exists, the architect may enter `RESUME` and then go directly into `EXECUTE`. That is expected and does **not** mean Swarm stopped using agents.
625
-
626
- Use `/swarm status` if you are unsure what Swarm is doing.
627
-
628
- Release automation uses release-please and requires conventional commit prefixes such as `fix:` or `feat:` on changes merged to `main`.
629
-
630
- </details>
631
-
632
- <details>
633
- <summary><strong>Persistent Memory (What's in .swarm/)</strong></summary>
634
-
635
- ### plan.md: Your Project Roadmap
636
-
637
- ```markdown
638
- # Project: Auth System
639
- Current Phase: 2
640
-
641
- ## Phase 1: Foundation [COMPLETE]
642
- - [x] Task 1.1: Create user model [SMALL]
643
- - [x] Task 1.2: Add password hashing [SMALL]
644
- - [x] Task 1.3: Database migrations [MEDIUM]
645
-
646
- ## Phase 2: Core Auth [IN PROGRESS]
647
- - [x] Task 2.1: Login endpoint [MEDIUM]
648
- - [ ] Task 2.2: JWT generation [MEDIUM] (depends: 2.1) ← CURRENT
649
- - Acceptance: Returns valid JWT with user claims, 15-minute expiry
650
- - Attempt 1: REJECTED — missing expiration claim
651
- - [ ] Task 2.3: Token validation middleware [MEDIUM]
652
- ```
653
-
654
- ### context.md: What's Been Decided
655
-
656
- ```markdown
657
- ## Technical Decisions
658
- - bcrypt cost factor: 12
659
- - JWT TTL: 15 minutes; refresh TTL: 7 days
660
-
661
- ## SME Guidance (cached, never re-asked)
662
- ### security (Phase 1)
663
- - Never log tokens or passwords
664
- - Rate-limit login: 5 attempts / 15 min per IP
665
-
666
- ### api (Phase 1)
667
- - Return 401 for invalid credentials (not 404)
668
- ```
669
-
670
- ### Evidence Bundles
671
-
672
- Every completed task writes structured evidence to `.swarm/evidence/`:
673
-
674
- | Type | What It Captures |
675
- |------|--------------------|
676
- | review | Verdict, risk level, specific issues |
677
- | test | Pass/fail counts, coverage %, failure messages |
678
- | diff | Files changed, additions/deletions |
679
- | retrospective | Phase metrics, lessons learned, error taxonomy classification (injected into next phase) |
680
- | secretscan | Secret scan results: findings count, files scanned, skipped files (v6.33) |
681
- | completion-verify | Deterministic gate: verifies plan task identifiers exist in source files (written automatically by `completion-verify` tool; required before `phase_complete`) |
682
- | drift-verifier | Phase-close drift gate: `critic_drift_verifier` verdict (APPROVED/NEEDS_REVISION) and summary (written by architect via `write_drift_evidence`; required before `phase_complete`) |
683
-
684
- ### telemetry.jsonl: Session Observability
685
-
686
- Swarm emits structured JSONL events to `.swarm/telemetry.jsonl` for observability tooling (dashboards, alerting, audit logs). Events are fire-and-forget — failures never affect execution.
687
-
688
- ```json
689
- {"timestamp":"2026-03-25T14:30:00.000Z","event":"session_started","sessionId":"abc123","agentName":"architect"}
690
- {"timestamp":"2026-03-25T14:30:05.000Z","event":"delegation_begin","sessionId":"abc123","agentName":"coder","taskId":"1.1"}
691
- {"timestamp":"2026-03-25T14:31:00.000Z","event":"delegation_end","sessionId":"abc123","agentName":"coder","taskId":"1.1","result":"success"}
692
- {"timestamp":"2026-03-25T14:31:10.000Z","event":"gate_passed","sessionId":"abc123","gate":"reviewer","taskId":"1.1"}
693
- {"timestamp":"2026-03-25T14:32:00.000Z","event":"phase_changed","sessionId":"abc123","oldPhase":1,"newPhase":2}
694
- ```
695
-
696
- | Event | When Emitted |
697
- |-------|-------------|
698
- | `session_started` | New agent session created |
699
- | `session_ended` | Session ends (reason: normal, timeout, error) |
700
- | `agent_activated` | Agent identity confirmed via chat.message |
701
- | `delegation_begin` | Task dispatched to a sub-agent |
702
- | `delegation_end` | Sub-agent returns (success, rejected, error) |
703
- | `task_state_changed` | Task workflow state transitions |
704
- | `gate_passed` | Evidence written to `.swarm/evidence/{taskId}.json` |
705
- | `gate_failed` | Gate check blocked task completion |
706
- | `phase_changed` | Phase completed and new phase started |
707
- | `budget_updated` | Context budget crossed warning/critical threshold |
708
- | `hard_limit_hit` | Tool call/duration/repetition limit reached |
709
- | `revision_limit_hit` | Coder revision limit exceeded |
710
- | `loop_detected` | Repetitive tool call pattern detected |
711
- | `scope_violation` | Architect wrote outside declared scope |
712
- | `qa_skip_violation` | QA gate skipped without valid reason |
713
- | `model_fallback` | Transient error triggered model fallback |
714
- | `heartbeat` | 30-second throttled keep-alive signal |
715
-
716
- File rotates automatically at 10MB to `.swarm/telemetry.jsonl.1`.
717
-
718
- </details>
719
-
720
- <details>
721
- <summary><strong>Working Directory Requirement: No process.cwd() Fallback</strong></summary>
722
-
723
- All Swarm tools that accept a `working_directory` parameter **require an explicit path**. They do **not** fall back to `process.cwd()`. This prevents `.swarm` state from being created in project subdirectories when the host process's working directory differs from the actual project root (issue [#922](https://github.com/zaxbysauce/opencode-swarm/issues/922)).
724
-
725
- ### Defense-in-Depth
726
-
727
- This safety guarantee is implemented in two layers:
728
-
729
- 1. **Fast-path filter** (`resolveWorkingDirectory` in `src/tools/resolve-working-directory.ts`) — validates all incoming `working_directory` values for null-byte injection, path traversal, Windows device paths, and subdirectory containment before any file system access
730
- 2. **Canonical write-time guard** (`validateProjectRoot` in `src/evidence/manager.ts`) — uses `realpathSync` to canonicalize paths at evidence-write time, catching any symlink-based subdirectory bypasses that slip past the fast-path filter
731
-
732
- ### Tools That Require Explicit working_directory
733
-
734
- The following tools require an explicit `working_directory` and reject subdirectory paths:
735
-
736
- - `save_plan`
737
- - `update_task_status`
738
- - `declare_scope`
739
- - `pre_check_batch`
740
- - `test_impact`
741
- - `mutation_test`
742
- - `diff_summary`
743
-
744
- ### Failure Conditions
745
-
746
- | Condition | Behavior |
747
- |-----------|----------|
748
- | Missing (`undefined` / `null`) | Fails with: "Target workspace is required" |
749
- | Empty or whitespace-only | Fails with: "Target workspace cannot be empty or whitespace" |
750
- | Path traversal (`..`) | Fails with: "Target workspace cannot contain path traversal" |
751
- | Subdirectory of project root | Fails with: "...is a subdirectory of fallback..." |
752
- | Windows device path | Fails with: "Windows device paths are not allowed" |
753
-
754
- ### Usage Contract
755
-
756
- When using any affected tool, always pass a valid `working_directory`:
757
-
758
- ```typescript
759
- // save_plan example
760
- save_plan({
761
- title: "My Project",
762
- swarm_id: "mega",
763
- phases: [{ id: 1, name: "Setup", tasks: [{ id: "1.1", description: "Initialize project" }] }],
764
- working_directory: "/path/to/project" // Required - no process.cwd() fallback
765
- })
766
-
767
- // update_task_status example
768
- update_task_status({
769
- task_id: "1.1",
770
- status: "completed",
771
- working_directory: "/path/to/project" // Required - no fallback
772
- })
773
- ```
774
-
775
- ### Stray .swarm Detection
776
-
777
- `/swarm doctor` now detects and reports stray `.swarm` directories found in project subdirectories (created by older versions or misconfigured tools). It offers cleanup guidance to prevent state pollution.
778
-
779
- </details>
780
-
781
- <details>
782
- <summary><strong>Guardrails and Circuit Breakers</strong></summary>
783
-
784
- Every agent runs inside a circuit breaker that kills runaway behavior before it burns your credits.
785
-
786
- | Signal | Default Limit | What Happens |
787
- |--------|:---:|-------------|
788
- | Tool calls | 200 | Agent is stopped |
789
- | Duration | 30 min | Agent is stopped |
790
- | Same tool repeated | 10x | Agent is warned, then stopped |
791
- | Consecutive errors | 5 | Agent is stopped |
792
-
793
- Limits reset per task. A coder working on Task 2.3 is not penalized for tool calls made during Task 2.2.
794
-
795
- #### Architect Self-Coding Block
796
-
797
- If the architect writes files directly instead of delegating to the coder, a hard block fires:
798
-
799
- | Write count | Behavior |
800
- |:-----------:|----------|
801
- | 1–2 | Warning injected into next architect message |
802
- | ≥ 3 | `Error` thrown with `SELF_CODING_BLOCK` — identifies file paths written and count |
803
-
804
- The counter resets only when a coder delegation is dispatched. This is a hard enforcement — not advisory.
805
-
806
- Per-agent overrides:
807
-
808
- ```json
809
- {
810
- "guardrails": {
811
- "profiles": {
812
- "coder": { "max_tool_calls": 500, "max_duration_minutes": 60 },
813
- "explorer": { "max_tool_calls": 50 }
814
- }
815
- }
816
- }
817
- ```
818
-
819
- </details>
820
-
821
- <details>
822
- <summary><strong>File Authority (Per-Agent Write Permissions)</strong></summary>
823
-
824
- Swarm enforces per-agent file write authority — each agent can only write to specific paths. By default, these rules are hardcoded, but you can override them via config.
825
-
826
- ### Default Rules
827
-
828
- | Agent | Can Write | Blocked | Zones |
829
- |-------|-----------|---------|-------|
830
- | `architect` | Everything (except plan files) | `.swarm/plan.md`, `.swarm/plan.json` | `generated` |
831
- | `coder` | `src/`, `tests/`, `docs/`, `scripts/` | `.swarm/` (entire directory) | `generated`, `config` |
832
- | `reviewer` | `.swarm/evidence/`, `.swarm/outputs/` | `src/`, `.swarm/plan.md`, `.swarm/plan.json` | `generated` |
833
- | `test_engineer` | `tests/`, `.swarm/evidence/` | `src/`, `.swarm/plan.md`, `.swarm/plan.json` | `generated` |
834
- | `explorer` | Read-only | Everything | — |
835
- | `sme` | Read-only | Everything | — |
836
- | `docs` | `docs/`, `.swarm/outputs/` | — | `generated` |
837
- | `designer` | `docs/`, `.swarm/outputs/` | — | `generated` |
838
- | `critic` | `.swarm/evidence/` | — | `generated` |
839
-
840
- ### Prefixed Agents
841
-
842
- Prefixed agents (e.g., `paid_coder`, `mega_reviewer`, `local_architect`) inherit defaults from their canonical base agent via `stripKnownSwarmPrefix`. The lookup order is:
843
-
844
- 1. Exact match for the prefixed name (if explicitly defined in user config)
845
- 2. Fall back to the canonical agent's defaults (e.g., `paid_coder` → `coder`)
846
-
847
- ```json
848
- {
849
- "authority": {
850
- "rules": {
851
- "coder": { "allowedPrefix": ["src/", "lib/"] },
852
- "paid_coder": { "allowedPrefix": ["vendor/", "plugins/"] }
853
- }
854
- }
855
- }
856
- ```
857
-
858
- In this example, `paid_coder` gets its own explicit rule, while other prefixed coders (e.g., `mega_coder`) fall back to `coder`.
556
+ ### Skill Propagation
859
557
 
860
- #### Selecting the primary agent in multi-swarm configs (`default_agent`)
558
+ Swarm includes an intelligent skill propagation system that tracks, validates, and scores skill usage across agent delegations. When the architect delegates to subagents (coder, reviewer, test_engineer, etc.), the system:
861
559
 
862
- The top-level `default_agent` field controls which generated agents OpenCode treats as primary. **It is optional.** Behavior:
560
+ - **Logs skill usage** to `.swarm/skill-usage.jsonl` with session-scoped entries (agent, task ID, skill paths, timestamp)
561
+ - **Scores skill relevance** based on frequency, compliance, recency, task ID diversity, and context matching
562
+ - **Provides recommendations** — when delegating without a `SKILLS:` field, the system suggests relevant skills with relevance scores
563
+ - **Enforces compliance** — optional enforcement mode can block delegations missing the `SKILLS:` field
564
+ - **Auto-populates skill index** — maintains a `## Available Skills` section in `.swarm/context.md` with usage counts and compliance rates
565
+ - **Supports explicit routing** — `.opencode/skill-routing.yaml` maps agent types to specific skill paths with optional keyword descriptions
863
566
 
864
- - **Omitted** — every architect-role agent is primary. In a multi-swarm config that means each swarm exposes its own architect (`local_architect`, `mega_architect`, `paid_architect`, `modelrelay_architect`, …) as a selectable session default. This is the v7.0.0-compatible behavior and the recommended setup.
865
- - **Base role** (e.g. `"coder"`) every generated agent whose canonical base role matches becomes primary (`local_coder`, `mega_coder`, …).
866
- - **Exact generated name** (e.g. `"local_architect"`) — only that agent is primary.
867
- - **Unknown / invalid value** a one-time warning is logged and the resolver falls back to architect-role primaries (or the first generated agent if architects are disabled). The plugin never produces zero primaries when at least one agent exists.
567
+ **Guardrails:**
568
+ - Relevance scoring threshold: 0.5 (skills below this are not recommended)
569
+ - Maximum recommendations per delegation: 5
570
+ - Scoring budget safeguard: Skipped when session exceeds 500 skill-usage entries to prevent unbounded file reads
571
+ - Graceful degradation: Zero installed skills = zero friction — no warnings, no blocks, no auto-population
868
572
 
869
- See [`docs/configuration.md`](docs/configuration.md) for the full table.
870
-
871
- ### Runtime Enforcement
872
-
873
- Architect direct writes are enforced at runtime via `toolBefore` hook. This tracks writes to source code paths outside `.swarm/` and protects `.swarm/plan.md` and `.swarm/plan.json` from direct modification.
874
-
875
- ### Configuration
876
-
877
- Override default rules in `.opencode/opencode-swarm.json`:
573
+ **Configuration:**
878
574
 
879
575
  ```json
880
576
  {
881
- "authority": {
577
+ "skill_propagation": {
882
578
  "enabled": true,
883
- "rules": {
884
- "coder": {
885
- "allowedPrefix": ["src/", "lib/", "scripts/"],
886
- "blockedPrefix": [".swarm/"],
887
- "blockedZones": ["generated"]
888
- },
889
- "explorer": {
890
- "readOnly": false,
891
- "allowedPrefix": ["notes/", "scratch/"]
892
- }
893
- }
894
- }
895
- }
896
- ```
897
-
898
- ### Rule Fields
899
-
900
- | Field | Type | Description |
901
- |-------|------|-------------|
902
- | `readOnly` | boolean | If `true`, agent cannot write anywhere |
903
- | `blockedExact` | string[] | Exact file paths that are blocked |
904
- | `allowedExact` | string[] | Exact file paths that are allowed (overrides prefix/glob restrictions) |
905
- | `blockedPrefix` | string[] | Path prefixes that are blocked (e.g., `.swarm/`) |
906
- | `allowedPrefix` | string[] | Only these path prefixes are allowed. Omit to remove restriction; set `[]` to deny all |
907
- | `blockedGlobs` | string[] | Glob patterns that are blocked (uses picomatch: `**`, `*`, `?`) |
908
- | `allowedGlobs` | string[] | Glob patterns that are allowed (uses picomatch: `**`, `*`, `?`) |
909
- | `blockedZones` | string[] | File zones to block: `production`, `test`, `config`, `generated`, `docs`, `build` |
910
-
911
- ### Merge Behavior
912
-
913
- - User rules **override** hardcoded defaults for the specified agent
914
- - Scalar fields (`readOnly`) — user value replaces default
915
- - Array fields (`blockedPrefix`, `allowedPrefix`, etc.) — user array **replaces** entirely (not merged)
916
- - If a field is omitted in the user rule for a **known agent** (one with hardcoded defaults), the default value for that field is preserved
917
- - If a field is omitted in the user rule for a **custom agent** (not in the defaults list), that field is `undefined` — there are no defaults to inherit
918
- - `allowedPrefix: []` explicitly denies all writes; omitting `allowedPrefix` entirely means no allowlist restriction is applied (all paths are evaluated against blocklist rules only)
919
- - Setting `enabled: false` ignores all custom rules and uses hardcoded defaults
920
-
921
- ### Custom Agents
922
-
923
- Custom agents (not in the defaults list) start with no rules. Their write authority depends entirely on what you configure:
924
-
925
- - **Not in config at all** — agent is denied with `Unknown agent` (no rule exists; this is not the same as "blocked from all writes")
926
- - **In config without `allowedPrefix`** — no allowlist restriction applies; only any `blockedPrefix`, `blockedZones`, or `readOnly` rules you explicitly set will enforce limits
927
- - **In config with `allowedPrefix: []`** — all writes are denied
928
-
929
- To safely restrict a custom agent, always set `allowedPrefix` explicitly:
930
-
931
- ```json
932
- {
933
- "authority": {
934
- "rules": {
935
- "my_custom_agent": {
936
- "allowedPrefix": ["plugins/", "extensions/"],
937
- "blockedZones": ["generated"]
938
- }
579
+ "enforce": false,
580
+ "scoring": {
581
+ "threshold": 0.5,
582
+ "max_recommendations": 5
939
583
  }
940
584
  }
941
585
  }
942
586
  ```
943
587
 
944
- ### Advanced Examples
945
-
946
- #### Glob Pattern Support
947
-
948
- Use glob patterns for complex path matching:
949
-
950
- ```json
951
- {
952
- "authority": {
953
- "rules": {
954
- "coder": {
955
- "allowedGlobs": ["src/**/*.ts", "tests/**/*.test.ts"],
956
- "blockedGlobs": ["src/**/*.generated.ts", "**/*.d.ts"],
957
- "allowedExact": ["src/index.ts", "package.json"]
958
- },
959
- "docs_agent": {
960
- "allowedGlobs": ["docs/**/*.md", "*.md"],
961
- "blockedExact": [".swarm/plan.md"]
962
- }
963
- }
964
- }
965
- }
588
+ **Skill routing file format** (`.opencode/skill-routing.yaml`):
589
+
590
+ ```yaml
591
+ version: 1
592
+ routing:
593
+ coder:
594
+ - path: .claude/skills/writing-tests/SKILL.md
595
+ keywords: ["test", "testing", "writing tests"]
596
+ - path: .claude/skills/engineering-conventions/SKILL.md
597
+ keywords: ["engineering", "conventions", "invariants"]
598
+ reviewer:
599
+ - path: .claude/skills/swarm-pr-review/SKILL.md
600
+ keywords: ["review", "security", "audit"]
966
601
  ```
967
602
 
968
- **Glob Pattern Features:**
969
- - `**` — Match any number of directories: `src/**/*.ts` matches all TypeScript files in src/ and subdirectories
970
- - `*` — Match any characters except path separators: `*.md` matches all Markdown files in current directory
971
- - `?` — Match single character: `test?.js` matches `test1.js`, `testa.js`
972
- - Uses [picomatch](https://github.com/micromatch/picomatch) for cross-platform compatibility
973
-
974
- **Path Normalization and Symlinks:**
975
- Paths are resolved via `realpathSync` before matching, which resolves symlinks and prevents path-traversal escapes. However, if a symlink's target does not exist, `realpathSync` throws and the fallback returns the symlink's own path (unresolved). A dangling symlink inside an `allowedPrefix` directory will therefore pass prefix-based checks even if its intended target is outside the project. Use `blockedExact` or `blockedGlobs` to deny known dangling-symlink paths explicitly.
976
-
977
- **Evaluation Order:**
978
- 1. `readOnly` check (if true, deny all writes)
979
- 2. `blockedExact` (exact path matches, highest priority)
980
- 3. `blockedGlobs` (glob pattern matches)
981
- 4. `allowedExact` (exact path matches, overrides prefix/glob restrictions)
982
- 5. `allowedGlobs` (glob pattern matches)
983
- 6. `blockedPrefix` (prefix matches)
984
- 7. `allowedPrefix` (prefix matches)
985
- 8. `blockedZones` (zone classification)
986
-
987
- </details>
988
-
989
- <details>
990
- <summary><strong>Context Budget Guard</strong></summary>
991
-
992
- The Context Budget Guard monitors how much context Swarm is injecting into the conversation. It helps prevent context overflow before it becomes a problem.
993
-
994
- ### Default Behavior
995
-
996
- - **Enabled automatically** — No setup required. Swarm starts tracking context usage right away.
997
- - **What it measures** — Only the context that Swarm injects (plan, context, evidence, retrospectives). It does **not** count your chat history or the model's responses.
998
- - **Warning threshold (0.7 ratio)** — When swarm-injected context reaches ~2800 tokens (70% of 4000), the architect receives a one-time advisory warning. This is informational — execution continues normally.
999
- - **Critical threshold (0.9 ratio)** — When context reaches ~3600 tokens (90% of 4000), the architect receives a critical alert with a recommendation to run `/swarm handoff`. This is also one-time only.
1000
- - **Non-nagging** — Alerts fire once per session, not repeatedly. You won't be pestered every turn.
1001
- - **Who sees warnings** — Only the architect receives these warnings. Other agents are unaware of the budget.
1002
-
1003
- To disable entirely, set `context_budget.enabled: false` in your swarm config.
603
+ Routing skills are merged with scored recommendations, with explicitly routed skills receiving a boosted score (0.9) to prioritize them.
1004
604
 
1005
605
  ### Configuration Reference
1006
606
 
package/dist/cli/index.js CHANGED
@@ -34,7 +34,7 @@ var package_default;
34
34
  var init_package = __esm(() => {
35
35
  package_default = {
36
36
  name: "opencode-swarm",
37
- version: "7.28.3",
37
+ version: "7.29.1",
38
38
  description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
39
39
  main: "dist/index.js",
40
40
  types: "dist/index.d.ts",
@@ -40260,19 +40260,9 @@ function validateConfigKey(path24, value, _config) {
40260
40260
  case "guardrails.profiles": {
40261
40261
  const profiles = value;
40262
40262
  if (profiles) {
40263
- const validAgents = [
40264
- "architect",
40265
- "coder",
40266
- "test_engineer",
40267
- "explorer",
40268
- "reviewer",
40269
- "critic",
40270
- "sme",
40271
- "docs",
40272
- "designer"
40273
- ];
40263
+ const validAgents = new Set(ALL_AGENT_NAMES);
40274
40264
  for (const [agentName, profile] of Object.entries(profiles)) {
40275
- if (!validAgents.includes(agentName)) {
40265
+ if (!validAgents.has(agentName)) {
40276
40266
  findings.push({
40277
40267
  id: "unknown-agent-profile",
40278
40268
  title: "Unknown agent profile",
@@ -40433,23 +40423,23 @@ function validateConfigKey(path24, value, _config) {
40433
40423
  case "swarms": {
40434
40424
  const swarms = value;
40435
40425
  if (swarms && typeof swarms === "object") {
40426
+ const validAgents = new Set(ALL_AGENT_NAMES);
40436
40427
  for (const [swarmId, swarmConfig] of Object.entries(swarms)) {
40437
40428
  const swarm = swarmConfig;
40438
40429
  if (swarm.agents && typeof swarm.agents === "object") {
40439
40430
  for (const [agentName] of Object.entries(swarm.agents)) {
40440
- const validAgents = [
40441
- "architect",
40442
- "coder",
40443
- "test_engineer",
40444
- "explorer",
40445
- "reviewer",
40446
- "critic",
40447
- "sme",
40448
- "docs",
40449
- "designer"
40450
- ];
40451
- const baseName = agentName.replace(/^[a-zA-Z0-9]+_/, "");
40452
- if (!validAgents.includes(baseName)) {
40431
+ const baseName = stripKnownSwarmPrefix(agentName);
40432
+ if (baseName !== agentName && agentName.startsWith(`${swarmId}_`) && validAgents.has(baseName)) {
40433
+ findings.push({
40434
+ id: "prefixed-swarm-agent-override",
40435
+ title: "Prefixed agent override is ignored",
40436
+ description: `Agent "${agentName}" in swarm "${swarmId}" uses a generated agent name. ` + `Per-swarm overrides must use the canonical key "${baseName}", e.g. ` + `"swarms.${swarmId}.agents.${baseName}.model". Otherwise the override is ignored and the agent falls back to its default model.`,
40437
+ severity: "warn",
40438
+ path: `swarms.${swarmId}.agents.${agentName}`,
40439
+ currentValue: swarm.agents[agentName],
40440
+ autoFixable: false
40441
+ });
40442
+ } else if (!validAgents.has(baseName)) {
40453
40443
  findings.push({
40454
40444
  id: "unknown-swarm-agent",
40455
40445
  title: "Unknown agent in swarm",
@@ -40800,6 +40790,8 @@ function removeStraySwarmDir(projectRoot, strayPath) {
40800
40790
  }
40801
40791
  var VALID_CONFIG_PATTERNS, DANGEROUS_PATH_SEGMENTS;
40802
40792
  var init_config_doctor = __esm(() => {
40793
+ init_constants();
40794
+ init_schema();
40803
40795
  init_utils();
40804
40796
  VALID_CONFIG_PATTERNS = [
40805
40797
  /^\.config[\\/]opencode[\\/]opencode-swarm\.json$/,
@@ -90,6 +90,18 @@ export declare function gateKnowledgeApplication(args: {
90
90
  recentArchitectText: string;
91
91
  config: KnowledgeApplicationConfig;
92
92
  }): GateResult;
93
+ /**
94
+ * Filter knowledge entries by minimum confidence threshold.
95
+ * Used by the knowledge reinjection hook to surface high-confidence
96
+ * knowledge after context compression events.
97
+ *
98
+ * @param entries - Array of knowledge entries (swarm or hive)
99
+ * @param threshold - Minimum confidence score (default 0.8)
100
+ * @returns Filtered entries with confidence >= threshold
101
+ */
102
+ export declare function filterHighConfidenceKnowledge<T extends {
103
+ confidence: number;
104
+ }>(entries: T[], threshold?: number): T[];
93
105
  export declare const _internals: {
94
106
  parseAcknowledgments: typeof parseAcknowledgments;
95
107
  recordKnowledgeShown: typeof recordKnowledgeShown;
@@ -20,6 +20,12 @@ import * as fs from 'node:fs';
20
20
  import type { MessageWithParts } from './knowledge-types.js';
21
21
  import { computeSkillRelevanceScore, formatSkillIndexWithContext } from './skill-scoring.js';
22
22
  import { appendSkillUsageEntry, readSkillUsageEntries, readSkillUsageEntriesTail } from './skill-usage-log.js';
23
+ /**
24
+ * Load routing skills from .opencode/skill-routing.yaml for a target agent.
25
+ * Returns array of skill paths that are explicitly routed for the agent.
26
+ * Best-effort: returns empty array on any error or if file doesn't exist.
27
+ */
28
+ export declare function loadRoutingSkills(directory: string, targetAgent: string): string[];
23
29
  /** Agents that should receive skill context in delegations. */
24
30
  export declare const SKILL_CAPABLE_AGENTS: Set<string>;
25
31
  /**
@@ -58,11 +64,12 @@ export declare const _internals: {
58
64
  appendSkillUsageEntry: typeof appendSkillUsageEntry;
59
65
  readSkillUsageEntries: typeof readSkillUsageEntries;
60
66
  readSkillUsageEntriesTail: typeof readSkillUsageEntriesTail;
61
- extractSkillsFieldFromPrompt: typeof extractSkillsFieldFromPrompt;
62
67
  parseSkillPaths: typeof parseSkillPaths;
63
68
  extractTaskIdFromPrompt: typeof extractTaskIdFromPrompt;
69
+ extractSkillsFieldFromPrompt: typeof extractSkillsFieldFromPrompt;
64
70
  computeSkillRelevanceScore: typeof computeSkillRelevanceScore;
65
71
  formatSkillIndexWithContext: typeof formatSkillIndexWithContext;
72
+ loadRoutingSkills: typeof loadRoutingSkills;
66
73
  };
67
74
  /**
68
75
  * Scans project for available skill SKILL.md files.
@@ -117,13 +124,18 @@ export declare function extractTaskIdFromPrompt(prompt: string): string;
117
124
  * the architect delegates to a skill-capable agent with a non-empty, non-"none"
118
125
  * SKILLS field.
119
126
  *
120
- * @returns { blocked: false, reason: null } when no action needed.
121
- * { blocked: false, reason: "warning message" } when warning only (enforce=false).
122
- * { blocked: true, reason: "blocked: ..." } when blocking (enforce=true).
127
+ * @returns { blocked: boolean; reason: string | null; recommendedSkills?: Array<{ skillPath: string; score: number; usageCount: number }> }
128
+ * When scoring has computed results, includes `recommendedSkills` with ranked skill recommendations.
129
+ * When scoring was skipped or errored, `recommendedSkills` is undefined.
123
130
  */
124
131
  export declare function skillPropagationGateBefore(directory: string, input: SkillGateInput, config: SkillPropagationConfig): Promise<{
125
132
  blocked: boolean;
126
133
  reason: string | null;
134
+ recommendedSkills?: Array<{
135
+ skillPath: string;
136
+ score: number;
137
+ usageCount: number;
138
+ }>;
127
139
  }>;
128
140
  /**
129
141
  * Chat messages transform hook. Scans reviewer output for SKILL_COMPLIANCE