pantheon-opencode 1.6.0-beta.7 → 1.6.0-beta.8

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 (49) hide show
  1. package/AGENTS.md +58 -15
  2. package/CHANGELOG.md +37 -100
  3. package/README.md +78 -102
  4. package/README.pt-BR.md +19 -37
  5. package/package.json +1 -2
  6. package/plugin.json +2 -2
  7. package/pyproject.toml +1 -1
  8. package/scripts/build-agents-md.mjs +2 -3
  9. package/scripts/doctor.mjs +22 -104
  10. package/scripts/hooks/scan-secrets.sh +97 -0
  11. package/scripts/install/config-migration.mjs +9 -42
  12. package/scripts/install/interactive.mjs +1 -1
  13. package/scripts/install/opencode.mjs +114 -43
  14. package/scripts/install/strings.mjs +0 -8
  15. package/scripts/probe-pantheon-cost.mjs +3 -16
  16. package/scripts/test-opencode-v2-sandbox.sh +203 -786
  17. package/scripts/validate-sandbox-install.mjs +138 -0
  18. package/skills-lock.json +3 -4
  19. package/src/agents/aphrodite.md +2 -9
  20. package/src/agents/demeter.md +0 -9
  21. package/src/agents/gaia.md +1 -0
  22. package/src/agents/hephaestus.md +0 -9
  23. package/src/agents/hermes.md +2 -9
  24. package/src/agents/mnemosyne.md +14 -42
  25. package/src/agents/prometheus.md +0 -9
  26. package/src/agents/themis.md +0 -1
  27. package/src/agents/zeus.md +180 -19
  28. package/src/instructions/agent-return-format.instructions.md +48 -3
  29. package/src/instructions/memory-protocol.instructions.md +8 -9
  30. package/src/instructions/zeus-anti-stall.instructions.md +54 -6
  31. package/src/instructions/zeus-council-synthesis.instructions.md +71 -0
  32. package/src/instructions/zeus-memory-operations.instructions.md +4 -4
  33. package/src/instructions/zeus-timeout-retry.instructions.md +81 -7
  34. package/src/pantheon/v2-delegation-enforce.ts +2 -1
  35. package/src/plugin-v2.ts +251 -168
  36. package/src/plugins/pantheon-hooks.ts +35 -82
  37. package/src/plugins/tui/dist/tui.d.ts.map +1 -1
  38. package/src/plugins/tui/dist/tui.js +26 -50
  39. package/src/plugins/tui/package-lock.json +2 -2
  40. package/src/plugins/tui/package.json +1 -1
  41. package/src/plugins/tui/src/index.tsx +25 -60
  42. package/src/routing.yml +4 -2
  43. package/src/skills/README.md +7 -12
  44. package/src/skills/agent-coordination/SKILL.md +83 -13
  45. package/src/skills/auto-continue/SKILL.md +13 -14
  46. package/src/skills/context-compression/SKILL.md +26 -879
  47. package/src/pantheon/secret-scanner.ts +0 -489
  48. package/src/plugins/tui/README.md +0 -14
  49. package/src/skills/council-synthesis/SKILL.md +0 -29
@@ -4,13 +4,14 @@
4
4
  * Re-created per Council Synthesis 2026-08-05 (P0): the previous hook plugin
5
5
  * was removed on 24-25/07 because it used the Bun Shell `$` from the plugin
6
6
  * API, whose interface has NO `.timeout()` method — every hook crashed with
7
- * `TypeError: $.quiet().nothrow().timeout is not a function`. The remaining
7
+ * `TypeError: $.quiet().nothrow().timeout is not a function`. The 10 functional
8
8
  * scripts in scripts/hooks/ (gitleaks covers only staged files, NOT tool
9
9
  * calls) were left orphaned. This plugin wires them back into the opencode
10
10
  * runtime via `node:child_process` (version-proof — see hook-runner.ts).
11
11
  *
12
- * Hook mapping:
13
- * - tool.execute.before → in-process secret scan, validate-talos-scope.sh,
12
+ * Hook mapping (Claude Code protocol payload {tool_name, tool_input,
13
+ * agent_id, session_id} delivered on the script stdin):
14
+ * - tool.execute.before → validate-talos-scope.sh, scan-secrets.sh,
14
15
  * validate-tool-safety.sh + on-subagent-delegation-start.sh (only when the
15
16
  * tool is a delegation tool such as `task`, to keep the audit log honest)
16
17
  * - tool.execute.after → format-multi-language.sh, log-session-start.sh +
@@ -22,7 +23,7 @@
22
23
  * ({tool, sessionID, callID}). P0-4 (2026-08-06): a `chat.params` hook
23
24
  * records the active agent per session (sessionAgent map), so payloadFor()
24
25
  * now resolves the real agent_id — validate-talos-scope.sh enforces the
25
- * Talos file-count boundary; the agent-agnostic secret scanner and
26
+ * Talos file-count boundary; the agent-agnostic scan-secrets and
26
27
  * validate-tool-safety protections remain fully active.
27
28
  * - tool.execute.before CAN hard-deny — the old "cannot hard-deny" claim
28
29
  * below this line is OUTDATED for 1.18.x (verified 2026-08-06 on the
@@ -33,15 +34,15 @@
33
34
  * the tool body runs (the docs' .env-protection example relies on this).
34
35
  * The permission system remains the primary deny path, but a thrown
35
36
  * hook error is now a supported hard block (hybrid blocking, Fix 2
36
- * 2026-08-06): high-confidence token matches → the plugin logs via
37
- * reportFailure (non-TUI channels) and THEN throws
37
+ * 2026-08-06): scan-secrets.sh exits 2 on high-confidence token matches
38
+ * → the plugin logs via reportFailure (non-TUI channels) and THEN throws
38
39
  * the DELIBERATE block error — the ONLY throw in this plugin. Exit 1
39
40
  * (low-confidence header/KEY names) stays advisory: toast + logs, no
40
41
  * block.
41
42
  * - Logging policy (P0 fix 2026-08-06): NON-ZERO exit → NEVER console.
42
43
  * `console.error` in a plugin writes to the process stderr, which the
43
44
  * opencode TUI renders directly into the terminal — that polluted the
44
- * chat with noisy nonzero hook output
45
+ * chat with `[pantheon-hooks:scan-secrets.sh] exit 1: [SECRET SCAN]...`
45
46
  * spam on every tool call. Non-zero exits now go to THREE non-TUI channels:
46
47
  * 1. client.tui.showToast() — one short, deduped TUI toast (per
47
48
  * (script, exit code, masked match) per session) so the user gets a
@@ -59,8 +60,8 @@
59
60
  * - Hooks NEVER throw: every body is try/catch'd and only logs. All three
60
61
  * reporting channels (toast, app.log, file append) are individually
61
62
  * try/catch'd so a failure in one can never take down the session.
62
- * EXCEPTION (Fix 2, 2026-08-06): the deliberate high-confidence secret
63
- * block — when the in-process scanner finds a token, the hook awaits
63
+ * EXCEPTION (Fix 2, 2026-08-06): the deliberate scan-secrets high-
64
+ * confidence block — when scan-secrets.sh exits 2, the hook awaits
64
65
  * reportFailure FIRST (app.log + hooks.log) and only then throws. The
65
66
  * throw is deliberately NOT swallowed by the surrounding try/catch
66
67
  * (handled via a blockError variable thrown after it), because that
@@ -176,7 +177,6 @@ import { appendFile, mkdir } from 'node:fs/promises'
176
177
  import { join } from 'node:path'
177
178
  import type { Plugin, PluginInput } from '@opencode-ai/plugin'
178
179
  import { pantheonPluginOnce } from '../pantheon/plugin-once.ts'
179
- import { scanSecretPayload } from '../pantheon/secret-scanner.ts'
180
180
  import { getSharedBoard } from '../pantheon/shared-board.ts'
181
181
  import { type HookPayload, type HookResult, runHook } from './hook-runner.ts'
182
182
 
@@ -320,7 +320,8 @@ const sessionAgent = new Map<string, { agent: string; at: number }>()
320
320
  const RISKY_PATH_RE = /migration|schema\.sql|alembic|models\/|auth|password|token|secret/i
321
321
 
322
322
  /**
323
- * Fix 1 (2026-08-06): high-confidence secret token formats. Used by maskSecret()
323
+ * Fix 1 (2026-08-06): high-confidence secret token formats — mirrors
324
+ * scripts/hooks/scan-secrets.sh HIGH_CONFIDENCE_PATTERNS. Used by maskSecret()
324
325
  * to REDACT secret VALUES from any plugin log line: the talos-guard must
325
326
  * never write a raw credential to hooks.log (a real `sk-bf-...` token leaked
326
327
  * in plain text there on 2026-08-06). Header/KEY names (the Bifrost header alone,
@@ -340,8 +341,8 @@ function maskSecret(text: string): string {
340
341
  }
341
342
 
342
343
  /**
343
- * Fix 2 (2026-08-06): message thrown on a high-confidence token match.
344
- * This is the ONLY deliberate throw in the
344
+ * Fix 2 (2026-08-06): message thrown when scan-secrets.sh exits 2
345
+ * (HIGH_CONFIDENCE token match). This is the ONLY deliberate throw in the
345
346
  * plugin — it is thrown AFTER reportFailure so the log channels always write
346
347
  * first, and it is NOT swallowed by the hook's try/catch: opencode's
347
348
  * Plugin.trigger has no catch around hook invocations, so the rejection
@@ -1092,8 +1093,9 @@ function flushIdleReminders(ctx: HookContext): void {
1092
1093
  * (echo routed to structured log + hooks.log, never the TUI).
1093
1094
  * No console.error / console.log anywhere in this path — the TUI stays clean.
1094
1095
  *
1095
- * Returns the underlying HookResult (or null on unexpected failure) for the
1096
- * non-scanner script hooks.
1096
+ * Returns the underlying HookResult (or null on unexpected failure) so the
1097
+ * tool.execute.before handler can inspect scan-secrets.sh's exit code for the
1098
+ * Fix-2 hybrid block (code 2 = high-confidence token → throw after logging).
1097
1099
  */
1098
1100
  async function safeRun(
1099
1101
  ctx: HookContext,
@@ -1175,26 +1177,15 @@ const plugin: Plugin = async ({ client, directory }) => {
1175
1177
  // catch: opencode propagates the rejection to fail the tool call.
1176
1178
  let blockError: Error | null = null
1177
1179
  try {
1178
- const hooks = ['validate-talos-scope.sh', 'validate-tool-safety.sh']
1180
+ const hooks = ['validate-talos-scope.sh', 'scan-secrets.sh', 'validate-tool-safety.sh']
1179
1181
  // P0-2: the delegation record must exist BEFORE the payload is built
1180
1182
  // so the start hook receives the delegation_id the stop hook joins on.
1181
- const args = output?.args ?? (input as { args?: unknown }).args
1182
- let payload = payloadFor(input, args)
1183
- // Validate and scan before touching delegation args or launching any
1184
- // hook subprocess. The shared scanner catches malformed serialization
1185
- // and returns a fixed fail-closed result.
1186
- const scan = scanSecretPayload({ ...payload, tool_input: args })
1187
- if (scan.status === 'invalid') {
1188
- blockError = new Error(
1189
- '[pantheon-hooks] Bloqueado: o scanner de segredos não validou a entrada da ferramenta (fail-closed)',
1190
- )
1191
- } else if (scan.status === 'block') {
1192
- blockError = new Error(SECRET_BLOCK_MESSAGE)
1193
- }
1194
- if (blockError === null && DELEGATION_TOOL_RE.test(input.tool)) {
1183
+ let payload = payloadFor(input, output?.args ?? {})
1184
+ if (DELEGATION_TOOL_RE.test(input.tool)) {
1195
1185
  hooks.push('on-subagent-delegation-start.sh')
1196
1186
  // tool.execute.before carries args on `output` (typed); `input.args`
1197
1187
  // also exists at runtime — read from whichever is present.
1188
+ const args = output?.args ?? (input as { args?: unknown }).args
1198
1189
  const start: DelegationStart = {
1199
1190
  delegation_id: crypto.randomUUID(),
1200
1191
  agent: delegationAgent(input.tool, args),
@@ -1231,7 +1222,7 @@ const plugin: Plugin = async ({ client, directory }) => {
1231
1222
  const isWriteIntent =
1232
1223
  WRITE_INTENT_TOOLS.has(input.tool) ||
1233
1224
  (input.tool === 'bash' && BASH_WRITE_INTENT_RE.test(bashCommand))
1234
- if (blockError === null && isWriteIntent) {
1225
+ if (isWriteIntent) {
1235
1226
  try {
1236
1227
  const values: string[] = []
1237
1228
  const scan = (v: unknown): void => {
@@ -1282,63 +1273,25 @@ const plugin: Plugin = async ({ client, directory }) => {
1282
1273
  // Advisory only — a failure here must never take down the hook.
1283
1274
  }
1284
1275
  }
1285
- // Scan the same serialized hook payload the removed stdin protocol
1286
- // delivered, but in-process and without logging untrusted input.
1287
- if (scan.status === 'invalid') {
1288
- const message =
1289
- '[pantheon-hooks] Bloqueado: o scanner de segredos não validou a entrada da ferramenta (fail-closed)'
1290
- await reportFailure(
1291
- ctx,
1292
- message,
1293
- { script: 'secret-scan', tool: input.tool, blocked: true, reason: 'invalid-input' },
1294
- 'error',
1295
- )
1296
- blockError = new Error(message)
1297
- } else if (scan.status === 'block') {
1298
- for (const finding of scan.findings) {
1299
- await reportFailure(
1300
- ctx,
1301
- `[SECRET SCAN] High-confidence secret detected: ${finding.masked} (pattern: ${finding.pattern})`,
1302
- { script: 'secret-scan', tool: input.tool, pattern: finding.pattern },
1303
- 'error',
1304
- )
1305
- }
1276
+ // Fix 2 hybrid blocking: safeRun returns each script's HookResult;
1277
+ // scan-secrets.sh exits 2 on HIGH_CONFIDENCE token matches
1278
+ // (sk-bf-*, AKIA..., ghp_..., glpat-..., JWT, bearer...) → BLOCK the
1279
+ // tool call. Exit 1 (low-confidence header/KEY names) stays advisory
1280
+ // — safeRun already logged + toasted it, no block. The toast for
1281
+ // code 1/2 fires inside safeRun BEFORE we reach the throw, and the
1282
+ // reportFailure below is awaited first — log channels always write.
1283
+ const results = await Promise.all(
1284
+ hooks.map(async (script) => ({ script, result: await safeRun(ctx, script, payload) })),
1285
+ )
1286
+ const scan = results.find((r) => r.script === 'scan-secrets.sh')
1287
+ if (scan?.result?.code === 2) {
1306
1288
  await reportFailure(
1307
1289
  ctx,
1308
1290
  SECRET_BLOCK_MESSAGE,
1309
- { script: 'secret-scan', tool: input.tool, blocked: true },
1291
+ { script: 'scan-secrets', tool: input.tool, blocked: true, code: 2 },
1310
1292
  'error',
1311
1293
  )
1312
- void notifyToast(ctx, {
1313
- title: 'Pantheon Hook',
1314
- message: '⚠️ High-confidence secret detected — tool call blocked',
1315
- variant: 'error',
1316
- duration: 4000,
1317
- category: 'errors',
1318
- dedupeKey: `secret-scan|block|${input.sessionID}`,
1319
- })
1320
1294
  blockError = new Error(SECRET_BLOCK_MESSAGE)
1321
- } else if (scan.status === 'advisory') {
1322
- for (const finding of scan.findings) {
1323
- await reportFailure(
1324
- ctx,
1325
- `[SECRET SCAN] Potential secret detected (low confidence): ${finding.masked} (pattern: ${finding.pattern})`,
1326
- { script: 'secret-scan', tool: input.tool, pattern: finding.pattern },
1327
- 'warn',
1328
- )
1329
- }
1330
- void notifyToast(ctx, {
1331
- title: 'Pantheon Hook',
1332
- message: '⚠️ Possible secret detected — see log',
1333
- variant: 'warning',
1334
- duration: 4000,
1335
- category: 'errors',
1336
- dedupeKey: `secret-scan|advisory|${input.sessionID}`,
1337
- })
1338
- }
1339
-
1340
- if (blockError === null) {
1341
- await Promise.all(hooks.map(async (script) => safeRun(ctx, script, payload)))
1342
1295
  }
1343
1296
  } catch {
1344
1297
  // Never let a hook take down the opencode session — except the
@@ -1 +1 @@
1
- {"version":3,"file":"tui.d.ts","names":[],"sources":["../src/index.tsx"],"mappings":"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"}
1
+ {"version":3,"file":"tui.d.ts","names":[],"sources":["../src/index.tsx"],"mappings":"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"}
@@ -167,7 +167,7 @@ async function detectVersion(api) {
167
167
  if (ver) return ver;
168
168
  }
169
169
  } catch {}
170
- return "1.6.0-beta.7";
170
+ return "1.6.0-beta.8";
171
171
  }
172
172
  /**
173
173
  * usage-bar — AI subscription usage gauge for the opencode TUI.
@@ -509,22 +509,12 @@ async function loadConfig(configPath) {
509
509
  return defaultConfig();
510
510
  }
511
511
  }
512
- /** Vendored usage-bar plugin init: reads config, seeds the kv cache, registers
513
- * the `app_bottom` slot, then starts the poll loop. Skips everything (and
512
+ /** Vendored usage-bar plugin init: reads config, seeds the kv cache, starts
513
+ * the poll loop and registers the `app_bottom` slot. Skips everything (and
514
514
  * registers nothing) when no provider is enabled — mirrors the standalone
515
515
  * plugin's early return. */
516
516
  async function setupUsageBar(api) {
517
- let disposed = false;
518
- let clockInterval;
519
- const pollTimeouts = /* @__PURE__ */ new Set();
520
- api.lifecycle.onDispose(() => {
521
- disposed = true;
522
- if (clockInterval !== void 0) clearInterval(clockInterval);
523
- for (const timeout of pollTimeouts) clearTimeout(timeout);
524
- pollTimeouts.clear();
525
- });
526
517
  const config = await loadConfig(api.state.path?.config);
527
- if (disposed) return;
528
518
  if (api.state.path?.state) opencodeAuthFile = join(api.state.path.state, "auth.json");
529
519
  const enabled = providers.filter((p) => config.providers[p.id].enabled);
530
520
  if (enabled.length === 0) return;
@@ -536,6 +526,28 @@ async function setupUsageBar(api) {
536
526
  const [byProvider, setByProvider] = createSignal(seed);
537
527
  const [byStatus, setByStatus] = createSignal({});
538
528
  const [now, setNow] = createSignal(Date.now());
529
+ setInterval(() => setNow(Date.now()), 1e3);
530
+ for (const p of enabled) {
531
+ const cfg = config.providers[p.id];
532
+ const poll = async () => {
533
+ const statusP = config.showStatus && p.statusUrl ? fetchStatus(p.statusUrl) : null;
534
+ const [all, status] = await Promise.all([p.fetchUsage(cfg), statusP]);
535
+ if (all) {
536
+ const windows = all.filter((w) => cfg.show[w.category] && w.resetsAt > Date.now());
537
+ setByProvider((prev) => ({
538
+ ...prev,
539
+ [p.id]: windows
540
+ }));
541
+ api.kv.set(`usage-bar.${p.id}.windows`, windows);
542
+ }
543
+ if (status !== null) setByStatus((prev) => ({
544
+ ...prev,
545
+ [p.id]: status
546
+ }));
547
+ setTimeout(poll, all ? POLL_MS : POLL_MS * 3);
548
+ };
549
+ poll();
550
+ }
539
551
  api.slots.register({
540
552
  order: 60,
541
553
  slots: { app_bottom() {
@@ -684,42 +696,6 @@ async function setupUsageBar(api) {
684
696
  });
685
697
  } }
686
698
  });
687
- clockInterval = setInterval(() => setNow(Date.now()), 1e3);
688
- const schedulePoll = (run, delay) => {
689
- if (disposed) return;
690
- const timeout = setTimeout(() => {
691
- pollTimeouts.delete(timeout);
692
- if (!disposed) run();
693
- }, delay);
694
- pollTimeouts.add(timeout);
695
- };
696
- for (const p of enabled) {
697
- const cfg = config.providers[p.id];
698
- const poll = async () => {
699
- if (disposed) return;
700
- const statusP = config.showStatus && p.statusUrl ? fetchStatus(p.statusUrl) : null;
701
- const [all, status] = await Promise.all([p.fetchUsage(cfg), statusP]);
702
- if (disposed) return;
703
- if (all) {
704
- const windows = all.filter((w) => cfg.show[w.category] && w.resetsAt > Date.now());
705
- setByProvider((prev) => ({
706
- ...prev,
707
- [p.id]: windows
708
- }));
709
- api.kv.set(`usage-bar.${p.id}.windows`, windows);
710
- }
711
- if (status !== null) setByStatus((prev) => ({
712
- ...prev,
713
- [p.id]: status
714
- }));
715
- schedulePoll(runPoll, all ? POLL_MS : POLL_MS * 3);
716
- };
717
- const runPoll = () => {
718
- if (disposed) return;
719
- poll().catch(() => schedulePoll(runPoll, POLL_MS * 3));
720
- };
721
- runPoll();
722
- }
723
699
  }
724
700
  /** True for `\s` characters (space, tab, newline, CR) — plain char checks so
725
701
  * the parser stays regex-free (CodeQL flagged the old `\s*`/`\s+` + `(.+)`
@@ -2232,7 +2208,7 @@ const tui = (api, _options, _meta) => {
2232
2208
  if (pantheonPluginOnce("pantheon:tui")) return;
2233
2209
  const [version, setVersion] = createSignal(null);
2234
2210
  detectVersion(api).then((detected) => setVersion(detected)).catch(() => setVersion(null));
2235
- setupUsageBar(api).catch(() => void 0);
2211
+ setupUsageBar(api);
2236
2212
  const liveStore = createLiveDelegationStore();
2237
2213
  const unsubscribeLive = registerLiveDelegationEvents(api, liveStore);
2238
2214
  api.lifecycle.onDispose(() => {
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "pantheon-tui",
3
- "version": "1.6.0-beta.7",
3
+ "version": "1.6.0-beta.8",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "pantheon-tui",
9
- "version": "1.6.0-beta.7",
9
+ "version": "1.6.0-beta.8",
10
10
  "license": "MIT",
11
11
  "dependencies": {
12
12
  "@opencode-ai/plugin": "1.18.33",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pantheon-tui",
3
3
  "type": "module",
4
- "version": "1.6.0-beta.7",
4
+ "version": "1.6.0-beta.8",
5
5
  "description": "Pantheon TUI plugin — sidebar (version, preset, sessions, real-time delegations) + AI subscription usage gauges incl. OpenCode Go/Zen dollar limits (vendored, MIT)",
6
6
  "license": "MIT",
7
7
  "engines": {
@@ -740,27 +740,12 @@ async function loadConfig(configPath: string | undefined): Promise<UsageBarConfi
740
740
  }
741
741
  }
742
742
 
743
- /** Vendored usage-bar plugin init: reads config, seeds the kv cache, registers
744
- * the `app_bottom` slot, then starts the poll loop. Skips everything (and
743
+ /** Vendored usage-bar plugin init: reads config, seeds the kv cache, starts
744
+ * the poll loop and registers the `app_bottom` slot. Skips everything (and
745
745
  * registers nothing) when no provider is enabled — mirrors the standalone
746
746
  * plugin's early return. */
747
747
  async function setupUsageBar(api: TuiPluginApi) {
748
- let disposed = false
749
- let clockInterval: ReturnType<typeof setInterval> | undefined
750
- const pollTimeouts = new Set<ReturnType<typeof setTimeout>>()
751
-
752
- // This optional feature owns timers independently from the sidebar. A slow
753
- // config read may finish after shutdown, so register cleanup before awaiting
754
- // anything and check the disposed flag at every async boundary.
755
- api.lifecycle.onDispose(() => {
756
- disposed = true
757
- if (clockInterval !== undefined) clearInterval(clockInterval)
758
- for (const timeout of pollTimeouts) clearTimeout(timeout)
759
- pollTimeouts.clear()
760
- })
761
-
762
748
  const config = await loadConfig(api.state.path?.config)
763
- if (disposed) return
764
749
  if (api.state.path?.state) opencodeAuthFile = join(api.state.path.state, 'auth.json')
765
750
 
766
751
  const enabled = providers.filter((p) => config.providers[p.id].enabled)
@@ -777,6 +762,28 @@ async function setupUsageBar(api: TuiPluginApi) {
777
762
  const [byStatus, setByStatus] = createSignal<Record<string, StatusIndicator>>({})
778
763
  const [now, setNow] = createSignal(Date.now())
779
764
 
765
+ setInterval(() => setNow(Date.now()), 1_000)
766
+
767
+ for (const p of enabled) {
768
+ const cfg = config.providers[p.id]
769
+ const poll = async () => {
770
+ // Fetch usage and status concurrently; status pages are CDN-backed and
771
+ // never throttle, so we poll them on the same cadence as usage.
772
+ const statusP = config.showStatus && p.statusUrl ? fetchStatus(p.statusUrl) : null
773
+ const [all, status] = await Promise.all([p.fetchUsage(cfg), statusP])
774
+ if (all) {
775
+ const windows = all.filter((w) => cfg.show[w.category] && w.resetsAt > Date.now())
776
+ setByProvider((prev) => ({ ...prev, [p.id]: windows }))
777
+ api.kv.set(`usage-bar.${p.id}.windows`, windows)
778
+ }
779
+ // `null` means the status fetch failed — keep the last known indicator.
780
+ if (status !== null) setByStatus((prev) => ({ ...prev, [p.id]: status }))
781
+ // Back off when the usage fetch failed (e.g. 429 — these endpoints throttle).
782
+ setTimeout(poll, all ? POLL_MS : POLL_MS * 3)
783
+ }
784
+ void poll()
785
+ }
786
+
780
787
  api.slots.register({
781
788
  order: 60,
782
789
  slots: {
@@ -864,46 +871,6 @@ async function setupUsageBar(api: TuiPluginApi) {
864
871
  },
865
872
  },
866
873
  })
867
-
868
- // Start background work only once the optional UI slot was accepted. If
869
- // registration fails, the outer fail-open handler can suppress it without
870
- // leaving a timer or provider request running in the background.
871
- clockInterval = setInterval(() => setNow(Date.now()), 1_000)
872
-
873
- const schedulePoll = (run: () => void, delay: number) => {
874
- if (disposed) return
875
- const timeout = setTimeout(() => {
876
- pollTimeouts.delete(timeout)
877
- if (!disposed) run()
878
- }, delay)
879
- pollTimeouts.add(timeout)
880
- }
881
-
882
- for (const p of enabled) {
883
- const cfg = config.providers[p.id]
884
- const poll = async () => {
885
- if (disposed) return
886
- // Fetch usage and status concurrently; status pages are CDN-backed and
887
- // never throttle, so we poll them on the same cadence as usage.
888
- const statusP = config.showStatus && p.statusUrl ? fetchStatus(p.statusUrl) : null
889
- const [all, status] = await Promise.all([p.fetchUsage(cfg), statusP])
890
- if (disposed) return
891
- if (all) {
892
- const windows = all.filter((w) => cfg.show[w.category] && w.resetsAt > Date.now())
893
- setByProvider((prev) => ({ ...prev, [p.id]: windows }))
894
- api.kv.set(`usage-bar.${p.id}.windows`, windows)
895
- }
896
- // `null` means the status fetch failed — keep the last known indicator.
897
- if (status !== null) setByStatus((prev) => ({ ...prev, [p.id]: status }))
898
- // Back off when the usage fetch failed (e.g. 429 — these endpoints throttle).
899
- schedulePoll(runPoll, all ? POLL_MS : POLL_MS * 3)
900
- }
901
- const runPoll = () => {
902
- if (disposed) return
903
- void poll().catch(() => schedulePoll(runPoll, POLL_MS * 3))
904
- }
905
- runPoll()
906
- }
907
874
  }
908
875
 
909
876
  /* ─── Delegations (real-time panel) ────────────────────────
@@ -3167,9 +3134,7 @@ const tui: TuiPlugin = (api, _options, _meta) => {
3167
3134
  .catch(() => setVersion(null))
3168
3135
 
3169
3136
  // Vendored feature (MIT): AI subscription usage gauges.
3170
- // Usage indicators are optional: a config, storage, or slot failure must
3171
- // never turn an otherwise healthy TUI startup into an unhandled rejection.
3172
- void setupUsageBar(api).catch(() => undefined)
3137
+ void setupUsageBar(api) // async init — never blocks sidebar registration
3173
3138
 
3174
3139
  // ── Live delegation store (tool-part tracker + sidebar invalidation) ──
3175
3140
  // The Delegations panel's PRIMARY source is `api.client.session.children`
package/src/routing.yml CHANGED
@@ -133,7 +133,7 @@ agents:
133
133
  - artifact-management
134
134
  - context-compression
135
135
  - auto-continue
136
- - council-synthesis
136
+ - incremental-implementation
137
137
  athena:
138
138
  role: Strategic planner
139
139
  reasoning_effort: high
@@ -188,6 +188,8 @@ agents:
188
188
  - pantheon_code_mode
189
189
  skills:
190
190
  - tdd-with-agents
191
+ - file-prompts
192
+ - streaming-patterns
191
193
  - git-workflow-and-versioning
192
194
  - incremental-implementation
193
195
  aphrodite:
@@ -209,7 +211,7 @@ agents:
209
211
  skills:
210
212
  - tdd-with-agents
211
213
  - visual-review-pipeline
212
- - incremental-implementation
214
+ - file-prompts
213
215
  demeter:
214
216
  role: Database specialist
215
217
  reasoning_effort: medium
@@ -4,14 +4,14 @@
4
4
 
5
5
  Skills are reference modules loaded on demand by agents. Each skill provides domain-specific knowledge — architecture patterns, security checklists, optimization strategies, and implementation guides. Agents load skills when their task matches the skill's description, keeping context focused and token-efficient.
6
6
 
7
- There are **21 skills** divided into **5 domains**.
7
+ There are **20 skills** divided into **5 domains**.
8
8
 
9
9
  ## Skills by Domain
10
10
 
11
11
  ### Domain 1: Orchestration & Workflow
12
12
 
13
13
  1. **agent-coordination** — `agent-coordination/SKILL.md`
14
- - Lightweight coordination for substantial tasks with multiple independent specialists.
14
+ - Multi-agent orchestration with model routing, category delegation, and sprint management.
15
15
  - Used by: Zeus
16
16
 
17
17
  2. **artifact-management** — `artifact-management/SKILL.md`
@@ -23,8 +23,8 @@ There are **21 skills** divided into **5 domains**.
23
23
  - Used by: Zeus, Apollo, Hephaestus, Nyx, Gaia
24
24
 
25
25
  4. **context-compression** — `context-compression/SKILL.md`
26
- - Level 2 — Pantheon-native context compression with priority scoring, semantic summarization, budget allocation, and cross-references.
27
- - Used by: Zeus, Mnemosyne, Themis
26
+ - Native compaction guidance and explicit checkpoint rules; no automatic compression MCP calls.
27
+ - Used by: Zeus, Mnemosyne
28
28
 
29
29
  5. **memory-bank** — `memory-bank/SKILL.md`
30
30
  - Memory bank rules, structure, and optimization — complete guide to Pantheon memory management.
@@ -34,10 +34,6 @@ There are **21 skills** divided into **5 domains**.
34
34
  - Pin session objectives to prevent scope creep across long multi-agent sessions.
35
35
  - Used by: Zeus, Mnemosyne
36
36
 
37
- - **council-synthesis** — `council-synthesis/SKILL.md`
38
- - Full structured council workflow, loaded only for `/pantheon` or a material multi-perspective decision.
39
- - Used by: Zeus
40
-
41
37
  ### Domain 2: Development Tools
42
38
 
43
39
  7. **clonedeps** — `clonedeps/SKILL.md`
@@ -50,7 +46,7 @@ There are **21 skills** divided into **5 domains**.
50
46
 
51
47
  9. **incremental-implementation** — `incremental-implementation/SKILL.md`
52
48
  - Implement in thin vertical slices — one commit per task, testable, rollback-safe.
53
- - Used by: Hermes, Aphrodite, Demeter, Prometheus, Talos
49
+ - Used by: Zeus, Hermes, Aphrodite, Demeter, Prometheus, Talos
54
50
 
55
51
  10. **loop-engineering** — `loop-engineering/SKILL.md`
56
52
  - Iterative refinement pattern for complex problems — solve, review, improve, repeat in controlled cycles.
@@ -128,12 +124,12 @@ The `SKILL.md` file contains YAML frontmatter with `name`, `description`, and pl
128
124
  | 1 | agent-coordination | Orchestration & Workflow | Zeus |
129
125
  | 2 | artifact-management | Orchestration & Workflow | Zeus, Mnemosyne, Iris |
130
126
  | 3 | auto-continue | Orchestration & Workflow | Zeus, Apollo, Hephaestus, Nyx, Gaia |
131
- | 4 | context-compression | Orchestration & Workflow | Zeus, Mnemosyne, Themis |
127
+ | 4 | context-compression | Orchestration & Workflow | Zeus, Mnemosyne |
132
128
  | 5 | memory-bank | Orchestration & Workflow | Mnemosyne, Athena |
133
129
  | 6 | session-goal | Orchestration & Workflow | Zeus, Mnemosyne |
134
130
  | 7 | clonedeps | Development Tools | Hermes, Hephaestus |
135
131
  | 8 | git-workflow-and-versioning | Development Tools | Hermes, Prometheus, Iris |
136
- | 9 | incremental-implementation | Development Tools | Hermes, Aphrodite, Demeter, Prometheus, Talos |
132
+ | 9 | incremental-implementation | Development Tools | Zeus, Hermes, Aphrodite, Demeter, Prometheus, Talos |
137
133
  | 10 | loop-engineering | Development Tools | All agents |
138
134
  | 11 | reflect | Development Tools | All agents |
139
135
  | 12 | simplify | Development Tools | All agents |
@@ -145,7 +141,6 @@ The `SKILL.md` file contains YAML frontmatter with `name`, `description`, and pl
145
141
  | 18 | spec-driven-development | Planning | Athena |
146
142
  | 19 | verification-planning | Planning | Athena, Zeus |
147
143
  | 20 | visual-review-pipeline | Frontend Development | Aphrodite |
148
- | 21 | council-synthesis | Orchestration & Workflow | Zeus |
149
144
 
150
145
  ---
151
146