pantheon-opencode 1.6.0-beta.10 → 1.6.0-beta.11

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 (46) hide show
  1. package/AGENTS.global.md +42 -18
  2. package/AGENTS.md +1 -1
  3. package/CHANGELOG.md +24 -0
  4. package/README.md +66 -21
  5. package/bin/pantheon-init.mjs +44 -1
  6. package/package.json +4 -4
  7. package/plugin.json +1 -1
  8. package/pyproject.toml +1 -1
  9. package/scripts/build-agents-md.mjs +60 -6
  10. package/scripts/install/backup.mjs +475 -0
  11. package/scripts/install/cli-ui.mjs +6 -1
  12. package/scripts/install/exit-codes.mjs +26 -0
  13. package/scripts/install/interactive.mjs +15 -1
  14. package/scripts/install/migrate.mjs +41 -22
  15. package/scripts/install/opencode.mjs +562 -50
  16. package/scripts/install/plan.mjs +287 -0
  17. package/scripts/install/plugin.mjs +22 -8
  18. package/scripts/install/shared.mjs +89 -55
  19. package/scripts/install/strings.mjs +20 -0
  20. package/scripts/install/venv.mjs +13 -3
  21. package/scripts/install/vision-prompt.mjs +11 -1
  22. package/scripts/mcp_persistence.py +50 -15
  23. package/scripts/prune.mjs +9 -3
  24. package/scripts/restore.mjs +222 -0
  25. package/skills-lock.json +1 -1
  26. package/src/agents/iris.md +6 -0
  27. package/src/agents/zeus.md +19 -62
  28. package/src/instructions/agent-return-format.instructions.md +17 -15
  29. package/src/instructions/memory-protocol.instructions.md +3 -2
  30. package/src/instructions/test-economy.instructions.md +21 -0
  31. package/src/instructions/zeus-anti-stall.instructions.md +6 -6
  32. package/src/instructions/zeus-council-synthesis.instructions.md +10 -11
  33. package/src/instructions/zeus-memory-operations.instructions.md +10 -6
  34. package/src/instructions/zeus-timeout-retry.instructions.md +28 -9
  35. package/src/mcp/mcp_persistence.py +50 -15
  36. package/src/pantheon/cost-command.ts +5 -2
  37. package/src/pantheon/model-agents.ts +19 -0
  38. package/src/pantheon/model-command.ts +218 -56
  39. package/src/pantheon/v2-model-overrides.ts +275 -0
  40. package/src/pantheon/v2-tools.ts +18 -29
  41. package/src/plugin-v2.ts +13 -0
  42. package/src/plugins/tui/dist/tui-v2.js +2 -2
  43. package/src/plugins/tui/dist/tui.js +1 -1
  44. package/src/plugins/tui/package-lock.json +2 -2
  45. package/src/plugins/tui/package.json +1 -1
  46. package/src/skills/auto-continue/SKILL.md +14 -6
package/AGENTS.global.md CHANGED
@@ -1,7 +1,7 @@
1
1
  <!-- pantheon:generated:begin — Pantheon-managed region; regenerated on install/upgrade, never edit inside -->
2
2
  <!-- Generated by scripts/build-agents-md.mjs — do not edit directly. -->
3
3
  <!-- Source of truth: src/instructions/*.instructions.md + the static sections in this script. -->
4
- <!-- pantheon:agents-md v2 artifact=global generated-at=2026-10-14T15:44:12-03:00 sha=a305942 -->
4
+ <!-- pantheon:agents-md v2 artifact=global generated-at=2026-10-15T14:32:32-03:00 sha=66bf918 -->
5
5
 
6
6
  # Pantheon Agent System — OpenCode
7
7
 
@@ -76,7 +76,7 @@ the complete plan in the current conversation before requesting approval.
76
76
 
77
77
  ## Council Specialist Response Format
78
78
 
79
- When responding to a `/pantheon` council synthesis invocation, specialists MUST return this structured format:
79
+ When responding to a `/pantheon` council synthesis invocation, specialists MUST return this structured format. First-round specialists receive independent prompts without peer responses, whether dispatch is parallel or synchronous/sequential; they therefore cannot know actual peer positions. Do not guess them, claim agreement/disagreement with unseen peers, or self-reference in agreement signals. Omit `agreement_signals` or write `none` in the first round. In a rebuttal round, signals may reference only peer responses included in that round's prompt. Zeus derives agreement and disagreement during synthesis by comparing actual responses.
80
80
 
81
81
  ```
82
82
  ## specialist_response
@@ -85,8 +85,8 @@ When responding to a `/pantheon` council synthesis invocation, specialists MUST
85
85
  **trade_offs:** <what's gained vs lost, specific>
86
86
  **risks:** <what could go wrong, max 3 items>
87
87
  **confidence:** High | Medium | Low
88
- **agreement_signals:** agree:@agent1 on <issue>, agree:@agent2 on <issue> | disagree:@agent3 on <issue>
89
- **specific_claims:** <count of specific factual claims in response>
88
+ **agreement_signals:** <omit or `none` in first round; rebuttal may cite only visible peer responses>
89
+ **specific_claims:** <optional self-reported count; advisory only, not evidence>
90
90
  ```
91
91
 
92
92
  ### Field Rules
@@ -98,29 +98,31 @@ When responding to a `/pantheon` council synthesis invocation, specialists MUST
98
98
  | trade_offs | ✅ | At least one gain and one loss, specific to context |
99
99
  | risks | ✅ | Max 3 bullet points |
100
100
  | confidence | ✅ | One of: High, Medium, Low |
101
- | agreement_signals | Optional | Format: `agree:@agent on issue \| disagree:@agent on issue`. Use only when you can reference other specialists expected positions. |
102
- | specific_claims | ✅ | Integer count of verifiable factual statements in the response |
101
+ | agreement_signals | Optional | Omit or use `none` in the first round. In rebuttal, reference only peer responses visible in the prompt; never speculate or self-reference. Zeus derives synthesis positions from actual responses, not these signals. |
102
+ | specific_claims | Optional | Self-reported integer count only; advisory and not independently validated. Do not treat the count alone as evidence or proof of confidence. |
103
103
 
104
104
  ### Confidence Guidelines
105
105
 
106
- - **High**: Position backed by 3+ specific claims with evidence or direct experience
107
- - **Medium**: Position backed by 1-2 specific claims
106
+ - **High**: Position backed by at least 3 verifiable claims supported by evidence in the response; a self-reported count alone does not qualify
107
+ - **Medium**: Position backed by at least 1 verifiable claim supported by evidence in the response
108
108
  - **Low**: Position is opinion-based or speculative
109
109
 
110
+ Equal self-reported counts (for example, several specialists each reporting 7) do not by themselves prove fabrication; assess whether the actual response contains verifiable, supported claims.
111
+
110
112
  ### Example
111
113
  ```
112
114
  ## specialist_response
113
- **position:** The BackgroundJobBoard should be used for council crash recovery
114
- **reasoning:** The board already supports running→completed→reconciled state machine with persistence. Registering council dispatches there adds ~50ms overhead but prevents total session loss on context crash. The WAL pattern ensures no data loss on restart.
115
- **trade_offs:** Gain: crash recovery for multi-minute council sessions. Lose: ~50ms registration overhead per council.
116
- **risks:** Board persistence failure could block council start — implement with fire-and-forget error handling
117
- **confidence:** High
118
- **agreement_signals:** agree:@themis on persist-before-notify
119
- **specific_claims:** 3
115
+ **position:** Use a bounded local cache for the read-heavy endpoint.
116
+ **reasoning:** The endpoint repeatedly reads the same reference data. A bounded cache reduces repeated lookups, while a short TTL limits staleness.
117
+ **trade_offs:** Gain: fewer repeated reads. Lose: results may remain stale until the TTL expires.
118
+ **risks:** Cache invalidation may lag after updates.
119
+ **confidence:** Medium
120
+ **agreement_signals:** none
121
+ **specific_claims:** 2 (self-reported; advisory)
120
122
  ```
121
123
 
122
124
  ## Memory Context
123
- If memory affected the result, state the takeaway briefly; do not repeat the entry or task context.
125
+ If memory affected the result, state the takeaway briefly; do not repeat the entry or task context. If persistence was expected but no write tool was exposed or a write failed, state that it was not persisted; never imply a successful store without confirmation.
124
126
 
125
127
  <!-- Source: src/instructions/memory-protocol.instructions.md -->
126
128
  ## Memory Protocol
@@ -138,12 +140,34 @@ These rules apply to all agents; each agent file may add domain-specific rules.
138
140
  - Use a targeted query (`top_k=2`); do not search the whole prompt by default. Missing or irrelevant results never block current work.
139
141
 
140
142
  ### Persistence
141
- - `memory_store()` is manual, not automatic. Store at most one concise, reusable top-level result; never persist routine child summaries, phase updates, test logs, or temporary state.
142
- - Confirm the store succeeded before claiming persistence. There is no automatic memory WAL or recovery. Add ADRs to the memory bank only when explicitly requested.
143
+ - A memory write is optional and conditional on a write tool being exposed and permitted in the active session. Search permission does not imply write permission; in the reported Beta 10 Zeus root, `memory_store` is not exposed/granted.
144
+ - Store at most one concise, reusable top-level result; never persist routine child summaries, phase updates, test logs, or temporary state. Confirm a successful write before claiming persistence.
145
+ - If no write tool is available or the write did not succeed, say that the information was not persisted. Do not imply automatic memory storage or recovery. Add ADRs to the memory bank only when explicitly requested.
143
146
 
144
147
  ### Relevance and permanent records
145
148
  Ignore weak matches; routine summaries do not belong in durable memory.
146
149
 
150
+ <!-- Source: src/instructions/test-economy.instructions.md -->
151
+ ## Test Economy
152
+
153
+
154
+ Write the smallest test that proves the behavior: one behavior → one focused
155
+ test, never the same coverage duplicated across suites or files.
156
+
157
+ - Do NOT add E2E, full-install, or network-dependent tests unless the change
158
+ actually alters that end-to-end path. Prefer unit or integration tests.
159
+ - Do not assert on copy/format strings redundantly, and do not write
160
+ assertions that only restate the implementation ("test the test").
161
+ - Heavy or slow suites (e.g. real install/E2E) MUST be opt-in via a separate
162
+ script/tier. Never run them per edit or per phase; run only the affected
163
+ subset.
164
+ - Respect a per-phase validation time budget: if a suite takes minutes,
165
+ justify running it and prefer the targeted subset. Reserve the full heavy
166
+ suites for release gates.
167
+ - Fixing a bug: add one targeted regression test, not a broad new suite.
168
+ - This economy rule does NOT weaken stronger gates: auth, security,
169
+ data-integrity, and required coverage thresholds stay mandatory.
170
+
147
171
  <!-- Source: src/instructions/yagni.instructions.md -->
148
172
  ## YAGNI Anti-overengineering
149
173
 
package/AGENTS.md CHANGED
@@ -1,7 +1,7 @@
1
1
  <!-- pantheon:generated:begin — Pantheon-managed region; regenerated on install/upgrade, never edit inside -->
2
2
  <!-- Generated by scripts/build-agents-md.mjs — do not edit directly. -->
3
3
  <!-- Source of truth: the static project sections in scripts/build-agents-md.mjs. -->
4
- <!-- pantheon:agents-md v2 artifact=project generated-at=2026-10-14T15:44:12-03:00 sha=a305942 -->
4
+ <!-- pantheon:agents-md v2 artifact=project generated-at=2026-10-15T14:32:32-03:00 sha=66bf918 -->
5
5
 
6
6
  # Pantheon — Repository Setup
7
7
 
package/CHANGELOG.md CHANGED
@@ -18,6 +18,30 @@ 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.11] - 2026-10-15
22
+
23
+ ## 🆕 What's New
24
+
25
+ - **record final Beta 11 scope decisions**
26
+ - **record Setup delivery and final-gate lessons in roadmap**
27
+ - **forbid hook bypass in Iris commit contract**
28
+ - **add installer failure reporting and secret-safe output audit**
29
+ - **detect drift between installer plan and apply**
30
+ - **require explicit --yes authorization before installer apply**
31
+ - **exact installer backup set with restore**
32
+ - **add Beta 11 test-economy requirement to shared instructions**
33
+ - **preview per-file change set in installer dry-run**
34
+ - **reconcile Beta 11 roadmap status with completed phases**
35
+ - **describe pantheon_cost window and aggregation limits**
36
+ - **align lean-flow guidance and cost wording**
37
+ - **add project-scoped V2 model overrides**
38
+ - **align host tool and background contracts**
39
+
40
+ ## 🐞 Fixed
41
+
42
+ - **restore explicit "both" in zeus memory operations instruction**
43
+ - **correct council protocol and persistence bounds**
44
+
21
45
  ## [v1.6.0-beta.10] - 2026-10-12
22
46
 
23
47
  ## 🆕 What's New
package/README.md CHANGED
@@ -52,6 +52,23 @@ opencode
52
52
  The installer guides you through the available setup. For optional MCP servers,
53
53
  project-local installation, or non-interactive setup, see the
54
54
  [installation guide](docs/INSTALLATION.md).
55
+
56
+ A non-interactive apply requires `--yes` (alias `-y`): without a TTY and
57
+ without `--yes`, `init` prints the change plan and applies nothing. Exit codes:
58
+ `0` success or plan/no-apply, `1` failure, `2` apply requested without
59
+ authorization, `3` drift (a planned target changed between plan and apply),
60
+ `130` canceled. Drift is checked within a single run (plan → apply); it cannot
61
+ be detected across two separate CLI invocations (no plan is persisted).
62
+
63
+ Preview without writing anything with `init --dry-run`. Before the first write,
64
+ `init` saves an exact backup set under `.pantheon/backups/<timestamp>/`
65
+ (directory `0700`, files `0600`, the 5 most recent kept); it is a byte-for-byte
66
+ copy and therefore also contains any API keys or tokens already present in your
67
+ files — it is never printed. Recover with
68
+ `pantheon-opencode restore [--project] [--yes]` (there is no automatic
69
+ rollback; an interrupted apply is recoverable the same way). The
70
+ `postinstall`/`sync-tui` step only refreshes TUI artifacts and is deliberately
71
+ outside this approval flow — no prompt is possible there.
55
72
  The bundled Python MCP servers use the pinned MCP SDK 2.2.0 and standalone
56
73
  FastMCP 4.0.10 runtime; see the [MCP guide](docs/MCP.md).
57
74
  The installer also turns each agent's `mcp_tools` allowlist into native
@@ -182,12 +199,17 @@ session/compaction hooks, so the 1.6 line is not full V2 feature parity.
182
199
  | V2 native subagent admission | OpenCode v2.0.29 | `tests/canary/run-v2-permission-proof.sh`; identity, root/child checks and permission-before-child ordering are recorded in [`bench/results/f4a-v229-admission-identity-2026-10-14.md`](bench/results/f4a-v229-admission-identity-2026-10-14.md) |
183
200
  | Exact 1.X host version the 1.6 line is exercised against end-to-end | **Unverified.** No V1 host leg exists: the sandbox runner is V2-only and `--run v1` is rejected | — |
184
201
 
185
- ## Delegation (native `task()`)
186
-
187
- Pantheon delegates exclusively through OpenCode's native `task()` child-session
188
- engine. The former custom `pantheon_delegate` tool and the V1 delegation engine
189
- (`delegation.ts`, `delegate-manager.ts` and supporting modules) were removed;
190
- there is no Pantheon-specific delegation tool surface to configure. See
202
+ ## Delegation (root `subagent`; host/API `task()`)
203
+
204
+ The host's child-session engine is referred to as native `task()` in API and
205
+ implementation documentation. In the user-reported Beta 10 Zeus-root runtime,
206
+ the callable dispatch tool was `subagent`; `task()` was not exposed as a root
207
+ tool. Treat the API name and examples below as host/API terminology, not a
208
+ guaranteed root-tool name or signature. The active runtime determines the
209
+ `subagent` schema, background options, and completion observability. The former
210
+ custom `pantheon_delegate` tool and the V1 delegation engine (`delegation.ts`,
211
+ `delegate-manager.ts` and supporting modules) were removed; there is no
212
+ Pantheon-specific delegation tool surface to configure. See
191
213
  [ADR-0011](.pantheon/memory-bank/adr/0011-delegation-engine-contract.md) for the
192
214
  historical engine contract.
193
215
 
@@ -210,8 +232,12 @@ global database. Both OpenCode services were running, so the 404 in the global
210
232
  log may come from the other instance; this remains an end-to-end verification
211
233
  item.
212
234
 
213
- Delegated tasks get at most one retry after an initial timeout or failure. If
214
- that retry fails, the configured fallback or escalation chain applies.
235
+ Delegated work gets at most one retry after an initial transient failure. If no
236
+ completion/status tool is exposed, prefer synchronous `subagent` dispatch or
237
+ wait for a host-provided completion notification; do not assume polling,
238
+ cancellation, job tracking, or board-based recovery. If the runtime does not
239
+ make completion observable, report that limitation. The configured fallback or
240
+ escalation chain still applies.
215
241
 
216
242
  ## Cost tool backend
217
243
 
@@ -230,6 +256,8 @@ available.
230
256
  |----------|---------|---------|
231
257
  | `PANTHEON_NODE` | `node` from `PATH` | Path to a Node.js >= 22.5 binary used by the CLI fallback; an explicit path must exist and be executable, otherwise the tool fails fast instead of ignoring the override |
232
258
 
259
+ Tool interpretation matters: `pantheon_model status` reports configuration, not the model serving a completion. V2 manual model overrides are project-only; global override files are neither read nor applied, and global mutation is unsupported until a future surface can obtain separate explicit human authorization. V2 ignores static presets and applies only explicit per-agent project model/variant overrides, leaving the host default unchanged otherwise; V1 preset behavior remains legacy pending compatibility decisions. `pantheon_cost` is a rolling token aggregate, not a session-level or monetary cost report. See the [OpenCode plugin tool notes](docs/mcp-tools.md#opencode-plugin-tools) for details and measurement limits.
260
+
233
261
 
234
262
  ## Code-mode execution (explicit opt-in)
235
263
 
@@ -550,23 +578,35 @@ Two freshness guarantees back it up:
550
578
  current package.
551
579
 
552
580
  `init` also gained `--components agents,skills,...` (narrow install),
553
- `--clean` (alias of `--force`), `--opencode-version auto`, atomic config
554
- writes with an `opencode.json.bak` backup, preflight checks for python3/npm
555
- before any file is written, and a non-fatal Python runtime: if the venv fails,
556
- the install completes but MCP entries are omitted (with a warning) instead of
557
- pointing at a broken interpreter. Installer messages auto-detect pt-BR via
558
- `LANG`/`LC_ALL`.
581
+ `--clean` (alias of `--force`), `--opencode-version auto`, preflight checks for
582
+ python3/npm before any file is written, and a non-fatal Python runtime: if the
583
+ venv fails, the install completes but MCP entries are omitted (with a warning)
584
+ instead of pointing at a broken interpreter. Before the first modifying write,
585
+ `init` preserves, byte-for-byte, every regular file it can modify or remove
586
+ inside the config dir (including files a `--clean` run deletes) under
587
+ `.pantheon/backups/<timestamp>/` (directory `0700`, files `0600`, kept 5 deep);
588
+ bulk directories
589
+ (`.venv`, the plugin's `node_modules`) and out-of-target global files
590
+ (`tui.json` copies elsewhere, the global `opencode-vision.json`) are listed in
591
+ the manifest as `notRevertible` rather than silently omitted. The exact-set
592
+ warning is printed once and never echoes file contents. Restore with
593
+ `pantheon-opencode restore [--project] [--yes]` (the old `opencode.json.bak`
594
+ sidecar is gone; the global vision config keeps its own `.bak`). Installer
595
+ messages auto-detect pt-BR via `LANG`/`LC_ALL`.
559
596
 
560
597
  ## Migrating to 1.5.x (from 1.4.x)
561
598
 
562
599
  1.5.0 removed the custom `pantheon_delegate` tool (and the V1 delegation
563
- engine) in favor of OpenCode's native `task()`; see
564
- [Delegation (native `task()`)](#delegation-native-task). Two things change on
600
+ engine) in favor of the host child-session engine; see
601
+ [Delegation (`subagent` / host API `task()`)](#delegation-root-subagent-hostapi-task). In the
602
+ reported Beta 10 runtime Zeus dispatches through `subagent`; `task()` remains
603
+ host/API terminology, not a guaranteed root tool. Two things change on
565
604
  an existing install:
566
605
 
567
606
  1. **The tool disappears from the plugin surface.** `pantheon_delegate` is no
568
607
  longer registered by `src/plugin.ts` or `src/plugin-v2.ts`. Agents now
569
- delegate through `task()` only — no configuration is needed.
608
+ delegate through the dispatch tool actually exposed by the active runtime —
609
+ no Pantheon-specific delegation tool configuration is needed.
570
610
  2. **A lockfile-pinned copy can keep the old tool alive.** `npm install` is
571
611
  lockfile-authoritative: a `package-lock.json` pinned to `1.4.1` (which
572
612
  satisfies `^1.4.1`) is never re-resolved, so a project's
@@ -839,13 +879,18 @@ with the other.
839
879
 
840
880
  ### Task-result guard
841
881
 
842
- A `task-result-guard` intercepts calls to the native `task()` tool where the
843
- child session returns an empty or missing result. Instead of surfacing a silent
882
+ A `task-result-guard` intercepts host/API child-session dispatch calls where the
883
+ child session returns an empty or missing result. In the reported Beta 10 Zeus
884
+ root, dispatch is exposed as `subagent`; host/API `task()` terminology does not
885
+ guarantee a root tool by that name. Instead of surfacing a silent
844
886
  `completed` with no payload (which confuses the orchestrator), the guard now
845
887
  raises an explicit error. This catches the common free-tier failure mode where a
846
888
  child session exceeds the uncached-prefill token budget (`BackendAdmissionRejected`)
847
- and returns nothing. Prefer `background=true` dispatches with an explicit
848
- `task_status(wait=true)` fan-in so large payloads are collected deterministically.
889
+ and returns nothing. Background dispatch and status/fan-in facilities are
890
+ runtime-dependent. The reported Beta 10 Zeus root did not expose `task_status`;
891
+ use synchronous `subagent` dispatch or wait for a host-provided completion
892
+ notification when no status tool is visible. Do not claim deterministic fan-in
893
+ or job recovery without an exposed, verified mechanism.
849
894
 
850
895
 
851
896
  ## Configuration (environment variables)
@@ -21,6 +21,8 @@ import os from 'node:os'
21
21
  import path from 'node:path'
22
22
  import { fileURLToPath } from 'node:url'
23
23
 
24
+ import { resolveInstallExitCode } from '../scripts/install/exit-codes.mjs'
25
+
24
26
  const __dirname = path.dirname(fileURLToPath(import.meta.url))
25
27
  const ROOT = path.resolve(__dirname, '..')
26
28
 
@@ -69,10 +71,24 @@ function printUsage() {
69
71
  console.log(' npx pantheon-opencode prune # List legacy artifacts (dry-run)')
70
72
  console.log(' npx pantheon-opencode prune --apply # Remove stale backups')
71
73
  console.log(' npx pantheon-opencode doctor [--profile] # Run health check')
74
+ console.log(
75
+ ' npx pantheon-opencode restore [--project] [--yes] # Restore an exact installer backup',
76
+ )
72
77
  console.log(' npx pantheon-opencode uninstall --project # Remove project artifacts safely')
73
78
  console.log(' npx pantheon-opencode uninstall --global # Remove global artifacts safely')
74
79
  console.log(' npx pantheon-opencode --version, -v # Print version and exit')
75
80
  console.log(' npx pantheon-opencode --help # Show this help')
81
+ console.log('')
82
+ console.log('Apply authorization:')
83
+ console.log(' A non-interactive run without --yes prints the plan and applies NOTHING.')
84
+ console.log(' Re-run with --yes to apply; an interactive TTY run prompts (default: no).')
85
+ console.log('')
86
+ console.log('Exit codes:')
87
+ console.log(' 0 success, or plan/no-apply (declined)')
88
+ console.log(' 1 failure')
89
+ console.log(' 2 apply was requested without authorization (non-interactive, no --yes)')
90
+ console.log(' 3 drift detected (a planned file changed after the plan)')
91
+ console.log(' 130 canceled')
76
92
  }
77
93
 
78
94
  async function main() {
@@ -109,6 +125,17 @@ async function main() {
109
125
  process.exit(result.status ?? 1)
110
126
  }
111
127
 
128
+ // restore delegates to scripts/restore.mjs (same spawnSync pattern as prune).
129
+ if (command === 'restore') {
130
+ const { spawnSync } = await import('node:child_process')
131
+ const restoreScript = path.join(ROOT, 'scripts', 'restore.mjs')
132
+ const result = spawnSync(process.execPath, [restoreScript, ...args.slice(1)], {
133
+ stdio: 'inherit',
134
+ cwd: process.cwd(),
135
+ })
136
+ process.exit(result.status ?? 1)
137
+ }
138
+
112
139
  // doctor delegates to scripts/doctor.mjs (same spawnSync pattern as prune).
113
140
  // Forwarding the FULL args (profile, target, --fix, ...) keeps the packaged
114
141
  // CLI a thin wrapper; the doctor banner reports the resolved profile.
@@ -414,10 +441,11 @@ async function main() {
414
441
  console.log('')
415
442
  }
416
443
 
444
+ let result
417
445
  try {
418
446
  const { installOpenCode } = await import('../scripts/install/opencode.mjs')
419
447
  const target = isProject ? process.cwd() : undefined
420
- await installOpenCode(target, isDryRun, forceReinstall, components, {
448
+ result = await installOpenCode(target, isDryRun, forceReinstall, components, {
421
449
  interactive: forceInteractive,
422
450
  headless: forceHeadless,
423
451
  yes: autoYes,
@@ -436,6 +464,21 @@ async function main() {
436
464
  process.exit(1)
437
465
  }
438
466
 
467
+ // Beta 11 Setup phases 3-4: a run that did NOT apply (declined /
468
+ // unauthorized / drift) must never print a success banner. Exit codes:
469
+ // 0 ok / no-apply · 2 apply-requested-without-authorization · 3 drift
470
+ // 130 canceled · 1 failure (thrown above).
471
+ if (result?.applied === false) {
472
+ if (!isDryRun) {
473
+ if (result.reason === 'canceled') console.error(S.canceled)
474
+ process.exit(resolveInstallExitCode(result, isDryRun))
475
+ }
476
+ // Dry-run preview: report honestly instead of the "installed!" banner.
477
+ console.log('')
478
+ console.log(` ${S.dryRunNothingWritten}`)
479
+ return
480
+ }
481
+
439
482
  if (runDoctor && !isDryRun) {
440
483
  console.log('')
441
484
  console.log(` ${S.runningHealthCheck}`)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pantheon-opencode",
3
- "version": "1.6.0-beta.10",
3
+ "version": "1.6.0-beta.11",
4
4
  "description": "Multi-agent orchestration for OpenCode — 14 specialized agents, TDD, Themis review gates.",
5
5
  "type": "module",
6
6
  "workspaces": [
@@ -18,12 +18,12 @@
18
18
  "scripts": {
19
19
  "postinstall": "node scripts/postinstall.mjs && node scripts/sync-tui.mjs",
20
20
  "prepare": "husky",
21
- "setup": "node bin/pantheon-init.mjs init",
21
+ "setup": "node bin/pantheon-init.mjs init --yes",
22
22
  "build:agents-md": "node scripts/build-agents-md.mjs",
23
- "setup:lite": "node bin/pantheon-init.mjs init --clean --components agents,skills,instructions",
23
+ "setup:lite": "node bin/pantheon-init.mjs init --yes --clean --components agents,skills,instructions",
24
24
  "doctor": "node scripts/doctor.mjs",
25
25
  "prune": "node scripts/prune.mjs",
26
- "init": "node bin/pantheon-init.mjs init",
26
+ "init": "node bin/pantheon-init.mjs init --yes",
27
27
  "opencode": "OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS=true opencode",
28
28
  "start": "OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS=true opencode",
29
29
  "prepublishOnly": "node scripts/validate-routing.mjs",
package/plugin.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pantheon",
3
3
  "publisher": "ils15",
4
- "version": "1.6.0-beta.10",
4
+ "version": "1.6.0-beta.11",
5
5
  "displayName": "Pantheon",
6
6
  "description": "Conductor-Delegate orchestration platform with 14 specialized agents, 20 skills, and OpenCode support.",
7
7
  "agents": "./src/agents",
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.10"
7
+ version = "1.6.0-beta.11"
8
8
  description = "Multi-agent orchestration framework"
9
9
  requires-python = ">=3.11"
10
10
  dependencies = []
@@ -36,6 +36,8 @@ import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSy
36
36
  import { dirname, join, resolve } from 'node:path'
37
37
  import { fileURLToPath } from 'node:url'
38
38
 
39
+ import { ACTIONS, isRecording, recordChange } from './install/plan.mjs'
40
+
39
41
  const __dirname = dirname(fileURLToPath(import.meta.url))
40
42
  export const ROOT = join(__dirname, '..')
41
43
  const INSTRUCTIONS_DIR = join(ROOT, 'src', 'instructions')
@@ -323,16 +325,26 @@ export function generateProjectAgentsMd(git = readGitInfo()) {
323
325
  * @param {boolean} [dryRun=false] - Do not write when true
324
326
  * @returns {'created' | 'skipped' | 'updated' | 'upgraded' | 'merged'}
325
327
  */
326
- export function writeAgentsMdManaged(filePath, generated, dryRun = false) {
328
+ export function writeAgentsMdManaged(filePath, generated, dryRun = false, recorder = null) {
327
329
  const existing = existsSync(filePath) ? readFileSync(filePath, 'utf8') : null
328
330
  if (existing === null) {
329
331
  if (!dryRun) {
330
332
  mkdirSync(dirname(filePath), { recursive: true })
331
333
  writeFileSync(filePath, generated, 'utf8')
332
334
  }
335
+ recordChange(
336
+ { path: filePath, action: ACTIONS.CREATE, bytes: Buffer.byteLength(generated, 'utf8') },
337
+ recorder,
338
+ )
333
339
  return 'created'
334
340
  }
335
- if (existing === generated) return 'skipped'
341
+ if (existing === generated) {
342
+ recordChange(
343
+ { path: filePath, action: ACTIONS.UNCHANGED, bytes: Buffer.byteLength(existing, 'utf8') },
344
+ recorder,
345
+ )
346
+ return 'skipped'
347
+ }
336
348
 
337
349
  const begin = existing.indexOf(MANAGED_BEGIN)
338
350
  const end = existing.indexOf(MANAGED_END)
@@ -340,22 +352,41 @@ export function writeAgentsMdManaged(filePath, generated, dryRun = false) {
340
352
  const before = existing.slice(0, begin)
341
353
  const after = existing.slice(end + MANAGED_END.length)
342
354
  const next = `${before}${generated}${after}`
343
- if (next === existing) return 'skipped'
355
+ if (next === existing) {
356
+ recordChange(
357
+ { path: filePath, action: ACTIONS.UNCHANGED, bytes: Buffer.byteLength(existing, 'utf8') },
358
+ recorder,
359
+ )
360
+ return 'skipped'
361
+ }
344
362
  if (!dryRun) writeFileSync(filePath, next, 'utf8')
363
+ recordChange(
364
+ { path: filePath, action: ACTIONS.UPDATE, bytes: Buffer.byteLength(next, 'utf8') },
365
+ recorder,
366
+ )
345
367
  return before.trim() === '' && after.trim() === '' ? 'updated' : 'merged'
346
368
  }
347
369
 
348
370
  if (existing.startsWith(GENERATED_BY)) {
349
371
  // Legacy format: the header declares the whole file machine-generated.
350
372
  if (!dryRun) writeFileSync(filePath, generated, 'utf8')
373
+ recordChange(
374
+ { path: filePath, action: ACTIONS.UPDATE, bytes: Buffer.byteLength(generated, 'utf8') },
375
+ recorder,
376
+ )
351
377
  return 'upgraded'
352
378
  }
353
379
 
354
380
  // Foreign content: preserve it verbatim and append the managed region.
381
+ const separator = existing.endsWith('\n') ? '\n' : '\n\n'
382
+ const payload = `${existing}${separator}${generated}`
355
383
  if (!dryRun) {
356
- const separator = existing.endsWith('\n') ? '\n' : '\n\n'
357
- writeFileSync(filePath, `${existing}${separator}${generated}`, 'utf8')
384
+ writeFileSync(filePath, payload, 'utf8')
358
385
  }
386
+ recordChange(
387
+ { path: filePath, action: ACTIONS.UPDATE, bytes: Buffer.byteLength(payload, 'utf8') },
388
+ recorder,
389
+ )
359
390
  return 'merged'
360
391
  }
361
392
 
@@ -423,9 +454,16 @@ export function generateAgentPrompt(agentName, prompt, instructions = readInstru
423
454
  * @param {string} targetDir - OpenCode agents directory
424
455
  * @param {boolean} dryRun - Do not write when true
425
456
  * @param {boolean} existingOnly - Refresh installed prompts without adding missing agents
457
+ * @param {(change: object) => void} [recorder] - optional change recorder for --dry-run preview
426
458
  * @returns {{ created: number, skipped: number }}
427
459
  */
428
- export function copyAgentPrompts(sourceDir, targetDir, dryRun = false, existingOnly = false) {
460
+ export function copyAgentPrompts(
461
+ sourceDir,
462
+ targetDir,
463
+ dryRun = false,
464
+ existingOnly = false,
465
+ recorder = null,
466
+ ) {
429
467
  const instructions = readInstructions()
430
468
  if (!dryRun && !existingOnly) mkdirSync(targetDir, { recursive: true })
431
469
  let created = 0
@@ -443,9 +481,25 @@ export function copyAgentPrompts(sourceDir, targetDir, dryRun = false, existingO
443
481
  const existing = existsSync(targetPath) ? readFileSync(targetPath, 'utf8') : null
444
482
  if (existing === output) {
445
483
  skipped++
484
+ if (isRecording(recorder)) {
485
+ recordChange(
486
+ { path: targetPath, action: ACTIONS.UNCHANGED, bytes: Buffer.byteLength(output, 'utf8') },
487
+ recorder,
488
+ )
489
+ }
446
490
  } else {
447
491
  if (!dryRun) writeFileSync(targetPath, output, 'utf8')
448
492
  created++
493
+ if (isRecording(recorder)) {
494
+ recordChange(
495
+ {
496
+ path: targetPath,
497
+ action: existing === null ? ACTIONS.CREATE : ACTIONS.UPDATE,
498
+ bytes: Buffer.byteLength(output, 'utf8'),
499
+ },
500
+ recorder,
501
+ )
502
+ }
449
503
  }
450
504
  }
451
505
  return { created, skipped }