@bobsworkshop/cli 0.7.0 → 0.7.2
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/README.md +107 -10
- package/dist/analyse-auto-DCC6UDFV.js +529 -0
- package/dist/analyse-results-4S577CG5.js +8 -0
- package/dist/bob.js +240 -348
- package/dist/chunk-NUMFL5IZ.js +1204 -0
- package/package.json +1 -1
package/dist/bob.js
CHANGED
|
@@ -1,27 +1,37 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
1
|
import {
|
|
3
2
|
buildLocalContext,
|
|
4
3
|
callCloudFunction,
|
|
5
4
|
callLocalModel,
|
|
5
|
+
completeTask,
|
|
6
|
+
createAnalysisRun,
|
|
7
|
+
ensureProjectStructure,
|
|
6
8
|
extractAllProposedFiles,
|
|
7
9
|
extractProposedFile,
|
|
10
|
+
getActiveConversationId,
|
|
8
11
|
getConfig,
|
|
9
12
|
getConfigPath,
|
|
13
|
+
getProjectName,
|
|
10
14
|
isAuthenticated,
|
|
15
|
+
loadDependencies,
|
|
11
16
|
loadLocalSuggestions,
|
|
17
|
+
loadSummaries,
|
|
12
18
|
markSuggestionStatus,
|
|
13
19
|
processAllProposedFiles,
|
|
14
20
|
proposeAndWriteFile,
|
|
15
21
|
readFileContent,
|
|
16
22
|
registerLoginCommand,
|
|
23
|
+
saveDependencies,
|
|
24
|
+
saveSummaries,
|
|
25
|
+
setActiveConversationId,
|
|
17
26
|
setConfigValue,
|
|
18
|
-
stripCodeBlockFromResponse
|
|
19
|
-
|
|
27
|
+
stripCodeBlockFromResponse,
|
|
28
|
+
updateManifestProgress
|
|
29
|
+
} from "./chunk-NUMFL5IZ.js";
|
|
20
30
|
|
|
21
31
|
// bin/bob.ts
|
|
22
32
|
import { Command } from "commander";
|
|
23
33
|
import chalk25 from "chalk";
|
|
24
|
-
import * as
|
|
34
|
+
import * as path13 from "path";
|
|
25
35
|
|
|
26
36
|
// src/commands/config.ts
|
|
27
37
|
import chalk from "chalk";
|
|
@@ -109,8 +119,8 @@ function registerConfigCommand(program2) {
|
|
|
109
119
|
|
|
110
120
|
// src/commands/chat.ts
|
|
111
121
|
import chalk9 from "chalk";
|
|
112
|
-
import * as
|
|
113
|
-
import * as
|
|
122
|
+
import * as fs5 from "fs";
|
|
123
|
+
import * as path6 from "path";
|
|
114
124
|
import * as readline2 from "readline";
|
|
115
125
|
|
|
116
126
|
// src/core/profile-store.ts
|
|
@@ -356,143 +366,24 @@ Do NOT mention that you are adapting to their profile. Do NOT reference this sec
|
|
|
356
366
|
}
|
|
357
367
|
|
|
358
368
|
// src/core/conversation-store.ts
|
|
359
|
-
import * as fs3 from "fs";
|
|
360
|
-
import * as path3 from "path";
|
|
361
|
-
|
|
362
|
-
// src/core/project-map.ts
|
|
363
369
|
import * as fs2 from "fs";
|
|
364
370
|
import * as path2 from "path";
|
|
365
|
-
import * as os2 from "os";
|
|
366
|
-
var BOB_DIR2 = path2.join(os2.homedir(), ".bob");
|
|
367
|
-
var PROJECTS_DIR = path2.join(BOB_DIR2, "projects");
|
|
368
|
-
function getProjectName(workingDir) {
|
|
369
|
-
return path2.basename(workingDir);
|
|
370
|
-
}
|
|
371
|
-
function getProjectDir(workingDir) {
|
|
372
|
-
const name = getProjectName(workingDir);
|
|
373
|
-
return path2.join(PROJECTS_DIR, name);
|
|
374
|
-
}
|
|
375
|
-
function ensureProjectStructure(workingDir) {
|
|
376
|
-
const projectDir = getProjectDir(workingDir);
|
|
377
|
-
const conversationsDir = path2.join(projectDir, "conversations");
|
|
378
|
-
const analysisDir = path2.join(projectDir, "analysis");
|
|
379
|
-
const runsDir = path2.join(analysisDir, "runs");
|
|
380
|
-
for (const dir of [BOB_DIR2, PROJECTS_DIR, projectDir, conversationsDir, analysisDir, runsDir]) {
|
|
381
|
-
if (!fs2.existsSync(dir)) fs2.mkdirSync(dir, { recursive: true });
|
|
382
|
-
}
|
|
383
|
-
const metaPath = path2.join(projectDir, "project.json");
|
|
384
|
-
if (!fs2.existsSync(metaPath)) {
|
|
385
|
-
const meta = {
|
|
386
|
-
name: getProjectName(workingDir),
|
|
387
|
-
path: workingDir,
|
|
388
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
389
|
-
lastIndexed: null
|
|
390
|
-
};
|
|
391
|
-
fs2.writeFileSync(metaPath, JSON.stringify(meta, null, 2));
|
|
392
|
-
}
|
|
393
|
-
return { projectDir, conversationsDir, analysisDir, runsDir };
|
|
394
|
-
}
|
|
395
|
-
function createAnalysisRun(workingDir, files) {
|
|
396
|
-
const { runsDir } = ensureProjectStructure(workingDir);
|
|
397
|
-
const runId = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
398
|
-
const runDir = path2.join(runsDir, runId);
|
|
399
|
-
const tasksDir = path2.join(runDir, "tasks");
|
|
400
|
-
fs2.mkdirSync(runDir, { recursive: true });
|
|
401
|
-
fs2.mkdirSync(tasksDir, { recursive: true });
|
|
402
|
-
const manifest = {
|
|
403
|
-
runId,
|
|
404
|
-
status: "in_progress",
|
|
405
|
-
totalFiles: files.length,
|
|
406
|
-
completedFiles: 0,
|
|
407
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
408
|
-
projectPath: workingDir
|
|
409
|
-
};
|
|
410
|
-
fs2.writeFileSync(path2.join(runDir, "manifest.json"), JSON.stringify(manifest, null, 2));
|
|
411
|
-
for (const filePath of files) {
|
|
412
|
-
const taskId = filePath.replace(/[\/\\]/g, "_");
|
|
413
|
-
const task = {
|
|
414
|
-
filePath,
|
|
415
|
-
status: false,
|
|
416
|
-
summary: null,
|
|
417
|
-
dependencies: [],
|
|
418
|
-
error: null
|
|
419
|
-
};
|
|
420
|
-
fs2.writeFileSync(path2.join(tasksDir, `${taskId}.json`), JSON.stringify(task, null, 2));
|
|
421
|
-
}
|
|
422
|
-
return { runId, runDir, tasksDir };
|
|
423
|
-
}
|
|
424
|
-
function completeTask(tasksDir, filePath, summary) {
|
|
425
|
-
const taskId = filePath.replace(/[\/\\]/g, "_");
|
|
426
|
-
const taskPath = path2.join(tasksDir, `${taskId}.json`);
|
|
427
|
-
if (fs2.existsSync(taskPath)) {
|
|
428
|
-
const task = JSON.parse(fs2.readFileSync(taskPath, "utf-8"));
|
|
429
|
-
task.status = true;
|
|
430
|
-
task.summary = summary;
|
|
431
|
-
fs2.writeFileSync(taskPath, JSON.stringify(task, null, 2));
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
function updateManifestProgress(runDir, completedFiles, status) {
|
|
435
|
-
const manifestPath = path2.join(runDir, "manifest.json");
|
|
436
|
-
if (fs2.existsSync(manifestPath)) {
|
|
437
|
-
const manifest = JSON.parse(fs2.readFileSync(manifestPath, "utf-8"));
|
|
438
|
-
manifest.completedFiles = completedFiles;
|
|
439
|
-
if (status) manifest.status = status;
|
|
440
|
-
fs2.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
function saveSummaries(workingDir, summaries) {
|
|
444
|
-
const { analysisDir } = ensureProjectStructure(workingDir);
|
|
445
|
-
fs2.writeFileSync(path2.join(analysisDir, "summaries.json"), JSON.stringify(summaries, null, 2));
|
|
446
|
-
const projectDir = getProjectDir(workingDir);
|
|
447
|
-
const metaPath = path2.join(projectDir, "project.json");
|
|
448
|
-
if (fs2.existsSync(metaPath)) {
|
|
449
|
-
const meta = JSON.parse(fs2.readFileSync(metaPath, "utf-8"));
|
|
450
|
-
meta.lastIndexed = (/* @__PURE__ */ new Date()).toISOString();
|
|
451
|
-
fs2.writeFileSync(metaPath, JSON.stringify(meta, null, 2));
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
function saveDependencies(workingDir, dependencies) {
|
|
455
|
-
const { analysisDir } = ensureProjectStructure(workingDir);
|
|
456
|
-
fs2.writeFileSync(path2.join(analysisDir, "dependencies.json"), JSON.stringify(dependencies, null, 2));
|
|
457
|
-
}
|
|
458
|
-
function loadSummaries(workingDir) {
|
|
459
|
-
const { analysisDir } = ensureProjectStructure(workingDir);
|
|
460
|
-
const summariesPath = path2.join(analysisDir, "summaries.json");
|
|
461
|
-
if (!fs2.existsSync(summariesPath)) return null;
|
|
462
|
-
try {
|
|
463
|
-
return JSON.parse(fs2.readFileSync(summariesPath, "utf-8"));
|
|
464
|
-
} catch {
|
|
465
|
-
return null;
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
function loadDependencies(workingDir) {
|
|
469
|
-
const { analysisDir } = ensureProjectStructure(workingDir);
|
|
470
|
-
const depsPath = path2.join(analysisDir, "dependencies.json");
|
|
471
|
-
if (!fs2.existsSync(depsPath)) return null;
|
|
472
|
-
try {
|
|
473
|
-
return JSON.parse(fs2.readFileSync(depsPath, "utf-8"));
|
|
474
|
-
} catch {
|
|
475
|
-
return null;
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
// src/core/conversation-store.ts
|
|
480
371
|
function saveMessage(conversationId, message, meta) {
|
|
481
372
|
const { conversationsDir } = ensureProjectStructure(process.cwd());
|
|
482
|
-
const convoDir =
|
|
483
|
-
const messagesDir =
|
|
484
|
-
if (!
|
|
485
|
-
if (!
|
|
373
|
+
const convoDir = path2.join(conversationsDir, conversationId);
|
|
374
|
+
const messagesDir = path2.join(convoDir, "messages");
|
|
375
|
+
if (!fs2.existsSync(convoDir)) fs2.mkdirSync(convoDir, { recursive: true });
|
|
376
|
+
if (!fs2.existsSync(messagesDir)) fs2.mkdirSync(messagesDir, { recursive: true });
|
|
486
377
|
const messageFilename = `${Date.now()}_${message.sender}.json`;
|
|
487
|
-
|
|
488
|
-
|
|
378
|
+
fs2.writeFileSync(
|
|
379
|
+
path2.join(messagesDir, messageFilename),
|
|
489
380
|
JSON.stringify(message, null, 2)
|
|
490
381
|
);
|
|
491
|
-
const metaPath =
|
|
382
|
+
const metaPath = path2.join(convoDir, "conversation.json");
|
|
492
383
|
let convoMeta;
|
|
493
|
-
if (
|
|
384
|
+
if (fs2.existsSync(metaPath)) {
|
|
494
385
|
try {
|
|
495
|
-
convoMeta = JSON.parse(
|
|
386
|
+
convoMeta = JSON.parse(fs2.readFileSync(metaPath, "utf-8"));
|
|
496
387
|
} catch {
|
|
497
388
|
convoMeta = createMeta(conversationId, meta);
|
|
498
389
|
}
|
|
@@ -505,7 +396,7 @@ function saveMessage(conversationId, message, meta) {
|
|
|
505
396
|
if (!convoMeta.title && message.sender === "user") {
|
|
506
397
|
convoMeta.title = message.message.slice(0, 80);
|
|
507
398
|
}
|
|
508
|
-
|
|
399
|
+
fs2.writeFileSync(metaPath, JSON.stringify(convoMeta, null, 2));
|
|
509
400
|
}
|
|
510
401
|
function createMeta(conversationId, meta) {
|
|
511
402
|
return {
|
|
@@ -523,8 +414,8 @@ function createMeta(conversationId, meta) {
|
|
|
523
414
|
}
|
|
524
415
|
|
|
525
416
|
// src/core/file-retrieval.ts
|
|
526
|
-
import * as
|
|
527
|
-
import * as
|
|
417
|
+
import * as fs3 from "fs";
|
|
418
|
+
import * as path3 from "path";
|
|
528
419
|
async function getRelevantFileContents(userMessage, localEndpoint) {
|
|
529
420
|
const cwd = process.cwd();
|
|
530
421
|
const summaries = loadSummaries(cwd);
|
|
@@ -572,10 +463,10 @@ Return ONLY the JSON array of relevant file paths:`
|
|
|
572
463
|
let fileContents = "## RELEVANT FILES (selected by Bob from project index) ##\n\n";
|
|
573
464
|
const validFiles = [];
|
|
574
465
|
for (const filePath of selectedFiles.slice(0, 10)) {
|
|
575
|
-
const absolutePath =
|
|
466
|
+
const absolutePath = path3.join(cwd, filePath);
|
|
576
467
|
try {
|
|
577
|
-
if (
|
|
578
|
-
const content =
|
|
468
|
+
if (fs3.existsSync(absolutePath)) {
|
|
469
|
+
const content = fs3.readFileSync(absolutePath, "utf-8");
|
|
579
470
|
fileContents += `--- FILE: ${filePath} ---
|
|
580
471
|
${content}
|
|
581
472
|
--- END FILE ---
|
|
@@ -789,8 +680,8 @@ function startDeepDiveAnimation() {
|
|
|
789
680
|
|
|
790
681
|
// src/ui/chat-renderer.ts
|
|
791
682
|
import chalk4 from "chalk";
|
|
792
|
-
import * as
|
|
793
|
-
import * as
|
|
683
|
+
import * as fs4 from "fs";
|
|
684
|
+
import * as path4 from "path";
|
|
794
685
|
import { diffLines } from "diff";
|
|
795
686
|
|
|
796
687
|
// src/ui/renderer.ts
|
|
@@ -991,8 +882,8 @@ function renderFileDiff(filePath, newContent, isNew) {
|
|
|
991
882
|
console.log("");
|
|
992
883
|
return;
|
|
993
884
|
}
|
|
994
|
-
const absolutePath =
|
|
995
|
-
if (!
|
|
885
|
+
const absolutePath = path4.join(process.cwd(), filePath);
|
|
886
|
+
if (!fs4.existsSync(absolutePath)) {
|
|
996
887
|
const lineCount = newContent.split("\n").length;
|
|
997
888
|
console.log("");
|
|
998
889
|
console.log(SUCCESS(` \u25C6 Created ${filePath}`));
|
|
@@ -1000,7 +891,7 @@ function renderFileDiff(filePath, newContent, isNew) {
|
|
|
1000
891
|
console.log("");
|
|
1001
892
|
return;
|
|
1002
893
|
}
|
|
1003
|
-
const existingContent =
|
|
894
|
+
const existingContent = fs4.readFileSync(absolutePath, "utf-8");
|
|
1004
895
|
const changes = diffLines(existingContent, newContent);
|
|
1005
896
|
let additions = 0;
|
|
1006
897
|
let removals = 0;
|
|
@@ -1152,7 +1043,8 @@ function registerDeepDiveCommand(program2) {
|
|
|
1152
1043
|
console.log("");
|
|
1153
1044
|
return;
|
|
1154
1045
|
}
|
|
1155
|
-
|
|
1046
|
+
const conversationId = getActiveConversationId(process.cwd()) || config.conversationId;
|
|
1047
|
+
if (!conversationId) {
|
|
1156
1048
|
console.log("");
|
|
1157
1049
|
console.log(ERROR2(" \u274C No active conversation."));
|
|
1158
1050
|
console.log("");
|
|
@@ -1160,7 +1052,7 @@ function registerDeepDiveCommand(program2) {
|
|
|
1160
1052
|
}
|
|
1161
1053
|
const spinner = ora({ text: MODE_DEEPDIVE2(" Loading deep dives..."), spinner: "dots" }).start();
|
|
1162
1054
|
try {
|
|
1163
|
-
const result = await callCloudFunction("listCLIDeepDives", { conversationId
|
|
1055
|
+
const result = await callCloudFunction("listCLIDeepDives", { conversationId });
|
|
1164
1056
|
spinner.stop();
|
|
1165
1057
|
const dives = result.deepDives || [];
|
|
1166
1058
|
console.log("");
|
|
@@ -1198,7 +1090,8 @@ function registerDeepDiveCommand(program2) {
|
|
|
1198
1090
|
console.log("");
|
|
1199
1091
|
return;
|
|
1200
1092
|
}
|
|
1201
|
-
|
|
1093
|
+
const conversationId = getActiveConversationId(process.cwd()) || config.conversationId;
|
|
1094
|
+
if (!conversationId) {
|
|
1202
1095
|
console.log("");
|
|
1203
1096
|
console.log(ERROR2(" \u274C No active conversation."));
|
|
1204
1097
|
console.log("");
|
|
@@ -1206,7 +1099,7 @@ function registerDeepDiveCommand(program2) {
|
|
|
1206
1099
|
}
|
|
1207
1100
|
const spinner = ora({ text: MODE_DEEPDIVE2(" Loading deep dives..."), spinner: "dots" }).start();
|
|
1208
1101
|
try {
|
|
1209
|
-
const result = await callCloudFunction("listCLIDeepDives", { conversationId
|
|
1102
|
+
const result = await callCloudFunction("listCLIDeepDives", { conversationId });
|
|
1210
1103
|
spinner.stop();
|
|
1211
1104
|
const dives = result.deepDives || [];
|
|
1212
1105
|
if (dives.length === 0) {
|
|
@@ -1246,7 +1139,7 @@ function registerDeepDiveCommand(program2) {
|
|
|
1246
1139
|
await new Promise((resolve3) => setTimeout(resolve3, 3e3));
|
|
1247
1140
|
animation.stop();
|
|
1248
1141
|
await new Promise((resolve3) => setTimeout(resolve3, 300));
|
|
1249
|
-
await runDeepDiveSession(config,
|
|
1142
|
+
await runDeepDiveSession(config, conversationId, parentMessageId, initiatingPrompt, rl);
|
|
1250
1143
|
rl.close();
|
|
1251
1144
|
} catch (error) {
|
|
1252
1145
|
spinner.stop();
|
|
@@ -1263,7 +1156,8 @@ function registerDeepDiveCommand(program2) {
|
|
|
1263
1156
|
console.log("");
|
|
1264
1157
|
return;
|
|
1265
1158
|
}
|
|
1266
|
-
|
|
1159
|
+
const conversationId = getActiveConversationId(process.cwd()) || config.conversationId;
|
|
1160
|
+
if (!conversationId) {
|
|
1267
1161
|
console.log("");
|
|
1268
1162
|
console.log(ERROR2(" \u274C No active conversation."));
|
|
1269
1163
|
console.log(MUTED2(" Join one with `bob conversations join` first."));
|
|
@@ -1271,7 +1165,7 @@ function registerDeepDiveCommand(program2) {
|
|
|
1271
1165
|
return;
|
|
1272
1166
|
}
|
|
1273
1167
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
1274
|
-
await enterDeepDive(config,
|
|
1168
|
+
await enterDeepDive(config, conversationId, rl);
|
|
1275
1169
|
rl.close();
|
|
1276
1170
|
});
|
|
1277
1171
|
}
|
|
@@ -1496,7 +1390,7 @@ Current request: ${trimmed}` : trimmed;
|
|
|
1496
1390
|
|
|
1497
1391
|
// src/ui/session-header.ts
|
|
1498
1392
|
import chalk6 from "chalk";
|
|
1499
|
-
import * as
|
|
1393
|
+
import * as path5 from "path";
|
|
1500
1394
|
var BRAND_PRIMARY2 = chalk6.hex("#E66F24");
|
|
1501
1395
|
var BRAND_SECONDARY3 = chalk6.hex("#FFAB00");
|
|
1502
1396
|
var SUCCESS3 = chalk6.hex("#66BB6A");
|
|
@@ -1518,7 +1412,7 @@ function pad(content, visibleLen) {
|
|
|
1518
1412
|
}
|
|
1519
1413
|
function renderSessionHeader(mode) {
|
|
1520
1414
|
const config = getConfig();
|
|
1521
|
-
const projectName =
|
|
1415
|
+
const projectName = path5.basename(process.cwd());
|
|
1522
1416
|
const summaries = loadSummaries(process.cwd());
|
|
1523
1417
|
const fileCount = summaries ? Object.keys(summaries).length : 0;
|
|
1524
1418
|
const isIndexed = fileCount > 0;
|
|
@@ -1695,9 +1589,10 @@ var lastConstraints = [];
|
|
|
1695
1589
|
function registerChatCommand(program2) {
|
|
1696
1590
|
program2.command("chat [message]").description("Chat with Bob \u2014 code-friendly engineering partner").option("-f, --file <path>", "Include a specific file as context").option("--no-context", "Skip local directory context").option("--personalized", "Use personalization mode (Tier 3 only)").option("--new", "Start a fresh conversation").option("-i, --interactive", "Enter interactive conversation mode").action(async (message, options) => {
|
|
1697
1591
|
const config = getConfig();
|
|
1698
|
-
let conversationId = config.conversationId;
|
|
1592
|
+
let conversationId = getActiveConversationId(process.cwd()) || config.conversationId || null;
|
|
1699
1593
|
if (options.new || !conversationId) {
|
|
1700
1594
|
conversationId = `cli_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
1595
|
+
setActiveConversationId(conversationId, process.cwd());
|
|
1701
1596
|
setConfigValue("conversationId", conversationId);
|
|
1702
1597
|
}
|
|
1703
1598
|
let localContext = "";
|
|
@@ -1876,6 +1771,7 @@ async function runInteractiveSession(config, conversationId, localContext, perso
|
|
|
1876
1771
|
history.length = 0;
|
|
1877
1772
|
lastConstraints = [];
|
|
1878
1773
|
conversationId = `cli_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
1774
|
+
setActiveConversationId(conversationId, process.cwd());
|
|
1879
1775
|
setConfigValue("conversationId", conversationId);
|
|
1880
1776
|
console.log(INFO6(" \u{1F504} New session started."));
|
|
1881
1777
|
console.log("");
|
|
@@ -1912,8 +1808,8 @@ ${content}
|
|
|
1912
1808
|
}
|
|
1913
1809
|
if (trimmed.startsWith("/delete ")) {
|
|
1914
1810
|
const filePath = trimmed.slice(8).trim();
|
|
1915
|
-
const absolutePath =
|
|
1916
|
-
if (!
|
|
1811
|
+
const absolutePath = path6.resolve(process.cwd(), filePath);
|
|
1812
|
+
if (!fs5.existsSync(absolutePath)) {
|
|
1917
1813
|
console.log(ERROR4(` \u274C File not found: ${filePath}`));
|
|
1918
1814
|
console.log("");
|
|
1919
1815
|
prompt();
|
|
@@ -1942,10 +1838,10 @@ ${content}
|
|
|
1942
1838
|
rl.resume();
|
|
1943
1839
|
if (confirm.toLowerCase() === "y" || confirm.toLowerCase() === "yes") {
|
|
1944
1840
|
try {
|
|
1945
|
-
const backupDir =
|
|
1946
|
-
if (!
|
|
1947
|
-
|
|
1948
|
-
|
|
1841
|
+
const backupDir = path6.join(process.cwd(), ".bob-backups");
|
|
1842
|
+
if (!fs5.existsSync(backupDir)) fs5.mkdirSync(backupDir, { recursive: true });
|
|
1843
|
+
fs5.copyFileSync(absolutePath, path6.join(backupDir, filePath.replace(/[\/\\]/g, "_") + `.${Date.now()}.deleted`));
|
|
1844
|
+
fs5.unlinkSync(absolutePath);
|
|
1949
1845
|
console.log(SUCCESS6(` \u2705 Deleted: ${filePath}`));
|
|
1950
1846
|
console.log(MUTED6(` \u{1F4E6} Backup saved to .bob-backups/`));
|
|
1951
1847
|
} catch (e) {
|
|
@@ -1988,9 +1884,10 @@ var lastConstraints2 = [];
|
|
|
1988
1884
|
function registerConsultCommand(program2) {
|
|
1989
1885
|
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) => {
|
|
1990
1886
|
const config = getConfig();
|
|
1991
|
-
let conversationId = config.conversationId;
|
|
1887
|
+
let conversationId = getActiveConversationId(process.cwd()) || config.conversationId || null;
|
|
1992
1888
|
if (options.new || !conversationId) {
|
|
1993
1889
|
conversationId = `cli_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
1890
|
+
setActiveConversationId(conversationId, process.cwd());
|
|
1994
1891
|
setConfigValue("conversationId", conversationId);
|
|
1995
1892
|
}
|
|
1996
1893
|
let localContext = "";
|
|
@@ -2122,6 +2019,7 @@ async function runInteractiveSession2(config, conversationId, localContext) {
|
|
|
2122
2019
|
history.length = 0;
|
|
2123
2020
|
lastConstraints2 = [];
|
|
2124
2021
|
conversationId = `cli_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
2022
|
+
setActiveConversationId(conversationId, process.cwd());
|
|
2125
2023
|
setConfigValue("conversationId", conversationId);
|
|
2126
2024
|
console.log(MODE_CONSULTANT4(" \u{1F504} New consultant session started."));
|
|
2127
2025
|
console.log("");
|
|
@@ -2174,8 +2072,8 @@ ${content}
|
|
|
2174
2072
|
|
|
2175
2073
|
// src/commands/index.ts
|
|
2176
2074
|
import chalk11 from "chalk";
|
|
2177
|
-
import * as
|
|
2178
|
-
import * as
|
|
2075
|
+
import * as fs6 from "fs";
|
|
2076
|
+
import * as path7 from "path";
|
|
2179
2077
|
var BRAND_PRIMARY5 = chalk11.hex("#E66F24");
|
|
2180
2078
|
var BRAND_SECONDARY8 = chalk11.hex("#FFAB00");
|
|
2181
2079
|
var SUCCESS8 = chalk11.hex("#66BB6A");
|
|
@@ -2218,10 +2116,10 @@ function registerIndexCommand(program2) {
|
|
|
2218
2116
|
const summaries = {};
|
|
2219
2117
|
let completed = 0;
|
|
2220
2118
|
for (const filePath of files) {
|
|
2221
|
-
const absolutePath =
|
|
2119
|
+
const absolutePath = path7.join(cwd, filePath);
|
|
2222
2120
|
let content;
|
|
2223
2121
|
try {
|
|
2224
|
-
content =
|
|
2122
|
+
content = fs6.readFileSync(absolutePath, "utf-8");
|
|
2225
2123
|
} catch {
|
|
2226
2124
|
console.log(ERROR6(` \u274C Could not read: ${filePath}`));
|
|
2227
2125
|
continue;
|
|
@@ -2298,11 +2196,11 @@ Respond with ONLY the JSON object:`
|
|
|
2298
2196
|
saveDependencies(cwd, dependencies);
|
|
2299
2197
|
for (const [filePath, deps] of Object.entries(dependencies)) {
|
|
2300
2198
|
const taskId = filePath.replace(/[\/\\]/g, "_");
|
|
2301
|
-
const taskPath =
|
|
2302
|
-
if (
|
|
2303
|
-
const task = JSON.parse(
|
|
2199
|
+
const taskPath = path7.join(tasksDir, `${taskId}.json`);
|
|
2200
|
+
if (fs6.existsSync(taskPath)) {
|
|
2201
|
+
const task = JSON.parse(fs6.readFileSync(taskPath, "utf-8"));
|
|
2304
2202
|
task.dependencies = deps;
|
|
2305
|
-
|
|
2203
|
+
fs6.writeFileSync(taskPath, JSON.stringify(task, null, 2));
|
|
2306
2204
|
}
|
|
2307
2205
|
}
|
|
2308
2206
|
updateManifestProgress(runDir, completed, "completed");
|
|
@@ -2325,16 +2223,16 @@ function scanProjectFiles(rootDir, currentDir, depth = 0) {
|
|
|
2325
2223
|
const dir = currentDir || rootDir;
|
|
2326
2224
|
const files = [];
|
|
2327
2225
|
try {
|
|
2328
|
-
const entries =
|
|
2226
|
+
const entries = fs6.readdirSync(dir, { withFileTypes: true });
|
|
2329
2227
|
for (const entry of entries) {
|
|
2330
2228
|
if (IGNORE_DIRS.includes(entry.name)) continue;
|
|
2331
2229
|
if (entry.name.startsWith(".")) continue;
|
|
2332
|
-
const fullPath =
|
|
2333
|
-
const relativePath =
|
|
2230
|
+
const fullPath = path7.join(dir, entry.name);
|
|
2231
|
+
const relativePath = path7.relative(rootDir, fullPath).replace(/\\/g, "/");
|
|
2334
2232
|
if (entry.isDirectory()) {
|
|
2335
2233
|
files.push(...scanProjectFiles(rootDir, fullPath, depth + 1));
|
|
2336
2234
|
} else {
|
|
2337
|
-
const ext =
|
|
2235
|
+
const ext = path7.extname(entry.name).toLowerCase();
|
|
2338
2236
|
if (CODE_EXTENSIONS.has(ext)) {
|
|
2339
2237
|
files.push(relativePath);
|
|
2340
2238
|
}
|
|
@@ -2738,6 +2636,7 @@ function registerConversationsCommand(program2) {
|
|
|
2738
2636
|
return;
|
|
2739
2637
|
}
|
|
2740
2638
|
const selected = conversations[selection - 1];
|
|
2639
|
+
setActiveConversationId(selected.id, process.cwd());
|
|
2741
2640
|
setConfigValue("conversationId", selected.id);
|
|
2742
2641
|
console.log("");
|
|
2743
2642
|
console.log(SUCCESS11(` \u2705 Joined: "${selected.title}"`));
|
|
@@ -2982,14 +2881,14 @@ function registerForkCommand(program2) {
|
|
|
2982
2881
|
console.log("");
|
|
2983
2882
|
return;
|
|
2984
2883
|
}
|
|
2985
|
-
|
|
2884
|
+
const parentConvoId = getActiveConversationId(process.cwd()) || config.conversationId;
|
|
2885
|
+
if (!parentConvoId) {
|
|
2986
2886
|
console.log("");
|
|
2987
2887
|
console.log(ERROR10(" \u274C No active conversation to fork from."));
|
|
2988
2888
|
console.log(MUTED12(" Start a conversation first with `bob chat`, or join one with `bob conversations join`."));
|
|
2989
2889
|
console.log("");
|
|
2990
2890
|
return;
|
|
2991
2891
|
}
|
|
2992
|
-
const parentConvoId = config.conversationId;
|
|
2993
2892
|
console.log("");
|
|
2994
2893
|
console.log(chalk16.bold(MODE_CONSULTANT6(` \u26A1 Forking: "${title}"`)));
|
|
2995
2894
|
console.log(MUTED12(` From: ${parentConvoId.slice(0, 24)}...`));
|
|
@@ -3006,6 +2905,7 @@ function registerForkCommand(program2) {
|
|
|
3006
2905
|
animation.stop();
|
|
3007
2906
|
await new Promise((resolve3) => setTimeout(resolve3, 200));
|
|
3008
2907
|
if (result?.conversationId) {
|
|
2908
|
+
setActiveConversationId(result.conversationId, process.cwd());
|
|
3009
2909
|
setConfigValue("conversationId", result.conversationId);
|
|
3010
2910
|
console.log("");
|
|
3011
2911
|
console.log(SUCCESS12(` \u2705 Fork created: "${title}"`));
|
|
@@ -3052,7 +2952,8 @@ function registerForkCommand(program2) {
|
|
|
3052
2952
|
console.log("");
|
|
3053
2953
|
return;
|
|
3054
2954
|
}
|
|
3055
|
-
|
|
2955
|
+
const conversationId = getActiveConversationId(process.cwd()) || config.conversationId;
|
|
2956
|
+
if (!conversationId) {
|
|
3056
2957
|
console.log("");
|
|
3057
2958
|
console.log(ERROR10(" \u274C No active conversation."));
|
|
3058
2959
|
console.log("");
|
|
@@ -3061,9 +2962,7 @@ function registerForkCommand(program2) {
|
|
|
3061
2962
|
console.log("");
|
|
3062
2963
|
console.log(chalk16.bold(MODE_CONSULTANT6(" \u{1F500} Loading forks...")));
|
|
3063
2964
|
try {
|
|
3064
|
-
const result = await callCloudFunction("listConversationForks", {
|
|
3065
|
-
conversationId: config.conversationId
|
|
3066
|
-
});
|
|
2965
|
+
const result = await callCloudFunction("listConversationForks", { conversationId });
|
|
3067
2966
|
const forks = result.forks || [];
|
|
3068
2967
|
console.log("");
|
|
3069
2968
|
console.log(chalk16.bold(MODE_CONSULTANT6(" \u{1F500} Forks")));
|
|
@@ -3093,8 +2992,8 @@ function registerForkCommand(program2) {
|
|
|
3093
2992
|
// src/commands/analyse.ts
|
|
3094
2993
|
import chalk17 from "chalk";
|
|
3095
2994
|
import ora5 from "ora";
|
|
3096
|
-
import * as
|
|
3097
|
-
import * as
|
|
2995
|
+
import * as fs7 from "fs";
|
|
2996
|
+
import * as path8 from "path";
|
|
3098
2997
|
var BRAND_PRIMARY8 = chalk17.hex("#E66F24");
|
|
3099
2998
|
var BRAND_SECONDARY12 = chalk17.hex("#FFAB00");
|
|
3100
2999
|
var SUCCESS13 = chalk17.hex("#66BB6A");
|
|
@@ -3108,7 +3007,7 @@ function registerAnalyseCommand(program2) {
|
|
|
3108
3007
|
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) => {
|
|
3109
3008
|
const config = getConfig();
|
|
3110
3009
|
if (options.auto) {
|
|
3111
|
-
const { runAutoFix } = await import("./analyse-auto-
|
|
3010
|
+
const { runAutoFix } = await import("./analyse-auto-DCC6UDFV.js");
|
|
3112
3011
|
const category = options.bugs ? "bugs" : options.features ? "features" : options.improvements ? "improvements" : options.upgrades ? "upgrades" : void 0;
|
|
3113
3012
|
await runAutoFix({
|
|
3114
3013
|
category,
|
|
@@ -3118,7 +3017,7 @@ function registerAnalyseCommand(program2) {
|
|
|
3118
3017
|
return;
|
|
3119
3018
|
}
|
|
3120
3019
|
if (options.bugs || options.features || options.improvements || options.upgrades) {
|
|
3121
|
-
const { showInteractiveResults } = await import("./analyse-results-
|
|
3020
|
+
const { showInteractiveResults } = await import("./analyse-results-4S577CG5.js");
|
|
3122
3021
|
const category = options.bugs ? "bugs" : options.features ? "features" : options.improvements ? "improvements" : "upgrades";
|
|
3123
3022
|
await showInteractiveResults(config, category, options.sort, options.search);
|
|
3124
3023
|
return;
|
|
@@ -3240,7 +3139,8 @@ function renderAnalysisDashboard(counts) {
|
|
|
3240
3139
|
console.log("");
|
|
3241
3140
|
}
|
|
3242
3141
|
async function showStatus(config) {
|
|
3243
|
-
|
|
3142
|
+
const conversationId = getActiveConversationId(process.cwd()) || config.conversationId;
|
|
3143
|
+
if (!config.loggedIn || !config.authToken || !conversationId) {
|
|
3244
3144
|
console.log("");
|
|
3245
3145
|
console.log(WARNING12(" \u26A0\uFE0F Status check requires Tier 3 with an active conversation."));
|
|
3246
3146
|
console.log("");
|
|
@@ -3249,7 +3149,7 @@ async function showStatus(config) {
|
|
|
3249
3149
|
const spinner = ora5({ text: INFO13(" Checking analysis status..."), spinner: "dots" }).start();
|
|
3250
3150
|
try {
|
|
3251
3151
|
const result = await callCloudFunction("getCLIAnalysisResults", {
|
|
3252
|
-
conversationId
|
|
3152
|
+
conversationId,
|
|
3253
3153
|
action: "status"
|
|
3254
3154
|
});
|
|
3255
3155
|
spinner.stop();
|
|
@@ -3318,15 +3218,15 @@ async function runAnalysis(config) {
|
|
|
3318
3218
|
console.log("");
|
|
3319
3219
|
console.log("");
|
|
3320
3220
|
const { analysisDir } = ensureProjectStructure(cwd);
|
|
3321
|
-
const resultsDir =
|
|
3322
|
-
if (!
|
|
3221
|
+
const resultsDir = path8.join(analysisDir, "results");
|
|
3222
|
+
if (!fs7.existsSync(resultsDir)) fs7.mkdirSync(resultsDir, { recursive: true });
|
|
3323
3223
|
let completed = 0;
|
|
3324
3224
|
const allResults = {};
|
|
3325
3225
|
for (const filePath of files) {
|
|
3326
|
-
const absolutePath =
|
|
3226
|
+
const absolutePath = path8.join(cwd, filePath);
|
|
3327
3227
|
let content;
|
|
3328
3228
|
try {
|
|
3329
|
-
content =
|
|
3229
|
+
content = fs7.readFileSync(absolutePath, "utf-8");
|
|
3330
3230
|
} catch (error) {
|
|
3331
3231
|
console.error(ERROR11(` \u274C Could not read file ${filePath}: ${error.message}`));
|
|
3332
3232
|
completed++;
|
|
@@ -3396,7 +3296,7 @@ ${content}`;
|
|
|
3396
3296
|
}
|
|
3397
3297
|
completed++;
|
|
3398
3298
|
}
|
|
3399
|
-
|
|
3299
|
+
fs7.writeFileSync(path8.join(resultsDir, "analysis.json"), JSON.stringify(allResults, null, 2));
|
|
3400
3300
|
let totalBugs = 0, totalFeatures = 0, totalImprovements = 0, totalUpgrades = 0;
|
|
3401
3301
|
for (const fileResults of Object.values(allResults)) {
|
|
3402
3302
|
const r = fileResults;
|
|
@@ -3405,7 +3305,7 @@ ${content}`;
|
|
|
3405
3305
|
totalImprovements += r.improvements?.length || 0;
|
|
3406
3306
|
totalUpgrades += r.upgrades?.length || 0;
|
|
3407
3307
|
}
|
|
3408
|
-
|
|
3308
|
+
fs7.writeFileSync(path8.join(resultsDir, "counts.json"), JSON.stringify({
|
|
3409
3309
|
bugs: totalBugs,
|
|
3410
3310
|
features: totalFeatures,
|
|
3411
3311
|
improvements: totalImprovements,
|
|
@@ -3422,18 +3322,18 @@ ${content}`;
|
|
|
3422
3322
|
function loadLocalCounts() {
|
|
3423
3323
|
const cwd = process.cwd();
|
|
3424
3324
|
const { analysisDir } = ensureProjectStructure(cwd);
|
|
3425
|
-
const countsPath =
|
|
3426
|
-
if (!
|
|
3427
|
-
return JSON.parse(
|
|
3325
|
+
const countsPath = path8.join(analysisDir, "results", "counts.json");
|
|
3326
|
+
if (!fs7.existsSync(countsPath)) return null;
|
|
3327
|
+
return JSON.parse(fs7.readFileSync(countsPath, "utf-8"));
|
|
3428
3328
|
}
|
|
3429
3329
|
function loadAddressedCount() {
|
|
3430
3330
|
const cwd = process.cwd();
|
|
3431
|
-
const projectName =
|
|
3331
|
+
const projectName = path8.basename(cwd);
|
|
3432
3332
|
const homeDir = process.env.HOME || process.env.USERPROFILE || "";
|
|
3433
|
-
const analysisPath =
|
|
3434
|
-
if (!
|
|
3333
|
+
const analysisPath = path8.join(homeDir, ".bob", "projects", projectName, "analysis", "results", "analysis.json");
|
|
3334
|
+
if (!fs7.existsSync(analysisPath)) return 0;
|
|
3435
3335
|
try {
|
|
3436
|
-
const allResults = JSON.parse(
|
|
3336
|
+
const allResults = JSON.parse(fs7.readFileSync(analysisPath, "utf-8"));
|
|
3437
3337
|
let addressed = 0;
|
|
3438
3338
|
for (const fileResults of Object.values(allResults)) {
|
|
3439
3339
|
for (const category of ["bugs", "features", "improvements", "upgrades"]) {
|
|
@@ -3474,8 +3374,8 @@ import chalk18 from "chalk";
|
|
|
3474
3374
|
import ora6 from "ora";
|
|
3475
3375
|
import * as readline6 from "readline";
|
|
3476
3376
|
import simpleGit2 from "simple-git";
|
|
3477
|
-
import * as
|
|
3478
|
-
import * as
|
|
3377
|
+
import * as fs8 from "fs";
|
|
3378
|
+
import * as path9 from "path";
|
|
3479
3379
|
var RED = chalk18.hex("#EF5350");
|
|
3480
3380
|
var GREEN = chalk18.hex("#66BB6A");
|
|
3481
3381
|
var AMBER2 = chalk18.hex("#FFAB00");
|
|
@@ -3486,33 +3386,34 @@ var CYAN = chalk18.cyan;
|
|
|
3486
3386
|
function registerAutonomyCommand(program2) {
|
|
3487
3387
|
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) => {
|
|
3488
3388
|
const config = getConfig();
|
|
3389
|
+
const conversationId = getActiveConversationId(process.cwd()) || config.conversationId;
|
|
3489
3390
|
if (options.status) {
|
|
3490
|
-
await showAutonomyStatus(config);
|
|
3391
|
+
await showAutonomyStatus(config, conversationId);
|
|
3491
3392
|
return;
|
|
3492
3393
|
}
|
|
3493
3394
|
if (options.stop) {
|
|
3494
3395
|
console.log(chalk18.yellow(" \u26A0\uFE0F Stop command not yet implemented for Tier 3."));
|
|
3495
3396
|
return;
|
|
3496
3397
|
}
|
|
3497
|
-
if (config.tier === "platform" && config.provider !== "local" && config.loggedIn &&
|
|
3498
|
-
await runTier3Autonomy(config);
|
|
3398
|
+
if (config.tier === "platform" && config.provider !== "local" && config.loggedIn && conversationId) {
|
|
3399
|
+
await runTier3Autonomy(config, conversationId);
|
|
3499
3400
|
} else {
|
|
3500
3401
|
await runTier1Autonomy(config, options);
|
|
3501
3402
|
}
|
|
3502
3403
|
});
|
|
3503
3404
|
}
|
|
3504
|
-
async function runTier3Autonomy(config) {
|
|
3405
|
+
async function runTier3Autonomy(config, conversationId) {
|
|
3505
3406
|
console.log("");
|
|
3506
3407
|
console.log(chalk18.bold.cyan(" \u26A1 MiniBob Autonomy Mode (Platform)"));
|
|
3507
3408
|
console.log(GRAY(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
3508
|
-
console.log(GRAY(` \u{1F4E1} Conversation: ${
|
|
3509
|
-
console.log(GRAY(` \u{1F517} https://bobs-workshop.web.app/#/bobcodeassistant/${
|
|
3409
|
+
console.log(GRAY(` \u{1F4E1} Conversation: ${conversationId?.slice(0, 24)}...`));
|
|
3410
|
+
console.log(GRAY(` \u{1F517} https://bobs-workshop.web.app/#/bobcodeassistant/${conversationId}`));
|
|
3510
3411
|
console.log(GRAY(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
3511
3412
|
console.log("");
|
|
3512
3413
|
const spinner = ora6({ text: CYAN(" Igniting autonomy workers..."), spinner: "dots" }).start();
|
|
3513
3414
|
try {
|
|
3514
3415
|
const result = await callCloudFunction("startMiniBobAutonomy", {
|
|
3515
|
-
conversationId
|
|
3416
|
+
conversationId,
|
|
3516
3417
|
proxyEmail: null
|
|
3517
3418
|
});
|
|
3518
3419
|
spinner.stop();
|
|
@@ -3547,7 +3448,7 @@ async function runTier3Autonomy(config) {
|
|
|
3547
3448
|
while (running) {
|
|
3548
3449
|
try {
|
|
3549
3450
|
const updates = await callCloudFunction("getCLITerminalUpdates", {
|
|
3550
|
-
conversationId
|
|
3451
|
+
conversationId,
|
|
3551
3452
|
since: lastTimestamp
|
|
3552
3453
|
});
|
|
3553
3454
|
if (updates?.lines && updates.lines.length > 0) {
|
|
@@ -3579,7 +3480,7 @@ async function runTier3Autonomy(config) {
|
|
|
3579
3480
|
rl.close();
|
|
3580
3481
|
if (answer.toLowerCase() === "y" || answer.toLowerCase() === "yes") {
|
|
3581
3482
|
try {
|
|
3582
|
-
await callCloudFunction("commitAndPushChanges", { conversationId
|
|
3483
|
+
await callCloudFunction("commitAndPushChanges", { conversationId });
|
|
3583
3484
|
console.log(GREEN(" \u2705 Pushed to GitHub!"));
|
|
3584
3485
|
} catch (pushErr) {
|
|
3585
3486
|
console.log(RED(` \u274C Push failed: ${pushErr.message}`));
|
|
@@ -3729,17 +3630,16 @@ Autonomous repair by Bob's CLI.`;
|
|
|
3729
3630
|
console.log(GRAY(" \u{1F4E6} All original files backed up to .bob-backups/"));
|
|
3730
3631
|
console.log("");
|
|
3731
3632
|
}
|
|
3732
|
-
async function showAutonomyStatus(config) {
|
|
3733
|
-
if (!config.loggedIn || !
|
|
3633
|
+
async function showAutonomyStatus(config, conversationId) {
|
|
3634
|
+
if (!config.loggedIn || !conversationId) {
|
|
3734
3635
|
console.log(chalk18.yellow(" \u26A0\uFE0F Status requires Tier 3 with an active conversation."));
|
|
3735
3636
|
return;
|
|
3736
3637
|
}
|
|
3737
3638
|
const spinner = ora6({ text: CYAN(" Checking autonomy status..."), spinner: "dots" }).start();
|
|
3738
3639
|
try {
|
|
3739
3640
|
const result = await callCloudFunction("getCLITerminalUpdates", {
|
|
3740
|
-
conversationId
|
|
3641
|
+
conversationId,
|
|
3741
3642
|
since: new Date(Date.now() - 6e4).toISOString(),
|
|
3742
|
-
// Last 60 seconds
|
|
3743
3643
|
limit: 5
|
|
3744
3644
|
});
|
|
3745
3645
|
spinner.stop();
|
|
@@ -3813,17 +3713,17 @@ Return the complete file content now:`;
|
|
|
3813
3713
|
return false;
|
|
3814
3714
|
}
|
|
3815
3715
|
}
|
|
3816
|
-
const absolutePath =
|
|
3817
|
-
const backupDir =
|
|
3818
|
-
if (!
|
|
3819
|
-
if (
|
|
3716
|
+
const absolutePath = path9.join(process.cwd(), suggestion.filePath);
|
|
3717
|
+
const backupDir = path9.join(process.cwd(), ".bob-backups");
|
|
3718
|
+
if (!fs8.existsSync(backupDir)) fs8.mkdirSync(backupDir, { recursive: true });
|
|
3719
|
+
if (fs8.existsSync(absolutePath)) {
|
|
3820
3720
|
const timestamp = Date.now();
|
|
3821
3721
|
const backupName = suggestion.filePath.replace(/[\/\\]/g, "_") + `.${timestamp}.bak`;
|
|
3822
|
-
|
|
3722
|
+
fs8.copyFileSync(absolutePath, path9.join(backupDir, backupName));
|
|
3823
3723
|
}
|
|
3824
|
-
const dir =
|
|
3825
|
-
if (!
|
|
3826
|
-
|
|
3724
|
+
const dir = path9.dirname(absolutePath);
|
|
3725
|
+
if (!fs8.existsSync(dir)) fs8.mkdirSync(dir, { recursive: true });
|
|
3726
|
+
fs8.writeFileSync(absolutePath, newContent, "utf-8");
|
|
3827
3727
|
return true;
|
|
3828
3728
|
} catch {
|
|
3829
3729
|
return false;
|
|
@@ -3831,11 +3731,11 @@ Return the complete file content now:`;
|
|
|
3831
3731
|
}
|
|
3832
3732
|
function detectLocalCategory(suggestion) {
|
|
3833
3733
|
const cwd = process.cwd();
|
|
3834
|
-
const projectName =
|
|
3734
|
+
const projectName = path9.basename(cwd);
|
|
3835
3735
|
const homeDir = process.env.HOME || process.env.USERPROFILE || "";
|
|
3836
|
-
const analysisPath =
|
|
3837
|
-
if (!
|
|
3838
|
-
const allResults = JSON.parse(
|
|
3736
|
+
const analysisPath = path9.join(homeDir, ".bob", "projects", projectName, "analysis", "results", "analysis.json");
|
|
3737
|
+
if (!fs8.existsSync(analysisPath)) return "bugs";
|
|
3738
|
+
const allResults = JSON.parse(fs8.readFileSync(analysisPath, "utf-8"));
|
|
3839
3739
|
const fileResults = allResults[suggestion.filePath];
|
|
3840
3740
|
if (!fileResults) return "bugs";
|
|
3841
3741
|
for (const cat of ["bugs", "features", "improvements", "upgrades"]) {
|
|
@@ -3922,9 +3822,9 @@ function renderLocalTodoList(queue) {
|
|
|
3922
3822
|
|
|
3923
3823
|
// src/commands/serve.ts
|
|
3924
3824
|
import chalk19 from "chalk";
|
|
3925
|
-
import * as
|
|
3926
|
-
import * as
|
|
3927
|
-
import * as
|
|
3825
|
+
import * as fs9 from "fs";
|
|
3826
|
+
import * as os2 from "os";
|
|
3827
|
+
import * as path10 from "path";
|
|
3928
3828
|
import * as crypto2 from "crypto";
|
|
3929
3829
|
import axios from "axios";
|
|
3930
3830
|
var GREEN2 = chalk19.hex("#66BB6A");
|
|
@@ -3933,7 +3833,7 @@ var RED2 = chalk19.hex("#EF5350");
|
|
|
3933
3833
|
var GRAY2 = chalk19.gray;
|
|
3934
3834
|
var CYAN2 = chalk19.cyan;
|
|
3935
3835
|
var BORDER5 = chalk19.hex("#455A64");
|
|
3936
|
-
var
|
|
3836
|
+
var BOB_DIR2 = path10.join(os2.homedir(), ".bob");
|
|
3937
3837
|
var ALGORITHM = "aes-256-cbc";
|
|
3938
3838
|
var TIER_CONFIGS = {
|
|
3939
3839
|
"Power": {
|
|
@@ -3964,13 +3864,13 @@ function encrypt(inputPath, outputPath, uid) {
|
|
|
3964
3864
|
const key = deriveKey(uid, salt);
|
|
3965
3865
|
const iv = crypto2.randomBytes(16);
|
|
3966
3866
|
const cipher = crypto2.createCipheriv(ALGORITHM, key, iv);
|
|
3967
|
-
const input =
|
|
3867
|
+
const input = fs9.readFileSync(inputPath);
|
|
3968
3868
|
const encrypted = Buffer.concat([cipher.update(input), cipher.final()]);
|
|
3969
3869
|
const header = Buffer.from(salt + iv.toString("hex"), "utf-8");
|
|
3970
|
-
|
|
3870
|
+
fs9.writeFileSync(outputPath, Buffer.concat([header, encrypted]));
|
|
3971
3871
|
}
|
|
3972
3872
|
function decrypt(inputPath, outputPath, uid) {
|
|
3973
|
-
const data =
|
|
3873
|
+
const data = fs9.readFileSync(inputPath);
|
|
3974
3874
|
const header = data.slice(0, 64).toString("utf-8");
|
|
3975
3875
|
const salt = header.slice(0, 32);
|
|
3976
3876
|
const iv = Buffer.from(header.slice(32, 64), "hex");
|
|
@@ -3978,16 +3878,16 @@ function decrypt(inputPath, outputPath, uid) {
|
|
|
3978
3878
|
const key = deriveKey(uid, salt);
|
|
3979
3879
|
const decipher = crypto2.createDecipheriv(ALGORITHM, key, iv);
|
|
3980
3880
|
const decrypted = Buffer.concat([decipher.update(encrypted), decipher.final()]);
|
|
3981
|
-
|
|
3881
|
+
fs9.writeFileSync(outputPath, decrypted);
|
|
3982
3882
|
}
|
|
3983
3883
|
function getTempDir() {
|
|
3984
|
-
const tmpDir =
|
|
3985
|
-
|
|
3884
|
+
const tmpDir = path10.join(os2.tmpdir(), `bob-remote-${Date.now()}`);
|
|
3885
|
+
fs9.mkdirSync(tmpDir, { recursive: true });
|
|
3986
3886
|
return tmpDir;
|
|
3987
3887
|
}
|
|
3988
3888
|
function cleanupTemp(tmpDir) {
|
|
3989
3889
|
try {
|
|
3990
|
-
|
|
3890
|
+
fs9.rmSync(tmpDir, { recursive: true, force: true });
|
|
3991
3891
|
} catch {
|
|
3992
3892
|
}
|
|
3993
3893
|
}
|
|
@@ -3998,16 +3898,16 @@ function scanProjectFiles2(rootDir, currentDir, depth = 0) {
|
|
|
3998
3898
|
const dir = currentDir || rootDir;
|
|
3999
3899
|
const files = [];
|
|
4000
3900
|
try {
|
|
4001
|
-
const entries =
|
|
3901
|
+
const entries = fs9.readdirSync(dir, { withFileTypes: true });
|
|
4002
3902
|
for (const entry of entries) {
|
|
4003
3903
|
if (IGNORE_DIRS2.includes(entry.name)) continue;
|
|
4004
3904
|
if (entry.name.startsWith(".")) continue;
|
|
4005
|
-
const fullPath =
|
|
4006
|
-
const relativePath =
|
|
3905
|
+
const fullPath = path10.join(dir, entry.name);
|
|
3906
|
+
const relativePath = path10.relative(rootDir, fullPath).replace(/\\/g, "/");
|
|
4007
3907
|
if (entry.isDirectory()) {
|
|
4008
3908
|
files.push(...scanProjectFiles2(rootDir, fullPath, depth + 1));
|
|
4009
3909
|
} else {
|
|
4010
|
-
const ext =
|
|
3910
|
+
const ext = path10.extname(entry.name).toLowerCase();
|
|
4011
3911
|
if (CODE_EXTENSIONS2.has(ext)) files.push(relativePath);
|
|
4012
3912
|
}
|
|
4013
3913
|
}
|
|
@@ -4074,8 +3974,8 @@ function registerServeCommand(program2) {
|
|
|
4074
3974
|
return;
|
|
4075
3975
|
}
|
|
4076
3976
|
const tierConfig = TIER_CONFIGS[userTier] || TIER_CONFIGS["Starter"];
|
|
4077
|
-
const machineId =
|
|
4078
|
-
const projectName =
|
|
3977
|
+
const machineId = os2.hostname();
|
|
3978
|
+
const projectName = path10.basename(process.cwd());
|
|
4079
3979
|
const sessionId = `${machineId}_${Date.now()}`;
|
|
4080
3980
|
await startActiveBob(config, sessionId, machineId, projectName, tierConfig, userTier);
|
|
4081
3981
|
});
|
|
@@ -4351,10 +4251,10 @@ async function executeIndex(payload, config) {
|
|
|
4351
4251
|
const summaries = {};
|
|
4352
4252
|
let completed = 0;
|
|
4353
4253
|
for (const filePath of files) {
|
|
4354
|
-
const absolutePath =
|
|
4254
|
+
const absolutePath = path10.join(cwd, filePath);
|
|
4355
4255
|
let content;
|
|
4356
4256
|
try {
|
|
4357
|
-
content =
|
|
4257
|
+
content = fs9.readFileSync(absolutePath, "utf-8");
|
|
4358
4258
|
} catch {
|
|
4359
4259
|
completed++;
|
|
4360
4260
|
continue;
|
|
@@ -4446,18 +4346,18 @@ async function executeAnalyse(payload, config) {
|
|
|
4446
4346
|
const dependencies = loadDependencies(cwd) || {};
|
|
4447
4347
|
const files = Object.keys(summaries);
|
|
4448
4348
|
const { analysisDir } = ensureProjectStructure(cwd);
|
|
4449
|
-
const resultsDir =
|
|
4450
|
-
if (!
|
|
4349
|
+
const resultsDir = path10.join(analysisDir, "results");
|
|
4350
|
+
if (!fs9.existsSync(resultsDir)) fs9.mkdirSync(resultsDir, { recursive: true });
|
|
4451
4351
|
const allResults = {};
|
|
4452
4352
|
let totalBugs = 0;
|
|
4453
4353
|
let totalFeatures = 0;
|
|
4454
4354
|
let totalImprovements = 0;
|
|
4455
4355
|
let totalUpgrades = 0;
|
|
4456
4356
|
for (const filePath of files) {
|
|
4457
|
-
const absolutePath =
|
|
4357
|
+
const absolutePath = path10.join(cwd, filePath);
|
|
4458
4358
|
let content;
|
|
4459
4359
|
try {
|
|
4460
|
-
content =
|
|
4360
|
+
content = fs9.readFileSync(absolutePath, "utf-8");
|
|
4461
4361
|
} catch {
|
|
4462
4362
|
continue;
|
|
4463
4363
|
}
|
|
@@ -4508,12 +4408,12 @@ ${content}`;
|
|
|
4508
4408
|
continue;
|
|
4509
4409
|
}
|
|
4510
4410
|
}
|
|
4511
|
-
|
|
4512
|
-
|
|
4411
|
+
fs9.writeFileSync(
|
|
4412
|
+
path10.join(resultsDir, "analysis.json"),
|
|
4513
4413
|
JSON.stringify(allResults, null, 2)
|
|
4514
4414
|
);
|
|
4515
|
-
|
|
4516
|
-
|
|
4415
|
+
fs9.writeFileSync(
|
|
4416
|
+
path10.join(resultsDir, "counts.json"),
|
|
4517
4417
|
JSON.stringify({ bugs: totalBugs, features: totalFeatures, improvements: totalImprovements, upgrades: totalUpgrades }, null, 2)
|
|
4518
4418
|
);
|
|
4519
4419
|
return {
|
|
@@ -4532,33 +4432,33 @@ async function executeBackup(payload, config) {
|
|
|
4532
4432
|
return { success: false, error: "User UID not available. Re-login required." };
|
|
4533
4433
|
}
|
|
4534
4434
|
const cwd = process.cwd();
|
|
4535
|
-
const projectName =
|
|
4435
|
+
const projectName = path10.basename(cwd);
|
|
4536
4436
|
let sourceDir;
|
|
4537
4437
|
let displayLabel;
|
|
4538
4438
|
if (isGlobal) {
|
|
4539
|
-
sourceDir =
|
|
4439
|
+
sourceDir = BOB_DIR2;
|
|
4540
4440
|
displayLabel = "global (~/.bob/)";
|
|
4541
4441
|
} else if (isSource) {
|
|
4542
4442
|
sourceDir = cwd;
|
|
4543
4443
|
displayLabel = `source: ${projectName}`;
|
|
4544
4444
|
} else {
|
|
4545
|
-
sourceDir =
|
|
4445
|
+
sourceDir = path10.join(BOB_DIR2, "projects", projectName);
|
|
4546
4446
|
displayLabel = `context: ${projectName}`;
|
|
4547
4447
|
}
|
|
4548
|
-
if (!
|
|
4448
|
+
if (!fs9.existsSync(sourceDir)) {
|
|
4549
4449
|
return { success: false, error: `Source directory not found: ${sourceDir}` };
|
|
4550
4450
|
}
|
|
4551
4451
|
const tmpDir = getTempDir();
|
|
4552
|
-
const archivePath =
|
|
4553
|
-
const encryptedPath =
|
|
4452
|
+
const archivePath = path10.join(tmpDir, "bob-backup.tar.gz");
|
|
4453
|
+
const encryptedPath = path10.join(tmpDir, "bob-backup.bob.enc");
|
|
4554
4454
|
try {
|
|
4555
4455
|
const tar = await import("tar");
|
|
4556
|
-
const relativeSource =
|
|
4456
|
+
const relativeSource = path10.relative(os2.homedir(), sourceDir);
|
|
4557
4457
|
await tar.create(
|
|
4558
|
-
{ gzip: true, file: archivePath, cwd:
|
|
4458
|
+
{ gzip: true, file: archivePath, cwd: os2.homedir() },
|
|
4559
4459
|
[relativeSource]
|
|
4560
4460
|
);
|
|
4561
|
-
const archiveStats =
|
|
4461
|
+
const archiveStats = fs9.statSync(archivePath);
|
|
4562
4462
|
const estimatedSizeGB = archiveStats.size / (1024 * 1024 * 1024);
|
|
4563
4463
|
const sizeLabel = archiveStats.size < 1024 * 1024 ? `${(archiveStats.size / 1024).toFixed(1)} KB` : `${(archiveStats.size / (1024 * 1024)).toFixed(1)} MB`;
|
|
4564
4464
|
encrypt(archivePath, encryptedPath, config.uid);
|
|
@@ -4572,7 +4472,7 @@ async function executeBackup(payload, config) {
|
|
|
4572
4472
|
archiveName: archiveName || null,
|
|
4573
4473
|
estimatedSizeGB
|
|
4574
4474
|
});
|
|
4575
|
-
const encryptedData =
|
|
4475
|
+
const encryptedData = fs9.readFileSync(encryptedPath);
|
|
4576
4476
|
await axios.put(uploadResult.uploadUrl, encryptedData, {
|
|
4577
4477
|
headers: {
|
|
4578
4478
|
"Content-Type": "application/octet-stream",
|
|
@@ -4613,10 +4513,10 @@ async function executeRestore(payload, config) {
|
|
|
4613
4513
|
return { success: false, error: "User UID not available. Re-login required." };
|
|
4614
4514
|
}
|
|
4615
4515
|
const cwd = process.cwd();
|
|
4616
|
-
const projectName =
|
|
4516
|
+
const projectName = path10.basename(cwd);
|
|
4617
4517
|
const tmpDir = getTempDir();
|
|
4618
|
-
const downloadPath =
|
|
4619
|
-
const decryptedPath =
|
|
4518
|
+
const downloadPath = path10.join(tmpDir, "bob-backup.bob.enc");
|
|
4519
|
+
const decryptedPath = path10.join(tmpDir, "bob-backup.tar.gz");
|
|
4620
4520
|
try {
|
|
4621
4521
|
const downloadResult = await callCloudFunction("cliBackupLicense", {
|
|
4622
4522
|
action: isSource ? "requestSourceDownload" : "requestDownload",
|
|
@@ -4630,26 +4530,26 @@ async function executeRestore(payload, config) {
|
|
|
4630
4530
|
responseType: "arraybuffer",
|
|
4631
4531
|
maxContentLength: Infinity
|
|
4632
4532
|
});
|
|
4633
|
-
|
|
4533
|
+
fs9.writeFileSync(downloadPath, Buffer.from(response.data));
|
|
4634
4534
|
decrypt(downloadPath, decryptedPath, config.uid);
|
|
4635
4535
|
let restoreTarget;
|
|
4636
4536
|
if (isGlobal) {
|
|
4637
|
-
restoreTarget =
|
|
4537
|
+
restoreTarget = BOB_DIR2;
|
|
4638
4538
|
} else if (isSource) {
|
|
4639
4539
|
restoreTarget = cwd;
|
|
4640
4540
|
} else {
|
|
4641
|
-
restoreTarget =
|
|
4541
|
+
restoreTarget = path10.join(BOB_DIR2, "projects", projectName);
|
|
4642
4542
|
}
|
|
4643
4543
|
const preRestoreBackup = `${restoreTarget}-pre-restore-${Date.now()}`;
|
|
4644
|
-
if (
|
|
4645
|
-
|
|
4544
|
+
if (fs9.existsSync(restoreTarget)) {
|
|
4545
|
+
fs9.cpSync(restoreTarget, preRestoreBackup, { recursive: true });
|
|
4646
4546
|
}
|
|
4647
4547
|
const tar = await import("tar");
|
|
4648
4548
|
if (isSource) {
|
|
4649
|
-
const parentDir =
|
|
4549
|
+
const parentDir = path10.dirname(cwd);
|
|
4650
4550
|
await tar.extract({ file: decryptedPath, cwd: parentDir });
|
|
4651
4551
|
} else {
|
|
4652
|
-
await tar.extract({ file: decryptedPath, cwd:
|
|
4552
|
+
await tar.extract({ file: decryptedPath, cwd: os2.homedir() });
|
|
4653
4553
|
}
|
|
4654
4554
|
const scopeLabel = isGlobal ? "global (~/.bob/)" : isSource ? `source: ${projectName}` : `context: ${projectName}`;
|
|
4655
4555
|
return {
|
|
@@ -4658,7 +4558,7 @@ async function executeRestore(payload, config) {
|
|
|
4658
4558
|
projectName,
|
|
4659
4559
|
isSource,
|
|
4660
4560
|
isGlobal,
|
|
4661
|
-
preRestoreBackup:
|
|
4561
|
+
preRestoreBackup: path10.basename(preRestoreBackup)
|
|
4662
4562
|
};
|
|
4663
4563
|
} catch (error) {
|
|
4664
4564
|
return { success: false, error: `Restore failed: ${error.message}` };
|
|
@@ -4945,9 +4845,7 @@ async function showConnectionStatus(config) {
|
|
|
4945
4845
|
}
|
|
4946
4846
|
const spinner = ora7({ text: INFO14(" Checking Active Bob status..."), spinner: "dots" }).start();
|
|
4947
4847
|
try {
|
|
4948
|
-
const result = await callCloudFunction("listActiveBobs", {
|
|
4949
|
-
conversationId: config.conversationId
|
|
4950
|
-
});
|
|
4848
|
+
const result = await callCloudFunction("listActiveBobs", { conversationId: config.conversationId });
|
|
4951
4849
|
spinner.stop();
|
|
4952
4850
|
const sessions = result?.sessions || [];
|
|
4953
4851
|
const activeSessions = sessions.filter((s) => s.active);
|
|
@@ -5026,6 +4924,7 @@ async function discoverAndConnect(config) {
|
|
|
5026
4924
|
return;
|
|
5027
4925
|
}
|
|
5028
4926
|
const selected = bobs[selection - 1];
|
|
4927
|
+
setActiveConversationId(selected.conversationId, process.cwd());
|
|
5029
4928
|
setConfigValue("conversationId", selected.conversationId);
|
|
5030
4929
|
console.log("");
|
|
5031
4930
|
console.log(SUCCESS14(` \u2705 Connected to: ${selected.machineId} (${selected.projectName})`));
|
|
@@ -5720,7 +5619,7 @@ async function renderProfileDashboard() {
|
|
|
5720
5619
|
}
|
|
5721
5620
|
|
|
5722
5621
|
// src/commands/profile.ts
|
|
5723
|
-
import * as
|
|
5622
|
+
import * as path11 from "path";
|
|
5724
5623
|
var AMBER5 = chalk23.hex("#FFAB00");
|
|
5725
5624
|
var GREEN4 = chalk23.hex("#66BB6A");
|
|
5726
5625
|
var BLUE2 = chalk23.hex("#42A5F5");
|
|
@@ -5856,7 +5755,7 @@ async function generateDailyProfile(config) {
|
|
|
5856
5755
|
return;
|
|
5857
5756
|
}
|
|
5858
5757
|
const userMessages = messages.filter((m) => m.role === "user");
|
|
5859
|
-
const projectName =
|
|
5758
|
+
const projectName = path11.basename(process.cwd());
|
|
5860
5759
|
console.log("");
|
|
5861
5760
|
console.log(CYAN4(` \u{1F9EC} Generating daily profile from ${userMessages.length} messages...`));
|
|
5862
5761
|
console.log("");
|
|
@@ -6177,9 +6076,9 @@ function getWeekNumber(date) {
|
|
|
6177
6076
|
// src/commands/backup.ts
|
|
6178
6077
|
import chalk24 from "chalk";
|
|
6179
6078
|
import ora11 from "ora";
|
|
6180
|
-
import * as
|
|
6181
|
-
import * as
|
|
6182
|
-
import * as
|
|
6079
|
+
import * as fs10 from "fs";
|
|
6080
|
+
import * as path12 from "path";
|
|
6081
|
+
import * as os3 from "os";
|
|
6183
6082
|
import * as crypto3 from "crypto";
|
|
6184
6083
|
import axios2 from "axios";
|
|
6185
6084
|
import inquirer from "inquirer";
|
|
@@ -6190,7 +6089,7 @@ var RED5 = chalk24.hex("#EF5350");
|
|
|
6190
6089
|
var AMBER6 = chalk24.hex("#FFAB00");
|
|
6191
6090
|
var GRAY5 = chalk24.gray;
|
|
6192
6091
|
var BORDER10 = chalk24.hex("#455A64");
|
|
6193
|
-
var
|
|
6092
|
+
var BOB_DIR3 = path12.join(os3.homedir(), ".bob");
|
|
6194
6093
|
var ALGORITHM2 = "aes-256-cbc";
|
|
6195
6094
|
function deriveKey2(uid, salt) {
|
|
6196
6095
|
return crypto3.pbkdf2Sync(uid, salt, 1e5, 32, "sha256");
|
|
@@ -6200,13 +6099,13 @@ function encrypt2(inputPath, outputPath, uid) {
|
|
|
6200
6099
|
const key = deriveKey2(uid, salt);
|
|
6201
6100
|
const iv = crypto3.randomBytes(16);
|
|
6202
6101
|
const cipher = crypto3.createCipheriv(ALGORITHM2, key, iv);
|
|
6203
|
-
const input =
|
|
6102
|
+
const input = fs10.readFileSync(inputPath);
|
|
6204
6103
|
const encrypted = Buffer.concat([cipher.update(input), cipher.final()]);
|
|
6205
6104
|
const header = Buffer.from(salt + iv.toString("hex"), "utf-8");
|
|
6206
|
-
|
|
6105
|
+
fs10.writeFileSync(outputPath, Buffer.concat([header, encrypted]));
|
|
6207
6106
|
}
|
|
6208
6107
|
function decrypt2(inputPath, outputPath, uid) {
|
|
6209
|
-
const data =
|
|
6108
|
+
const data = fs10.readFileSync(inputPath);
|
|
6210
6109
|
const header = data.slice(0, 64).toString("utf-8");
|
|
6211
6110
|
const salt = header.slice(0, 32);
|
|
6212
6111
|
const iv = Buffer.from(header.slice(32, 64), "hex");
|
|
@@ -6214,7 +6113,7 @@ function decrypt2(inputPath, outputPath, uid) {
|
|
|
6214
6113
|
const key = deriveKey2(uid, salt);
|
|
6215
6114
|
const decipher = crypto3.createDecipheriv(ALGORITHM2, key, iv);
|
|
6216
6115
|
const decrypted = Buffer.concat([decipher.update(encrypted), decipher.final()]);
|
|
6217
|
-
|
|
6116
|
+
fs10.writeFileSync(outputPath, decrypted);
|
|
6218
6117
|
}
|
|
6219
6118
|
function formatBytes(bytes) {
|
|
6220
6119
|
if (bytes < 1024) return `${bytes} B`;
|
|
@@ -6223,13 +6122,13 @@ function formatBytes(bytes) {
|
|
|
6223
6122
|
return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`;
|
|
6224
6123
|
}
|
|
6225
6124
|
function getTempDir2() {
|
|
6226
|
-
const tmpDir =
|
|
6227
|
-
|
|
6125
|
+
const tmpDir = path12.join(os3.tmpdir(), `bob-backup-${Date.now()}`);
|
|
6126
|
+
fs10.mkdirSync(tmpDir, { recursive: true });
|
|
6228
6127
|
return tmpDir;
|
|
6229
6128
|
}
|
|
6230
6129
|
function cleanupTemp2(tmpDir) {
|
|
6231
6130
|
try {
|
|
6232
|
-
|
|
6131
|
+
fs10.rmSync(tmpDir, { recursive: true, force: true });
|
|
6233
6132
|
} catch {
|
|
6234
6133
|
}
|
|
6235
6134
|
}
|
|
@@ -6244,10 +6143,10 @@ function requireAuth(config) {
|
|
|
6244
6143
|
return true;
|
|
6245
6144
|
}
|
|
6246
6145
|
function getCurrentProjectName() {
|
|
6247
|
-
return
|
|
6146
|
+
return path12.basename(process.cwd());
|
|
6248
6147
|
}
|
|
6249
6148
|
function getProjectBackupDir(projectName) {
|
|
6250
|
-
return
|
|
6149
|
+
return path12.join(BOB_DIR3, "projects", projectName);
|
|
6251
6150
|
}
|
|
6252
6151
|
function normalizeFilePath(filePath) {
|
|
6253
6152
|
return filePath.replace(/\\/g, "/");
|
|
@@ -6276,7 +6175,7 @@ function handleBackupError(error) {
|
|
|
6276
6175
|
console.log("");
|
|
6277
6176
|
}
|
|
6278
6177
|
function loadGitignorePatterns(projectDir) {
|
|
6279
|
-
const gitignorePath =
|
|
6178
|
+
const gitignorePath = path12.join(projectDir, ".gitignore");
|
|
6280
6179
|
const defaultIgnore = [
|
|
6281
6180
|
"node_modules",
|
|
6282
6181
|
".git",
|
|
@@ -6293,9 +6192,9 @@ function loadGitignorePatterns(projectDir) {
|
|
|
6293
6192
|
"coverage",
|
|
6294
6193
|
".nyc_output"
|
|
6295
6194
|
];
|
|
6296
|
-
if (!
|
|
6195
|
+
if (!fs10.existsSync(gitignorePath)) return defaultIgnore;
|
|
6297
6196
|
try {
|
|
6298
|
-
const lines =
|
|
6197
|
+
const lines = fs10.readFileSync(gitignorePath, "utf-8").split("\n").map((l) => l.trim()).filter((l) => l && !l.startsWith("#"));
|
|
6299
6198
|
return [...defaultIgnore, ...lines];
|
|
6300
6199
|
} catch {
|
|
6301
6200
|
return defaultIgnore;
|
|
@@ -6323,7 +6222,7 @@ async function runBackup(options) {
|
|
|
6323
6222
|
console.log(BORDER10(" \u2551") + GRAY5(` Scope: ${isGlobal ? "\u{1F310} Global (~/.bob/)" : `\u{1F4C1} Project: ${projectName}`}`));
|
|
6324
6223
|
if (archiveName) console.log(BORDER10(" \u2551") + GRAY5(` Archive: "${archiveName}"`));
|
|
6325
6224
|
console.log(BORDER10(" \u2551"));
|
|
6326
|
-
if (!
|
|
6225
|
+
if (!fs10.existsSync(sourceDir)) {
|
|
6327
6226
|
console.log(BORDER10(" \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"));
|
|
6328
6227
|
console.log("");
|
|
6329
6228
|
console.log(RED5(` \u274C Source directory not found: ${sourceDir}`));
|
|
@@ -6332,22 +6231,22 @@ async function runBackup(options) {
|
|
|
6332
6231
|
return;
|
|
6333
6232
|
}
|
|
6334
6233
|
const tmpDir = getTempDir2();
|
|
6335
|
-
const archivePath =
|
|
6336
|
-
const encryptedPath =
|
|
6234
|
+
const archivePath = path12.join(tmpDir, "bob-backup.tar.gz");
|
|
6235
|
+
const encryptedPath = path12.join(tmpDir, "bob-backup.bob.enc");
|
|
6337
6236
|
try {
|
|
6338
6237
|
const compressSpinner = ora11({ text: GRAY5(" Compressing ..."), spinner: "dots" }).start();
|
|
6339
6238
|
const tar = await import("tar");
|
|
6340
|
-
const relativeSource =
|
|
6239
|
+
const relativeSource = path12.relative(os3.homedir(), sourceDir);
|
|
6341
6240
|
await tar.create(
|
|
6342
|
-
{ gzip: true, file: archivePath, cwd:
|
|
6241
|
+
{ gzip: true, file: archivePath, cwd: os3.homedir() },
|
|
6343
6242
|
[relativeSource]
|
|
6344
6243
|
);
|
|
6345
|
-
const archiveStats =
|
|
6244
|
+
const archiveStats = fs10.statSync(archivePath);
|
|
6346
6245
|
const estimatedSizeGB = archiveStats.size / (1024 * 1024 * 1024);
|
|
6347
6246
|
compressSpinner.succeed(GREEN5(` Compressing ${displayName} ...`) + GRAY5(` ${formatBytes(archiveStats.size)}`));
|
|
6348
6247
|
const encryptSpinner = ora11({ text: GRAY5(" Encrypting archive ..."), spinner: "dots" }).start();
|
|
6349
6248
|
encrypt2(archivePath, encryptedPath, config.uid);
|
|
6350
|
-
const encryptedStats =
|
|
6249
|
+
const encryptedStats = fs10.statSync(encryptedPath);
|
|
6351
6250
|
encryptSpinner.succeed(GREEN5(" Encrypting archive ...") + GRAY5(` ${formatBytes(encryptedStats.size)}`));
|
|
6352
6251
|
const urlSpinner = ora11({ text: GRAY5(" Requesting upload authorization ..."), spinner: "dots" }).start();
|
|
6353
6252
|
let uploadResult;
|
|
@@ -6378,7 +6277,7 @@ async function runBackup(options) {
|
|
|
6378
6277
|
return;
|
|
6379
6278
|
}
|
|
6380
6279
|
const uploadSpinner = ora11({ text: GRAY5(" Uploading to S3 ..."), spinner: "dots" }).start();
|
|
6381
|
-
const encryptedData =
|
|
6280
|
+
const encryptedData = fs10.readFileSync(encryptedPath);
|
|
6382
6281
|
await axios2.put(uploadResult.uploadUrl, encryptedData, {
|
|
6383
6282
|
headers: { "Content-Type": "application/octet-stream", "Content-Length": encryptedData.length },
|
|
6384
6283
|
maxBodyLength: Infinity,
|
|
@@ -6437,8 +6336,8 @@ async function runSourceBackup(options) {
|
|
|
6437
6336
|
if (archiveName) console.log(BORDER10(" \u2551") + GRAY5(` Archive: "${archiveName}"`));
|
|
6438
6337
|
console.log(BORDER10(" \u2551"));
|
|
6439
6338
|
if (isFileMode) {
|
|
6440
|
-
const absoluteFilePath =
|
|
6441
|
-
if (!
|
|
6339
|
+
const absoluteFilePath = path12.resolve(projectDir, filePath);
|
|
6340
|
+
if (!fs10.existsSync(absoluteFilePath)) {
|
|
6442
6341
|
console.log(BORDER10(" \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"));
|
|
6443
6342
|
console.log("");
|
|
6444
6343
|
console.log(RED5(` \u274C File not found: ${filePath}`));
|
|
@@ -6446,7 +6345,7 @@ async function runSourceBackup(options) {
|
|
|
6446
6345
|
return;
|
|
6447
6346
|
}
|
|
6448
6347
|
} else {
|
|
6449
|
-
if (!
|
|
6348
|
+
if (!fs10.existsSync(projectDir)) {
|
|
6450
6349
|
console.log(BORDER10(" \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"));
|
|
6451
6350
|
console.log("");
|
|
6452
6351
|
console.log(RED5(` \u274C Project directory not found: ${projectDir}`));
|
|
@@ -6455,21 +6354,21 @@ async function runSourceBackup(options) {
|
|
|
6455
6354
|
}
|
|
6456
6355
|
}
|
|
6457
6356
|
const tmpDir = getTempDir2();
|
|
6458
|
-
const archivePath =
|
|
6459
|
-
const encryptedPath =
|
|
6357
|
+
const archivePath = path12.join(tmpDir, "bob-source.tar.gz");
|
|
6358
|
+
const encryptedPath = path12.join(tmpDir, "bob-source.bob.enc");
|
|
6460
6359
|
try {
|
|
6461
6360
|
const compressSpinner = ora11({ text: GRAY5(" Compressing source ..."), spinner: "dots" }).start();
|
|
6462
6361
|
const tar = await import("tar");
|
|
6463
6362
|
if (isFileMode) {
|
|
6464
|
-
const relativeFilePath =
|
|
6363
|
+
const relativeFilePath = path12.normalize(filePath).replace(/\\/g, "/");
|
|
6465
6364
|
await tar.create(
|
|
6466
6365
|
{ gzip: true, file: archivePath, cwd: projectDir },
|
|
6467
6366
|
[relativeFilePath]
|
|
6468
6367
|
);
|
|
6469
6368
|
} else {
|
|
6470
6369
|
const ignorePatterns = loadGitignorePatterns(projectDir);
|
|
6471
|
-
const parentDir =
|
|
6472
|
-
const projectDirName =
|
|
6370
|
+
const parentDir = path12.dirname(projectDir);
|
|
6371
|
+
const projectDirName = path12.basename(projectDir);
|
|
6473
6372
|
await tar.create(
|
|
6474
6373
|
{
|
|
6475
6374
|
gzip: true,
|
|
@@ -6480,14 +6379,14 @@ async function runSourceBackup(options) {
|
|
|
6480
6379
|
[projectDirName]
|
|
6481
6380
|
);
|
|
6482
6381
|
}
|
|
6483
|
-
const archiveStats =
|
|
6382
|
+
const archiveStats = fs10.statSync(archivePath);
|
|
6484
6383
|
const estimatedSizeGB = archiveStats.size / (1024 * 1024 * 1024);
|
|
6485
6384
|
compressSpinner.succeed(
|
|
6486
6385
|
GREEN5(` Compressing ${isFileMode ? filePath : projectName} ...`) + GRAY5(` ${formatBytes(archiveStats.size)}`)
|
|
6487
6386
|
);
|
|
6488
6387
|
const encryptSpinner = ora11({ text: GRAY5(" Encrypting ..."), spinner: "dots" }).start();
|
|
6489
6388
|
encrypt2(archivePath, encryptedPath, config.uid);
|
|
6490
|
-
const encryptedStats =
|
|
6389
|
+
const encryptedStats = fs10.statSync(encryptedPath);
|
|
6491
6390
|
encryptSpinner.succeed(GREEN5(" Encrypting ...") + GRAY5(` ${formatBytes(encryptedStats.size)}`));
|
|
6492
6391
|
const urlSpinner = ora11({ text: GRAY5(" Requesting upload authorization ..."), spinner: "dots" }).start();
|
|
6493
6392
|
let uploadResult;
|
|
@@ -6510,7 +6409,7 @@ async function runSourceBackup(options) {
|
|
|
6510
6409
|
return;
|
|
6511
6410
|
}
|
|
6512
6411
|
const uploadSpinner = ora11({ text: GRAY5(" Uploading to S3 ..."), spinner: "dots" }).start();
|
|
6513
|
-
const encryptedData =
|
|
6412
|
+
const encryptedData = fs10.readFileSync(encryptedPath);
|
|
6514
6413
|
await axios2.put(uploadResult.uploadUrl, encryptedData, {
|
|
6515
6414
|
headers: { "Content-Type": "application/octet-stream", "Content-Length": encryptedData.length },
|
|
6516
6415
|
maxBodyLength: Infinity,
|
|
@@ -6571,7 +6470,7 @@ function registerBackupCommand(program2) {
|
|
|
6571
6470
|
let sourceDir;
|
|
6572
6471
|
let displayName;
|
|
6573
6472
|
if (isGlobal) {
|
|
6574
|
-
sourceDir =
|
|
6473
|
+
sourceDir = BOB_DIR3;
|
|
6575
6474
|
displayName = "~/.bob/ (global)";
|
|
6576
6475
|
} else {
|
|
6577
6476
|
sourceDir = getProjectBackupDir(projectName);
|
|
@@ -6748,13 +6647,10 @@ function registerBackupCommand(program2) {
|
|
|
6748
6647
|
return;
|
|
6749
6648
|
}
|
|
6750
6649
|
const tmpDir = getTempDir2();
|
|
6751
|
-
const downloadPath =
|
|
6752
|
-
const decryptedPath =
|
|
6650
|
+
const downloadPath = path12.join(tmpDir, "bob-backup.bob.enc");
|
|
6651
|
+
const decryptedPath = path12.join(tmpDir, "bob-backup.tar.gz");
|
|
6753
6652
|
try {
|
|
6754
|
-
const dlSpinner = ora11({
|
|
6755
|
-
text: GRAY5(` Downloading ${label} ...`),
|
|
6756
|
-
spinner: "dots"
|
|
6757
|
-
}).start();
|
|
6653
|
+
const dlSpinner = ora11({ text: GRAY5(` Downloading ${label} ...`), spinner: "dots" }).start();
|
|
6758
6654
|
let downloadAction;
|
|
6759
6655
|
if (isSource) {
|
|
6760
6656
|
downloadAction = selected.type === "archive" ? "requestSourceArchiveDownload" : "requestSourceDownload";
|
|
@@ -6774,49 +6670,44 @@ function registerBackupCommand(program2) {
|
|
|
6774
6670
|
responseType: "arraybuffer",
|
|
6775
6671
|
maxContentLength: Infinity
|
|
6776
6672
|
});
|
|
6777
|
-
|
|
6673
|
+
fs10.writeFileSync(downloadPath, Buffer.from(response.data));
|
|
6778
6674
|
dlSpinner.succeed(GREEN5(` Downloading ${label} ...`));
|
|
6779
6675
|
const decryptSpinner = ora11({ text: GRAY5(" Decrypting ..."), spinner: "dots" }).start();
|
|
6780
6676
|
decrypt2(downloadPath, decryptedPath, config.uid);
|
|
6781
6677
|
decryptSpinner.succeed(GREEN5(" Decrypting ..."));
|
|
6782
|
-
const backupSpinner = ora11({
|
|
6783
|
-
text: GRAY5(" Backing up current state ..."),
|
|
6784
|
-
spinner: "dots"
|
|
6785
|
-
}).start();
|
|
6678
|
+
const backupSpinner = ora11({ text: GRAY5(" Backing up current state ..."), spinner: "dots" }).start();
|
|
6786
6679
|
let preRestoreBackup;
|
|
6787
6680
|
if (isSource && filePath) {
|
|
6788
|
-
const absoluteFilePath =
|
|
6789
|
-
const backupDir =
|
|
6790
|
-
if (!
|
|
6681
|
+
const absoluteFilePath = path12.resolve(projectDir, filePath);
|
|
6682
|
+
const backupDir = path12.join(projectDir, ".bob-backups");
|
|
6683
|
+
if (!fs10.existsSync(backupDir)) fs10.mkdirSync(backupDir, { recursive: true });
|
|
6791
6684
|
const backupFileName = filePath.replace(/[\/\\]/g, "_") + `.${Date.now()}.bak`;
|
|
6792
|
-
preRestoreBackup =
|
|
6793
|
-
if (
|
|
6794
|
-
|
|
6685
|
+
preRestoreBackup = path12.join(backupDir, backupFileName);
|
|
6686
|
+
if (fs10.existsSync(absoluteFilePath)) {
|
|
6687
|
+
fs10.copyFileSync(absoluteFilePath, preRestoreBackup);
|
|
6795
6688
|
}
|
|
6796
6689
|
} else if (isSource) {
|
|
6797
6690
|
preRestoreBackup = `${projectDir}-pre-restore-${Date.now()}`;
|
|
6798
|
-
if (
|
|
6799
|
-
|
|
6691
|
+
if (fs10.existsSync(projectDir)) {
|
|
6692
|
+
fs10.cpSync(projectDir, preRestoreBackup, { recursive: true });
|
|
6800
6693
|
}
|
|
6801
6694
|
} else {
|
|
6802
|
-
const restoreTarget = isGlobal ?
|
|
6695
|
+
const restoreTarget = isGlobal ? BOB_DIR3 : getProjectBackupDir(projectName);
|
|
6803
6696
|
preRestoreBackup = `${restoreTarget}-pre-restore-${Date.now()}`;
|
|
6804
|
-
if (
|
|
6805
|
-
|
|
6697
|
+
if (fs10.existsSync(restoreTarget)) {
|
|
6698
|
+
fs10.cpSync(restoreTarget, preRestoreBackup, { recursive: true });
|
|
6806
6699
|
}
|
|
6807
6700
|
}
|
|
6808
|
-
backupSpinner.succeed(
|
|
6809
|
-
GREEN5(" Backing up current state ...") + GRAY5(` \u2192 ${path13.basename(preRestoreBackup)}`)
|
|
6810
|
-
);
|
|
6701
|
+
backupSpinner.succeed(GREEN5(" Backing up current state ...") + GRAY5(` \u2192 ${path12.basename(preRestoreBackup)}`));
|
|
6811
6702
|
const extractSpinner = ora11({ text: GRAY5(" Extracting ..."), spinner: "dots" }).start();
|
|
6812
6703
|
const tar = await import("tar");
|
|
6813
6704
|
if (isSource && filePath) {
|
|
6814
6705
|
await tar.extract({ file: decryptedPath, cwd: projectDir });
|
|
6815
6706
|
} else if (isSource) {
|
|
6816
|
-
const parentDir =
|
|
6707
|
+
const parentDir = path12.dirname(projectDir);
|
|
6817
6708
|
await tar.extract({ file: decryptedPath, cwd: parentDir });
|
|
6818
6709
|
} else {
|
|
6819
|
-
await tar.extract({ file: decryptedPath, cwd:
|
|
6710
|
+
await tar.extract({ file: decryptedPath, cwd: os3.homedir() });
|
|
6820
6711
|
}
|
|
6821
6712
|
extractSpinner.succeed(GREEN5(" Extracting ..."));
|
|
6822
6713
|
console.log("");
|
|
@@ -6824,13 +6715,13 @@ function registerBackupCommand(program2) {
|
|
|
6824
6715
|
console.log(BORDER10(" \u2551") + GREEN5(" \u2705 Restore complete. ") + BORDER10("\u2551"));
|
|
6825
6716
|
if (isSource && filePath) {
|
|
6826
6717
|
console.log(BORDER10(" \u2551") + GRAY5(` Restored: ${filePath}`));
|
|
6827
|
-
console.log(BORDER10(" \u2551") + GRAY5(` Backup: .bob-backups/${
|
|
6718
|
+
console.log(BORDER10(" \u2551") + GRAY5(` Backup: .bob-backups/${path12.basename(preRestoreBackup)}`));
|
|
6828
6719
|
} else if (isSource) {
|
|
6829
6720
|
console.log(BORDER10(" \u2551") + GRAY5(` Restored: ${projectName}/ source`));
|
|
6830
|
-
console.log(BORDER10(" \u2551") + GRAY5(` Backup: ${
|
|
6721
|
+
console.log(BORDER10(" \u2551") + GRAY5(` Backup: ${path12.basename(preRestoreBackup)}/`));
|
|
6831
6722
|
} else {
|
|
6832
6723
|
console.log(BORDER10(" \u2551") + GRAY5(` Restored: ${isGlobal ? "~/.bob/" : `~/.bob/projects/${projectName}/`}`));
|
|
6833
|
-
console.log(BORDER10(" \u2551") + GRAY5(` Backup: ${
|
|
6724
|
+
console.log(BORDER10(" \u2551") + GRAY5(` Backup: ${path12.basename(preRestoreBackup)}`));
|
|
6834
6725
|
}
|
|
6835
6726
|
console.log(BORDER10(" \u2551") + GRAY5(` From: ${label}`));
|
|
6836
6727
|
console.log(BORDER10(" \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"));
|
|
@@ -6871,10 +6762,11 @@ function registerBackupCommand(program2) {
|
|
|
6871
6762
|
|
|
6872
6763
|
// bin/bob.ts
|
|
6873
6764
|
var program = new Command();
|
|
6874
|
-
program.name("bob").description("Bob's CLI \u2014 AI coding assistant and Forge orchestrator").version("0.
|
|
6765
|
+
program.name("bob").description("Bob's CLI \u2014 AI coding assistant and Forge orchestrator").version("0.7.0");
|
|
6875
6766
|
program.command("whoami").description("Show current authentication status and configuration").action(() => {
|
|
6876
6767
|
const config = getConfig();
|
|
6877
|
-
const projectName =
|
|
6768
|
+
const projectName = path13.basename(process.cwd());
|
|
6769
|
+
const projectConvoId = getActiveConversationId(process.cwd()) || config.conversationId;
|
|
6878
6770
|
console.log("");
|
|
6879
6771
|
console.log(chalk25.bold(" \u{1F916} Bob's CLI"));
|
|
6880
6772
|
console.log(chalk25.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"));
|
|
@@ -6884,7 +6776,7 @@ program.command("whoami").description("Show current authentication status and co
|
|
|
6884
6776
|
console.log(` ${chalk25.cyan("Mode:")} ${config.personalizationMode ? "Personalized" : config.consultantMode ? "Consultant" : "Standard"}`);
|
|
6885
6777
|
console.log(` ${chalk25.cyan("IDRP:")} ${config.idrp ? "Enabled" : "Disabled"}`);
|
|
6886
6778
|
console.log(` ${chalk25.cyan("Project:")} ${projectName} (${process.cwd()})`);
|
|
6887
|
-
console.log(` ${chalk25.cyan("Session:")} ${
|
|
6779
|
+
console.log(` ${chalk25.cyan("Session:")} ${projectConvoId ? projectConvoId.slice(0, 20) + "..." : "None"}`);
|
|
6888
6780
|
console.log("");
|
|
6889
6781
|
if (!config.loggedIn) {
|
|
6890
6782
|
console.log(chalk25.gray(" Run `bob login` to authenticate."));
|