pantheon-opencode 1.5.0-beta.3 → 1.5.0-beta.5
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/.pantheon/code-mode/manifest.json +21 -0
- package/CHANGELOG.md +871 -0
- package/README.md +101 -1
- package/README.pt-BR.md +55 -1
- package/bin/pantheon-init.mjs +178 -21
- package/package.json +14 -10
- package/plugin.json +1 -1
- package/pyproject.toml +1 -1
- package/scripts/code_mode_server.py +339 -28
- package/scripts/doctor.mjs +178 -9
- package/scripts/install/cli-ui.mjs +17 -11
- package/scripts/install/config-migration.mjs +4 -6
- package/scripts/install/opencode.mjs +116 -35
- package/scripts/install/shared.mjs +43 -2
- package/scripts/install/strings.mjs +129 -0
- package/scripts/install-mcp.mjs +1 -0
- package/scripts/probe-pantheon-cost.mjs +10 -1
- package/scripts/release-beta-version.mjs +2 -7
- package/scripts/sync-artifacts.mjs +169 -0
- package/scripts/sync-tui.mjs +124 -34
- package/src/agents/zeus.md +2 -2
- package/src/mcp/code_mode_server.py +339 -28
- package/src/pantheon/auto-wake.ts +1 -5
- package/src/pantheon/background-job-board.ts +14 -0
- package/src/pantheon/cost-command.ts +62 -42
- package/src/pantheon/delegate-manager.ts +109 -12
- package/src/pantheon/native-probe.ts +234 -0
- package/src/pantheon/native-task-status.ts +139 -0
- package/src/pantheon/shared-board.ts +37 -0
- package/src/pantheon/task-result-guard.ts +107 -0
- package/src/pantheon/token-opt.ts +18 -110
- package/src/pantheon/tool-ceiling.ts +159 -0
- package/src/pantheon/v2-events.ts +10 -1
- package/src/plugin.ts +25 -9
- package/src/plugins/pantheon-hooks.ts +51 -0
- package/src/plugins/tui/dist/server.d.ts +1 -1
- package/src/plugins/tui/dist/server.d.ts.map +1 -1
- package/src/plugins/tui/dist/server.js +1 -1
- package/src/plugins/tui/dist/server.js.map +1 -1
- package/src/plugins/tui/dist/tui.d.ts +1 -1
- package/src/plugins/tui/dist/tui.js +1 -1
- package/src/plugins/tui/dist/tui.js.map +1 -1
- package/src/plugins/tui/package-lock.json +2 -2
- package/src/plugins/tui/package.json +1 -1
package/README.md
CHANGED
|
@@ -30,7 +30,7 @@ way to plan work, make progress, check results, and keep useful project context.
|
|
|
30
30
|
|
|
31
31
|
## Start in 2 minutes
|
|
32
32
|
|
|
33
|
-
Requirements: [OpenCode 1.18.4+](https://opencode.ai/docs/) and Node.js
|
|
33
|
+
Requirements: [OpenCode 1.18.4+](https://opencode.ai/docs/) and Node.js 22+.
|
|
34
34
|
|
|
35
35
|
From the project where you want to use Pantheon:
|
|
36
36
|
|
|
@@ -76,6 +76,65 @@ availability and configuration of OpenCode and any optional services you choose
|
|
|
76
76
|
to use. Check the [releases](https://github.com/ils15/pantheon-opencode/releases)
|
|
77
77
|
and [changelog](CHANGELOG.md) for the latest published changes.
|
|
78
78
|
|
|
79
|
+
## Native delegation mode
|
|
80
|
+
|
|
81
|
+
Set `PANTHEON_DELEGATE_MODE=native` to use OpenCode's native child-session
|
|
82
|
+
delegation. Native mode is strict: it does not use foreground fallback, model
|
|
83
|
+
failover, or retry; it performs a lazy capability probe on the first dispatch,
|
|
84
|
+
and includes the resulting status in the delegation receipt.
|
|
85
|
+
|
|
86
|
+
## Cost tool backend
|
|
87
|
+
|
|
88
|
+
`pantheon_cost` has one backend: read-only `node:sqlite` against the selected
|
|
89
|
+
`opencode.db`. On Node versions without `node:sqlite` (Node < 22.5), the tool
|
|
90
|
+
returns `status: UNSUPPORTED`; it has no CLI fallback. Database failures return
|
|
91
|
+
a contract status such as `UNAVAILABLE` or
|
|
92
|
+
`CORRUPT_DATA` and include diagnostic detail, including captured stderr when
|
|
93
|
+
available.
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
## Code-mode execution (explicit opt-in)
|
|
97
|
+
|
|
98
|
+
Scripts run through the `pantheon-code-mode` MCP server (`execute_code_script`)
|
|
99
|
+
only when they are explicitly approved. Approval is recorded in
|
|
100
|
+
`.pantheon/code-mode/manifest.json` with the SHA-256 of each script:
|
|
101
|
+
|
|
102
|
+
```json
|
|
103
|
+
{
|
|
104
|
+
"version": 1,
|
|
105
|
+
"scripts": {
|
|
106
|
+
"example-sync.sh": "<sha256>"
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
- **No manifest → nothing executes.** A missing manifest returns
|
|
112
|
+
`INVALID_STATE`; scripts are never run implicitly.
|
|
113
|
+
- **Not listed → `CONFLICT`.** The script must be approved first.
|
|
114
|
+
- **Hash mismatch → `CORRUPT_DATA`.** The SHA-256 of the file on disk must
|
|
115
|
+
match the manifest entry, so edits after approval are detected.
|
|
116
|
+
|
|
117
|
+
Approve or re-approve a script with the `approve_code_script` MCP tool:
|
|
118
|
+
|
|
119
|
+
```
|
|
120
|
+
approve_code_script("checkpoint-session.sh")
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
The installer seeds the manifest with the SHA-256 of every bundled script, so
|
|
124
|
+
shipped scripts stay approved across installs. Re-run `approve_code_script`
|
|
125
|
+
after intentionally editing a script. Status codes follow the shared nine-code
|
|
126
|
+
contract (`OK`, `UNSUPPORTED`, `UNAVAILABLE`, `INVALID_INPUT`, `INVALID_STATE`,
|
|
127
|
+
`CONFLICT`, `CORRUPT_DATA`, `TIMEOUT`, `ESCALATE`); with `json_output=true`,
|
|
128
|
+
results carry a `status` field.
|
|
129
|
+
|
|
130
|
+
Code-mode resolves project-first: `.opencode/.pantheon/code-mode` is preferred,
|
|
131
|
+
then `.pantheon/code-mode`. `PANTHEON_PROJECT` overrides the working directory,
|
|
132
|
+
and the installed MCP uses `cwd: "."` so OpenCode supplies the workspace. A
|
|
133
|
+
global directory is used only when no usable project directory is available;
|
|
134
|
+
once a project directory is selected, a missing or corrupt manifest fails
|
|
135
|
+
closed instead of falling back. `doctor` validates the manifest and every
|
|
136
|
+
script's SHA-256 without regenerating it.
|
|
137
|
+
|
|
79
138
|
|
|
80
139
|
## What's new in 1.5.0-beta.2
|
|
81
140
|
|
|
@@ -154,6 +213,36 @@ The installer still writes the compatibility settings required by the selected
|
|
|
154
213
|
OpenCode host, such as `experimental.subagent_depth`; this does not convert a
|
|
155
214
|
V1 plugin into V2 or provide V2 with V1 hooks.
|
|
156
215
|
|
|
216
|
+
## Updating between releases (beta.5+)
|
|
217
|
+
|
|
218
|
+
One command keeps an existing installation current:
|
|
219
|
+
|
|
220
|
+
```bash
|
|
221
|
+
npx pantheon-opencode update # npm beta channel + config refresh
|
|
222
|
+
npx pantheon-opencode update --stable # stable channel instead
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
`update` compares your installed version with the npm dist-tag, runs
|
|
226
|
+
`npm install -g pantheon-opencode@<channel>`, then re-runs `init --yes
|
|
227
|
+
--headless` so config merges, the venv and MCP entries match the new package.
|
|
228
|
+
Two freshness guarantees back it up:
|
|
229
|
+
|
|
230
|
+
- **Postinstall artifact sync** — after any `npm install`, copy-only artifacts
|
|
231
|
+
(agents, skills, AGENTS.md, commands, MCP scripts, code-mode payload) are
|
|
232
|
+
refreshed into the existing config dir automatically; you never have to
|
|
233
|
+
re-run `init` just for file copies.
|
|
234
|
+
- **Drift detection** — the installer stamps the installed version in
|
|
235
|
+
`.pantheon/install-state.json` and `doctor` warns when the package is newer
|
|
236
|
+
than the last sync, pointing at `update`.
|
|
237
|
+
|
|
238
|
+
`init` also gained `--components agents,skills,...` (narrow install),
|
|
239
|
+
`--clean` (alias of `--force`), `--opencode-version auto`, atomic config
|
|
240
|
+
writes with an `opencode.json.bak` backup, preflight checks for python3/npm
|
|
241
|
+
before any file is written, and a non-fatal Python runtime: if the venv fails,
|
|
242
|
+
the install completes but MCP entries are omitted (with a warning) instead of
|
|
243
|
+
pointing at a broken interpreter. Installer messages auto-detect pt-BR via
|
|
244
|
+
`LANG`/`LC_ALL`.
|
|
245
|
+
|
|
157
246
|
## Releases
|
|
158
247
|
|
|
159
248
|
Publication is authorized **only** by an explicit `workflow_dispatch` of the
|
|
@@ -218,6 +307,17 @@ optional codemap schema and `code_index`, `code_query`, and `code_neighbors`.
|
|
|
218
307
|
The other shared MCP copies remain identical. Do not overwrite one memory copy
|
|
219
308
|
with the other.
|
|
220
309
|
|
|
310
|
+
### Task-result guard
|
|
311
|
+
|
|
312
|
+
A `task-result-guard` intercepts calls to the native `task()` tool where the
|
|
313
|
+
child session returns an empty or missing result. Instead of surfacing a silent
|
|
314
|
+
`completed` with no payload (which confuses the orchestrator), the guard now
|
|
315
|
+
raises an explicit error. This catches the common free-tier failure mode where a
|
|
316
|
+
child session exceeds the uncached-prefill token budget (`BackendAdmissionRejected`)
|
|
317
|
+
and returns nothing. For large payloads, prefer `pantheon_delegate` over native
|
|
318
|
+
`task()` — the delegation layer has better timeout and error handling.
|
|
319
|
+
|
|
320
|
+
|
|
221
321
|
## Documentation
|
|
222
322
|
|
|
223
323
|
- [Installation](docs/INSTALLATION.md) · [Quick start](docs/QUICKSTART.md)
|
package/README.pt-BR.md
CHANGED
|
@@ -78,6 +78,31 @@ escolher. Veja as [releases](https://github.com/ils15/pantheon-opencode/releases
|
|
|
78
78
|
e o [changelog](CHANGELOG.md) para acompanhar as mudanças publicadas.
|
|
79
79
|
|
|
80
80
|
|
|
81
|
+
## Execução code-mode (opt-in explícito)
|
|
82
|
+
|
|
83
|
+
Scripts executados pelo MCP server `pantheon-code-mode` (`execute_code_script`)
|
|
84
|
+
só rodam quando aprovados explicitamente. A aprovação fica em
|
|
85
|
+
`.pantheon/code-mode/manifest.json` com o SHA-256 de cada script.
|
|
86
|
+
|
|
87
|
+
- **Sem manifest → nada executa** (`INVALID_STATE`); nunca há execução implícita.
|
|
88
|
+
- **Script fora do manifest → `CONFLICT`**; é preciso aprovar antes.
|
|
89
|
+
- **Hash divergente → `CORRUPT_DATA`**; edições após a aprovação são detectadas.
|
|
90
|
+
|
|
91
|
+
Aprove (ou reaprove) um script com a tool `approve_code_script`. O instalador
|
|
92
|
+
semeia o manifest com o hash de todos os scripts empacotados. Os resultados
|
|
93
|
+
seguem o contrato de nove códigos (`OK`, `UNSUPPORTED`, `UNAVAILABLE`,
|
|
94
|
+
`INVALID_INPUT`, `INVALID_STATE`, `CONFLICT`, `CORRUPT_DATA`, `TIMEOUT`,
|
|
95
|
+
`ESCALATE`) e, com `json_output=true`, incluem o campo `status`.
|
|
96
|
+
|
|
97
|
+
O code-mode resolve primeiro o projeto: `.opencode/.pantheon/code-mode` vem
|
|
98
|
+
antes de `.pantheon/code-mode`. `PANTHEON_PROJECT` sobrescreve o diretório de
|
|
99
|
+
trabalho, e o MCP instalado usa `cwd: "."` para receber o workspace do
|
|
100
|
+
OpenCode. O diretório global só é usado sem um diretório de projeto utilizável;
|
|
101
|
+
depois de selecionar um diretório de projeto, manifest ausente ou corrompido
|
|
102
|
+
falha fechado, sem fallback silencioso. O `doctor` valida o manifest e o
|
|
103
|
+
SHA-256 de cada script sem regenerá-lo.
|
|
104
|
+
|
|
105
|
+
|
|
81
106
|
## Novidades da 1.5.0-beta.2
|
|
82
107
|
|
|
83
108
|
- Instalador exclusivo para OpenCode: guias de plataformas consolidados em um
|
|
@@ -142,7 +167,7 @@ instalador remove referências Pantheon das duas formas de config antes de
|
|
|
142
167
|
gravar apenas o registro Pantheon selecionado. Entradas de terceiros não são
|
|
143
168
|
convertidas nem reivindicadas por essa regra.
|
|
144
169
|
|
|
145
|
-
O relatório `pantheon_cost
|
|
170
|
+
O relatório `pantheon_cost`, disponível nos plugins V1 e V2, pode selecionar o banco com
|
|
146
171
|
`PANTHEON_OPENCODE_VERSION=v1` ou `v2` (`opencode.db` ou `opencode-v2.db`).
|
|
147
172
|
`PANTHEON_COST_DB=/caminho/absoluto/para/opencode.db` tem precedência sobre o
|
|
148
173
|
seletor de versão, e um `dbPath` explícito fornecido pelo chamador da
|
|
@@ -154,6 +179,35 @@ O instalador continua gravando as configurações de compatibilidade exigidas
|
|
|
154
179
|
pelo host OpenCode selecionado, como `experimental.subagent_depth`; isso não
|
|
155
180
|
converte um plugin V1 em V2 nem dá hooks V1 ao V2.
|
|
156
181
|
|
|
182
|
+
## Atualizando entre releases (beta.5+)
|
|
183
|
+
|
|
184
|
+
Um comando mantém uma instalação existente em dia:
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
npx pantheon-opencode update # canal beta do npm + refresh do config
|
|
188
|
+
npx pantheon-opencode update --stable # canal estável
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
O `update` compara a versão instalada com o dist-tag do npm, roda
|
|
192
|
+
`npm install -g pantheon-opencode@<canal>` e re-executa `init --yes
|
|
193
|
+
--headless` para alinhar merge de config, venv e entradas de MCP com o novo
|
|
194
|
+
pacote. Duas garantias de freshness sustentam isso:
|
|
195
|
+
|
|
196
|
+
- **Sync de artifacts no postinstall** — depois de qualquer `npm install`, os
|
|
197
|
+
artefatos de cópia (agents, skills, AGENTS.md, commands, scripts MCP,
|
|
198
|
+
payload code-mode) são atualizados automaticamente no config dir existente;
|
|
199
|
+
você nunca precisa re-rodar `init` só para atualizar arquivos.
|
|
200
|
+
- **Detecção de drift** — o instalador grava a versão instalada em
|
|
201
|
+
`.pantheon/install-state.json` e o `doctor` avisa quando o pacote é mais
|
|
202
|
+
novo que o último sync, apontando para o `update`.
|
|
203
|
+
|
|
204
|
+
O `init` também ganhou `--components agents,skills,...` (instalação enxuta),
|
|
205
|
+
`--clean` (alias de `--force`), `--opencode-version auto`, escrita atômica do
|
|
206
|
+
config com backup em `opencode.json.bak`, pré-checagens de python3/npm antes
|
|
207
|
+
de escrever qualquer arquivo e runtime Python não-fatal: se a venv falha, a
|
|
208
|
+
instalação completa mas as entradas MCP ficam de fora (com aviso) em vez de
|
|
209
|
+
apontar para um interpretador quebrado.
|
|
210
|
+
|
|
157
211
|
## Releases
|
|
158
212
|
|
|
159
213
|
A publicação é autorizada **somente** por um `workflow_dispatch` explícito do
|
package/bin/pantheon-init.mjs
CHANGED
|
@@ -22,9 +22,10 @@ import { fileURLToPath } from 'node:url'
|
|
|
22
22
|
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
23
23
|
const ROOT = path.resolve(__dirname, '..')
|
|
24
24
|
|
|
25
|
-
const REQUIRED_NODE_MAJOR =
|
|
25
|
+
const REQUIRED_NODE_MAJOR = 22
|
|
26
26
|
if (parseInt(process.versions.node.split('.')[0], 10) < REQUIRED_NODE_MAJOR) {
|
|
27
27
|
console.error(`❌ Node.js >= ${REQUIRED_NODE_MAJOR} required (current: ${process.versions.node})`)
|
|
28
|
+
console.error(' Install a newer Node via nvm: https://github.com/nvm-sh/nvm')
|
|
28
29
|
process.exit(1)
|
|
29
30
|
}
|
|
30
31
|
|
|
@@ -50,9 +51,13 @@ function printUsage() {
|
|
|
50
51
|
console.log(' npx pantheon-opencode init --interactive # Force interactive TUI mode')
|
|
51
52
|
console.log(' npx pantheon-opencode init --headless # Force non-interactive mode')
|
|
52
53
|
console.log(' npx pantheon-opencode init -y # Skip confirmations, use defaults')
|
|
54
|
+
console.log(' npx pantheon-opencode init --components agents,skills,instructions')
|
|
53
55
|
console.log(' npx pantheon-opencode init --opencode-version v1|v2|auto')
|
|
54
56
|
console.log(' npx pantheon-opencode init --preset <name> # Install and activate model preset')
|
|
55
57
|
console.log(' npx pantheon-opencode set-tier <name> # Set active model preset (global)')
|
|
58
|
+
console.log(
|
|
59
|
+
' npx pantheon-opencode update [--stable] # Update package + re-run init (default: beta channel)',
|
|
60
|
+
)
|
|
56
61
|
console.log(
|
|
57
62
|
' npx pantheon-opencode set-tier <name> --project # Set active model preset (project)',
|
|
58
63
|
)
|
|
@@ -112,6 +117,78 @@ async function main() {
|
|
|
112
117
|
process.exit(result.status ?? 1)
|
|
113
118
|
}
|
|
114
119
|
|
|
120
|
+
// beta.5: one-command update — check npm dist-tag, install, re-init.
|
|
121
|
+
// Default channel is `beta` (the prerelease line); `--stable` uses `latest`.
|
|
122
|
+
if (command === 'update') {
|
|
123
|
+
const channel = args.includes('--stable') ? 'latest' : 'beta'
|
|
124
|
+
const current = readVersion()
|
|
125
|
+
const { spawnSync } = await import('node:child_process')
|
|
126
|
+
const _S = (await import('../scripts/install/strings.mjs')).strings()
|
|
127
|
+
|
|
128
|
+
console.log(`Pantheon OpenCode v${current} — checking the "${channel}" channel...`)
|
|
129
|
+
const view = spawnSync('npm', ['view', `pantheon-opencode@${channel}`, 'version'], {
|
|
130
|
+
encoding: 'utf8',
|
|
131
|
+
timeout: 30_000,
|
|
132
|
+
})
|
|
133
|
+
const latest = (view.stdout || '').trim().split('\n').filter(Boolean).pop()
|
|
134
|
+
if (view.status !== 0 || !latest) {
|
|
135
|
+
console.error(
|
|
136
|
+
`❌ Could not reach the npm registry: ${(view.stderr || view.stdout || '').trim() || 'no version returned'}`,
|
|
137
|
+
)
|
|
138
|
+
process.exit(1)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Prerelease-aware compare: core (major.minor.patch) first, then the
|
|
142
|
+
// beta counter; a stable release outranks any beta of the same core.
|
|
143
|
+
const parsePantheonVersion = (v) => {
|
|
144
|
+
const m = String(v)
|
|
145
|
+
.trim()
|
|
146
|
+
.match(/^(\d+)\.(\d+)\.(\d+)(?:-beta\.(\d+))?$/)
|
|
147
|
+
if (!m) return null
|
|
148
|
+
return {
|
|
149
|
+
core: [+m[1], +m[2], +m[3]],
|
|
150
|
+
beta: m[4] === undefined ? Number.POSITIVE_INFINITY : +m[4],
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
const cmp = (a, b) => {
|
|
154
|
+
const pa = parsePantheonVersion(a)
|
|
155
|
+
const pb = parsePantheonVersion(b)
|
|
156
|
+
if (!pa || !pb) return 0
|
|
157
|
+
for (let i = 0; i < 3; i++) {
|
|
158
|
+
if (pa.core[i] !== pb.core[i]) return pa.core[i] - pb.core[i]
|
|
159
|
+
}
|
|
160
|
+
return pa.beta - pb.beta
|
|
161
|
+
}
|
|
162
|
+
if (cmp(latest, current) <= 0) {
|
|
163
|
+
console.log(`✅ Already up to date (installed v${current}; ${channel} channel: v${latest}).`)
|
|
164
|
+
return
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
console.log(`Updating v${current} → v${latest} (channel: ${channel})...`)
|
|
168
|
+
const install = spawnSync('npm', ['install', '-g', `pantheon-opencode@${channel}`], {
|
|
169
|
+
stdio: 'inherit',
|
|
170
|
+
})
|
|
171
|
+
if (install.status !== 0) {
|
|
172
|
+
console.error('❌ npm install failed — the previous installation is untouched.')
|
|
173
|
+
process.exit(install.status ?? 1)
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Refresh config/venv/MCP entries with the NEW package (resolved via
|
|
177
|
+
// PATH — the freshly installed global bin).
|
|
178
|
+
const init = spawnSync('pantheon-opencode', ['init', '--yes', '--headless'], {
|
|
179
|
+
stdio: 'inherit',
|
|
180
|
+
shell: process.platform === 'win32',
|
|
181
|
+
})
|
|
182
|
+
if (init.status !== 0 || init.error) {
|
|
183
|
+
console.error(
|
|
184
|
+
'⚠️ Updated, but the config refresh did not complete — run `npx pantheon-opencode init` manually.',
|
|
185
|
+
)
|
|
186
|
+
process.exit(init.status ?? 1)
|
|
187
|
+
}
|
|
188
|
+
console.log(`✅ Updated to v${latest}.`)
|
|
189
|
+
return
|
|
190
|
+
}
|
|
191
|
+
|
|
115
192
|
if (args.includes('--help')) {
|
|
116
193
|
printUsage()
|
|
117
194
|
process.exit(0)
|
|
@@ -195,11 +272,49 @@ async function main() {
|
|
|
195
272
|
const isProject = args.includes('--project')
|
|
196
273
|
const isDryRun = args.includes('--dry-run')
|
|
197
274
|
const skipMCP = args.includes('--no-mcp')
|
|
198
|
-
|
|
275
|
+
// --clean is an alias of --force (both wipe component dirs + recreate venv).
|
|
276
|
+
const forceReinstall = args.includes('--force') || args.includes('--clean')
|
|
199
277
|
const runDoctor = args.includes('--doctor')
|
|
200
278
|
const forceInteractive = args.includes('--interactive')
|
|
201
279
|
const forceHeadless = args.includes('--headless')
|
|
202
280
|
const autoYes = args.includes('--yes') || args.includes('-y')
|
|
281
|
+
|
|
282
|
+
// beta.5: warn on unrecognized flags instead of silently ignoring them.
|
|
283
|
+
const VALUE_FLAGS = new Set([
|
|
284
|
+
'--preset',
|
|
285
|
+
'--model',
|
|
286
|
+
'--small-model',
|
|
287
|
+
'--version',
|
|
288
|
+
'--opencode-version',
|
|
289
|
+
'--components',
|
|
290
|
+
])
|
|
291
|
+
for (let i = 1; i < args.length; i++) {
|
|
292
|
+
const arg = args[i]
|
|
293
|
+
if (!arg.startsWith('--') || arg === '--help') continue
|
|
294
|
+
if (VALUE_FLAGS.has(arg)) {
|
|
295
|
+
i++ // skip the flag's value
|
|
296
|
+
continue
|
|
297
|
+
}
|
|
298
|
+
const bare = arg.split('=', 1)[0]
|
|
299
|
+
if (
|
|
300
|
+
!VALUE_FLAGS.has(bare) &&
|
|
301
|
+
![
|
|
302
|
+
'--project',
|
|
303
|
+
'--dry-run',
|
|
304
|
+
'--no-mcp',
|
|
305
|
+
'--force',
|
|
306
|
+
'--clean',
|
|
307
|
+
'--doctor',
|
|
308
|
+
'--interactive',
|
|
309
|
+
'--headless',
|
|
310
|
+
'--yes',
|
|
311
|
+
'-y',
|
|
312
|
+
].includes(bare)
|
|
313
|
+
) {
|
|
314
|
+
console.error(`⚠️ Unknown option ignored: ${arg}`)
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
203
318
|
const presetIndex = args.indexOf('--preset')
|
|
204
319
|
const presetOpt = presetIndex >= 0 ? (args[presetIndex + 1] ?? null) : null
|
|
205
320
|
// Explicit top-level model overrides for the generated config.
|
|
@@ -207,8 +322,9 @@ async function main() {
|
|
|
207
322
|
const modelOpt = modelIndex >= 0 ? (args[modelIndex + 1] ?? null) : null
|
|
208
323
|
const smallModelIndex = args.indexOf('--small-model')
|
|
209
324
|
const smallModelOpt = smallModelIndex >= 0 ? (args[smallModelIndex + 1] ?? null) : null
|
|
210
|
-
// --version v1|v2 labels the install target
|
|
211
|
-
//
|
|
325
|
+
// --version v1|v2|auto labels the install target. `auto` resolves the
|
|
326
|
+
// generation from the opencode binary at install time (see
|
|
327
|
+
// scripts/install/opencode-version.mjs). Invalid values fail fast.
|
|
212
328
|
const versionIndex = args.indexOf('--version')
|
|
213
329
|
const legacyVersionIndex = args.indexOf('--opencode-version')
|
|
214
330
|
const inlineVersion = args.find((arg) => arg.startsWith('--opencode-version='))
|
|
@@ -218,20 +334,61 @@ async function main() {
|
|
|
218
334
|
: legacyVersionIndex >= 0
|
|
219
335
|
? (args[legacyVersionIndex + 1] ?? null)
|
|
220
336
|
: (inlineVersion?.split('=', 2)[1] ?? null)
|
|
221
|
-
if (
|
|
222
|
-
|
|
337
|
+
if (
|
|
338
|
+
versionOpt !== null &&
|
|
339
|
+
versionOpt !== 'v1' &&
|
|
340
|
+
versionOpt !== 'v2' &&
|
|
341
|
+
versionOpt !== 'auto'
|
|
342
|
+
) {
|
|
343
|
+
console.error(`❌ Invalid --version "${versionOpt}" — expected v1, v2 or auto`)
|
|
223
344
|
process.exit(1)
|
|
224
345
|
}
|
|
225
346
|
|
|
226
|
-
|
|
227
|
-
|
|
347
|
+
// --components agents,skills,... narrows the install; --no-mcp drops the
|
|
348
|
+
// runtime component. Unknown component names fail fast (typo protection).
|
|
349
|
+
const KNOWN_COMPONENTS = ['agents', 'skills', 'instructions', 'commands', 'plugins', 'runtime']
|
|
350
|
+
const componentsIndex = args.indexOf('--components')
|
|
351
|
+
const inlineComponents = args.find((arg) => arg.startsWith('--components='))
|
|
352
|
+
const componentsOpt =
|
|
353
|
+
componentsIndex >= 0
|
|
354
|
+
? (args[componentsIndex + 1] ?? null)
|
|
355
|
+
: (inlineComponents?.split('=', 2)[1] ?? null)
|
|
356
|
+
let requestedComponents = null
|
|
357
|
+
if (componentsOpt !== null) {
|
|
358
|
+
requestedComponents = componentsOpt
|
|
359
|
+
.split(',')
|
|
360
|
+
.map((c) => c.trim())
|
|
361
|
+
.filter(Boolean)
|
|
362
|
+
const unknown = requestedComponents.filter((c) => !KNOWN_COMPONENTS.includes(c))
|
|
363
|
+
if (requestedComponents.length === 0 || unknown.length > 0) {
|
|
364
|
+
console.error(
|
|
365
|
+
`❌ Invalid --components "${componentsOpt}" — expected comma-separated names from: ${KNOWN_COMPONENTS.join(', ')}`,
|
|
366
|
+
)
|
|
367
|
+
process.exit(1)
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
const components = requestedComponents ?? [
|
|
372
|
+
'agents',
|
|
373
|
+
'skills',
|
|
374
|
+
'instructions',
|
|
375
|
+
'commands',
|
|
376
|
+
'plugins',
|
|
377
|
+
]
|
|
378
|
+
if (!skipMCP && !components.includes('runtime')) components.push('runtime')
|
|
379
|
+
if (skipMCP) {
|
|
380
|
+
const runtimeIndex = components.indexOf('runtime')
|
|
381
|
+
if (runtimeIndex >= 0) components.splice(runtimeIndex, 1)
|
|
382
|
+
}
|
|
228
383
|
|
|
229
384
|
// Version info
|
|
230
385
|
const version = readVersion()
|
|
386
|
+
const { strings } = await import('../scripts/install/strings.mjs')
|
|
387
|
+
const S = strings()
|
|
231
388
|
|
|
232
389
|
console.log('')
|
|
233
390
|
if (!forceInteractive || isDryRun) {
|
|
234
|
-
console.log(`Pantheon OpenCode v${version} — ${isDryRun ?
|
|
391
|
+
console.log(`Pantheon OpenCode v${version} — ${isDryRun ? S.dryRun : S.installing}`)
|
|
235
392
|
console.log('')
|
|
236
393
|
}
|
|
237
394
|
|
|
@@ -248,15 +405,17 @@ async function main() {
|
|
|
248
405
|
version: versionOpt ?? 'v1',
|
|
249
406
|
})
|
|
250
407
|
} catch (err) {
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
408
|
+
if (err?.message === 'Canceled') {
|
|
409
|
+
console.error(S.canceled)
|
|
410
|
+
process.exit(130)
|
|
411
|
+
}
|
|
412
|
+
console.error(`${S.installFailed(err.message)}\n${S.failHintNoMcp}\n${S.failHintForce}`)
|
|
254
413
|
process.exit(1)
|
|
255
414
|
}
|
|
256
415
|
|
|
257
416
|
if (runDoctor && !isDryRun) {
|
|
258
417
|
console.log('')
|
|
259
|
-
console.log(
|
|
418
|
+
console.log(` ${S.runningHealthCheck}`)
|
|
260
419
|
try {
|
|
261
420
|
const { spawnSync } = await import('node:child_process')
|
|
262
421
|
const doctorScript = path.join(ROOT, 'scripts', 'doctor.mjs')
|
|
@@ -268,16 +427,14 @@ async function main() {
|
|
|
268
427
|
|
|
269
428
|
console.log('')
|
|
270
429
|
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━')
|
|
271
|
-
console.log(`
|
|
430
|
+
console.log(` ${S.installedTitle(version)}`)
|
|
272
431
|
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━')
|
|
273
432
|
console.log('')
|
|
274
|
-
console.log(
|
|
275
|
-
console.log(
|
|
276
|
-
console.log(
|
|
277
|
-
console.log(
|
|
278
|
-
console.log(
|
|
279
|
-
console.log(' 4. For project-local install:')
|
|
280
|
-
console.log(' npx pantheon-opencode init --project')
|
|
433
|
+
console.log(` ${S.nextSteps}`)
|
|
434
|
+
console.log(` ${S.nextVerify}`)
|
|
435
|
+
console.log(` ${S.nextLaunch}`)
|
|
436
|
+
console.log(` ${S.nextAgents}`)
|
|
437
|
+
console.log(` ${S.nextProject}`)
|
|
281
438
|
console.log('')
|
|
282
439
|
|
|
283
440
|
return
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pantheon-opencode",
|
|
3
|
-
"version": "1.5.0-beta.
|
|
3
|
+
"version": "1.5.0-beta.5",
|
|
4
4
|
"description": "Multi-agent orchestration for OpenCode — 14 specialized agents, TDD, Themis review gates.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|
|
@@ -32,15 +32,15 @@
|
|
|
32
32
|
"package:evidence": "node scripts/package-evidence.mjs",
|
|
33
33
|
"package:validate": "node scripts/validate-package.mjs",
|
|
34
34
|
"publish:next": "npm publish --tag next --access public",
|
|
35
|
-
"lint": "
|
|
36
|
-
"typecheck": "
|
|
35
|
+
"lint": "biome ci .",
|
|
36
|
+
"typecheck": "tsc --noEmit",
|
|
37
37
|
"audit": "npm audit --audit-level=high",
|
|
38
38
|
"secret-scan": "node scripts/secret-scan.mjs",
|
|
39
39
|
"hooks:install": "pre-commit install",
|
|
40
40
|
"hooks:update": "pre-commit autoupdate",
|
|
41
|
-
"test:security": "npm run secret-scan
|
|
41
|
+
"test:security": "npm run secret-scan",
|
|
42
42
|
"test:node": "node --test tests/*.mjs tests/pantheon/*.mjs",
|
|
43
|
-
"test:ts": "for f in tests/pantheon/*.test.ts; do if [ \"$f\" = tests/pantheon/plugin-v2-contract.test.ts ]; then NODE_OPTIONS=--conditions=import
|
|
43
|
+
"test:ts": "for f in tests/pantheon/*.test.ts; do if [ \"$f\" = tests/pantheon/plugin-v2-contract.test.ts ]; then NODE_OPTIONS=--conditions=import tsx \"$f\"; else tsx \"$f\"; fi || exit 1; done",
|
|
44
44
|
"test": "python3 -m pytest tests/ -q 2>&1; status=$?; if [ \"$status\" -eq 5 ]; then echo \"No tests found\"; fi; exit \"$status\"",
|
|
45
45
|
"test:ci": "python3 -m pytest tests/ -q --tb=short 2>&1"
|
|
46
46
|
},
|
|
@@ -68,6 +68,7 @@
|
|
|
68
68
|
"!src/agents/README.md",
|
|
69
69
|
"bin/*.mjs",
|
|
70
70
|
"AGENTS.md",
|
|
71
|
+
"CHANGELOG.md",
|
|
71
72
|
"opencode.json",
|
|
72
73
|
"src/mcp/*.py",
|
|
73
74
|
"src/instructions/*.instructions.md",
|
|
@@ -87,14 +88,17 @@
|
|
|
87
88
|
"!**/*.pyc"
|
|
88
89
|
],
|
|
89
90
|
"dependencies": {
|
|
90
|
-
"@opencode-ai/plugin": "1.18.
|
|
91
|
+
"@opencode-ai/plugin": "1.18.30",
|
|
91
92
|
"js-yaml": "^4.3.2",
|
|
92
93
|
"zod": "4.1.8"
|
|
93
94
|
},
|
|
94
95
|
"devDependencies": {
|
|
95
|
-
"@
|
|
96
|
-
"@commitlint/
|
|
97
|
-
"
|
|
96
|
+
"@biomejs/biome": "2.5.13",
|
|
97
|
+
"@commitlint/cli": "21.2.2",
|
|
98
|
+
"@commitlint/config-conventional": "21.2.2",
|
|
99
|
+
"husky": "^9.1.7",
|
|
100
|
+
"tsx": "4.23.13",
|
|
101
|
+
"typescript": "6.0.3"
|
|
98
102
|
},
|
|
99
103
|
"repository": {
|
|
100
104
|
"type": "git",
|
|
@@ -115,7 +119,7 @@
|
|
|
115
119
|
"author": "ils15",
|
|
116
120
|
"license": "MIT",
|
|
117
121
|
"engines": {
|
|
118
|
-
"node": ">=
|
|
122
|
+
"node": ">=22"
|
|
119
123
|
},
|
|
120
124
|
"preferGlobal": true
|
|
121
125
|
}
|
package/plugin.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pantheon",
|
|
3
3
|
"publisher": "ils15",
|
|
4
|
-
"version": "1.5.0-beta.
|
|
4
|
+
"version": "1.5.0-beta.5",
|
|
5
5
|
"displayName": "Pantheon",
|
|
6
6
|
"description": "Conductor-Delegate orchestration platform with 14 specialized agents, 21 skills, and OpenCode support.",
|
|
7
7
|
"agents": "./src/agents",
|
package/pyproject.toml
CHANGED