pantheon-opencode 1.1.3-beta.0 → 1.2.0-beta.9.7bef03e
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +74 -1
- package/bin/pantheon-init.mjs +79 -0
- package/commands/pantheon-vision.md +67 -0
- package/opencode.json +12 -41
- package/package.json +5 -1
- package/scripts/doctor.mjs +4 -2
- package/scripts/install/health-check.mjs +101 -1
- package/scripts/install/model-picker.mjs +118 -0
- package/scripts/install/opencode.mjs +98 -17
- package/scripts/install-mcp.mjs +98 -13
- package/scripts/mcp_persistence_server.py +331 -1
- package/scripts/secret-scan.mjs +76 -0
- package/scripts/validate-routing.mjs +15 -0
- package/src/agents/aphrodite.md +7 -0
- package/src/agents/demeter.md +7 -0
- package/src/agents/hephaestus.md +7 -0
- package/src/agents/hermes.md +7 -0
- package/src/agents/zeus.md +31 -18
- package/src/instructions/agent-return-format.instructions.md +1 -0
- package/src/instructions/zeus-anti-stall.instructions.md +43 -5
- package/src/instructions/zeus-timeout-retry.instructions.md +47 -10
- package/src/mcp/install-mcp.mjs +6 -913
- package/src/mcp/mcp_persistence_server.py +332 -0
- package/src/mcp/pantheon_vision_server.py +495 -0
- package/src/mcp/requirements-vision.txt +3 -0
- package/src/pantheon/auto-wake.ts +91 -0
- package/src/pantheon/background-job-board.ts +568 -0
- package/src/pantheon/file-persistence.ts +95 -0
- package/src/pantheon/presets.d.mts +99 -0
- package/src/pantheon/presets.mjs +491 -0
- package/src/pantheon/vision.ts +1340 -0
- package/src/plugin.ts +40 -25
- package/src/plugins/tui/dist/tui.d.ts.map +1 -1
- package/src/plugins/tui/dist/tui.js +806 -222
- package/src/plugins/tui/dist/tui.tsx +866 -121
- package/src/plugins/tui/package.json +2 -2
- package/src/plugins/tui/src/index.tsx +866 -121
- package/src/routing.yml +179 -0
|
@@ -3,12 +3,33 @@
|
|
|
3
3
|
* opencode.mjs — OpenCode platform installer
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
chmodSync,
|
|
8
|
+
existsSync,
|
|
9
|
+
mkdirSync,
|
|
10
|
+
readdirSync,
|
|
11
|
+
readFileSync,
|
|
12
|
+
rmSync,
|
|
13
|
+
writeFileSync,
|
|
14
|
+
} from 'node:fs'
|
|
7
15
|
import { homedir } from 'node:os'
|
|
8
16
|
import { join, resolve } from 'node:path'
|
|
17
|
+
import {
|
|
18
|
+
bullet,
|
|
19
|
+
colors,
|
|
20
|
+
configure,
|
|
21
|
+
error,
|
|
22
|
+
info,
|
|
23
|
+
printSummary,
|
|
24
|
+
section,
|
|
25
|
+
spinner,
|
|
26
|
+
step,
|
|
27
|
+
success,
|
|
28
|
+
warning,
|
|
29
|
+
} from './cli-ui.mjs'
|
|
9
30
|
import { healthCheck } from './health-check.mjs'
|
|
10
31
|
import { detectVersion, runMigrations } from './migrate.mjs'
|
|
11
|
-
import {
|
|
32
|
+
import { installPlugin, registerPlugin, unregisterPlugin } from './plugin.mjs'
|
|
12
33
|
import {
|
|
13
34
|
collectSkillNames,
|
|
14
35
|
copyFiles,
|
|
@@ -21,10 +42,16 @@ import {
|
|
|
21
42
|
writeIfChanged,
|
|
22
43
|
} from './shared.mjs'
|
|
23
44
|
import { setupVenv } from './venv.mjs'
|
|
24
|
-
import { installPlugin, registerPlugin, unregisterPlugin } from './plugin.mjs'
|
|
25
|
-
import { resolveComponents } from './component.mjs'
|
|
26
45
|
|
|
27
|
-
const COMPONENT_NAMES = [
|
|
46
|
+
const COMPONENT_NAMES = [
|
|
47
|
+
'agents',
|
|
48
|
+
'skills',
|
|
49
|
+
'instructions',
|
|
50
|
+
'prompts',
|
|
51
|
+
'commands',
|
|
52
|
+
'plugins',
|
|
53
|
+
'runtime',
|
|
54
|
+
]
|
|
28
55
|
|
|
29
56
|
function deepClone(value) {
|
|
30
57
|
return JSON.parse(JSON.stringify(value))
|
|
@@ -63,7 +90,7 @@ function mergeMissing(target, source) {
|
|
|
63
90
|
* Global installs use a flat layout: agents/ skills/ commands/ plugins/
|
|
64
91
|
* Project installs use the .opencode/ sub-directory layout.
|
|
65
92
|
*/
|
|
66
|
-
function isGlobalConfigDir(target) {
|
|
93
|
+
export function isGlobalConfigDir(target) {
|
|
67
94
|
// Primary: ~/.opencode (actual OpenCode installation)
|
|
68
95
|
const homeDir = resolve(join(homedir(), '.opencode'))
|
|
69
96
|
if (resolve(target) === homeDir) return true
|
|
@@ -261,9 +288,12 @@ export async function installOpenCode(
|
|
|
261
288
|
try {
|
|
262
289
|
const cfg = JSON.parse(readFileSync(cfgPath, 'utf8'))
|
|
263
290
|
let changed = false
|
|
264
|
-
if (cfg.subagent_depth === undefined) {
|
|
291
|
+
if (cfg.subagent_depth === undefined) {
|
|
292
|
+
cfg.subagent_depth = 2
|
|
293
|
+
changed = true
|
|
294
|
+
}
|
|
265
295
|
if (changed && !dryRun) {
|
|
266
|
-
writeFileSync(cfgPath, JSON.stringify(cfg, null, 2)
|
|
296
|
+
writeFileSync(cfgPath, `${JSON.stringify(cfg, null, 2)}\n`)
|
|
267
297
|
success('subagent_depth: 2 added to opencode.json')
|
|
268
298
|
}
|
|
269
299
|
} catch (e) {
|
|
@@ -314,12 +344,16 @@ export async function installOpenCode(
|
|
|
314
344
|
'scrub-secrets.py',
|
|
315
345
|
'_pantheon_paths.py',
|
|
316
346
|
'mcp_persistence_server.py',
|
|
347
|
+
'pantheon_vision_server.py',
|
|
317
348
|
]
|
|
318
349
|
const srcScriptsDir = join(ROOT, 'scripts')
|
|
350
|
+
const canonicalMcpScripts = {
|
|
351
|
+
'pantheon_vision_server.py': join(ROOT, 'src', 'mcp', 'pantheon_vision_server.py'),
|
|
352
|
+
}
|
|
319
353
|
const dstScriptsDir = join(runtimeTarget, 'scripts')
|
|
320
354
|
if (!dryRun) mkdirSync(dstScriptsDir, { recursive: true })
|
|
321
355
|
for (const script of mcpScripts) {
|
|
322
|
-
const src = join(srcScriptsDir, script)
|
|
356
|
+
const src = canonicalMcpScripts[script] || join(srcScriptsDir, script)
|
|
323
357
|
if (!existsSync(src)) {
|
|
324
358
|
warning(`Script not found: ${src}`)
|
|
325
359
|
stats.errors++
|
|
@@ -341,6 +375,18 @@ export async function installOpenCode(
|
|
|
341
375
|
} else stats.skipped++
|
|
342
376
|
}
|
|
343
377
|
|
|
378
|
+
// Keep Vision's dependency contract available to the installed runtime.
|
|
379
|
+
const visionRequirements = join(ROOT, 'src', 'mcp', 'requirements-vision.txt')
|
|
380
|
+
if (existsSync(visionRequirements)) {
|
|
381
|
+
const destination = join(runtimeTarget, 'requirements-vision.txt')
|
|
382
|
+
const status = writeIfChanged(destination, readFileSync(visionRequirements, 'utf8'), dryRun)
|
|
383
|
+
if (status === 'created') stats.created++
|
|
384
|
+
else stats.skipped++
|
|
385
|
+
} else {
|
|
386
|
+
warning(`Requirements file not found: ${visionRequirements}`)
|
|
387
|
+
stats.errors++
|
|
388
|
+
}
|
|
389
|
+
|
|
344
390
|
// ── Code-mode scripts ──
|
|
345
391
|
const srcCodeModeDir = join(ROOT, '.pantheon', 'code-mode')
|
|
346
392
|
const dstCodeModeDir = join(runtimeTarget, '.pantheon', 'code-mode')
|
|
@@ -605,9 +651,13 @@ export async function installOpenCode(
|
|
|
605
651
|
if (componentSet.has('runtime')) {
|
|
606
652
|
config.mcp = config.mcp || {}
|
|
607
653
|
const runtimeTarget = isGlobal ? target : join(target, '.opencode')
|
|
608
|
-
const
|
|
609
|
-
|
|
610
|
-
|
|
654
|
+
const venvPython =
|
|
655
|
+
process.platform === 'win32'
|
|
656
|
+
? join(runtimeTarget, '.venv', 'Scripts', 'python.exe')
|
|
657
|
+
: join(runtimeTarget, '.venv', 'bin', 'python3')
|
|
658
|
+
// Point the config at the venv even on a first install; setupVenv runs
|
|
659
|
+
// later in this function and creates this path before OpenCode starts.
|
|
660
|
+
const memoryPython = venvPython
|
|
611
661
|
|
|
612
662
|
// Only add if not already configured by user
|
|
613
663
|
if (!config.mcp['pantheon-resources']) {
|
|
@@ -642,6 +692,14 @@ export async function installOpenCode(
|
|
|
642
692
|
enabled: true,
|
|
643
693
|
}
|
|
644
694
|
}
|
|
695
|
+
if (!config.mcp['pantheon-vision']) {
|
|
696
|
+
config.mcp['pantheon-vision'] = {
|
|
697
|
+
type: 'local',
|
|
698
|
+
cwd: runtimeTarget,
|
|
699
|
+
command: [memoryPython, 'scripts/pantheon_vision_server.py'],
|
|
700
|
+
enabled: true,
|
|
701
|
+
}
|
|
702
|
+
}
|
|
645
703
|
|
|
646
704
|
// Default MCP permissions
|
|
647
705
|
config.permission = config.permission || {}
|
|
@@ -658,6 +716,9 @@ export async function installOpenCode(
|
|
|
658
716
|
if (!config.permission.mcp['pantheon-persistence']) {
|
|
659
717
|
config.permission.mcp['pantheon-persistence'] = 'allow'
|
|
660
718
|
}
|
|
719
|
+
if (!config.permission.mcp['pantheon-vision']) {
|
|
720
|
+
config.permission.mcp['pantheon-vision'] = 'ask'
|
|
721
|
+
}
|
|
661
722
|
}
|
|
662
723
|
|
|
663
724
|
const configContent = `${JSON.stringify(config, null, 2)}\n`
|
|
@@ -707,7 +768,7 @@ export async function installOpenCode(
|
|
|
707
768
|
}
|
|
708
769
|
} catch (err) {
|
|
709
770
|
error(`Setup failed: ${err.message}`)
|
|
710
|
-
throw err
|
|
771
|
+
throw err // Fatal — abort installation
|
|
711
772
|
}
|
|
712
773
|
}
|
|
713
774
|
|
|
@@ -718,18 +779,38 @@ export async function installOpenCode(
|
|
|
718
779
|
|
|
719
780
|
process.stdout.write('\n')
|
|
720
781
|
process.stdout.write(` ${colors.bold(colors.green('Installation complete'))}\n`)
|
|
721
|
-
process.stdout.write(
|
|
782
|
+
process.stdout.write(
|
|
783
|
+
` ${colors.dim('\u2500'.repeat(Math.min(process.stdout.columns || 60, 60)))}\n`,
|
|
784
|
+
)
|
|
722
785
|
process.stdout.write(` ${colors.green('\u2713')} ${created} component(s) installed\n`)
|
|
723
|
-
if (skipped > 0)
|
|
724
|
-
|
|
786
|
+
if (skipped > 0)
|
|
787
|
+
process.stdout.write(` ${colors.dim('\u2014')} ${skipped} already up-to-date\n`)
|
|
788
|
+
if (errors > 0)
|
|
789
|
+
process.stdout.write(` ${colors.yellow('\u26a0')} ${errors} error(s) encountered\n`)
|
|
725
790
|
process.stdout.write('\n')
|
|
726
791
|
process.stdout.write(` ${colors.bold('Next steps:')}\n`)
|
|
727
792
|
process.stdout.write(` ${colors.dim('\u2022')} Configure agents in opencode.json\n`)
|
|
728
793
|
process.stdout.write(` ${colors.dim('\u2022')} Add MCP servers in mcp.json\n`)
|
|
729
794
|
process.stdout.write(` ${colors.dim('\u2022')} Run 'opencode doctor' to verify\n`)
|
|
730
|
-
process.stdout.write(
|
|
795
|
+
process.stdout.write(
|
|
796
|
+
` ${colors.dim('\u2022')} Run 'opencode' to start using Pantheon agents\n`,
|
|
797
|
+
)
|
|
731
798
|
process.stdout.write('\n')
|
|
732
799
|
} else {
|
|
733
800
|
printSummary(target, ['opencode'], stats)
|
|
734
801
|
}
|
|
802
|
+
|
|
803
|
+
// -----------------------------------------------------------------------
|
|
804
|
+
// 5. Model preset selection (--components agents). Interactive picker runs
|
|
805
|
+
// unless autoYes (use defaults) or an explicit --preset was given.
|
|
806
|
+
// -----------------------------------------------------------------------
|
|
807
|
+
if (interactive && !dryRun && !autoYes && !opts.preset) {
|
|
808
|
+
const { runModelPicker } = await import('./model-picker.mjs')
|
|
809
|
+
await runModelPicker({ presetDir: target, logger: console })
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
if (opts.preset && !dryRun) {
|
|
813
|
+
const { writeActivePreset } = await import('./model-picker.mjs')
|
|
814
|
+
writeActivePreset(target, opts.preset, { source: 'cli' })
|
|
815
|
+
}
|
|
735
816
|
}
|
package/scripts/install-mcp.mjs
CHANGED
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
import { execSync } from 'node:child_process'
|
|
24
24
|
import { copyFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'
|
|
25
25
|
import { homedir } from 'node:os'
|
|
26
|
-
import { dirname, join } from 'node:path'
|
|
26
|
+
import { dirname, join, resolve } from 'node:path'
|
|
27
27
|
import { createInterface } from 'node:readline'
|
|
28
28
|
import { fileURLToPath } from 'node:url'
|
|
29
29
|
|
|
@@ -171,11 +171,31 @@ const MCPS = {
|
|
|
171
171
|
name: 'Pantheon Resources',
|
|
172
172
|
description: 'Pantheon framework resources — agents, skills, routing, deepwork, memory bank',
|
|
173
173
|
platforms: {
|
|
174
|
-
opencode: {
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
174
|
+
opencode: {
|
|
175
|
+
type: 'local',
|
|
176
|
+
command: join(ROOT, '.venv/bin/python3'),
|
|
177
|
+
args: [join(ROOT, 'scripts/mcp_resources_server.py')],
|
|
178
|
+
},
|
|
179
|
+
vscode: {
|
|
180
|
+
type: 'stdio',
|
|
181
|
+
command: join(ROOT, '.venv/bin/python3'),
|
|
182
|
+
args: [join(ROOT, 'scripts/mcp_resources_server.py')],
|
|
183
|
+
},
|
|
184
|
+
cursor: {
|
|
185
|
+
type: 'local',
|
|
186
|
+
command: join(ROOT, '.venv/bin/python3'),
|
|
187
|
+
args: [join(ROOT, 'scripts/mcp_resources_server.py')],
|
|
188
|
+
},
|
|
189
|
+
claude: {
|
|
190
|
+
type: 'local',
|
|
191
|
+
command: join(ROOT, '.venv/bin/python3'),
|
|
192
|
+
args: [join(ROOT, 'scripts/mcp_resources_server.py')],
|
|
193
|
+
},
|
|
194
|
+
windsurf: {
|
|
195
|
+
type: 'local',
|
|
196
|
+
command: join(ROOT, '.venv/bin/python3'),
|
|
197
|
+
args: [join(ROOT, 'scripts/mcp_resources_server.py')],
|
|
198
|
+
},
|
|
179
199
|
},
|
|
180
200
|
env: [],
|
|
181
201
|
validate: async () => {
|
|
@@ -191,11 +211,31 @@ const MCPS = {
|
|
|
191
211
|
name: 'Pantheon Code Mode',
|
|
192
212
|
description: 'Execute orchestration scripts from .pantheon/code-mode/ directory',
|
|
193
213
|
platforms: {
|
|
194
|
-
opencode: {
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
214
|
+
opencode: {
|
|
215
|
+
type: 'local',
|
|
216
|
+
command: join(ROOT, '.venv/bin/python3'),
|
|
217
|
+
args: [join(ROOT, 'scripts/code_mode_server.py')],
|
|
218
|
+
},
|
|
219
|
+
vscode: {
|
|
220
|
+
type: 'stdio',
|
|
221
|
+
command: join(ROOT, '.venv/bin/python3'),
|
|
222
|
+
args: [join(ROOT, 'scripts/code_mode_server.py')],
|
|
223
|
+
},
|
|
224
|
+
cursor: {
|
|
225
|
+
type: 'local',
|
|
226
|
+
command: join(ROOT, '.venv/bin/python3'),
|
|
227
|
+
args: [join(ROOT, 'scripts/code_mode_server.py')],
|
|
228
|
+
},
|
|
229
|
+
claude: {
|
|
230
|
+
type: 'local',
|
|
231
|
+
command: join(ROOT, '.venv/bin/python3'),
|
|
232
|
+
args: [join(ROOT, 'scripts/code_mode_server.py')],
|
|
233
|
+
},
|
|
234
|
+
windsurf: {
|
|
235
|
+
type: 'local',
|
|
236
|
+
command: join(ROOT, '.venv/bin/python3'),
|
|
237
|
+
args: [join(ROOT, 'scripts/code_mode_server.py')],
|
|
238
|
+
},
|
|
199
239
|
},
|
|
200
240
|
env: [],
|
|
201
241
|
validate: async () => {
|
|
@@ -278,6 +318,49 @@ const MCPS = {
|
|
|
278
318
|
return { ok: false, message: 'scripts/mcp_persistence_server.py not found' }
|
|
279
319
|
},
|
|
280
320
|
},
|
|
321
|
+
'pantheon-vision': {
|
|
322
|
+
tier: 1,
|
|
323
|
+
name: 'Pantheon Vision',
|
|
324
|
+
description:
|
|
325
|
+
'Image description, OCR, and structured analysis through the OpenCode vision gateway',
|
|
326
|
+
requirements: join(ROOT, 'src', 'mcp', 'requirements-vision.txt'),
|
|
327
|
+
platforms: {
|
|
328
|
+
opencode: {
|
|
329
|
+
type: 'local',
|
|
330
|
+
command: join(ROOT, '.venv/bin/python3'),
|
|
331
|
+
args: [join(ROOT, 'src', 'mcp', 'pantheon_vision_server.py')],
|
|
332
|
+
},
|
|
333
|
+
vscode: {
|
|
334
|
+
type: 'stdio',
|
|
335
|
+
command: join(ROOT, '.venv/bin/python3'),
|
|
336
|
+
args: [join(ROOT, 'src', 'mcp', 'pantheon_vision_server.py')],
|
|
337
|
+
},
|
|
338
|
+
claude: {
|
|
339
|
+
type: 'local',
|
|
340
|
+
command: join(ROOT, '.venv/bin/python3'),
|
|
341
|
+
args: [join(ROOT, 'src', 'mcp', 'pantheon_vision_server.py')],
|
|
342
|
+
},
|
|
343
|
+
cursor: {
|
|
344
|
+
type: 'local',
|
|
345
|
+
command: join(ROOT, '.venv/bin/python3'),
|
|
346
|
+
args: [join(ROOT, 'src', 'mcp', 'pantheon_vision_server.py')],
|
|
347
|
+
},
|
|
348
|
+
windsurf: {
|
|
349
|
+
type: 'local',
|
|
350
|
+
command: join(ROOT, '.venv/bin/python3'),
|
|
351
|
+
args: [join(ROOT, 'src', 'mcp', 'pantheon_vision_server.py')],
|
|
352
|
+
},
|
|
353
|
+
},
|
|
354
|
+
env: [],
|
|
355
|
+
validate: async () => {
|
|
356
|
+
const sourcePath = join(ROOT, 'src', 'mcp', 'pantheon_vision_server.py')
|
|
357
|
+
const requirementsPath = join(ROOT, 'src', 'mcp', 'requirements-vision.txt')
|
|
358
|
+
if (existsSync(sourcePath) && existsSync(requirementsPath)) {
|
|
359
|
+
return { ok: true, message: 'canonical server source and isolated requirements present' }
|
|
360
|
+
}
|
|
361
|
+
return { ok: false, message: 'canonical vision source or requirements not found' }
|
|
362
|
+
},
|
|
363
|
+
},
|
|
281
364
|
docker: {
|
|
282
365
|
tier: 2,
|
|
283
366
|
name: 'Docker MCP',
|
|
@@ -790,7 +873,7 @@ function logValidation(mcpKey, result) {
|
|
|
790
873
|
// Main
|
|
791
874
|
// ---------------------------------------------------------------------------
|
|
792
875
|
|
|
793
|
-
async function main() {
|
|
876
|
+
export async function main() {
|
|
794
877
|
const args = parseArgs(process.argv)
|
|
795
878
|
|
|
796
879
|
if (args.help) {
|
|
@@ -918,4 +1001,6 @@ async function main() {
|
|
|
918
1001
|
process.exit(progress.errors.length > 0 ? 1 : 0)
|
|
919
1002
|
}
|
|
920
1003
|
|
|
921
|
-
|
|
1004
|
+
if (process.argv[1] && fileURLToPath(import.meta.url) === resolve(process.argv[1])) {
|
|
1005
|
+
main()
|
|
1006
|
+
}
|