pantheon-opencode 1.6.0-beta.6 → 1.6.0-beta.8

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.
Files changed (54) hide show
  1. package/AGENTS.md +19 -769
  2. package/CHANGELOG.md +53 -0
  3. package/README.md +78 -9
  4. package/README.pt-BR.md +10 -4
  5. package/package.json +5 -1
  6. package/plugin.json +2 -2
  7. package/pyproject.toml +3 -1
  8. package/scripts/build-agents-md.mjs +159 -26
  9. package/scripts/install/agents-md.mjs +2 -2
  10. package/scripts/install/config-migration.mjs +115 -1
  11. package/scripts/install/interactive.mjs +1 -1
  12. package/scripts/install/opencode.mjs +245 -12
  13. package/scripts/install/shared.mjs +115 -7
  14. package/scripts/sync-artifacts.mjs +2 -2
  15. package/scripts/test-opencode-v2-sandbox.sh +155 -4
  16. package/scripts/validate-sandbox-install.mjs +138 -0
  17. package/skills-lock.json +5 -5
  18. package/src/agents/aphrodite.md +18 -36
  19. package/src/agents/demeter.md +8 -27
  20. package/src/agents/gaia.md +1 -0
  21. package/src/agents/hephaestus.md +10 -20
  22. package/src/agents/hermes.md +15 -37
  23. package/src/agents/mnemosyne.md +14 -42
  24. package/src/agents/prometheus.md +16 -19
  25. package/src/agents/talos.md +4 -1
  26. package/src/agents/themis.md +4 -4
  27. package/src/agents/zeus.md +24 -127
  28. package/src/instructions/agent-return-format.instructions.md +2 -3
  29. package/src/instructions/backend-standards.instructions.md +2 -0
  30. package/src/instructions/frontend-standards.instructions.md +3 -1
  31. package/src/instructions/memory-protocol.instructions.md +11 -121
  32. package/src/instructions/zeus-anti-stall.instructions.md +14 -69
  33. package/src/instructions/zeus-communication-rules.instructions.md +0 -1
  34. package/src/instructions/zeus-council-synthesis.instructions.md +32 -260
  35. package/src/instructions/zeus-memory-operations.instructions.md +26 -0
  36. package/src/instructions/zeus-timeout-retry.instructions.md +40 -87
  37. package/src/pantheon/v2-delegation-enforce.ts +87 -0
  38. package/src/pantheon/v2-execute-after.ts +186 -0
  39. package/src/pantheon/v2-unsupported.mjs +0 -23
  40. package/src/plugin-v2.ts +271 -36
  41. package/src/plugins/tui/dist/tui.d.ts +11 -8
  42. package/src/plugins/tui/dist/tui.d.ts.map +1 -1
  43. package/src/plugins/tui/dist/tui.js +53 -37
  44. package/src/plugins/tui/package-lock.json +30 -33
  45. package/src/plugins/tui/package.json +5 -1
  46. package/src/plugins/tui/src/index.tsx +103 -82
  47. package/src/plugins/tui/tsdown.config.ts +17 -0
  48. package/src/routing.yml +10 -5
  49. package/src/skills/README.md +3 -3
  50. package/src/skills/artifact-management/SKILL.md +12 -5
  51. package/src/skills/auto-continue/SKILL.md +37 -28
  52. package/src/skills/code-review-checklist/SKILL.md +5 -5
  53. package/src/skills/context-compression/SKILL.md +26 -879
  54. package/src/skills/tdd-with-agents/SKILL.md +18 -10
package/CHANGELOG.md CHANGED
@@ -18,6 +18,59 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
18
18
 
19
19
  ## ✅ Closed Issues
20
20
 
21
+ ## [v1.6.0-beta.8] - 2026-10-11
22
+
23
+ <!-- Add new changes here. Running `node scripts/versioning.mjs apply` will
24
+ move this section to a versioned entry and reset the template below. -->
25
+
26
+ ## 🐞 Fixed
27
+
28
+ - **opencode-v2** — repair session lookup and beta sandbox reuse
29
+ - **scripts** — isolate beta background service port
30
+ ## [v1.6.0-beta.7] - 2026-10-10
31
+
32
+ <!-- Add new changes here. Running `node scripts/versioning.mjs apply` will
33
+ move this section to a versioned entry and reset the template below. -->
34
+
35
+ ## 🆕 What's New
36
+
37
+ - **Permissões MCP nativas por agente no OpenCode V1 e V2.** O instalador
38
+ converte a allowlist `mcp_tools` de cada agente em regras nativas: nega por
39
+ padrão os servidores Pantheon omitidos, libera apenas as ferramentas
40
+ declaradas e preserva MCPs de terceiros. O V2 mantém os servidores
41
+ conectados; as regras limitam quais ferramentas cada agente vê.
42
+ - **Guarda V2 de delegação alinhada ao contrato de permissões.** Os hooks de
43
+ permissão e de sessão aplicam as restrições de sessão somente-leitura e a
44
+ matriz caller→target sem conceder acesso além do configurado pelo host.
45
+ - **Menos chamadas automáticas de memória e compressão.** A busca de contexto
46
+ do Zeus reutiliza o resultado de início de tarefa, e o protocolo de
47
+ compressão deixa de recomendar pipelines automáticos extensos.
48
+ - **Prova de identidade da instalação do sandbox.** O teste global compara a
49
+ versão e arquivos do pacote instalado com este checkout e rejeita referências
50
+ npm antigas nas configurações ativas antes de validar o runtime.
51
+ - O `--prepare` agora gera `start-pantheon.sh` para abrir o TUI interativo com o
52
+ beta instalado e HOME, configuração XDG, projeto e banco de dados isolados do
53
+ sandbox. O launcher fixa a porta do serviço gerenciado em `49376` e falha sem
54
+ parar serviços existentes se a porta dedicada já estiver ocupada.
55
+
56
+ ## 🐞 Fixed
57
+
58
+ - Referências versionadas/alias do pacote Pantheon que sobravam na lista
59
+ `plugins` agora são reconhecidas pelo instalador e substituídas pelo caminho
60
+ do plugin local correto, sem afetar plugins de terceiros com nomes parecidos.
61
+ - A suíte CI foi separada do caminho crítico de instalação/empacotamento para
62
+ reduzir o tempo até o resultado das verificações do pacote.
63
+ - O `run-test.sh` gerado agora define seu timeout do `mcp list` antes do uso
64
+ sob `set -u`; antes, a variável ausente interrompia o teste antes de chamar
65
+ o CLI. A saída do comando também fica disponível quando o gate falha.
66
+
67
+ ## ⚠️ Known Issues
68
+
69
+ - **A delegação real ainda precisa de confirmação nesta beta.** O teste manual
70
+ anterior de `HERMES-PONG` foi negado com
71
+ `Pantheon delegation denied: authoritative caller session is unavailable`;
72
+ a correção de permissões e a instalação isolada precisam ser verificadas no
73
+ TUI OpenCode V2 antes de considerar a delegação validada.
21
74
  ## [v1.6.0-beta.6] - 2026-10-05
22
75
 
23
76
  <!-- Add new changes here. Running `node scripts/versioning.mjs apply` will
package/README.md CHANGED
@@ -54,6 +54,9 @@ project-local installation, or non-interactive setup, see the
54
54
  [installation guide](docs/INSTALLATION.md).
55
55
  The bundled Python MCP servers use the pinned MCP SDK 2.2.0 and standalone
56
56
  FastMCP 4.0.10 runtime; see the [MCP guide](docs/MCP.md).
57
+ The installer also turns each agent's `mcp_tools` allowlist into native
58
+ per-agent permissions for OpenCode V1 and V2; see the
59
+ [agent MCP reference](docs/AGENT-MCP.md).
57
60
 
58
61
  ## A simple example
59
62
 
@@ -80,6 +83,13 @@ clear handoff between stages of work.
80
83
  - Project memory that helps preserve relevant context between sessions.
81
84
  - Optional integrations for common development tasks.
82
85
 
86
+ Memory is used selectively: the coordinator searches once only when project
87
+ history could affect a task, then passes relevant results to specialists. Store
88
+ one concise, reusable outcome at task completion when it will matter later;
89
+ routine child summaries and phase updates stay in the current handoff. The
90
+ session-checkpoint tools are separate, optional persistence APIs and are not
91
+ called automatically by compaction hooks. See [Memory usage](docs/MEMORY.md).
92
+
83
93
  ## Status
84
94
 
85
95
  Candidate version: the manifests in this checkout express the version being
@@ -131,10 +141,15 @@ host is an unsupported mixed/wrong-generation configuration, not a supported mod
131
141
  **Not inert.** The V2 path (`src/plugin-v2`, registered as a directory because
132
142
  the V2 loader rejects bare file paths) registers real tools with real
133
143
  `input`/`output` schemas via `ctx.tool.transform()`, 5 event subscriptions,
134
- session hooks and a tool `execute.before` hook that enforces read-only sessions.
135
- It is a smaller surface than V1 by design, not a stub: the 3 goal tools and the
136
- V1 caller→target delegation matrix are absent, and `getUnsupportedFeatures()`
137
- reports that as `goal-tools` and `delegation-matrix`.
144
+ session hooks and tool/permission hooks that enforce read-only sessions and the
145
+ caller→target delegation matrix. The matrix applies only to Pantheon-managed
146
+ targets: Zeus may delegate to any registered agent; Athena and Hermes may target
147
+ Apollo only; other Pantheon callers and unknown callers are denied. Child
148
+ sessions cannot delegate to Pantheon agents. Calls between native/custom agents
149
+ remain under host permissions. The V2 path never adds an allow grant or
150
+ overrides an explicit host deny; missing or mismatched identity fails closed
151
+ for the attempted Pantheon delegation. The 3 goal tools remain unsupported and
152
+ are reported as `goal-tools`.
138
153
 
139
154
  **Not complete either, and the V2 support claim is currently narrowed.** Two
140
155
  facts, both recorded in this repository rather than smoothed over:
@@ -175,6 +190,28 @@ there is no Pantheon-specific delegation tool surface to configure. See
175
190
  [ADR-0011](.pantheon/memory-bank/adr/0011-delegation-engine-contract.md) for the
176
191
  historical engine contract.
177
192
 
193
+ This beta.8 candidate combines native per-agent MCP permissions for OpenCode V1
194
+ and V2, V2 delegation-permission alignment, simpler memory/compression
195
+ instructions, and a sandbox check that proves the installed package matches
196
+ this checkout instead of silently accepting stale npm-cached code. The isolated
197
+ sandbox launcher also pins the OpenCode background service to its dedicated
198
+ loopback port, rather than colliding with another OpenCode installation. The Zeus
199
+ prompt reuses one task-start memory-search result for context and routing;
200
+ runtime call counts and latency have not been measured. A beta.7 V2 dispatch
201
+ failed because the permission hook passed a bare string to `ctx.session.get`
202
+ instead of the V2 input object `{ sessionID }`; beta.8 corrects the call and
203
+ adds contract tests for the success and failure cases. The user subsequently
204
+ reported a successful Hermes liveness delegation in the active session, but
205
+ freshly restarting and testing the beta.8 sandbox remains the end-to-end gate.
206
+ The original report also included a separate `GET /api/session/{id}/permission`
207
+ 404; the session ID exists in the beta7-candidate database but not the default
208
+ global database. Both OpenCode services were running, so the 404 in the global
209
+ log may come from the other instance; this remains an end-to-end verification
210
+ item.
211
+
212
+ Delegated tasks get at most one retry after an initial timeout or failure. If
213
+ that retry fails, the configured fallback or escalation chain applies.
214
+
178
215
  ## Cost tool backend
179
216
 
180
217
  `pantheon_cost` prefers a read-only `node:sqlite` backend against the selected
@@ -285,10 +322,16 @@ surface: the host includes the active agent on the event, a read-only agent
285
322
  `createEnforcementGuard` throws to deny `edit`, `write`, `bash`, `task`,
286
323
  `hashline_edit` and `pantheon_model`. It does **not** rely on the V1
287
324
  `tool.execute.before` hook, which lives in `src/plugin.ts` and is not loaded
288
- when only `plugin-v2` is configured. The companion `execute.after` hook and the
289
- `permission.evaluate` hook are registration points with no V2-side behaviour,
290
- and the V1 caller/target delegation matrix for native `task()` is not enforced
291
- on V2.
325
+ when only `plugin-v2` is configured. The `permission.evaluate` hook enforces
326
+ the V2 caller/target matrix for Pantheon-managed targets across every entry in
327
+ the host's `resources` array. Native-only resources pass through unchanged to
328
+ OpenCode's permission policy; mixed native/Pantheon requests and multi-target
329
+ requests are denied unless every Pantheon target is explicitly allowed for the
330
+ authoritative caller. Explicit host denies remain final, and missing or
331
+ unknown resource data fails closed. If a denied evaluation has no mutable host
332
+ output argument, the hook throws to fail closed rather than silently omitting
333
+ the denial. V2 `execute.after` runs the completed-result chain:
334
+ `task-result-guard` → `context-sandbox` → `read-enhancer`.
292
335
 
293
336
  Every V2 tool declares an `output` schema. OpenCode 2.0.x requires the
294
337
  declaration and the resolved result to agree in both directions, so an
@@ -473,6 +516,10 @@ Release validation keeps each manifest with its lockfile: the root
473
516
  SHA-256 of that same artifact, and binds the tarball and GitHub release to the
474
517
  full `TARGET_SHA`; a second pack is not interchangeable.
475
518
 
519
+ CI also rebuilds the TUI with `npm run build --prefix src/plugins/tui` and
520
+ checks every packaged `dist` artifact byte-for-byte against its checked-in
521
+ version, including final newlines.
522
+
476
523
  ## Sandbox validation (V2)
477
524
 
478
525
  `scripts/test-opencode-v2-sandbox.sh` validates the globally installed
@@ -484,6 +531,11 @@ agent delegation. The gate is fail-closed: every required check must return an
484
531
  explicit PASS; timeouts, auth/network/provider failures and missing
485
532
  prerequisites block the run.
486
533
 
534
+ The runner defaults to the current stable `@opencode/cli` package and its
535
+ `opencode2` alias; set `OPENCODE_V2_SPEC` only when a test needs a different
536
+ host build. This keeps Pantheon beta testing separate from the OpenCode CLI
537
+ release channel.
538
+
487
539
  "V2" here refers only to the hook canary observed against an OpenCode v2.0.18
488
540
  host, where at least one tested hook callback fired; it does not establish
489
541
  compatibility with the stable `@opencode/plugin@2.0.18` SDK or full 2.x
@@ -495,6 +547,7 @@ single leg.
495
547
 
496
548
  ```bash
497
549
  scripts/test-opencode-v2-sandbox.sh --prepare # tarball + install + init in the sandbox
550
+ ~/pantheon-sandbox/start-pantheon.sh # interactive TUI in that isolated beta sandbox
498
551
  scripts/test-opencode-v2-sandbox.sh --run v2 # base validation only
499
552
  scripts/test-opencode-v2-sandbox.sh --prompts # base validation + prompt battery
500
553
  scripts/test-opencode-v2-sandbox.sh --rehydrate # offline context rehydration/session-summary probe
@@ -533,7 +586,7 @@ Env overrides:
533
586
  |----------|---------|---------|
534
587
  | `PANTHEON_SANDBOX_ROOT` | `~/pantheon-sandbox` | Sandbox root (refused if unsafe for `--reset`) |
535
588
  | `OPENCODE_V1_SPEC` | `opencode-ai@1.18.18` | Configures the V1 plugin only; the V2-only sandbox runner does not consume or support it |
536
- | `OPENCODE_V2_SPEC` | `@opencode-ai/cli@beta` | npm spec providing the `opencode2` binary |
589
+ | `OPENCODE_V2_SPEC` | `@opencode/cli@latest` | npm spec providing the `opencode2` binary |
537
590
  | `PANTHEON_SANDBOX_MODEL` | `opencode-go/mimo-v2.5` | Model used by init and prompts |
538
591
  | `PANTHEON_PROMPT_TIMEOUT` | `300` | Per-prompt timeout in seconds |
539
592
 
@@ -591,6 +644,22 @@ Ideas, bug reports, documentation improvements, and code contributions are
591
644
  welcome. Please read [CONTRIBUTING.md](CONTRIBUTING.md) before opening an issue
592
645
  or pull request.
593
646
 
647
+ ### Reproducible benchmark evidence
648
+
649
+ The beta2 benchmark records reported token usage separately from its
650
+ character-based fallback estimate; source bytes and logical line counts remain
651
+ provenance data and are never converted into tokens. Cross-report comparisons
652
+ require matching model/config provenance and reported OpenCode usage. Offline
653
+ fixture responses are mocked: their reported values are labeled separately,
654
+ excluded from `actual` usage, and blocked from comparative metrics and GEPA.
655
+ Fixture quality outcomes test the offline contract; they are not evaluation
656
+ evidence.
657
+ Reproduce the
658
+ checked-in AGENTS.md history manifest with
659
+ `python3 -m benchmarks.beta2.provenance --output benchmarks/beta2/fixtures/agent-baseline-provenance.json`.
660
+ For OpenCode benchmark runs, provide `--model` and `--config-sha256` so reports
661
+ can be compared safely.
662
+
594
663
  ### Project-level `opencode.json`
595
664
 
596
665
  The published package carries no `opencode.json` template. `init` still writes
package/README.pt-BR.md CHANGED
@@ -141,10 +141,16 @@ dessa superfície: o host inclui o agente ativo no evento, um agente somente-lei
141
141
  compartilhado lança para negar `edit`, `write`, `bash`, `task`,
142
142
  `hashline_edit` e `pantheon_model`. Ele **não** depende do hook
143
143
  `tool.execute.before` do V1, que vive em `src/plugin.ts` e não é carregado
144
- quando apenas `plugin-v2` está configurado. Os hooks companheiros
145
- `execute.after` e `permission.evaluate` são pontos de registro sem comportamento
146
- do lado V2, e a matriz de delegação caller/target da V1 para o `task()` nativo
147
- não é imposta no V2.
144
+ quando apenas `plugin-v2` está configurado. O hook `permission.evaluate`
145
+ aplica no V2 a matriz caller/target para destinos gerenciados pelo Pantheon,
146
+ considerando todos os itens do array `resources` do host. Recursos
147
+ exclusivamente nativos são repassados sem alteração à política de permissões do
148
+ OpenCode; pedidos mistos (nativos/Pantheon) e com vários destinos são negados,
149
+ a menos que todos os destinos Pantheon sejam explicitamente permitidos para o
150
+ chamador autoritativo. Uma negação explícita do host é final, e dados de
151
+ recursos ausentes ou desconhecidos são negados por padrão. O `execute.after` do
152
+ V2 executa a cadeia de resultados concluídos `task-result-guard` →
153
+ `context-sandbox` → `read-enhancer`; eventos de erro permanecem inalterados.
148
154
 
149
155
  Toda ferramenta V2 declara um schema `output`. O OpenCode 2.0.x exige que a
150
156
  declaração e o resultado resolvido concordem nos dois sentidos, então uma
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pantheon-opencode",
3
- "version": "1.6.0-beta.6",
3
+ "version": "1.6.0-beta.8",
4
4
  "description": "Multi-agent orchestration for OpenCode — 14 specialized agents, TDD, Themis review gates.",
5
5
  "type": "module",
6
6
  "workspaces": [
@@ -112,8 +112,12 @@
112
112
  "typescript": "7.0.2"
113
113
  },
114
114
  "overrides": {
115
+ "@babel/core": "7.29.7",
116
+ "seroval": "1.6.8",
117
+ "solid-js": "1.9.12",
115
118
  "rolldown": "1.2.7"
116
119
  },
120
+ "//overrides": "TUI production advisory fixes: Babel >=7.29.6 and Seroval >=1.6.3; Solid stays at 1.9.12 to satisfy OpenTUI's exact peer. Remove only after upstream ranges include fixed versions; Seroval compatibility is covered by the Solid serializer regression test.",
117
121
  "repository": {
118
122
  "type": "git",
119
123
  "url": "https://github.com/ils15/pantheon-opencode.git"
package/plugin.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "pantheon",
3
3
  "publisher": "ils15",
4
- "version": "1.6.0-beta.6",
4
+ "version": "1.6.0-beta.8",
5
5
  "displayName": "Pantheon",
6
- "description": "Conductor-Delegate orchestration platform with 14 specialized agents, 21 skills, and OpenCode support.",
6
+ "description": "Conductor-Delegate orchestration platform with 14 specialized agents, 20 skills, and OpenCode support.",
7
7
  "agents": "./src/agents",
8
8
  "skills": "./src/skills",
9
9
  "instructions": "./src/instructions",
package/pyproject.toml CHANGED
@@ -4,7 +4,7 @@ build-backend = "setuptools.backends._legacy:_Backend"
4
4
 
5
5
  [project]
6
6
  name = "pantheon"
7
- version = "1.6.0-beta.6"
7
+ version = "1.6.0-beta.8"
8
8
  description = "Multi-agent orchestration framework"
9
9
  requires-python = ">=3.11"
10
10
  dependencies = []
@@ -16,6 +16,7 @@ dependencies = []
16
16
  testpaths = [
17
17
  "tests",
18
18
  "benchmarks/beta2/test_beta2.py",
19
+ "benchmarks/beta2/test_t1.py",
19
20
  ]
20
21
  asyncio_mode = "auto"
21
22
  addopts = "-v --tb=short"
@@ -48,6 +49,7 @@ select = ["E", "F", "W", "I", "N", "UP", "B", "SIM", "PL", "RUF", "C901"]
48
49
  "tests/test_code_mode_frontmatter.py" = ["PLR2004"]
49
50
  "tests/test_eval.py" = ["PLR2004"]
50
51
  "tests/test_mcp_persistence_server.py" = ["PLR2004"]
52
+ "benchmarks/beta2/test_t1.py" = ["PLR2004"]
51
53
 
52
54
  [tool.mypy]
53
55
  python_version = "3.11"
@@ -8,11 +8,11 @@
8
8
  * across both versions without duplication, this script consolidates:
9
9
  *
10
10
  * (a) a static header (project description + agent table + setup notes)
11
- * (b) every src/instructions/*.instructions.md body, embedded under a
12
- * `## <name>` section marker (name taken from frontmatter)
11
+ * (b) shared instruction bodies embedded under a `## <name>` section marker;
12
+ * agent-scoped bodies are rendered into only their matching agent prompt
13
13
  *
14
- * into one generated AGENTS.md. src/instructions/*.instructions.md remains
15
- * the source of truth — AGENTS.md is a committed build artifact, never
14
+ * into AGENTS.md plus selective agent prompt content. The instruction files
15
+ * remain the source of truth; AGENTS.md is a committed build artifact, never
16
16
  * hand-edited.
17
17
  *
18
18
  * Usage:
@@ -22,8 +22,8 @@
22
22
  * No runtime dependencies (Node >= 18, stdlib only). Idempotent: running it
23
23
  * twice produces byte-identical output.
24
24
  */
25
- import { existsSync, readdirSync, readFileSync, writeFileSync } from 'node:fs'
26
- import { dirname, join } from 'node:path'
25
+ import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from 'node:fs'
26
+ import { dirname, join, resolve } from 'node:path'
27
27
  import { fileURLToPath } from 'node:url'
28
28
 
29
29
  const __dirname = dirname(fileURLToPath(import.meta.url))
@@ -44,19 +44,19 @@ This project uses the Pantheon multi-agent framework with 14 specialized agents.
44
44
 
45
45
  | Agent | Role |
46
46
  |-------|------|
47
- | @aphrodite | Frontend specialist — React 19, TypeScript strict, WCAG accessibility, responsive design, TDD, modern API patterns, deprecated npm detection. Calls apollo for discovery, sends to themis for review. |
47
+ | @aphrodite | Frontend specialist — React 19, TypeScript strict, WCAG accessibility, responsive design, TDD, modern API patterns, deprecated npm detection. Uses discovery/review proportionally to scope and risk. |
48
48
  | @apollo | Read-only investigation scout — 3–10 parallel searches across codebase, external docs, and GitHub. Called by: athena, zeus, hermes, aphrodite, demeter. No edits, no commands. |
49
49
  | @athena | Strategic planner & architect — research-first, plan-only, never implements. Plans include quality gates (ruff/Biome, dep detection, LTS policy). Calls apollo for discovery. |
50
- | @demeter | Database specialist — SQLAlchemy 2.0, Alembic, query optimization, N+1 prevention, TDD migrations, modern DB libs. Calls apollo for discovery, sends to themis. |
50
+ | @demeter | Database specialist — SQLAlchemy 2.0, Alembic, query optimization, N+1 prevention, TDD migrations, modern DB libs. Preserves review gates for schema/data changes. |
51
51
  | @gaia | Remote sensing domain specialist — satellite image processing, spectral analysis, SAR, change detection, time series, ML/DL classification. Read-only analysis of geospatial data. |
52
52
  | @hephaestus | AI tooling & pipelines specialist — LangChain/LangGraph chains, RAG architecture, vector stores, embedding strategies. Forges AI infrastructure. Calls apollo, sends to themis. |
53
- | @hermes | Backend specialist — FastAPI, Python, async, TDD (RED→GREEN→REFACTOR), modern Python stdlib, obsolete lib detection. Calls apollo for discovery, sends to themis. |
53
+ | @hermes | Backend specialist — FastAPI, Python, async, TDD (RED→GREEN→REFACTOR), modern Python stdlib, obsolete lib detection. Uses discovery/review proportionally to scope and risk. |
54
54
  | @iris | GitHub operations specialist — branches, pull requests, issues, releases, tags. Called by zeus after review. Never pushes or merges without explicit human approval. Integrates with VS Code GitHub Pull Requests extension. |
55
55
  | @mnemosyne | Memory bank quality owner — initializes .pantheon/memory-bank/, writes ADRs and task records on explicit request. Called by zeus. Never invoked automatically after phases. |
56
56
  | @nyx | Observability & monitoring specialist — OpenTelemetry tracing, token/cost tracking, agent performance analytics, LangSmith integration. Calls apollo for discovery, sends to themis. |
57
57
  | @prometheus | Infrastructure + model provider specialist — Docker, CI/CD, multi-model routing, cost optimization, provider abstraction |
58
58
  | @talos | Hotfix express lane — direct fixes for small bugs, CSS, typos, minor logic. No TDD ceremony, no orchestration overhead. Standalone, no subagents. Escalates complex issues to zeus. |
59
- | @themis | Quality & security gate — ruff/Biome linting, dead/legacy code detection, OWASP Top 10, coverage >80%, correctness, deprecation audit. Called by implementers; escalates blockers to zeus. |
59
+ | @themis | Quality & security gate — ruff/Biome linting, dead/legacy code detection, OWASP Top 10, applicable coverage requirements, correctness, deprecation audit. Reviews material/sensitive changes. |
60
60
  | @zeus | Central orchestrator — never implements. Delegates to: athena, apollo, hermes, aphrodite, demeter, prometheus, themis, iris, mnemosyne, talos, hephaestus, nyx |
61
61
 
62
62
  ## OpenCode Setup
@@ -78,8 +78,8 @@ Para validar a instalação global do pacote pantheon-opencode COMO UM USUÁRIO
78
78
 
79
79
  ## Conventions
80
80
 
81
- - TDD: Write failing test first, then implement
82
- - Coverage minimum: 80%
81
+ - TDD: RED→GREEN→REFACTOR for testable behavior; use focused checks for micro-edits
82
+ - Honor repository coverage thresholds when relevant; preserve stronger auth/security/data-integrity gates
83
83
  - Async/await on all I/O
84
84
  - Type hints on all functions
85
85
  - PRs always update the README — every pull request must document newly added features, behaviors, env vars, or commands in the README before being opened.
@@ -92,20 +92,41 @@ Para validar a instalação global do pacote pantheon-opencode COMO UM USUÁRIO
92
92
  /**
93
93
  * Parse YAML-ish frontmatter from an instruction file.
94
94
  *
95
- * Only the `name` field is extracted (single-line scalar). The frontmatter
96
- * block itself is stripped from the embedded body.
95
+ * Preserve the file-scope `applyTo` glob separately from the explicit
96
+ * `agents` selectors. In particular, a Python workspace glob is never
97
+ * interpreted as an agent name. The frontmatter block is stripped from
98
+ * the embedded body.
97
99
  *
98
100
  * @param {string} content - Raw file content
99
- * @returns {{ name: string, body: string } | null}
101
+ * @returns {{ name: string, body: string, applyTo: string | null,
102
+ * agents: string[], fallback: string | null, appliesToAgent: (name: string) => boolean } | null}
100
103
  */
101
- function parseInstruction(content) {
104
+ export function parseInstruction(content) {
102
105
  const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?/)
103
106
  if (!match) return null
104
107
  const fm = match[1]
105
108
  const nameMatch = fm.match(/^name:\s*["']?([^"'\r\n]+)["']?\s*$/m)
106
109
  if (!nameMatch) return null
110
+ const applyToMatch = fm.match(/^applyTo:\s*["']?([^"'\r\n]+)["']?\s*$/m)
111
+ const agentsMatch = fm.match(/^agents:\s*\[([^\]\r\n]*)\]\s*$/m)
112
+ const agents = agentsMatch
113
+ ? agentsMatch[1]
114
+ .split(',')
115
+ .map((agent) => agent.trim().replace(/^(["'])(.*)\1$/, '$2'))
116
+ .filter(Boolean)
117
+ : fm.match(/^agents:/m)
118
+ ? [] // Malformed explicit selector: fail closed; never make it global.
119
+ : applyToMatch
120
+ ? [] // File-scoped instructions need an explicit agent association.
121
+ : ['*'] // Legacy instructions without selectors remain shared.
122
+ const fallbackMatch = fm.match(/^fallback:\s*["']?([^"'\r\n]+)["']?\s*$/m)
123
+ const appliesToAgent = (agent) => agents.includes('*') || agents.includes(agent)
107
124
  return {
108
125
  name: nameMatch[1].trim(),
126
+ applyTo: applyToMatch?.[1].trim() ?? null,
127
+ agents,
128
+ fallback: fallbackMatch?.[1].trim() ?? null,
129
+ appliesToAgent,
109
130
  body: `${content.slice(match[0].length).trimEnd()}\n`,
110
131
  }
111
132
  }
@@ -114,7 +135,8 @@ function parseInstruction(content) {
114
135
  * Read all src/instructions/*.instructions.md files, sorted by filename for
115
136
  * deterministic output.
116
137
  *
117
- * @returns {Array<{ file: string, name: string, body: string }>}
138
+ * @returns {Array<{ file: string, name: string, body: string, applyTo: string | null,
139
+ * agents: string[], fallback: string | null, appliesToAgent: (name: string) => boolean }>}
118
140
  */
119
141
  export function readInstructions() {
120
142
  if (!existsSync(INSTRUCTIONS_DIR)) return []
@@ -137,10 +159,13 @@ export function readInstructions() {
137
159
  /**
138
160
  * Generate the full AGENTS.md content.
139
161
  *
140
- * @param {Array<{ file: string, name: string, body: string }>} instructions
162
+ * @param {Array<{ file: string, name: string, body: string, agents?: string[] }>} instructions
141
163
  * @returns {string}
142
164
  */
143
165
  export function generateAgentsMd(instructions) {
166
+ const sharedInstructions = instructions.filter(
167
+ (instr) => !instr.agents || instr.agents.includes('*'),
168
+ )
144
169
  const sections = [
145
170
  '<!-- Generated by scripts/build-agents-md.mjs — do not edit directly. -->',
146
171
  '<!-- Source of truth: src/instructions/*.instructions.md (AGENTS.md is a committed build artifact). -->',
@@ -153,10 +178,11 @@ export function generateAgentsMd(instructions) {
153
178
  '',
154
179
  'The following sections are consolidated from `src/instructions/*.instructions.md`',
155
180
  'so both OpenCode V1 (auto-loads AGENTS.md) and V2 (loads AGENTS.md, ignores the',
156
- '`instructions` config key) receive identical instruction content.',
181
+ '`instructions` config key) receive identical shared rules. Agent-scoped rules',
182
+ 'are rendered into only the matching installed agent prompts.',
157
183
  '',
158
184
  ]
159
- for (const instr of instructions) {
185
+ for (const instr of sharedInstructions) {
160
186
  sections.push(
161
187
  `<!-- Source: src/instructions/${instr.file} -->`,
162
188
  `## ${instr.name}`,
@@ -168,6 +194,110 @@ export function generateAgentsMd(instructions) {
168
194
  return `${sections.join('\n').trimEnd()}\n`
169
195
  }
170
196
 
197
+ const GENERATED_AGENT_BLOCK =
198
+ /\n?<!-- Pantheon selective instructions: generated -->[\s\S]*?<!-- \/Pantheon selective instructions -->\n?/g
199
+
200
+ /**
201
+ * Render the agent-specific instruction set into the supported agent prompt.
202
+ * The shared rules remain in AGENTS.md; file globs are retained as scope
203
+ * guidance and are never used as agent selectors.
204
+ *
205
+ * @param {string} agentName - Canonical agent name from the prompt filename
206
+ * @param {string} prompt - Canonical source agent prompt
207
+ * @param {Array<{ file: string, name: string, body: string, applyTo?: string | null,
208
+ * agents?: string[], fallback?: string | null }>} instructions
209
+ * @returns {string}
210
+ */
211
+ export function generateAgentPrompt(agentName, prompt, instructions = readInstructions()) {
212
+ const basePrompt = prompt.replace(GENERATED_AGENT_BLOCK, '').trimEnd()
213
+ const selected = instructions.filter(
214
+ (instr) => instr.agents && !instr.agents.includes('*') && instr.agents.includes(agentName),
215
+ )
216
+ if (selected.length === 0) {
217
+ return [
218
+ basePrompt,
219
+ '',
220
+ '<!-- Pantheon selective instructions: generated -->',
221
+ 'No agent-specific instructions are assigned; follow the shared rules in AGENTS.md.',
222
+ '<!-- /Pantheon selective instructions -->',
223
+ '',
224
+ ].join('\n')
225
+ }
226
+
227
+ const sections = selected.map((instr) => {
228
+ const scope = instr.applyTo
229
+ ? `\n\n> Apply only when a target workspace file matches \`${instr.applyTo}\`.`
230
+ : ''
231
+ const fallback =
232
+ instr.fallback === 'shared'
233
+ ? '\n\n> For work outside this scope, use the shared rules in AGENTS.md.'
234
+ : ''
235
+ return [
236
+ `<!-- Source: src/instructions/${instr.file} -->`,
237
+ `## ${instr.name}${scope}`,
238
+ '',
239
+ `${instr.body.trimEnd()}${fallback}`,
240
+ ].join('\n')
241
+ })
242
+ return [
243
+ basePrompt,
244
+ '',
245
+ '<!-- Pantheon selective instructions: generated -->',
246
+ sections.join('\n\n'),
247
+ '<!-- /Pantheon selective instructions -->',
248
+ '',
249
+ ].join('\n')
250
+ }
251
+
252
+ /**
253
+ * Copy agent prompts with scoped instructions embedded in each actual prompt.
254
+ * Used by both init and postinstall sync so the installed runtime receives the
255
+ * same selection without a per-turn injection hook.
256
+ *
257
+ * @param {string} sourceDir - Canonical src/agents directory
258
+ * @param {string} targetDir - OpenCode agents directory
259
+ * @param {boolean} dryRun - Do not write when true
260
+ * @param {boolean} existingOnly - Refresh installed prompts without adding missing agents
261
+ * @returns {{ created: number, skipped: number }}
262
+ */
263
+ export function copyAgentPrompts(sourceDir, targetDir, dryRun = false, existingOnly = false) {
264
+ const instructions = readInstructions()
265
+ if (!dryRun && !existingOnly) mkdirSync(targetDir, { recursive: true })
266
+ let created = 0
267
+ let skipped = 0
268
+ for (const file of readdirSync(sourceDir)
269
+ .filter((entry) => entry.endsWith('.md'))
270
+ .sort()) {
271
+ if (file.toLowerCase() === 'readme.md') continue
272
+ const sourcePath = join(sourceDir, file)
273
+ if (!statSync(sourcePath).isFile()) continue
274
+ const agentName = file.replace(/\.md$/, '')
275
+ const output = generateAgentPrompt(agentName, readFileSync(sourcePath, 'utf8'), instructions)
276
+ const targetPath = join(targetDir, file)
277
+ if (existingOnly && !existsSync(targetPath)) continue
278
+ const existing = existsSync(targetPath) ? readFileSync(targetPath, 'utf8') : null
279
+ if (existing === output) {
280
+ skipped++
281
+ } else {
282
+ if (!dryRun) writeFileSync(targetPath, output, 'utf8')
283
+ created++
284
+ }
285
+ }
286
+ return { created, skipped }
287
+ }
288
+
289
+ /**
290
+ * Count LF-delimited logical lines without assuming a trailing newline.
291
+ *
292
+ * @param {string} content - File content
293
+ * @returns {number}
294
+ */
295
+ export function logicalLineCount(content) {
296
+ if (content.length === 0) return 0
297
+ const separators = content.match(/\n/g)?.length ?? 0
298
+ return separators + (content.endsWith('\n') ? 0 : 1)
299
+ }
300
+
171
301
  // ---------------------------------------------------------------------------
172
302
  // CLI entry point
173
303
  // ---------------------------------------------------------------------------
@@ -175,6 +305,9 @@ export function generateAgentsMd(instructions) {
175
305
  function main() {
176
306
  const checkOnly = process.argv.includes('--check')
177
307
  const instructions = readInstructions()
308
+ const sharedCount = instructions.filter(
309
+ (instr) => !instr.agents || instr.agents.includes('*'),
310
+ ).length
178
311
  const content = generateAgentsMd(instructions)
179
312
 
180
313
  if (checkOnly) {
@@ -183,15 +316,15 @@ function main() {
183
316
  console.error(`❌ AGENTS.md is stale — run: node scripts/build-agents-md.mjs`)
184
317
  process.exit(1)
185
318
  }
186
- console.log(`✅ AGENTS.md is up to date (${instructions.length} instruction sections)`)
319
+ console.log(`✅ AGENTS.md is up to date (${sharedCount} shared instruction sections)`)
187
320
  return
188
321
  }
189
322
 
190
323
  writeFileSync(AGENTS_MD_PATH, content, 'utf8')
191
- const lines = content.split('\n').length
192
- console.log(
193
- `✅ Generated AGENTS.md (${instructions.length} instruction sections, ${lines} lines)`,
194
- )
324
+ const lines = logicalLineCount(content)
325
+ console.log(`✅ Generated AGENTS.md (${sharedCount} shared instruction sections, ${lines} lines)`)
195
326
  }
196
327
 
197
- main()
328
+ if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
329
+ main()
330
+ }
@@ -2,7 +2,7 @@
2
2
  /**
3
3
  * agents-md.mjs — Dynamic AGENTS.md generator from canonical agent frontmatter
4
4
  *
5
- * Reads all agents/*.agent.md files, extracts name + description from frontmatter,
5
+ * Reads all canonical src/agents/*.md files, extracts name + description from frontmatter,
6
6
  * and generates AGENTS.md content dynamically. Eliminates hardcoded agent tables
7
7
  * in platform installers (claude.mjs, cursor.mjs, windsurf.mjs, cline.mjs, etc.).
8
8
  *
@@ -64,7 +64,7 @@ function parseAgentFrontmatter(content) {
64
64
  // ---------------------------------------------------------------------------
65
65
 
66
66
  /**
67
- * Read all canonical agents/*.agent.md files, parse frontmatter,
67
+ * Read all canonical src/agents/*.md files, parse frontmatter,
68
68
  * and return an array of agent metadata objects sorted alphabetically by name.
69
69
  *
70
70
  * Skips files that don't match the `*.agent.md` pattern, files without