dual-brain 3.0.1 → 3.1.0

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.
@@ -200,6 +200,8 @@ function allOpusCost(records, rateMap, datePrefix = null) {
200
200
  // Formatting helpers
201
201
  // ---------------------------------------------------------------------------
202
202
 
203
+ const W = 50;
204
+
203
205
  const TIER_ORDER = ["search", "execute", "think"];
204
206
 
205
207
  const TIER_LABELS = {
@@ -224,8 +226,6 @@ function renderTable(title, aggregated, allOpus, records = []) {
224
226
  const savings = allOpus - totalCost;
225
227
  const savingsPct = allOpus > 0 ? Math.round((savings / allOpus) * 100) : 0;
226
228
 
227
- const W = 50; // total inner width (between ║ chars)
228
-
229
229
  const line = (s) => `║ ${pad(s, W - 2)} ║`;
230
230
  const border = (l, r, m) => l + "═".repeat(W) + r;
231
231
  const sep = () => "╠" + "═".repeat(W) + "╣";
@@ -14,7 +14,7 @@
14
14
 
15
15
  import { execSync, spawnSync } from 'child_process';
16
16
  import { readFileSync } from 'fs';
17
- import { dirname, resolve } from 'path';
17
+ import { dirname, join, resolve } from 'path';
18
18
  import { fileURLToPath } from 'url';
19
19
 
20
20
  const __dirname = dirname(fileURLToPath(import.meta.url));
@@ -119,7 +119,7 @@ async function main() {
119
119
 
120
120
  console.log(` ║ Quality gate: ${gateEnabled ? 'enabled' : 'disabled'}`.padEnd(53) + '║');
121
121
  console.log(' ╠══════════════════════════════════════════════════╣');
122
- console.log(' ║ Restart Claude Code to activate the orchestrator ║');
122
+ console.log(' ║ Hooks are active no restart needed ║');
123
123
  console.log(' ╚══════════════════════════════════════════════════╝');
124
124
  console.log('');
125
125
 
@@ -0,0 +1,5 @@
1
+ {"timestamp":"2026-05-14T00:33:17.462Z","type":"tier_recommendation","detected_tier":"search","recommended_model":"haiku","actual_model":"opus","prompt_hash":"9990d6fd8943","followed":false}
2
+ {"timestamp":"2026-05-14T00:33:17.501Z","type":"tier_recommendation","detected_tier":"execute","recommended_model":"sonnet","actual_model":"sonnet","prompt_hash":"c636a5e74cdc","followed":true}
3
+ {"timestamp":"2026-05-14T00:33:17.536Z","type":"tier_recommendation","detected_tier":"think","recommended_model":"opus","actual_model":"haiku","prompt_hash":"bd666c33402c","followed":false}
4
+ {"timestamp":"2026-05-14T00:33:17.606Z","type":"tier_recommendation","detected_tier":"execute","recommended_model":"sonnet","actual_model":"unknown-model-xyz","prompt_hash":"913203af69f8","followed":false}
5
+ {"timestamp":"2026-05-14T00:33:17.746Z","type":"tier_recommendation","detected_tier":"think","recommended_model":"opus","actual_model":"gpt-4.1-mini","prompt_hash":"7196103e6569","followed":false}
package/install.mjs CHANGED
@@ -24,6 +24,11 @@ const force = flag('--force');
24
24
  const dryRun = flag('--dry-run');
25
25
  const jsonOut = flag('--json');
26
26
 
27
+ if (flag('--version') || flag('-v')) {
28
+ console.log(`dual-brain v${VERSION}`);
29
+ process.exit(0);
30
+ }
31
+
27
32
  if (flag('--help') || flag('-h')) {
28
33
  console.log(`
29
34
  dual-brain v${VERSION} — Dual-provider orchestrator for Claude Code
@@ -240,7 +245,20 @@ function generateSettings(workspace) {
240
245
  ],
241
246
  };
242
247
 
243
- return { ...existing, hooks };
248
+ const DUAL_BRAIN_CMDS = [
249
+ 'node .claude/hooks/enforce-tier.mjs',
250
+ 'node .claude/hooks/cost-logger.mjs',
251
+ ];
252
+
253
+ const merged = { ...(existing.hooks || {}) };
254
+ for (const [event, entries] of Object.entries(hooks)) {
255
+ const existingEntries = (merged[event] || []).filter(e =>
256
+ !e.hooks?.some(h => DUAL_BRAIN_CMDS.includes(h.command))
257
+ );
258
+ merged[event] = [...existingEntries, ...entries];
259
+ }
260
+
261
+ return { ...existing, hooks: merged };
244
262
  }
245
263
 
246
264
  function generateClaudeMd(mode) {
@@ -322,7 +340,7 @@ function install(workspace, env, mode) {
322
340
  if (needed.length > 0) {
323
341
  writeFileSync(
324
342
  join(workspace, '.gitignore'),
325
- gi + '\n# Dual-Brain Orchestrator\n' + needed.join('\n') + '\n'
343
+ (gi && !gi.endsWith('\n') ? gi + '\n' : gi) + '\n# Dual-Brain Orchestrator\n' + needed.join('\n') + '\n'
326
344
  );
327
345
  actions.push('✓ .gitignore updated');
328
346
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dual-brain",
3
- "version": "3.0.1",
3
+ "version": "3.1.0",
4
4
  "description": "Dual-provider orchestration for Claude Code — tiered routing, budget balancing, and GPT dual-brain review across Claude + OpenAI subscriptions",
5
5
  "type": "module",
6
6
  "bin": {