pantheon-opencode 1.5.2 → 1.6.0-beta.10

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 (146) hide show
  1. package/.pantheon/code-mode/README.md +4 -2
  2. package/.pantheon/code-mode/checkpoint_session.py +138 -102
  3. package/.pantheon/code-mode/compress-inline.py +102 -57
  4. package/.pantheon/code-mode/manifest.json +2 -8
  5. package/AGENTS.global.md +182 -0
  6. package/AGENTS.md +10 -891
  7. package/CHANGELOG.md +343 -0
  8. package/README.md +577 -79
  9. package/README.pt-BR.md +345 -60
  10. package/bin/pantheon-init.mjs +26 -3
  11. package/commands/pantheon-audit.md +7 -1
  12. package/commands/pantheon-consolidate.md +1 -1
  13. package/commands/pantheon-deepwork.md +14 -3
  14. package/commands/pantheon-focus.md +17 -0
  15. package/commands/pantheon-optimize.md +2 -2
  16. package/commands/pantheon-vision.md +61 -12
  17. package/package.json +30 -11
  18. package/plugin.json +2 -2
  19. package/pyproject.toml +11 -16
  20. package/scripts/build-agents-md.mjs +376 -58
  21. package/scripts/changelog-merge.mjs +161 -0
  22. package/scripts/check-plugin-v2-coverage.mjs +213 -0
  23. package/scripts/ci-validate-yaml.py +7 -5
  24. package/scripts/{code_mode_server.py → code_mode.py} +119 -27
  25. package/scripts/cost.mjs +244 -41
  26. package/scripts/doctor.mjs +236 -29
  27. package/scripts/install/agents-md.mjs +4 -4
  28. package/scripts/install/cli-ui.mjs +1 -1
  29. package/scripts/install/config-migration.mjs +319 -6
  30. package/scripts/install/health-check.mjs +26 -37
  31. package/scripts/install/interactive.mjs +7 -7
  32. package/scripts/install/migrate.mjs +1 -1
  33. package/scripts/install/opencode-version.mjs +246 -4
  34. package/scripts/install/opencode.mjs +627 -179
  35. package/scripts/install/plugin.mjs +43 -9
  36. package/scripts/install/shared.mjs +120 -12
  37. package/scripts/install/vision-prompt.mjs +161 -0
  38. package/scripts/install-mcp.mjs +28 -9
  39. package/scripts/{mcp_persistence_server.py → mcp_persistence.py} +392 -78
  40. package/{src/mcp/mcp_resources_server.py → scripts/mcp_resources.py} +2 -2
  41. package/scripts/{memory_mcp_server.py → memory_mcp.py} +372 -198
  42. package/scripts/postinstall.mjs +0 -7
  43. package/scripts/probe-context-rehydrate.mjs +36 -13
  44. package/scripts/probe-pantheon-cost.mjs +71 -15
  45. package/scripts/prune.mjs +2 -2
  46. package/scripts/run-node-tests.mjs +189 -0
  47. package/scripts/sync-artifacts.mjs +26 -13
  48. package/scripts/sync-tui.mjs +24 -3
  49. package/scripts/test-opencode-v2-sandbox.sh +1913 -0
  50. package/scripts/validate-routing.mjs +43 -3
  51. package/scripts/validate-sandbox-install.mjs +138 -0
  52. package/scripts/versioning.mjs +13 -1
  53. package/scripts/vision-status.mjs +682 -0
  54. package/skills-lock.json +15 -16
  55. package/src/agents/aphrodite.md +20 -37
  56. package/src/agents/apollo.md +7 -3
  57. package/src/agents/athena.md +63 -35
  58. package/src/agents/demeter.md +8 -28
  59. package/src/agents/gaia.md +1 -1
  60. package/src/agents/hephaestus.md +10 -21
  61. package/src/agents/hermes.md +15 -38
  62. package/src/agents/iris.md +1 -1
  63. package/src/agents/mnemosyne.md +23 -60
  64. package/src/agents/nyx.md +1 -1
  65. package/src/agents/prometheus.md +16 -20
  66. package/src/agents/talos.md +10 -2
  67. package/src/agents/themis.md +5 -5
  68. package/src/agents/zeus.md +99 -130
  69. package/src/instructions/agent-return-format.instructions.md +18 -3
  70. package/src/instructions/backend-standards.instructions.md +2 -0
  71. package/src/instructions/frontend-standards.instructions.md +3 -1
  72. package/src/instructions/memory-protocol.instructions.md +11 -110
  73. package/src/instructions/zeus-anti-stall.instructions.md +14 -61
  74. package/src/instructions/zeus-communication-rules.instructions.md +0 -1
  75. package/src/instructions/zeus-council-synthesis.instructions.md +32 -260
  76. package/src/instructions/zeus-memory-operations.instructions.md +26 -0
  77. package/src/instructions/zeus-timeout-retry.instructions.md +40 -87
  78. package/src/mcp/{code_mode_server.py → code_mode.py} +119 -27
  79. package/src/mcp/eval_store.py +2 -2
  80. package/src/mcp/mcp_codemap_module.py +559 -129
  81. package/src/mcp/{mcp_persistence_server.py → mcp_persistence.py} +392 -78
  82. package/{scripts/mcp_resources_server.py → src/mcp/mcp_resources.py} +2 -2
  83. package/src/mcp/{memory_mcp_server.py → memory_mcp.py} +373 -250
  84. package/src/mcp/{pantheon_vision_server.py → pantheon_vision.py} +396 -40
  85. package/src/mcp/requirements-mcp.txt +10 -6
  86. package/src/mcp/requirements-vision.txt +4 -2
  87. package/src/pantheon/cost-command.ts +399 -101
  88. package/src/pantheon/delegation-enforce.ts +10 -1
  89. package/src/pantheon/model-command.ts +18 -7
  90. package/src/pantheon/presets.d.mts +0 -8
  91. package/src/pantheon/presets.mjs +5 -32
  92. package/src/pantheon/todo-enforcer.ts +1 -1
  93. package/src/pantheon/v2-bounded-read.ts +640 -0
  94. package/src/pantheon/v2-delegation-enforce.ts +214 -0
  95. package/src/pantheon/v2-execute-after.ts +186 -0
  96. package/src/pantheon/v2-tools.ts +429 -0
  97. package/src/pantheon/v2-unsupported.d.mts +11 -0
  98. package/src/pantheon/v2-unsupported.mjs +90 -0
  99. package/src/pantheon/v2-vision.ts +622 -0
  100. package/src/pantheon/vision-local.ts +531 -0
  101. package/src/pantheon/vision.ts +1161 -116
  102. package/src/plugin-v2.ts +1244 -288
  103. package/src/plugin.ts +12 -9
  104. package/src/plugins/tui/dist/server.d.ts +1 -2
  105. package/src/plugins/tui/dist/server.d.ts.map +1 -1
  106. package/src/plugins/tui/dist/tui-v2.d.ts +154 -0
  107. package/src/plugins/tui/dist/tui-v2.d.ts.map +1 -0
  108. package/src/plugins/tui/dist/tui-v2.js +659 -0
  109. package/src/plugins/tui/dist/tui-v2.js.map +1 -0
  110. package/src/plugins/tui/dist/tui.d.ts +85 -82
  111. package/src/plugins/tui/dist/tui.d.ts.map +1 -1
  112. package/src/plugins/tui/dist/tui.js +72 -59
  113. package/src/plugins/tui/index-v2.ts +6 -0
  114. package/src/plugins/tui/package-lock.json +5504 -1341
  115. package/src/plugins/tui/package.json +25 -15
  116. package/src/plugins/tui/src/index-v2.tsx +734 -0
  117. package/src/plugins/tui/src/index.tsx +106 -85
  118. package/src/plugins/tui/src/opencode-v2.d.ts +13 -0
  119. package/src/plugins/tui/tsconfig.build.json +5 -0
  120. package/src/plugins/tui/tsconfig.typecheck.json +5 -0
  121. package/src/plugins/tui/tsdown.config.ts +22 -2
  122. package/src/plugins/tui/tui-v2.ts +2 -0
  123. package/src/plugins/tui/tui.ts +3 -0
  124. package/src/routing.yml +11 -22
  125. package/src/skills/README.md +34 -39
  126. package/src/skills/agent-coordination/SKILL.md +7 -2
  127. package/src/skills/artifact-management/SKILL.md +12 -5
  128. package/src/skills/auto-continue/SKILL.md +54 -95
  129. package/src/skills/code-review-checklist/SKILL.md +5 -5
  130. package/src/skills/context-compression/SKILL.md +26 -879
  131. package/src/skills/session-goal/SKILL.md +14 -5
  132. package/src/skills/spec-driven-development/SKILL.md +3 -1
  133. package/src/skills/tdd-with-agents/SKILL.md +18 -10
  134. package/.pantheon/code-mode/eval-llm-judge.py +0 -255
  135. package/.pantheon/code-mode/eval-monte-carlo.py +0 -368
  136. package/.pantheon/code-mode/eval-run.py +0 -197
  137. package/.pantheon/code-mode/eval-static.py +0 -321
  138. package/.pantheon/code-mode/session-end-save.py +0 -186
  139. package/.pantheon/code-mode/session-end-save.sh +0 -18
  140. package/opencode.json +0 -58
  141. package/scripts/recover-zenodo-deposition.mjs +0 -146
  142. package/scripts/test-opencode-v1-v2-sandbox.sh +0 -862
  143. package/scripts/validate-zenodo-release.mjs +0 -154
  144. package/scripts/zenodo-artifact.mjs +0 -94
  145. package/scripts/zenodo-release-state.mjs +0 -53
  146. package/src/skills/orchestration-workflow/SKILL.md +0 -311
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env node
2
+
2
3
  /**
3
4
  * validate-routing.mjs — Validate routing.yml consistency
4
5
  *
@@ -14,10 +15,11 @@
14
15
  * node scripts/validate-routing.mjs --verbose # show all entries checked
15
16
  */
16
17
 
18
+ import { createHash } from 'node:crypto'
17
19
  import { existsSync, readdirSync, readFileSync } from 'node:fs'
18
20
  import { dirname, join } from 'node:path'
19
21
  import { fileURLToPath } from 'node:url'
20
- import yaml from 'js-yaml'
22
+ import { load } from 'js-yaml'
21
23
  import { validatePresetDefs } from '../src/pantheon/presets.mjs'
22
24
 
23
25
  const __dirname = dirname(fileURLToPath(import.meta.url))
@@ -50,7 +52,7 @@ if (!existsSync(routingPath)) {
50
52
  console.error('❌ routing.yml not found')
51
53
  process.exit(1)
52
54
  }
53
- const routing = yaml.load(readFileSync(routingPath, 'utf8'))
55
+ const routing = load(readFileSync(routingPath, 'utf8'))
54
56
 
55
57
  console.log('🔍 Validating routing.yml...\n')
56
58
 
@@ -106,6 +108,44 @@ for (const [name, info] of Object.entries(routing.agents || {})) {
106
108
  }
107
109
  }
108
110
 
111
+ // B2. skills-lock.json must match src/skills/*/SKILL.md exactly — content
112
+ // digests stay in sync, and the set of locked paths equals the set on disk
113
+ // (so adding/removing a skill without updating the lock fails the gate).
114
+ console.log('\n skills-lock.json integrity:')
115
+ const lockPath = join(ROOT, 'skills-lock.json')
116
+ let skillLock = null
117
+ try {
118
+ skillLock = JSON.parse(readFileSync(lockPath, 'utf8'))
119
+ } catch (err) {
120
+ check(false, `skills-lock.json missing or malformed: ${err.message}`)
121
+ }
122
+ if (skillLock && typeof skillLock === 'object' && !Array.isArray(skillLock)) {
123
+ const lockedFiles = Object.keys(skillLock).sort()
124
+ const skillFiles = existsSync(srcSkillsDir)
125
+ ? readdirSync(srcSkillsDir)
126
+ .filter((d) => existsSync(join(srcSkillsDir, d, 'SKILL.md')))
127
+ .map((d) => `src/skills/${d}/SKILL.md`)
128
+ .sort()
129
+ : []
130
+ console.log(` Locked skills: ${lockedFiles.length} (on disk: ${skillFiles.length})`)
131
+
132
+ for (const rel of skillFiles) {
133
+ const actual = createHash('sha256')
134
+ .update(readFileSync(join(ROOT, rel)))
135
+ .digest('hex')
136
+ check(
137
+ skillLock[rel] === actual,
138
+ `skills-lock.json stale for "${rel}": expected ${actual}, got ${skillLock[rel] ?? 'no entry'}`,
139
+ )
140
+ }
141
+ for (const rel of lockedFiles) {
142
+ check(
143
+ skillFiles.includes(rel),
144
+ `skills-lock.json lists "${rel}" but that SKILL.md does not exist`,
145
+ )
146
+ }
147
+ }
148
+
109
149
  // C. Handoff validation
110
150
  console.log(
111
151
  `\n Handoff definitions: ${Object.keys(routing.handoffs || {}).length} agents with handoffs`,
@@ -213,7 +253,7 @@ function parseFrontmatter(filePath) {
213
253
  const match = content.match(FRONTMATTER_RE)
214
254
  if (!match) return null
215
255
  try {
216
- return yaml.load(match[1])
256
+ return load(match[1])
217
257
  } catch {
218
258
  return null
219
259
  }
@@ -0,0 +1,138 @@
1
+ #!/usr/bin/env node
2
+ import { createHash } from 'node:crypto'
3
+ import { readFileSync } from 'node:fs'
4
+ import { join, resolve } from 'node:path'
5
+ import { fileURLToPath } from 'node:url'
6
+
7
+ const PACKAGE_REF = /^(?:npm:)?pantheon-opencode(?:@[^/]+)?(?:\/(?:plugin|plugin-v2))?$/
8
+ export const SANDBOX_SOURCE_FILES = [
9
+ 'bin/pantheon-init.mjs',
10
+ 'scripts/install/config-migration.mjs',
11
+ 'scripts/install/opencode.mjs',
12
+ 'scripts/validate-sandbox-install.mjs',
13
+ 'src/plugin-v2.ts',
14
+ ]
15
+
16
+ function readJson(path, label) {
17
+ try {
18
+ return JSON.parse(readFileSync(path, 'utf8'))
19
+ } catch (error) {
20
+ throw new Error(`cannot read ${label} at ${path}: ${error.message}`)
21
+ }
22
+ }
23
+
24
+ function pluginReferences(config) {
25
+ const refs = []
26
+ for (const key of ['plugin', 'plugins']) {
27
+ const value = config?.[key]
28
+ const entries = Array.isArray(value) ? value : value === undefined ? [] : [value]
29
+ for (const entry of entries) {
30
+ if (typeof entry === 'string') refs.push(entry)
31
+ else if (entry && typeof entry === 'object' && !Array.isArray(entry)) {
32
+ for (const field of ['package', 'id', 'name', 'path', 'source']) {
33
+ if (typeof entry[field] === 'string') refs.push(entry[field])
34
+ }
35
+ }
36
+ }
37
+ }
38
+ return refs
39
+ }
40
+
41
+ /**
42
+ * Prove that the sandbox is executing the package built from this checkout and
43
+ * that the project config cannot silently load an older npm-cached Pantheon.
44
+ */
45
+ export function validateSandboxInstall({ repoDir, installDir, projectConfig, configPaths = [] }) {
46
+ const sourcePackagePath = join(resolve(repoDir), 'package.json')
47
+ const installedPackagePath = join(resolve(installDir), 'package.json')
48
+ const sourcePackage = readJson(sourcePackagePath, 'checkout package manifest')
49
+ const installedPackage = readJson(installedPackagePath, 'sandbox package manifest')
50
+ if (sourcePackage.version !== installedPackage.version) {
51
+ throw new Error(
52
+ `sandbox package version ${installedPackage.version} does not match checkout ${sourcePackage.version}`,
53
+ )
54
+ }
55
+
56
+ const sourceHash = createHash('sha256')
57
+ for (const relativePath of SANDBOX_SOURCE_FILES) {
58
+ let source
59
+ let installed
60
+ try {
61
+ source = readFileSync(join(resolve(repoDir), relativePath))
62
+ installed = readFileSync(join(resolve(installDir), relativePath))
63
+ } catch (error) {
64
+ throw new Error(`cannot compare package source ${relativePath}: ${error.message}`)
65
+ }
66
+ if (!source.equals(installed)) {
67
+ throw new Error(`installed package source differs from checkout: ${relativePath}`)
68
+ }
69
+ sourceHash.update(relativePath).update('\0').update(installed).update('\0')
70
+ }
71
+
72
+ const expectedPluginPath = resolve(installDir, 'src', 'plugin-v2')
73
+ const allConfigPaths = [...new Set([projectConfig, ...configPaths].map((path) => resolve(path)))]
74
+ let activeProjectPluginCount = 0
75
+ for (const configPath of allConfigPaths) {
76
+ const config = readJson(configPath, 'active OpenCode config')
77
+ if (!config || typeof config !== 'object' || Array.isArray(config)) {
78
+ throw new Error(`active OpenCode config must be an object: ${configPath}`)
79
+ }
80
+ for (const ref of pluginReferences(config)) {
81
+ const normalized = ref.replaceAll('\\', '/').replace(/\/$/, '')
82
+ if (PACKAGE_REF.test(normalized)) {
83
+ throw new Error(`stale Pantheon package reference in ${configPath}: ${ref}`)
84
+ }
85
+ if (resolve(ref) === expectedPluginPath) {
86
+ if (configPath === resolve(projectConfig)) activeProjectPluginCount += 1
87
+ }
88
+ }
89
+ }
90
+ if (activeProjectPluginCount !== 1) {
91
+ throw new Error(
92
+ `project config must reference the sandbox V2 plugin exactly once; found ${activeProjectPluginCount}: ${resolve(projectConfig)}`,
93
+ )
94
+ }
95
+
96
+ return {
97
+ status: 'PASS',
98
+ version: installedPackage.version,
99
+ sourceSha256: sourceHash.digest('hex'),
100
+ }
101
+ }
102
+
103
+ function parseArgs(args) {
104
+ const options = { configPaths: [] }
105
+ for (let i = 0; i < args.length; i += 1) {
106
+ const arg = args[i]
107
+ if (arg === '--config') options.configPaths.push(args[++i])
108
+ else if (['--repo', '--install', '--project-config'].includes(arg))
109
+ options[arg.slice(2).replace('-', '')] = args[++i]
110
+ else throw new Error(`unknown argument: ${arg}`)
111
+ }
112
+ for (const required of ['repo', 'install', 'projectconfig']) {
113
+ if (!options[required])
114
+ throw new Error(
115
+ `missing required option --${required.replace('projectconfig', 'project-config')}`,
116
+ )
117
+ }
118
+ return {
119
+ repoDir: options.repo,
120
+ installDir: options.install,
121
+ projectConfig: options.projectconfig,
122
+ configPaths: options.configPaths,
123
+ }
124
+ }
125
+
126
+ function main() {
127
+ try {
128
+ const result = validateSandboxInstall(parseArgs(process.argv.slice(2)))
129
+ process.stdout.write(
130
+ `Sandbox install proof: ${result.status} (version ${result.version}, source sha256 ${result.sourceSha256})\n`,
131
+ )
132
+ } catch (error) {
133
+ process.stderr.write(`Sandbox install proof: FAIL — ${error.message}\n`)
134
+ process.exitCode = 1
135
+ }
136
+ }
137
+
138
+ if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) main()
@@ -24,6 +24,7 @@ import { execFileSync } from 'node:child_process'
24
24
  import { existsSync, readFileSync } from 'node:fs'
25
25
  import { basename, dirname, join } from 'node:path'
26
26
  import { fileURLToPath } from 'node:url'
27
+ import { mergeUnreleasedIntoVersion } from './changelog-merge.mjs'
27
28
  import {
28
29
  commitPreparedUpdates,
29
30
  prepareInventoryVersion,
@@ -471,6 +472,17 @@ switch (command) {
471
472
  case 'changelog': {
472
473
  const version = arg || getCurrentVersion()
473
474
  const date = new Date().toISOString().slice(0, 10)
475
+ const currentChangelog = readFileSync(CHANGELOG_PATH, 'utf-8')
476
+ const mergePlan = mergeUnreleasedIntoVersion(currentChangelog, version)
477
+ if (mergePlan) {
478
+ if (mergePlan.changed) {
479
+ commitPreparedUpdates([{ path: CHANGELOG_PATH, content: mergePlan.content }])
480
+ console.log(` ✓ CHANGELOG: merged [Unreleased] into existing [v${version}]`)
481
+ } else {
482
+ console.log(` ✓ CHANGELOG: [Unreleased] is empty; [v${version}] unchanged`)
483
+ }
484
+ break
485
+ }
474
486
  const changelogPlan = prepareUnreleased(version, date)
475
487
  if (changelogPlan.changed) {
476
488
  commitPreparedUpdates([{ path: CHANGELOG_PATH, content: changelogPlan.content }])
@@ -492,7 +504,7 @@ Commands:
492
504
  --beta: advance the committed X.Y.Z-beta.N line and
493
505
  promote [Unreleased] → [vX.Y.Z-beta.N] in CHANGELOG
494
506
  beta Alias for 'apply --beta'
495
- changelog [version] Promote [Unreleased] → [vX.Y.Z] without bumping
507
+ changelog [version] Promote [Unreleased], or merge into an existing version section
496
508
 
497
509
  Release flow (stable):
498
510
  1. node scripts/versioning.mjs apply [minor]