@bobsworkshop/cli 0.1.2 → 0.1.4

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.
package/dist/bin/bob.js CHANGED
@@ -1,5 +1,4 @@
1
1
  #!/usr/bin/env node
2
- #!/usr/bin/env node
3
2
  import {
4
3
  buildLocalContext,
5
4
  callCloudFunction,
@@ -7,19 +6,21 @@ import {
7
6
  extractProposedFile,
8
7
  getConfig,
9
8
  getConfigPath,
9
+ isAuthenticated,
10
10
  loadLocalSuggestions,
11
11
  markSuggestionStatus,
12
+ processAllProposedFiles,
12
13
  proposeAndWriteFile,
13
14
  readFileContent,
14
15
  registerLoginCommand,
15
16
  setConfigValue,
16
17
  stripCodeBlockFromResponse
17
- } from "./chunk-LHWBSCJ4.js";
18
+ } from "./chunk-J4RRWEHU.js";
18
19
 
19
20
  // bin/bob.ts
20
21
  import { Command } from "commander";
21
- import chalk17 from "chalk";
22
- import * as path9 from "path";
22
+ import chalk22 from "chalk";
23
+ import * as path12 from "path";
23
24
 
24
25
  // src/commands/config.ts
25
26
  import chalk from "chalk";
@@ -108,9 +109,189 @@ function registerConfigCommand(program2) {
108
109
  // src/commands/chat.ts
109
110
  import chalk7 from "chalk";
110
111
  import ora2 from "ora";
111
- import * as fs4 from "fs";
112
- import * as path5 from "path";
113
- import * as readline from "readline";
112
+ import * as fs5 from "fs";
113
+ import * as path6 from "path";
114
+ import * as readline2 from "readline";
115
+
116
+ // src/core/profile-store.ts
117
+ import * as fs from "fs";
118
+ import * as path from "path";
119
+ import * as os from "os";
120
+ import * as crypto from "crypto";
121
+ var BOB_DIR = path.join(os.homedir(), ".bob");
122
+ function getProfileDir(projectName) {
123
+ const name = projectName || path.basename(process.cwd());
124
+ const profileDir = path.join(BOB_DIR, "projects", name, "profile");
125
+ const dirs = [
126
+ path.join(profileDir, "daily"),
127
+ path.join(profileDir, "weekly"),
128
+ path.join(profileDir, "monthly")
129
+ ];
130
+ for (const dir of dirs) {
131
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
132
+ }
133
+ return profileDir;
134
+ }
135
+ function saveDailyProfile(profile) {
136
+ const profileDir = getProfileDir();
137
+ const filePath = path.join(profileDir, "daily", `${profile.date}.json`);
138
+ fs.writeFileSync(filePath, JSON.stringify(profile, null, 2));
139
+ updateCurrentDNA(profile);
140
+ }
141
+ function saveWeeklyProfile(profile) {
142
+ const profileDir = getProfileDir();
143
+ const filePath = path.join(profileDir, "weekly", `${profile.weekOf}.json`);
144
+ fs.writeFileSync(filePath, JSON.stringify(profile, null, 2));
145
+ }
146
+ function saveMonthlyProfile(profile) {
147
+ const profileDir = getProfileDir();
148
+ const filePath = path.join(profileDir, "monthly", `${profile.month}.json`);
149
+ fs.writeFileSync(filePath, JSON.stringify(profile, null, 2));
150
+ const dnaPath = path.join(profileDir, "current-dna.json");
151
+ fs.writeFileSync(dnaPath, JSON.stringify({
152
+ ...profile.personalitySnapshot,
153
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
154
+ source: "monthly",
155
+ month: profile.month
156
+ }, null, 2));
157
+ }
158
+ function loadDailyProfiles(days) {
159
+ const profileDir = getProfileDir();
160
+ const dailyDir = path.join(profileDir, "daily");
161
+ if (!fs.existsSync(dailyDir)) return [];
162
+ const files = fs.readdirSync(dailyDir).filter((f) => f.endsWith(".json")).sort().reverse().slice(0, days);
163
+ return files.map((f) => {
164
+ try {
165
+ return JSON.parse(fs.readFileSync(path.join(dailyDir, f), "utf-8"));
166
+ } catch {
167
+ return null;
168
+ }
169
+ }).filter(Boolean);
170
+ }
171
+ function loadWeeklyProfiles(weeks) {
172
+ const profileDir = getProfileDir();
173
+ const weeklyDir = path.join(profileDir, "weekly");
174
+ if (!fs.existsSync(weeklyDir)) return [];
175
+ const files = fs.readdirSync(weeklyDir).filter((f) => f.endsWith(".json")).sort().reverse().slice(0, weeks);
176
+ return files.map((f) => {
177
+ try {
178
+ return JSON.parse(fs.readFileSync(path.join(weeklyDir, f), "utf-8"));
179
+ } catch {
180
+ return null;
181
+ }
182
+ }).filter(Boolean);
183
+ }
184
+ function loadCurrentDNA() {
185
+ const profileDir = getProfileDir();
186
+ const dnaPath = path.join(profileDir, "current-dna.json");
187
+ if (!fs.existsSync(dnaPath)) return null;
188
+ try {
189
+ return JSON.parse(fs.readFileSync(dnaPath, "utf-8"));
190
+ } catch {
191
+ return null;
192
+ }
193
+ }
194
+ function updateCurrentDNA(dailyProfile) {
195
+ const profileDir = getProfileDir();
196
+ const dnaPath = path.join(profileDir, "current-dna.json");
197
+ fs.writeFileSync(dnaPath, JSON.stringify({
198
+ archetype: inferArchetype(dailyProfile),
199
+ communicationStyle: dailyProfile.communicationStyle.tone,
200
+ workRhythm: dailyProfile.workFrequency.pattern,
201
+ emotionalState: dailyProfile.emotionalState.dominant,
202
+ decisionMaking: dailyProfile.decisionStyle.speed + ", " + dailyProfile.decisionStyle.riskTolerance,
203
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
204
+ source: "daily",
205
+ date: dailyProfile.date
206
+ }, null, 2));
207
+ }
208
+ function inferArchetype(profile) {
209
+ const style = profile.communicationStyle.tone.toLowerCase();
210
+ const decision = profile.decisionStyle.speed.toLowerCase();
211
+ const work = profile.workFrequency.pattern.toLowerCase();
212
+ if (style.includes("direct") && decision.includes("fast")) return "The Builder Who Ships";
213
+ if (style.includes("cautious") && decision.includes("deliberate")) return "The Methodical Architect";
214
+ if (work.includes("burst")) return "The Sprint Specialist";
215
+ if (style.includes("curious") || style.includes("exploratory")) return "The Explorer";
216
+ return "The Adaptive Engineer";
217
+ }
218
+ function getLocalConversationMessages(projectName) {
219
+ const name = projectName || path.basename(process.cwd());
220
+ const convosDir = path.join(BOB_DIR, "projects", name, "conversations");
221
+ if (!fs.existsSync(convosDir)) return [];
222
+ const allMessages = [];
223
+ const conversationDirs = fs.readdirSync(convosDir, { withFileTypes: true }).filter((d) => d.isDirectory());
224
+ for (const convoDir of conversationDirs) {
225
+ const messagesDir = path.join(convosDir, convoDir.name, "messages");
226
+ if (!fs.existsSync(messagesDir)) continue;
227
+ const messageFiles = fs.readdirSync(messagesDir).filter((f) => f.endsWith(".json")).sort();
228
+ for (const msgFile of messageFiles) {
229
+ try {
230
+ const msg = JSON.parse(fs.readFileSync(path.join(messagesDir, msgFile), "utf-8"));
231
+ allMessages.push({
232
+ role: msg.sender || "unknown",
233
+ content: msg.message || "",
234
+ timestamp: msg.timestamp || ""
235
+ });
236
+ } catch {
237
+ }
238
+ }
239
+ }
240
+ return allMessages;
241
+ }
242
+ async function getFirestoreTodayMessages() {
243
+ try {
244
+ if (!isAuthenticated()) return [];
245
+ const response = await callCloudFunction("getCLITodayMessages", {});
246
+ if (!response || !response.success || !Array.isArray(response.messages)) {
247
+ return [];
248
+ }
249
+ return response.messages.map((msg) => ({
250
+ role: msg.sender || "unknown",
251
+ content: msg.message || "",
252
+ timestamp: msg.timestamp ? new Date(msg.timestamp).toISOString() : ""
253
+ }));
254
+ } catch (error) {
255
+ console.error("[PROFILE_STORE] Firestore message fetch failed (non-fatal):", error.message);
256
+ return [];
257
+ }
258
+ }
259
+ function generateMessageKey(msg) {
260
+ const normalizedContent = (msg.content || "").trim().substring(0, 100);
261
+ const raw = `${msg.timestamp}|${normalizedContent}`;
262
+ return crypto.createHash("md5").update(raw).digest("hex");
263
+ }
264
+ function deduplicateMessages(localMessages, firestoreMessages) {
265
+ const seen = /* @__PURE__ */ new Map();
266
+ for (const msg of localMessages) {
267
+ const key = generateMessageKey(msg);
268
+ seen.set(key, msg);
269
+ }
270
+ for (const msg of firestoreMessages) {
271
+ const key = generateMessageKey(msg);
272
+ if (!seen.has(key)) {
273
+ seen.set(key, msg);
274
+ }
275
+ }
276
+ return Array.from(seen.values()).sort((a, b) => {
277
+ if (!a.timestamp || !b.timestamp) return 0;
278
+ return a.timestamp.localeCompare(b.timestamp);
279
+ });
280
+ }
281
+ async function getConversationMessages(projectName) {
282
+ const localMessages = getLocalConversationMessages(projectName);
283
+ if (!isAuthenticated()) return localMessages;
284
+ const firestoreMessages = await getFirestoreTodayMessages();
285
+ return deduplicateMessages(localMessages, firestoreMessages);
286
+ }
287
+ async function getTodayMessages(projectName) {
288
+ const all = await getConversationMessages(projectName);
289
+ const twentyFourHoursAgo = new Date(Date.now() - 24 * 60 * 60 * 1e3).toISOString();
290
+ return all.filter((msg) => {
291
+ if (!msg.timestamp) return false;
292
+ return msg.timestamp >= twentyFourHoursAgo;
293
+ });
294
+ }
114
295
 
115
296
  // src/ai/persona.ts
116
297
  var STANDARD_STYLE_PROMPT = `You are Bob: friendly, direct, senior-level engineering partner.
@@ -139,6 +320,40 @@ CONSULTANT MODE RULES (VERY STRICT):
139
320
  - DO NOT, under any circumstances, generate code.
140
321
  - Focus entirely on the conceptual and strategic aspects of the user's query.
141
322
  - Be warm, concise, and direct in your advice.`;
323
+ function buildPersonalizedPrompt(mode) {
324
+ const basePrompt = mode === "consultant" ? CONSULTANT_STYLE_PROMPT : STANDARD_STYLE_PROMPT;
325
+ const dna = loadCurrentDNA();
326
+ if (!dna) {
327
+ return basePrompt;
328
+ }
329
+ const personalizationBlock = `
330
+
331
+ ### USER PROFILE \u2014 ADAPT YOUR STYLE ###
332
+ This user has a known behavioral profile. Adjust your tone, pacing, and approach to match their personality:
333
+
334
+ Archetype: ${dna.archetype || "Unknown"}
335
+ Communication Style: ${dna.communicationStyle || "Unknown"}
336
+ Work Rhythm: ${dna.workRhythm || "Unknown"}
337
+ Emotional State: ${dna.emotionalState || "Unknown"}
338
+ Decision Making: ${dna.decisionMaking || "Unknown"}
339
+ ${dna.growth ? `Recent Growth: ${dna.growth}` : ""}
340
+
341
+ ADAPTATION RULES:
342
+ - If their communication style is "direct and impatient" \u2192 lead with the answer, skip preamble.
343
+ - If their communication style is "exploratory and curious" \u2192 offer context, ask clarifying questions.
344
+ - If their work rhythm is "burst-mode" \u2192 keep responses tight and actionable for momentum.
345
+ - If their work rhythm is "steady" \u2192 provide thorough explanations at a measured pace.
346
+ - If their emotional state is "frustrated" \u2192 validate first, then solve. Don't lecture.
347
+ - If their emotional state is "excited" \u2192 match their energy, move fast.
348
+ - If their decision making is "fast" \u2192 give a single recommendation, not a list of options.
349
+ - If their decision making is "deliberate" \u2192 present options with tradeoffs.
350
+ - If they show high independence \u2192 don't over-explain. Trust them to figure out details.
351
+ - If they seek validation \u2192 affirm their thinking before extending it.
352
+
353
+ Do NOT mention that you are adapting to their profile. Do NOT reference this section. Just naturally embody the appropriate style.
354
+ ### END USER PROFILE ###`;
355
+ return basePrompt + personalizationBlock;
356
+ }
142
357
 
143
358
  // src/ui/renderer.ts
144
359
  import chalk2 from "chalk";
@@ -147,49 +362,49 @@ function renderMarkdown(text) {
147
362
  }
148
363
 
149
364
  // src/core/conversation-store.ts
150
- import * as fs2 from "fs";
151
- import * as path2 from "path";
365
+ import * as fs3 from "fs";
366
+ import * as path3 from "path";
152
367
 
153
368
  // src/core/project-map.ts
154
- import * as fs from "fs";
155
- import * as path from "path";
156
- import * as os from "os";
157
- var BOB_DIR = path.join(os.homedir(), ".bob");
158
- var PROJECTS_DIR = path.join(BOB_DIR, "projects");
369
+ import * as fs2 from "fs";
370
+ import * as path2 from "path";
371
+ import * as os2 from "os";
372
+ var BOB_DIR2 = path2.join(os2.homedir(), ".bob");
373
+ var PROJECTS_DIR = path2.join(BOB_DIR2, "projects");
159
374
  function getProjectName(workingDir) {
160
- return path.basename(workingDir);
375
+ return path2.basename(workingDir);
161
376
  }
162
377
  function getProjectDir(workingDir) {
163
378
  const name = getProjectName(workingDir);
164
- return path.join(PROJECTS_DIR, name);
379
+ return path2.join(PROJECTS_DIR, name);
165
380
  }
166
381
  function ensureProjectStructure(workingDir) {
167
382
  const projectDir = getProjectDir(workingDir);
168
- const conversationsDir = path.join(projectDir, "conversations");
169
- const analysisDir = path.join(projectDir, "analysis");
170
- const runsDir = path.join(analysisDir, "runs");
171
- for (const dir of [BOB_DIR, PROJECTS_DIR, projectDir, conversationsDir, analysisDir, runsDir]) {
172
- if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
383
+ const conversationsDir = path2.join(projectDir, "conversations");
384
+ const analysisDir = path2.join(projectDir, "analysis");
385
+ const runsDir = path2.join(analysisDir, "runs");
386
+ for (const dir of [BOB_DIR2, PROJECTS_DIR, projectDir, conversationsDir, analysisDir, runsDir]) {
387
+ if (!fs2.existsSync(dir)) fs2.mkdirSync(dir, { recursive: true });
173
388
  }
174
- const metaPath = path.join(projectDir, "project.json");
175
- if (!fs.existsSync(metaPath)) {
389
+ const metaPath = path2.join(projectDir, "project.json");
390
+ if (!fs2.existsSync(metaPath)) {
176
391
  const meta = {
177
392
  name: getProjectName(workingDir),
178
393
  path: workingDir,
179
394
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
180
395
  lastIndexed: null
181
396
  };
182
- fs.writeFileSync(metaPath, JSON.stringify(meta, null, 2));
397
+ fs2.writeFileSync(metaPath, JSON.stringify(meta, null, 2));
183
398
  }
184
399
  return { projectDir, conversationsDir, analysisDir, runsDir };
185
400
  }
186
401
  function createAnalysisRun(workingDir, files) {
187
402
  const { runsDir } = ensureProjectStructure(workingDir);
188
403
  const runId = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
189
- const runDir = path.join(runsDir, runId);
190
- const tasksDir = path.join(runDir, "tasks");
191
- fs.mkdirSync(runDir, { recursive: true });
192
- fs.mkdirSync(tasksDir, { recursive: true });
404
+ const runDir = path2.join(runsDir, runId);
405
+ const tasksDir = path2.join(runDir, "tasks");
406
+ fs2.mkdirSync(runDir, { recursive: true });
407
+ fs2.mkdirSync(tasksDir, { recursive: true });
193
408
  const manifest = {
194
409
  runId,
195
410
  status: "in_progress",
@@ -198,7 +413,7 @@ function createAnalysisRun(workingDir, files) {
198
413
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
199
414
  projectPath: workingDir
200
415
  };
201
- fs.writeFileSync(path.join(runDir, "manifest.json"), JSON.stringify(manifest, null, 2));
416
+ fs2.writeFileSync(path2.join(runDir, "manifest.json"), JSON.stringify(manifest, null, 2));
202
417
  for (const filePath of files) {
203
418
  const taskId = filePath.replace(/[\/\\]/g, "_");
204
419
  const task = {
@@ -208,60 +423,60 @@ function createAnalysisRun(workingDir, files) {
208
423
  dependencies: [],
209
424
  error: null
210
425
  };
211
- fs.writeFileSync(path.join(tasksDir, `${taskId}.json`), JSON.stringify(task, null, 2));
426
+ fs2.writeFileSync(path2.join(tasksDir, `${taskId}.json`), JSON.stringify(task, null, 2));
212
427
  }
213
428
  return { runId, runDir, tasksDir };
214
429
  }
215
430
  function completeTask(tasksDir, filePath, summary) {
216
431
  const taskId = filePath.replace(/[\/\\]/g, "_");
217
- const taskPath = path.join(tasksDir, `${taskId}.json`);
218
- if (fs.existsSync(taskPath)) {
219
- const task = JSON.parse(fs.readFileSync(taskPath, "utf-8"));
432
+ const taskPath = path2.join(tasksDir, `${taskId}.json`);
433
+ if (fs2.existsSync(taskPath)) {
434
+ const task = JSON.parse(fs2.readFileSync(taskPath, "utf-8"));
220
435
  task.status = true;
221
436
  task.summary = summary;
222
- fs.writeFileSync(taskPath, JSON.stringify(task, null, 2));
437
+ fs2.writeFileSync(taskPath, JSON.stringify(task, null, 2));
223
438
  }
224
439
  }
225
440
  function updateManifestProgress(runDir, completedFiles, status) {
226
- const manifestPath = path.join(runDir, "manifest.json");
227
- if (fs.existsSync(manifestPath)) {
228
- const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
441
+ const manifestPath = path2.join(runDir, "manifest.json");
442
+ if (fs2.existsSync(manifestPath)) {
443
+ const manifest = JSON.parse(fs2.readFileSync(manifestPath, "utf-8"));
229
444
  manifest.completedFiles = completedFiles;
230
445
  if (status) manifest.status = status;
231
- fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
446
+ fs2.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
232
447
  }
233
448
  }
234
449
  function saveSummaries(workingDir, summaries) {
235
450
  const { analysisDir } = ensureProjectStructure(workingDir);
236
- fs.writeFileSync(path.join(analysisDir, "summaries.json"), JSON.stringify(summaries, null, 2));
451
+ fs2.writeFileSync(path2.join(analysisDir, "summaries.json"), JSON.stringify(summaries, null, 2));
237
452
  const projectDir = getProjectDir(workingDir);
238
- const metaPath = path.join(projectDir, "project.json");
239
- if (fs.existsSync(metaPath)) {
240
- const meta = JSON.parse(fs.readFileSync(metaPath, "utf-8"));
453
+ const metaPath = path2.join(projectDir, "project.json");
454
+ if (fs2.existsSync(metaPath)) {
455
+ const meta = JSON.parse(fs2.readFileSync(metaPath, "utf-8"));
241
456
  meta.lastIndexed = (/* @__PURE__ */ new Date()).toISOString();
242
- fs.writeFileSync(metaPath, JSON.stringify(meta, null, 2));
457
+ fs2.writeFileSync(metaPath, JSON.stringify(meta, null, 2));
243
458
  }
244
459
  }
245
460
  function saveDependencies(workingDir, dependencies) {
246
461
  const { analysisDir } = ensureProjectStructure(workingDir);
247
- fs.writeFileSync(path.join(analysisDir, "dependencies.json"), JSON.stringify(dependencies, null, 2));
462
+ fs2.writeFileSync(path2.join(analysisDir, "dependencies.json"), JSON.stringify(dependencies, null, 2));
248
463
  }
249
464
  function loadSummaries(workingDir) {
250
465
  const { analysisDir } = ensureProjectStructure(workingDir);
251
- const summariesPath = path.join(analysisDir, "summaries.json");
252
- if (!fs.existsSync(summariesPath)) return null;
466
+ const summariesPath = path2.join(analysisDir, "summaries.json");
467
+ if (!fs2.existsSync(summariesPath)) return null;
253
468
  try {
254
- return JSON.parse(fs.readFileSync(summariesPath, "utf-8"));
469
+ return JSON.parse(fs2.readFileSync(summariesPath, "utf-8"));
255
470
  } catch {
256
471
  return null;
257
472
  }
258
473
  }
259
474
  function loadDependencies(workingDir) {
260
475
  const { analysisDir } = ensureProjectStructure(workingDir);
261
- const depsPath = path.join(analysisDir, "dependencies.json");
262
- if (!fs.existsSync(depsPath)) return null;
476
+ const depsPath = path2.join(analysisDir, "dependencies.json");
477
+ if (!fs2.existsSync(depsPath)) return null;
263
478
  try {
264
- return JSON.parse(fs.readFileSync(depsPath, "utf-8"));
479
+ return JSON.parse(fs2.readFileSync(depsPath, "utf-8"));
265
480
  } catch {
266
481
  return null;
267
482
  }
@@ -270,20 +485,20 @@ function loadDependencies(workingDir) {
270
485
  // src/core/conversation-store.ts
271
486
  function saveMessage(conversationId, message, meta) {
272
487
  const { conversationsDir } = ensureProjectStructure(process.cwd());
273
- const convoDir = path2.join(conversationsDir, conversationId);
274
- const messagesDir = path2.join(convoDir, "messages");
275
- if (!fs2.existsSync(convoDir)) fs2.mkdirSync(convoDir, { recursive: true });
276
- if (!fs2.existsSync(messagesDir)) fs2.mkdirSync(messagesDir, { recursive: true });
488
+ const convoDir = path3.join(conversationsDir, conversationId);
489
+ const messagesDir = path3.join(convoDir, "messages");
490
+ if (!fs3.existsSync(convoDir)) fs3.mkdirSync(convoDir, { recursive: true });
491
+ if (!fs3.existsSync(messagesDir)) fs3.mkdirSync(messagesDir, { recursive: true });
277
492
  const messageFilename = `${Date.now()}_${message.sender}.json`;
278
- fs2.writeFileSync(
279
- path2.join(messagesDir, messageFilename),
493
+ fs3.writeFileSync(
494
+ path3.join(messagesDir, messageFilename),
280
495
  JSON.stringify(message, null, 2)
281
496
  );
282
- const metaPath = path2.join(convoDir, "conversation.json");
497
+ const metaPath = path3.join(convoDir, "conversation.json");
283
498
  let convoMeta;
284
- if (fs2.existsSync(metaPath)) {
499
+ if (fs3.existsSync(metaPath)) {
285
500
  try {
286
- convoMeta = JSON.parse(fs2.readFileSync(metaPath, "utf-8"));
501
+ convoMeta = JSON.parse(fs3.readFileSync(metaPath, "utf-8"));
287
502
  } catch {
288
503
  convoMeta = createMeta(conversationId, meta);
289
504
  }
@@ -296,7 +511,7 @@ function saveMessage(conversationId, message, meta) {
296
511
  if (!convoMeta.title && message.sender === "user") {
297
512
  convoMeta.title = message.message.slice(0, 80);
298
513
  }
299
- fs2.writeFileSync(metaPath, JSON.stringify(convoMeta, null, 2));
514
+ fs3.writeFileSync(metaPath, JSON.stringify(convoMeta, null, 2));
300
515
  }
301
516
  function createMeta(conversationId, meta) {
302
517
  return {
@@ -314,8 +529,8 @@ function createMeta(conversationId, meta) {
314
529
  }
315
530
 
316
531
  // src/core/file-retrieval.ts
317
- import * as fs3 from "fs";
318
- import * as path3 from "path";
532
+ import * as fs4 from "fs";
533
+ import * as path4 from "path";
319
534
  async function getRelevantFileContents(userMessage, localEndpoint) {
320
535
  const cwd = process.cwd();
321
536
  const summaries = loadSummaries(cwd);
@@ -331,8 +546,9 @@ async function getRelevantFileContents(userMessage, localEndpoint) {
331
546
  if (dependencies && Object.keys(dependencies).length > 0) {
332
547
  mapContext += "\nDEPENDENCIES:\n";
333
548
  for (const [filePath, deps] of Object.entries(dependencies)) {
334
- if (deps.length > 0) {
335
- mapContext += `- ${filePath} depends on: [${deps.join(", ")}]
549
+ const depArray = Array.isArray(deps) ? deps : typeof deps === "object" && deps !== null ? Object.keys(deps) : [];
550
+ if (depArray.length > 0) {
551
+ mapContext += `- ${filePath} depends on: [${depArray.join(", ")}]
336
552
  `;
337
553
  }
338
554
  }
@@ -361,11 +577,11 @@ Return ONLY the JSON array of relevant file paths:`
361
577
  }
362
578
  let fileContents = "## RELEVANT FILES (selected by Bob from project index) ##\n\n";
363
579
  const validFiles = [];
364
- for (const filePath of selectedFiles.slice(0, 5)) {
365
- const absolutePath = path3.join(cwd, filePath);
580
+ for (const filePath of selectedFiles.slice(0, 10)) {
581
+ const absolutePath = path4.join(cwd, filePath);
366
582
  try {
367
- if (fs3.existsSync(absolutePath)) {
368
- const content = fs3.readFileSync(absolutePath, "utf-8");
583
+ if (fs4.existsSync(absolutePath)) {
584
+ const content = fs4.readFileSync(absolutePath, "utf-8");
369
585
  fileContents += `--- FILE: ${filePath} ---
370
586
  ${content}
371
587
  --- END FILE ---
@@ -385,6 +601,7 @@ ${content}
385
601
  // src/commands/deepdive.ts
386
602
  import chalk4 from "chalk";
387
603
  import ora from "ora";
604
+ import * as readline from "readline";
388
605
 
389
606
  // src/ui/animations/deep-dive.ts
390
607
  import chalk3 from "chalk";
@@ -578,6 +795,9 @@ function startDeepDiveAnimation() {
578
795
 
579
796
  // src/commands/deepdive.ts
580
797
  var DIVE_BORDER = chalk4.blue;
798
+ var AMBER = chalk4.hex("#FFAB00");
799
+ var GRAY = chalk4.gray;
800
+ var BORDER = chalk4.hex("#455A64");
581
801
  function registerDeepDiveCommand(program2) {
582
802
  program2.command("deepdives").description("List all deep dives in the current conversation").action(async () => {
583
803
  const config = getConfig();
@@ -595,9 +815,7 @@ function registerDeepDiveCommand(program2) {
595
815
  }
596
816
  const spinner = ora({ text: chalk4.cyan(" Loading deep dives..."), spinner: "dots" }).start();
597
817
  try {
598
- const result = await callCloudFunction("listCLIDeepDives", {
599
- conversationId: config.conversationId
600
- });
818
+ const result = await callCloudFunction("listCLIDeepDives", { conversationId: config.conversationId });
601
819
  spinner.stop();
602
820
  const dives = result.deepDives || [];
603
821
  console.log("");
@@ -606,7 +824,7 @@ function registerDeepDiveCommand(program2) {
606
824
  console.log(DIVE_BORDER(" \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"));
607
825
  if (dives.length === 0) {
608
826
  console.log(DIVE_BORDER(" \u2551") + chalk4.gray(" No deep dives yet. ") + DIVE_BORDER("\u2551"));
609
- console.log(DIVE_BORDER(" \u2551") + chalk4.gray(" Use /deepdive in interactive mode. ") + DIVE_BORDER("\u2551"));
827
+ console.log(DIVE_BORDER(" \u2551") + chalk4.gray(" Use `bob deepdive` to create one. ") + DIVE_BORDER("\u2551"));
610
828
  } else {
611
829
  for (const dive of dives) {
612
830
  const preview = (dive.initiatingPrompt || "No prompt").slice(0, 35);
@@ -617,12 +835,100 @@ function registerDeepDiveCommand(program2) {
617
835
  }
618
836
  console.log(DIVE_BORDER(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
619
837
  console.log("");
838
+ console.log(chalk4.gray(" Commands:"));
839
+ console.log(chalk4.gray(" bob deepdive \u2014 Create a new deep dive"));
840
+ console.log(chalk4.gray(" bob deepdives-join \u2014 Re-enter an existing deep dive"));
841
+ console.log("");
842
+ } catch (error) {
843
+ spinner.stop();
844
+ console.log(chalk4.red(` \u274C ${error.message}`));
845
+ console.log("");
846
+ }
847
+ });
848
+ program2.command("deepdives-join").description("Re-enter an existing deep dive").action(async () => {
849
+ const config = getConfig();
850
+ if (!config.loggedIn || !config.authToken) {
851
+ console.log("");
852
+ console.log(chalk4.red(" \u274C Not logged in. Deep dives require Tier 3."));
853
+ console.log("");
854
+ return;
855
+ }
856
+ if (!config.conversationId) {
857
+ console.log("");
858
+ console.log(chalk4.red(" \u274C No active conversation."));
859
+ console.log("");
860
+ return;
861
+ }
862
+ const spinner = ora({ text: chalk4.cyan(" Loading deep dives..."), spinner: "dots" }).start();
863
+ try {
864
+ const result = await callCloudFunction("listCLIDeepDives", { conversationId: config.conversationId });
865
+ spinner.stop();
866
+ const dives = result.deepDives || [];
867
+ if (dives.length === 0) {
868
+ console.log("");
869
+ console.log(chalk4.yellow(" \u26A0\uFE0F No deep dives in this conversation."));
870
+ console.log(chalk4.gray(" Use `bob deepdive` to create one."));
871
+ console.log("");
872
+ return;
873
+ }
874
+ console.log("");
875
+ console.log(DIVE_BORDER(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
876
+ console.log(DIVE_BORDER(" \u2551") + chalk4.bold.blue(" \u{1F93F} Select a deep dive to re-enter ") + DIVE_BORDER("\u2551"));
877
+ console.log(DIVE_BORDER(" \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"));
878
+ for (let i = 0; i < dives.length; i++) {
879
+ const dive = dives[i];
880
+ const preview = (dive.initiatingPrompt || "No prompt").slice(0, 35);
881
+ const msgs = dive.messageCount || 0;
882
+ console.log(DIVE_BORDER(" \u2551") + ` ${chalk4.cyan(String(i + 1).padStart(2))}. ${chalk4.white(preview)}${preview.length >= 35 ? "..." : ""}`);
883
+ console.log(DIVE_BORDER(" \u2551") + chalk4.gray(` ${msgs} messages | ${dive.status || "active"}`));
884
+ }
885
+ console.log(DIVE_BORDER(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
886
+ console.log("");
887
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
888
+ const answer = await new Promise((resolve2) => {
889
+ rl.question(chalk4.blue(" Select (1-" + dives.length + ") or 0 to cancel: "), resolve2);
890
+ });
891
+ const selection = parseInt(answer.trim());
892
+ if (isNaN(selection) || selection === 0 || selection < 1 || selection > dives.length) {
893
+ rl.close();
894
+ console.log(chalk4.gray(" Cancelled."));
895
+ return;
896
+ }
897
+ const selectedDive = dives[selection - 1];
898
+ const parentMessageId = selectedDive.parentMessageId;
899
+ const initiatingPrompt = selectedDive.initiatingPrompt || "Deep dive session";
900
+ const animation = startDeepDiveAnimation();
901
+ await new Promise((resolve2) => setTimeout(resolve2, 3e3));
902
+ animation.stop();
903
+ await new Promise((resolve2) => setTimeout(resolve2, 300));
904
+ await runDeepDiveSession(config, config.conversationId, parentMessageId, initiatingPrompt, rl);
905
+ rl.close();
620
906
  } catch (error) {
621
907
  spinner.stop();
622
908
  console.log(chalk4.red(` \u274C ${error.message}`));
623
909
  console.log("");
624
910
  }
625
911
  });
912
+ program2.command("deepdive").description("Create a new deep dive on a Bob message").action(async () => {
913
+ const config = getConfig();
914
+ if (!config.loggedIn || !config.authToken) {
915
+ console.log("");
916
+ console.log(chalk4.red(" \u274C Not logged in. Deep dives require Tier 3."));
917
+ console.log(chalk4.gray(" Run `bob login` to authenticate."));
918
+ console.log("");
919
+ return;
920
+ }
921
+ if (!config.conversationId) {
922
+ console.log("");
923
+ console.log(chalk4.red(" \u274C No active conversation."));
924
+ console.log(chalk4.gray(" Join one with `bob conversations join` first."));
925
+ console.log("");
926
+ return;
927
+ }
928
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
929
+ await enterDeepDive(config, config.conversationId, rl);
930
+ rl.close();
931
+ });
626
932
  }
627
933
  async function enterDeepDive(config, conversationId, rl) {
628
934
  if (!config.loggedIn || !config.authToken) {
@@ -632,10 +938,7 @@ async function enterDeepDive(config, conversationId, rl) {
632
938
  const spinner = ora({ text: chalk4.cyan(" Loading messages..."), spinner: "dots" }).start();
633
939
  let messages;
634
940
  try {
635
- const result = await callCloudFunction("listCLIDeepDives", {
636
- conversationId,
637
- action: "listMessages"
638
- });
941
+ const result = await callCloudFunction("listCLIDeepDives", { conversationId, action: "listMessages" });
639
942
  messages = result.messages || [];
640
943
  spinner.stop();
641
944
  } catch (error) {
@@ -670,13 +973,9 @@ async function enterDeepDive(config, conversationId, rl) {
670
973
  const parentMessageId = selectedMessage.id;
671
974
  const initiatingPrompt = selectedMessage.message.slice(0, 100);
672
975
  const animation = startDeepDiveAnimation();
673
- const divePromise = callCloudFunction("initiateCLIDeepDive", {
674
- conversationId,
675
- parentMessageId,
676
- initiatingPrompt
677
- });
976
+ const divePromise = callCloudFunction("initiateCLIDeepDive", { conversationId, parentMessageId, initiatingPrompt });
678
977
  try {
679
- await divePromise;
978
+ await Promise.all([divePromise, new Promise((resolve2) => setTimeout(resolve2, 3e3))]);
680
979
  animation.stop();
681
980
  await new Promise((resolve2) => setTimeout(resolve2, 300));
682
981
  await runDeepDiveSession(config, conversationId, parentMessageId, initiatingPrompt, rl);
@@ -688,14 +987,19 @@ async function enterDeepDive(config, conversationId, rl) {
688
987
  }
689
988
  async function runDeepDiveSession(config, conversationId, parentMessageId, initiatingPrompt, rl) {
690
989
  const previewText = initiatingPrompt.slice(0, 50) + (initiatingPrompt.length > 50 ? "..." : "");
990
+ const isLocalProvider = config.provider === "local" && config.localEndpoint;
691
991
  console.log("");
692
992
  console.log(DIVE_BORDER(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
693
993
  console.log(DIVE_BORDER(" \u2551") + chalk4.bold.blue(" \u{1F93F} DEEP DIVE ") + DIVE_BORDER("\u2551"));
694
994
  console.log(DIVE_BORDER(" \u2551") + chalk4.gray(` On: "${previewText}"`));
995
+ if (isLocalProvider) {
996
+ console.log(DIVE_BORDER(" \u2551") + chalk4.gray(" Provider: Local model (sovereign handoff)"));
997
+ }
695
998
  console.log(DIVE_BORDER(" \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"));
696
- console.log(DIVE_BORDER(" \u2551") + chalk4.gray(" Commands: /surface /promote /clear ") + DIVE_BORDER("\u2551"));
999
+ console.log(DIVE_BORDER(" \u2551") + chalk4.gray(" Commands: /surface /promote /clear /personalized ") + DIVE_BORDER("\u2551"));
697
1000
  console.log(DIVE_BORDER(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
698
1001
  console.log("");
1002
+ let lastBobResponse = "";
699
1003
  return new Promise((resolve2) => {
700
1004
  const deepDivePrompt = () => {
701
1005
  rl.question(chalk4.blue(" \u{1F93F} You: "), async (input) => {
@@ -717,10 +1021,7 @@ async function runDeepDiveSession(config, conversationId, parentMessageId, initi
717
1021
  if (trimmed === "/promote") {
718
1022
  const promoSpinner = ora({ text: chalk4.blue(" Promoting deep dive..."), spinner: "dots" }).start();
719
1023
  try {
720
- await callCloudFunction("promoteDeepDive", {
721
- conversationId,
722
- parentMessageId
723
- });
1024
+ await callCloudFunction("promoteDeepDive", { conversationId, parentMessageId });
724
1025
  promoSpinner.stop();
725
1026
  console.log("");
726
1027
  console.log(chalk4.green(" \u2705 Deep dive promoted! Summary merged into main conversation."));
@@ -742,23 +1043,69 @@ async function runDeepDiveSession(config, conversationId, parentMessageId, initi
742
1043
  deepDivePrompt();
743
1044
  return;
744
1045
  }
1046
+ if (trimmed === "/personalized" || trimmed === "/personalize") {
1047
+ console.log("");
1048
+ console.log(BORDER(" \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510"));
1049
+ console.log(BORDER(" \u2502") + AMBER(" \u26A0\uFE0F Personalization Mode"));
1050
+ console.log(BORDER(" \u2502"));
1051
+ console.log(BORDER(" \u2502") + GRAY(" This mode is not available inside deep dives."));
1052
+ console.log(BORDER(" \u2502") + GRAY(" Deep dives are powered by the Frank Reasoning Engine"));
1053
+ console.log(BORDER(" \u2502") + GRAY(" which already adapts using your DNA profile."));
1054
+ console.log(BORDER(" \u2502"));
1055
+ console.log(BORDER(" \u2502") + GRAY(" To use full Personalization Mode, return to the"));
1056
+ console.log(BORDER(" \u2502") + GRAY(" main conversation."));
1057
+ console.log(BORDER(" \u2502"));
1058
+ console.log(BORDER(" \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518"));
1059
+ console.log("");
1060
+ const answer = await new Promise((resolve3) => {
1061
+ rl.question(chalk4.blue(" Leave deep dive for main conversation? (y/N): "), resolve3);
1062
+ });
1063
+ if (answer.trim().toLowerCase() === "y") {
1064
+ console.log("");
1065
+ console.log(chalk4.green(" \u2705 Surfacing from deep dive. Personalization Mode will activate in main conversation."));
1066
+ console.log("");
1067
+ resolve2();
1068
+ return;
1069
+ }
1070
+ console.log(chalk4.gray(" Staying in deep dive."));
1071
+ console.log("");
1072
+ deepDivePrompt();
1073
+ return;
1074
+ }
745
1075
  const msgSpinner = ora({ text: chalk4.blue(" \u{1F93F} Bob is diving deep..."), spinner: "dots" }).start();
746
1076
  try {
747
- await callCloudFunction("generateDeepDiveResponse", {
748
- conversationId,
749
- parentMessageId,
750
- userMessage: trimmed,
751
- isLocalModel: false,
752
- activePersonaId: null
753
- });
1077
+ let localContext = buildLocalContext(process.cwd());
1078
+ if (config.localEndpoint) {
1079
+ try {
1080
+ const retrievalQuery = lastBobResponse ? `Previous context: ${lastBobResponse.slice(0, 500)}
1081
+
1082
+ Current request: ${trimmed}` : trimmed;
1083
+ const retrieval = await getRelevantFileContents(retrievalQuery, config.localEndpoint);
1084
+ if (retrieval.fileContents) {
1085
+ localContext += "\n\n" + retrieval.fileContents;
1086
+ }
1087
+ } catch {
1088
+ }
1089
+ }
1090
+ await callCloudFunction("saveCLIDeepDiveMessage", { conversationId, parentMessageId, message: trimmed, sender: "user" });
1091
+ let responseText;
1092
+ if (isLocalProvider) {
1093
+ const handoffResult = await callCloudFunction("generateDeepDiveResponse", { conversationId, parentMessageId, userMessage: trimmed, isLocalModel: true, activePersonaId: null, localContext });
1094
+ if (!handoffResult?.isHandoff || !handoffResult?.masterPrompt) {
1095
+ throw new Error("Handoff failed \u2014 no master prompt returned.");
1096
+ }
1097
+ const localMessages = [{ role: "user", content: handoffResult.masterPrompt }];
1098
+ responseText = await callLocalModel(config.localEndpoint, localMessages);
1099
+ await callCloudFunction("saveCLIDeepDiveMessage", { conversationId, parentMessageId, message: responseText, sender: "bob", origin: "local-sovereign" });
1100
+ } else {
1101
+ await callCloudFunction("generateDeepDiveResponse", { conversationId, parentMessageId, userMessage: trimmed, isLocalModel: false, activePersonaId: null, localContext });
1102
+ const latestResult = await callCloudFunction("listCLIDeepDives", { conversationId, action: "getLatestSandboxMessage", parentMessageId });
1103
+ responseText = latestResult?.message || "Deep dive response saved.";
1104
+ }
754
1105
  msgSpinner.stop();
755
- const latestResult = await callCloudFunction("listCLIDeepDives", {
756
- conversationId,
757
- action: "getLatestSandboxMessage",
758
- parentMessageId
759
- });
760
- const responseText = latestResult?.message || "Deep dive response saved.";
761
- const rendered = renderMarkdown(responseText);
1106
+ lastBobResponse = responseText;
1107
+ const displayResponse = stripCodeBlockFromResponse(responseText);
1108
+ const rendered = renderMarkdown(displayResponse);
762
1109
  console.log("");
763
1110
  console.log(DIVE_BORDER(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
764
1111
  console.log(DIVE_BORDER(" \u2551") + chalk4.bold.blue(" \u{1F93F} Bob (Deep Dive): ") + DIVE_BORDER("\u2551"));
@@ -768,6 +1115,7 @@ async function runDeepDiveSession(config, conversationId, parentMessageId, initi
768
1115
  }
769
1116
  console.log(DIVE_BORDER(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
770
1117
  console.log("");
1118
+ await processAllProposedFiles(responseText, false, rl);
771
1119
  } catch (error) {
772
1120
  msgSpinner.stop();
773
1121
  console.log(chalk4.red(` \u274C ${error.message}`));
@@ -782,15 +1130,15 @@ async function runDeepDiveSession(config, conversationId, parentMessageId, initi
782
1130
 
783
1131
  // src/ui/session-header.ts
784
1132
  import chalk5 from "chalk";
785
- import * as path4 from "path";
786
- var AMBER = chalk5.hex("#FFAB00");
1133
+ import * as path5 from "path";
1134
+ var AMBER2 = chalk5.hex("#FFAB00");
787
1135
  var ORANGE = chalk5.hex("#E66F24");
788
1136
  var GREEN = chalk5.hex("#2E7D32");
789
1137
  var BLUE = chalk5.hex("#42A5F5");
790
1138
  var DARK_BG = chalk5.bgHex("#222C22");
791
1139
  function renderSessionHeader(mode) {
792
1140
  const config = getConfig();
793
- const projectName = path4.basename(process.cwd());
1141
+ const projectName = path5.basename(process.cwd());
794
1142
  const summaries = loadSummaries(process.cwd());
795
1143
  const fileCount = summaries ? Object.keys(summaries).length : 0;
796
1144
  const isIndexed = fileCount > 0;
@@ -798,7 +1146,7 @@ function renderSessionHeader(mode) {
798
1146
  const modeColor = mode === "chat" ? chalk5.cyan : chalk5.magenta;
799
1147
  console.log("");
800
1148
  console.log(DARK_BG(chalk5.gray(" \u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E")));
801
- console.log(DARK_BG(chalk5.gray(" \u2502 ") + ORANGE("\u25C9") + AMBER(" BOB CLI") + chalk5.gray(" v0.1.0") + chalk5.gray(" \u2502")));
1149
+ console.log(DARK_BG(chalk5.gray(" \u2502 ") + ORANGE("\u25C9") + AMBER2(" BOB CLI") + chalk5.gray(" v0.1.0") + chalk5.gray(" \u2502")));
802
1150
  console.log(DARK_BG(chalk5.gray(" \u2502 ") + modeColor(modeLabel) + chalk5.gray(" \u2502")));
803
1151
  console.log(DARK_BG(chalk5.gray(" \u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F")));
804
1152
  console.log("");
@@ -822,12 +1170,12 @@ function renderSessionHeader(mode) {
822
1170
 
823
1171
  // src/ui/welcome.ts
824
1172
  import chalk6 from "chalk";
825
- var AMBER2 = chalk6.hex("#FFAB00");
1173
+ var AMBER3 = chalk6.hex("#FFAB00");
826
1174
  var ORANGE2 = chalk6.hex("#E66F24");
827
1175
  var GREEN2 = chalk6.hex("#2E7D32");
828
1176
  var SKY2 = chalk6.hex("#87CEEB");
829
1177
  var WHITE = chalk6.white;
830
- var BORDER = chalk6.hex("#2E7D32");
1178
+ var BORDER2 = chalk6.hex("#2E7D32");
831
1179
  var TYPEWRITER_DELAY = 80;
832
1180
  async function showWelcomeIfFirstRun() {
833
1181
  const config = getConfig();
@@ -838,45 +1186,45 @@ async function showWelcomeIfFirstRun() {
838
1186
  async function playWelcomeAnimation() {
839
1187
  console.clear();
840
1188
  console.log("");
841
- console.log(BORDER(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
842
- console.log(BORDER(" \u2551") + SKY2(" \u2601 \u2601 \u2601 \u2601 \u2601 \u2601 \u2601") + BORDER("\u2551"));
843
- console.log(BORDER(" \u2551") + SKY2(" \u2601 \u2601 ") + chalk6.yellow("\u2600\uFE0F") + SKY2(" \u2601 \u2601 \u2601") + BORDER("\u2551"));
844
- console.log(BORDER(" \u2551") + SKY2(" \u2601 \u2601 \u2601 \u2601 \u2601 \u2601 \u2601 \u2601") + BORDER("\u2551"));
845
- console.log(BORDER(" \u2551") + SKY2(" \u2601 \u2601 \u2601 \u2601 \u2601 \u2601 \u2601 ") + BORDER(" \u2551"));
846
- console.log(BORDER(" \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"));
847
- console.log(BORDER(" \u2551") + ORANGE2(" \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 ") + AMBER2("\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557") + " " + BORDER("\u2551"));
848
- console.log(BORDER(" \u2551") + ORANGE2(" \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557") + AMBER2("\u255A\u2550\u255D \u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D") + " " + BORDER("\u2551"));
849
- console.log(BORDER(" \u2551") + ORANGE2(" \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D") + AMBER2(" \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557") + " " + BORDER("\u2551"));
850
- console.log(BORDER(" \u2551") + ORANGE2(" \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557") + AMBER2(" \u255A\u2550\u2550\u2550\u2550\u2588\u2588\u2551") + " " + BORDER("\u2551"));
851
- console.log(BORDER(" \u2551") + ORANGE2(" \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D") + AMBER2(" \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551") + " " + BORDER("\u2551"));
852
- console.log(BORDER(" \u2551") + ORANGE2(" \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D ") + AMBER2(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D") + " " + BORDER("\u2551"));
853
- console.log(BORDER(" \u2551") + " " + BORDER("\u2551"));
854
- console.log(BORDER(" \u2551") + WHITE(" C L I") + chalk6.gray(" v0.1.0") + " " + BORDER("\u2551"));
855
- console.log(BORDER(" \u2551") + " " + BORDER("\u2551"));
856
- console.log(BORDER(" \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"));
857
- console.log(BORDER(" \u2551") + " " + BORDER("\u2551"));
858
- process.stdout.write(BORDER(" \u2551"));
1189
+ console.log(BORDER2(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
1190
+ console.log(BORDER2(" \u2551") + SKY2(" \u2601 \u2601 \u2601 \u2601 \u2601 \u2601 \u2601") + BORDER2("\u2551"));
1191
+ console.log(BORDER2(" \u2551") + SKY2(" \u2601 \u2601 ") + chalk6.yellow("\u2600\uFE0F") + SKY2(" \u2601 \u2601 \u2601") + BORDER2("\u2551"));
1192
+ console.log(BORDER2(" \u2551") + SKY2(" \u2601 \u2601 \u2601 \u2601 \u2601 \u2601 \u2601 \u2601") + BORDER2("\u2551"));
1193
+ console.log(BORDER2(" \u2551") + SKY2(" \u2601 \u2601 \u2601 \u2601 \u2601 \u2601 \u2601 ") + BORDER2(" \u2551"));
1194
+ console.log(BORDER2(" \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"));
1195
+ console.log(BORDER2(" \u2551") + ORANGE2(" \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 ") + AMBER3("\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557") + " " + BORDER2("\u2551"));
1196
+ console.log(BORDER2(" \u2551") + ORANGE2(" \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557") + AMBER3("\u255A\u2550\u255D \u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D") + " " + BORDER2("\u2551"));
1197
+ console.log(BORDER2(" \u2551") + ORANGE2(" \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D") + AMBER3(" \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557") + " " + BORDER2("\u2551"));
1198
+ console.log(BORDER2(" \u2551") + ORANGE2(" \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557") + AMBER3(" \u255A\u2550\u2550\u2550\u2550\u2588\u2588\u2551") + " " + BORDER2("\u2551"));
1199
+ console.log(BORDER2(" \u2551") + ORANGE2(" \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D") + AMBER3(" \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551") + " " + BORDER2("\u2551"));
1200
+ console.log(BORDER2(" \u2551") + ORANGE2(" \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D ") + AMBER3(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D") + " " + BORDER2("\u2551"));
1201
+ console.log(BORDER2(" \u2551") + " " + BORDER2("\u2551"));
1202
+ console.log(BORDER2(" \u2551") + WHITE(" C L I") + chalk6.gray(" v0.1.0") + " " + BORDER2("\u2551"));
1203
+ console.log(BORDER2(" \u2551") + " " + BORDER2("\u2551"));
1204
+ console.log(BORDER2(" \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"));
1205
+ console.log(BORDER2(" \u2551") + " " + BORDER2("\u2551"));
1206
+ process.stdout.write(BORDER2(" \u2551"));
859
1207
  const tagline = " \u{1F528}\u{1FA9B}\u{1F4BB} We Can Build It!";
860
1208
  for (let i = 0; i <= tagline.length; i++) {
861
- process.stdout.write(`\r${BORDER(" \u2551")}${AMBER2(tagline.slice(0, i))}`);
1209
+ process.stdout.write(`\r${BORDER2(" \u2551")}${AMBER3(tagline.slice(0, i))}`);
862
1210
  await sleep2(TYPEWRITER_DELAY);
863
1211
  }
864
1212
  const pad = 56 - tagline.length;
865
- process.stdout.write(" ".repeat(pad > 0 ? pad : 0) + BORDER("\u2551") + "\n");
866
- console.log(BORDER(" \u2551") + " " + BORDER("\u2551"));
867
- console.log(BORDER(" \u2551") + chalk6.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500") + " " + BORDER("\u2551"));
868
- console.log(BORDER(" \u2551") + GREEN2(" \u{1F331} Bob's Workshop") + chalk6.gray(" | A Seedling Company") + " " + BORDER("\u2551"));
869
- console.log(BORDER(" \u2551") + chalk6.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500") + " " + BORDER("\u2551"));
870
- console.log(BORDER(" \u2551") + " " + BORDER("\u2551"));
871
- console.log(BORDER(" \u2551") + chalk6.gray(" Quick Start:") + " " + BORDER("\u2551"));
872
- console.log(BORDER(" \u2551") + chalk6.gray(" ") + AMBER2("bob chat") + chalk6.gray(" \u2014 Talk to Bob") + " " + BORDER("\u2551"));
873
- console.log(BORDER(" \u2551") + chalk6.gray(" ") + AMBER2("bob consult") + chalk6.gray(" \u2014 Strategic advice (no code)") + " " + BORDER("\u2551"));
874
- console.log(BORDER(" \u2551") + chalk6.gray(" ") + AMBER2("bob index") + chalk6.gray(" \u2014 Index your project") + " " + BORDER("\u2551"));
875
- console.log(BORDER(" \u2551") + chalk6.gray(" ") + AMBER2("bob login") + chalk6.gray(" \u2014 Connect to the platform") + " " + BORDER("\u2551"));
876
- console.log(BORDER(" \u2551") + chalk6.gray(" ") + AMBER2('bob push "msg"') + chalk6.gray(" \u2014 Git commit + push") + " " + BORDER("\u2551"));
877
- console.log(BORDER(" \u2551") + chalk6.gray(" ") + AMBER2("bob --help") + chalk6.gray(" \u2014 See all commands") + " " + BORDER("\u2551"));
878
- console.log(BORDER(" \u2551") + " " + BORDER("\u2551"));
879
- console.log(BORDER(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
1213
+ process.stdout.write(" ".repeat(pad > 0 ? pad : 0) + BORDER2("\u2551") + "\n");
1214
+ console.log(BORDER2(" \u2551") + " " + BORDER2("\u2551"));
1215
+ console.log(BORDER2(" \u2551") + chalk6.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500") + " " + BORDER2("\u2551"));
1216
+ console.log(BORDER2(" \u2551") + GREEN2(" \u{1F331} Bob's Workshop") + chalk6.gray(" | A Seedling Company") + " " + BORDER2("\u2551"));
1217
+ console.log(BORDER2(" \u2551") + chalk6.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500") + " " + BORDER2("\u2551"));
1218
+ console.log(BORDER2(" \u2551") + " " + BORDER2("\u2551"));
1219
+ console.log(BORDER2(" \u2551") + chalk6.gray(" Quick Start:") + " " + BORDER2("\u2551"));
1220
+ console.log(BORDER2(" \u2551") + chalk6.gray(" ") + AMBER3("bob chat") + chalk6.gray(" \u2014 Talk to Bob") + " " + BORDER2("\u2551"));
1221
+ console.log(BORDER2(" \u2551") + chalk6.gray(" ") + AMBER3("bob consult") + chalk6.gray(" \u2014 Strategic advice (no code)") + " " + BORDER2("\u2551"));
1222
+ console.log(BORDER2(" \u2551") + chalk6.gray(" ") + AMBER3("bob index") + chalk6.gray(" \u2014 Index your project") + " " + BORDER2("\u2551"));
1223
+ console.log(BORDER2(" \u2551") + chalk6.gray(" ") + AMBER3("bob login") + chalk6.gray(" \u2014 Connect to the platform") + " " + BORDER2("\u2551"));
1224
+ console.log(BORDER2(" \u2551") + chalk6.gray(" ") + AMBER3('bob push "msg"') + chalk6.gray(" \u2014 Git commit + push") + " " + BORDER2("\u2551"));
1225
+ console.log(BORDER2(" \u2551") + chalk6.gray(" ") + AMBER3("bob --help") + chalk6.gray(" \u2014 See all commands") + " " + BORDER2("\u2551"));
1226
+ console.log(BORDER2(" \u2551") + " " + BORDER2("\u2551"));
1227
+ console.log(BORDER2(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
880
1228
  console.log("");
881
1229
  await sleep2(800);
882
1230
  }
@@ -909,18 +1257,19 @@ ${fileContent}
909
1257
  console.log(chalk7.yellow(` \u26A0\uFE0F Could not read file: ${options.file}`));
910
1258
  }
911
1259
  }
912
- if (options.interactive || !message) {
913
- await runInteractiveSession(config, conversationId, localContext, options.personalized || false, "standard");
1260
+ if (options.interactive || !message || options.personalized) {
1261
+ if (options.personalized && message) {
1262
+ await runInteractiveSession(config, conversationId, localContext, true, "personalized", message);
1263
+ } else {
1264
+ await runInteractiveSession(config, conversationId, localContext, options.personalized || false, "standard");
1265
+ }
914
1266
  return;
915
1267
  }
916
1268
  await sendMessage(message, config, conversationId, localContext, options.personalized || false, "standard", []);
917
1269
  });
918
1270
  }
919
1271
  async function sendMessage(message, config, conversationId, localContext, personalized, mode, history) {
920
- const spinner = ora2({
921
- text: chalk7.cyan(" Bob is thinking..."),
922
- spinner: "dots"
923
- }).start();
1272
+ const spinner = ora2({ text: chalk7.cyan(" Bob is thinking..."), spinner: "dots" }).start();
924
1273
  let selectedFiles = [];
925
1274
  let hasProjectContext = null;
926
1275
  try {
@@ -928,7 +1277,11 @@ async function sendMessage(message, config, conversationId, localContext, person
928
1277
  let relevantFiles = "";
929
1278
  if (config.localEndpoint) {
930
1279
  spinner.text = chalk7.cyan(" Bob is finding relevant files...");
931
- const retrieval = await getRelevantFileContents(message, config.localEndpoint);
1280
+ const lastAssistantMsg = history.length > 0 ? history[history.length - 1]?.content?.slice(0, 500) || "" : "";
1281
+ const retrievalQuery = lastAssistantMsg ? `Previous context: ${lastAssistantMsg}
1282
+
1283
+ Current request: ${message}` : message;
1284
+ const retrieval = await getRelevantFileContents(retrievalQuery, config.localEndpoint);
932
1285
  relevantFiles = retrieval.fileContents;
933
1286
  selectedFiles = retrieval.selectedFiles;
934
1287
  }
@@ -943,11 +1296,11 @@ ${relevantFiles}`;
943
1296
  if (!config.localEndpoint) {
944
1297
  spinner.stop();
945
1298
  console.log(chalk7.red(" \u274C No local endpoint configured."));
946
- console.log(chalk7.gray(" Run `bob config set localEndpoint http://127.0.0.1:11434/api/chat`"));
947
1299
  return "";
948
1300
  }
1301
+ const systemPrompt = buildPersonalizedPrompt("standard");
949
1302
  const messages = [
950
- { role: "system", content: STANDARD_STYLE_PROMPT + (fullContext ? `
1303
+ { role: "system", content: systemPrompt + (fullContext ? `
951
1304
 
952
1305
  ## PROJECT CONTEXT ##
953
1306
  ${fullContext}` : "") },
@@ -955,33 +1308,25 @@ ${fullContext}` : "") },
955
1308
  { role: "user", content: message }
956
1309
  ];
957
1310
  response = await callLocalModel(config.localEndpoint, messages);
958
- saveMessage(conversationId, {
959
- sender: "user",
960
- message,
961
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
962
- type: "text"
963
- }, { tier: "local", provider: config.provider, mode });
964
- saveMessage(conversationId, {
965
- sender: "bob",
966
- message: response,
967
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
968
- type: "text"
969
- }, { tier: "local", provider: config.provider, mode });
1311
+ saveMessage(conversationId, { sender: "user", message, timestamp: (/* @__PURE__ */ new Date()).toISOString(), type: "text" }, { tier: "local", provider: config.provider, mode });
1312
+ saveMessage(conversationId, { sender: "bob", message: response, timestamp: (/* @__PURE__ */ new Date()).toISOString(), type: "text" }, { tier: "local", provider: config.provider, mode });
970
1313
  } else if (personalized || config.personalizationMode) {
971
1314
  if (!config.loggedIn || !config.authToken) {
972
1315
  spinner.stop();
973
- console.log(chalk7.red(" \u274C Personalization mode requires Tier 3 (platform login)."));
1316
+ console.log(chalk7.red(" \u274C Personalization mode requires Tier 3."));
974
1317
  return "";
975
1318
  }
1319
+ await callCloudFunction("saveCLIConversationMessage", { conversationId, message, sender: "user" });
976
1320
  const result = await callCloudFunction("getPersonalizedResponse", {
977
- userEmail: config.email,
978
- userId: config.uid,
1321
+ email: config.email,
1322
+ uid: config.uid,
979
1323
  conversationId,
980
1324
  userMessage: message,
981
- useContext: true,
982
- localContext: fullContext || null
1325
+ additionalContext: { localContext: fullContext || null },
1326
+ isLocalModel: false,
1327
+ activePersonaId: null
983
1328
  });
984
- response = result?.text || result?.response || result?.message || "No response received.";
1329
+ response = result?.response || result?.data?.response || result?.text || result?.message || "No response received.";
985
1330
  hasProjectContext = result?.hasProjectContext ?? null;
986
1331
  } else {
987
1332
  if (!config.loggedIn || !config.authToken) {
@@ -990,27 +1335,12 @@ ${fullContext}` : "") },
990
1335
  console.log(chalk7.gray(" Run `bob login` to authenticate, or set provider to local."));
991
1336
  return "";
992
1337
  }
993
- const result = await callCloudFunction("chatWithBobStream", {
994
- userEmail: config.email,
995
- userId: config.uid,
996
- conversationId,
997
- userMessage: message,
998
- useContext: true,
999
- consultantModelId: "gemini-2.5-flash",
1000
- useOrgContext: false,
1001
- isPassalongActive: false,
1002
- linkedConvoId: null,
1003
- localContext: fullContext || null
1004
- });
1338
+ const result = await callCloudFunction("chatWithBobStream", { userEmail: config.email, userId: config.uid, conversationId, userMessage: message, useContext: true, consultantModelId: "gemini-2.5-flash", useOrgContext: false, isPassalongActive: false, linkedConvoId: null, localContext: fullContext || null });
1005
1339
  response = result?.text || result?.response || result?.message || "No response received.";
1006
1340
  hasProjectContext = result?.hasProjectContext ?? null;
1007
1341
  }
1008
1342
  spinner.stop();
1009
- const proposed = extractProposedFile(response);
1010
- let displayResponse = response;
1011
- if (proposed) {
1012
- displayResponse = stripCodeBlockFromResponse(response);
1013
- }
1343
+ const displayResponse = stripCodeBlockFromResponse(response);
1014
1344
  const rendered = renderMarkdown(displayResponse);
1015
1345
  console.log("");
1016
1346
  console.log(chalk7.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
@@ -1032,9 +1362,7 @@ ${fullContext}` : "") },
1032
1362
  }
1033
1363
  console.log(chalk7.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
1034
1364
  console.log("");
1035
- if (proposed) {
1036
- await proposeAndWriteFile(proposed);
1037
- }
1365
+ await processAllProposedFiles(response);
1038
1366
  return response;
1039
1367
  } catch (error) {
1040
1368
  spinner.stop();
@@ -1042,17 +1370,21 @@ ${fullContext}` : "") },
1042
1370
  return "";
1043
1371
  }
1044
1372
  }
1045
- async function runInteractiveSession(config, conversationId, localContext, personalized, mode) {
1373
+ async function runInteractiveSession(config, conversationId, localContext, personalized, mode, initialMessage) {
1046
1374
  if (!config.hasSeenWelcome) {
1047
1375
  await showWelcomeIfFirstRun();
1048
1376
  setConfigValue("hasSeenWelcome", true);
1049
1377
  }
1050
1378
  renderSessionHeader("chat");
1051
- const rl = readline.createInterface({
1052
- input: process.stdin,
1053
- output: process.stdout
1054
- });
1379
+ const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
1055
1380
  const history = [];
1381
+ if (initialMessage) {
1382
+ const response = await sendMessage(initialMessage, config, conversationId, localContext, personalized, mode, history);
1383
+ if (response) {
1384
+ history.push({ role: "user", content: initialMessage });
1385
+ history.push({ role: "assistant", content: response });
1386
+ }
1387
+ }
1056
1388
  const prompt = () => {
1057
1389
  rl.question(chalk7.green(" You: "), async (input) => {
1058
1390
  const trimmed = input.trim();
@@ -1095,8 +1427,7 @@ async function runInteractiveSession(config, conversationId, localContext, perso
1095
1427
  --- INCLUDED FILE: ${filePath} ---
1096
1428
  ${content}
1097
1429
  --- END FILE ---`;
1098
- const lineCount = content.split("\n").length;
1099
- console.log(chalk7.green(` \u{1F4C4} Loaded: ${filePath} (${lineCount} lines)`));
1430
+ console.log(chalk7.green(` \u{1F4C4} Loaded: ${filePath} (${content.split("\n").length} lines)`));
1100
1431
  } else {
1101
1432
  console.log(chalk7.red(` \u274C Could not read: ${filePath}`));
1102
1433
  }
@@ -1106,28 +1437,26 @@ ${content}
1106
1437
  }
1107
1438
  if (trimmed.startsWith("/delete ")) {
1108
1439
  const filePath = trimmed.slice(8).trim();
1109
- const absolutePath = path5.resolve(process.cwd(), filePath);
1110
- if (!fs4.existsSync(absolutePath)) {
1440
+ const absolutePath = path6.resolve(process.cwd(), filePath);
1441
+ if (!fs5.existsSync(absolutePath)) {
1111
1442
  console.log(chalk7.red(` \u274C File not found: ${filePath}`));
1112
1443
  console.log("");
1113
1444
  prompt();
1114
1445
  return;
1115
1446
  }
1116
- const rl2 = readline.createInterface({ input: process.stdin, output: process.stdout });
1447
+ const rl2 = readline2.createInterface({ input: process.stdin, output: process.stdout });
1117
1448
  const confirm = await new Promise((resolve2) => {
1118
- rl2.question(chalk7.red(` \u{1F5D1}\uFE0F Delete ${filePath}? This cannot be undone. (y/n): `), resolve2);
1449
+ rl2.question(chalk7.red(` \u{1F5D1}\uFE0F Delete ${filePath}? (y/n): `), resolve2);
1119
1450
  });
1120
1451
  rl2.close();
1121
1452
  if (confirm.toLowerCase() === "y" || confirm.toLowerCase() === "yes") {
1122
1453
  try {
1123
- const backupDir = path5.join(process.cwd(), ".bob-backups");
1124
- if (!fs4.existsSync(backupDir)) fs4.mkdirSync(backupDir, { recursive: true });
1125
- const timestamp = Date.now();
1126
- const backupName = filePath.replace(/[\/\\]/g, "_") + `.${timestamp}.deleted`;
1127
- fs4.copyFileSync(absolutePath, path5.join(backupDir, backupName));
1128
- fs4.unlinkSync(absolutePath);
1454
+ const backupDir = path6.join(process.cwd(), ".bob-backups");
1455
+ if (!fs5.existsSync(backupDir)) fs5.mkdirSync(backupDir, { recursive: true });
1456
+ fs5.copyFileSync(absolutePath, path6.join(backupDir, filePath.replace(/[\/\\]/g, "_") + `.${Date.now()}.deleted`));
1457
+ fs5.unlinkSync(absolutePath);
1129
1458
  console.log(chalk7.green(` \u2705 Deleted: ${filePath}`));
1130
- console.log(chalk7.gray(` \u{1F4E6} Backup saved to .bob-backups/ (recoverable)`));
1459
+ console.log(chalk7.gray(` \u{1F4E6} Backup saved to .bob-backups/`));
1131
1460
  } catch (e) {
1132
1461
  console.log(chalk7.red(` \u274C Delete failed: ${e.message}`));
1133
1462
  }
@@ -1157,7 +1486,7 @@ ${content}
1157
1486
  // src/commands/consult.ts
1158
1487
  import chalk8 from "chalk";
1159
1488
  import ora3 from "ora";
1160
- import * as readline2 from "readline";
1489
+ import * as readline3 from "readline";
1161
1490
  function registerConsultCommand(program2) {
1162
1491
  program2.command("consult [message]").description("Consult with Bob \u2014 strategic advice only, no code").option("-f, --file <path>", "Include a specific file as context").option("--no-context", "Skip local directory context").option("--new", "Start a fresh conversation").option("-i, --interactive", "Enter interactive consultant session").action(async (message, options) => {
1163
1492
  const config = getConfig();
@@ -1190,10 +1519,7 @@ ${fileContent}
1190
1519
  });
1191
1520
  }
1192
1521
  async function sendConsultMessage(message, config, conversationId, localContext, history) {
1193
- const spinner = ora3({
1194
- text: chalk8.cyan(" Bob is thinking (consultant mode)..."),
1195
- spinner: "dots"
1196
- }).start();
1522
+ const spinner = ora3({ text: chalk8.cyan(" Bob is thinking (consultant mode)..."), spinner: "dots" }).start();
1197
1523
  let selectedFiles = [];
1198
1524
  let hasProjectContext = null;
1199
1525
  try {
@@ -1202,7 +1528,6 @@ async function sendConsultMessage(message, config, conversationId, localContext,
1202
1528
  if (!config.localEndpoint) {
1203
1529
  spinner.stop();
1204
1530
  console.log(chalk8.red(" \u274C No local endpoint configured."));
1205
- console.log(chalk8.gray(" Run `bob config set localEndpoint http://127.0.0.1:11434/api/chat`"));
1206
1531
  return "";
1207
1532
  }
1208
1533
  spinner.text = chalk8.cyan(" Bob is finding relevant files...");
@@ -1216,8 +1541,9 @@ async function sendConsultMessage(message, config, conversationId, localContext,
1216
1541
 
1217
1542
  ${relevantFiles}`;
1218
1543
  }
1544
+ const systemPrompt = buildPersonalizedPrompt("consultant");
1219
1545
  const messages = [
1220
- { role: "system", content: CONSULTANT_STYLE_PROMPT + (fullContext ? `
1546
+ { role: "system", content: systemPrompt + (fullContext ? `
1221
1547
 
1222
1548
  ## PROJECT CONTEXT ##
1223
1549
  ${fullContext}` : "") },
@@ -1225,37 +1551,15 @@ ${fullContext}` : "") },
1225
1551
  { role: "user", content: message }
1226
1552
  ];
1227
1553
  response = await callLocalModel(config.localEndpoint, messages);
1228
- saveMessage(conversationId, {
1229
- sender: "user",
1230
- message,
1231
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1232
- type: "text"
1233
- }, { tier: "local", provider: config.provider, mode: "consultant" });
1234
- saveMessage(conversationId, {
1235
- sender: "bob",
1236
- message: response,
1237
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1238
- type: "text"
1239
- }, { tier: "local", provider: config.provider, mode: "consultant" });
1554
+ saveMessage(conversationId, { sender: "user", message, timestamp: (/* @__PURE__ */ new Date()).toISOString(), type: "text" }, { tier: "local", provider: config.provider, mode: "consultant" });
1555
+ saveMessage(conversationId, { sender: "bob", message: response, timestamp: (/* @__PURE__ */ new Date()).toISOString(), type: "text" }, { tier: "local", provider: config.provider, mode: "consultant" });
1240
1556
  } else {
1241
1557
  if (!config.loggedIn || !config.authToken) {
1242
1558
  spinner.stop();
1243
1559
  console.log(chalk8.red(" \u274C Not logged in."));
1244
- console.log(chalk8.gray(" Run `bob login` to authenticate, or set provider to local."));
1245
1560
  return "";
1246
1561
  }
1247
- const result = await callCloudFunction("consultWithBobStream", {
1248
- userEmail: config.email,
1249
- userId: config.uid,
1250
- conversationId,
1251
- userMessage: message,
1252
- useContext: true,
1253
- consultantModelId: "gemini-2.5-flash",
1254
- useOrgContext: false,
1255
- isPassalongActive: false,
1256
- linkedConvoId: null,
1257
- localContext: localContext || null
1258
- });
1562
+ const result = await callCloudFunction("consultWithBobStream", { userEmail: config.email, userId: config.uid, conversationId, userMessage: message, useContext: true, consultantModelId: "gemini-2.5-flash", useOrgContext: false, isPassalongActive: false, linkedConvoId: null, localContext: localContext || null });
1259
1563
  response = result?.text || result?.response || result?.message || "No response received.";
1260
1564
  hasProjectContext = result?.hasProjectContext ?? null;
1261
1565
  }
@@ -1275,8 +1579,7 @@ ${fullContext}` : "") },
1275
1579
  if (config.tier === "platform" && config.provider !== "local") {
1276
1580
  console.log(chalk8.gray(` \u{1F517} https://bobs-workshop.web.app/#/bobcodeassistant/${conversationId}`));
1277
1581
  if (hasProjectContext === false) {
1278
- console.log(chalk8.red(" \u26A0\uFE0F No project workspace connected. Upload a project via the web app"));
1279
- console.log(chalk8.red(" for full RAG + workspace capabilities."));
1582
+ console.log(chalk8.red(" \u26A0\uFE0F No project workspace connected."));
1280
1583
  }
1281
1584
  }
1282
1585
  console.log(chalk8.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
@@ -1289,15 +1592,12 @@ ${fullContext}` : "") },
1289
1592
  }
1290
1593
  }
1291
1594
  async function runInteractiveSession2(config, conversationId, localContext) {
1292
- if (config.hasSeenWelcome === void 0 || !config.hasSeenWelcome) {
1595
+ if (!config.hasSeenWelcome) {
1293
1596
  await showWelcomeIfFirstRun();
1294
1597
  setConfigValue("hasSeenWelcome", true);
1295
1598
  }
1296
1599
  renderSessionHeader("consult");
1297
- const rl = readline2.createInterface({
1298
- input: process.stdin,
1299
- output: process.stdout
1300
- });
1600
+ const rl = readline3.createInterface({ input: process.stdin, output: process.stdout });
1301
1601
  const history = [];
1302
1602
  const prompt = () => {
1303
1603
  rl.question(chalk8.green(" You: "), async (input) => {
@@ -1341,8 +1641,7 @@ async function runInteractiveSession2(config, conversationId, localContext) {
1341
1641
  --- INCLUDED FILE: ${filePath} ---
1342
1642
  ${content}
1343
1643
  --- END FILE ---`;
1344
- const lineCount = content.split("\n").length;
1345
- console.log(chalk8.green(` \u{1F4C4} Loaded: ${filePath} (${lineCount} lines)`));
1644
+ console.log(chalk8.green(` \u{1F4C4} Loaded: ${filePath} (${content.split("\n").length} lines)`));
1346
1645
  } else {
1347
1646
  console.log(chalk8.red(` \u274C Could not read: ${filePath}`));
1348
1647
  }
@@ -1363,8 +1662,8 @@ ${content}
1363
1662
 
1364
1663
  // src/commands/index.ts
1365
1664
  import chalk9 from "chalk";
1366
- import * as fs5 from "fs";
1367
- import * as path6 from "path";
1665
+ import * as fs6 from "fs";
1666
+ import * as path7 from "path";
1368
1667
  var IGNORE_DIRS = ["node_modules", ".git", "dist", "build", ".dart_tool", ".idea", ".gradle", ".pub-cache", ".bob"];
1369
1668
  var CODE_EXTENSIONS = /* @__PURE__ */ new Set([".dart", ".js", ".ts", ".html", ".css", ".json", ".yaml", ".yml", ".xml", ".sh", ".md"]);
1370
1669
  function registerIndexCommand(program2) {
@@ -1400,10 +1699,10 @@ function registerIndexCommand(program2) {
1400
1699
  const summaries = {};
1401
1700
  let completed = 0;
1402
1701
  for (const filePath of files) {
1403
- const absolutePath = path6.join(cwd, filePath);
1702
+ const absolutePath = path7.join(cwd, filePath);
1404
1703
  let content;
1405
1704
  try {
1406
- content = fs5.readFileSync(absolutePath, "utf-8");
1705
+ content = fs6.readFileSync(absolutePath, "utf-8");
1407
1706
  } catch {
1408
1707
  console.log(chalk9.red(` \u274C Could not read: ${filePath}`));
1409
1708
  continue;
@@ -1479,11 +1778,11 @@ Respond with ONLY the JSON object:`
1479
1778
  saveDependencies(cwd, dependencies);
1480
1779
  for (const [filePath, deps] of Object.entries(dependencies)) {
1481
1780
  const taskId = filePath.replace(/[\/\\]/g, "_");
1482
- const taskPath = path6.join(tasksDir, `${taskId}.json`);
1483
- if (fs5.existsSync(taskPath)) {
1484
- const task = JSON.parse(fs5.readFileSync(taskPath, "utf-8"));
1781
+ const taskPath = path7.join(tasksDir, `${taskId}.json`);
1782
+ if (fs6.existsSync(taskPath)) {
1783
+ const task = JSON.parse(fs6.readFileSync(taskPath, "utf-8"));
1485
1784
  task.dependencies = deps;
1486
- fs5.writeFileSync(taskPath, JSON.stringify(task, null, 2));
1785
+ fs6.writeFileSync(taskPath, JSON.stringify(task, null, 2));
1487
1786
  }
1488
1787
  }
1489
1788
  updateManifestProgress(runDir, completed, "completed");
@@ -1506,16 +1805,16 @@ function scanProjectFiles(rootDir, currentDir, depth = 0) {
1506
1805
  const dir = currentDir || rootDir;
1507
1806
  const files = [];
1508
1807
  try {
1509
- const entries = fs5.readdirSync(dir, { withFileTypes: true });
1808
+ const entries = fs6.readdirSync(dir, { withFileTypes: true });
1510
1809
  for (const entry of entries) {
1511
1810
  if (IGNORE_DIRS.includes(entry.name)) continue;
1512
1811
  if (entry.name.startsWith(".")) continue;
1513
- const fullPath = path6.join(dir, entry.name);
1514
- const relativePath = path6.relative(rootDir, fullPath).replace(/\\/g, "/");
1812
+ const fullPath = path7.join(dir, entry.name);
1813
+ const relativePath = path7.relative(rootDir, fullPath).replace(/\\/g, "/");
1515
1814
  if (entry.isDirectory()) {
1516
1815
  files.push(...scanProjectFiles(rootDir, fullPath, depth + 1));
1517
1816
  } else {
1518
- const ext = path6.extname(entry.name).toLowerCase();
1817
+ const ext = path7.extname(entry.name).toLowerCase();
1519
1818
  if (CODE_EXTENSIONS.has(ext)) {
1520
1819
  files.push(relativePath);
1521
1820
  }
@@ -1643,7 +1942,7 @@ function registerPushCommand(program2) {
1643
1942
  // src/commands/byok.ts
1644
1943
  import chalk11 from "chalk";
1645
1944
  import ora5 from "ora";
1646
- import * as readline3 from "readline";
1945
+ import * as readline4 from "readline";
1647
1946
  var VALID_PROVIDERS2 = ["google", "bedrock", "claude", "openai", "grok"];
1648
1947
  function registerByokCommand(program2) {
1649
1948
  const byokCmd = program2.command("byok").description("Manage your Bring Your Own Key (BYOK) configuration");
@@ -1710,7 +2009,7 @@ function registerByokCommand(program2) {
1710
2009
  console.log("");
1711
2010
  return;
1712
2011
  }
1713
- const rl = readline3.createInterface({ input: process.stdin, output: process.stdout });
2012
+ const rl = readline4.createInterface({ input: process.stdin, output: process.stdout });
1714
2013
  const answer = await new Promise((resolve2) => {
1715
2014
  rl.question(chalk11.yellow(` Remove ${provider} key? (y/n): `), resolve2);
1716
2015
  });
@@ -1801,7 +2100,7 @@ function registerByokCommand(program2) {
1801
2100
  // src/commands/conversations.ts
1802
2101
  import chalk12 from "chalk";
1803
2102
  import ora6 from "ora";
1804
- import * as readline4 from "readline";
2103
+ import * as readline5 from "readline";
1805
2104
  function registerConversationsCommand(program2) {
1806
2105
  const convosCmd = program2.command("conversations").description("List, search, and join existing conversations").option("-p, --page <number>", "Page number", "1").option("-s, --search <query>", "Search conversations by title or content").action(async (options) => {
1807
2106
  const config = getConfig();
@@ -1905,7 +2204,7 @@ function registerConversationsCommand(program2) {
1905
2204
  console.log(chalk12.gray(` ${sourceIcon} ${timeAgo}`));
1906
2205
  });
1907
2206
  console.log("");
1908
- const rl = readline4.createInterface({ input: process.stdin, output: process.stdout });
2207
+ const rl = readline5.createInterface({ input: process.stdin, output: process.stdout });
1909
2208
  const answer = await new Promise((resolve2) => {
1910
2209
  rl.question(chalk12.cyan(" Select (1-" + conversations.length + ") or 0 to cancel: "), resolve2);
1911
2210
  });
@@ -2188,15 +2487,15 @@ function registerForkCommand(program2) {
2188
2487
  // src/commands/analyse.ts
2189
2488
  import chalk15 from "chalk";
2190
2489
  import ora7 from "ora";
2191
- import * as fs6 from "fs";
2192
- import * as path7 from "path";
2490
+ import * as fs7 from "fs";
2491
+ import * as path8 from "path";
2193
2492
  var RED = chalk15.hex("#EF5350");
2194
2493
  var PURPLE = chalk15.hex("#AB47BC");
2195
2494
  var BLUE2 = chalk15.hex("#42A5F5");
2196
2495
  var TEAL = chalk15.hex("#26A69A");
2197
- var AMBER3 = chalk15.hex("#FFAB00");
2198
- var GRAY = chalk15.gray;
2199
- var BORDER2 = chalk15.hex("#455A64");
2496
+ var AMBER4 = chalk15.hex("#FFAB00");
2497
+ var GRAY2 = chalk15.gray;
2498
+ var BORDER3 = chalk15.hex("#455A64");
2200
2499
  var BG_RED = chalk15.bgHex("#2D1111");
2201
2500
  var BG_PURPLE = chalk15.bgHex("#1A0D2B");
2202
2501
  var BG_BLUE = chalk15.bgHex("#0D1B2A");
@@ -2205,7 +2504,7 @@ function registerAnalyseCommand(program2) {
2205
2504
  program2.command("analyse").description("Analyse the current project for bugs, features, improvements, and upgrades").option("--results", "Show analysis dashboard or filtered list").option("--bugs", "Show bugs list (interactive)").option("--features", "Show features list (interactive)").option("--improvements", "Show improvements list (interactive)").option("--upgrades", "Show upgrades list (interactive)").option("--sort <method>", "Sort by: priority (default) or file").option("--search <query>", "Filter results by keyword").option("--status", "Show current analysis job status").option("--auto", "Auto-fix mode: Bob triages and MiniBob implements").option("--confidence <number>", "Confidence gate for auto-fix (default: 90)", "90").option("--priority <level>", "Priority gate for auto-fix: critical, high, medium, low (default: critical)", "critical").action(async (options) => {
2206
2505
  const config = getConfig();
2207
2506
  if (options.auto) {
2208
- const { runAutoFix } = await import("./analyse-auto-OBCDWYWX.js");
2507
+ const { runAutoFix } = await import("./analyse-auto-KZNPVVCR.js");
2209
2508
  const category = options.bugs ? "bugs" : options.features ? "features" : options.improvements ? "improvements" : options.upgrades ? "upgrades" : void 0;
2210
2509
  await runAutoFix({
2211
2510
  category,
@@ -2215,7 +2514,7 @@ function registerAnalyseCommand(program2) {
2215
2514
  return;
2216
2515
  }
2217
2516
  if (options.bugs || options.features || options.improvements || options.upgrades) {
2218
- const { showInteractiveResults } = await import("./analyse-results-QSOD3KVC.js");
2517
+ const { showInteractiveResults } = await import("./analyse-results-KM5C7XL7.js");
2219
2518
  const category = options.bugs ? "bugs" : options.features ? "features" : options.improvements ? "improvements" : "upgrades";
2220
2519
  await showInteractiveResults(config, category, options.sort, options.search);
2221
2520
  return;
@@ -2248,7 +2547,7 @@ async function showDashboard(config) {
2248
2547
  if (!counts) {
2249
2548
  console.log("");
2250
2549
  console.log(chalk15.yellow(" \u26A0\uFE0F No analysis results found."));
2251
- console.log(GRAY(" Run `bob analyse` first to analyse your project."));
2550
+ console.log(GRAY2(" Run `bob analyse` first to analyse your project."));
2252
2551
  console.log("");
2253
2552
  return;
2254
2553
  }
@@ -2262,31 +2561,31 @@ async function showDashboard(config) {
2262
2561
  function renderDashboard(counts) {
2263
2562
  const total = counts.bugs + counts.features + counts.improvements + counts.upgrades;
2264
2563
  console.log("");
2265
- console.log(BORDER2(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2566\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2566\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2566\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
2266
- console.log(BORDER2(" \u2551") + AMBER3(" \u25C6 MINIBOB ANALYSIS COMPLETE") + GRAY(` ${total} pts`) + BORDER2(" \u2551"));
2267
- console.log(BORDER2(" \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256C\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256C\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256C\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"));
2268
- console.log(BORDER2(" \u2551") + BG_RED(" ") + BORDER2("\u2551") + BG_PURPLE(" ") + BORDER2("\u2551") + BG_BLUE(" ") + BORDER2("\u2551") + BG_TEAL(" ") + BORDER2("\u2551"));
2269
- console.log(BORDER2(" \u2551") + BG_RED(` ${RED("\u{1F534} BUGS")} `) + BORDER2("\u2551") + BG_PURPLE(` ${PURPLE("\u{1F7E3} FEAT")} `) + BORDER2("\u2551") + BG_BLUE(` ${BLUE2("\u{1F535} OPTZ")} `) + BORDER2("\u2551") + BG_TEAL(` ${TEAL("\u{1F7E2} UPGR")} `) + BORDER2("\u2551"));
2564
+ console.log(BORDER3(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2566\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2566\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2566\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
2565
+ console.log(BORDER3(" \u2551") + AMBER4(" \u25C6 MINIBOB ANALYSIS COMPLETE") + GRAY2(` ${total} pts`) + BORDER3(" \u2551"));
2566
+ console.log(BORDER3(" \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256C\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256C\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256C\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"));
2567
+ console.log(BORDER3(" \u2551") + BG_RED(" ") + BORDER3("\u2551") + BG_PURPLE(" ") + BORDER3("\u2551") + BG_BLUE(" ") + BORDER3("\u2551") + BG_TEAL(" ") + BORDER3("\u2551"));
2568
+ console.log(BORDER3(" \u2551") + BG_RED(` ${RED("\u{1F534} BUGS")} `) + BORDER3("\u2551") + BG_PURPLE(` ${PURPLE("\u{1F7E3} FEAT")} `) + BORDER3("\u2551") + BG_BLUE(` ${BLUE2("\u{1F535} OPTZ")} `) + BORDER3("\u2551") + BG_TEAL(` ${TEAL("\u{1F7E2} UPGR")} `) + BORDER3("\u2551"));
2270
2569
  const bugsStr = String(counts.bugs).padStart(4);
2271
2570
  const featStr = String(counts.features).padStart(4);
2272
2571
  const imprStr = String(counts.improvements).padStart(4);
2273
2572
  const upgrStr = String(counts.upgrades).padStart(4);
2274
- console.log(BORDER2(" \u2551") + BG_RED(` ${RED(bugsStr)} `) + BORDER2("\u2551") + BG_PURPLE(` ${PURPLE(featStr)} `) + BORDER2("\u2551") + BG_BLUE(` ${BLUE2(imprStr)} `) + BORDER2("\u2551") + BG_TEAL(` ${TEAL(upgrStr)} `) + BORDER2("\u2551"));
2275
- console.log(BORDER2(" \u2551") + BG_RED(" ") + BORDER2("\u2551") + BG_PURPLE(" ") + BORDER2("\u2551") + BG_BLUE(" ") + BORDER2("\u2551") + BG_TEAL(" ") + BORDER2("\u2551"));
2276
- console.log(BORDER2(" \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2569\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2569\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2569\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"));
2277
- console.log(BORDER2(" \u2551") + chalk15.white(` ${total} POINTS IDENTIFIED`) + BORDER2(" \u2551"));
2278
- console.log(BORDER2(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
2573
+ console.log(BORDER3(" \u2551") + BG_RED(` ${RED(bugsStr)} `) + BORDER3("\u2551") + BG_PURPLE(` ${PURPLE(featStr)} `) + BORDER3("\u2551") + BG_BLUE(` ${BLUE2(imprStr)} `) + BORDER3("\u2551") + BG_TEAL(` ${TEAL(upgrStr)} `) + BORDER3("\u2551"));
2574
+ console.log(BORDER3(" \u2551") + BG_RED(" ") + BORDER3("\u2551") + BG_PURPLE(" ") + BORDER3("\u2551") + BG_BLUE(" ") + BORDER3("\u2551") + BG_TEAL(" ") + BORDER3("\u2551"));
2575
+ console.log(BORDER3(" \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2569\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2569\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2569\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"));
2576
+ console.log(BORDER3(" \u2551") + chalk15.white(` ${total} POINTS IDENTIFIED`) + BORDER3(" \u2551"));
2577
+ console.log(BORDER3(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
2279
2578
  console.log("");
2280
- console.log(GRAY(" View details (interactive):"));
2281
- console.log(GRAY(" bob analyse --results --bugs"));
2282
- console.log(GRAY(" bob analyse --results --features"));
2283
- console.log(GRAY(" bob analyse --results --improvements"));
2284
- console.log(GRAY(" bob analyse --results --upgrades"));
2579
+ console.log(GRAY2(" View details (interactive):"));
2580
+ console.log(GRAY2(" bob analyse --results --bugs"));
2581
+ console.log(GRAY2(" bob analyse --results --features"));
2582
+ console.log(GRAY2(" bob analyse --results --improvements"));
2583
+ console.log(GRAY2(" bob analyse --results --upgrades"));
2285
2584
  console.log("");
2286
- console.log(GRAY(" Auto-fix:"));
2287
- console.log(GRAY(" bob analyse --auto"));
2288
- console.log(GRAY(" bob analyse --auto --bugs --confidence 80"));
2289
- console.log(GRAY(" bob analyse --auto --priority high"));
2585
+ console.log(GRAY2(" Auto-fix:"));
2586
+ console.log(GRAY2(" bob analyse --auto"));
2587
+ console.log(GRAY2(" bob analyse --auto --bugs --confidence 80"));
2588
+ console.log(GRAY2(" bob analyse --auto --priority high"));
2290
2589
  console.log("");
2291
2590
  }
2292
2591
  async function showStatus(config) {
@@ -2305,7 +2604,7 @@ async function showStatus(config) {
2305
2604
  spinner.stop();
2306
2605
  if (result?.status) {
2307
2606
  console.log("");
2308
- console.log(AMBER3(` \u25C6 Analysis Status: ${result.status.toUpperCase()}`));
2607
+ console.log(AMBER4(` \u25C6 Analysis Status: ${result.status.toUpperCase()}`));
2309
2608
  if (result.progress) {
2310
2609
  const pct = Math.round(result.progress.completed / result.progress.total * 100);
2311
2610
  const barLen = 30;
@@ -2315,13 +2614,13 @@ async function showStatus(config) {
2315
2614
  else if (pct < 50) barColor = chalk15.hex("#FF8C00");
2316
2615
  else if (pct < 75) barColor = chalk15.yellow;
2317
2616
  else barColor = chalk15.green;
2318
- const bar = barColor("\u2588".repeat(filled)) + GRAY("\u2591".repeat(barLen - filled));
2319
- console.log(` [${bar}] ${result.progress.completed}/${result.progress.total} (${pct}%)`);
2617
+ const bar2 = barColor("\u2588".repeat(filled)) + GRAY2("\u2591".repeat(barLen - filled));
2618
+ console.log(` [${bar2}] ${result.progress.completed}/${result.progress.total} (${pct}%)`);
2320
2619
  }
2321
2620
  console.log("");
2322
2621
  } else {
2323
2622
  console.log("");
2324
- console.log(GRAY(" No active analysis job found."));
2623
+ console.log(GRAY2(" No active analysis job found."));
2325
2624
  console.log("");
2326
2625
  }
2327
2626
  } catch (error) {
@@ -2335,8 +2634,8 @@ async function runAnalysis(config) {
2335
2634
  const projectName = getProjectName(cwd);
2336
2635
  console.log("");
2337
2636
  console.log(chalk15.bold.cyan(` \u26A1 Analysing project: ${projectName}`));
2338
- console.log(GRAY(` \u{1F4C1} ${cwd}`));
2339
- console.log(GRAY(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
2637
+ console.log(GRAY2(` \u{1F4C1} ${cwd}`));
2638
+ console.log(GRAY2(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
2340
2639
  console.log("");
2341
2640
  if (config.tier === "platform" && config.provider !== "local" && config.loggedIn && config.conversationId) {
2342
2641
  const spinner = ora7({ text: chalk15.cyan(" Triggering platform analysis..."), spinner: "dots" }).start();
@@ -2347,8 +2646,8 @@ async function runAnalysis(config) {
2347
2646
  spinner.stop();
2348
2647
  if (result?.success) {
2349
2648
  console.log(chalk15.green(` \u2705 Analysis job created: ${result.jobId}`));
2350
- console.log(GRAY(" Run `bob analyse --status` to check progress."));
2351
- console.log(GRAY(" Run `bob analyse --results` when complete."));
2649
+ console.log(GRAY2(" Run `bob analyse --status` to check progress."));
2650
+ console.log(GRAY2(" Run `bob analyse --results` when complete."));
2352
2651
  } else {
2353
2652
  console.log(chalk15.red(` \u274C ${result?.message || "Failed to start analysis."}`));
2354
2653
  }
@@ -2362,8 +2661,8 @@ async function runAnalysis(config) {
2362
2661
  }
2363
2662
  if (config.provider !== "local" || !config.localEndpoint) {
2364
2663
  console.log(chalk15.red(" \u274C Local analysis requires a local model."));
2365
- console.log(GRAY(" Run `bob config set provider local`"));
2366
- console.log(GRAY(" Run `bob config set localEndpoint http://127.0.0.1:11434/api/chat`"));
2664
+ console.log(GRAY2(" Run `bob config set provider local`"));
2665
+ console.log(GRAY2(" Run `bob config set localEndpoint http://127.0.0.1:11434/api/chat`"));
2367
2666
  console.log("");
2368
2667
  return;
2369
2668
  }
@@ -2375,21 +2674,21 @@ async function runAnalysis(config) {
2375
2674
  }
2376
2675
  const dependencies = loadDependencies(cwd) || {};
2377
2676
  const files = Object.keys(summaries);
2378
- console.log(GRAY(` Found ${files.length} indexed files. Starting deep analysis...`));
2677
+ console.log(GRAY2(` Found ${files.length} indexed files. Starting deep analysis...`));
2379
2678
  console.log("");
2380
2679
  console.log("");
2381
2680
  console.log("");
2382
2681
  console.log("");
2383
2682
  const { analysisDir } = ensureProjectStructure(cwd);
2384
- const resultsDir = path7.join(analysisDir, "results");
2385
- if (!fs6.existsSync(resultsDir)) fs6.mkdirSync(resultsDir, { recursive: true });
2683
+ const resultsDir = path8.join(analysisDir, "results");
2684
+ if (!fs7.existsSync(resultsDir)) fs7.mkdirSync(resultsDir, { recursive: true });
2386
2685
  let completed = 0;
2387
2686
  const allResults = {};
2388
2687
  for (const filePath of files) {
2389
- const absolutePath = path7.join(cwd, filePath);
2688
+ const absolutePath = path8.join(cwd, filePath);
2390
2689
  let content;
2391
2690
  try {
2392
- content = fs6.readFileSync(absolutePath, "utf-8");
2691
+ content = fs7.readFileSync(absolutePath, "utf-8");
2393
2692
  } catch (error) {
2394
2693
  console.error(chalk15.red(` \u274C Could not read file ${filePath}: ${error.message}`));
2395
2694
  completed++;
@@ -2470,7 +2769,7 @@ ${fileDeps.map((d) => `- ${d}: ${summaries[d] || "unknown"}`).join("\n")}
2470
2769
  }
2471
2770
  completed++;
2472
2771
  }
2473
- fs6.writeFileSync(path7.join(resultsDir, "analysis.json"), JSON.stringify(allResults, null, 2));
2772
+ fs7.writeFileSync(path8.join(resultsDir, "analysis.json"), JSON.stringify(allResults, null, 2));
2474
2773
  let totalBugs = 0, totalFeatures = 0, totalImprovements = 0, totalUpgrades = 0;
2475
2774
  for (const fileResults of Object.values(allResults)) {
2476
2775
  const r = fileResults;
@@ -2479,7 +2778,7 @@ ${fileDeps.map((d) => `- ${d}: ${summaries[d] || "unknown"}`).join("\n")}
2479
2778
  totalImprovements += r.improvements?.length || 0;
2480
2779
  totalUpgrades += r.upgrades?.length || 0;
2481
2780
  }
2482
- fs6.writeFileSync(path7.join(resultsDir, "counts.json"), JSON.stringify({
2781
+ fs7.writeFileSync(path8.join(resultsDir, "counts.json"), JSON.stringify({
2483
2782
  bugs: totalBugs,
2484
2783
  features: totalFeatures,
2485
2784
  improvements: totalImprovements,
@@ -2488,17 +2787,17 @@ ${fileDeps.map((d) => `- ${d}: ${summaries[d] || "unknown"}`).join("\n")}
2488
2787
  console.log("");
2489
2788
  console.log("");
2490
2789
  console.log(chalk15.bold.green(` \u2705 Analysis complete: ${projectName}`));
2491
- console.log(GRAY(` \u{1F4BE} Saved to: ~/.bob/projects/${projectName}/analysis/results/`));
2492
- console.log(GRAY(" Run `bob analyse --results` to view the dashboard."));
2493
- console.log(GRAY(" Run `bob analyse --auto` for auto-fix mode."));
2790
+ console.log(GRAY2(` \u{1F4BE} Saved to: ~/.bob/projects/${projectName}/analysis/results/`));
2791
+ console.log(GRAY2(" Run `bob analyse --results` to view the dashboard."));
2792
+ console.log(GRAY2(" Run `bob analyse --auto` for auto-fix mode."));
2494
2793
  console.log("");
2495
2794
  }
2496
2795
  function loadLocalCounts() {
2497
2796
  const cwd = process.cwd();
2498
2797
  const { analysisDir } = ensureProjectStructure(cwd);
2499
- const countsPath = path7.join(analysisDir, "results", "counts.json");
2500
- if (!fs6.existsSync(countsPath)) return null;
2501
- return JSON.parse(fs6.readFileSync(countsPath, "utf-8"));
2798
+ const countsPath = path8.join(analysisDir, "results", "counts.json");
2799
+ if (!fs7.existsSync(countsPath)) return null;
2800
+ return JSON.parse(fs7.readFileSync(countsPath, "utf-8"));
2502
2801
  }
2503
2802
  function printProgress2(completed, total, filePath, info) {
2504
2803
  const percent = completed / total;
@@ -2510,28 +2809,28 @@ function printProgress2(completed, total, filePath, info) {
2510
2809
  else if (percent < 0.75) barColor = chalk15.yellow;
2511
2810
  else barColor = chalk15.green;
2512
2811
  const filledBar = barColor("\u2588".repeat(filled));
2513
- const emptyBar = GRAY("\u2591".repeat(barLength - filled));
2812
+ const emptyBar = GRAY2("\u2591".repeat(barLength - filled));
2514
2813
  const percentText = barColor(`${Math.round(percent * 100)}%`);
2515
2814
  process.stdout.write("\x1B[2K\x1B[1A\x1B[2K\x1B[1A\x1B[2K\x1B[1A\x1B[2K\r");
2516
2815
  console.log(` ${chalk15.cyan("\u26A1")} Analysing [${filledBar}${emptyBar}] ${completed}/${total} ${percentText}`);
2517
2816
  console.log(chalk15.green(` \u2705 ${filePath}`));
2518
- console.log(GRAY(` ${info}`));
2817
+ console.log(GRAY2(` ${info}`));
2519
2818
  console.log("");
2520
2819
  }
2521
2820
 
2522
2821
  // src/commands/autonomy.ts
2523
2822
  import chalk16 from "chalk";
2524
2823
  import ora8 from "ora";
2525
- import * as readline5 from "readline";
2824
+ import * as readline6 from "readline";
2526
2825
  import simpleGit2 from "simple-git";
2527
- import * as fs7 from "fs";
2528
- import * as path8 from "path";
2826
+ import * as fs8 from "fs";
2827
+ import * as path9 from "path";
2529
2828
  var RED2 = chalk16.hex("#EF5350");
2530
2829
  var GREEN3 = chalk16.hex("#66BB6A");
2531
- var AMBER4 = chalk16.hex("#FFAB00");
2830
+ var AMBER5 = chalk16.hex("#FFAB00");
2532
2831
  var BLUE3 = chalk16.hex("#42A5F5");
2533
- var GRAY2 = chalk16.gray;
2534
- var BORDER3 = chalk16.hex("#455A64");
2832
+ var GRAY3 = chalk16.gray;
2833
+ var BORDER4 = chalk16.hex("#455A64");
2535
2834
  var CYAN = chalk16.cyan;
2536
2835
  function registerAutonomyCommand(program2) {
2537
2836
  program2.command("autonomy").description("Launch autonomous repair mode \u2014 MiniBob fixes all analysed issues").option("--status", "Check current autonomy run progress (Tier 3)").option("--stop", "Stop the current autonomy run (Tier 3)").option("--category <cat>", "Limit to: bugs, features, improvements, upgrades").option("--priority <level>", "Minimum priority: critical, high, medium, low (default: high)", "high").option("--no-push", "Skip git push after completion").action(async (options) => {
@@ -2554,10 +2853,10 @@ function registerAutonomyCommand(program2) {
2554
2853
  async function runTier3Autonomy(config) {
2555
2854
  console.log("");
2556
2855
  console.log(chalk16.bold.cyan(" \u26A1 MiniBob Autonomy Mode (Platform)"));
2557
- console.log(GRAY2(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
2558
- console.log(GRAY2(` \u{1F4E1} Conversation: ${config.conversationId?.slice(0, 24)}...`));
2559
- console.log(GRAY2(` \u{1F517} https://bobs-workshop.web.app/#/bobcodeassistant/${config.conversationId}`));
2560
- console.log(GRAY2(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
2856
+ console.log(GRAY3(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
2857
+ console.log(GRAY3(` \u{1F4E1} Conversation: ${config.conversationId?.slice(0, 24)}...`));
2858
+ console.log(GRAY3(` \u{1F517} https://bobs-workshop.web.app/#/bobcodeassistant/${config.conversationId}`));
2859
+ console.log(GRAY3(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
2561
2860
  console.log("");
2562
2861
  const spinner = ora8({ text: CYAN(" Igniting autonomy workers..."), spinner: "dots" }).start();
2563
2862
  try {
@@ -2571,7 +2870,7 @@ async function runTier3Autonomy(config) {
2571
2870
  return;
2572
2871
  }
2573
2872
  console.log(GREEN3(" \u2705 Autonomy loop ignited!"));
2574
- console.log(GRAY2(" Streaming progress..."));
2873
+ console.log(GRAY3(" Streaming progress..."));
2575
2874
  console.log("");
2576
2875
  } catch (error) {
2577
2876
  spinner.stop();
@@ -2582,15 +2881,15 @@ async function runTier3Autonomy(config) {
2582
2881
  let running = true;
2583
2882
  let tasksDone = 0;
2584
2883
  let totalTasks = 0;
2585
- console.log(GRAY2(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
2586
- console.log(GRAY2(" Press Ctrl+C to stop streaming (workers continue in background)"));
2587
- console.log(GRAY2(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
2884
+ console.log(GRAY3(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
2885
+ console.log(GRAY3(" Press Ctrl+C to stop streaming (workers continue in background)"));
2886
+ console.log(GRAY3(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
2588
2887
  console.log("");
2589
2888
  process.on("SIGINT", () => {
2590
2889
  running = false;
2591
2890
  console.log("");
2592
- console.log(GRAY2(" \u{1F4E1} Stopped streaming. Workers continue in the background."));
2593
- console.log(GRAY2(` Check progress: bob autonomy --status`));
2891
+ console.log(GRAY3(" \u{1F4E1} Stopped streaming. Workers continue in the background."));
2892
+ console.log(GRAY3(` Check progress: bob autonomy --status`));
2594
2893
  console.log("");
2595
2894
  process.exit(0);
2596
2895
  });
@@ -2621,8 +2920,8 @@ async function runTier3Autonomy(config) {
2621
2920
  if (text.includes("[ACTION:GITHUB_PUSH_REQUEST:")) {
2622
2921
  console.log("");
2623
2922
  console.log(GREEN3(" \u2705 All tasks complete!"));
2624
- console.log(AMBER4(" \u{1F4E4} MiniBob wants to push to GitHub."));
2625
- const rl = readline5.createInterface({ input: process.stdin, output: process.stdout });
2923
+ console.log(AMBER5(" \u{1F4E4} MiniBob wants to push to GitHub."));
2924
+ const rl = readline6.createInterface({ input: process.stdin, output: process.stdout });
2626
2925
  const answer = await new Promise((resolve2) => {
2627
2926
  rl.question(CYAN(" Approve push? (y/n): "), resolve2);
2628
2927
  });
@@ -2635,7 +2934,7 @@ async function runTier3Autonomy(config) {
2635
2934
  console.log(RED2(` \u274C Push failed: ${pushErr.message}`));
2636
2935
  }
2637
2936
  } else {
2638
- console.log(GRAY2(" Push skipped. You can push manually later."));
2937
+ console.log(GRAY3(" Push skipped. You can push manually later."));
2639
2938
  }
2640
2939
  running = false;
2641
2940
  continue;
@@ -2646,7 +2945,7 @@ async function runTier3Autonomy(config) {
2646
2945
  let lineColor;
2647
2946
  if (type === "stderr") lineColor = RED2;
2648
2947
  else if (type === "stdout") lineColor = GREEN3;
2649
- else lineColor = GRAY2;
2948
+ else lineColor = GRAY3;
2650
2949
  console.log(lineColor(` ${text}`));
2651
2950
  lastTimestamp = line.timestamp || lastTimestamp;
2652
2951
  }
@@ -2658,18 +2957,18 @@ async function runTier3Autonomy(config) {
2658
2957
  }
2659
2958
  }
2660
2959
  console.log("");
2661
- console.log(BORDER3(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
2662
- console.log(BORDER3(" \u2551") + AMBER4(" \u25C6 AUTONOMY SESSION COMPLETE"));
2663
- console.log(BORDER3(" \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"));
2664
- console.log(BORDER3(" \u2551") + GREEN3(` \u2705 Tasks completed: ${tasksDone}/${totalTasks}`));
2665
- console.log(BORDER3(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
2960
+ console.log(BORDER4(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
2961
+ console.log(BORDER4(" \u2551") + AMBER5(" \u25C6 AUTONOMY SESSION COMPLETE"));
2962
+ console.log(BORDER4(" \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"));
2963
+ console.log(BORDER4(" \u2551") + GREEN3(` \u2705 Tasks completed: ${tasksDone}/${totalTasks}`));
2964
+ console.log(BORDER4(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
2666
2965
  console.log("");
2667
2966
  }
2668
2967
  async function runTier1Autonomy(config, options) {
2669
2968
  if (config.provider !== "local" || !config.localEndpoint) {
2670
2969
  console.log(RED2(" \u274C Local autonomy requires a local model."));
2671
- console.log(GRAY2(" Run `bob config set provider local`"));
2672
- console.log(GRAY2(" Run `bob config set localEndpoint http://127.0.0.1:11434/api/chat`"));
2970
+ console.log(GRAY3(" Run `bob config set provider local`"));
2971
+ console.log(GRAY3(" Run `bob config set localEndpoint http://127.0.0.1:11434/api/chat`"));
2673
2972
  return;
2674
2973
  }
2675
2974
  const categories = options.category ? [options.category] : ["bugs", "features", "improvements", "upgrades"];
@@ -2677,11 +2976,11 @@ async function runTier1Autonomy(config, options) {
2677
2976
  const shouldPush = options.push !== false;
2678
2977
  console.log("");
2679
2978
  console.log(chalk16.bold.cyan(" \u26A1 MiniBob Autonomy Mode (Local)"));
2680
- console.log(GRAY2(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
2681
- console.log(GRAY2(` Priority gate: ${priorityGate}+`));
2682
- console.log(GRAY2(` Categories: ${categories.join(", ")}`));
2683
- console.log(GRAY2(` Git push: ${shouldPush ? "enabled" : "disabled"}`));
2684
- console.log(GRAY2(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
2979
+ console.log(GRAY3(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
2980
+ console.log(GRAY3(` Priority gate: ${priorityGate}+`));
2981
+ console.log(GRAY3(` Categories: ${categories.join(", ")}`));
2982
+ console.log(GRAY3(` Git push: ${shouldPush ? "enabled" : "disabled"}`));
2983
+ console.log(GRAY3(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
2685
2984
  console.log("");
2686
2985
  let allSuggestions = [];
2687
2986
  for (const cat of categories) {
@@ -2699,7 +2998,7 @@ async function runTier1Autonomy(config, options) {
2699
2998
  console.log(GREEN3(" \u2705 No pending tasks. Project is clean!"));
2700
2999
  return;
2701
3000
  }
2702
- console.log(GRAY2(` Found ${allSuggestions.length} tasks to process.`));
3001
+ console.log(GRAY3(` Found ${allSuggestions.length} tasks to process.`));
2703
3002
  console.log("");
2704
3003
  const workQueue = allSuggestions.map((s) => ({
2705
3004
  suggestion: s,
@@ -2732,14 +3031,14 @@ async function runTier1Autonomy(config, options) {
2732
3031
  }
2733
3032
  console.log("");
2734
3033
  console.log("");
2735
- console.log(BORDER3(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
2736
- console.log(BORDER3(" \u2551") + AMBER4(" \u25C6 MINIBOB AUTONOMY REPORT"));
2737
- console.log(BORDER3(" \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"));
2738
- console.log(BORDER3(" \u2551") + GREEN3(` \u2705 Fixed: ${fixed} files`));
3034
+ console.log(BORDER4(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
3035
+ console.log(BORDER4(" \u2551") + AMBER5(" \u25C6 MINIBOB AUTONOMY REPORT"));
3036
+ console.log(BORDER4(" \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"));
3037
+ console.log(BORDER4(" \u2551") + GREEN3(` \u2705 Fixed: ${fixed} files`));
2739
3038
  if (failed > 0) {
2740
- console.log(BORDER3(" \u2551") + RED2(` \u274C Failed: ${failed} files`));
3039
+ console.log(BORDER4(" \u2551") + RED2(` \u274C Failed: ${failed} files`));
2741
3040
  }
2742
- console.log(BORDER3(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
3041
+ console.log(BORDER4(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
2743
3042
  console.log("");
2744
3043
  if (shouldPush && fixed > 0) {
2745
3044
  const git = simpleGit2(process.cwd());
@@ -2766,17 +3065,17 @@ Autonomous repair by Bob's CLI.`;
2766
3065
  }
2767
3066
  }
2768
3067
  console.log(GREEN3(` \u2705 Pushed to ${branch}!`));
2769
- console.log(GRAY2(` Commit: MiniBob Autonomy: Fixed ${fixed} issue(s)`));
3068
+ console.log(GRAY3(` Commit: MiniBob Autonomy: Fixed ${fixed} issue(s)`));
2770
3069
  } catch (gitErr) {
2771
3070
  console.log(RED2(` \u274C Git push failed: ${gitErr.message}`));
2772
- console.log(GRAY2(' Files are saved locally. Push manually with `bob push "message"`.'));
3071
+ console.log(GRAY3(' Files are saved locally. Push manually with `bob push "message"`.'));
2773
3072
  }
2774
3073
  } else {
2775
- console.log(GRAY2(" Not a git repo. Files saved locally only."));
3074
+ console.log(GRAY3(" Not a git repo. Files saved locally only."));
2776
3075
  }
2777
3076
  }
2778
3077
  console.log("");
2779
- console.log(GRAY2(" \u{1F4E6} All original files backed up to .bob-backups/"));
3078
+ console.log(GRAY3(" \u{1F4E6} All original files backed up to .bob-backups/"));
2780
3079
  console.log("");
2781
3080
  }
2782
3081
  async function showAutonomyStatus(config) {
@@ -2795,15 +3094,15 @@ async function showAutonomyStatus(config) {
2795
3094
  spinner.stop();
2796
3095
  if (result?.lines && result.lines.length > 0) {
2797
3096
  console.log("");
2798
- console.log(AMBER4(" \u25C6 Recent Autonomy Activity:"));
2799
- console.log(GRAY2(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
3097
+ console.log(AMBER5(" \u25C6 Recent Autonomy Activity:"));
3098
+ console.log(GRAY3(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
2800
3099
  for (const line of result.lines) {
2801
- console.log(GRAY2(` ${line.text}`));
3100
+ console.log(GRAY3(` ${line.text}`));
2802
3101
  }
2803
3102
  console.log("");
2804
3103
  } else {
2805
3104
  console.log("");
2806
- console.log(GRAY2(" No recent autonomy activity."));
3105
+ console.log(GRAY3(" No recent autonomy activity."));
2807
3106
  console.log("");
2808
3107
  }
2809
3108
  } catch (error) {
@@ -2863,17 +3162,17 @@ Return the complete file content now:`;
2863
3162
  return false;
2864
3163
  }
2865
3164
  }
2866
- const absolutePath = path8.join(process.cwd(), suggestion.filePath);
2867
- const backupDir = path8.join(process.cwd(), ".bob-backups");
2868
- if (!fs7.existsSync(backupDir)) fs7.mkdirSync(backupDir, { recursive: true });
2869
- if (fs7.existsSync(absolutePath)) {
3165
+ const absolutePath = path9.join(process.cwd(), suggestion.filePath);
3166
+ const backupDir = path9.join(process.cwd(), ".bob-backups");
3167
+ if (!fs8.existsSync(backupDir)) fs8.mkdirSync(backupDir, { recursive: true });
3168
+ if (fs8.existsSync(absolutePath)) {
2870
3169
  const timestamp = Date.now();
2871
3170
  const backupName = suggestion.filePath.replace(/[\/\\]/g, "_") + `.${timestamp}.bak`;
2872
- fs7.copyFileSync(absolutePath, path8.join(backupDir, backupName));
3171
+ fs8.copyFileSync(absolutePath, path9.join(backupDir, backupName));
2873
3172
  }
2874
- const dir = path8.dirname(absolutePath);
2875
- if (!fs7.existsSync(dir)) fs7.mkdirSync(dir, { recursive: true });
2876
- fs7.writeFileSync(absolutePath, newContent, "utf-8");
3173
+ const dir = path9.dirname(absolutePath);
3174
+ if (!fs8.existsSync(dir)) fs8.mkdirSync(dir, { recursive: true });
3175
+ fs8.writeFileSync(absolutePath, newContent, "utf-8");
2877
3176
  return true;
2878
3177
  } catch {
2879
3178
  return false;
@@ -2881,11 +3180,11 @@ Return the complete file content now:`;
2881
3180
  }
2882
3181
  function detectLocalCategory(suggestion) {
2883
3182
  const cwd = process.cwd();
2884
- const projectName = path8.basename(cwd);
3183
+ const projectName = path9.basename(cwd);
2885
3184
  const homeDir = process.env.HOME || process.env.USERPROFILE || "";
2886
- const analysisPath = path8.join(homeDir, ".bob", "projects", projectName, "analysis", "results", "analysis.json");
2887
- if (!fs7.existsSync(analysisPath)) return "bugs";
2888
- const allResults = JSON.parse(fs7.readFileSync(analysisPath, "utf-8"));
3185
+ const analysisPath = path9.join(homeDir, ".bob", "projects", projectName, "analysis", "results", "analysis.json");
3186
+ if (!fs8.existsSync(analysisPath)) return "bugs";
3187
+ const allResults = JSON.parse(fs8.readFileSync(analysisPath, "utf-8"));
2889
3188
  const fileResults = allResults[suggestion.filePath];
2890
3189
  if (!fileResults) return "bugs";
2891
3190
  for (const cat of ["bugs", "features", "improvements", "upgrades"]) {
@@ -2905,16 +3204,16 @@ function renderTickerHUD(done, total, bugs, features, improvements, upgrades, to
2905
3204
  else if (percent < 0.5) barColor = chalk16.hex("#FF8C00");
2906
3205
  else if (percent < 0.75) barColor = chalk16.yellow;
2907
3206
  else barColor = chalk16.green;
2908
- const bar = barColor("\u2588".repeat(filled)) + GRAY2("\u2591".repeat(barLen - filled));
2909
- console.log(` \u26A1 [${bar}] ${done}/${total} ${barColor(Math.round(percent * 100) + "%")}`);
2910
- console.log(GRAY2(` \u{1F41B} ${bugs} \u2B50 ${features} \u{1F527} ${improvements} \u2B06\uFE0F ${upgrades} | Tokens: ${tokens.toLocaleString()}`));
3207
+ const bar2 = barColor("\u2588".repeat(filled)) + GRAY3("\u2591".repeat(barLen - filled));
3208
+ console.log(` \u26A1 [${bar2}] ${done}/${total} ${barColor(Math.round(percent * 100) + "%")}`);
3209
+ console.log(GRAY3(` \u{1F41B} ${bugs} \u2B50 ${features} \u{1F527} ${improvements} \u2B06\uFE0F ${upgrades} | Tokens: ${tokens.toLocaleString()}`));
2911
3210
  }
2912
3211
  var lastLocalTodoLines = 0;
2913
3212
  function renderLocalTodoList(queue) {
2914
3213
  const lines = [];
2915
3214
  lines.push("");
2916
- lines.push(AMBER4(" \u{1F4CB} MiniBob Autonomy Queue"));
2917
- lines.push(GRAY2(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
3215
+ lines.push(AMBER5(" \u{1F4CB} MiniBob Autonomy Queue"));
3216
+ lines.push(GRAY3(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
2918
3217
  for (let i = 0; i < queue.length; i++) {
2919
3218
  const task = queue[i];
2920
3219
  const label = task.suggestion.title || task.suggestion.description?.slice(0, 35) || "No title";
@@ -2927,7 +3226,7 @@ function renderLocalTodoList(queue) {
2927
3226
  break;
2928
3227
  case "working":
2929
3228
  icon = "\u23F3";
2930
- color = AMBER4;
3229
+ color = AMBER5;
2931
3230
  break;
2932
3231
  case "failed":
2933
3232
  icon = "\u2717";
@@ -2935,11 +3234,11 @@ function renderLocalTodoList(queue) {
2935
3234
  break;
2936
3235
  case "skipped":
2937
3236
  icon = "\u23F8\uFE0F";
2938
- color = GRAY2;
3237
+ color = GRAY3;
2939
3238
  break;
2940
3239
  default:
2941
3240
  icon = "\u2610";
2942
- color = GRAY2;
3241
+ color = GRAY3;
2943
3242
  }
2944
3243
  lines.push(color(` ${icon} [${i + 1}/${queue.length}] ${task.suggestion.filePath}`));
2945
3244
  lines.push(color(` ${label}`));
@@ -2955,7 +3254,7 @@ function renderLocalTodoList(queue) {
2955
3254
  else if (percent < 0.75) barColor = chalk16.yellow;
2956
3255
  else barColor = chalk16.green;
2957
3256
  lines.push("");
2958
- lines.push(` [${barColor("\u2588".repeat(filled))}${GRAY2("\u2591".repeat(barLen - filled))}] ${completed}/${total} ${barColor(Math.round(percent * 100) + "%")}`);
3257
+ lines.push(` [${barColor("\u2588".repeat(filled))}${GRAY3("\u2591".repeat(barLen - filled))}] ${completed}/${total} ${barColor(Math.round(percent * 100) + "%")}`);
2959
3258
  lines.push("");
2960
3259
  if (lastLocalTodoLines > 0) {
2961
3260
  process.stdout.write(`\x1B[${lastLocalTodoLines}A`);
@@ -2970,25 +3269,1852 @@ function renderLocalTodoList(queue) {
2970
3269
  lastLocalTodoLines = lines.length;
2971
3270
  }
2972
3271
 
2973
- // bin/bob.ts
2974
- var program = new Command();
2975
- program.name("bob").description("Bob's CLI \u2014 AI coding assistant and Forge orchestrator").version("0.1.0");
2976
- program.command("whoami").description("Show current authentication status and configuration").action(() => {
2977
- const config = getConfig();
2978
- const projectName = path9.basename(process.cwd());
2979
- console.log("");
2980
- console.log(chalk17.bold(" \u{1F916} Bob's CLI"));
2981
- console.log(chalk17.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
2982
- console.log(` ${chalk17.cyan("Status:")} ${config.loggedIn ? chalk17.green("Logged in as " + config.email) : "Not logged in"}`);
2983
- console.log(` ${chalk17.cyan("Tier:")} ${config.tier === "platform" ? "Platform (Tier 3)" : "Local-first (Tier 1)"}`);
2984
- console.log(` ${chalk17.cyan("Provider:")} ${config.provider || "Not configured"}`);
2985
- console.log(` ${chalk17.cyan("Mode:")} ${config.personalizationMode ? "Personalized" : config.consultantMode ? "Consultant" : "Standard"}`);
2986
- console.log(` ${chalk17.cyan("IDRP:")} ${config.idrp ? "Enabled" : "Disabled"}`);
2987
- console.log(` ${chalk17.cyan("Project:")} ${projectName} (${process.cwd()})`);
2988
- console.log(` ${chalk17.cyan("Session:")} ${config.conversationId ? config.conversationId.slice(0, 20) + "..." : "None"}`);
3272
+ // src/commands/serve.ts
3273
+ import chalk17 from "chalk";
3274
+ import * as os3 from "os";
3275
+ import * as path10 from "path";
3276
+ var GREEN4 = chalk17.hex("#66BB6A");
3277
+ var AMBER6 = chalk17.hex("#FFAB00");
3278
+ var BLUE4 = chalk17.hex("#42A5F5");
3279
+ var RED3 = chalk17.hex("#EF5350");
3280
+ var GRAY4 = chalk17.gray;
3281
+ var CYAN2 = chalk17.cyan;
3282
+ var BORDER5 = chalk17.hex("#455A64");
3283
+ var TIER_CONFIGS = {
3284
+ "Power": {
3285
+ activeInterval: 2e3,
3286
+ // 2 seconds
3287
+ sleepInterval: 12e4,
3288
+ // 120 seconds (2 minutes) when sleeping
3289
+ idleThreshold: 5 * 6e4,
3290
+ // 5 min → enter sleep
3291
+ extendedIdleTimeout: null
3292
+ // Never auto-exit
3293
+ },
3294
+ "Pro": {
3295
+ activeInterval: 1e4,
3296
+ // 10 seconds
3297
+ sleepInterval: 3e4,
3298
+ // 30 seconds when sleeping
3299
+ idleThreshold: 5 * 6e4,
3300
+ // 5 min → enter sleep
3301
+ extendedIdleTimeout: 60 * 6e4
3302
+ // 1 hour → auto-exit
3303
+ },
3304
+ "Starter": {
3305
+ activeInterval: 15e3,
3306
+ // 15 seconds
3307
+ sleepInterval: null,
3308
+ // No sleep mode — goes straight to auto-exit
3309
+ idleThreshold: null,
3310
+ // No idle detection
3311
+ extendedIdleTimeout: 15 * 6e4
3312
+ // 15 minutes → auto-exit
3313
+ }
3314
+ };
3315
+ var BLOCKED_TIERS = ["Explore", "Free", "free", "explore"];
3316
+ function registerServeCommand(program2) {
3317
+ program2.command("serve").description("Start an Active Bob \u2014 receive and execute commands from the web app or another device").action(async () => {
3318
+ const config = getConfig();
3319
+ if (!config.loggedIn || !config.authToken) {
3320
+ console.log("");
3321
+ console.log(RED3(" \u274C Not logged in. Active Bob requires Tier 3."));
3322
+ console.log(GRAY4(" Run `bob login` to authenticate."));
3323
+ console.log("");
3324
+ return;
3325
+ }
3326
+ if (!config.conversationId) {
3327
+ console.log("");
3328
+ console.log(RED3(" \u274C No active conversation."));
3329
+ console.log(GRAY4(" Active Bob must be bound to a conversation."));
3330
+ console.log(GRAY4(" Run `bob conversations join` first, then `bob serve`."));
3331
+ console.log("");
3332
+ return;
3333
+ }
3334
+ if (!config.localEndpoint) {
3335
+ console.log("");
3336
+ console.log(RED3(" \u274C No local endpoint configured."));
3337
+ console.log(GRAY4(" Active Bob uses your local model for sovereign execution."));
3338
+ console.log(GRAY4(" Run `bob config set localEndpoint http://127.0.0.1:11434/api/chat`"));
3339
+ console.log("");
3340
+ return;
3341
+ }
3342
+ let userTier = "Explore";
3343
+ try {
3344
+ const tierResult = await callCloudFunction("getCLIUserTier", {});
3345
+ userTier = tierResult?.tier || "Explore";
3346
+ } catch {
3347
+ userTier = "Explore";
3348
+ }
3349
+ if (BLOCKED_TIERS.includes(userTier)) {
3350
+ const email = config.email || "";
3351
+ const domain = email.split("@").pop()?.toLowerCase() || "";
3352
+ const genericDomains = ["gmail.com", "outlook.com", "yahoo.com", "hotmail.com", "icloud.com", "protonmail.com", "ymail.com"];
3353
+ const isOrgUser = !genericDomains.includes(domain);
3354
+ console.log("");
3355
+ console.log(RED3(" \u274C Active Bob requires a paid subscription."));
3356
+ console.log(GRAY4(` Your current tier: ${userTier}`));
3357
+ console.log("");
3358
+ if (isOrgUser) {
3359
+ console.log(AMBER6(" \u{1F3E2} Contact your organization administrator to upgrade your tier."));
3360
+ console.log(GRAY4(` Organization: ${domain}`));
3361
+ console.log(GRAY4(" Admin Dashboard: https://bobs-workshop.web.app/#/bobsadmindashboard"));
3362
+ } else {
3363
+ console.log(AMBER6(" \u{1F680} Upgrade to unlock remote execution:"));
3364
+ console.log("");
3365
+ console.log(GRAY4(" Starter \u2014 15s polling, 15 min idle timeout"));
3366
+ console.log(GRAY4(" Pro \u2014 10s polling, sleep mode, 1 hour idle timeout"));
3367
+ console.log(GRAY4(" Power \u2014 2s polling, sleep mode, never disconnects"));
3368
+ console.log("");
3369
+ console.log(CYAN2(" Upgrade at: https://bobs-workshop.web.app/#/pricing"));
3370
+ }
3371
+ console.log("");
3372
+ return;
3373
+ }
3374
+ const tierConfig = TIER_CONFIGS[userTier] || TIER_CONFIGS["Starter"];
3375
+ const machineId = os3.hostname();
3376
+ const projectName = path10.basename(process.cwd());
3377
+ const sessionId = `${machineId}_${Date.now()}`;
3378
+ await startActiveBob(config, sessionId, machineId, projectName, tierConfig, userTier);
3379
+ });
3380
+ }
3381
+ async function startActiveBob(config, sessionId, machineId, projectName, tierConfig, userTier) {
3382
+ console.log("");
3383
+ console.log(BORDER5(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
3384
+ console.log(BORDER5(" \u2551") + CYAN2(" \u{1F310} Bob Serve \u2014 Active Bob Online ") + BORDER5("\u2551"));
3385
+ console.log(BORDER5(" \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"));
3386
+ console.log(BORDER5(" \u2551") + GRAY4(` Account: ${config.email}`));
3387
+ console.log(BORDER5(" \u2551") + GRAY4(` Machine: ${machineId}`));
3388
+ console.log(BORDER5(" \u2551") + GRAY4(` Project: ${projectName} (${process.cwd()})`));
3389
+ console.log(BORDER5(" \u2551") + GRAY4(` Session: ${sessionId.slice(0, 30)}...`));
3390
+ console.log(BORDER5(" \u2551") + GRAY4(` Convo: ${config.conversationId?.slice(0, 24)}...`));
3391
+ console.log(BORDER5(" \u2551") + AMBER6(` Tier: ${userTier}`));
3392
+ console.log(BORDER5(" \u2551") + GRAY4(` Polling: every ${tierConfig.activeInterval / 1e3}s`));
3393
+ if (tierConfig.sleepInterval) {
3394
+ console.log(BORDER5(" \u2551") + GRAY4(` Sleep: every ${tierConfig.sleepInterval / 1e3}s after ${tierConfig.idleThreshold / 6e4} min idle`));
3395
+ } else {
3396
+ console.log(BORDER5(" \u2551") + GRAY4(` Sleep: disabled`));
3397
+ }
3398
+ if (tierConfig.extendedIdleTimeout) {
3399
+ console.log(BORDER5(" \u2551") + GRAY4(` Auto-exit: after ${tierConfig.extendedIdleTimeout / 6e4} min idle`));
3400
+ } else {
3401
+ console.log(BORDER5(" \u2551") + GREEN4(` Auto-exit: never (Power tier)`));
3402
+ }
3403
+ console.log(BORDER5(" \u2551"));
3404
+ console.log(BORDER5(" \u2551") + GRAY4(" Press Ctrl+C to stop."));
3405
+ console.log(BORDER5(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
3406
+ console.log("");
3407
+ try {
3408
+ await callCloudFunction("registerRemoteDaemonSession", {
3409
+ conversationId: config.conversationId,
3410
+ sessionId,
3411
+ machineId,
3412
+ projectName,
3413
+ projectPath: process.cwd(),
3414
+ localEndpoint: config.localEndpoint
3415
+ });
3416
+ console.log(GREEN4(" \u2705 Active Bob registered. Listening for commands..."));
3417
+ console.log("");
3418
+ } catch (error) {
3419
+ console.log(RED3(` \u274C Failed to register: ${error.message}`));
3420
+ return;
3421
+ }
3422
+ let running = true;
3423
+ const cleanup = async () => {
3424
+ running = false;
3425
+ console.log("");
3426
+ console.log(GRAY4(" \u{1F50C} Shutting down Active Bob..."));
3427
+ try {
3428
+ await callCloudFunction("deregisterRemoteDaemonSession", {
3429
+ conversationId: config.conversationId,
3430
+ sessionId
3431
+ });
3432
+ console.log(GRAY4(" \u2705 Session deregistered. Bob is offline."));
3433
+ } catch {
3434
+ console.log(GRAY4(" \u26A0\uFE0F Could not deregister (will timeout automatically)."));
3435
+ }
3436
+ console.log("");
3437
+ process.exit(0);
3438
+ };
3439
+ process.on("SIGINT", cleanup);
3440
+ process.on("SIGTERM", cleanup);
3441
+ let lastCommandTime = Date.now();
3442
+ let isSleeping = false;
3443
+ while (running) {
3444
+ const timeSinceLastCommand = Date.now() - lastCommandTime;
3445
+ if (tierConfig.extendedIdleTimeout && timeSinceLastCommand > tierConfig.extendedIdleTimeout) {
3446
+ console.log("");
3447
+ console.log(AMBER6(` \u23F8\uFE0F No commands received in ${Math.round(tierConfig.extendedIdleTimeout / 6e4)} minutes.`));
3448
+ console.log(GRAY4(" Active Bob is going offline. Run `bob serve` to restart."));
3449
+ console.log("");
3450
+ try {
3451
+ await callCloudFunction("deregisterRemoteDaemonSession", {
3452
+ conversationId: config.conversationId,
3453
+ sessionId
3454
+ });
3455
+ } catch {
3456
+ }
3457
+ break;
3458
+ }
3459
+ if (tierConfig.sleepInterval && tierConfig.idleThreshold) {
3460
+ const shouldSleep = timeSinceLastCommand > tierConfig.idleThreshold;
3461
+ if (shouldSleep && !isSleeping) {
3462
+ isSleeping = true;
3463
+ const timestamp = (/* @__PURE__ */ new Date()).toLocaleTimeString();
3464
+ console.log(GRAY4(` [${timestamp}] \u{1F4A4} Entering sleep mode (polling every ${tierConfig.sleepInterval / 1e3}s)...`));
3465
+ }
3466
+ }
3467
+ const currentInterval = isSleeping && tierConfig.sleepInterval ? tierConfig.sleepInterval : tierConfig.activeInterval;
3468
+ try {
3469
+ const result = await callCloudFunction("pollRemoteCommands", {
3470
+ conversationId: config.conversationId,
3471
+ sessionId
3472
+ });
3473
+ if (result?.command) {
3474
+ const cmd = result.command;
3475
+ const type = cmd.type;
3476
+ const payload = cmd.payload || {};
3477
+ if (isSleeping) {
3478
+ isSleeping = false;
3479
+ console.log(GREEN4(" \u26A1 Waking up \u2014 command received!"));
3480
+ }
3481
+ lastCommandTime = Date.now();
3482
+ const timestamp = (/* @__PURE__ */ new Date()).toLocaleTimeString();
3483
+ console.log(AMBER6(` [${timestamp}] \u23F3 Received: ${type} ${payload.message ? '"' + payload.message.slice(0, 40) + (payload.message.length > 40 ? "..." : "") + '"' : ""}`));
3484
+ const commandResult = await executeRemoteCommand(type, payload, config);
3485
+ await callCloudFunction("completeRemoteCommand", {
3486
+ conversationId: config.conversationId,
3487
+ commandId: cmd.id,
3488
+ sessionId,
3489
+ result: commandResult
3490
+ });
3491
+ const endTimestamp = (/* @__PURE__ */ new Date()).toLocaleTimeString();
3492
+ if (commandResult.success) {
3493
+ console.log(GREEN4(` [${endTimestamp}] \u2705 Completed: ${type}`));
3494
+ } else {
3495
+ console.log(RED3(` [${endTimestamp}] \u274C Failed: ${type} \u2014 ${commandResult.error || "unknown"}`));
3496
+ }
3497
+ console.log("");
3498
+ }
3499
+ } catch (error) {
3500
+ if (error.message?.includes("Session expired")) {
3501
+ console.log(RED3(" \u274C Session expired. Run `bob login` and restart `bob serve`."));
3502
+ running = false;
3503
+ }
3504
+ }
3505
+ if (running) {
3506
+ await new Promise((resolve2) => setTimeout(resolve2, currentInterval));
3507
+ }
3508
+ }
3509
+ }
3510
+ async function executeRemoteCommand(type, payload, config) {
3511
+ switch (type) {
3512
+ case "chat":
3513
+ return await executeChat(payload, config);
3514
+ case "consult":
3515
+ return await executeConsult(payload, config);
3516
+ case "push":
3517
+ return await executePush(payload);
3518
+ case "index":
3519
+ return { success: true, message: "Index command received. Feature in progress." };
3520
+ case "analyse":
3521
+ return { success: true, message: "Analyse command received. Feature in progress." };
3522
+ case "autonomy":
3523
+ return { success: true, message: "Autonomy command received. Feature in progress." };
3524
+ default:
3525
+ return { success: false, error: `Unknown command type: ${type}` };
3526
+ }
3527
+ }
3528
+ async function executeChat(payload, config) {
3529
+ const { message } = payload;
3530
+ if (!message) return { success: false, error: "No message provided." };
3531
+ try {
3532
+ const localContext = buildLocalContext(process.cwd());
3533
+ let relevantFiles = "";
3534
+ let selectedFiles = [];
3535
+ if (config.localEndpoint) {
3536
+ const retrieval = await getRelevantFileContents(message, config.localEndpoint);
3537
+ relevantFiles = retrieval.fileContents;
3538
+ selectedFiles = retrieval.selectedFiles;
3539
+ }
3540
+ let fullContext = localContext;
3541
+ if (relevantFiles) fullContext += `
3542
+
3543
+ ${relevantFiles}`;
3544
+ const messages = [
3545
+ { role: "system", content: STANDARD_STYLE_PROMPT + (fullContext ? `
3546
+
3547
+ ## PROJECT CONTEXT ##
3548
+ ${fullContext}` : "") },
3549
+ { role: "user", content: message }
3550
+ ];
3551
+ const response = await callLocalModel(config.localEndpoint, messages);
3552
+ const proposed = extractProposedFile(response);
3553
+ if (proposed) {
3554
+ await proposeAndWriteFile(proposed, true);
3555
+ }
3556
+ return {
3557
+ success: true,
3558
+ text: response,
3559
+ referencedFiles: selectedFiles,
3560
+ fileProposed: proposed ? proposed.filePath : null
3561
+ };
3562
+ } catch (error) {
3563
+ return { success: false, error: error.message };
3564
+ }
3565
+ }
3566
+ async function executeConsult(payload, config) {
3567
+ const { message } = payload;
3568
+ if (!message) return { success: false, error: "No message provided." };
3569
+ try {
3570
+ const localContext = buildLocalContext(process.cwd());
3571
+ let relevantFiles = "";
3572
+ let selectedFiles = [];
3573
+ if (config.localEndpoint) {
3574
+ const retrieval = await getRelevantFileContents(message, config.localEndpoint);
3575
+ relevantFiles = retrieval.fileContents;
3576
+ selectedFiles = retrieval.selectedFiles;
3577
+ }
3578
+ let fullContext = localContext;
3579
+ if (relevantFiles) fullContext += `
3580
+
3581
+ ${relevantFiles}`;
3582
+ const messages = [
3583
+ { role: "system", content: CONSULTANT_STYLE_PROMPT + (fullContext ? `
3584
+
3585
+ ## PROJECT CONTEXT ##
3586
+ ${fullContext}` : "") },
3587
+ { role: "user", content: message }
3588
+ ];
3589
+ const response = await callLocalModel(config.localEndpoint, messages);
3590
+ return {
3591
+ success: true,
3592
+ text: response,
3593
+ referencedFiles: selectedFiles
3594
+ };
3595
+ } catch (error) {
3596
+ return { success: false, error: error.message };
3597
+ }
3598
+ }
3599
+ async function executePush(payload) {
3600
+ const { message } = payload;
3601
+ if (!message) return { success: false, error: "No commit message provided." };
3602
+ try {
3603
+ const simpleGit3 = (await import("simple-git")).default;
3604
+ const git = simpleGit3(process.cwd());
3605
+ const isRepo = await git.checkIsRepo();
3606
+ if (!isRepo) return { success: false, error: "Not a git repository." };
3607
+ const status = await git.status();
3608
+ if (status.files.length === 0) return { success: true, message: "Nothing to commit. Working tree clean." };
3609
+ await git.add(".");
3610
+ const commitResult = await git.commit(message);
3611
+ const branch = (await git.branchLocal()).current;
3612
+ try {
3613
+ await git.push("origin", branch);
3614
+ } catch (pushErr) {
3615
+ if (pushErr.message?.includes("no upstream")) {
3616
+ await git.push(["--set-upstream", "origin", branch]);
3617
+ } else {
3618
+ throw pushErr;
3619
+ }
3620
+ }
3621
+ return {
3622
+ success: true,
3623
+ message: `Pushed to ${branch}`,
3624
+ commit: commitResult.commit?.slice(0, 7),
3625
+ filesChanged: status.files.length
3626
+ };
3627
+ } catch (error) {
3628
+ return { success: false, error: error.message };
3629
+ }
3630
+ }
3631
+
3632
+ // src/commands/remote.ts
3633
+ import chalk18 from "chalk";
3634
+ import ora9 from "ora";
3635
+ import * as readline7 from "readline";
3636
+ var GREEN5 = chalk18.hex("#66BB6A");
3637
+ var AMBER7 = chalk18.hex("#FFAB00");
3638
+ var RED4 = chalk18.hex("#EF5350");
3639
+ var GRAY5 = chalk18.gray;
3640
+ var CYAN3 = chalk18.cyan;
3641
+ var BORDER6 = chalk18.hex("#455A64");
3642
+ function registerRemoteCommand(program2) {
3643
+ program2.command("remote [type] [message]").description("Send commands to an Active Bob on a remote machine").option("--new", "Discover and connect to a different Active Bob").option("--auto", "For analyse: run auto-fix mode").option("-i, --interactive", "Enter interactive remote session").option("--session <id>", "Target a specific Active Bob session").action(async (type, message, options) => {
3644
+ const config = getConfig();
3645
+ if (!config.loggedIn || !config.authToken) {
3646
+ console.log("");
3647
+ console.log(RED4(" \u274C Not logged in."));
3648
+ console.log(GRAY5(" Run `bob login` to authenticate."));
3649
+ console.log("");
3650
+ return;
3651
+ }
3652
+ if (options.new || !config.conversationId) {
3653
+ await discoverAndConnect(config);
3654
+ return;
3655
+ }
3656
+ if (options.interactive || !type && !options.new) {
3657
+ await runInteractiveRemote(config, options.session);
3658
+ return;
3659
+ }
3660
+ if (!type) {
3661
+ await showConnectionStatus(config);
3662
+ return;
3663
+ }
3664
+ const validTypes = ["chat", "consult", "index", "analyse", "push", "autonomy"];
3665
+ if (!validTypes.includes(type)) {
3666
+ console.log("");
3667
+ console.log(RED4(` \u274C Invalid command type: "${type}"`));
3668
+ console.log(GRAY5(` Valid types: ${validTypes.join(", ")}`));
3669
+ console.log("");
3670
+ return;
3671
+ }
3672
+ const payload = { conversationId: config.conversationId };
3673
+ if (message) payload.message = message;
3674
+ if (options.auto) payload.auto = true;
3675
+ if ((type === "chat" || type === "consult" || type === "push") && !message) {
3676
+ console.log("");
3677
+ console.log(RED4(` \u274C ${type} requires a message.`));
3678
+ console.log(GRAY5(` Example: bob remote ${type} "your message here"`));
3679
+ console.log("");
3680
+ return;
3681
+ }
3682
+ await dispatchCommand(config, type, payload, options.session);
3683
+ });
3684
+ }
3685
+ async function runInteractiveRemote(config, targetSession) {
3686
+ const spinner = ora9({ text: CYAN3(" Connecting to Active Bob..."), spinner: "dots" }).start();
3687
+ let activeBobName = "Unknown";
3688
+ try {
3689
+ const result = await callCloudFunction("listActiveBobs", {
3690
+ conversationId: config.conversationId
3691
+ });
3692
+ const sessions = (result?.sessions || []).filter((s) => s.active);
3693
+ if (sessions.length === 0) {
3694
+ spinner.stop();
3695
+ console.log("");
3696
+ console.log(RED4(" \u{1F534} No Active Bob found on this conversation."));
3697
+ console.log(GRAY5(" Run `bob serve` on the target machine first."));
3698
+ console.log("");
3699
+ return;
3700
+ }
3701
+ activeBobName = sessions[0].machineId || "Active Bob";
3702
+ spinner.stop();
3703
+ } catch (error) {
3704
+ spinner.stop();
3705
+ console.log(RED4(` \u274C ${error.message}`));
3706
+ return;
3707
+ }
3708
+ console.log("");
3709
+ console.log(BORDER6(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
3710
+ console.log(BORDER6(" \u2551") + CYAN3(` \u{1F310} Active Bob \u2014 Remote Session (${activeBobName})`) + BORDER6(""));
3711
+ console.log(BORDER6(" \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"));
3712
+ console.log(BORDER6(" \u2551") + GRAY5(` Conversation: ${config.conversationId?.slice(0, 28)}...`));
3713
+ console.log(BORDER6(" \u2551") + GRAY5(" Commands dispatched to the remote machine."));
3714
+ console.log(BORDER6(" \u2551") + GRAY5(" Type your message. /exit to disconnect."));
3715
+ console.log(BORDER6(" \u2551") + GRAY5(' /consult "msg" for consultant mode.'));
3716
+ console.log(BORDER6(" \u2551") + GRAY5(' /push "msg" to git push remotely.'));
3717
+ console.log(BORDER6(" \u2551") + GRAY5(" /index to re-index remotely."));
3718
+ console.log(BORDER6(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
3719
+ console.log("");
3720
+ const rl = readline7.createInterface({
3721
+ input: process.stdin,
3722
+ output: process.stdout
3723
+ });
3724
+ const prompt = () => {
3725
+ rl.question(chalk18.green(" You (remote): "), async (input) => {
3726
+ const trimmed = input.trim();
3727
+ if (!trimmed) {
3728
+ prompt();
3729
+ return;
3730
+ }
3731
+ if (trimmed === "/exit" || trimmed === "/quit") {
3732
+ console.log("");
3733
+ console.log(GRAY5(" \u{1F4E1} Disconnected from remote session."));
3734
+ console.log("");
3735
+ rl.close();
3736
+ return;
3737
+ }
3738
+ if (trimmed.startsWith("/consult ")) {
3739
+ const msg = trimmed.slice(9).trim().replace(/^["']|["']$/g, "");
3740
+ if (msg) {
3741
+ await dispatchAndShow(config, "consult", { message: msg, conversationId: config.conversationId }, targetSession);
3742
+ } else {
3743
+ console.log(RED4(' \u274C Provide a message: /consult "your question"'));
3744
+ }
3745
+ prompt();
3746
+ return;
3747
+ }
3748
+ if (trimmed.startsWith("/push ")) {
3749
+ const msg = trimmed.slice(6).trim().replace(/^["']|["']$/g, "");
3750
+ if (msg) {
3751
+ await dispatchAndShow(config, "push", { message: msg, conversationId: config.conversationId }, targetSession);
3752
+ } else {
3753
+ console.log(RED4(' \u274C Provide a commit message: /push "your message"'));
3754
+ }
3755
+ prompt();
3756
+ return;
3757
+ }
3758
+ if (trimmed === "/index") {
3759
+ await dispatchAndShow(config, "index", { conversationId: config.conversationId }, targetSession);
3760
+ prompt();
3761
+ return;
3762
+ }
3763
+ if (trimmed === "/analyse" || trimmed === "/analyze") {
3764
+ await dispatchAndShow(config, "analyse", { conversationId: config.conversationId }, targetSession);
3765
+ prompt();
3766
+ return;
3767
+ }
3768
+ await dispatchAndShow(config, "chat", { message: trimmed, conversationId: config.conversationId }, targetSession);
3769
+ prompt();
3770
+ });
3771
+ };
3772
+ prompt();
3773
+ }
3774
+ async function dispatchAndShow(config, type, payload, targetSession) {
3775
+ const spinner = ora9({ text: CYAN3(` \u{1F4E1} Active Bob executing: ${type}...`), spinner: "dots" }).start();
3776
+ try {
3777
+ const result = await callCloudFunction("sendRemoteCommand", {
3778
+ conversationId: config.conversationId,
3779
+ type,
3780
+ payload,
3781
+ targetSession: targetSession || null
3782
+ });
3783
+ if (!result?.success) {
3784
+ spinner.stop();
3785
+ console.log(RED4(` \u274C ${result?.message || "Failed to dispatch."}`));
3786
+ console.log("");
3787
+ return;
3788
+ }
3789
+ const commandId = result.commandId;
3790
+ while (true) {
3791
+ try {
3792
+ const pollResult = await callCloudFunction("getRemoteCommandResult", {
3793
+ conversationId: config.conversationId,
3794
+ commandId
3795
+ });
3796
+ if (pollResult?.status === "completed") {
3797
+ spinner.stop();
3798
+ if (pollResult.result?.text) {
3799
+ const rendered = renderMarkdown(pollResult.result.text);
3800
+ console.log("");
3801
+ console.log(GRAY5(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
3802
+ console.log(chalk18.bold.cyan(" \u{1F916} Bob (Remote):"));
3803
+ console.log("");
3804
+ for (const line of rendered.split("\n")) {
3805
+ console.log(` ${line}`);
3806
+ }
3807
+ console.log("");
3808
+ if (pollResult.result?.referencedFiles?.length > 0) {
3809
+ console.log(GRAY5(` \u{1F4C2} Referenced: ${pollResult.result.referencedFiles.join(", ")}`));
3810
+ }
3811
+ console.log(GRAY5(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
3812
+ } else if (pollResult.result?.message) {
3813
+ console.log("");
3814
+ console.log(GREEN5(` \u2705 ${pollResult.result.message}`));
3815
+ } else if (pollResult.result?.error) {
3816
+ console.log("");
3817
+ console.log(RED4(` \u274C ${pollResult.result.error}`));
3818
+ }
3819
+ console.log("");
3820
+ return;
3821
+ }
3822
+ if (pollResult?.status === "failed") {
3823
+ spinner.stop();
3824
+ console.log("");
3825
+ console.log(RED4(` \u274C ${pollResult.result?.error || "Command failed."}`));
3826
+ console.log("");
3827
+ return;
3828
+ }
3829
+ } catch {
3830
+ }
3831
+ await new Promise((resolve2) => setTimeout(resolve2, 2e3));
3832
+ }
3833
+ } catch (error) {
3834
+ spinner.stop();
3835
+ console.log(RED4(` \u274C ${error.message}`));
3836
+ console.log("");
3837
+ }
3838
+ }
3839
+ async function showConnectionStatus(config) {
3840
+ if (!config.conversationId) {
3841
+ console.log("");
3842
+ console.log(RED4(" \u{1F534} No conversation selected."));
3843
+ console.log(GRAY5(" Run `bob remote --new` to find and connect to an Active Bob."));
3844
+ console.log("");
3845
+ return;
3846
+ }
3847
+ const spinner = ora9({ text: CYAN3(" Checking Active Bob status..."), spinner: "dots" }).start();
3848
+ try {
3849
+ const result = await callCloudFunction("listActiveBobs", {
3850
+ conversationId: config.conversationId
3851
+ });
3852
+ spinner.stop();
3853
+ const sessions = result?.sessions || [];
3854
+ const activeSessions = sessions.filter((s) => s.active);
3855
+ console.log("");
3856
+ console.log(BORDER6(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
3857
+ console.log(BORDER6(" \u2551") + CYAN3(" \u{1F310} Remote Connection Status ") + BORDER6("\u2551"));
3858
+ console.log(BORDER6(" \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"));
3859
+ console.log(BORDER6(" \u2551") + GRAY5(` Conversation: ${config.conversationId?.slice(0, 28)}...`));
3860
+ console.log(BORDER6(" \u2551"));
3861
+ if (activeSessions.length === 0) {
3862
+ console.log(BORDER6(" \u2551") + RED4(" \u{1F534} No Active Bob found on this conversation."));
3863
+ console.log(BORDER6(" \u2551") + GRAY5(" Run `bob serve` on the target machine."));
3864
+ } else {
3865
+ for (const session of activeSessions) {
3866
+ const ago = session.lastHeartbeat ? getTimeAgo2(session.lastHeartbeat) : "unknown";
3867
+ console.log(BORDER6(" \u2551") + GREEN5(` \u{1F7E2} ${session.machineId}`) + GRAY5(` (${session.projectName}) \u2014 ${ago}`));
3868
+ }
3869
+ }
3870
+ console.log(BORDER6(" \u2551"));
3871
+ console.log(BORDER6(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
3872
+ console.log("");
3873
+ if (activeSessions.length > 0) {
3874
+ console.log(GRAY5(" Commands:"));
3875
+ console.log(GRAY5(" bob remote --interactive \u2014 persistent session"));
3876
+ console.log(GRAY5(' bob remote chat "message" \u2014 one-shot'));
3877
+ console.log(GRAY5(' bob remote consult "msg" \u2014 strategic advice'));
3878
+ console.log(GRAY5(' bob remote push "msg" \u2014 git push'));
3879
+ console.log(GRAY5(" bob remote index \u2014 re-index"));
3880
+ console.log(GRAY5(" bob remote analyse \u2014 run analysis"));
3881
+ console.log("");
3882
+ }
3883
+ } catch (error) {
3884
+ spinner.stop();
3885
+ console.log(RED4(` \u274C ${error.message}`));
3886
+ console.log("");
3887
+ }
3888
+ }
3889
+ async function discoverAndConnect(config) {
3890
+ const spinner = ora9({ text: CYAN3(" Searching for Active Bobs..."), spinner: "dots" }).start();
3891
+ try {
3892
+ const result = await callCloudFunction("listActiveBobs", {});
3893
+ spinner.stop();
3894
+ const bobs = result?.activeBobs || [];
3895
+ if (bobs.length === 0) {
3896
+ console.log("");
3897
+ console.log(AMBER7(" \u26A0\uFE0F No Active Bobs found."));
3898
+ console.log(GRAY5(" Run `bob serve` on a machine to start an Active Bob."));
3899
+ console.log("");
3900
+ return;
3901
+ }
3902
+ console.log("");
3903
+ console.log(BORDER6(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
3904
+ console.log(BORDER6(" \u2551") + CYAN3(" \u{1F310} Active Bobs Available ") + BORDER6("\u2551"));
3905
+ console.log(BORDER6(" \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"));
3906
+ for (let i = 0; i < bobs.length; i++) {
3907
+ const bob = bobs[i];
3908
+ const ago = bob.lastHeartbeat ? getTimeAgo2(bob.lastHeartbeat) : "unknown";
3909
+ console.log(BORDER6(" \u2551") + ` ${chalk18.cyan(String(i + 1).padStart(2))}. ${GREEN5("\u{1F7E2}")} ${chalk18.white(bob.machineId)} \u2014 ${GRAY5(bob.projectName)}`);
3910
+ console.log(BORDER6(" \u2551") + GRAY5(` Convo: ${bob.conversationTitle || bob.conversationId?.slice(0, 20) + "..."} | ${ago}`));
3911
+ }
3912
+ console.log(BORDER6(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
3913
+ console.log("");
3914
+ const rl = readline7.createInterface({ input: process.stdin, output: process.stdout });
3915
+ const answer = await new Promise((resolve2) => {
3916
+ rl.question(chalk18.cyan(" Select (1-" + bobs.length + ") or 0 to cancel: "), resolve2);
3917
+ });
3918
+ rl.close();
3919
+ const selection = parseInt(answer.trim());
3920
+ if (isNaN(selection) || selection === 0 || selection < 1 || selection > bobs.length) {
3921
+ console.log(GRAY5(" Cancelled."));
3922
+ return;
3923
+ }
3924
+ const selected = bobs[selection - 1];
3925
+ setConfigValue("conversationId", selected.conversationId);
3926
+ console.log("");
3927
+ console.log(GREEN5(` \u2705 Connected to: ${selected.machineId} (${selected.projectName})`));
3928
+ console.log(GRAY5(` Conversation: ${selected.conversationId?.slice(0, 24)}...`));
3929
+ console.log(GRAY5(" Run `bob remote --interactive` for a persistent session."));
3930
+ console.log("");
3931
+ } catch (error) {
3932
+ spinner.stop();
3933
+ console.log(RED4(` \u274C ${error.message}`));
3934
+ console.log("");
3935
+ }
3936
+ }
3937
+ async function dispatchCommand(config, type, payload, targetSession) {
3938
+ await dispatchAndShow(config, type, payload, targetSession);
3939
+ }
3940
+ function getTimeAgo2(isoDate) {
3941
+ const now = Date.now();
3942
+ const then = new Date(isoDate).getTime();
3943
+ const diffMs = now - then;
3944
+ const diffMins = Math.floor(diffMs / 6e4);
3945
+ const diffHours = Math.floor(diffMs / 36e5);
3946
+ if (diffMins < 1) return "just now";
3947
+ if (diffMins < 60) return `${diffMins}m ago`;
3948
+ if (diffHours < 24) return `${diffHours}h ago`;
3949
+ return `${Math.floor(diffHours / 24)}d ago`;
3950
+ }
3951
+
3952
+ // src/commands/profile.ts
3953
+ import chalk21 from "chalk";
3954
+ import ora12 from "ora";
3955
+
3956
+ // src/core/cloud-profiler.ts
3957
+ import chalk19 from "chalk";
3958
+ import ora10 from "ora";
3959
+ var AMBER8 = chalk19.hex("#FFAB00");
3960
+ var ORANGE3 = chalk19.hex("#E66F24");
3961
+ var GREEN6 = chalk19.hex("#66BB6A");
3962
+ var CYAN4 = chalk19.cyan;
3963
+ var RED5 = chalk19.hex("#EF5350");
3964
+ var GRAY6 = chalk19.gray;
3965
+ var WHITE2 = chalk19.white;
3966
+ var BORDER7 = chalk19.hex("#455A64");
3967
+ var POLL_INTERVAL = 3e3;
3968
+ function renderProgressBar(score, width = 30) {
3969
+ const filled = Math.round(score / 100 * width);
3970
+ const empty = width - filled;
3971
+ let barColor;
3972
+ if (score >= 75) barColor = chalk19.hex("#66BB6A");
3973
+ else if (score >= 50) barColor = chalk19.hex("#FFAB00");
3974
+ else if (score >= 25) barColor = chalk19.hex("#E66F24");
3975
+ else barColor = chalk19.hex("#EF5350");
3976
+ const filledBar = barColor("\u2588".repeat(filled));
3977
+ const emptyBar = chalk19.hex("#333333")("\u2591".repeat(empty));
3978
+ return `${filledBar}${emptyBar} ${barColor(`${score}/100`)}`;
3979
+ }
3980
+ function renderChunkBar(current, total, width = 30) {
3981
+ const filled = Math.round(current / total * width);
3982
+ const empty = width - filled;
3983
+ return AMBER8("\u2588".repeat(filled)) + chalk19.hex("#333333")("\u2591".repeat(empty)) + GRAY6(` ${current}/${total}`);
3984
+ }
3985
+ function truncate2(text, max) {
3986
+ if (!text) return "";
3987
+ if (text.length <= max) return text;
3988
+ return text.substring(0, max - 3) + "...";
3989
+ }
3990
+ function renderBox(icon, title, lines) {
3991
+ console.log("");
3992
+ console.log(BORDER7(" \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510"));
3993
+ console.log(BORDER7(" \u2502") + ` ${icon} ${WHITE2(title)}`);
3994
+ for (const line of lines) {
3995
+ console.log(BORDER7(" \u2502") + ` ${line}`);
3996
+ }
3997
+ console.log(BORDER7(" \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518"));
3998
+ }
3999
+ function getEmotionEmoji(emotion) {
4000
+ const lower = (emotion || "").toLowerCase();
4001
+ if (lower.includes("anger") || lower.includes("angry")) return "\u{1F620}";
4002
+ if (lower.includes("happiness") || lower.includes("happy") || lower.includes("joy")) return "\u{1F60A}";
4003
+ if (lower.includes("sadness") || lower.includes("sad")) return "\u{1F622}";
4004
+ if (lower.includes("fear") || lower.includes("anxiety")) return "\u{1F630}";
4005
+ if (lower.includes("surprise")) return "\u{1F632}";
4006
+ if (lower.includes("disgust")) return "\u{1F922}";
4007
+ if (lower.includes("contempt")) return "\u{1F624}";
4008
+ if (lower.includes("pride")) return "\u{1F60F}";
4009
+ if (lower.includes("gratitude")) return "\u{1F64F}";
4010
+ if (lower.includes("frustrat")) return "\u{1F624}";
4011
+ if (lower.includes("flow")) return "\u26A1";
4012
+ if (lower.includes("curiosity") || lower.includes("curious")) return "\u{1F9D0}";
4013
+ if (lower.includes("satisfaction") || lower.includes("accomplish")) return "\u2705";
4014
+ if (lower.includes("burnout")) return "\u{1F525}";
4015
+ if (lower.includes("imposter")) return "\u{1F3AD}";
4016
+ if (lower.includes("confusion") || lower.includes("confused")) return "\u{1F635}";
4017
+ return "\u25C9";
4018
+ }
4019
+ function capitalize(text) {
4020
+ if (!text) return "";
4021
+ return text.charAt(0).toUpperCase() + text.slice(1);
4022
+ }
4023
+ async function runCloudProfiler(options) {
4024
+ const { scope } = options;
4025
+ const config = getConfig();
4026
+ if (!isAuthenticated()) {
4027
+ throw new Error("Cloud profiling requires authentication. Run `bob login` first.");
4028
+ }
4029
+ console.log("");
4030
+ console.log(AMBER8(` \u{1F9EC} Running cloud ${scope} profiling (Power tier)...`));
4031
+ console.log("");
4032
+ const spinner = ora10({ text: CYAN4(" Initiating profiling job..."), spinner: "dots" }).start();
4033
+ const startResult = await callCloudFunction("startCloudProfiling", {
4034
+ scope,
4035
+ isLocalModel: false
4036
+ });
4037
+ if (!startResult?.success || !startResult?.jobPath) {
4038
+ spinner.fail(RED5(" \u274C Failed to start profiling job."));
4039
+ throw new Error("Failed to start profiling job.");
4040
+ }
4041
+ const jobPath = startResult.jobPath;
4042
+ const jobId = startResult.jobId;
4043
+ spinner.succeed(GREEN6(` \u{1F680} Job started: ${chalk19.gray(jobId.slice(0, 30))}`));
4044
+ let lastSeenLines = 0;
4045
+ let currentChunks = 0;
4046
+ let totalChunks = 0;
4047
+ let chunkBarRendered = false;
4048
+ let dataBoxRendered = false;
4049
+ let stage1Rendered = false;
4050
+ let stage2Rendered = false;
4051
+ let stage3Rendered = false;
4052
+ let stage4Rendered = false;
4053
+ let weeklyBehavioralRendered = false;
4054
+ let weeklyDecisionRendered = false;
4055
+ let weeklySynthesisRendered = false;
4056
+ let monthlyRendered = false;
4057
+ let activeSpinner = null;
4058
+ while (true) {
4059
+ await sleep4(POLL_INTERVAL);
4060
+ let jobData;
4061
+ try {
4062
+ jobData = await callCloudFunction("getCloudProfilingStatus", { jobPath });
4063
+ } catch (error) {
4064
+ continue;
4065
+ }
4066
+ if (!jobData) continue;
4067
+ const { status, statusLines, error: jobError } = jobData;
4068
+ if (statusLines && statusLines.length > lastSeenLines) {
4069
+ for (let i = lastSeenLines; i < statusLines.length; i++) {
4070
+ const line = statusLines[i];
4071
+ const msg = line.message || "";
4072
+ if (msg.includes("Found") && msg.includes("messages") && !dataBoxRendered) {
4073
+ if (activeSpinner) {
4074
+ activeSpinner.stop();
4075
+ activeSpinner = null;
4076
+ }
4077
+ const msgMatch = msg.match(/Found (\d+) messages \((\d+) conversation, (\d+) deep dive\)/);
4078
+ if (msgMatch) {
4079
+ renderBox("\u{1F4E5}", "Collecting Data", [
4080
+ GRAY6(`Found ${WHITE2(msgMatch[1])} messages (${msgMatch[2]} \u{1F4AC} | ${msgMatch[3]} \u{1F93F})`)
4081
+ ]);
4082
+ }
4083
+ dataBoxRendered = true;
4084
+ continue;
4085
+ }
4086
+ if (msg.includes("Processing chunk")) {
4087
+ const chunkMatch = msg.match(/Processing chunk (\d+)\/(\d+)/);
4088
+ if (chunkMatch) {
4089
+ currentChunks = parseInt(chunkMatch[1]);
4090
+ totalChunks = parseInt(chunkMatch[2]);
4091
+ if (!chunkBarRendered) {
4092
+ process.stdout.write(BORDER7(" \u2502") + ` Summarizing: `);
4093
+ chunkBarRendered = true;
4094
+ }
4095
+ process.stdout.write(`\r${BORDER7(" \u2502")} Summarizing: ${renderChunkBar(currentChunks, totalChunks)}`);
4096
+ if (currentChunks === totalChunks) {
4097
+ process.stdout.write("\n");
4098
+ console.log(BORDER7(" \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518"));
4099
+ }
4100
+ }
4101
+ continue;
4102
+ }
4103
+ if (msg.includes("Summarizing") && msg.includes("chunks")) continue;
4104
+ if (msg.includes("Summarization complete")) continue;
4105
+ if (msg.includes("Profiling job created")) continue;
4106
+ if (msg.includes("Scanning conversations")) {
4107
+ activeSpinner = ora10({ text: GRAY6(" Scanning conversations..."), spinner: "dots" }).start();
4108
+ continue;
4109
+ }
4110
+ if (msg.includes("Running emotion analyzers")) {
4111
+ if (activeSpinner) {
4112
+ activeSpinner.stop();
4113
+ activeSpinner = null;
4114
+ }
4115
+ activeSpinner = ora10({ text: CYAN4(" Analyzing emotions (primary + social + cognitive)..."), spinner: "dots" }).start();
4116
+ continue;
4117
+ }
4118
+ if (msg.includes("Stage 1 complete") && !stage1Rendered) {
4119
+ if (activeSpinner) {
4120
+ activeSpinner.stop();
4121
+ activeSpinner = null;
4122
+ }
4123
+ const primaryMatch = msg.match(/Primary=(\w+)/);
4124
+ const socialMatch = msg.match(/Social=(\w+)/);
4125
+ const cognitiveMatch = msg.match(/Cognitive=([^,]+)/);
4126
+ const primary = primaryMatch ? primaryMatch[1] : "analyzed";
4127
+ const social = socialMatch ? socialMatch[1] : "analyzed";
4128
+ const cognitive = cognitiveMatch ? truncate2(cognitiveMatch[1], 45) : "analyzed";
4129
+ renderBox("\u{1F9E0}", "Stage 1: Emotion Analysis", [
4130
+ `${GRAY6("Primary:")} ${getEmotionEmoji(primary)} ${WHITE2(primary)}`,
4131
+ `${GRAY6("Social:")} ${getEmotionEmoji(social)} ${WHITE2(social)}`,
4132
+ `${GRAY6("Cognitive:")} ${WHITE2(cognitive)}`
4133
+ ]);
4134
+ stage1Rendered = true;
4135
+ continue;
4136
+ }
4137
+ if (msg.includes("Synthesizing unified mood")) {
4138
+ activeSpinner = ora10({ text: CYAN4(" Synthesizing mood..."), spinner: "dots" }).start();
4139
+ continue;
4140
+ }
4141
+ if (msg.includes("Stage 2 complete") && !stage2Rendered) {
4142
+ if (activeSpinner) {
4143
+ activeSpinner.stop();
4144
+ activeSpinner = null;
4145
+ }
4146
+ const moodMatch = msg.match(/Mood=([^(]+)/);
4147
+ const scoreMatch = msg.match(/score: (\d+)\/100/);
4148
+ const mood = moodMatch ? truncate2(moodMatch[1].trim(), 50) : "synthesized";
4149
+ const score = scoreMatch ? parseInt(scoreMatch[1]) : 0;
4150
+ renderBox("\u{1F321}\uFE0F", "Stage 2: Mood Synthesis", [
4151
+ `${GRAY6("Mood:")} ${WHITE2(mood)}`,
4152
+ `${GRAY6("Score:")} ${renderProgressBar(score)}`
4153
+ ]);
4154
+ stage2Rendered = true;
4155
+ continue;
4156
+ }
4157
+ if (msg.includes("Auditing work patterns")) {
4158
+ activeSpinner = ora10({ text: CYAN4(" Auditing work patterns..."), spinner: "dots" }).start();
4159
+ continue;
4160
+ }
4161
+ if (msg.includes("Stage 3 complete") && !stage3Rendered) {
4162
+ if (activeSpinner) {
4163
+ activeSpinner.stop();
4164
+ activeSpinner = null;
4165
+ }
4166
+ const styleMatch = msg.match(/Work Style=([^,]+)/);
4167
+ const prodMatch = msg.match(/Productivity=(\d+)\/100/);
4168
+ const style = styleMatch ? truncate2(styleMatch[1].trim(), 45) : "audited";
4169
+ const productivity = prodMatch ? parseInt(prodMatch[1]) : 0;
4170
+ renderBox("\u{1F4CA}", "Stage 3: Behavioral Audit", [
4171
+ `${GRAY6("Style:")} ${WHITE2(style)}`,
4172
+ `${GRAY6("Productivity:")} ${renderProgressBar(productivity)}`
4173
+ ]);
4174
+ stage3Rendered = true;
4175
+ continue;
4176
+ }
4177
+ if (msg.includes("Profiling decision-making")) {
4178
+ activeSpinner = ora10({ text: CYAN4(" Profiling decisions..."), spinner: "dots" }).start();
4179
+ continue;
4180
+ }
4181
+ if (msg.includes("Stage 4 complete") && !stage4Rendered) {
4182
+ if (activeSpinner) {
4183
+ activeSpinner.stop();
4184
+ activeSpinner = null;
4185
+ }
4186
+ const archMatch = msg.match(/Archetype=([^—]+)/);
4187
+ const descMatch = msg.match(/—\s*(.+)/);
4188
+ const archetype = archMatch ? archMatch[1].trim() : "profiled";
4189
+ const description = descMatch ? truncate2(descMatch[1].trim(), 50) : "";
4190
+ renderBox("\u{1F3AF}", "Stage 4: Decision Profile", [
4191
+ `${GRAY6("Archetype:")} ${AMBER8(archetype)}`,
4192
+ description ? `${GRAY6('"' + description + '"')}` : ""
4193
+ ].filter(Boolean));
4194
+ stage4Rendered = true;
4195
+ continue;
4196
+ }
4197
+ if (msg.includes("Analyzing behavioral patterns across the week")) {
4198
+ if (activeSpinner) {
4199
+ activeSpinner.stop();
4200
+ activeSpinner = null;
4201
+ }
4202
+ activeSpinner = ora10({ text: CYAN4(" Analyzing weekly behavioral patterns..."), spinner: "dots" }).start();
4203
+ continue;
4204
+ }
4205
+ if (msg.includes("Found") && msg.includes("daily audits")) {
4206
+ if (activeSpinner) {
4207
+ activeSpinner.stop();
4208
+ activeSpinner = null;
4209
+ }
4210
+ const countMatch = msg.match(/Found (\d+) daily audits/);
4211
+ const count = countMatch ? countMatch[1] : "?";
4212
+ activeSpinner = ora10({ text: CYAN4(` Synthesizing from ${count} daily audits...`), spinner: "dots" }).start();
4213
+ continue;
4214
+ }
4215
+ if (msg.includes("Weekly behavioral complete") && !weeklyBehavioralRendered) {
4216
+ if (activeSpinner) {
4217
+ activeSpinner.stop();
4218
+ activeSpinner = null;
4219
+ }
4220
+ const styleMatch = msg.match(/Weekly behavioral complete: (.+)/);
4221
+ const style = styleMatch ? truncate2(styleMatch[1].trim(), 50) : "audited";
4222
+ renderBox("\u{1F4C8}", "Weekly Behavioral Audit", [
4223
+ `${GRAY6("Work Style:")} ${WHITE2(style)}`
4224
+ ]);
4225
+ weeklyBehavioralRendered = true;
4226
+ continue;
4227
+ }
4228
+ if (msg.includes("Analyzing decision evolution")) {
4229
+ activeSpinner = ora10({ text: CYAN4(" Analyzing weekly decision evolution..."), spinner: "dots" }).start();
4230
+ continue;
4231
+ }
4232
+ if (msg.includes("Weekly decision complete") && !weeklyDecisionRendered) {
4233
+ if (activeSpinner) {
4234
+ activeSpinner.stop();
4235
+ activeSpinner = null;
4236
+ }
4237
+ const archMatch = msg.match(/Archetype=([^,]+)/);
4238
+ const edgeMatch = msg.match(/Edge=(\d+)\/100/);
4239
+ const archetype = archMatch ? archMatch[1].trim() : "profiled";
4240
+ const edge = edgeMatch ? parseInt(edgeMatch[1]) : 0;
4241
+ renderBox("\u{1F3AF}", "Weekly Decision Profile", [
4242
+ `${GRAY6("Archetype:")} ${AMBER8(archetype)}`,
4243
+ `${GRAY6("Edge Score:")} ${renderProgressBar(edge)}`
4244
+ ]);
4245
+ weeklyDecisionRendered = true;
4246
+ continue;
4247
+ }
4248
+ if (msg.includes("Synthesizing weekly personality DNA")) {
4249
+ activeSpinner = ora10({ text: CYAN4(" Synthesizing weekly DNA..."), spinner: "dots" }).start();
4250
+ continue;
4251
+ }
4252
+ if (msg.includes("Weekly synthesis complete") && !weeklySynthesisRendered) {
4253
+ if (activeSpinner) {
4254
+ activeSpinner.stop();
4255
+ activeSpinner = null;
4256
+ }
4257
+ const synthMatch = msg.match(/Weekly synthesis complete: (.+)/);
4258
+ const synthesis = synthMatch ? truncate2(synthMatch[1].trim(), 50) : "synthesized";
4259
+ renderBox("\u{1F9EC}", "Weekly DNA Synthesis", [
4260
+ `${GRAY6("Archetype:")} ${AMBER8(synthesis)}`,
4261
+ `${GRAY6("Your weekly personality profile has been updated.")}`
4262
+ ]);
4263
+ weeklySynthesisRendered = true;
4264
+ continue;
4265
+ }
4266
+ if (msg.includes("Gathering weekly profiles for monthly")) {
4267
+ if (activeSpinner) {
4268
+ activeSpinner.stop();
4269
+ activeSpinner = null;
4270
+ }
4271
+ activeSpinner = ora10({ text: CYAN4(" Gathering weekly profiles..."), spinner: "dots" }).start();
4272
+ continue;
4273
+ }
4274
+ if (msg.includes("Found") && msg.includes("weekly profiles")) {
4275
+ if (activeSpinner) {
4276
+ activeSpinner.stop();
4277
+ activeSpinner = null;
4278
+ }
4279
+ const countMatch = msg.match(/Found (\d+) weekly profiles/);
4280
+ const count = countMatch ? countMatch[1] : "?";
4281
+ activeSpinner = ora10({ text: CYAN4(` Generating monthly DNA from ${count} weeks of data...`), spinner: "dots" }).start();
4282
+ continue;
4283
+ }
4284
+ if (msg.includes("Monthly synthesis complete") && !monthlyRendered) {
4285
+ if (activeSpinner) {
4286
+ activeSpinner.stop();
4287
+ activeSpinner = null;
4288
+ }
4289
+ const archMatch = msg.match(/Monthly synthesis complete: (.+)/);
4290
+ const archetype = archMatch ? truncate2(archMatch[1].trim(), 50) : "synthesized";
4291
+ renderBox("\u{1F9EC}", "Monthly DNA Synthesis", [
4292
+ `${GRAY6("Monthly Archetype:")} ${AMBER8(archetype)}`,
4293
+ `${GRAY6("Your complete monthly personality profile is now active.")}`,
4294
+ `${GRAY6("Bob will adapt to match your patterns going forward.")}`
4295
+ ]);
4296
+ monthlyRendered = true;
4297
+ continue;
4298
+ }
4299
+ if (msg.includes("profiling complete!") && msg.includes("\u2705")) continue;
4300
+ if (msg.includes("\u274C") || msg.includes("Failed")) {
4301
+ if (activeSpinner) {
4302
+ activeSpinner.stop();
4303
+ activeSpinner = null;
4304
+ }
4305
+ const cleanMsg = msg.replace("\u274C Failed: ", "").replace("\u274C ", "");
4306
+ console.log(RED5(` \u274C ${truncate2(cleanMsg, 70)}`));
4307
+ continue;
4308
+ }
4309
+ }
4310
+ lastSeenLines = statusLines.length;
4311
+ }
4312
+ if (status === "complete") {
4313
+ if (activeSpinner) {
4314
+ activeSpinner.stop();
4315
+ activeSpinner = null;
4316
+ }
4317
+ console.log("");
4318
+ console.log(GREEN6(" \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"));
4319
+ console.log(GREEN6(` \u2705 ${capitalize(scope)} DNA Profile Complete`));
4320
+ console.log(GRAY6(" Your profile has been updated."));
4321
+ console.log(GRAY6(" Personalization Mode will now use this data."));
4322
+ console.log(GREEN6(" \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"));
4323
+ console.log("");
4324
+ return;
4325
+ }
4326
+ if (status === "failed") {
4327
+ if (activeSpinner) {
4328
+ activeSpinner.stop();
4329
+ activeSpinner = null;
4330
+ }
4331
+ console.log("");
4332
+ const cleanError = (jobError || "Cloud profiling failed. Check logs for details.").replace(/firestore/gi, "database").replace(/Firestore/g, "database");
4333
+ console.log(RED5(` \u274C ${cleanError}`));
4334
+ console.log("");
4335
+ throw new Error(cleanError);
4336
+ }
4337
+ }
4338
+ }
4339
+ function sleep4(ms) {
4340
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
4341
+ }
4342
+
4343
+ // src/ui/profile-dashboard.ts
4344
+ import chalk20 from "chalk";
4345
+ import ora11 from "ora";
4346
+ var AMBER9 = chalk20.hex("#FFAB00");
4347
+ var ORANGE4 = chalk20.hex("#E66F24");
4348
+ var GREEN7 = chalk20.hex("#66BB6A");
4349
+ var CYAN5 = chalk20.cyan;
4350
+ var RED6 = chalk20.hex("#EF5350");
4351
+ var GRAY7 = chalk20.gray;
4352
+ var WHITE3 = chalk20.white;
4353
+ var BORDER8 = chalk20.hex("#455A64");
4354
+ var MAGENTA = chalk20.hex("#CE93D8");
4355
+ var GOLD = chalk20.hex("#FFD700");
4356
+ function bar(score, width = 25) {
4357
+ const numScore = typeof score === "number" ? score : parseInt(score) || 0;
4358
+ const filled = Math.round(numScore / 100 * width);
4359
+ const empty = width - filled;
4360
+ let barColor;
4361
+ if (numScore >= 75) barColor = chalk20.hex("#66BB6A");
4362
+ else if (numScore >= 50) barColor = chalk20.hex("#FFAB00");
4363
+ else if (numScore >= 25) barColor = chalk20.hex("#E66F24");
4364
+ else barColor = chalk20.hex("#EF5350");
4365
+ return `${barColor("\u2588".repeat(filled))}${chalk20.hex("#333333")("\u2591".repeat(empty))} ${barColor(`${numScore}`)}`;
4366
+ }
4367
+ function trunc(text, max) {
4368
+ if (!text) return "";
4369
+ const str = String(text);
4370
+ if (str.length <= max) return str;
4371
+ return str.substring(0, max - 3) + "...";
4372
+ }
4373
+ function emo(emotion) {
4374
+ const lower = (emotion || "").toLowerCase();
4375
+ if (lower.includes("anger")) return "\u{1F620}";
4376
+ if (lower.includes("happiness") || lower.includes("happy")) return "\u{1F60A}";
4377
+ if (lower.includes("sadness")) return "\u{1F622}";
4378
+ if (lower.includes("fear")) return "\u{1F630}";
4379
+ if (lower.includes("surprise")) return "\u{1F632}";
4380
+ if (lower.includes("disgust")) return "\u{1F922}";
4381
+ if (lower.includes("contempt")) return "\u{1F624}";
4382
+ if (lower.includes("pride")) return "\u{1F60F}";
4383
+ if (lower.includes("gratitude")) return "\u{1F64F}";
4384
+ if (lower.includes("frustrat")) return "\u{1F624}";
4385
+ if (lower.includes("flow")) return "\u26A1";
4386
+ if (lower.includes("curiosity")) return "\u{1F9D0}";
4387
+ if (lower.includes("satisfaction")) return "\u2705";
4388
+ if (lower.includes("burnout")) return "\u{1F525}";
4389
+ if (lower.includes("imposter")) return "\u{1F3AD}";
4390
+ return "\u25C9";
4391
+ }
4392
+ function trend(value) {
4393
+ if (!value) return "";
4394
+ const text = typeof value === "string" ? value : value?.direction || value?.trend || "";
4395
+ const lower = text.toLowerCase();
4396
+ if (lower.includes("undetermined") || lower.includes("insufficient") || lower.includes("unconfirmed")) return "";
4397
+ if (lower.includes("rising") || lower.includes("improving")) return chalk20.green("\u2197 " + text);
4398
+ if (lower.includes("falling") || lower.includes("declining")) return chalk20.red("\u2198 " + text);
4399
+ if (lower.includes("stable")) return chalk20.gray("\u2192 " + text);
4400
+ return "";
4401
+ }
4402
+ function extractScore(obj) {
4403
+ if (typeof obj === "number") return obj;
4404
+ if (typeof obj === "object" && obj !== null) return obj.score || obj.level || 0;
4405
+ return 0;
4406
+ }
4407
+ async function renderProfileDashboard() {
4408
+ if (!isAuthenticated()) {
4409
+ console.log("");
4410
+ console.log(RED6(" \u274C Dashboard requires authentication."));
4411
+ console.log(GRAY7(" Run `bob login` to authenticate."));
4412
+ console.log("");
4413
+ return;
4414
+ }
4415
+ const spinner = ora11({ text: CYAN5(" Loading your DNA profile..."), spinner: "dots" }).start();
4416
+ try {
4417
+ const data = await callCloudFunction("getCLIProfileDashboard", {});
4418
+ if (!data?.success) {
4419
+ spinner.fail(RED6(" \u274C Failed to load profile data."));
4420
+ return;
4421
+ }
4422
+ spinner.stop();
4423
+ const { daily, weekly, monthly } = data;
4424
+ if (!daily?.decision && !weekly?.decision && !monthly) {
4425
+ console.log("");
4426
+ console.log(AMBER9(" \u26A0\uFE0F No profile data found."));
4427
+ console.log(GRAY7(" Run `bob profile --cloud` to generate your first profile."));
4428
+ console.log("");
4429
+ return;
4430
+ }
4431
+ console.log("");
4432
+ if (daily?.decision || daily?.mood || daily?.behavioral) {
4433
+ console.log(BORDER8(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
4434
+ console.log(BORDER8(" \u2551") + CYAN5(" \u{1F4C5} DAILY PROFILE"));
4435
+ console.log(BORDER8(" \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"));
4436
+ if (daily.decision) {
4437
+ const d = daily.decision;
4438
+ console.log(BORDER8(" \u2551") + ` ${GRAY7("Archetype:")} ${AMBER9(trunc(d.dailyArchetype || "Unknown", 55))}`);
4439
+ console.log(BORDER8(" \u2551") + ` ${GRAY7("Date:")} ${WHITE3(d.profileDate || "Unknown")}`);
4440
+ if (d.psychologicalState) {
4441
+ console.log(BORDER8(" \u2551"));
4442
+ console.log(BORDER8(" \u2551") + ` ${GRAY7("Confidence:")} ${bar(d.psychologicalState.confidence || 0)}`);
4443
+ console.log(BORDER8(" \u2551") + ` ${GRAY7("Autonomy:")} ${bar(d.psychologicalState.autonomy || 0)}`);
4444
+ console.log(BORDER8(" \u2551") + ` ${GRAY7("Clarity:")} ${bar(d.psychologicalState.clarity || 0)}`);
4445
+ console.log(BORDER8(" \u2551") + ` ${GRAY7("Momentum:")} ${bar(d.psychologicalState.momentum || 0)}`);
4446
+ }
4447
+ if (d.brutallyHonestAssessment) {
4448
+ console.log(BORDER8(" \u2551"));
4449
+ console.log(BORDER8(" \u2551") + ` ${GRAY7('"' + trunc(d.brutallyHonestAssessment, 58) + '"')}`);
4450
+ }
4451
+ }
4452
+ if (daily.primaryEmotion || daily.socialEmotion || daily.cognitiveEmotion) {
4453
+ console.log(BORDER8(" \u2551"));
4454
+ console.log(BORDER8(" \u2551") + ` ${WHITE3("Emotions:")}`);
4455
+ if (daily.primaryEmotion?.dominantEmotion) {
4456
+ console.log(BORDER8(" \u2551") + ` ${GRAY7("Primary:")} ${emo(daily.primaryEmotion.dominantEmotion)} ${WHITE3(daily.primaryEmotion.dominantEmotion)}`);
4457
+ }
4458
+ if (daily.socialEmotion?.dominantSocialEmotion) {
4459
+ console.log(BORDER8(" \u2551") + ` ${GRAY7("Social:")} ${emo(daily.socialEmotion.dominantSocialEmotion)} ${WHITE3(daily.socialEmotion.dominantSocialEmotion)}`);
4460
+ }
4461
+ if (daily.cognitiveEmotion?.dominantCognitiveState) {
4462
+ console.log(BORDER8(" \u2551") + ` ${GRAY7("Cognitive:")} ${emo(daily.cognitiveEmotion.dominantCognitiveState)} ${WHITE3(trunc(daily.cognitiveEmotion.dominantCognitiveState, 40))}`);
4463
+ }
4464
+ }
4465
+ if (daily.mood) {
4466
+ console.log(BORDER8(" \u2551"));
4467
+ console.log(BORDER8(" \u2551") + ` ${WHITE3("Mood:")} ${trunc(daily.mood.unifiedMood || "", 50)}`);
4468
+ console.log(BORDER8(" \u2551") + ` ${GRAY7("Score:")} ${bar(daily.mood.moodScore || 0)}`);
4469
+ }
4470
+ if (daily.behavioral) {
4471
+ const b = daily.behavioral;
4472
+ console.log(BORDER8(" \u2551"));
4473
+ console.log(BORDER8(" \u2551") + ` ${WHITE3("Behavior:")}`);
4474
+ console.log(BORDER8(" \u2551") + ` ${GRAY7("Style:")} ${WHITE3(trunc(b.workStyle || "", 45))}`);
4475
+ console.log(BORDER8(" \u2551") + ` ${GRAY7("Productivity:")} ${bar(b.overallProductivity || 0)}`);
4476
+ if (b.consistency) console.log(BORDER8(" \u2551") + ` ${GRAY7("Consistency:")} ${bar(extractScore(b.consistency))}`);
4477
+ if (b.followThrough) console.log(BORDER8(" \u2551") + ` ${GRAY7("Follow-thru:")} ${bar(extractScore(b.followThrough))}`);
4478
+ }
4479
+ console.log(BORDER8(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
4480
+ console.log("");
4481
+ }
4482
+ if (weekly?.decision) {
4483
+ console.log(BORDER8(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
4484
+ console.log(BORDER8(" \u2551") + MAGENTA(" \u{1F4CA} WEEKLY PROFILE"));
4485
+ console.log(BORDER8(" \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"));
4486
+ const w = weekly.decision;
4487
+ console.log(BORDER8(" \u2551") + ` ${GRAY7("Archetype:")} ${AMBER9(w.archetypeOfWeek || "Unknown")}`);
4488
+ console.log(BORDER8(" \u2551") + ` ${GRAY7("Edge Score:")} ${bar(w.userEdgeScore || 0)}`);
4489
+ if (w.gritProfile) {
4490
+ const gritTrend = trend(w.gritProfile.trend || w.gritProfile);
4491
+ console.log(BORDER8(" \u2551") + ` ${GRAY7("Grit:")} ${bar(extractScore(w.gritProfile))}${gritTrend ? " " + gritTrend : ""}`);
4492
+ }
4493
+ if (w.innovationProfile) {
4494
+ const innovTrend = trend(w.innovationProfile.trend || w.innovationProfile);
4495
+ console.log(BORDER8(" \u2551") + ` ${GRAY7("Innovation:")} ${bar(extractScore(w.innovationProfile))}${innovTrend ? " " + innovTrend : ""}`);
4496
+ }
4497
+ if (w.executionProfile) {
4498
+ const execTrend = trend(w.executionProfile.trend || w.executionProfile);
4499
+ console.log(BORDER8(" \u2551") + ` ${GRAY7("Execution:")} ${bar(extractScore(w.executionProfile))}${execTrend ? " " + execTrend : ""}`);
4500
+ }
4501
+ if (w.workflowProfile?.style) {
4502
+ console.log(BORDER8(" \u2551"));
4503
+ console.log(BORDER8(" \u2551") + ` ${GRAY7("Work Style:")} ${WHITE3(trunc(w.workflowProfile.style, 45))}`);
4504
+ }
4505
+ if (w.psychologicalState?.resilience) {
4506
+ console.log(BORDER8(" \u2551") + ` ${GRAY7("Resilience:")} ${bar(w.psychologicalState.resilience)}`);
4507
+ }
4508
+ if (w.strategicAnalysis) {
4509
+ console.log(BORDER8(" \u2551"));
4510
+ if (w.strategicAnalysis.biggestWin) {
4511
+ console.log(BORDER8(" \u2551") + ` ${GREEN7("+")} ${GRAY7("Win:")} ${trunc(w.strategicAnalysis.biggestWin, 50)}`);
4512
+ }
4513
+ if (w.strategicAnalysis.biggestMiss) {
4514
+ console.log(BORDER8(" \u2551") + ` ${RED6("-")} ${GRAY7("Miss:")} ${trunc(w.strategicAnalysis.biggestMiss, 50)}`);
4515
+ }
4516
+ if (w.strategicAnalysis.blindSpot) {
4517
+ console.log(BORDER8(" \u2551") + ` ${ORANGE4("?")} ${GRAY7("Blind:")} ${trunc(w.strategicAnalysis.blindSpot, 50)}`);
4518
+ }
4519
+ if (w.strategicAnalysis.growthEdge) {
4520
+ console.log(BORDER8(" \u2551") + ` ${CYAN5("\u2197")} ${GRAY7("Growth:")} ${trunc(w.strategicAnalysis.growthEdge, 50)}`);
4521
+ }
4522
+ }
4523
+ if (w.communicationGuidance) {
4524
+ const cg = w.communicationGuidance;
4525
+ console.log(BORDER8(" \u2551"));
4526
+ console.log(BORDER8(" \u2551") + ` ${WHITE3("Communication Guidance:")}`);
4527
+ if (cg.preferredTone) {
4528
+ console.log(BORDER8(" \u2551") + ` ${GRAY7("Tone:")} ${trunc(cg.preferredTone, 52)}`);
4529
+ }
4530
+ if (cg.paceRecommendation) {
4531
+ console.log(BORDER8(" \u2551") + ` ${GRAY7("Pace:")} ${trunc(cg.paceRecommendation, 52)}`);
4532
+ }
4533
+ if (cg.avoidTopics && cg.avoidTopics.length > 0) {
4534
+ console.log(BORDER8(" \u2551") + ` ${GRAY7("Avoid:")} ${trunc(cg.avoidTopics[0], 52)}`);
4535
+ if (cg.avoidTopics.length > 1) {
4536
+ console.log(BORDER8(" \u2551") + ` ${trunc(cg.avoidTopics[1], 52)}`);
4537
+ }
4538
+ }
4539
+ if (cg.encourageTopics && cg.encourageTopics.length > 0) {
4540
+ console.log(BORDER8(" \u2551") + ` ${GRAY7("Push:")} ${trunc(cg.encourageTopics[0], 52)}`);
4541
+ if (cg.encourageTopics.length > 1) {
4542
+ console.log(BORDER8(" \u2551") + ` ${trunc(cg.encourageTopics[1], 52)}`);
4543
+ }
4544
+ }
4545
+ }
4546
+ if (w.weekSummary) {
4547
+ console.log(BORDER8(" \u2551"));
4548
+ console.log(BORDER8(" \u2551") + ` ${GRAY7('"' + trunc(w.weekSummary, 58) + '"')}`);
4549
+ }
4550
+ if (weekly.behavioral) {
4551
+ const wb = weekly.behavioral;
4552
+ const consistencyVal = trend(wb.consistencyTrend?.direction || wb.consistencyTrend);
4553
+ const followVal = trend(wb.followThroughTrend?.direction || wb.followThroughTrend);
4554
+ const focusVal = trend(wb.focusTrend?.direction || wb.focusTrend);
4555
+ if (consistencyVal || followVal || focusVal) {
4556
+ console.log(BORDER8(" \u2551"));
4557
+ console.log(BORDER8(" \u2551") + ` ${WHITE3("Weekly Behavior Trends:")}`);
4558
+ if (consistencyVal) console.log(BORDER8(" \u2551") + ` ${GRAY7("Consistency:")} ${consistencyVal}`);
4559
+ if (followVal) console.log(BORDER8(" \u2551") + ` ${GRAY7("Follow-thru:")} ${followVal}`);
4560
+ if (focusVal) console.log(BORDER8(" \u2551") + ` ${GRAY7("Focus:")} ${focusVal}`);
4561
+ }
4562
+ if (wb.weekSummary) {
4563
+ console.log(BORDER8(" \u2551"));
4564
+ console.log(BORDER8(" \u2551") + ` ${GRAY7('"' + trunc(wb.weekSummary, 58) + '"')}`);
4565
+ }
4566
+ }
4567
+ console.log(BORDER8(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
4568
+ console.log("");
4569
+ }
4570
+ if (monthly) {
4571
+ console.log(BORDER8(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
4572
+ console.log(BORDER8(" \u2551") + GOLD(" \u{1F3C6} MONTHLY DNA"));
4573
+ console.log(BORDER8(" \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"));
4574
+ console.log(BORDER8(" \u2551") + ` ${GRAY7("Archetype:")} ${AMBER9(monthly.monthlyArchetype || "Unknown")}`);
4575
+ if (monthly.trendAnalysis) {
4576
+ const gritT = trend(monthly.trendAnalysis.gritTrend);
4577
+ const innovT = trend(monthly.trendAnalysis.innovationTrend);
4578
+ const execT = trend(monthly.trendAnalysis.executionTrend);
4579
+ if (gritT || innovT || execT) {
4580
+ console.log(BORDER8(" \u2551"));
4581
+ if (gritT) console.log(BORDER8(" \u2551") + ` ${GRAY7("Grit:")} ${gritT}`);
4582
+ if (innovT) console.log(BORDER8(" \u2551") + ` ${GRAY7("Innovation:")} ${innovT}`);
4583
+ if (execT) console.log(BORDER8(" \u2551") + ` ${GRAY7("Execution:")} ${execT}`);
4584
+ }
4585
+ }
4586
+ if (monthly.trendAnalysis?.overallTrajectory) {
4587
+ console.log(BORDER8(" \u2551"));
4588
+ console.log(BORDER8(" \u2551") + ` ${GRAY7('"' + trunc(monthly.trendAnalysis.overallTrajectory, 58) + '"')}`);
4589
+ }
4590
+ if (monthly.personalityDNA) {
4591
+ const dna = monthly.personalityDNA;
4592
+ console.log(BORDER8(" \u2551"));
4593
+ console.log(BORDER8(" \u2551") + ` ${WHITE3("Personality DNA:")}`);
4594
+ if (dna.coreMotivation) console.log(BORDER8(" \u2551") + ` ${GRAY7("Motivation:")} ${trunc(dna.coreMotivation, 48)}`);
4595
+ if (dna.workIdentity) console.log(BORDER8(" \u2551") + ` ${GRAY7("Work Identity:")} ${trunc(dna.workIdentity, 48)}`);
4596
+ if (dna.stressResponse) console.log(BORDER8(" \u2551") + ` ${GRAY7("Under Stress:")} ${trunc(dna.stressResponse, 48)}`);
4597
+ if (dna.learningStyle) console.log(BORDER8(" \u2551") + ` ${GRAY7("Learning:")} ${trunc(dna.learningStyle, 48)}`);
4598
+ }
4599
+ if (monthly.psychologicalState) {
4600
+ const ps = monthly.psychologicalState;
4601
+ console.log(BORDER8(" \u2551"));
4602
+ console.log(BORDER8(" \u2551") + ` ${WHITE3("State:")}`);
4603
+ if (ps.confidence) console.log(BORDER8(" \u2551") + ` ${GRAY7("Confidence:")} ${bar(ps.confidence)}`);
4604
+ if (ps.resilience) console.log(BORDER8(" \u2551") + ` ${GRAY7("Resilience:")} ${bar(ps.resilience)}`);
4605
+ if (ps.burnoutRisk) console.log(BORDER8(" \u2551") + ` ${GRAY7("Burnout:")} ${bar(ps.burnoutRisk)}`);
4606
+ if (ps.overallWellbeing && typeof ps.overallWellbeing === "string" && ps.overallWellbeing.length < 30) {
4607
+ console.log(BORDER8(" \u2551") + ` ${GRAY7("Wellbeing:")} ${WHITE3(ps.overallWellbeing)}`);
4608
+ }
4609
+ }
4610
+ if (monthly.predictiveInsights) {
4611
+ const pi = monthly.predictiveInsights;
4612
+ console.log(BORDER8(" \u2551"));
4613
+ console.log(BORDER8(" \u2551") + ` ${WHITE3("Predictions:")}`);
4614
+ if (pi.likelyNextMonthArchetype) {
4615
+ console.log(BORDER8(" \u2551") + ` ${GRAY7("Next Month:")} ${AMBER9(trunc(pi.likelyNextMonthArchetype, 45))}`);
4616
+ }
4617
+ if (pi.communicationStrategy) {
4618
+ console.log(BORDER8(" \u2551") + ` ${GRAY7("Strategy:")} ${trunc(pi.communicationStrategy, 48)}`);
4619
+ }
4620
+ }
4621
+ if (monthly.monthSummary) {
4622
+ console.log(BORDER8(" \u2551"));
4623
+ console.log(BORDER8(" \u2551") + ` ${GRAY7('"' + trunc(monthly.monthSummary, 58) + '"')}`);
4624
+ }
4625
+ console.log(BORDER8(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
4626
+ console.log("");
4627
+ }
4628
+ console.log(BORDER8(" \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510"));
4629
+ console.log(BORDER8(" \u2502") + GRAY7(" Commands:"));
4630
+ console.log(BORDER8(" \u2502") + GRAY7(" bob profile --cloud \u2014 Refresh daily profile"));
4631
+ console.log(BORDER8(" \u2502") + GRAY7(" bob profile --cloud-weekly \u2014 Refresh weekly synthesis"));
4632
+ console.log(BORDER8(" \u2502") + GRAY7(" bob profile --cloud-monthly \u2014 Refresh monthly DNA"));
4633
+ console.log(BORDER8(" \u2502") + GRAY7(" bob chat --personalized \u2014 Chat with DNA-aware Bob"));
4634
+ console.log(BORDER8(" \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518"));
4635
+ console.log("");
4636
+ } catch (error) {
4637
+ spinner.stop();
4638
+ console.log("");
4639
+ console.log(RED6(` \u274C ${error.message}`));
4640
+ console.log("");
4641
+ }
4642
+ }
4643
+
4644
+ // src/commands/profile.ts
4645
+ import * as path11 from "path";
4646
+ var AMBER10 = chalk21.hex("#FFAB00");
4647
+ var GREEN8 = chalk21.hex("#66BB6A");
4648
+ var BLUE5 = chalk21.hex("#42A5F5");
4649
+ var GRAY8 = chalk21.gray;
4650
+ var CYAN6 = chalk21.cyan;
4651
+ var RED7 = chalk21.hex("#EF5350");
4652
+ var BORDER9 = chalk21.hex("#455A64");
4653
+ function registerProfileCommand(program2) {
4654
+ program2.command("profile").description("Generate and view your behavioral profile \u2014 how you work, think, and communicate").option("--today", "Generate today's profile from today's conversations").option("--week", "Synthesize the last 7 daily profiles into a weekly profile").option("--month", "Synthesize all dailies + weeklies into a monthly profile").option("--cloud", "Run cloud-powered profiling (Power tier only)").option("--cloud-weekly", "Run cloud weekly synthesis (Power tier only)").option("--cloud-monthly", "Run cloud monthly synthesis (Power tier only)").option("--view", "View your DNA dashboard").action(async (options) => {
4655
+ const config = getConfig();
4656
+ if (options.cloud) {
4657
+ await handleCloudProfile("daily");
4658
+ return;
4659
+ }
4660
+ if (options.cloudWeekly) {
4661
+ await handleCloudProfile("weekly");
4662
+ return;
4663
+ }
4664
+ if (options.cloudMonthly) {
4665
+ await handleCloudProfile("monthly");
4666
+ return;
4667
+ }
4668
+ if (options.view) {
4669
+ await renderProfileDashboard();
4670
+ return;
4671
+ }
4672
+ if (config.provider !== "local" || !config.localEndpoint) {
4673
+ if (isAuthenticated()) {
4674
+ await renderProfileDashboard();
4675
+ } else {
4676
+ showLocalHelp();
4677
+ }
4678
+ return;
4679
+ }
4680
+ if (options.today) {
4681
+ await generateDailyProfile(config);
4682
+ } else if (options.week) {
4683
+ await generateWeeklyProfile(config);
4684
+ } else if (options.month) {
4685
+ await generateMonthlyProfile(config);
4686
+ } else {
4687
+ if (isAuthenticated()) {
4688
+ await renderProfileDashboard();
4689
+ } else {
4690
+ showCurrentProfile();
4691
+ }
4692
+ }
4693
+ });
4694
+ }
4695
+ async function handleCloudProfile(scope) {
4696
+ if (!isAuthenticated()) {
4697
+ console.log("");
4698
+ console.log(RED7(" \u274C Cloud profiling requires authentication."));
4699
+ console.log(GRAY8(" Run `bob login` to authenticate."));
4700
+ console.log("");
4701
+ return;
4702
+ }
4703
+ console.log("");
4704
+ console.log(AMBER10(` \u{1F9EC} Running cloud ${scope} profiling (Power tier)...`));
4705
+ console.log("");
4706
+ try {
4707
+ await runCloudProfiler({
4708
+ scope,
4709
+ onProgress: (msg) => console.log(msg)
4710
+ });
4711
+ } catch (error) {
4712
+ console.log("");
4713
+ if (error.message.includes("Power tier")) {
4714
+ console.log(RED7(" \u274C Cloud profiling requires Power tier."));
4715
+ console.log(GRAY8(" Upgrade at: app.bobsworkshop.com/upgrade"));
4716
+ } else {
4717
+ console.log(RED7(` \u274C ${error.message}`));
4718
+ }
4719
+ console.log("");
4720
+ }
4721
+ }
4722
+ function showLocalHelp() {
4723
+ console.log("");
4724
+ console.log(RED7(" \u274C No profile source available."));
4725
+ console.log("");
4726
+ console.log(GRAY8(" For local profiling:"));
4727
+ console.log(GRAY8(" bob config set provider local"));
4728
+ console.log(GRAY8(" bob config set localEndpoint http://127.0.0.1:11434/api/chat"));
4729
+ console.log("");
4730
+ console.log(GRAY8(" For cloud profiling (Power tier):"));
4731
+ console.log(GRAY8(" bob login"));
4732
+ console.log(GRAY8(" bob profile --cloud \u2014 Daily cloud profile"));
4733
+ console.log(GRAY8(" bob profile --cloud-weekly \u2014 Weekly synthesis"));
4734
+ console.log(GRAY8(" bob profile --cloud-monthly \u2014 Monthly DNA"));
4735
+ console.log("");
4736
+ }
4737
+ function showCurrentProfile() {
4738
+ const dna = loadCurrentDNA();
4739
+ if (!dna) {
4740
+ console.log("");
4741
+ console.log(AMBER10(" \u26A0\uFE0F No profile generated yet."));
4742
+ console.log(GRAY8(" Run `bob profile --today` to generate your first profile."));
4743
+ console.log("");
4744
+ return;
4745
+ }
4746
+ console.log("");
4747
+ console.log(BORDER9(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
4748
+ console.log(BORDER9(" \u2551") + AMBER10(" \u{1F9EC} Your Current DNA Profile"));
4749
+ console.log(BORDER9(" \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"));
4750
+ console.log(BORDER9(" \u2551") + CYAN6(` Archetype: ${dna.archetype || "Unknown"}`));
4751
+ console.log(BORDER9(" \u2551") + GRAY8(` Communication: ${dna.communicationStyle || "Unknown"}`));
4752
+ console.log(BORDER9(" \u2551") + GRAY8(` Work Rhythm: ${dna.workRhythm || "Unknown"}`));
4753
+ console.log(BORDER9(" \u2551") + GRAY8(` Emotional State: ${dna.emotionalState || "Unknown"}`));
4754
+ console.log(BORDER9(" \u2551") + GRAY8(` Decision Making: ${dna.decisionMaking || "Unknown"}`));
4755
+ if (dna.growth) {
4756
+ console.log(BORDER9(" \u2551") + GREEN8(` Growth: ${dna.growth}`));
4757
+ }
4758
+ console.log(BORDER9(" \u2551"));
4759
+ console.log(BORDER9(" \u2551") + GRAY8(` Last updated: ${dna.lastUpdated || "Never"} (${dna.source || "unknown"})`));
4760
+ console.log(BORDER9(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
4761
+ console.log("");
4762
+ console.log(GRAY8(" Commands:"));
4763
+ console.log(GRAY8(" bob profile --today \u2014 Local daily profile"));
4764
+ console.log(GRAY8(" bob profile --week \u2014 Local weekly synthesis"));
4765
+ console.log(GRAY8(" bob profile --month \u2014 Local monthly synthesis"));
4766
+ console.log(GRAY8(" bob profile --cloud \u2014 Cloud daily (Power tier)"));
4767
+ console.log(GRAY8(" bob profile --cloud-weekly \u2014 Cloud weekly (Power tier)"));
4768
+ console.log(GRAY8(" bob profile --cloud-monthly \u2014 Cloud monthly (Power tier)"));
4769
+ console.log("");
4770
+ }
4771
+ async function generateDailyProfile(config) {
4772
+ const messages = await getTodayMessages();
4773
+ if (messages.length === 0) {
4774
+ console.log("");
4775
+ console.log(AMBER10(" \u26A0\uFE0F No conversations found for today."));
4776
+ console.log(GRAY8(" Chat with Bob first, then run this command."));
4777
+ console.log("");
4778
+ return;
4779
+ }
4780
+ const userMessages = messages.filter((m) => m.role === "user");
4781
+ const projectName = path11.basename(process.cwd());
4782
+ console.log("");
4783
+ console.log(CYAN6(` \u{1F9EC} Generating daily profile from ${userMessages.length} messages...`));
4784
+ console.log("");
4785
+ const spinner = ora12({ text: CYAN6(" Analyzing your communication patterns..."), spinner: "dots" }).start();
4786
+ const conversationText = messages.map((m) => `[${m.role.toUpperCase()}]: ${m.content}`).join("\n\n");
4787
+ const prompt = `You are a behavioral psychologist and communication analyst. Analyze the following conversation transcript from today and produce a detailed behavioral profile of the USER (not the assistant).
4788
+
4789
+ CONVERSATION TRANSCRIPT:
4790
+ ${conversationText}
4791
+
4792
+ Respond with ONLY a valid JSON object matching this EXACT structure. Include REAL quotes from the user as evidence for each assessment. Provide a confidence score (0-100) for each dimension based on how much evidence exists in the transcript.
4793
+
4794
+ {
4795
+ "communicationStyle": {
4796
+ "tone": "description of their tone (e.g., direct and impatient, curious and exploratory, formal and precise)",
4797
+ "verbosity": "terse/moderate/verbose",
4798
+ "questionRatio": 0.0 to 1.0 (how much they ask vs state),
4799
+ "confidence": 0-100,
4800
+ "examples": ["exact quote 1 that demonstrates this", "exact quote 2"]
4801
+ },
4802
+ "mentality": {
4803
+ "patience": "high/moderate/low",
4804
+ "approach": "methodical/chaotic/adaptive/burst-driven",
4805
+ "optimism": "optimistic/pragmatic/pessimistic/frustrated",
4806
+ "confidence": 0-100,
4807
+ "examples": ["exact quote showing their mentality", "another quote"]
4808
+ },
4809
+ "goalPatterns": {
4810
+ "clarity": "very clear/somewhat clear/vague/exploratory",
4811
+ "followThrough": "high/moderate/scattered",
4812
+ "currentGoals": ["goal 1 they're working toward", "goal 2"],
4813
+ "confidence": 0-100,
4814
+ "examples": ["quote about their goals", "quote showing follow-through"]
4815
+ },
4816
+ "workFrequency": {
4817
+ "sessionCount": number of distinct work sessions today,
4818
+ "averageDuration": "short (< 30 min) / medium (30-90 min) / long (90+ min)",
4819
+ "peakHours": "morning/afternoon/evening/night",
4820
+ "pattern": "steady/burst-mode/intermittent",
4821
+ "confidence": 0-100
4822
+ },
4823
+ "emotionalState": {
4824
+ "dominant": "primary emotion today (frustrated/excited/calm/anxious/determined/etc)",
4825
+ "secondary": "secondary emotion",
4826
+ "triggers": ["what caused emotional shifts"],
4827
+ "confidence": 0-100,
4828
+ "quotes": ["exact quote showing emotion 1", "exact quote showing emotion 2"]
4829
+ },
4830
+ "decisionStyle": {
4831
+ "speed": "instant/fast/deliberate/slow/indecisive",
4832
+ "riskTolerance": "high/moderate/cautious/risk-averse",
4833
+ "independence": "fully independent/seeks validation/collaborative/dependent",
4834
+ "confidence": 0-100,
4835
+ "examples": ["quote showing decision-making", "another example"]
4836
+ },
4837
+ "technicalDepth": {
4838
+ "level": "beginner/intermediate/senior/expert",
4839
+ "focusAreas": ["area 1", "area 2"],
4840
+ "confidence": 0-100
4841
+ },
4842
+ "userQuotes": ["5-8 most revealing/characteristic quotes from the user that capture who they are today"]
4843
+ }
4844
+
4845
+ IMPORTANT: Only include assessments you have EVIDENCE for. Use exact quotes from the transcript. Do NOT fabricate or assume beyond what the text shows.`;
4846
+ try {
4847
+ const aiMessages = [
4848
+ { role: "system", content: "You are a behavioral analyst. Respond with ONLY valid JSON. No explanation." },
4849
+ { role: "user", content: prompt }
4850
+ ];
4851
+ const response = await callLocalModel(config.localEndpoint, aiMessages);
4852
+ spinner.stop();
4853
+ const jsonMatch = response.match(/\{[\s\S]*\}/);
4854
+ if (!jsonMatch) {
4855
+ console.log(RED7(" \u274C Could not parse profile response."));
4856
+ return;
4857
+ }
4858
+ const parsed = JSON.parse(jsonMatch[0]);
4859
+ const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
4860
+ const profile = {
4861
+ date: today,
4862
+ projectName,
4863
+ messageCount: messages.length,
4864
+ ...parsed,
4865
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString()
4866
+ };
4867
+ saveDailyProfile(profile);
4868
+ console.log("");
4869
+ console.log(BORDER9(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
4870
+ console.log(BORDER9(" \u2551") + AMBER10(` \u{1F9EC} Daily Profile \u2014 ${today}`));
4871
+ console.log(BORDER9(" \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"));
4872
+ console.log(BORDER9(" \u2551") + CYAN6(` Communication: ${parsed.communicationStyle?.tone || "Unknown"} (${parsed.communicationStyle?.confidence || 0}%)`));
4873
+ console.log(BORDER9(" \u2551") + CYAN6(` Mentality: ${parsed.mentality?.approach || "Unknown"}, ${parsed.mentality?.optimism || ""} (${parsed.mentality?.confidence || 0}%)`));
4874
+ console.log(BORDER9(" \u2551") + CYAN6(` Emotional State: ${parsed.emotionalState?.dominant || "Unknown"} (${parsed.emotionalState?.confidence || 0}%)`));
4875
+ console.log(BORDER9(" \u2551") + CYAN6(` Decision Style: ${parsed.decisionStyle?.speed || "Unknown"}, ${parsed.decisionStyle?.riskTolerance || ""} (${parsed.decisionStyle?.confidence || 0}%)`));
4876
+ console.log(BORDER9(" \u2551") + CYAN6(` Work Pattern: ${parsed.workFrequency?.pattern || "Unknown"}`));
4877
+ console.log(BORDER9(" \u2551"));
4878
+ if (parsed.userQuotes && parsed.userQuotes.length > 0) {
4879
+ console.log(BORDER9(" \u2551") + GRAY8(" Key Quotes:"));
4880
+ for (const quote of parsed.userQuotes.slice(0, 4)) {
4881
+ console.log(BORDER9(" \u2551") + GRAY8(` "${quote.slice(0, 70)}${quote.length > 70 ? "..." : ""}"`));
4882
+ }
4883
+ }
4884
+ console.log(BORDER9(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
4885
+ console.log("");
4886
+ console.log(GREEN8(` \u2705 Saved to: ~/.bob/projects/${projectName}/profile/daily/${today}.json`));
4887
+ console.log("");
4888
+ } catch (error) {
4889
+ spinner.stop();
4890
+ console.log(RED7(` \u274C ${error.message}`));
4891
+ }
4892
+ }
4893
+ async function generateWeeklyProfile(config) {
4894
+ const dailies = loadDailyProfiles(7);
4895
+ if (dailies.length === 0) {
4896
+ console.log("");
4897
+ console.log(AMBER10(" \u26A0\uFE0F No daily profiles found."));
4898
+ console.log(GRAY8(" Run `bob profile --today` for at least a few days first."));
4899
+ console.log("");
4900
+ return;
4901
+ }
4902
+ console.log("");
4903
+ console.log(CYAN6(` \u{1F9EC} Synthesizing weekly profile from ${dailies.length} daily profiles...`));
4904
+ console.log("");
4905
+ const spinner = ora12({ text: CYAN6(" Analyzing patterns across days..."), spinner: "dots" }).start();
4906
+ const dailySummaries = dailies.map((d) => ({
4907
+ date: d.date,
4908
+ communication: d.communicationStyle?.tone,
4909
+ mentality: d.mentality?.approach + ", " + d.mentality?.optimism,
4910
+ emotion: d.emotionalState?.dominant,
4911
+ workPattern: d.workFrequency?.pattern,
4912
+ goals: d.goalPatterns?.currentGoals,
4913
+ quotes: d.userQuotes?.slice(0, 3)
4914
+ }));
4915
+ const prompt = `You are a behavioral psychologist analyzing how a person changed over a week. Below are their daily profiles from the past ${dailies.length} days. Analyze the EVOLUTION and PATTERNS across days.
4916
+
4917
+ DAILY PROFILES:
4918
+ ${JSON.stringify(dailySummaries, null, 2)}
4919
+
4920
+ Respond with ONLY a valid JSON object:
4921
+
4922
+ {
4923
+ "trajectory": "One sentence describing the overall direction of change this week",
4924
+ "energyPattern": "When their energy peaked and dropped across the week",
4925
+ "moodShift": "How their emotional state changed from the start to end of the week, with quotes as evidence",
4926
+ "focusEvolution": "How their focus/goals shifted day by day",
4927
+ "communicationShift": "How their communication style changed over the week",
4928
+ "dailySummaries": [{"date": "2026-06-01", "summary": "one-line summary of that day"}],
4929
+ "keyMoments": [{"date": "2026-06-03", "event": "what happened", "quote": "exact user quote from that day"}],
4930
+ "confidence": 0-100
4931
+ }
4932
+
4933
+ Use REAL quotes from the daily profiles as evidence. Show how the person CHANGED, not just what they were on average.`;
4934
+ try {
4935
+ const aiMessages = [
4936
+ { role: "system", content: "You are a behavioral analyst synthesizing weekly patterns. Respond with ONLY valid JSON." },
4937
+ { role: "user", content: prompt }
4938
+ ];
4939
+ const response = await callLocalModel(config.localEndpoint, aiMessages);
4940
+ spinner.stop();
4941
+ const jsonMatch = response.match(/\{[\s\S]*\}/);
4942
+ if (!jsonMatch) {
4943
+ console.log(RED7(" \u274C Could not parse weekly profile."));
4944
+ return;
4945
+ }
4946
+ const parsed = JSON.parse(jsonMatch[0]);
4947
+ const now = /* @__PURE__ */ new Date();
4948
+ const weekNum = getWeekNumber(now);
4949
+ const weekId = `${now.getFullYear()}-W${String(weekNum).padStart(2, "0")}`;
4950
+ const profile = {
4951
+ weekOf: weekId,
4952
+ dateRange: `${dailies[dailies.length - 1]?.date || "?"} to ${dailies[0]?.date || "?"}`,
4953
+ ...parsed,
4954
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString()
4955
+ };
4956
+ saveWeeklyProfile(profile);
4957
+ console.log("");
4958
+ console.log(BORDER9(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
4959
+ console.log(BORDER9(" \u2551") + AMBER10(` \u{1F9EC} Weekly Profile \u2014 ${weekId}`));
4960
+ console.log(BORDER9(" \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"));
4961
+ console.log(BORDER9(" \u2551") + CYAN6(` Trajectory: ${parsed.trajectory || "Unknown"}`));
4962
+ console.log(BORDER9(" \u2551") + GRAY8(` Energy: ${parsed.energyPattern || "Unknown"}`));
4963
+ console.log(BORDER9(" \u2551") + GRAY8(` Mood Shift: ${parsed.moodShift || "Unknown"}`));
4964
+ console.log(BORDER9(" \u2551") + GRAY8(` Focus: ${parsed.focusEvolution || "Unknown"}`));
4965
+ console.log(BORDER9(" \u2551") + GRAY8(` Communication: ${parsed.communicationShift || "Unknown"}`));
4966
+ if (parsed.keyMoments && parsed.keyMoments.length > 0) {
4967
+ console.log(BORDER9(" \u2551"));
4968
+ console.log(BORDER9(" \u2551") + GRAY8(" Key Moments:"));
4969
+ for (const moment of parsed.keyMoments.slice(0, 3)) {
4970
+ console.log(BORDER9(" \u2551") + GRAY8(` ${moment.date}: ${moment.event}`));
4971
+ if (moment.quote) console.log(BORDER9(" \u2551") + GRAY8(` "${moment.quote.slice(0, 60)}..."`));
4972
+ }
4973
+ }
4974
+ console.log(BORDER9(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
4975
+ console.log("");
4976
+ console.log(GREEN8(` \u2705 Saved: weekly/${weekId}.json`));
4977
+ console.log("");
4978
+ } catch (error) {
4979
+ spinner.stop();
4980
+ console.log(RED7(` \u274C ${error.message}`));
4981
+ }
4982
+ }
4983
+ async function generateMonthlyProfile(config) {
4984
+ const dailies = loadDailyProfiles(31);
4985
+ const weeklies = loadWeeklyProfiles(5);
4986
+ if (dailies.length === 0 && weeklies.length === 0) {
4987
+ console.log("");
4988
+ console.log(AMBER10(" \u26A0\uFE0F No profiles found for this month."));
4989
+ console.log(GRAY8(" Run `bob profile --today` daily and `bob profile --week` weekly first."));
4990
+ console.log("");
4991
+ return;
4992
+ }
4993
+ console.log("");
4994
+ console.log(CYAN6(` \u{1F9EC} Synthesizing monthly profile from ${dailies.length} dailies + ${weeklies.length} weeklies...`));
4995
+ console.log("");
4996
+ const spinner = ora12({ text: CYAN6(" Analyzing monthly evolution..."), spinner: "dots" }).start();
4997
+ const weekSummaries = weeklies.map((w) => ({
4998
+ week: w.weekOf,
4999
+ trajectory: w.trajectory,
5000
+ moodShift: w.moodShift,
5001
+ keyMoments: w.keyMoments?.slice(0, 2)
5002
+ }));
5003
+ const dailyHighlights = dailies.map((d) => ({
5004
+ date: d.date,
5005
+ emotion: d.emotionalState?.dominant,
5006
+ communication: d.communicationStyle?.tone,
5007
+ topQuote: d.userQuotes?.[0]
5008
+ }));
5009
+ const prompt = `You are a behavioral psychologist writing a monthly personality assessment. You have daily snapshots showing how this person felt and communicated each day, plus weekly synthesis showing trends. Analyze their GROWTH and EVOLUTION over the entire month.
5010
+
5011
+ WEEKLY SYNTHESES:
5012
+ ${JSON.stringify(weekSummaries, null, 2)}
5013
+
5014
+ DAILY HIGHLIGHTS (showing day-to-day shifts):
5015
+ ${JSON.stringify(dailyHighlights, null, 2)}
5016
+
5017
+ Respond with ONLY a valid JSON object:
5018
+
5019
+ {
5020
+ "overallTrajectory": "2-3 sentences describing the overall arc of this month",
5021
+ "weeklyProgression": ["Week 1: description", "Week 2: description", "Week 3: description", "Week 4: description"],
5022
+ "personalitySnapshot": {
5023
+ "archetype": "A name for their personality type",
5024
+ "communicationStyle": "How they typically communicate \u2014 with evidence",
5025
+ "workRhythm": "Their natural work pattern",
5026
+ "emotionalPattern": "Their emotional cycle",
5027
+ "decisionMaking": "How they make decisions",
5028
+ "growth": "How they grew this month"
5029
+ },
5030
+ "keyQuotes": ["4-6 most revealing user quotes from across the entire month"],
5031
+ "confidence": 0-100
5032
+ }
5033
+
5034
+ Show the JOURNEY, not just the destination. Use real quotes as evidence for every claim.`;
5035
+ try {
5036
+ const aiMessages = [
5037
+ { role: "system", content: "You are a behavioral psychologist writing a monthly assessment. Respond with ONLY valid JSON." },
5038
+ { role: "user", content: prompt }
5039
+ ];
5040
+ const response = await callLocalModel(config.localEndpoint, aiMessages);
5041
+ spinner.stop();
5042
+ const jsonMatch = response.match(/\{[\s\S]*\}/);
5043
+ if (!jsonMatch) {
5044
+ console.log(RED7(" \u274C Could not parse monthly profile."));
5045
+ return;
5046
+ }
5047
+ const parsed = JSON.parse(jsonMatch[0]);
5048
+ const now = /* @__PURE__ */ new Date();
5049
+ const monthId = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}`;
5050
+ const profile = {
5051
+ month: monthId,
5052
+ ...parsed,
5053
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString()
5054
+ };
5055
+ saveMonthlyProfile(profile);
5056
+ console.log("");
5057
+ console.log(BORDER9(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
5058
+ console.log(BORDER9(" \u2551") + AMBER10(` \u{1F9EC} Monthly Profile \u2014 ${monthId}`));
5059
+ console.log(BORDER9(" \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"));
5060
+ console.log(BORDER9(" \u2551") + CYAN6(` Archetype: ${parsed.personalitySnapshot?.archetype || "Unknown"}`));
5061
+ console.log(BORDER9(" \u2551"));
5062
+ console.log(BORDER9(" \u2551") + GRAY8(` Trajectory: ${parsed.overallTrajectory?.slice(0, 70) || "Unknown"}...`));
5063
+ console.log(BORDER9(" \u2551"));
5064
+ if (parsed.weeklyProgression) {
5065
+ console.log(BORDER9(" \u2551") + GRAY8(" Weekly Progression:"));
5066
+ for (const week of parsed.weeklyProgression.slice(0, 4)) {
5067
+ console.log(BORDER9(" \u2551") + GRAY8(` ${week.slice(0, 65)}...`));
5068
+ }
5069
+ }
5070
+ console.log(BORDER9(" \u2551"));
5071
+ if (parsed.personalitySnapshot?.growth) {
5072
+ console.log(BORDER9(" \u2551") + GREEN8(` Growth: ${parsed.personalitySnapshot.growth.slice(0, 65)}...`));
5073
+ }
5074
+ if (parsed.keyQuotes && parsed.keyQuotes.length > 0) {
5075
+ console.log(BORDER9(" \u2551"));
5076
+ console.log(BORDER9(" \u2551") + GRAY8(" Defining Quotes:"));
5077
+ for (const quote of parsed.keyQuotes.slice(0, 3)) {
5078
+ console.log(BORDER9(" \u2551") + GRAY8(` "${quote.slice(0, 65)}${quote.length > 65 ? "..." : ""}"`));
5079
+ }
5080
+ }
5081
+ console.log(BORDER9(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
5082
+ console.log("");
5083
+ console.log(GREEN8(` \u2705 Saved: monthly/${monthId}.json`));
5084
+ console.log(GRAY8(" This is now your current DNA profile. Bob will adapt to match."));
5085
+ console.log("");
5086
+ } catch (error) {
5087
+ spinner.stop();
5088
+ console.log(RED7(` \u274C ${error.message}`));
5089
+ }
5090
+ }
5091
+ function getWeekNumber(date) {
5092
+ const d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()));
5093
+ const dayNum = d.getUTCDay() || 7;
5094
+ d.setUTCDate(d.getUTCDate() + 4 - dayNum);
5095
+ const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));
5096
+ return Math.ceil(((d.getTime() - yearStart.getTime()) / 864e5 + 1) / 7);
5097
+ }
5098
+
5099
+ // bin/bob.ts
5100
+ var program = new Command();
5101
+ program.name("bob").description("Bob's CLI \u2014 AI coding assistant and Forge orchestrator").version("0.1.3");
5102
+ program.command("whoami").description("Show current authentication status and configuration").action(() => {
5103
+ const config = getConfig();
5104
+ const projectName = path12.basename(process.cwd());
5105
+ console.log("");
5106
+ console.log(chalk22.bold(" \u{1F916} Bob's CLI"));
5107
+ console.log(chalk22.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
5108
+ console.log(` ${chalk22.cyan("Status:")} ${config.loggedIn ? chalk22.green("Logged in as " + config.email) : "Not logged in"}`);
5109
+ console.log(` ${chalk22.cyan("Tier:")} ${config.tier === "platform" ? "Platform (Tier 3)" : "Local-first (Tier 1)"}`);
5110
+ console.log(` ${chalk22.cyan("Provider:")} ${config.provider || "Not configured"}`);
5111
+ console.log(` ${chalk22.cyan("Mode:")} ${config.personalizationMode ? "Personalized" : config.consultantMode ? "Consultant" : "Standard"}`);
5112
+ console.log(` ${chalk22.cyan("IDRP:")} ${config.idrp ? "Enabled" : "Disabled"}`);
5113
+ console.log(` ${chalk22.cyan("Project:")} ${projectName} (${process.cwd()})`);
5114
+ console.log(` ${chalk22.cyan("Session:")} ${config.conversationId ? config.conversationId.slice(0, 20) + "..." : "None"}`);
2989
5115
  console.log("");
2990
5116
  if (!config.loggedIn) {
2991
- console.log(chalk17.gray(" Run `bob login` to authenticate."));
5117
+ console.log(chalk22.gray(" Run `bob login` to authenticate."));
2992
5118
  console.log("");
2993
5119
  }
2994
5120
  });
@@ -3004,4 +5130,7 @@ registerForkCommand(program);
3004
5130
  registerDeepDiveCommand(program);
3005
5131
  registerAnalyseCommand(program);
3006
5132
  registerAutonomyCommand(program);
5133
+ registerServeCommand(program);
5134
+ registerRemoteCommand(program);
5135
+ registerProfileCommand(program);
3007
5136
  program.parse();