@polderlabs/bizar 4.4.13 → 4.5.1

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 (118) hide show
  1. package/bizar-dash/CHANGELOG.md +37 -276
  2. package/bizar-dash/dist/assets/__vite-browser-external-BIHI7g3E.js +2 -0
  3. package/bizar-dash/dist/assets/__vite-browser-external-BIHI7g3E.js.map +1 -0
  4. package/bizar-dash/dist/assets/main-eWZ4NlCL.css +1 -0
  5. package/bizar-dash/dist/assets/main-usWhlPWa.js +362 -0
  6. package/bizar-dash/dist/assets/main-usWhlPWa.js.map +1 -0
  7. package/bizar-dash/dist/assets/{mobile-DSb-t42Y.js → mobile-DQLFCjwJ.js} +2 -2
  8. package/bizar-dash/dist/assets/{mobile-DSb-t42Y.js.map → mobile-DQLFCjwJ.js.map} +1 -1
  9. package/bizar-dash/dist/assets/mobile-O6ANdD4W.js +352 -0
  10. package/bizar-dash/dist/assets/mobile-O6ANdD4W.js.map +1 -0
  11. package/bizar-dash/dist/index.html +3 -3
  12. package/bizar-dash/dist/mobile.html +2 -2
  13. package/bizar-dash/skills/agent-baseline/SKILL.md +80 -0
  14. package/bizar-dash/skills/bizar/SKILL.md +116 -0
  15. package/bizar-dash/skills/chat/SKILL.md +74 -0
  16. package/bizar-dash/skills/headroom/SKILL.md +94 -0
  17. package/bizar-dash/skills/lightrag/SKILL.md +86 -0
  18. package/bizar-dash/skills/minimax/SKILL.md +80 -0
  19. package/bizar-dash/skills/obsidian/SKILL.md +68 -0
  20. package/bizar-dash/skills/providers/SKILL.md +75 -0
  21. package/bizar-dash/skills/sdk/SKILL.md +138 -0
  22. package/bizar-dash/skills/self-improvement/SKILL.md +53 -0
  23. package/bizar-dash/skills/skills-cli/SKILL.md +94 -0
  24. package/bizar-dash/skills/usage/SKILL.md +62 -0
  25. package/bizar-dash/src/server/api.mjs +18 -0
  26. package/bizar-dash/src/server/headroom.mjs +645 -0
  27. package/bizar-dash/src/server/memory-lightrag.mjs +272 -2
  28. package/bizar-dash/src/server/memory-obsidian.mjs +230 -0
  29. package/bizar-dash/src/server/memory-store.mjs +189 -0
  30. package/bizar-dash/src/server/minimax-usage-store.mjs +372 -0
  31. package/bizar-dash/src/server/minimax.mjs +196 -5
  32. package/bizar-dash/src/server/providers-store.mjs +956 -0
  33. package/bizar-dash/src/server/routes/_shared.mjs +17 -0
  34. package/bizar-dash/src/server/routes/config.mjs +52 -1
  35. package/bizar-dash/src/server/routes/env-vars.mjs +165 -0
  36. package/bizar-dash/src/server/routes/headroom.mjs +126 -0
  37. package/bizar-dash/src/server/routes/lightrag.mjs +154 -0
  38. package/bizar-dash/src/server/routes/memory.mjs +668 -1
  39. package/bizar-dash/src/server/routes/opencode-session-detail.mjs +14 -29
  40. package/bizar-dash/src/server/routes/opencode-sessions.mjs +205 -3
  41. package/bizar-dash/src/server/routes/providers.mjs +266 -5
  42. package/bizar-dash/src/server/routes/skills.mjs +32 -43
  43. package/bizar-dash/src/server/routes/update.mjs +340 -0
  44. package/bizar-dash/src/server/routes/usage.mjs +136 -0
  45. package/bizar-dash/src/server/serve-info.mjs +135 -4
  46. package/bizar-dash/src/server/server.mjs +20 -0
  47. package/bizar-dash/src/server/skills-store.mjs +152 -262
  48. package/bizar-dash/src/web/App.tsx +120 -29
  49. package/bizar-dash/src/web/components/EnvVarManager.tsx +247 -0
  50. package/bizar-dash/src/web/components/HeadroomSettings.tsx +418 -0
  51. package/bizar-dash/src/web/components/HeadroomStatus.tsx +158 -0
  52. package/bizar-dash/src/web/components/SettingsSearch.tsx +213 -0
  53. package/bizar-dash/src/web/components/Topbar.tsx +2 -1
  54. package/bizar-dash/src/web/components/UsageChart.tsx +250 -0
  55. package/bizar-dash/src/web/components/UsageTable.tsx +90 -0
  56. package/bizar-dash/src/web/components/chat/ChatComposer.tsx +21 -25
  57. package/bizar-dash/src/web/components/chat/ChatInfoPanel.tsx +199 -37
  58. package/bizar-dash/src/web/components/chat/ChatThread.tsx +29 -17
  59. package/bizar-dash/src/web/components/chat/FloatingComposer.tsx +7 -1
  60. package/bizar-dash/src/web/components/chat/InfoPanel.tsx +71 -6
  61. package/bizar-dash/src/web/components/chat/useChat.ts +751 -257
  62. package/bizar-dash/src/web/lib/api.ts +43 -0
  63. package/bizar-dash/src/web/lib/types.ts +16 -0
  64. package/bizar-dash/src/web/main.tsx +2 -0
  65. package/bizar-dash/src/web/mobile/views/MobileChat.tsx +110 -35
  66. package/bizar-dash/src/web/styles/chat.css +135 -1
  67. package/bizar-dash/src/web/styles/main.css +46 -0
  68. package/bizar-dash/src/web/styles/memory.css +955 -0
  69. package/bizar-dash/src/web/styles/minimax-usage.css +335 -0
  70. package/bizar-dash/src/web/styles/settings.css +418 -0
  71. package/bizar-dash/src/web/styles/skills.css +302 -0
  72. package/bizar-dash/src/web/styles/tasks.css +288 -0
  73. package/bizar-dash/src/web/views/Chat.tsx +276 -48
  74. package/bizar-dash/src/web/views/Config.tsx +3 -2065
  75. package/bizar-dash/src/web/views/Memory.tsx +140 -0
  76. package/bizar-dash/src/web/views/MiniMaxUsage.tsx +476 -461
  77. package/bizar-dash/src/web/views/Overview.tsx +3 -0
  78. package/bizar-dash/src/web/views/Settings.tsx +36 -0
  79. package/bizar-dash/src/web/views/Skills.tsx +208 -260
  80. package/bizar-dash/src/web/views/Tasks.tsx +348 -1119
  81. package/bizar-dash/src/web/views/memory/ConfigPanel.tsx +289 -0
  82. package/bizar-dash/src/web/views/memory/GitSyncPanel.tsx +220 -0
  83. package/bizar-dash/src/web/views/memory/LightragPanel.tsx +307 -0
  84. package/bizar-dash/src/web/views/memory/MemoryOverview.tsx +354 -0
  85. package/bizar-dash/src/web/views/memory/MemoryStatusCard.tsx +160 -0
  86. package/bizar-dash/src/web/views/memory/ObsidianPanel.tsx +642 -0
  87. package/bizar-dash/src/web/views/memory/SemanticSearchPanel.tsx +194 -0
  88. package/bizar-dash/tests/chat-session-create.test.mjs +391 -0
  89. package/bizar-dash/tests/chat-session-stream.test.mjs +308 -0
  90. package/bizar-dash/tests/env-vars-store.test.mjs +216 -0
  91. package/bizar-dash/tests/headroom-install.test.mjs +173 -0
  92. package/bizar-dash/tests/headroom-settings.test.mjs +126 -0
  93. package/bizar-dash/tests/headroom-status.test.mjs +117 -0
  94. package/bizar-dash/tests/lightrag-defaults.node.test.mjs +118 -0
  95. package/bizar-dash/tests/memory-lightrag-extended.test.mjs +162 -0
  96. package/bizar-dash/tests/memory-obsidian.test.mjs +269 -0
  97. package/bizar-dash/tests/memory-tab.test.mjs +322 -0
  98. package/bizar-dash/tests/minimax-chat-usage.test.mjs +178 -0
  99. package/bizar-dash/tests/minimax-usage-store.node.test.mjs +293 -0
  100. package/bizar-dash/tests/mod-upgrade.node.test.mjs +1 -1
  101. package/bizar-dash/tests/opencode-sessions-detail.test.mjs +12 -9
  102. package/bizar-dash/tests/providers-store-backup-keys.node.test.mjs +479 -3
  103. package/bizar-dash/tests/providers-store-search.node.test.mjs +166 -0
  104. package/bizar-dash/tests/skills-list.test.mjs +232 -0
  105. package/bizar-dash/tests/skills-search.test.mjs +222 -0
  106. package/bizar-dash/tests/submit-feedback.test.mjs +6 -6
  107. package/bizar-dash/tests/tasks-create.test.mjs +187 -0
  108. package/bizar-dash/tests/update-check.test.mjs +127 -0
  109. package/bizar-dash/tests/update-run.test.mjs +266 -0
  110. package/cli/bin.mjs +247 -1
  111. package/cli/provision.mjs +118 -4
  112. package/config/agents/_shared/SKILLS.md +109 -0
  113. package/package.json +1 -1
  114. package/bizar-dash/dist/assets/main-BB5mJurD.js +0 -352
  115. package/bizar-dash/dist/assets/main-BB5mJurD.js.map +0 -1
  116. package/bizar-dash/dist/assets/main-BsnQLXdh.css +0 -1
  117. package/bizar-dash/dist/assets/mobile-Dl1q7Cyq.js +0 -354
  118. package/bizar-dash/dist/assets/mobile-Dl1q7Cyq.js.map +0 -1
package/cli/bin.mjs CHANGED
@@ -91,6 +91,7 @@ function showHelp() {
91
91
  service Manage the background service daemon
92
92
  bg <subcommand> Manage background agents (list/view/kill/logs)
93
93
  memory <subcommand> Manage project memory (Bizar Memory Service)
94
+ headroom <subcommand> Manage Headroom context compression
94
95
  dash <subcommand> Manage the dashboard (start/stop/status/cleanup/tui)
95
96
  browser-harness-up Start Chromium for browser-harness (start/stop/status/restart)
96
97
  dev-link [src] Symlink the local plugin source into opencode's plugin dir
@@ -98,6 +99,7 @@ function showHelp() {
98
99
  doctor Check the BizarHarness install for health issues
99
100
  heads-up <subcommand> Manage pre-push / pre-release heads-ups (list/check/archive)
100
101
  minimax <subcommand> Manage MiniMax Token Plan integration (key + onboarding)
102
+ usage Show compact usage analytics summary (24h rolling)
101
103
  mod <subcommand> Manage mods (install/upgrade/list via the dashboard API)
102
104
 
103
105
  Examples:
@@ -196,6 +198,8 @@ function showUpdateHelp() {
196
198
 
197
199
  Usage:
198
200
  bizar update Update EVERYTHING (default; auto-kills + restarts)
201
+ bizar update --check Only print current vs. latest; do not update
202
+ bizar update --channel=stable|beta Pick the npm dist-tag (default: stable)
199
203
  bizar update --no-restart Don't auto-restart the dashboard after update
200
204
  bizar update --dry-run Print what would happen, change nothing
201
205
  bizar update --force Override .bizar/PRE_PUSH_NOTES.md blockers
@@ -223,12 +227,20 @@ function showUpdateHelp() {
223
227
  --no-restart).
224
228
  • Runs 'bizar doctor' after a successful update to catch config
225
229
  regressions before opencode tries to start.
230
+ • With --check: prints the version matrix and release-notes excerpt
231
+ between current and latest, exits non-zero if an update is available.
226
232
 
227
233
  Examples:
228
234
  bizar update Full auto-update (recommended)
235
+ bizar update --check Show version matrix + notes, do nothing
236
+ bizar update --channel=beta Upgrade to latest beta build
229
237
  bizar update --dry-run Preview what would change
230
- bizar update --pick Pick specific components
231
238
  bizar update plugin --no-restart Plugin-only, leave dashboard alone
239
+
240
+ Errors:
241
+ Network failures (registry offline / DNS) and npm permission issues
242
+ are surfaced with the raw npm output. The provisioner never silently
243
+ swallows them — look for the ✗ marker in the step output.
232
244
  `);
233
245
  }
234
246
 
@@ -731,6 +743,235 @@ async function runMinimaxCommand(minimaxArgs) {
731
743
  process.exit(1);
732
744
  }
733
745
 
746
+ // ── Usage subcommand ───────────────────────────────────────────────────────
747
+
748
+ function showUsageHelp() {
749
+ console.log(`
750
+ bizar usage — Show compact usage analytics summary
751
+
752
+ Usage:
753
+ bizar usage [24h|7d|30d] Show summary for the given range (default: 24h)
754
+
755
+ Examples:
756
+ bizar usage
757
+ bizar usage 7d
758
+ bizar usage 30d
759
+ `);
760
+ }
761
+
762
+ async function runUsageCommand(args) {
763
+ const range = (args[0] && ['24h', '7d', '30d'].includes(args[0])) ? args[0] : '24h';
764
+ const { port, secret } = readDashboardConn();
765
+ const url = `http://127.0.0.1:${port}/api/usage?range=${range}`;
766
+ const headers = { accept: 'application/json' };
767
+ if (secret) headers.authorization = `Basic ${Buffer.from(`opencode:${secret}`).toString('base64')}`;
768
+ try {
769
+ const resp = await fetch(url, { method: 'GET', headers });
770
+ const text = await resp.text();
771
+ let data = null;
772
+ try { data = text ? JSON.parse(text) : null; } catch { /* ignore */ }
773
+ if (!resp.ok || !data) {
774
+ console.error(chalk.red(` ✗ Failed to load usage data: ${data?.message ?? resp.statusText}`));
775
+ process.exit(1);
776
+ }
777
+ const t = data.totals;
778
+ console.log('');
779
+ console.log(chalk.bold(` Usage summary — ${range} (from JSONL store)`));
780
+ console.log('');
781
+ console.log(` ${chalk.dim('Requests:')} ${t.requests.toLocaleString()} (${t.errors} errors)`);
782
+ console.log(` ${chalk.dim('Tokens:')} ${t.totalTokens.toLocaleString()} total (${t.promptTokens.toLocaleString()} prompt · ${t.completionTokens.toLocaleString()} completion)`);
783
+ console.log(` ${chalk.dim('Cached:')} ${t.cachedTokens.toLocaleString()} tokens`);
784
+ console.log(` ${chalk.dim('Reasoning:')} ${t.reasoningTokens.toLocaleString()} tokens`);
785
+ console.log(` ${chalk.dim('Avg latency:')} ${t.avgLatencyMs}ms (p95: ${t.p95LatencyMs}ms)`);
786
+ if (t.costEstimate > 0) {
787
+ console.log(` ${chalk.dim('Est. cost:')} $${t.costEstimate.toFixed(4)} USD`);
788
+ }
789
+ console.log('');
790
+ if (data.daily && data.daily.length > 0) {
791
+ console.log(chalk.dim(` ${chalk.bold('Daily breakdown')}`));
792
+ for (const day of data.daily.slice(-7)) {
793
+ const barLen = Math.round((day.totalTokens / Math.max(...data.daily.map(d => d.totalTokens))) * 20);
794
+ const bar = '█'.repeat(barLen) + '░'.repeat(20 - barLen);
795
+ console.log(` ${day.date} ${bar} ${day.totalTokens.toLocaleString()} tok ${day.requests} req`);
796
+ }
797
+ }
798
+ if (data.perModel && data.perModel.length > 0) {
799
+ console.log('');
800
+ console.log(chalk.dim(` ${chalk.bold('Per model')}`));
801
+ for (const m of data.perModel.slice(0, 8)) {
802
+ console.log(` ${m.modelId.padEnd(24)} ${String(m.requests).padStart(6)} req ${String(m.totalTokens).padStart(8)} tok`);
803
+ }
804
+ }
805
+ console.log('');
806
+ } catch (err) {
807
+ console.error(chalk.red(` ✗ Network error: ${err && err.message ? err.message : String(err)}`));
808
+ console.error(chalk.dim(' Is the dashboard running? Run `bizar dash start` first.'));
809
+ process.exit(1);
810
+ }
811
+ }
812
+
813
+ function showHeadroomHelp() {
814
+ console.log(`
815
+ bizar headroom — Manage Headroom context compression
816
+
817
+ Usage:
818
+ bizar headroom status Show live status (installed, proxy, wrapped)
819
+ bizar headroom stats Show compression stats for the last 24h
820
+ bizar headroom install Install headroom via pip or npm
821
+ bizar headroom wrap Wrap opencode to route through the proxy
822
+ bizar headroom unwrap Unwrap opencode
823
+ bizar headroom start Start the proxy server
824
+ bizar headroom stop Stop the proxy server
825
+ bizar headroom doctor Run headroom doctor health check
826
+
827
+ Examples:
828
+ bizar headroom status
829
+ bizar headroom stats
830
+ bizar headroom install
831
+ bizar headroom wrap
832
+ `);
833
+ }
834
+
835
+ async function runHeadroomCommand(headroomArgs) {
836
+ const sub = headroomArgs[0];
837
+ const flags = headroomArgs.slice(1).filter((a) => a.startsWith('-'));
838
+ const positional = headroomArgs.slice(1).filter((a) => !a.startsWith('-'));
839
+
840
+ if (!sub || sub === '--help' || sub === '-h' || flags.includes('--help') || flags.includes('-h')) {
841
+ showHeadroomHelp();
842
+ return;
843
+ }
844
+
845
+ // Delegate to dashboard API
846
+ const { port, secret } = readDashboardConn();
847
+ const baseUrl = `http://127.0.0.1:${port}`;
848
+
849
+ async function apiGet(path) {
850
+ const url = `${baseUrl}${path}`;
851
+ const headers = { accept: 'application/json' };
852
+ if (secret) headers.authorization = `Basic ${Buffer.from(`opencode:${secret}`).toString('base64')}`;
853
+ const res = await fetch(url, { method: 'GET', headers });
854
+ const text = await res.text();
855
+ let data = null;
856
+ try { data = text ? JSON.parse(text) : null; } catch { /* ignore */ }
857
+ if (!res.ok) throw new Error(`${path}: ${data?.message || data?.error || res.statusText}`);
858
+ return data;
859
+ }
860
+
861
+ async function apiPost(path, body = {}) {
862
+ const url = `${baseUrl}${path}`;
863
+ const headers = { 'content-type': 'application/json', accept: 'application/json' };
864
+ if (secret) headers.authorization = `Basic ${Buffer.from(`opencode:${secret}`).toString('base64')}`;
865
+ const res = await fetch(url, { method: 'POST', headers, body: JSON.stringify(body) });
866
+ const text = await res.text();
867
+ let data = null;
868
+ try { data = text ? JSON.parse(text) : null; } catch { /* ignore */ }
869
+ if (!res.ok) throw new Error(`${path}: ${data?.message || data?.error || res.statusText}`);
870
+ return data;
871
+ }
872
+
873
+ if (sub === 'status') {
874
+ const s = await apiGet('/api/headroom/status');
875
+ console.log('');
876
+ console.log(chalk.bold(' Headroom status'));
877
+ console.log('');
878
+ console.log(` ${chalk.dim('installed:')} ${s.installed ? chalk.green('yes') : chalk.red('no')} ${s.version || ''}`);
879
+ console.log(` ${chalk.dim('proxy:')} ${s.proxyRunning ? chalk.green('running') : chalk.yellow('stopped')} ${s.proxyPort ? `@ ${s.proxyPort}` : ''} ${s.proxyPid ? `(PID ${s.proxyPid})` : ''}`);
880
+ console.log(` ${chalk.dim('wrapped:')} ${s.wrapped ? chalk.green('yes') : chalk.yellow('no')}`);
881
+ console.log(` ${chalk.dim('healthy:')} ${s.healthy === 'ok' ? chalk.green('ok') : s.healthy === 'warn' ? chalk.yellow('warn') : chalk.red('fail')}`);
882
+ if (s.messages && s.messages.length > 0) {
883
+ console.log('');
884
+ for (const m of s.messages) {
885
+ console.log(` ${m}`);
886
+ }
887
+ }
888
+ console.log('');
889
+ return;
890
+ }
891
+
892
+ if (sub === 'stats') {
893
+ const hours = parseInt(positional[0], 10) || 24;
894
+ const st = await apiGet(`/api/headroom/stats?hours=${hours}`);
895
+ console.log('');
896
+ console.log(chalk.bold(` Headroom stats (${hours}h)`));
897
+ console.log('');
898
+ if (st.error) {
899
+ console.log(chalk.yellow(` ${st.error}`));
900
+ } else {
901
+ console.log(` ${chalk.dim('tokens saved:')} ${(st.tokensSaved || 0).toLocaleString()}`);
902
+ console.log(` ${chalk.dim('compression:')} ${st.compressionRatio ? `${Math.round(st.compressionRatio * 100)}%` : '—'}`);
903
+ console.log(` ${chalk.dim('cache hits:')} ${st.cacheHits ?? '—'}`);
904
+ console.log(` ${chalk.dim('transforms:')} ${st.transforms ?? '—'}`);
905
+ }
906
+ console.log('');
907
+ return;
908
+ }
909
+
910
+ if (sub === 'install') {
911
+ const r = await apiPost('/api/headroom/install', { force: true });
912
+ if (r.installed) {
913
+ console.log(chalk.green(` ✓ Headroom installed via ${r.method}${r.version ? ` (${r.version})` : ''}`));
914
+ } else {
915
+ console.log(chalk.red(' ✗ Install failed. Try: pip install "headroom-ai[all]"'));
916
+ }
917
+ return;
918
+ }
919
+
920
+ if (sub === 'wrap') {
921
+ const port = parseInt(positional[0], 10) || 8787;
922
+ const r = await apiPost('/api/headroom/wrap', { port });
923
+ if (r.ok) {
924
+ console.log(chalk.green(` ✓ opencode wrapped with Headroom on port ${port}`));
925
+ } else {
926
+ console.log(chalk.red(` ✗ Wrap failed: ${r.log || 'unknown error'}`));
927
+ }
928
+ return;
929
+ }
930
+
931
+ if (sub === 'unwrap') {
932
+ const r = await apiPost('/api/headroom/unwrap');
933
+ if (r.ok) {
934
+ console.log(chalk.green(' ✓ opencode unwrapped from Headroom'));
935
+ } else {
936
+ console.log(chalk.red(' ✗ Unwrap failed'));
937
+ }
938
+ return;
939
+ }
940
+
941
+ if (sub === 'start') {
942
+ const port = parseInt(positional[0], 10) || 8787;
943
+ const r = await apiPost('/api/headroom/proxy/start', { port, host: '127.0.0.1' });
944
+ if (r.ok) {
945
+ console.log(chalk.green(` ✓ Proxy started on 127.0.0.1:${port} (PID ${r.pid})`));
946
+ } else {
947
+ console.log(chalk.red(' ✗ Proxy start failed'));
948
+ }
949
+ return;
950
+ }
951
+
952
+ if (sub === 'stop') {
953
+ const r = await apiPost('/api/headroom/proxy/stop');
954
+ if (r.ok) {
955
+ console.log(chalk.green(' ✓ Proxy stopped'));
956
+ } else {
957
+ console.log(chalk.red(' ✗ Proxy stop failed'));
958
+ }
959
+ return;
960
+ }
961
+
962
+ if (sub === 'doctor') {
963
+ // Run `headroom doctor` locally
964
+ const { spawn } = await import('node:child_process');
965
+ const child = spawn('headroom', ['doctor'], { stdio: 'inherit' });
966
+ await new Promise((resolve) => child.on('close', resolve));
967
+ return;
968
+ }
969
+
970
+ console.error(chalk.red(` ✗ Unknown headroom subcommand: ${sub}`));
971
+ showHeadroomHelp();
972
+ process.exit(1);
973
+ }
974
+
734
975
  function showDashHelp() {
735
976
  console.log(`
736
977
  bizar dash — Manage the Bizar dashboard
@@ -942,6 +1183,8 @@ async function main() {
942
1183
  const { runMemory } = await import('./memory.mjs');
943
1184
  await runMemory(args[1], args.slice(2));
944
1185
  }
1186
+ } else if (args[0] === 'headroom') {
1187
+ await runHeadroomCommand(args.slice(1));
945
1188
  } else if (args[0] === 'export') {
946
1189
  if (isHelpRequest) showExportHelp();
947
1190
  else await runExport(parseFlag('--target'));
@@ -1083,6 +1326,9 @@ async function main() {
1083
1326
  // test, config, clear, reset-onboarding. All commands shell out
1084
1327
  // to the dashboard's /api/minimax/* routes.
1085
1328
  await runMinimaxCommand(args.slice(1));
1329
+ } else if (args[0] === 'usage') {
1330
+ // v4.6.0 — Compact usage analytics summary from the JSONL store.
1331
+ await runUsageCommand(args.slice(1));
1086
1332
  } else if (args[0] === 'dash' || args[0] === 'dashboard') {
1087
1333
  // `bizar dashboard` is a deprecated alias for `bizar dash`
1088
1334
  if (args[0] === 'dashboard') {
package/cli/provision.mjs CHANGED
@@ -1121,17 +1121,131 @@ export async function runProvision(opts = {}) {
1121
1121
  * `cli/update.mjs` (the bin.mjs-facing module) can stay a thin shim
1122
1122
  * without duplicating flag parsing. Accepts the legacy `subargs: string[]`
1123
1123
  * shape so any external callers keep working.
1124
+ *
1125
+ * Recognized flags (v4.4.14):
1126
+ * --check Only print current vs. latest, don't update.
1127
+ * --channel <name> npm dist-tag (stable | beta). Default: stable.
1128
+ * --no-restart Don't auto-restart the dashboard after update.
1129
+ * --dry-run Print what would happen, change nothing.
1130
+ * --force Override .bizar/PRE_PUSH_NOTES.md blockers.
1131
+ * --yes / -y Same as --force, but named for one-line scripts.
1132
+ * --with-mods <csv> Opt-in: install specific mods as part of the run.
1133
+ *
1134
+ * With --check, we print the version matrix and release notes between
1135
+ * current and latest, then return without touching the system.
1124
1136
  */
1125
1137
  export async function runUpdate(subargs = []) {
1138
+ const args = Array.isArray(subargs) ? subargs : [];
1139
+ const checkOnly = args.includes('--check');
1140
+
1141
+ // --channel <name> — extract a single value. Accepts both
1142
+ // --channel=beta (equals form)
1143
+ // --channel beta (separate-arg form)
1144
+ // Default: 'stable'.
1145
+ let channel = 'stable';
1146
+ const eqArg = args.find((a) => a.startsWith('--channel='));
1147
+ if (eqArg) {
1148
+ const value = eqArg.slice('--channel='.length);
1149
+ if (value === 'stable' || value === 'beta') {
1150
+ channel = value;
1151
+ } else {
1152
+ console.log(chalk.yellow(` ⚠ Unknown channel "${value}". Using "stable".`));
1153
+ }
1154
+ } else {
1155
+ const channelIdx = args.indexOf('--channel');
1156
+ if (channelIdx >= 0) {
1157
+ const value = args[channelIdx + 1];
1158
+ if (!value || value.startsWith('--')) {
1159
+ console.log(chalk.yellow(' ⚠ --channel needs a value (stable | beta). Using "stable".'));
1160
+ } else if (value !== 'stable' && value !== 'beta') {
1161
+ console.log(chalk.yellow(` ⚠ Unknown channel "${value}". Using "stable".`));
1162
+ } else {
1163
+ channel = value;
1164
+ }
1165
+ }
1166
+ }
1167
+
1168
+ if (checkOnly) {
1169
+ return runCheck(channel);
1170
+ }
1171
+
1172
+ // Pass channel through to the provisioner. The provisioner does the
1173
+ // actual npm install; channel maps to the npm dist-tag suffix.
1126
1174
  return runProvision({
1127
1175
  mode: 'update',
1128
- dryRun: subargs.includes('--dry-run'),
1129
- force: subargs.includes('--force'),
1130
- yes: subargs.includes('--yes') || subargs.includes('-y'),
1131
- restart: !subargs.includes('--no-restart'),
1176
+ dryRun: args.includes('--dry-run'),
1177
+ force: args.includes('--force'),
1178
+ yes: args.includes('--yes') || args.includes('-y'),
1179
+ restart: !args.includes('--no-restart'),
1180
+ channel,
1132
1181
  });
1133
1182
  }
1134
1183
 
1184
+ /**
1185
+ * v4.4.14 — `bizar update --check`. Print the version matrix and (if
1186
+ * a newer version exists) the release notes between current and latest.
1187
+ * Does NOT touch the system. Exits non-zero when an update is available
1188
+ * so callers can use it in CI / pre-flight scripts.
1189
+ */
1190
+ export async function runCheck(channel = 'stable') {
1191
+ console.log(chalk.bold.hex('#a855f7')('\n ᚦ BIZAR UPDATE — CHECK ᚦ\n'));
1192
+ console.log(chalk.dim(` Channel: ${channel}\n`));
1193
+
1194
+ const state = detectState();
1195
+ printVersionMatrix([
1196
+ { label: 'opencode-ai', current: state.opencodeCli.version, latest: state.opencodeCli.latest },
1197
+ { label: PKG_MAIN, current: state.pkgVersion, latest: state.pkgLatest },
1198
+ ]);
1199
+
1200
+ const cur = state.pkgVersion;
1201
+ const lat = state.pkgLatest;
1202
+ if (cur && lat && cur !== lat) {
1203
+ console.log(chalk.dim(`\n Release notes (${cur} → ${lat}):`));
1204
+ const notes = fetchReleaseNotes(PKG_MAIN, cur, lat).catch((err) => {
1205
+ console.log(chalk.dim(` (could not fetch: ${err.message || err})`));
1206
+ return null;
1207
+ });
1208
+ const text = await notes;
1209
+ if (text) {
1210
+ // Trim to a sensible cap (first 30 lines) so the console stays
1211
+ // readable; the full notes are one `npm view` call away.
1212
+ const lines = text.split(/\r?\n/).slice(0, 30);
1213
+ for (const line of lines) console.log(` ${line}`);
1214
+ if (lines.length === 30) console.log(chalk.dim(' ... (truncated; run `npm view @polderlabs/bizar` for full)'));
1215
+ }
1216
+ console.log(chalk.yellow(`\n ⤵ Run \`bizar update\` to apply.\n`));
1217
+ // Non-zero exit so scripts can detect the update-available case.
1218
+ return { ok: true, updateAvailable: true, channel };
1219
+ }
1220
+
1221
+ console.log(chalk.green('\n ✓ Already on the latest version.\n'));
1222
+ return { ok: true, updateAvailable: false, channel };
1223
+ }
1224
+
1225
+ /**
1226
+ * Fetch the release notes between two versions from the npm registry.
1227
+ * Returns a plain-text summary (the registry's "description" field for
1228
+ * the latest version), or null on any failure. Never throws — failures
1229
+ * are surfaced to the caller as null so the check command stays useful
1230
+ * offline.
1231
+ */
1232
+ async function fetchReleaseNotes(pkg, _from, _to) {
1233
+ // The npm registry does not expose a structured per-version release-
1234
+ // notes field. The best we can do is `npm view <pkg> description`
1235
+ // which is the package's README excerpt. We surface that as "notes".
1236
+ try {
1237
+ const { execFileSync } = await import('node:child_process');
1238
+ const out = execFileSync('npm', ['view', pkg, 'description'], {
1239
+ stdio: ['ignore', 'pipe', 'ignore'],
1240
+ timeout: 10000,
1241
+ encoding: 'utf8',
1242
+ }).toString().trim();
1243
+ return out || null;
1244
+ } catch {
1245
+ return null;
1246
+ }
1247
+ }
1248
+
1135
1249
  /**
1136
1250
  * CLI-flag-parsing entrypoint for `bizar install`. Mirrors `runUpdate`
1137
1251
  * so `cli/install.mjs` can stay a thin shim too.
@@ -0,0 +1,109 @@
1
+ # Bizar Skills - Reference
2
+
3
+ Skills are specialized instruction sets that agents load before working on specific domains. They live as `SKILL.md` files in well-known directories and are injected into agent context when relevant.
4
+
5
+ ## What Skills Are
6
+
7
+ A skill is a Markdown file with YAML frontmatter. The frontmatter declares a `description` that the agent matcher uses to decide when to load the skill. The body contains domain-specific knowledge, patterns, gotchas, and code examples.
8
+
9
+ Example `SKILL.md`:
10
+
11
+ ```yaml
12
+ ---
13
+ name: my-skill
14
+ description: Use when working with X. Covers Y and Z.
15
+ ---
16
+ # My Skill
17
+
18
+ ## When to use
19
+ ...
20
+
21
+ ## Key concepts
22
+ ...
23
+
24
+ ## Code examples
25
+ ...
26
+
27
+ ## Common gotchas
28
+ ...
29
+ ```
30
+
31
+ ## Where Skills Live
32
+
33
+ | Path | Source | Priority | Notes |
34
+ |---|---|---|---|
35
+ | `~/.opencode/skills/<name>/SKILL.md` | User / System | Highest | User-overridable builtins |
36
+ | `~/.agents/skills/<name>/SKILL.md` | User-added | High | Installed via `skills add` |
37
+ | `bizar-dash/skills/<name>/SKILL.md` | Shipped | Medium | Ships with BizarHarness package |
38
+ | `.agents/skills/<name>/SKILL.md` | Project | Low | Project-local skills |
39
+ | `.opencode/skills/<name>/SKILL.md` | Project | Lowest | Project-local skills |
40
+
41
+ When the same skill name appears in multiple places, the highest-priority source wins.
42
+
43
+ ## Discovering Skills
44
+
45
+ Use the `skills` CLI:
46
+
47
+ ```bash
48
+ # List installed skills
49
+ skills list --json
50
+
51
+ # Search for a skill
52
+ skills search "react"
53
+
54
+ # Install from a repository
55
+ skills add owner/repo -s "skill-name" -y
56
+ ```
57
+
58
+ Known repositories:
59
+ - `vercel-labs/skills` - find-skills, skill-creator, general tools
60
+ - `vercel-labs/agent-skills` - React, Next.js, frontend performance
61
+ - `shadcn/ui` - shadcn/ui components
62
+ - `supabase/agent-skills` - Postgres, Auth, Edge Functions
63
+ - `mattpocock/skills` - TypeScript, TDD
64
+ - `anthropics/skills` - Claude patterns, agents
65
+ - `leonxlnx/taste-skill` - design, UI/UX
66
+
67
+ ## Shipped Bizar Skills
68
+
69
+ These skills ship with BizarHarness and are available immediately:
70
+
71
+ | Skill | Description |
72
+ |---|---|
73
+ | `bizar` | Norse-pantheon multi-agent system, Odin routing, agent tiers |
74
+ | `agent-baseline` | Always-on rules: Semble, Skills CLI, loop guard, copyright |
75
+ | `self-improvement` | .bizar/AGENTS_SELF_IMPROVEMENT.md protocol |
76
+ | `obsidian` | Bizar Memory Service (Obsidian + Git + LightRAG) |
77
+ | `minimax` | MiniMax provider, multi-key rotation, usage tracking |
78
+ | `providers` | Provider subsystem, backup keys, auto-add wizard |
79
+ | `chat` | Chat + opencode session integration |
80
+ | `usage` | Token usage monitoring, cost estimation, MiniMax usage dashboard |
81
+ | `skills-cli` | skills CLI reference, skill repos, discovery protocol |
82
+ | `lightrag` | LightRAG integration, opencode-free defaults, indexing |
83
+ | `sdk` | @polderlabs/bizar-sdk on Cloudflare Workers |
84
+
85
+ ## Skill Loading
86
+
87
+ Agents check skill relevance at dispatch time. You can also load a skill explicitly in conversation using the `skill` tool:
88
+
89
+ ```
90
+ Load the `minimax` skill before we discuss multi-key rotation.
91
+ ```
92
+
93
+ ## Adding a Project Skill
94
+
95
+ Create a skill directory in your project:
96
+
97
+ ```bash
98
+ mkdir -p .agents/skills/my-project-skill
99
+ cat > .agents/skills/my-project-skill/SKILL.md << 'EOF'
100
+ ---
101
+ name: my-project-skill
102
+ description: Use when working on my project's specific domain X.
103
+ ---
104
+ # My Project Skill
105
+ ...
106
+ EOF
107
+ ```
108
+
109
+ Project skills are picked up automatically on next session start.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@polderlabs/bizar",
3
- "version": "4.4.13",
3
+ "version": "4.5.1",
4
4
  "description": "Norse-pantheon multi-agent system for opencode — 13 agents across 4 cost tiers with cost-aware routing, plans, and a configurable agent harness. v4 ships as a single npm package bundling the dashboard server, opencode plugin, and typed SDK.",
5
5
  "type": "module",
6
6
  "bin": {