dual-brain 0.2.9 → 0.2.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.
@@ -18,7 +18,21 @@ import {
18
18
  loadCredentials, saveCredentials, getCredentialSummary, detectCredentials, addCredential, removeCredential, checkCredentialHealth,
19
19
  } from '../src/profile.mjs';
20
20
 
21
- import { detectTask } from '../src/detect.mjs';
21
+ import { detectTask, primeAgentRegistry } from '../src/detect.mjs';
22
+
23
+ // ─── Agent/skill registry cache (populated at startup) ───────────────────────
24
+ // These are set by _primeRegistryCache() so classifyInput can use them
25
+ // synchronously without async overhead on each keystroke.
26
+ let _cachedMatchSkill = null;
27
+ let _cachedSkillToTaskBrief = null;
28
+
29
+ async function _primeRegistryCache() {
30
+ try {
31
+ const reg = await import('../src/agents/registry.mjs');
32
+ _cachedMatchSkill = reg.matchSkill;
33
+ _cachedSkillToTaskBrief = reg.skillToTaskBrief;
34
+ } catch {}
35
+ }
22
36
 
23
37
  import {
24
38
  decideRoute, getAvailableModels,
@@ -2034,11 +2048,56 @@ function makeBoxRow(content, W) {
2034
2048
 
2035
2049
  // ─── Command palette: input classifier ───────────────────────────────────────
2036
2050
 
2051
+ // HEAD state — loaded lazily, shared across REPL turns
2052
+ let _headState = null;
2053
+ let _headModuleCache = null;
2054
+
2055
+ async function _getHeadModule() {
2056
+ if (!_headModuleCache) {
2057
+ try {
2058
+ _headModuleCache = await import('../src/head.mjs');
2059
+ } catch {
2060
+ _headModuleCache = null;
2061
+ }
2062
+ }
2063
+ return _headModuleCache;
2064
+ }
2065
+
2066
+ function _getHeadState() {
2067
+ if (!_headState) {
2068
+ try {
2069
+ const head = _headModuleCache;
2070
+ _headState = head ? head.loadState() : null;
2071
+ } catch {
2072
+ _headState = null;
2073
+ }
2074
+ }
2075
+ return _headState;
2076
+ }
2077
+
2078
+ const FREE_COMMANDS = new Map([
2079
+ ['resume', 'resume'], ['r', 'resume'],
2080
+ ['status', 'status'], ['sessions', 'sessions'], ['ss', 'sessions'],
2081
+ ['settings', 'settings'], ['s', 'settings'],
2082
+ ['team', 'team'], ['t', 'team'],
2083
+ ['doctor', 'doctor'], ['d', 'doctor'],
2084
+ ['health', 'health'], ['h', 'health'],
2085
+ ['projects', 'projects'], ['p', 'projects'],
2086
+ ['help', 'help'], ['?', 'help'],
2087
+ ['quit', 'quit'], ['q', 'quit'], ['exit', 'quit'],
2088
+ ['budget', 'budget'], ['b', 'budget'],
2089
+ ['auto', 'auto'], ['automode', 'auto'],
2090
+ ]);
2091
+
2037
2092
  /**
2038
- * Classify user input into one of three tiers:
2039
- * { tier: 'free', command, args } — deterministic, zero tokens
2040
- * { tier: 'cheap' } — question haiku
2041
- * { tier: 'full' } — work task → confirm then dispatch
2093
+ * Classify user input using HEAD's cognitive pipeline.
2094
+ * Returns a tier-compatible object that maps HEAD's deliberation to the
2095
+ * existing REPL routing: free/skill/cheap/full, plus HEAD judgment metadata.
2096
+ *
2097
+ * { tier: 'free', command, args } — deterministic, zero tokens
2098
+ * { tier: 'skill', skill, args, command } — slash command
2099
+ * { tier: 'cheap', headJudgment } — question → haiku
2100
+ * { tier: 'full', headJudgment, model } — work task → dispatch
2042
2101
  */
2043
2102
  function classifyInput(input) {
2044
2103
  const trimmed = input.trim();
@@ -2047,37 +2106,23 @@ function classifyInput(input) {
2047
2106
  const cmd = parts[0].toLowerCase();
2048
2107
  const args = parts.slice(1);
2049
2108
 
2050
- // Tier 1: FREEexact command matches
2051
- const FREE_COMMANDS = new Map([
2052
- ['resume', 'resume'],
2053
- ['r', 'resume'],
2054
- ['status', 'status'],
2055
- ['sessions', 'sessions'],
2056
- ['ss', 'sessions'],
2057
- ['settings', 'settings'],
2058
- ['s', 'settings'],
2059
- ['team', 'team'],
2060
- ['t', 'team'],
2061
- ['doctor', 'doctor'],
2062
- ['d', 'doctor'],
2063
- ['health', 'health'],
2064
- ['h', 'health'],
2065
- ['projects', 'projects'],
2066
- ['p', 'projects'],
2067
- ['help', 'help'],
2068
- ['?', 'help'],
2069
- ['quit', 'quit'],
2070
- ['q', 'quit'],
2071
- ['exit', 'quit'],
2072
- ['budget', 'budget'],
2073
- ['b', 'budget'],
2074
- ]);
2109
+ // Tier 0: SKILLslash commands (checked first, deterministic)
2110
+ if (trimmed.startsWith('/')) {
2111
+ try {
2112
+ if (typeof _cachedMatchSkill === 'function') {
2113
+ const skill = _cachedMatchSkill(trimmed);
2114
+ if (skill) {
2115
+ const skillArgs = trimmed.replace(/^\/\w+\s*/, '');
2116
+ return { tier: 'skill', skill, args: skillArgs, command: skill.command };
2117
+ }
2118
+ }
2119
+ } catch {}
2120
+ }
2075
2121
 
2122
+ // Tier 1: FREE — exact command matches (zero tokens, no HEAD needed)
2076
2123
  if (FREE_COMMANDS.has(cmd)) {
2077
2124
  return { tier: 'free', command: FREE_COMMANDS.get(cmd), args };
2078
2125
  }
2079
-
2080
- // Multi-word free commands
2081
2126
  if (lower.startsWith('search ')) {
2082
2127
  return { tier: 'free', command: 'search', args: parts.slice(1) };
2083
2128
  }
@@ -2085,14 +2130,65 @@ function classifyInput(input) {
2085
2130
  return { tier: 'free', command: 'init --replit', args: [] };
2086
2131
  }
2087
2132
 
2088
- // Tier 2: CHEAP question / diagnostic patterns → haiku
2133
+ // ── HEAD cognitive pipeline: replaces regex-based cheap/full split ──────
2134
+ const head = _headModuleCache;
2135
+ if (head) {
2136
+ const state = _getHeadState() || head.freshState();
2137
+ const turn = head.processTurn(state, trimmed, {});
2138
+ _headState = state; // persist across turns
2139
+
2140
+ const judgment = {
2141
+ depth: turn.depth,
2142
+ action: turn.action,
2143
+ shouldAskUser: turn.shouldAskUser,
2144
+ shouldDispatch: turn.shouldDispatch,
2145
+ shouldClarify: turn.shouldClarify,
2146
+ shouldThink: turn.shouldThink,
2147
+ rationale: turn.rationale,
2148
+ confidence: turn.result.confidence,
2149
+ obligations: turn.result.obligations,
2150
+ surfaceNoticings: turn.result.surfaceNoticings,
2151
+ };
2152
+
2153
+ // Map HEAD's depth → tier + model
2154
+ if (turn.depth === 'reflexive' && !turn.shouldDispatch) {
2155
+ return { tier: 'cheap', headJudgment: judgment };
2156
+ }
2157
+
2158
+ // HEAD says clarify → cheap tier (ask a question, don't dispatch work)
2159
+ if (turn.shouldClarify) {
2160
+ return { tier: 'cheap', headJudgment: judgment };
2161
+ }
2162
+
2163
+ // HEAD says think/plan → full tier with opus
2164
+ if (turn.shouldThink) {
2165
+ return { tier: 'full', headJudgment: judgment, model: 'opus' };
2166
+ }
2167
+
2168
+ // HEAD says dispatch → full tier, model based on depth
2169
+ if (turn.shouldDispatch) {
2170
+ const model = turn.depth === 'deep' ? 'opus' : 'sonnet';
2171
+ return { tier: 'full', headJudgment: judgment, model };
2172
+ }
2173
+
2174
+ // HEAD says respond (not dispatch) → cheap
2175
+ if (turn.action.type === 'respond') {
2176
+ return { tier: 'cheap', headJudgment: judgment };
2177
+ }
2178
+
2179
+ // Default: let depth drive it
2180
+ if (turn.depth === 'light' || turn.depth === 'reflexive') {
2181
+ return { tier: 'cheap', headJudgment: judgment };
2182
+ }
2183
+ return { tier: 'full', headJudgment: judgment };
2184
+ }
2185
+
2186
+ // ── Fallback: HEAD not loaded, use simple heuristics ───────────────────
2089
2187
  const QUESTION_WORDS = /^(why|what|how|where|when|who|is my|check|show me|explain|tell me|list|am i|are there|does|did|can i|will|should i)/i;
2090
- const QUESTION_CONTAINS = /\b(why|what|how is|how are|where is|where are|explain|tell me|show me)\b/i;
2091
- if (QUESTION_WORDS.test(lower) || QUESTION_CONTAINS.test(lower)) {
2188
+ if (QUESTION_WORDS.test(lower)) {
2092
2189
  return { tier: 'cheap' };
2093
2190
  }
2094
2191
 
2095
- // Tier 3: FULL — everything else is a work task
2096
2192
  return { tier: 'full' };
2097
2193
  }
2098
2194
 
@@ -2176,11 +2272,16 @@ async function mainScreen(rl, ask) {
2176
2272
  const auth = await detectAuth();
2177
2273
 
2178
2274
  // ── Dashboard load animation (full mode only) ─────────────────────────────
2179
- const fx = await getFx();
2275
+ let fx = null;
2276
+ try { fx = await Promise.race([getFx(), new Promise(r => setTimeout(() => r(null), 3000))]); } catch {}
2180
2277
  let dashSpinner = null;
2181
2278
  if (fx && fx.getMode && fx.getMode() === 'full') {
2182
2279
  dashSpinner = fx.spinner('Loading dashboard...').start();
2183
2280
  }
2281
+ // Safety: kill spinner after 8s no matter what
2282
+ const _spinnerTimeout = dashSpinner ? setTimeout(() => {
2283
+ if (dashSpinner) { try { dashSpinner.stop(); } catch {} dashSpinner = null; }
2284
+ }, 8000) : null;
2184
2285
 
2185
2286
  // ── One-time default shell prompt for returning users (never asked before) ─
2186
2287
  if (profile.setupComplete && !profile.defaultShellAsked) {
@@ -2199,10 +2300,10 @@ async function mainScreen(rl, ask) {
2199
2300
  const claudeExpired = claudeSub?.expiresAt && Date.parse(claudeSub.expiresAt) < now;
2200
2301
  const openaiExpired = openaiSub?.expiresAt && Date.parse(openaiSub.expiresAt) < now;
2201
2302
 
2202
- // Silent OAuth token auto-refresh
2303
+ // Silent OAuth token auto-refresh (3s timeout — never block dashboard)
2203
2304
  try {
2204
2305
  const { autoRefreshToken } = await import('../src/profile.mjs');
2205
- await autoRefreshToken(cwd);
2306
+ await Promise.race([autoRefreshToken(cwd), new Promise(r => setTimeout(r, 3000))]);
2206
2307
  } catch {}
2207
2308
 
2208
2309
  // Append-only session archive sync
@@ -2258,7 +2359,7 @@ async function mainScreen(rl, ask) {
2258
2359
  } else {
2259
2360
  process.stdout.write(` ${DIM}${interrupted.reason} · ${interrupted.ageLabel}${RST}\n`);
2260
2361
  }
2261
- process.stdout.write(` ${DIM}[Enter] resume [n] new [s] skip${RST}\n\n`);
2362
+ process.stdout.write(` \x1b[36mEnter\x1b[0m resume \x1b[36mn\x1b[0m new \x1b[36ms\x1b[0m skip\n\n`);
2262
2363
 
2263
2364
  // Wait for a keypress to decide what to do with the card
2264
2365
  const readline2 = await import('node:readline');
@@ -2598,6 +2699,7 @@ async function mainScreen(rl, ask) {
2598
2699
  });
2599
2700
 
2600
2701
  // ── Resolve dashboard spinner before rendering ────────────────────────────
2702
+ if (_spinnerTimeout) clearTimeout(_spinnerTimeout);
2601
2703
  if (dashSpinner) dashSpinner.succeed('Dashboard ready');
2602
2704
 
2603
2705
  // ── Stale hint ────────────────────────────────────────────────────────────
@@ -2641,12 +2743,22 @@ async function mainScreen(rl, ask) {
2641
2743
  process.stdout.write(panel('Signals', recentLines, { width: panelW }) + '\n\n');
2642
2744
  }
2643
2745
 
2644
- // Input bar — rendered below panels; the key handler will overwrite this line
2645
- const inputHint = `${DIM}[?] help${RST}`;
2746
+ // Shortcut bar — always visible so the user never has to guess
2747
+ const autoLabel = profile.automode ? `\x1b[32m⚡auto\x1b[0m` : `${DIM}auto${RST}`;
2748
+ const shortcutItems = [
2749
+ `${CYAN}Enter${RST} resume`,
2750
+ `${CYAN}n${RST} new`,
2751
+ `${CYAN}/${RST} search`,
2752
+ `${CYAN}s${RST} settings`,
2753
+ `${CYAN}d${RST} doctor`,
2754
+ autoLabel,
2755
+ `${CYAN}q${RST} quit`,
2756
+ ];
2757
+ process.stdout.write(` ${DIM}${shortcutItems.join(' ')}${RST}\n\n`);
2758
+
2759
+ // Input bar — rendered below shortcut bar
2646
2760
  const inputLeft = tuiPrompt('task or command...');
2647
- const inputLeftW = (inputLeft + ' task or command...').replace(/\x1b\[[0-9;]*m/g, '').length;
2648
- const inputGap = Math.max(1, panelW - inputLeftW - 8);
2649
- process.stdout.write(` ${inputLeft}${' '.repeat(inputGap)}${inputHint}\n`);
2761
+ process.stdout.write(` ${inputLeft}\n`);
2650
2762
 
2651
2763
  // ── Key handling ──────────────────────────────────────────────────────────
2652
2764
  // Use raw keypress mode so we can show a live type-to-start buffer.
@@ -2848,6 +2960,17 @@ async function mainScreen(rl, ask) {
2848
2960
  await ask('\n Press Enter to continue...');
2849
2961
  return { next: 'main' };
2850
2962
  }
2963
+ if (cmd === 'auto') {
2964
+ const cwd2 = process.cwd();
2965
+ const prof = loadProfile(cwd2);
2966
+ prof.automode = !prof.automode;
2967
+ saveProfile(prof, { cwd: cwd2 });
2968
+ const state = prof.automode ? '\x1b[32mON\x1b[0m' : '\x1b[2mOFF\x1b[0m';
2969
+ process.stdout.write(`\n Automode: ${state}\n`);
2970
+ process.stdout.write(` ${prof.automode ? 'Tasks dispatch immediately (HEAD still gates dangerous ops)' : 'Tasks require Enter to confirm'}\n\n`);
2971
+ await ask(' Press Enter to continue...');
2972
+ return { next: 'main' };
2973
+ }
2851
2974
  if (cmd === 'init --replit') {
2852
2975
  await cmdInit(rl);
2853
2976
  return { next: 'main' };
@@ -2855,20 +2978,94 @@ async function mainScreen(rl, ask) {
2855
2978
  // fallthrough: unknown free command → treat as full task
2856
2979
  }
2857
2980
 
2858
- // Tier 2: CHEAPquestion/diagnostic, route to haiku
2981
+ // Tier 0.5: SKILLslash command routed through agent registry
2982
+ if (classified.tier === 'skill') {
2983
+ const skill = classified.skill;
2984
+ const skillArgs = classified.args || '';
2985
+
2986
+ // Free skills (e.g. /status) run deterministically with no agent
2987
+ if (skill.tier === 'free' || !skill.agent) {
2988
+ if (skill.command === 'status') {
2989
+ await cmdStatus([]);
2990
+ await ask('\n Press Enter to continue...');
2991
+ return { next: 'main' };
2992
+ }
2993
+ return { next: 'main' };
2994
+ }
2995
+
2996
+ // Build the task brief from the skill declaration
2997
+ let brief = null;
2998
+ try {
2999
+ if (typeof _cachedSkillToTaskBrief === 'function') {
3000
+ brief = _cachedSkillToTaskBrief(input, skillArgs);
3001
+ }
3002
+ } catch {}
3003
+
3004
+ const model = brief?.model || skill.model || 'sonnet';
3005
+ const prompt = brief?.objective || `/${skill.command} ${skillArgs}`.trim();
3006
+
3007
+ process.stdout.write(`\n Skill: /${skill.command} Agent: ${skill.agent} Model: ${model}\n`);
3008
+ if (skill.description) process.stdout.write(` ${skill.description}\n`);
3009
+ process.stdout.write(` \x1b[36mEnter\x1b[0m run \x1b[36mn\x1b[0m cancel\n\n`);
3010
+ const skillConfirm = (await ask(' > ')).trim().toLowerCase();
3011
+ if (skillConfirm === 'n' || skillConfirm === 'no') return { next: 'main' };
3012
+
3013
+ return { next: 'go', prompt, model };
3014
+ }
3015
+
3016
+ // Tier 2: CHEAP — question/diagnostic/reflexive
2859
3017
  if (classified.tier === 'cheap') {
2860
- process.stdout.write(`\n Routing to haiku for quick answer...\n`);
2861
- return { next: 'go', prompt: input, model: 'haiku' };
3018
+ const hj = classified.headJudgment;
3019
+ const model = hj ? 'haiku' : 'haiku';
3020
+ if (hj?.surfaceNoticings?.length > 0) {
3021
+ for (const n of hj.surfaceNoticings) {
3022
+ process.stdout.write(`\n \x1b[33m[HEAD]\x1b[0m ${n.observation}\n`);
3023
+ }
3024
+ }
3025
+ process.stdout.write(`\n Routing to ${model} for quick answer...\n`);
3026
+ return { next: 'go', prompt: input, model };
2862
3027
  }
2863
3028
 
2864
- // Tier 3: FULL — work task, confirm before dispatching
3029
+ // Tier 3: FULL — work task, HEAD-informed dispatch
2865
3030
  if (classified.tier === 'full') {
3031
+ const hj = classified.headJudgment;
3032
+ const model = classified.model || 'sonnet';
2866
3033
  const summary = input.length > 60 ? input.slice(0, 57) + '...' : input;
2867
- process.stdout.write(`\n Launch coding session: ${summary}\n`);
2868
- process.stdout.write(` Model: sonnet [Enter] to proceed, [n] to cancel\n\n`);
3034
+
3035
+ // Surface HEAD noticings before confirming
3036
+ if (hj?.surfaceNoticings?.length > 0) {
3037
+ for (const n of hj.surfaceNoticings) {
3038
+ process.stdout.write(`\n \x1b[33m[HEAD]\x1b[0m ${n.observation}`);
3039
+ }
3040
+ process.stdout.write('\n');
3041
+ }
3042
+
3043
+ // HEAD's shouldAskUser gates the dispatch — dangerous/irreversible ops
3044
+ if (hj?.shouldAskUser) {
3045
+ const reason = hj.obligations?.find(o => o.type === 'askBeforeIrreversi')?.description || hj.rationale;
3046
+ process.stdout.write(`\n \x1b[31m⚠ CAUTION\x1b[0m ${reason}\n`);
3047
+ process.stdout.write(` Task: ${summary}\n`);
3048
+ process.stdout.write(` Depth: ${hj.depth} Model: ${model}\n`);
3049
+ process.stdout.write(` \x1b[36mEnter\x1b[0m proceed \x1b[36mn\x1b[0m cancel\n\n`);
3050
+ const confirm = (await ask(' > ')).trim().toLowerCase();
3051
+ if (confirm === 'n' || confirm === 'no') return { next: 'main' };
3052
+ return { next: 'go', prompt: input, model };
3053
+ }
3054
+
3055
+ // Automode: if HEAD says it's safe, just go — no confirmation needed
3056
+ const automode = profile.automode ?? profile.settings?.automode ?? false;
3057
+ if (automode) {
3058
+ process.stdout.write(`\n \x1b[36m⚡\x1b[0m ${summary} (${model}, depth: ${hj?.depth || '?'})\n`);
3059
+ return { next: 'go', prompt: input, model };
3060
+ }
3061
+
3062
+ // Manual mode — show depth, wait for confirmation
3063
+ process.stdout.write(`\n Launch: ${summary}\n`);
3064
+ process.stdout.write(` Depth: ${hj?.depth || '?'} Model: ${model}\n`);
3065
+ process.stdout.write(` \x1b[36mEnter\x1b[0m go \x1b[36mn\x1b[0m cancel\n\n`);
2869
3066
  const confirm = (await ask(' > ')).trim().toLowerCase();
2870
3067
  if (confirm === 'n' || confirm === 'no') return { next: 'main' };
2871
- return { next: 'go', prompt: input };
3068
+ return { next: 'go', prompt: input, model };
2872
3069
  }
2873
3070
 
2874
3071
  // Default fallback
@@ -2983,27 +3180,36 @@ async function paletteHelpScreen(rl, ask) {
2983
3180
  const DIM = '\x1b[2m';
2984
3181
  const RESET = '\x1b[0m';
2985
3182
 
3183
+ const CYAN = '\x1b[36m';
2986
3184
  const lines = [
2987
3185
  top,
2988
- row('Command Palette'),
3186
+ row(`${CYAN}Keyboard Shortcuts${RESET} (single key, no Enter needed)`),
3187
+ sep,
3188
+ row(`${CYAN}Enter${RESET} Resume last session`),
3189
+ row(`${CYAN}n${RESET} New coding session`),
3190
+ row(`${CYAN}1-9${RESET} Resume session by number`),
3191
+ row(`${CYAN}/${RESET} Search session history`),
3192
+ row(`${CYAN}s${RESET} Settings`),
3193
+ row(`${CYAN}d${RESET} Doctor (repo diagnostics)`),
3194
+ row(`${CYAN}t${RESET} Team`),
3195
+ row(`${CYAN}i${RESET} Import sessions`),
3196
+ row(`${CYAN}q${RESET} Quit`),
3197
+ row(`${CYAN}?${RESET} This help`),
3198
+ sep,
3199
+ row(`${CYAN}Typed Commands${RESET} (type then Enter)`),
2989
3200
  sep,
2990
- row(`${DIM}resume r${RESET} Resume last session`),
2991
3201
  row(`${DIM}status${RESET} Provider health + budget`),
2992
- row(`${DIM}sessions ss${RESET} List recent sessions`),
3202
+ row(`${DIM}sessions${RESET} List recent sessions`),
2993
3203
  row(`${DIM}search <query>${RESET} Search session history`),
2994
- row(`${DIM}budget b${RESET} Token usage + routing`),
2995
- row(`${DIM}health h${RESET} System health check`),
2996
- row(`${DIM}doctor d${RESET} Repo diagnostics`),
2997
- row(`${DIM}settings s${RESET} Settings screen`),
2998
- row(`${DIM}team t${RESET} Team screen`),
2999
- row(`${DIM}help ?${RESET} Show this help`),
3000
- row(`${DIM}quit q${RESET} Exit`),
3204
+ row(`${DIM}budget${RESET} Token usage + routing`),
3205
+ row(`${DIM}health${RESET} System health check`),
3001
3206
  sep,
3002
- row('Or type any task to launch a coding session'),
3003
- row(`${DIM}Questions (why/how/what) → haiku (cheap)${RESET}`),
3004
- row(`${DIM}Work tasks → confirm then dispatch${RESET}`),
3207
+ row(`${CYAN}Natural Language${RESET} (just type)`),
3005
3208
  sep,
3006
- row(`${DIM}[Enter] go back${RESET}`),
3209
+ row(`Questions quick answer (haiku)`),
3210
+ row(`Work tasks → HEAD evaluates, then dispatches`),
3211
+ sep,
3212
+ row(`${DIM}Enter${RESET} go back`),
3007
3213
  bot,
3008
3214
  ];
3009
3215
 
@@ -5967,6 +6173,12 @@ async function cmdSpecialistGo(specialist, args) {
5967
6173
  // ─── Entry point ─────────────────────────────────────────────────────────────
5968
6174
 
5969
6175
  async function main() {
6176
+ // Prime agent + skill registries early so detectTask and classifyInput
6177
+ // can match agents/skills synchronously during interactive sessions.
6178
+ primeAgentRegistry().catch(() => {});
6179
+ _primeRegistryCache().catch(() => {});
6180
+ _getHeadModule().catch(() => {});
6181
+
5970
6182
  const args = process.argv.slice(2);
5971
6183
  const cmd = args[0];
5972
6184
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dual-brain",
3
- "version": "0.2.9",
3
+ "version": "0.2.11",
4
4
  "description": "AI orchestration across Claude + OpenAI subscriptions — smart routing, budget awareness, and dual-brain collaboration",
5
5
  "type": "module",
6
6
  "bin": {
@@ -31,7 +31,10 @@
31
31
  "./integrity": "./src/integrity.mjs",
32
32
  "./prompt-audit": "./src/prompt-audit.mjs",
33
33
  "./head": "./src/head.mjs",
34
- "./templates": "./src/templates.mjs"
34
+ "./templates": "./src/templates.mjs",
35
+ "./agents": "./src/agents/registry.mjs",
36
+ "./collaboration": "./src/collaboration.mjs",
37
+ "./provider-context": "./src/provider-context.mjs"
35
38
  },
36
39
  "keywords": [
37
40
  "claude-code",
@@ -102,6 +105,9 @@
102
105
  "src/prompt-audit.mjs",
103
106
  "src/head.mjs",
104
107
  "src/templates.mjs",
108
+ "src/agents/registry.mjs",
109
+ "src/collaboration.mjs",
110
+ "src/provider-context.mjs",
105
111
  "bin/*.mjs",
106
112
  "hooks/enforce-tier.mjs",
107
113
  "hooks/cost-logger.mjs",