pantheon-opencode 1.5.0-beta.1 → 1.5.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 (83) hide show
  1. package/.pantheon/code-mode/manifest.json +21 -0
  2. package/CHANGELOG.md +1054 -0
  3. package/README.md +171 -16
  4. package/README.pt-BR.md +99 -13
  5. package/bin/pantheon-init.mjs +178 -21
  6. package/package.json +23 -15
  7. package/plugin.json +1 -1
  8. package/pyproject.toml +65 -0
  9. package/scripts/code_mode_server.py +431 -55
  10. package/scripts/cost.mjs +30 -14
  11. package/scripts/dev-tui.sh +48 -0
  12. package/scripts/doctor.mjs +207 -9
  13. package/scripts/install/cli-ui.mjs +17 -11
  14. package/scripts/install/config-migration.mjs +4 -6
  15. package/scripts/install/opencode.mjs +137 -35
  16. package/scripts/install/plugin.mjs +3 -33
  17. package/scripts/install/shared.mjs +43 -2
  18. package/scripts/install/strings.mjs +129 -0
  19. package/scripts/install-mcp.mjs +1 -0
  20. package/scripts/manifest-inventory.mjs +410 -0
  21. package/scripts/mcp_persistence_server.py +537 -128
  22. package/scripts/package-evidence.mjs +242 -0
  23. package/scripts/probe-context-rehydrate.mjs +240 -0
  24. package/scripts/probe-pantheon-cost.mjs +313 -0
  25. package/scripts/release-beta-version.mjs +56 -60
  26. package/scripts/sync-artifacts.mjs +169 -0
  27. package/scripts/sync-tui.mjs +121 -53
  28. package/scripts/test-opencode-v1-v2-sandbox.sh +367 -96
  29. package/scripts/validation-policy.mjs +7 -7
  30. package/scripts/version-check.mjs +113 -108
  31. package/scripts/versioning.mjs +230 -86
  32. package/src/agents/mnemosyne.md +1 -1
  33. package/src/agents/zeus.md +2 -2
  34. package/src/mcp/code_mode_server.py +431 -55
  35. package/src/mcp/mcp_persistence_server.py +537 -128
  36. package/src/pantheon/auto-wake.ts +1 -5
  37. package/src/pantheon/background-job-board.ts +43 -1
  38. package/src/pantheon/compaction-handoff.ts +304 -0
  39. package/src/pantheon/cost-command.ts +345 -62
  40. package/src/pantheon/delegate-manager.ts +390 -8
  41. package/src/pantheon/delegation-finalize.ts +32 -0
  42. package/src/pantheon/delegation.ts +235 -18
  43. package/src/pantheon/file-persistence-salvage.ts +64 -0
  44. package/src/pantheon/file-persistence.ts +194 -45
  45. package/src/pantheon/native-probe.ts +234 -0
  46. package/src/pantheon/native-task-status.ts +139 -0
  47. package/src/pantheon/provider-capabilities.ts +221 -0
  48. package/src/pantheon/provider-stream-tracker.ts +182 -0
  49. package/src/pantheon/shared-board.ts +37 -0
  50. package/src/pantheon/task-result-guard.ts +107 -0
  51. package/src/pantheon/token-opt.ts +146 -12
  52. package/src/pantheon/tool-ceiling.ts +159 -0
  53. package/src/pantheon/v2-events.ts +18 -1
  54. package/src/plugin-v2.ts +2 -14
  55. package/src/plugin.ts +28 -17
  56. package/src/plugins/pantheon-hooks.ts +51 -0
  57. package/src/plugins/tui/dist/server.d.ts +1 -1
  58. package/src/plugins/tui/dist/server.d.ts.map +1 -1
  59. package/src/plugins/tui/dist/server.js +1 -1
  60. package/src/plugins/tui/dist/server.js.map +1 -1
  61. package/src/plugins/tui/dist/tui.d.ts +137 -27
  62. package/src/plugins/tui/dist/tui.d.ts.map +1 -1
  63. package/src/plugins/tui/dist/tui.js +459 -163
  64. package/src/plugins/tui/dist/tui.js.map +1 -1
  65. package/src/plugins/tui/dist/tui.tsx +506 -103
  66. package/src/plugins/tui/package-lock.json +2 -2
  67. package/src/plugins/tui/package.json +1 -1
  68. package/src/plugins/tui/src/index.tsx +506 -103
  69. package/.pantheon/code-mode/.gitkeep +0 -0
  70. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-1.json +0 -8
  71. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-10.json +0 -8
  72. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-11.json +0 -8
  73. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-2.json +0 -8
  74. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-3.json +0 -8
  75. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-4.json +0 -8
  76. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-5.json +0 -8
  77. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-6.json +0 -8
  78. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-7.json +0 -8
  79. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-8.json +0 -8
  80. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/checkpoint-9.json +0 -8
  81. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/heartbeat.json +0 -8
  82. package/.pantheon/code-mode/.pantheon/deepwork/initial-token-40k/session.json +0 -11
  83. package/src/pantheon/delegate-task-adapter.ts +0 -444
@@ -13,7 +13,7 @@ import { execSync } from 'node:child_process'
13
13
  import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from 'node:fs'
14
14
  import { homedir } from 'node:os'
15
15
  import { join } from 'node:path'
16
- import { fileURLToPath } from 'node:url'
16
+ import { fileURLToPath, pathToFileURL } from 'node:url'
17
17
 
18
18
  const __dirname = fileURLToPath(new URL('.', import.meta.url))
19
19
  const ROOT = join(__dirname, '..')
@@ -106,68 +106,136 @@ function copyPluginFiles(srcDir, dstDir) {
106
106
  return result
107
107
  }
108
108
 
109
- // ── Main ───────────────────────────────────────────────────────────────
110
-
111
- try {
112
- const configDir = resolveConfigDir()
113
- if (!configDir) {
114
- // User hasn't run init yet — silent exit, no error
115
- process.exit(0)
116
- }
109
+ // ── npm env hygiene ────────────────────────────────────────────────────
117
110
 
118
- const tuiCopyDir = join(configDir, 'plugins', 'pantheon-tui')
119
- if (!existsSync(tuiCopyDir)) {
120
- // TUI plugin not installed — silent exit
121
- process.exit(0)
122
- }
111
+ /**
112
+ * npm lifecycle env vars that put a nested `npm ci` into global context and
113
+ * make it abort with ECIGLOBAL ("`npm ci` does not work for global packages").
114
+ * When the package itself is installed with `npm install -g`, npm injects these
115
+ * into the postinstall environment; the TUI dependency sync is a local
116
+ * operation and must never inherit them.
117
+ */
118
+ const GLOBAL_NPM_ENV_KEYS = new Set([
119
+ 'npm_config_global',
120
+ 'npm_config_globalconfig',
121
+ 'npm_config_prefix',
122
+ 'npm_config_location',
123
+ ])
123
124
 
124
- // Source: TUI plugin inside the installed package
125
- const tuiSrcDir = join(ROOT, 'src', 'plugins', 'tui')
126
- if (!existsSync(tuiSrcDir)) {
127
- // Source not found (shouldn't happen in a proper install)
128
- process.exit(0)
125
+ /**
126
+ * Return a copy of `env` with global-context npm config vars removed.
127
+ * Matching is case-insensitive so Windows-style uppercase keys are handled.
128
+ * @param {NodeJS.ProcessEnv} [env]
129
+ * @returns {NodeJS.ProcessEnv}
130
+ */
131
+ export function sanitizeNpmEnv(env = process.env) {
132
+ const clean = { ...env }
133
+ for (const key of Object.keys(clean)) {
134
+ if (GLOBAL_NPM_ENV_KEYS.has(key.toLowerCase())) delete clean[key]
129
135
  }
136
+ return clean
137
+ }
130
138
 
131
- // Compare versions before copy
132
- const installedVersion = readVersion(join(tuiCopyDir, 'package.json'))
133
- const sourceVersion = readVersion(join(tuiSrcDir, 'package.json'))
134
-
135
- // Copy fresh files
136
- const { created } = copyPluginFiles(tuiSrcDir, tuiCopyDir)
139
+ // ── Main ───────────────────────────────────────────────────────────────
137
140
 
138
- // Refresh dependencies in the copy. npm install is only allowed when the
139
- // operator explicitly opts into the non-deterministic legacy fallback.
141
+ async function main() {
140
142
  try {
141
- execSync('npm ci --omit=dev --no-audit --no-fund', { cwd: tuiCopyDir, stdio: 'pipe' })
142
- } catch (err) {
143
- if (process.env.PANTHEON_ALLOW_NPM_INSTALL_FALLBACK === '1') {
144
- console.warn(`⚠️ npm ci failed; explicit fallback enabled: ${err.message}`)
145
- try {
146
- execSync('npm install --omit=dev --no-audit --no-fund --package-lock=false', {
147
- cwd: tuiCopyDir,
148
- stdio: 'pipe',
149
- })
150
- } catch (fallbackError) {
151
- console.warn(`⚠️ TUI dependency refresh skipped: ${fallbackError.message}`)
143
+ const configDir = resolveConfigDir()
144
+ if (!configDir) {
145
+ // User hasn't run init yet — silent exit, no error
146
+ process.exit(0)
147
+ }
148
+
149
+ const tuiCopyDir = join(configDir, 'plugins', 'pantheon-tui')
150
+ if (!existsSync(tuiCopyDir)) {
151
+ // beta.5: no TUI install (plugins component omitted) — skip the TUI
152
+ // sync but STILL refresh the copy-only artifacts below.
153
+ const { syncCopyArtifacts } = await import('./sync-artifacts.mjs')
154
+ const synced = syncCopyArtifacts(configDir)
155
+ if (synced.errors.length > 0) {
156
+ for (const e of synced.errors) console.error(` ⚠️ artifact sync: ${e}`)
152
157
  }
158
+ console.log(
159
+ ` Artifacts synced: ${synced.created} updated, ${synced.skipped} already current`,
160
+ )
161
+ return
162
+ }
163
+
164
+ // Source: TUI plugin inside the installed package
165
+ const tuiSrcDir = join(ROOT, 'src', 'plugins', 'tui')
166
+ if (!existsSync(tuiSrcDir)) {
167
+ throw new Error(`TUI plugin source not found: ${tuiSrcDir}`)
168
+ }
169
+
170
+ // Compare versions before copy
171
+ const installedVersion = readVersion(join(tuiCopyDir, 'package.json'))
172
+ const sourceVersion = readVersion(join(tuiSrcDir, 'package.json'))
173
+
174
+ // Copy fresh files
175
+ const { created } = copyPluginFiles(tuiSrcDir, tuiCopyDir)
176
+
177
+ // Lockfile installation is the only accepted dependency path. Strip
178
+ // global-context npm vars so a global postinstall can't trigger ECIGLOBAL.
179
+ execSync('npm ci --omit=dev --no-audit --no-fund', {
180
+ cwd: tuiCopyDir,
181
+ stdio: 'pipe',
182
+ env: sanitizeNpmEnv(),
183
+ })
184
+
185
+ // Log result
186
+ if (installedVersion && sourceVersion && installedVersion === sourceVersion && created === 0) {
187
+ console.log(` TUI plugin already up to date (v${sourceVersion})`)
153
188
  } else {
154
- console.warn(
155
- `⚠️ TUI dependency refresh skipped after npm ci failure. Set ` +
156
- `PANTHEON_ALLOW_NPM_INSTALL_FALLBACK=1 to explicitly allow npm install: ${err.message}`,
189
+ console.log(
190
+ ` TUI plugin updated to v${sourceVersion || 'latest'}${installedVersion ? ` (was v${installedVersion})` : ''}`,
157
191
  )
158
192
  }
159
- }
160
193
 
161
- // Log result
162
- if (installedVersion && sourceVersion && installedVersion === sourceVersion && created === 0) {
163
- console.log(` TUI plugin already up to date (v${sourceVersion})`)
164
- } else {
165
- console.log(
166
- ` TUI plugin updated to v${sourceVersion || 'latest'}${installedVersion ? ` (was v${installedVersion})` : ''}`,
167
- )
194
+ // ── beta.5 freshness guarantee: refresh ALL copy-only artifacts ──
195
+ // Agents, skills, AGENTS.md, commands, MCP scripts, code-mode payload.
196
+ // The plugin references in opencode.json already point INTO this package,
197
+ // so only the copies would go stale after a package update.
198
+ const { syncCopyArtifacts } = await import('./sync-artifacts.mjs')
199
+ const { readState, writeState } = await import('./install/state.mjs')
200
+ const pkgVersion = readVersion(join(ROOT, 'package.json')) ?? 'unknown'
201
+ const synced = syncCopyArtifacts(configDir)
202
+ if (synced.errors.length > 0) {
203
+ for (const e of synced.errors) console.error(` ⚠️ artifact sync: ${e}`)
204
+ }
205
+ console.log(` Artifacts synced: ${synced.created} updated, ${synced.skipped} already current`)
206
+
207
+ // Version marker so doctor can detect a stale installation and point the
208
+ // user at `pantheon-opencode update` for the full refresh (config merge,
209
+ // venv, MCP entries).
210
+ try {
211
+ const previous = readState(configDir)
212
+ writeState(configDir, {
213
+ pantheon_version: pkgVersion,
214
+ previous_version: previous?.pantheon_version ?? null,
215
+ })
216
+ if (previous?.pantheon_version && previous.pantheon_version !== pkgVersion) {
217
+ console.log(
218
+ ` ⚠️ Installation moved ${previous.pantheon_version} → ${pkgVersion}. ` +
219
+ 'Run `npx pantheon-opencode init` to refresh config/venv/MCP entries.',
220
+ )
221
+ }
222
+ } catch {
223
+ // Marker is best-effort — never fail the postinstall over it.
224
+ }
225
+ } catch (err) {
226
+ console.error(`❌ TUI sync failed: ${err.message}`)
227
+ process.exitCode = 1
168
228
  }
169
- } catch (err) {
170
- // Postinstall scripts must never break npm install
171
- console.warn(`⚠️ TUI sync skipped: ${err.message}`)
172
- process.exit(0)
229
+ }
230
+
231
+ // Only run when invoked directly (`node scripts/sync-tui.mjs`). Importing this
232
+ // module for unit tests must not trigger the side-effecting postinstall sync.
233
+ const isDirectRun =
234
+ Boolean(process.argv[1]) && pathToFileURL(process.argv[1]).href === import.meta.url
235
+
236
+ if (isDirectRun) {
237
+ main().catch((err) => {
238
+ console.error(`❌ TUI sync failed: ${err.message}`)
239
+ process.exitCode = 1
240
+ })
173
241
  }