opencode-swarm 6.48.0 → 6.49.0
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 +94 -28
- package/dist/cli/index.js +77 -10
- package/dist/index.js +334 -13
- package/dist/lang/framework-detector.d.ts +74 -0
- package/dist/lang/profiles.d.ts +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -30,7 +30,7 @@ Most AI coding tools let one model write code and ask that same model whether th
|
|
|
30
30
|
- 🔒 **Gated pipeline** — code never ships without reviewer + test engineer approval (bypassed in turbo mode)
|
|
31
31
|
- 🔄 **Phase completion gates** — completion-verify and drift verifier gates enforced before phase completion (bypassed in turbo mode)
|
|
32
32
|
- 🔁 **Resumable sessions** — all state saved to `.swarm/`; pick up any project any day
|
|
33
|
-
- 🌐 **
|
|
33
|
+
- 🌐 **12 languages** — TypeScript, Python, Go, Rust, Java, Kotlin, C#, C/C++, Swift, Dart, Ruby, PHP
|
|
34
34
|
- 🛡️ **Built-in security** — SAST, secrets scanning, dependency audit per task
|
|
35
35
|
- 🆓 **Free tier** — works with OpenCode Zen's free model roster
|
|
36
36
|
- ⚙️ **Fully configurable** — override any agent's model, disable agents, tune guardrails
|
|
@@ -69,16 +69,26 @@ All project state lives in `.swarm/`:
|
|
|
69
69
|
|
|
70
70
|
```text
|
|
71
71
|
.swarm/
|
|
72
|
-
├── plan.md
|
|
73
|
-
├── plan.json
|
|
74
|
-
├──
|
|
75
|
-
├──
|
|
76
|
-
├──
|
|
77
|
-
├──
|
|
78
|
-
├──
|
|
79
|
-
|
|
72
|
+
├── plan.md # Projected plan (generated from ledger)
|
|
73
|
+
├── plan.json # Projected plan data (generated from ledger)
|
|
74
|
+
├── plan-ledger.jsonl # Durable append-only ledger — authoritative source of truth (v6.44)
|
|
75
|
+
├── SWARM_PLAN.md # Export checkpoint artifact written on save_plan / phase_complete / close
|
|
76
|
+
├── SWARM_PLAN.json # Export checkpoint artifact (importable via importCheckpoint)
|
|
77
|
+
├── context.md # Technical decisions and SME guidance
|
|
78
|
+
├── spec.md # Feature specification (written by /swarm specify)
|
|
79
|
+
├── close-summary.md # Written by /swarm close with project summary
|
|
80
|
+
├── close-lessons.md # Optional: explicit session lessons for /swarm close to curate
|
|
81
|
+
├── doc-manifest.json # Documentation index built by doc_scan tool
|
|
82
|
+
├── events.jsonl # Event stream for diagnostics
|
|
83
|
+
├── evidence/ # Review/test evidence bundles per task
|
|
84
|
+
├── telemetry.jsonl # Session observability events (JSONL)
|
|
85
|
+
├── curator-summary.json # Curator system state
|
|
86
|
+
├── curator-briefing.md # Curator init briefing injected at session start
|
|
87
|
+
└── drift-report-phase-N.json # Plan-vs-reality drift reports (Curator)
|
|
80
88
|
```
|
|
81
89
|
|
|
90
|
+
> **Plan durability (v6.44):** `plan-ledger.jsonl` is the authoritative source of truth for plan state. `plan.json` and `plan.md` are projections derived from the ledger — if they are missing or stale, `loadPlan()` auto-rebuilds them from the ledger. `SWARM_PLAN.md` / `SWARM_PLAN.json` are export-only checkpoint artifacts written automatically — use `SWARM_PLAN.json` to restore if both `plan.json` and the ledger are lost.
|
|
91
|
+
|
|
82
92
|
Swarm is resumable by design. If `.swarm/` already exists, the architect goes straight into **RESUME** → **EXECUTE** instead of repeating discovery.
|
|
83
93
|
|
|
84
94
|
---
|
|
@@ -312,6 +322,7 @@ When a task requires multiple coder attempts (e.g., reviewer rejections), Swarm
|
|
|
312
322
|
| `/swarm diagnose` | Health check for swarm state, including config parsing, grammar files, checkpoint manifest, events stream integrity, and steering directive staleness |
|
|
313
323
|
| `/swarm evidence 2.1` | Show review/test results for a specific task |
|
|
314
324
|
| `/swarm history` | What's been completed so far |
|
|
325
|
+
| `/swarm close [--prune-branches]` | Idempotent session close-out: writes retrospectives, curates lessons (reads `.swarm/close-lessons.md` if present), archives evidence, resets `context.md`, cleans config-backup files, optionally prunes merged branches |
|
|
315
326
|
| `/swarm reset --confirm` | Start over (clears all swarm state) |
|
|
316
327
|
|
|
317
328
|
---
|
|
@@ -336,9 +347,9 @@ Agent roles (see [Agent Categories](#agent-categories) for classification refere
|
|
|
336
347
|
| `architect` | Coordinates the workflow, writes plans, enforces gates | Always |
|
|
337
348
|
| `explorer` | Scans the codebase and gathers context | Before planning, after phase wrap |
|
|
338
349
|
| `sme` | Provides domain guidance | During planning / consultation |
|
|
339
|
-
| `critic` | Reviews the plan before execution | Before coding starts |
|
|
340
|
-
| `critic_sounding_board` | Pre-escalation pushback before user
|
|
341
|
-
| `critic_drift_verifier` | Phase
|
|
350
|
+
| `critic` | Reviews the plan before execution and blocks coding until approved | Before coding starts (CRITIC-GATE mode) |
|
|
351
|
+
| `critic_sounding_board` | Pre-escalation pushback — the architect consults this before contacting the user; returns UNNECESSARY / REPHRASE / APPROVED / RESOLVE | When architect hits an impasse |
|
|
352
|
+
| `critic_drift_verifier` | **Phase-close drift detector**: verifies that the completed implementation still matches the original plan spec. Returns APPROVED or NEEDS_REVISION. When NEEDS_REVISION is returned, the phase is **blocked** — the architect must address deviations before calling `phase_complete`. After receiving the verdict, the architect calls `write_drift_evidence` to record the gate result. Bypassed in turbo mode. | Before `phase_complete` (PHASE-WRAP mode) |
|
|
342
353
|
| `coder` | Implements one task at a time | During execution |
|
|
343
354
|
| `reviewer` | Reviews correctness and security | After each task |
|
|
344
355
|
| `test_engineer` | Writes and runs tests | After each task |
|
|
@@ -405,6 +416,9 @@ MODE: EXECUTE (per task)
|
|
|
405
416
|
├── 5j. @test_engineer (verification tests + coverage ≥70%)
|
|
406
417
|
├── 5k. @test_engineer (adversarial tests)
|
|
407
418
|
├── 5l. architect regression sweep (scope:"graph" to find cross-task test regressions)
|
|
419
|
+
├── 5l-ter. test drift detection (conditional — fires when changes involve command behaviour,
|
|
420
|
+
│ parsing/routing logic, user-visible output, public contracts, assertion-heavy areas,
|
|
421
|
+
│ or helper lifecycle changes; validates tests still align with current behaviour)
|
|
408
422
|
├── 5m. ⛔ Pre-commit checklist (all 4 items required, no override)
|
|
409
423
|
└── 5n. Task marked complete, evidence written
|
|
410
424
|
```
|
|
@@ -419,12 +433,12 @@ The architect moves through these modes automatically:
|
|
|
419
433
|
|---|---|
|
|
420
434
|
| `RESUME` | Existing `.swarm/` state was found, so Swarm continues where it left off |
|
|
421
435
|
| `CLARIFY` | Swarm asks for missing information it cannot infer |
|
|
422
|
-
| `DISCOVER` | Explorer scans the codebase |
|
|
436
|
+
| `DISCOVER` | Explorer scans the codebase; co-change dark matter analysis runs automatically to detect hidden file couplings (v6.41) |
|
|
423
437
|
| `CONSULT` | SME agents provide domain guidance |
|
|
424
438
|
| `PLAN` | Architect writes or updates the phased plan (includes CODEBASE REALITY CHECK on brownfield projects) |
|
|
425
439
|
| `CRITIC-GATE` | Critic reviews the plan before execution |
|
|
426
440
|
| `EXECUTE` | Tasks are implemented one at a time through the QA pipeline |
|
|
427
|
-
| `PHASE-WRAP` | A phase closes out, docs
|
|
441
|
+
| `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` |
|
|
428
442
|
|
|
429
443
|
> **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.
|
|
430
444
|
|
|
@@ -491,6 +505,8 @@ Every completed task writes structured evidence to `.swarm/evidence/`:
|
|
|
491
505
|
| diff | Files changed, additions/deletions |
|
|
492
506
|
| retrospective | Phase metrics, lessons learned, error taxonomy classification (injected into next phase) |
|
|
493
507
|
| secretscan | Secret scan results: findings count, files scanned, skipped files (v6.33) |
|
|
508
|
+
| completion-verify | Deterministic gate: verifies plan task identifiers exist in source files (written automatically by `completion-verify` tool; required before `phase_complete`) |
|
|
509
|
+
| 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`) |
|
|
494
510
|
|
|
495
511
|
### telemetry.jsonl: Session Observability
|
|
496
512
|
|
|
@@ -832,7 +848,7 @@ To disable entirely, set `context_budget.enabled: false` in your swarm config.
|
|
|
832
848
|
|
|
833
849
|
| Tool | What It Does |
|
|
834
850
|
|------|-------------|
|
|
835
|
-
| syntax_check | Tree-sitter validation across
|
|
851
|
+
| syntax_check | Tree-sitter validation across 12 languages |
|
|
836
852
|
| placeholder_scan | Catches TODOs, FIXMEs, stubs, placeholder text |
|
|
837
853
|
| sast_scan | Offline security analysis, 63+ rules, 9 languages |
|
|
838
854
|
| sbom_generate | CycloneDX dependency tracking, 8 ecosystems |
|
|
@@ -1119,6 +1135,7 @@ Control how tool outputs are summarized for LLM context.
|
|
|
1119
1135
|
| `/swarm benchmark` | Performance benchmarks |
|
|
1120
1136
|
| `/swarm retrieve [id]` | Retrieve auto-summarized tool outputs (supports offset/limit pagination) |
|
|
1121
1137
|
| `/swarm reset --confirm` | Clear swarm state files |
|
|
1138
|
+
| `/swarm reset-session` | Clear session state files in `.swarm/session/` (preserves plan and context) |
|
|
1122
1139
|
| `/swarm preflight` | Run phase preflight checks |
|
|
1123
1140
|
| `/swarm config doctor [--fix]` | Config validation with optional auto-fix |
|
|
1124
1141
|
| `/swarm doctor tools` | Tool registration coherence and binary readiness check |
|
|
@@ -1126,6 +1143,18 @@ Control how tool outputs are summarized for LLM context.
|
|
|
1126
1143
|
| `/swarm specify [description]` | Generate or import a feature specification |
|
|
1127
1144
|
| `/swarm clarify [topic]` | Clarify and refine an existing feature specification |
|
|
1128
1145
|
| `/swarm analyze` | Analyze spec.md vs plan.md for requirement coverage gaps |
|
|
1146
|
+
| `/swarm close [--prune-branches]` | Idempotent session close-out: retrospectives, lesson curation, evidence archive, context.md reset, config-backup cleanup, optional branch pruning |
|
|
1147
|
+
| `/swarm write-retro` | Write a phase retrospective manually |
|
|
1148
|
+
| `/swarm handoff` | Generate a handoff summary for context-budget-critical sessions |
|
|
1149
|
+
| `/swarm simulate` | Simulate plan execution without writing code |
|
|
1150
|
+
| `/swarm promote` | Promote swarm-scoped knowledge to hive (global) knowledge |
|
|
1151
|
+
| `/swarm evidence summary` | Generate a summary across all evidence bundles with completion ratio and blockers |
|
|
1152
|
+
| `/swarm knowledge` | List knowledge entries |
|
|
1153
|
+
| `/swarm knowledge migrate` | Migrate knowledge entries to the current format |
|
|
1154
|
+
| `/swarm knowledge quarantine [id]` | Move a knowledge entry to quarantine |
|
|
1155
|
+
| `/swarm knowledge restore [id]` | Restore a quarantined knowledge entry |
|
|
1156
|
+
| `/swarm turbo` | Enable turbo mode for the current session (bypasses QA gates) |
|
|
1157
|
+
| `/swarm checkpoint` | Save a git checkpoint for the current state |
|
|
1129
1158
|
|
|
1130
1159
|
</details>
|
|
1131
1160
|
|
|
@@ -1139,11 +1168,11 @@ Swarm limits which tools each agent can access based on their role. This prevent
|
|
|
1139
1168
|
|
|
1140
1169
|
| Agent | Tools | Count | Rationale |
|
|
1141
1170
|
|-------|-------|:---:|-----------|
|
|
1142
|
-
| **architect** | All
|
|
1143
|
-
| **reviewer** | diff, imports, lint, pkg_audit, pre_check_batch, secretscan, symbols, complexity_hotspots, retrieve_summary, extract_code_blocks, test_runner |
|
|
1144
|
-
| **coder** | diff, imports, lint, symbols, extract_code_blocks, retrieve_summary |
|
|
1145
|
-
| **test_engineer** | test_runner, diff, symbols, extract_code_blocks, retrieve_summary, imports, complexity_hotspots, pkg_audit |
|
|
1146
|
-
| **explorer** | complexity_hotspots, detect_domains, extract_code_blocks, gitingest, imports, retrieve_summary, schema_drift, symbols, todo_extract |
|
|
1171
|
+
| **architect** | All registered tools | — | Orchestrator needs full visibility |
|
|
1172
|
+
| **reviewer** | diff, imports, lint, pkg_audit, pre_check_batch, secretscan, symbols, complexity_hotspots, retrieve_summary, extract_code_blocks, test_runner, suggest_patch, batch_symbols | 13 | Security-focused QA |
|
|
1173
|
+
| **coder** | diff, imports, lint, symbols, extract_code_blocks, retrieve_summary, search | 7 | Write-focused, minimal read tools |
|
|
1174
|
+
| **test_engineer** | test_runner, diff, symbols, extract_code_blocks, retrieve_summary, imports, complexity_hotspots, pkg_audit, search | 9 | Testing and verification |
|
|
1175
|
+
| **explorer** | complexity_hotspots, detect_domains, extract_code_blocks, gitingest, imports, retrieve_summary, schema_drift, symbols, todo_extract, search, batch_symbols | 11 | Discovery and analysis |
|
|
1147
1176
|
| **sme** | complexity_hotspots, detect_domains, extract_code_blocks, imports, retrieve_summary, schema_drift, symbols | 7 | Domain expertise research |
|
|
1148
1177
|
| **critic** | complexity_hotspots, detect_domains, imports, retrieve_summary, symbols | 5 | Plan review, minimal toolset |
|
|
1149
1178
|
| **docs** | detect_domains, doc_extract, doc_scan, extract_code_blocks, gitingest, imports, retrieve_summary, schema_drift, symbols, todo_extract | 10 | Documentation synthesis and discovery |
|
|
@@ -1203,8 +1232,10 @@ The following tools can be assigned to agents via overrides:
|
|
|
1203
1232
|
|
|
1204
1233
|
| Tool | Purpose |
|
|
1205
1234
|
|------|---------|
|
|
1235
|
+
| `batch_symbols` | Extract exported symbols from multiple files in a single call; per-file error isolation; 75–98% call reduction vs sequential (v6.45); registered for architect, explorer, reviewer |
|
|
1206
1236
|
| `checkpoint` | Save/restore git checkpoints |
|
|
1207
1237
|
| `check_gate_status` | Read-only query of task gate status |
|
|
1238
|
+
| `co_change_analyzer` | Scan git history for files that co-change frequently; generates dark matter architecture knowledge entries during DISCOVER mode (v6.41); architect-only |
|
|
1208
1239
|
| `complexity_hotspots` | Identify high-risk code areas |
|
|
1209
1240
|
| `declare_scope` | Pre-declare the file scope for the next coder delegation (architect-only); violations trigger warnings |
|
|
1210
1241
|
| `detect_domains` | Detect SME domains from text |
|
|
@@ -1220,14 +1251,17 @@ The following tools can be assigned to agents via overrides:
|
|
|
1220
1251
|
| `pkg_audit` | Security audit of dependencies |
|
|
1221
1252
|
| `pre_check_batch` | Parallel pre-checks (lint, secrets, SAST, quality) |
|
|
1222
1253
|
| `retrieve_summary` | Retrieve summarized tool outputs |
|
|
1254
|
+
| `save_plan` | Persist plan to `.swarm/plan.json`, `plan.md`, and ledger; also writes `SWARM_PLAN.md` / `SWARM_PLAN.json` checkpoint artifacts; requires explicit `working_directory` parameter |
|
|
1223
1255
|
| `schema_drift` | Detect OpenAPI/schema drift |
|
|
1256
|
+
| `search` | Workspace-scoped ripgrep-style structured text search; literal and regex modes, glob filtering, result limits (v6.45); registered for architect, coder, reviewer, explorer, test_engineer |
|
|
1224
1257
|
| `secretscan` | Scan for secrets in code |
|
|
1258
|
+
| `suggest_patch` | Generate contextual diff hunks without modifying files; read-only patch suggestions for reviewer→coder handoff (v6.45); registered for reviewer and architect |
|
|
1225
1259
|
| `symbols` | Extract exported symbols |
|
|
1226
1260
|
| `test_runner` | Run project tests |
|
|
1227
1261
|
| `update_task_status` | Mark plan tasks as pending/in_progress/completed/blocked; track phase progress; acquires lock on `plan.json` before writing |
|
|
1228
1262
|
| `todo_extract` | Extract TODO/FIXME comments |
|
|
1229
1263
|
| `write_retro` | Document phase retrospectives via the phase_complete workflow; capture lessons learned |
|
|
1230
|
-
| `write_drift_evidence` | Write drift verification evidence after critic_drift_verifier completes |
|
|
1264
|
+
| `write_drift_evidence` | Write drift verification evidence after critic_drift_verifier completes; architect calls this after receiving the verifier’s verdict — the critic does not write files directly |
|
|
1231
1265
|
|
|
1232
1266
|
---
|
|
1233
1267
|
|
|
@@ -1236,8 +1270,34 @@ The following tools can be assigned to agents via overrides:
|
|
|
1236
1270
|
|
|
1237
1271
|
> For the complete version history, see [CHANGELOG.md](CHANGELOG.md) or [docs/releases/](docs/releases/).
|
|
1238
1272
|
|
|
1239
|
-
### v6.
|
|
1273
|
+
### v6.47.0 — `/swarm close` Full Session Close-Out
|
|
1274
|
+
|
|
1275
|
+
- **`/swarm close` expanded**: Now performs complete close-out: resets `context.md`, deletes stale `config-backup-*.json` files, supports plan-free sessions (PR reviews, investigations), and accepts `--prune-branches` to delete local branches whose remote tracking ref is `gone` (merged/deleted upstream).
|
|
1276
|
+
- **Lesson injection**: If `.swarm/close-lessons.md` exists when `/swarm close` runs, the architect’s explicit lessons are curated into the knowledge base before the file is deleted.
|
|
1277
|
+
|
|
1278
|
+
### v6.45.0 — New Search, Patch, and Batch Tools
|
|
1279
|
+
|
|
1280
|
+
- **`search` tool**: Workspace-scoped ripgrep-style structured search with literal/regex modes and glob filtering. Registered for architect, coder, reviewer, explorer, test_engineer.
|
|
1281
|
+
- **`suggest_patch` tool**: Reviewer-safe context-anchored patch suggestion. Generates diff hunks without writing files. Registered for reviewer and architect.
|
|
1282
|
+
- **`batch_symbols` tool**: Batched symbol extraction from multiple files in one call; per-file error isolation; 75–98% call reduction vs sequential single-file calls. Registered for architect, explorer, reviewer.
|
|
1283
|
+
- **Step 5l-ter**: Test drift detection step added to the EXECUTE pipeline. Fires conditionally when changes involve command behaviour, parsing/routing logic, user-visible output, public contracts, assertion-heavy areas, or helper lifecycle changes.
|
|
1284
|
+
|
|
1285
|
+
### v6.44.0 — Durable Plan Ledger
|
|
1286
|
+
|
|
1287
|
+
- **`plan-ledger.jsonl`**: Append-only JSONL ledger is now the authoritative source of truth for plan state. `plan.json` and `plan.md` are projections derived from the ledger. `loadPlan()` auto-rebuilds projections from the ledger on hash mismatch.
|
|
1288
|
+
- **Checkpoint artifacts**: `writeCheckpoint()` writes `SWARM_PLAN.md` and `SWARM_PLAN.json` at the project root on every `save_plan`, `phase_complete`, and `/swarm close`. Use `SWARM_PLAN.json` to restore after data loss.
|
|
1289
|
+
- **Auto-generated tool lists**: Architect prompt `YOUR TOOLS` and `Available Tools` sections are now generated from `AGENT_TOOL_MAP.architect` — no more hand-maintained lists that drift.
|
|
1290
|
+
- See [docs/plan-durability.md](docs/plan-durability.md) for migration notes.
|
|
1240
1291
|
|
|
1292
|
+
### v6.42.0 — Curator LLM Delegation Wired
|
|
1293
|
+
|
|
1294
|
+
- **Curator now performs real LLM analysis**: Previously the LLM delegation was scaffolded but never connected — every call fell through to data-only mode. All three call sites now invoke the Explorer agent with curator-specific system prompts.
|
|
1295
|
+
- **`curator.enabled` now defaults to `true`**: The curator falls back gracefully to data-only mode when no SDK client is available (e.g., in unit tests). If you relied on the previous `false` default, set `"curator": { "enabled": false }` explicitly.
|
|
1296
|
+
|
|
1297
|
+
### v6.41.0 — Dark Matter Detection + `/swarm close` + Drift Evidence Tool
|
|
1298
|
+
|
|
1299
|
+
- **Dark matter detection pipeline**: During DISCOVER mode, automatically scans git history for files that frequently co-change. Results are stored as `architecture` knowledge entries and the architect is guided to consider co-change partners when declaring scope. Silently skips repos with fewer than 20 commits or no git history.
|
|
1300
|
+
- **`/swarm close` command**: New idempotent close command. Writes retrospectives for in-progress phases, curates session lessons via the knowledge pipeline, archives evidence, marks phases/tasks as `closed`, writes `.swarm/close-summary.md`, and cleans state.
|
|
1241
1301
|
- **`write_drift_evidence` tool**: New architect tool for persisting drift verification evidence after critic_drift_verifier delegation
|
|
1242
1302
|
- Accepts phase number, verdict (APPROVED/NEEDS_REVISION), and summary
|
|
1243
1303
|
- Normalizes verdict automatically (APPROVED → approved, NEEDS_REVISION → rejected)
|
|
@@ -1291,7 +1351,7 @@ The following tools can be assigned to agents via overrides:
|
|
|
1291
1351
|
|
|
1292
1352
|
This release adds the optional Curator system for phase-level intelligence and fixes session snapshot persistence for task workflow states.
|
|
1293
1353
|
|
|
1294
|
-
- **Curator system**:
|
|
1354
|
+
- **Curator system**: Background analysis system (`curator.enabled = false` by default in v6.22; **changed to `true` in v6.42**). After each phase, collects events, checks compliance, and writes drift reports to `.swarm/drift-report-phase-N.json`. Three integration points: init on first phase, phase analysis after each phase, and drift injection into architect context at phase start.
|
|
1295
1355
|
- **Drift reports**: `runCriticDriftCheck` compares planned vs. actual decisions and writes structured drift reports with alignment scores (`ALIGNED` / `MINOR_DRIFT` / `MAJOR_DRIFT` / `OFF_SPEC`). Latest drift summary is prepended to the architect's knowledge context each phase.
|
|
1296
1356
|
- **Issue #81 fix — taskWorkflowStates persistence**: Session snapshots now correctly serialize and restore the per-task state machine. Invalid state values are filtered to `idle` on deserialization. `reconcileTaskStatesFromPlan` seeds task states from `plan.json` on snapshot load (completed → `tests_run`, in-progress → `coder_delegated`).
|
|
1297
1357
|
|
|
@@ -1423,7 +1483,7 @@ bun test
|
|
|
1423
1483
|
|
|
1424
1484
|
## Supported Languages
|
|
1425
1485
|
|
|
1426
|
-
OpenCode Swarm v6.
|
|
1486
|
+
OpenCode Swarm v6.46+ ships with language profiles for 12 languages across three quality tiers. All tools use graceful degradation — if a binary is not on PATH, the tool skips with a soft warning rather than a hard failure.
|
|
1427
1487
|
|
|
1428
1488
|
| Language | Tier | Syntax | Build | Test | Lint | Audit | SAST |
|
|
1429
1489
|
|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
|
|
@@ -1438,6 +1498,9 @@ OpenCode Swarm v6.16+ ships with language profiles for 11 languages across three
|
|
|
1438
1498
|
| Swift | 2 | ✅ | ✅ swift build | ✅ swift test | ✅ swiftlint | — | 🔶 Semgrep exp. |
|
|
1439
1499
|
| Dart / Flutter | 3 | ✅ | ✅ dart pub | ✅ dart test | ✅ dart analyze | ✅ dart pub outdated | — |
|
|
1440
1500
|
| Ruby | 3 | ✅ | — | ✅ RSpec / minitest | ✅ RuboCop | ✅ bundle-audit | 🔶 Semgrep exp. |
|
|
1501
|
+
| PHP / Laravel | 3 | ✅ | ✅ Composer install | ✅ PHPUnit / Pest / artisan test | ✅ Pint / PHP-CS-Fixer | ✅ composer audit | ✅ 10+ native rules |
|
|
1502
|
+
|
|
1503
|
+
> **PHP + Laravel baseline**: PHP v6.49+ ships with deterministic Laravel project detection (multi-signal: `artisan` file, `laravel/framework` dependency, `config/app.php`). When detected, commands are automatically overridden to `php artisan test`, Pint formatting, and PHPStan static analysis. Laravel-specific SAST rules cover SQL injection via raw queries, mass-assignment vulnerabilities, and destructive migrations without rollback. `.blade.php` files are included in all scanning pipelines.
|
|
1441
1504
|
|
|
1442
1505
|
**Tier definitions:**
|
|
1443
1506
|
- **Tier 1** — Full pipeline: all tools integrated and tested end-to-end.
|
|
@@ -1450,9 +1513,11 @@ OpenCode Swarm v6.16+ ships with language profiles for 11 languages across three
|
|
|
1450
1513
|
|
|
1451
1514
|
## Curator
|
|
1452
1515
|
|
|
1453
|
-
The Curator is
|
|
1516
|
+
The Curator is a background analysis system that runs after each phase. It is **enabled by default** as of v6.42 (`curator.enabled = true`) and never blocks execution — all Curator operations are wrapped in try/catch. It falls back gracefully to data-only mode when no SDK client is available.
|
|
1517
|
+
|
|
1518
|
+
Since v6.42, the Curator performs real LLM analysis by delegating to the Explorer agent with curator-specific prompts. Before v6.42, the LLM delegation was scaffolded but never wired.
|
|
1454
1519
|
|
|
1455
|
-
To
|
|
1520
|
+
To disable, set `"curator": { "enabled": false }` in your config. When enabled, it writes `.swarm/curator-summary.json`, `.swarm/curator-briefing.md`, and `.swarm/drift-report-phase-N.json` files.
|
|
1456
1521
|
|
|
1457
1522
|
### What the Curator Does
|
|
1458
1523
|
|
|
@@ -1470,7 +1535,7 @@ Add a `curator` block to `.opencode/opencode-swarm.json`:
|
|
|
1470
1535
|
```json
|
|
1471
1536
|
{
|
|
1472
1537
|
"curator": {
|
|
1473
|
-
"enabled":
|
|
1538
|
+
"enabled": true,
|
|
1474
1539
|
"init_enabled": true,
|
|
1475
1540
|
"phase_enabled": true,
|
|
1476
1541
|
"max_summary_tokens": 2000,
|
|
@@ -1484,7 +1549,7 @@ Add a `curator` block to `.opencode/opencode-swarm.json`:
|
|
|
1484
1549
|
|
|
1485
1550
|
| Field | Default | Description |
|
|
1486
1551
|
|-------|---------|-------------|
|
|
1487
|
-
| `enabled` | `
|
|
1552
|
+
| `enabled` | `true` | Master switch. Set to `false` to disable the Curator pipeline. |
|
|
1488
1553
|
| `init_enabled` | `true` | Initialize curator summary on first phase (requires `enabled: true`). |
|
|
1489
1554
|
| `phase_enabled` | `true` | Run phase analysis and knowledge updates after each phase. |
|
|
1490
1555
|
| `max_summary_tokens` | `2000` | Maximum token budget for curator knowledge summaries. |
|
|
@@ -1533,6 +1598,7 @@ Run `/swarm reset --confirm`.
|
|
|
1533
1598
|
- [Getting Started](docs/getting-started.md)
|
|
1534
1599
|
- [Architecture Deep Dive](docs/architecture.md)
|
|
1535
1600
|
- [Design Rationale](docs/design-rationale.md)
|
|
1601
|
+
- [Plan Durability & Ledger](docs/plan-durability.md)
|
|
1536
1602
|
- [Installation Guide](docs/installation.md)
|
|
1537
1603
|
- [Linux + Docker Desktop Install Guide](docs/installation-linux-docker.md)
|
|
1538
1604
|
- [LLM Operator Installation Guide](docs/installation-llm-operator.md)
|
package/dist/cli/index.js
CHANGED
|
@@ -35627,37 +35627,80 @@ LANGUAGE_REGISTRY.register({
|
|
|
35627
35627
|
id: "php",
|
|
35628
35628
|
displayName: "PHP",
|
|
35629
35629
|
tier: 3,
|
|
35630
|
-
extensions: [".php", ".phtml"],
|
|
35630
|
+
extensions: [".php", ".phtml", ".blade.php"],
|
|
35631
35631
|
treeSitter: { grammarId: "php", wasmFile: "tree-sitter-php.wasm" },
|
|
35632
35632
|
build: {
|
|
35633
35633
|
detectFiles: ["composer.json"],
|
|
35634
|
-
commands: [
|
|
35634
|
+
commands: [
|
|
35635
|
+
{
|
|
35636
|
+
name: "Composer Install",
|
|
35637
|
+
cmd: "composer install --no-interaction --prefer-dist",
|
|
35638
|
+
detectFile: "composer.json",
|
|
35639
|
+
priority: 1
|
|
35640
|
+
}
|
|
35641
|
+
]
|
|
35635
35642
|
},
|
|
35636
35643
|
test: {
|
|
35637
|
-
detectFiles: ["phpunit.xml", "phpunit.xml.dist"],
|
|
35644
|
+
detectFiles: ["Pest.php", "phpunit.xml", "phpunit.xml.dist"],
|
|
35638
35645
|
frameworks: [
|
|
35646
|
+
{
|
|
35647
|
+
name: "Pest",
|
|
35648
|
+
detect: "Pest.php",
|
|
35649
|
+
cmd: "vendor/bin/pest",
|
|
35650
|
+
priority: 1
|
|
35651
|
+
},
|
|
35639
35652
|
{
|
|
35640
35653
|
name: "PHPUnit",
|
|
35641
35654
|
detect: "phpunit.xml",
|
|
35642
35655
|
cmd: "vendor/bin/phpunit",
|
|
35643
|
-
priority:
|
|
35656
|
+
priority: 3
|
|
35657
|
+
},
|
|
35658
|
+
{
|
|
35659
|
+
name: "PHPUnit",
|
|
35660
|
+
detect: "phpunit.xml.dist",
|
|
35661
|
+
cmd: "vendor/bin/phpunit",
|
|
35662
|
+
priority: 4
|
|
35644
35663
|
}
|
|
35645
35664
|
]
|
|
35646
35665
|
},
|
|
35647
35666
|
lint: {
|
|
35648
|
-
detectFiles: [
|
|
35667
|
+
detectFiles: [
|
|
35668
|
+
"phpstan.neon",
|
|
35669
|
+
"phpstan.neon.dist",
|
|
35670
|
+
"pint.json",
|
|
35671
|
+
".php-cs-fixer.php",
|
|
35672
|
+
"phpcs.xml"
|
|
35673
|
+
],
|
|
35649
35674
|
linters: [
|
|
35675
|
+
{
|
|
35676
|
+
name: "PHPStan",
|
|
35677
|
+
detect: "phpstan.neon",
|
|
35678
|
+
cmd: "vendor/bin/phpstan analyse",
|
|
35679
|
+
priority: 1
|
|
35680
|
+
},
|
|
35681
|
+
{
|
|
35682
|
+
name: "PHPStan",
|
|
35683
|
+
detect: "phpstan.neon.dist",
|
|
35684
|
+
cmd: "vendor/bin/phpstan analyse",
|
|
35685
|
+
priority: 2
|
|
35686
|
+
},
|
|
35687
|
+
{
|
|
35688
|
+
name: "Pint",
|
|
35689
|
+
detect: "pint.json",
|
|
35690
|
+
cmd: "vendor/bin/pint --test",
|
|
35691
|
+
priority: 3
|
|
35692
|
+
},
|
|
35650
35693
|
{
|
|
35651
35694
|
name: "PHP-CS-Fixer",
|
|
35652
35695
|
detect: ".php-cs-fixer.php",
|
|
35653
35696
|
cmd: "vendor/bin/php-cs-fixer fix --dry-run --diff",
|
|
35654
|
-
priority:
|
|
35697
|
+
priority: 4
|
|
35655
35698
|
}
|
|
35656
35699
|
]
|
|
35657
35700
|
},
|
|
35658
35701
|
audit: {
|
|
35659
35702
|
detectFiles: ["composer.lock"],
|
|
35660
|
-
command: "composer audit --format=json",
|
|
35703
|
+
command: "composer audit --locked --format=json",
|
|
35661
35704
|
outputFormat: "json"
|
|
35662
35705
|
},
|
|
35663
35706
|
sast: { nativeRuleSet: "php", semgrepSupport: "ga" },
|
|
@@ -35666,13 +35709,25 @@ LANGUAGE_REGISTRY.register({
|
|
|
35666
35709
|
"Follow PSR-12 coding standards",
|
|
35667
35710
|
"Use strict types declaration: declare(strict_types=1)",
|
|
35668
35711
|
"Prefer type hints and return type declarations on all functions",
|
|
35669
|
-
"Use dependency injection over static methods and singletons"
|
|
35712
|
+
"Use dependency injection over static methods and singletons",
|
|
35713
|
+
"Prefer named constructors and value objects over primitive obsession"
|
|
35670
35714
|
],
|
|
35671
35715
|
reviewerChecklist: [
|
|
35672
35716
|
"Verify no user input reaches SQL queries without parameterised binding",
|
|
35673
35717
|
"Check for XSS \u2014 all output must be escaped with htmlspecialchars()",
|
|
35674
35718
|
"Confirm no eval(), exec(), or shell_exec() with user-controlled input",
|
|
35675
|
-
"Validate proper error handling \u2014 no bare catch blocks that swallow errors"
|
|
35719
|
+
"Validate proper error handling \u2014 no bare catch blocks that swallow errors",
|
|
35720
|
+
"Challenge any PHP/Laravel documentation or README claim that exceeds what is implemented and CI-verified in v6.49.0 (composer build, PHPUnit/Pest/artisan test, Pint/PHP-CS-Fixer lint, PHPStan static analysis, composer audit, Laravel detection, Blade scanning, 3 Laravel SAST rules). If a PR adds docs claiming broader support, verify it is backed by tests."
|
|
35721
|
+
],
|
|
35722
|
+
testConstraints: [
|
|
35723
|
+
"Prefer feature tests for HTTP, middleware, and authentication flows \u2014 use Laravel RefreshDatabase or DatabaseTransactions traits",
|
|
35724
|
+
"Use unit tests for isolated business logic classes that do not require the full Laravel application container",
|
|
35725
|
+
"Pest and PHPUnit coexist in many Laravel repos \u2014 php artisan test runs both; do not assume PHPUnit-only",
|
|
35726
|
+
"Use .env.testing for test environment configuration; run php artisan config:clear when environment changes affect tests",
|
|
35727
|
+
"For database tests, prefer RefreshDatabase over manual setUp/tearDown to avoid state leakage between tests",
|
|
35728
|
+
"Run php artisan config:clear and php artisan cache:clear before running tests if environment variables changed",
|
|
35729
|
+
"Use separate .env.testing file for test-specific configuration; never rely on .env for CI test runs",
|
|
35730
|
+
"For parallel testing with php artisan test --parallel, ensure database connections use separate databases per worker (APP_ENV=testing + test database suffix pattern)"
|
|
35676
35731
|
]
|
|
35677
35732
|
}
|
|
35678
35733
|
});
|
|
@@ -35813,6 +35868,17 @@ var ECOSYSTEMS = [
|
|
|
35813
35868
|
{ command: "make", priority: 1 },
|
|
35814
35869
|
{ command: "cmake -B build && cmake --build build", priority: 2 }
|
|
35815
35870
|
]
|
|
35871
|
+
},
|
|
35872
|
+
{
|
|
35873
|
+
ecosystem: "php-composer",
|
|
35874
|
+
buildFiles: ["composer.json"],
|
|
35875
|
+
toolchainCommands: ["composer"],
|
|
35876
|
+
commands: [
|
|
35877
|
+
{
|
|
35878
|
+
command: "composer install --no-interaction --prefer-dist",
|
|
35879
|
+
priority: 1
|
|
35880
|
+
}
|
|
35881
|
+
]
|
|
35816
35882
|
}
|
|
35817
35883
|
];
|
|
35818
35884
|
var PROFILE_TO_ECOSYSTEM_NAMES = {
|
|
@@ -35826,7 +35892,8 @@ var PROFILE_TO_ECOSYSTEM_NAMES = {
|
|
|
35826
35892
|
cpp: ["cpp"],
|
|
35827
35893
|
swift: ["swift"],
|
|
35828
35894
|
dart: ["dart"],
|
|
35829
|
-
ruby: []
|
|
35895
|
+
ruby: [],
|
|
35896
|
+
php: ["php-composer"]
|
|
35830
35897
|
};
|
|
35831
35898
|
var toolchainCache = new Map;
|
|
35832
35899
|
function isCommandAvailable(command) {
|
package/dist/index.js
CHANGED
|
@@ -34433,37 +34433,80 @@ var init_profiles = __esm(() => {
|
|
|
34433
34433
|
id: "php",
|
|
34434
34434
|
displayName: "PHP",
|
|
34435
34435
|
tier: 3,
|
|
34436
|
-
extensions: [".php", ".phtml"],
|
|
34436
|
+
extensions: [".php", ".phtml", ".blade.php"],
|
|
34437
34437
|
treeSitter: { grammarId: "php", wasmFile: "tree-sitter-php.wasm" },
|
|
34438
34438
|
build: {
|
|
34439
34439
|
detectFiles: ["composer.json"],
|
|
34440
|
-
commands: [
|
|
34440
|
+
commands: [
|
|
34441
|
+
{
|
|
34442
|
+
name: "Composer Install",
|
|
34443
|
+
cmd: "composer install --no-interaction --prefer-dist",
|
|
34444
|
+
detectFile: "composer.json",
|
|
34445
|
+
priority: 1
|
|
34446
|
+
}
|
|
34447
|
+
]
|
|
34441
34448
|
},
|
|
34442
34449
|
test: {
|
|
34443
|
-
detectFiles: ["phpunit.xml", "phpunit.xml.dist"],
|
|
34450
|
+
detectFiles: ["Pest.php", "phpunit.xml", "phpunit.xml.dist"],
|
|
34444
34451
|
frameworks: [
|
|
34452
|
+
{
|
|
34453
|
+
name: "Pest",
|
|
34454
|
+
detect: "Pest.php",
|
|
34455
|
+
cmd: "vendor/bin/pest",
|
|
34456
|
+
priority: 1
|
|
34457
|
+
},
|
|
34445
34458
|
{
|
|
34446
34459
|
name: "PHPUnit",
|
|
34447
34460
|
detect: "phpunit.xml",
|
|
34448
34461
|
cmd: "vendor/bin/phpunit",
|
|
34449
|
-
priority:
|
|
34462
|
+
priority: 3
|
|
34463
|
+
},
|
|
34464
|
+
{
|
|
34465
|
+
name: "PHPUnit",
|
|
34466
|
+
detect: "phpunit.xml.dist",
|
|
34467
|
+
cmd: "vendor/bin/phpunit",
|
|
34468
|
+
priority: 4
|
|
34450
34469
|
}
|
|
34451
34470
|
]
|
|
34452
34471
|
},
|
|
34453
34472
|
lint: {
|
|
34454
|
-
detectFiles: [
|
|
34473
|
+
detectFiles: [
|
|
34474
|
+
"phpstan.neon",
|
|
34475
|
+
"phpstan.neon.dist",
|
|
34476
|
+
"pint.json",
|
|
34477
|
+
".php-cs-fixer.php",
|
|
34478
|
+
"phpcs.xml"
|
|
34479
|
+
],
|
|
34455
34480
|
linters: [
|
|
34481
|
+
{
|
|
34482
|
+
name: "PHPStan",
|
|
34483
|
+
detect: "phpstan.neon",
|
|
34484
|
+
cmd: "vendor/bin/phpstan analyse",
|
|
34485
|
+
priority: 1
|
|
34486
|
+
},
|
|
34487
|
+
{
|
|
34488
|
+
name: "PHPStan",
|
|
34489
|
+
detect: "phpstan.neon.dist",
|
|
34490
|
+
cmd: "vendor/bin/phpstan analyse",
|
|
34491
|
+
priority: 2
|
|
34492
|
+
},
|
|
34493
|
+
{
|
|
34494
|
+
name: "Pint",
|
|
34495
|
+
detect: "pint.json",
|
|
34496
|
+
cmd: "vendor/bin/pint --test",
|
|
34497
|
+
priority: 3
|
|
34498
|
+
},
|
|
34456
34499
|
{
|
|
34457
34500
|
name: "PHP-CS-Fixer",
|
|
34458
34501
|
detect: ".php-cs-fixer.php",
|
|
34459
34502
|
cmd: "vendor/bin/php-cs-fixer fix --dry-run --diff",
|
|
34460
|
-
priority:
|
|
34503
|
+
priority: 4
|
|
34461
34504
|
}
|
|
34462
34505
|
]
|
|
34463
34506
|
},
|
|
34464
34507
|
audit: {
|
|
34465
34508
|
detectFiles: ["composer.lock"],
|
|
34466
|
-
command: "composer audit --format=json",
|
|
34509
|
+
command: "composer audit --locked --format=json",
|
|
34467
34510
|
outputFormat: "json"
|
|
34468
34511
|
},
|
|
34469
34512
|
sast: { nativeRuleSet: "php", semgrepSupport: "ga" },
|
|
@@ -34472,13 +34515,25 @@ var init_profiles = __esm(() => {
|
|
|
34472
34515
|
"Follow PSR-12 coding standards",
|
|
34473
34516
|
"Use strict types declaration: declare(strict_types=1)",
|
|
34474
34517
|
"Prefer type hints and return type declarations on all functions",
|
|
34475
|
-
"Use dependency injection over static methods and singletons"
|
|
34518
|
+
"Use dependency injection over static methods and singletons",
|
|
34519
|
+
"Prefer named constructors and value objects over primitive obsession"
|
|
34476
34520
|
],
|
|
34477
34521
|
reviewerChecklist: [
|
|
34478
34522
|
"Verify no user input reaches SQL queries without parameterised binding",
|
|
34479
34523
|
"Check for XSS \u2014 all output must be escaped with htmlspecialchars()",
|
|
34480
34524
|
"Confirm no eval(), exec(), or shell_exec() with user-controlled input",
|
|
34481
|
-
"Validate proper error handling \u2014 no bare catch blocks that swallow errors"
|
|
34525
|
+
"Validate proper error handling \u2014 no bare catch blocks that swallow errors",
|
|
34526
|
+
"Challenge any PHP/Laravel documentation or README claim that exceeds what is implemented and CI-verified in v6.49.0 (composer build, PHPUnit/Pest/artisan test, Pint/PHP-CS-Fixer lint, PHPStan static analysis, composer audit, Laravel detection, Blade scanning, 3 Laravel SAST rules). If a PR adds docs claiming broader support, verify it is backed by tests."
|
|
34527
|
+
],
|
|
34528
|
+
testConstraints: [
|
|
34529
|
+
"Prefer feature tests for HTTP, middleware, and authentication flows \u2014 use Laravel RefreshDatabase or DatabaseTransactions traits",
|
|
34530
|
+
"Use unit tests for isolated business logic classes that do not require the full Laravel application container",
|
|
34531
|
+
"Pest and PHPUnit coexist in many Laravel repos \u2014 php artisan test runs both; do not assume PHPUnit-only",
|
|
34532
|
+
"Use .env.testing for test environment configuration; run php artisan config:clear when environment changes affect tests",
|
|
34533
|
+
"For database tests, prefer RefreshDatabase over manual setUp/tearDown to avoid state leakage between tests",
|
|
34534
|
+
"Run php artisan config:clear and php artisan cache:clear before running tests if environment variables changed",
|
|
34535
|
+
"Use separate .env.testing file for test-specific configuration; never rely on .env for CI test runs",
|
|
34536
|
+
"For parallel testing with php artisan test --parallel, ensure database connections use separate databases per worker (APP_ENV=testing + test database suffix pattern)"
|
|
34482
34537
|
]
|
|
34483
34538
|
}
|
|
34484
34539
|
});
|
|
@@ -34846,6 +34901,17 @@ var init_discovery = __esm(() => {
|
|
|
34846
34901
|
{ command: "make", priority: 1 },
|
|
34847
34902
|
{ command: "cmake -B build && cmake --build build", priority: 2 }
|
|
34848
34903
|
]
|
|
34904
|
+
},
|
|
34905
|
+
{
|
|
34906
|
+
ecosystem: "php-composer",
|
|
34907
|
+
buildFiles: ["composer.json"],
|
|
34908
|
+
toolchainCommands: ["composer"],
|
|
34909
|
+
commands: [
|
|
34910
|
+
{
|
|
34911
|
+
command: "composer install --no-interaction --prefer-dist",
|
|
34912
|
+
priority: 1
|
|
34913
|
+
}
|
|
34914
|
+
]
|
|
34849
34915
|
}
|
|
34850
34916
|
];
|
|
34851
34917
|
PROFILE_TO_ECOSYSTEM_NAMES = {
|
|
@@ -34859,7 +34925,8 @@ var init_discovery = __esm(() => {
|
|
|
34859
34925
|
cpp: ["cpp"],
|
|
34860
34926
|
swift: ["swift"],
|
|
34861
34927
|
dart: ["dart"],
|
|
34862
|
-
ruby: []
|
|
34928
|
+
ruby: [],
|
|
34929
|
+
php: ["php-composer"]
|
|
34863
34930
|
};
|
|
34864
34931
|
toolchainCache = new Map;
|
|
34865
34932
|
build_discovery = tool({
|
|
@@ -56789,6 +56856,36 @@ function buildLanguageReviewerChecklist(currentTaskText) {
|
|
|
56789
56856
|
${allItems.map((i2) => `- [ ] ${i2}`).join(`
|
|
56790
56857
|
`)}`;
|
|
56791
56858
|
}
|
|
56859
|
+
function buildLanguageTestConstraints(currentTaskText) {
|
|
56860
|
+
if (!currentTaskText)
|
|
56861
|
+
return null;
|
|
56862
|
+
const filePaths = currentTaskText.match(/\bsrc\/\S+\.[a-zA-Z0-9]+\b/g) ?? [];
|
|
56863
|
+
if (filePaths.length === 0)
|
|
56864
|
+
return null;
|
|
56865
|
+
const allConstraints = [];
|
|
56866
|
+
const seenConstraints = new Set;
|
|
56867
|
+
let languageLabel = "";
|
|
56868
|
+
for (const filePath of filePaths) {
|
|
56869
|
+
const profile = getProfileForFile(filePath);
|
|
56870
|
+
if (!profile)
|
|
56871
|
+
continue;
|
|
56872
|
+
if (!languageLabel) {
|
|
56873
|
+
languageLabel = profile.displayName;
|
|
56874
|
+
}
|
|
56875
|
+
const testConstraints = profile.prompts.testConstraints ?? [];
|
|
56876
|
+
for (const constraint of testConstraints) {
|
|
56877
|
+
if (!seenConstraints.has(constraint) && allConstraints.length < 10) {
|
|
56878
|
+
seenConstraints.add(constraint);
|
|
56879
|
+
allConstraints.push(constraint);
|
|
56880
|
+
}
|
|
56881
|
+
}
|
|
56882
|
+
}
|
|
56883
|
+
if (allConstraints.length === 0)
|
|
56884
|
+
return null;
|
|
56885
|
+
return `[LANGUAGE-SPECIFIC TEST CONSTRAINTS \u2014 ${languageLabel}]
|
|
56886
|
+
${allConstraints.map((c) => `- ${c}`).join(`
|
|
56887
|
+
`)}`;
|
|
56888
|
+
}
|
|
56792
56889
|
function createSystemEnhancerHook(config3, directory) {
|
|
56793
56890
|
const enabled = config3.hooks?.system_enhancer !== false;
|
|
56794
56891
|
if (!enabled) {
|
|
@@ -57015,6 +57112,13 @@ ${handoffBlock}`);
|
|
|
57015
57112
|
tryInject(revChecklist_a);
|
|
57016
57113
|
}
|
|
57017
57114
|
}
|
|
57115
|
+
if (baseRole === "test_engineer") {
|
|
57116
|
+
const taskText_te_a = plan2 && plan2.migration_status !== "migration_failed" ? extractCurrentTaskFromPlan(plan2) : null;
|
|
57117
|
+
const testConstraints_a = buildLanguageTestConstraints(taskText_te_a);
|
|
57118
|
+
if (testConstraints_a) {
|
|
57119
|
+
tryInject(testConstraints_a);
|
|
57120
|
+
}
|
|
57121
|
+
}
|
|
57018
57122
|
const sessionId_retro = _input.sessionID;
|
|
57019
57123
|
const activeAgent_retro = swarmState.activeAgent.get(sessionId_retro ?? "");
|
|
57020
57124
|
const isArchitect2 = !activeAgent_retro || stripKnownSwarmPrefix(activeAgent_retro) === "architect";
|
|
@@ -57448,6 +57552,21 @@ ${handoffBlock}`;
|
|
|
57448
57552
|
});
|
|
57449
57553
|
}
|
|
57450
57554
|
}
|
|
57555
|
+
const isTestEngineer_b = activeAgent_coder_b && stripKnownSwarmPrefix(activeAgent_coder_b) === "test_engineer";
|
|
57556
|
+
if (isTestEngineer_b) {
|
|
57557
|
+
const taskText_te_b = plan && plan.migration_status !== "migration_failed" ? extractCurrentTaskFromPlan(plan) : null;
|
|
57558
|
+
const testConstraints_b = buildLanguageTestConstraints(taskText_te_b);
|
|
57559
|
+
if (testConstraints_b) {
|
|
57560
|
+
candidates.push({
|
|
57561
|
+
id: `candidate-${idCounter++}`,
|
|
57562
|
+
kind: "agent_context",
|
|
57563
|
+
text: testConstraints_b,
|
|
57564
|
+
tokens: estimateTokens(testConstraints_b),
|
|
57565
|
+
priority: 2,
|
|
57566
|
+
metadata: { contentType: "prose" }
|
|
57567
|
+
});
|
|
57568
|
+
}
|
|
57569
|
+
}
|
|
57451
57570
|
const automationCapabilities_b = config3.automation?.capabilities;
|
|
57452
57571
|
if (automationCapabilities_b?.decision_drift_detection === true && sessionId_retro_b) {
|
|
57453
57572
|
const activeAgentForDrift_b = swarmState.activeAgent.get(sessionId_retro_b ?? "");
|
|
@@ -64028,7 +64147,17 @@ import * as path63 from "path";
|
|
|
64028
64147
|
var MAX_OUTPUT_BYTES5 = 52428800;
|
|
64029
64148
|
var AUDIT_TIMEOUT_MS = 120000;
|
|
64030
64149
|
function isValidEcosystem(value) {
|
|
64031
|
-
return typeof value === "string" && [
|
|
64150
|
+
return typeof value === "string" && [
|
|
64151
|
+
"auto",
|
|
64152
|
+
"npm",
|
|
64153
|
+
"pip",
|
|
64154
|
+
"cargo",
|
|
64155
|
+
"go",
|
|
64156
|
+
"dotnet",
|
|
64157
|
+
"ruby",
|
|
64158
|
+
"dart",
|
|
64159
|
+
"composer"
|
|
64160
|
+
].includes(value);
|
|
64032
64161
|
}
|
|
64033
64162
|
function validateArgs3(args2) {
|
|
64034
64163
|
if (typeof args2 !== "object" || args2 === null)
|
|
@@ -64066,6 +64195,9 @@ function detectEcosystems(directory) {
|
|
|
64066
64195
|
if (fs50.existsSync(path63.join(cwd, "pubspec.yaml"))) {
|
|
64067
64196
|
ecosystems.push("dart");
|
|
64068
64197
|
}
|
|
64198
|
+
if (fs50.existsSync(path63.join(cwd, "composer.lock"))) {
|
|
64199
|
+
ecosystems.push("composer");
|
|
64200
|
+
}
|
|
64069
64201
|
return ecosystems;
|
|
64070
64202
|
}
|
|
64071
64203
|
async function runNpmAudit(directory) {
|
|
@@ -64957,6 +65089,147 @@ async function runDartAudit(directory) {
|
|
|
64957
65089
|
};
|
|
64958
65090
|
}
|
|
64959
65091
|
}
|
|
65092
|
+
async function runComposerAudit(directory) {
|
|
65093
|
+
const command = ["composer", "audit", "--locked", "--format=json"];
|
|
65094
|
+
if (!isCommandAvailable("composer")) {
|
|
65095
|
+
warn("[pkg-audit] composer not found, skipping Composer audit");
|
|
65096
|
+
return {
|
|
65097
|
+
ecosystem: "composer",
|
|
65098
|
+
command,
|
|
65099
|
+
findings: [],
|
|
65100
|
+
criticalCount: 0,
|
|
65101
|
+
highCount: 0,
|
|
65102
|
+
totalCount: 0,
|
|
65103
|
+
clean: true,
|
|
65104
|
+
note: "composer not installed or not on PATH"
|
|
65105
|
+
};
|
|
65106
|
+
}
|
|
65107
|
+
try {
|
|
65108
|
+
const proc = Bun.spawn(command, {
|
|
65109
|
+
stdout: "pipe",
|
|
65110
|
+
stderr: "pipe",
|
|
65111
|
+
cwd: directory
|
|
65112
|
+
});
|
|
65113
|
+
const timeoutPromise = new Promise((resolve23) => setTimeout(() => resolve23("timeout"), AUDIT_TIMEOUT_MS));
|
|
65114
|
+
const result = await Promise.race([
|
|
65115
|
+
Promise.all([
|
|
65116
|
+
new Response(proc.stdout).text(),
|
|
65117
|
+
new Response(proc.stderr).text()
|
|
65118
|
+
]).then(([stdout2, stderr]) => ({ stdout: stdout2, stderr })),
|
|
65119
|
+
timeoutPromise
|
|
65120
|
+
]);
|
|
65121
|
+
if (result === "timeout") {
|
|
65122
|
+
proc.kill();
|
|
65123
|
+
return {
|
|
65124
|
+
ecosystem: "composer",
|
|
65125
|
+
command,
|
|
65126
|
+
findings: [],
|
|
65127
|
+
criticalCount: 0,
|
|
65128
|
+
highCount: 0,
|
|
65129
|
+
totalCount: 0,
|
|
65130
|
+
clean: true,
|
|
65131
|
+
note: `composer audit timed out after ${AUDIT_TIMEOUT_MS / 1000}s`
|
|
65132
|
+
};
|
|
65133
|
+
}
|
|
65134
|
+
let { stdout } = result;
|
|
65135
|
+
if (stdout.length > MAX_OUTPUT_BYTES5) {
|
|
65136
|
+
stdout = stdout.slice(0, MAX_OUTPUT_BYTES5);
|
|
65137
|
+
}
|
|
65138
|
+
const exitCode = await proc.exited;
|
|
65139
|
+
const hasVulnerabilities = (exitCode & 1) !== 0;
|
|
65140
|
+
const hasAbandoned = (exitCode & 2) !== 0;
|
|
65141
|
+
if (exitCode === 0) {
|
|
65142
|
+
return {
|
|
65143
|
+
ecosystem: "composer",
|
|
65144
|
+
command,
|
|
65145
|
+
findings: [],
|
|
65146
|
+
criticalCount: 0,
|
|
65147
|
+
highCount: 0,
|
|
65148
|
+
totalCount: 0,
|
|
65149
|
+
clean: true
|
|
65150
|
+
};
|
|
65151
|
+
}
|
|
65152
|
+
if (hasVulnerabilities && !stdout.trim()) {
|
|
65153
|
+
return {
|
|
65154
|
+
ecosystem: "composer",
|
|
65155
|
+
command,
|
|
65156
|
+
findings: [],
|
|
65157
|
+
criticalCount: 0,
|
|
65158
|
+
highCount: 0,
|
|
65159
|
+
totalCount: 0,
|
|
65160
|
+
clean: false,
|
|
65161
|
+
note: `composer audit returned exit code ${exitCode} indicating vulnerabilities but produced no output`
|
|
65162
|
+
};
|
|
65163
|
+
}
|
|
65164
|
+
let parsed;
|
|
65165
|
+
try {
|
|
65166
|
+
parsed = JSON.parse(stdout || "{}");
|
|
65167
|
+
} catch {
|
|
65168
|
+
return {
|
|
65169
|
+
ecosystem: "composer",
|
|
65170
|
+
command,
|
|
65171
|
+
findings: [],
|
|
65172
|
+
criticalCount: 0,
|
|
65173
|
+
highCount: 0,
|
|
65174
|
+
totalCount: 0,
|
|
65175
|
+
clean: !hasVulnerabilities,
|
|
65176
|
+
note: `composer audit returned exit code ${exitCode} but output was not valid JSON`
|
|
65177
|
+
};
|
|
65178
|
+
}
|
|
65179
|
+
if (!hasVulnerabilities && hasAbandoned) {
|
|
65180
|
+
const abandonedList = Object.keys(parsed.abandoned ?? {});
|
|
65181
|
+
return {
|
|
65182
|
+
ecosystem: "composer",
|
|
65183
|
+
command,
|
|
65184
|
+
findings: [],
|
|
65185
|
+
criticalCount: 0,
|
|
65186
|
+
highCount: 0,
|
|
65187
|
+
totalCount: 0,
|
|
65188
|
+
clean: true,
|
|
65189
|
+
note: abandonedList.length > 0 ? `Abandoned packages detected: ${abandonedList.join(", ")}` : "composer audit exit 2 (abandoned packages)"
|
|
65190
|
+
};
|
|
65191
|
+
}
|
|
65192
|
+
const findings = [];
|
|
65193
|
+
for (const advisories of Object.values(parsed.advisories ?? {})) {
|
|
65194
|
+
for (const advisory of advisories) {
|
|
65195
|
+
const hasCve = Boolean(advisory.cve?.trim());
|
|
65196
|
+
findings.push({
|
|
65197
|
+
package: advisory.packageName,
|
|
65198
|
+
installedVersion: "see composer.lock",
|
|
65199
|
+
patchedVersion: null,
|
|
65200
|
+
severity: hasCve ? "high" : "moderate",
|
|
65201
|
+
title: advisory.title,
|
|
65202
|
+
cve: advisory.cve || null,
|
|
65203
|
+
url: advisory.link || null
|
|
65204
|
+
});
|
|
65205
|
+
}
|
|
65206
|
+
}
|
|
65207
|
+
const criticalCount = findings.filter((f) => f.severity === "critical").length;
|
|
65208
|
+
const highCount = findings.filter((f) => f.severity === "high").length;
|
|
65209
|
+
const abandonedNote = hasAbandoned && Object.keys(parsed.abandoned ?? {}).length > 0 ? ` Also abandoned: ${Object.keys(parsed.abandoned).join(", ")}` : "";
|
|
65210
|
+
return {
|
|
65211
|
+
ecosystem: "composer",
|
|
65212
|
+
command,
|
|
65213
|
+
findings,
|
|
65214
|
+
criticalCount,
|
|
65215
|
+
highCount,
|
|
65216
|
+
totalCount: findings.length,
|
|
65217
|
+
clean: false,
|
|
65218
|
+
...abandonedNote ? { note: abandonedNote.trim() } : {}
|
|
65219
|
+
};
|
|
65220
|
+
} catch (error93) {
|
|
65221
|
+
return {
|
|
65222
|
+
ecosystem: "composer",
|
|
65223
|
+
command,
|
|
65224
|
+
findings: [],
|
|
65225
|
+
criticalCount: 0,
|
|
65226
|
+
highCount: 0,
|
|
65227
|
+
totalCount: 0,
|
|
65228
|
+
clean: true,
|
|
65229
|
+
note: `composer audit error: ${error93 instanceof Error ? error93.message : String(error93)}`
|
|
65230
|
+
};
|
|
65231
|
+
}
|
|
65232
|
+
}
|
|
64960
65233
|
async function runAutoAudit(directory) {
|
|
64961
65234
|
const ecosystems = detectEcosystems(directory);
|
|
64962
65235
|
if (ecosystems.length === 0) {
|
|
@@ -64993,6 +65266,9 @@ async function runAutoAudit(directory) {
|
|
|
64993
65266
|
case "dart":
|
|
64994
65267
|
results.push(await runDartAudit(directory));
|
|
64995
65268
|
break;
|
|
65269
|
+
case "composer":
|
|
65270
|
+
results.push(await runComposerAudit(directory));
|
|
65271
|
+
break;
|
|
64996
65272
|
}
|
|
64997
65273
|
}
|
|
64998
65274
|
const allFindings = [];
|
|
@@ -65015,12 +65291,22 @@ async function runAutoAudit(directory) {
|
|
|
65015
65291
|
var pkg_audit = createSwarmTool({
|
|
65016
65292
|
description: 'Run package manager security audit (npm, pip, cargo, go, dotnet, ruby, dart) and return structured CVE data. Use ecosystem to specify which package manager, or "auto" to detect from project files.',
|
|
65017
65293
|
args: {
|
|
65018
|
-
ecosystem: tool.schema.enum([
|
|
65294
|
+
ecosystem: tool.schema.enum([
|
|
65295
|
+
"auto",
|
|
65296
|
+
"npm",
|
|
65297
|
+
"pip",
|
|
65298
|
+
"cargo",
|
|
65299
|
+
"go",
|
|
65300
|
+
"dotnet",
|
|
65301
|
+
"ruby",
|
|
65302
|
+
"dart",
|
|
65303
|
+
"composer"
|
|
65304
|
+
]).default("auto").describe('Package ecosystem to audit: "auto" (detect from project files), "npm", "pip", "cargo", "go" (govulncheck), "dotnet" (dotnet list package), "ruby" (bundle-audit), "dart" (dart pub outdated), or "composer" (Composer/PHP)')
|
|
65019
65305
|
},
|
|
65020
65306
|
async execute(args2, directory) {
|
|
65021
65307
|
if (!validateArgs3(args2)) {
|
|
65022
65308
|
const errorResult = {
|
|
65023
|
-
error: 'Invalid arguments: ecosystem must be "auto", "npm", "pip", "cargo", "go", "dotnet", "ruby", or "
|
|
65309
|
+
error: 'Invalid arguments: ecosystem must be "auto", "npm", "pip", "cargo", "go", "dotnet", "ruby", "dart", or "composer"'
|
|
65024
65310
|
};
|
|
65025
65311
|
return JSON.stringify(errorResult, null, 2);
|
|
65026
65312
|
}
|
|
@@ -65058,6 +65344,9 @@ var pkg_audit = createSwarmTool({
|
|
|
65058
65344
|
case "dart":
|
|
65059
65345
|
result = await runDartAudit(directory);
|
|
65060
65346
|
break;
|
|
65347
|
+
case "composer":
|
|
65348
|
+
result = await runComposerAudit(directory);
|
|
65349
|
+
break;
|
|
65061
65350
|
}
|
|
65062
65351
|
return JSON.stringify(result, null, 2);
|
|
65063
65352
|
}
|
|
@@ -65173,6 +65462,7 @@ var SUPPORTED_PARSER_EXTENSIONS = new Set([
|
|
|
65173
65462
|
".hpp",
|
|
65174
65463
|
".cs",
|
|
65175
65464
|
".php",
|
|
65465
|
+
".blade.php",
|
|
65176
65466
|
".rb"
|
|
65177
65467
|
]);
|
|
65178
65468
|
function isTestFile2(filePath) {
|
|
@@ -66272,6 +66562,36 @@ var phpRules = [
|
|
|
66272
66562
|
description: "preg_replace with /e modifier allows code execution",
|
|
66273
66563
|
remediation: "Use preg_replace_callback instead of preg_replace with /e modifier.",
|
|
66274
66564
|
pattern: /preg_replace\s*\(\s*\/[^/]*\/e/
|
|
66565
|
+
},
|
|
66566
|
+
{
|
|
66567
|
+
id: "sast/php-laravel-sql-injection",
|
|
66568
|
+
name: "Laravel raw SQL with string concatenation",
|
|
66569
|
+
severity: "high",
|
|
66570
|
+
languages: ["php"],
|
|
66571
|
+
description: "DB::raw(), whereRaw(), selectRaw(), orderByRaw(), or havingRaw() with concatenated user input creates SQL injection risk",
|
|
66572
|
+
remediation: "Use parameterized queries with ? bindings instead of string concatenation.",
|
|
66573
|
+
pattern: /(?:DB::raw|->whereRaw|->selectRaw|->orderByRaw|->havingRaw)\s*\(\s*(?:["'][^"']*["']\s*\.\s*\$|\$)/
|
|
66574
|
+
},
|
|
66575
|
+
{
|
|
66576
|
+
id: "sast/php-laravel-mass-assignment",
|
|
66577
|
+
name: "Laravel mass assignment protection disabled",
|
|
66578
|
+
severity: "medium",
|
|
66579
|
+
languages: ["php"],
|
|
66580
|
+
description: "$guarded = [] disables all mass-assignment protection, allowing any attribute to be set",
|
|
66581
|
+
remediation: "Use $fillable to explicitly whitelist allowed attributes, or remove $guarded = [] to keep default protection.",
|
|
66582
|
+
pattern: /(?:protected|public)\s+\$guarded\s*=\s*\[\s*\]/
|
|
66583
|
+
},
|
|
66584
|
+
{
|
|
66585
|
+
id: "sast/php-laravel-destructive-migration",
|
|
66586
|
+
name: "Destructive migration without down() method",
|
|
66587
|
+
severity: "medium",
|
|
66588
|
+
languages: ["php"],
|
|
66589
|
+
description: "Destructive migration operations (drop*) without a down() method cannot be rolled back",
|
|
66590
|
+
remediation: "Add a down() method that reverses the migration, or use a safer alternative like soft deletes.",
|
|
66591
|
+
pattern: /(?:Schema::drop(?:IfExists)?\(|->dropColumn\()/,
|
|
66592
|
+
validate: (_match, context) => {
|
|
66593
|
+
return !/function\s+down\s*\(/.test(context.content);
|
|
66594
|
+
}
|
|
66275
66595
|
}
|
|
66276
66596
|
];
|
|
66277
66597
|
|
|
@@ -70232,6 +70552,7 @@ var SUPPORTED_EXTENSIONS2 = new Set([
|
|
|
70232
70552
|
".cs",
|
|
70233
70553
|
".rb",
|
|
70234
70554
|
".php",
|
|
70555
|
+
".blade.php",
|
|
70235
70556
|
".swift",
|
|
70236
70557
|
".kt"
|
|
70237
70558
|
]);
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Framework Detection Utilities
|
|
3
|
+
*
|
|
4
|
+
* Provides deterministic multi-signal framework detection.
|
|
5
|
+
* Laravel detection uses at least 2 of 3 signals to avoid false positives.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Detection signals for Laravel framework identification.
|
|
9
|
+
* Each signal independently indicates a Laravel project.
|
|
10
|
+
*/
|
|
11
|
+
export interface LaravelDetectionSignals {
|
|
12
|
+
/** artisan file present in project root (no extension) */
|
|
13
|
+
hasArtisanFile: boolean;
|
|
14
|
+
/** laravel/framework present in composer.json require dependencies */
|
|
15
|
+
hasLaravelFrameworkDep: boolean;
|
|
16
|
+
/** config/app.php present (Laravel config directory structure) */
|
|
17
|
+
hasConfigApp: boolean;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Resolved command overlay for a detected Laravel project.
|
|
21
|
+
* All fields are set to best-available commands for CI-quality use.
|
|
22
|
+
*/
|
|
23
|
+
export interface LaravelCommandOverlay {
|
|
24
|
+
/** Primary test command. Always php artisan test for Laravel. */
|
|
25
|
+
testCommand: string;
|
|
26
|
+
/** Lint/format command. Pint if detected, PHP-CS-Fixer otherwise, null if neither. */
|
|
27
|
+
lintCommand: string | null;
|
|
28
|
+
/** Static analysis command. PHPStan if phpstan config is present, null otherwise. */
|
|
29
|
+
staticAnalysisCommand: string | null;
|
|
30
|
+
/** Dependency audit command (always composer audit --locked --format=json for Laravel). */
|
|
31
|
+
auditCommand: string;
|
|
32
|
+
/** Whether --parallel flag is supported (Pest parallel testing via artisan). */
|
|
33
|
+
supportsParallel: boolean;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Detect whether a directory is a Laravel project.
|
|
37
|
+
* Uses multi-signal detection: at least 2 of 3 signals must be present
|
|
38
|
+
* to minimize false positives against generic Composer PHP projects.
|
|
39
|
+
*
|
|
40
|
+
* Signals checked:
|
|
41
|
+
* 1. artisan file in project root (strong signal — only Laravel projects have this)
|
|
42
|
+
* 2. laravel/framework in composer.json require section
|
|
43
|
+
* 3. config/app.php file (Laravel directory structure)
|
|
44
|
+
*
|
|
45
|
+
* @param directory - Absolute path to the project root
|
|
46
|
+
* @returns true if project is a Laravel project, false otherwise
|
|
47
|
+
*/
|
|
48
|
+
export declare function detectLaravelProject(directory: string): boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Get individual Laravel detection signals for a directory.
|
|
51
|
+
* Exposed for testing and diagnostic purposes.
|
|
52
|
+
*
|
|
53
|
+
* @param directory - Absolute path to the project root
|
|
54
|
+
* @returns LaravelDetectionSignals with each signal's boolean state
|
|
55
|
+
*/
|
|
56
|
+
export declare function getLaravelSignals(directory: string): LaravelDetectionSignals;
|
|
57
|
+
/**
|
|
58
|
+
* Get the Laravel command overlay for a project directory.
|
|
59
|
+
* Returns null if the directory is not a Laravel project.
|
|
60
|
+
*
|
|
61
|
+
* Command selection logic:
|
|
62
|
+
* - testCommand: always 'php artisan test' (wraps both PHPUnit and Pest)
|
|
63
|
+
* - lintCommand: 'vendor/bin/pint --test' if pint.json present,
|
|
64
|
+
* 'vendor/bin/php-cs-fixer fix --dry-run --diff' if .php-cs-fixer.php present,
|
|
65
|
+
* null otherwise
|
|
66
|
+
* - staticAnalysisCommand: 'vendor/bin/phpstan analyse' if phpstan.neon or phpstan.neon.dist present,
|
|
67
|
+
* null otherwise
|
|
68
|
+
* - auditCommand: always 'composer audit --locked --format=json'
|
|
69
|
+
* - supportsParallel: true (php artisan test --parallel is supported)
|
|
70
|
+
*
|
|
71
|
+
* @param directory - Absolute path to the project root
|
|
72
|
+
* @returns LaravelCommandOverlay if Laravel detected, null if not a Laravel project
|
|
73
|
+
*/
|
|
74
|
+
export declare function getLaravelCommandOverlay(directory: string): LaravelCommandOverlay | null;
|
package/dist/lang/profiles.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-swarm",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.49.0",
|
|
4
4
|
"description": "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|