@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/analyse-auto-KKWLMLHZ.js → analyse-auto-OBCDWYWX.js} +1 -2
- package/dist/{bin/analyse-results-N5QLJNND.js → analyse-results-QSOD3KVC.js} +1 -2
- package/dist/bin/analyse-auto-6T42LV3G.js +529 -0
- package/dist/bin/analyse-auto-CM7XEPKT.js +529 -0
- package/dist/bin/analyse-auto-KZNPVVCR.js +529 -0
- package/dist/bin/analyse-auto-RDYNFTCD.js +529 -0
- package/dist/bin/analyse-auto-Y5QUQU4G.js +529 -0
- package/dist/bin/analyse-results-F7TH2YMO.js +8 -0
- package/dist/bin/analyse-results-KM5C7XL7.js +8 -0
- package/dist/bin/analyse-results-KU3KEG3E.js +8 -0
- package/dist/bin/analyse-results-OU6F6TRX.js +8 -0
- package/dist/bin/analyse-results-UHU4DPO3.js +8 -0
- package/dist/bin/bob.js +2529 -400
- package/dist/bin/chunk-J4RRWEHU.js +939 -0
- package/dist/bin/chunk-KSHHT2WT.js +939 -0
- package/dist/bin/chunk-L554PTBY.js +888 -0
- package/dist/bin/chunk-RSOPJT6F.js +883 -0
- package/dist/bin/chunk-TXCQFX4W.js +946 -0
- package/dist/bob.js +3006 -0
- package/dist/{bin/chunk-WEHSNZKO.js → chunk-LHWBSCJ4.js} +0 -2
- package/package.json +7 -5
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-
|
|
18
|
+
} from "./chunk-J4RRWEHU.js";
|
|
18
19
|
|
|
19
20
|
// bin/bob.ts
|
|
20
21
|
import { Command } from "commander";
|
|
21
|
-
import
|
|
22
|
-
import * as
|
|
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
|
|
112
|
-
import * as
|
|
113
|
-
import * as
|
|
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
|
|
151
|
-
import * as
|
|
365
|
+
import * as fs3 from "fs";
|
|
366
|
+
import * as path3 from "path";
|
|
152
367
|
|
|
153
368
|
// src/core/project-map.ts
|
|
154
|
-
import * as
|
|
155
|
-
import * as
|
|
156
|
-
import * as
|
|
157
|
-
var
|
|
158
|
-
var PROJECTS_DIR =
|
|
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
|
|
375
|
+
return path2.basename(workingDir);
|
|
161
376
|
}
|
|
162
377
|
function getProjectDir(workingDir) {
|
|
163
378
|
const name = getProjectName(workingDir);
|
|
164
|
-
return
|
|
379
|
+
return path2.join(PROJECTS_DIR, name);
|
|
165
380
|
}
|
|
166
381
|
function ensureProjectStructure(workingDir) {
|
|
167
382
|
const projectDir = getProjectDir(workingDir);
|
|
168
|
-
const conversationsDir =
|
|
169
|
-
const analysisDir =
|
|
170
|
-
const runsDir =
|
|
171
|
-
for (const dir of [
|
|
172
|
-
if (!
|
|
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 =
|
|
175
|
-
if (!
|
|
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
|
-
|
|
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 =
|
|
190
|
-
const tasksDir =
|
|
191
|
-
|
|
192
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
218
|
-
if (
|
|
219
|
-
const task = JSON.parse(
|
|
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
|
-
|
|
437
|
+
fs2.writeFileSync(taskPath, JSON.stringify(task, null, 2));
|
|
223
438
|
}
|
|
224
439
|
}
|
|
225
440
|
function updateManifestProgress(runDir, completedFiles, status) {
|
|
226
|
-
const manifestPath =
|
|
227
|
-
if (
|
|
228
|
-
const manifest = JSON.parse(
|
|
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
|
-
|
|
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
|
-
|
|
451
|
+
fs2.writeFileSync(path2.join(analysisDir, "summaries.json"), JSON.stringify(summaries, null, 2));
|
|
237
452
|
const projectDir = getProjectDir(workingDir);
|
|
238
|
-
const metaPath =
|
|
239
|
-
if (
|
|
240
|
-
const meta = JSON.parse(
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
252
|
-
if (!
|
|
466
|
+
const summariesPath = path2.join(analysisDir, "summaries.json");
|
|
467
|
+
if (!fs2.existsSync(summariesPath)) return null;
|
|
253
468
|
try {
|
|
254
|
-
return JSON.parse(
|
|
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 =
|
|
262
|
-
if (!
|
|
476
|
+
const depsPath = path2.join(analysisDir, "dependencies.json");
|
|
477
|
+
if (!fs2.existsSync(depsPath)) return null;
|
|
263
478
|
try {
|
|
264
|
-
return JSON.parse(
|
|
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 =
|
|
274
|
-
const messagesDir =
|
|
275
|
-
if (!
|
|
276
|
-
if (!
|
|
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
|
-
|
|
279
|
-
|
|
493
|
+
fs3.writeFileSync(
|
|
494
|
+
path3.join(messagesDir, messageFilename),
|
|
280
495
|
JSON.stringify(message, null, 2)
|
|
281
496
|
);
|
|
282
|
-
const metaPath =
|
|
497
|
+
const metaPath = path3.join(convoDir, "conversation.json");
|
|
283
498
|
let convoMeta;
|
|
284
|
-
if (
|
|
499
|
+
if (fs3.existsSync(metaPath)) {
|
|
285
500
|
try {
|
|
286
|
-
convoMeta = JSON.parse(
|
|
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
|
-
|
|
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
|
|
318
|
-
import * as
|
|
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
|
-
|
|
335
|
-
|
|
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,
|
|
365
|
-
const absolutePath =
|
|
580
|
+
for (const filePath of selectedFiles.slice(0, 10)) {
|
|
581
|
+
const absolutePath = path4.join(cwd, filePath);
|
|
366
582
|
try {
|
|
367
|
-
if (
|
|
368
|
-
const content =
|
|
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
|
|
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
|
|
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
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
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
|
-
|
|
756
|
-
|
|
757
|
-
|
|
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
|
|
786
|
-
var
|
|
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 =
|
|
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") +
|
|
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
|
|
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
|
|
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(
|
|
842
|
-
console.log(
|
|
843
|
-
console.log(
|
|
844
|
-
console.log(
|
|
845
|
-
console.log(
|
|
846
|
-
console.log(
|
|
847
|
-
console.log(
|
|
848
|
-
console.log(
|
|
849
|
-
console.log(
|
|
850
|
-
console.log(
|
|
851
|
-
console.log(
|
|
852
|
-
console.log(
|
|
853
|
-
console.log(
|
|
854
|
-
console.log(
|
|
855
|
-
console.log(
|
|
856
|
-
console.log(
|
|
857
|
-
console.log(
|
|
858
|
-
process.stdout.write(
|
|
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${
|
|
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) +
|
|
866
|
-
console.log(
|
|
867
|
-
console.log(
|
|
868
|
-
console.log(
|
|
869
|
-
console.log(
|
|
870
|
-
console.log(
|
|
871
|
-
console.log(
|
|
872
|
-
console.log(
|
|
873
|
-
console.log(
|
|
874
|
-
console.log(
|
|
875
|
-
console.log(
|
|
876
|
-
console.log(
|
|
877
|
-
console.log(
|
|
878
|
-
console.log(
|
|
879
|
-
console.log(
|
|
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
|
-
|
|
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
|
|
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:
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
978
|
-
|
|
1321
|
+
email: config.email,
|
|
1322
|
+
uid: config.uid,
|
|
979
1323
|
conversationId,
|
|
980
1324
|
userMessage: message,
|
|
981
|
-
|
|
982
|
-
|
|
1325
|
+
additionalContext: { localContext: fullContext || null },
|
|
1326
|
+
isLocalModel: false,
|
|
1327
|
+
activePersonaId: null
|
|
983
1328
|
});
|
|
984
|
-
response = result?.
|
|
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
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
1110
|
-
if (!
|
|
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 =
|
|
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}?
|
|
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 =
|
|
1124
|
-
if (!
|
|
1125
|
-
|
|
1126
|
-
|
|
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
|
|
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
|
|
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:
|
|
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
|
-
|
|
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.
|
|
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 (
|
|
1595
|
+
if (!config.hasSeenWelcome) {
|
|
1293
1596
|
await showWelcomeIfFirstRun();
|
|
1294
1597
|
setConfigValue("hasSeenWelcome", true);
|
|
1295
1598
|
}
|
|
1296
1599
|
renderSessionHeader("consult");
|
|
1297
|
-
const rl =
|
|
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
|
-
|
|
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
|
|
1367
|
-
import * as
|
|
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 =
|
|
1702
|
+
const absolutePath = path7.join(cwd, filePath);
|
|
1404
1703
|
let content;
|
|
1405
1704
|
try {
|
|
1406
|
-
content =
|
|
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 =
|
|
1483
|
-
if (
|
|
1484
|
-
const task = JSON.parse(
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
1514
|
-
const relativePath =
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
|
2192
|
-
import * as
|
|
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
|
|
2198
|
-
var
|
|
2199
|
-
var
|
|
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-
|
|
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-
|
|
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(
|
|
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(
|
|
2266
|
-
console.log(
|
|
2267
|
-
console.log(
|
|
2268
|
-
console.log(
|
|
2269
|
-
console.log(
|
|
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(
|
|
2275
|
-
console.log(
|
|
2276
|
-
console.log(
|
|
2277
|
-
console.log(
|
|
2278
|
-
console.log(
|
|
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(
|
|
2281
|
-
console.log(
|
|
2282
|
-
console.log(
|
|
2283
|
-
console.log(
|
|
2284
|
-
console.log(
|
|
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(
|
|
2287
|
-
console.log(
|
|
2288
|
-
console.log(
|
|
2289
|
-
console.log(
|
|
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(
|
|
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
|
|
2319
|
-
console.log(` [${
|
|
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(
|
|
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(
|
|
2339
|
-
console.log(
|
|
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(
|
|
2351
|
-
console.log(
|
|
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(
|
|
2366
|
-
console.log(
|
|
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(
|
|
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 =
|
|
2385
|
-
if (!
|
|
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 =
|
|
2688
|
+
const absolutePath = path8.join(cwd, filePath);
|
|
2390
2689
|
let content;
|
|
2391
2690
|
try {
|
|
2392
|
-
content =
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
2492
|
-
console.log(
|
|
2493
|
-
console.log(
|
|
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 =
|
|
2500
|
-
if (!
|
|
2501
|
-
return JSON.parse(
|
|
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 =
|
|
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(
|
|
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
|
|
2824
|
+
import * as readline6 from "readline";
|
|
2526
2825
|
import simpleGit2 from "simple-git";
|
|
2527
|
-
import * as
|
|
2528
|
-
import * as
|
|
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
|
|
2830
|
+
var AMBER5 = chalk16.hex("#FFAB00");
|
|
2532
2831
|
var BLUE3 = chalk16.hex("#42A5F5");
|
|
2533
|
-
var
|
|
2534
|
-
var
|
|
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(
|
|
2558
|
-
console.log(
|
|
2559
|
-
console.log(
|
|
2560
|
-
console.log(
|
|
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(
|
|
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(
|
|
2586
|
-
console.log(
|
|
2587
|
-
console.log(
|
|
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(
|
|
2593
|
-
console.log(
|
|
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(
|
|
2625
|
-
const rl =
|
|
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(
|
|
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 =
|
|
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(
|
|
2662
|
-
console.log(
|
|
2663
|
-
console.log(
|
|
2664
|
-
console.log(
|
|
2665
|
-
console.log(
|
|
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(
|
|
2672
|
-
console.log(
|
|
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(
|
|
2681
|
-
console.log(
|
|
2682
|
-
console.log(
|
|
2683
|
-
console.log(
|
|
2684
|
-
console.log(
|
|
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(
|
|
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(
|
|
2736
|
-
console.log(
|
|
2737
|
-
console.log(
|
|
2738
|
-
console.log(
|
|
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(
|
|
3039
|
+
console.log(BORDER4(" \u2551") + RED2(` \u274C Failed: ${failed} files`));
|
|
2741
3040
|
}
|
|
2742
|
-
console.log(
|
|
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(
|
|
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(
|
|
3071
|
+
console.log(GRAY3(' Files are saved locally. Push manually with `bob push "message"`.'));
|
|
2773
3072
|
}
|
|
2774
3073
|
} else {
|
|
2775
|
-
console.log(
|
|
3074
|
+
console.log(GRAY3(" Not a git repo. Files saved locally only."));
|
|
2776
3075
|
}
|
|
2777
3076
|
}
|
|
2778
3077
|
console.log("");
|
|
2779
|
-
console.log(
|
|
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(
|
|
2799
|
-
console.log(
|
|
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(
|
|
3100
|
+
console.log(GRAY3(` ${line.text}`));
|
|
2802
3101
|
}
|
|
2803
3102
|
console.log("");
|
|
2804
3103
|
} else {
|
|
2805
3104
|
console.log("");
|
|
2806
|
-
console.log(
|
|
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 =
|
|
2867
|
-
const backupDir =
|
|
2868
|
-
if (!
|
|
2869
|
-
if (
|
|
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
|
-
|
|
3171
|
+
fs8.copyFileSync(absolutePath, path9.join(backupDir, backupName));
|
|
2873
3172
|
}
|
|
2874
|
-
const dir =
|
|
2875
|
-
if (!
|
|
2876
|
-
|
|
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 =
|
|
3183
|
+
const projectName = path9.basename(cwd);
|
|
2885
3184
|
const homeDir = process.env.HOME || process.env.USERPROFILE || "";
|
|
2886
|
-
const analysisPath =
|
|
2887
|
-
if (!
|
|
2888
|
-
const allResults = JSON.parse(
|
|
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
|
|
2909
|
-
console.log(` \u26A1 [${
|
|
2910
|
-
console.log(
|
|
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(
|
|
2917
|
-
lines.push(
|
|
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 =
|
|
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 =
|
|
3237
|
+
color = GRAY3;
|
|
2939
3238
|
break;
|
|
2940
3239
|
default:
|
|
2941
3240
|
icon = "\u2610";
|
|
2942
|
-
color =
|
|
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))}${
|
|
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
|
-
//
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
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(
|
|
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();
|