@bobsworkshop/cli 0.5.4 → 0.6.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 +145 -20
- package/dist/analyse-auto-3JL5TO3G.js +529 -0
- package/dist/analyse-auto-XTPO6UCM.js +529 -0
- package/dist/analyse-results-I2QBXA5F.js +8 -0
- package/dist/analyse-results-NXVQJCO6.js +8 -0
- package/dist/bin/analyse-auto-3JL5TO3G.js +529 -0
- package/dist/bin/analyse-results-NXVQJCO6.js +8 -0
- package/dist/bin/bob.js +333 -35
- package/dist/bin/chunk-SADPOL7M.js +1061 -0
- package/dist/bob.js +1135 -35
- package/dist/chunk-7NRJGYXJ.js +1084 -0
- package/dist/chunk-SADPOL7M.js +1061 -0
- package/package.json +1 -1
package/dist/bob.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
buildLocalContext,
|
|
3
3
|
callCloudFunction,
|
|
4
|
+
callHTTPFunction,
|
|
4
5
|
callLocalModel,
|
|
5
6
|
extractAllProposedFiles,
|
|
6
7
|
extractProposedFile,
|
|
@@ -15,12 +16,12 @@ import {
|
|
|
15
16
|
registerLoginCommand,
|
|
16
17
|
setConfigValue,
|
|
17
18
|
stripCodeBlockFromResponse
|
|
18
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-7NRJGYXJ.js";
|
|
19
20
|
|
|
20
21
|
// bin/bob.ts
|
|
21
22
|
import { Command } from "commander";
|
|
22
|
-
import
|
|
23
|
-
import * as
|
|
23
|
+
import chalk26 from "chalk";
|
|
24
|
+
import * as path14 from "path";
|
|
24
25
|
|
|
25
26
|
// src/commands/config.ts
|
|
26
27
|
import chalk from "chalk";
|
|
@@ -291,6 +292,29 @@ async function getTodayMessages(projectName) {
|
|
|
291
292
|
return msg.timestamp >= twentyFourHoursAgo;
|
|
292
293
|
});
|
|
293
294
|
}
|
|
295
|
+
function buildDNAString() {
|
|
296
|
+
const dna = loadCurrentDNA();
|
|
297
|
+
if (!dna) return null;
|
|
298
|
+
const weekly = loadWeeklyProfiles(1)[0] || null;
|
|
299
|
+
const lines = [
|
|
300
|
+
"### USER BEHAVIORAL DNA ###",
|
|
301
|
+
`Archetype: ${dna.archetype || "Unknown"}`,
|
|
302
|
+
`Communication Style: ${dna.communicationStyle || "Unknown"}`,
|
|
303
|
+
`Work Rhythm: ${dna.workRhythm || "Unknown"}`,
|
|
304
|
+
`Decision Making: ${dna.decisionMaking || "Unknown"}`,
|
|
305
|
+
`Emotional State: ${dna.emotionalState || "Unknown"}`
|
|
306
|
+
];
|
|
307
|
+
if (dna.growth) lines.push(`Growth Focus: ${dna.growth}`);
|
|
308
|
+
if (dna.source) lines.push(`Profile Source: ${dna.source} (${dna.lastUpdated || "unknown date"})`);
|
|
309
|
+
if (weekly) {
|
|
310
|
+
lines.push("");
|
|
311
|
+
lines.push("--- WEEKLY TRAJECTORY ---");
|
|
312
|
+
if (weekly.trajectory) lines.push(`Weekly Arc: ${weekly.trajectory}`);
|
|
313
|
+
if (weekly.moodShift) lines.push(`Mood Shift: ${weekly.moodShift}`);
|
|
314
|
+
if (weekly.focusEvolution) lines.push(`Focus Evolution: ${weekly.focusEvolution}`);
|
|
315
|
+
}
|
|
316
|
+
return lines.join("\n");
|
|
317
|
+
}
|
|
294
318
|
|
|
295
319
|
// src/ai/persona.ts
|
|
296
320
|
var STANDARD_STYLE_PROMPT = `You are Bob: friendly, direct, senior-level engineering partner.
|
|
@@ -1617,6 +1641,11 @@ async function playWelcomeAnimation() {
|
|
|
1617
1641
|
console.log(MUTED4(" \u2551") + pad2(" " + BRAND_SECONDARY4('\u25B8 bob push "msg"') + MUTED4(" \u2014 Git commit + push"), 41));
|
|
1618
1642
|
console.log(MUTED4(" \u2551") + pad2(" " + BRAND_SECONDARY4("\u25B8 bob --help") + MUTED4(" \u2014 See all commands"), 40));
|
|
1619
1643
|
console.log(MUTED4(" \u2551") + pad2("", 0));
|
|
1644
|
+
console.log(hRule2());
|
|
1645
|
+
console.log(MUTED4(" \u2551") + pad2("", 0));
|
|
1646
|
+
console.log(MUTED4(" \u2551") + pad2(" " + SUCCESS4("\u{1F331} Join 1,700+ builders in our community:"), 43));
|
|
1647
|
+
console.log(MUTED4(" \u2551") + pad2(" " + INFO4("https://discord.gg/wM9ZBXdd"), 21));
|
|
1648
|
+
console.log(MUTED4(" \u2551") + pad2("", 0));
|
|
1620
1649
|
console.log(MUTED4(" \u255A" + "\u2550".repeat(BOX_WIDTH2) + "\u255D"));
|
|
1621
1650
|
console.log("");
|
|
1622
1651
|
await sleep3(800);
|
|
@@ -3102,7 +3131,7 @@ function registerAnalyseCommand(program2) {
|
|
|
3102
3131
|
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) => {
|
|
3103
3132
|
const config = getConfig();
|
|
3104
3133
|
if (options.auto) {
|
|
3105
|
-
const { runAutoFix } = await import("./analyse-auto-
|
|
3134
|
+
const { runAutoFix } = await import("./analyse-auto-XTPO6UCM.js");
|
|
3106
3135
|
const category = options.bugs ? "bugs" : options.features ? "features" : options.improvements ? "improvements" : options.upgrades ? "upgrades" : void 0;
|
|
3107
3136
|
await runAutoFix({
|
|
3108
3137
|
category,
|
|
@@ -3112,7 +3141,7 @@ function registerAnalyseCommand(program2) {
|
|
|
3112
3141
|
return;
|
|
3113
3142
|
}
|
|
3114
3143
|
if (options.bugs || options.features || options.improvements || options.upgrades) {
|
|
3115
|
-
const { showInteractiveResults } = await import("./analyse-results-
|
|
3144
|
+
const { showInteractiveResults } = await import("./analyse-results-I2QBXA5F.js");
|
|
3116
3145
|
const category = options.bugs ? "bugs" : options.features ? "features" : options.improvements ? "improvements" : "upgrades";
|
|
3117
3146
|
await showInteractiveResults(config, category, options.sort, options.search);
|
|
3118
3147
|
return;
|
|
@@ -5720,13 +5749,1079 @@ function getWeekNumber(date) {
|
|
|
5720
5749
|
return Math.ceil(((d.getTime() - yearStart.getTime()) / 864e5 + 1) / 7);
|
|
5721
5750
|
}
|
|
5722
5751
|
|
|
5752
|
+
// src/commands/userbob.ts
|
|
5753
|
+
import chalk24 from "chalk";
|
|
5754
|
+
import * as readline8 from "readline";
|
|
5755
|
+
import * as fs10 from "fs";
|
|
5756
|
+
import * as path13 from "path";
|
|
5757
|
+
import * as os4 from "os";
|
|
5758
|
+
var PURPLE = chalk24.hex("#AB47BC");
|
|
5759
|
+
var AMBER6 = chalk24.hex("#FFAB00");
|
|
5760
|
+
var GREEN5 = chalk24.hex("#66BB6A");
|
|
5761
|
+
var CYAN5 = chalk24.hex("#26C6DA");
|
|
5762
|
+
var RED5 = chalk24.hex("#EF5350");
|
|
5763
|
+
var GRAY5 = chalk24.gray;
|
|
5764
|
+
var BLUE4 = chalk24.hex("#42A5F5");
|
|
5765
|
+
var BOB_COLOR = chalk24.hex("#E66F24");
|
|
5766
|
+
var BORDER10 = chalk24.hex("#455A64");
|
|
5767
|
+
var WHITE3 = chalk24.white;
|
|
5768
|
+
var BOB_DIR3 = path13.join(os4.homedir(), ".bob");
|
|
5769
|
+
function getSessionFilePath() {
|
|
5770
|
+
const projectName = path13.basename(process.cwd());
|
|
5771
|
+
return path13.join(BOB_DIR3, "projects", projectName, "userbob-session.json");
|
|
5772
|
+
}
|
|
5773
|
+
function writeSessionFile(data) {
|
|
5774
|
+
const filePath = getSessionFilePath();
|
|
5775
|
+
const dir = path13.dirname(filePath);
|
|
5776
|
+
if (!fs10.existsSync(dir)) fs10.mkdirSync(dir, { recursive: true });
|
|
5777
|
+
fs10.writeFileSync(filePath, JSON.stringify(data, null, 2));
|
|
5778
|
+
}
|
|
5779
|
+
function readSessionFile() {
|
|
5780
|
+
const filePath = getSessionFilePath();
|
|
5781
|
+
if (!fs10.existsSync(filePath)) return null;
|
|
5782
|
+
try {
|
|
5783
|
+
return JSON.parse(fs10.readFileSync(filePath, "utf-8"));
|
|
5784
|
+
} catch {
|
|
5785
|
+
return null;
|
|
5786
|
+
}
|
|
5787
|
+
}
|
|
5788
|
+
function clearSessionFile() {
|
|
5789
|
+
const filePath = getSessionFilePath();
|
|
5790
|
+
if (fs10.existsSync(filePath)) fs10.unlinkSync(filePath);
|
|
5791
|
+
}
|
|
5792
|
+
function renderHUD(sat, target, stag, stagTarget, div, divTarget, grading) {
|
|
5793
|
+
const satBar = sat >= target ? GREEN5(`${sat}%`) : sat >= target * 0.7 ? AMBER6(`${sat}%`) : RED5(`${sat}%`);
|
|
5794
|
+
console.log("");
|
|
5795
|
+
console.log(BORDER10(" \u2500\u2500\u2500 MISSION CONTROL \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
5796
|
+
console.log(
|
|
5797
|
+
` SAT: ${satBar} \u2192 ${target}% \u2502 STAG: ${stag}/${stagTarget > 0 ? stagTarget : "\u221E"} \u2502 DIV: ${div}/${divTarget > 0 ? divTarget : "\u221E"} \u2502 GRADE: ${grading}`
|
|
5798
|
+
);
|
|
5799
|
+
console.log(BORDER10(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
5800
|
+
console.log("");
|
|
5801
|
+
}
|
|
5802
|
+
function stripMarkdown(text) {
|
|
5803
|
+
return text.replace(/\*\*(.+?)\*\*/g, "$1").replace(/\*(.+?)\*/g, "$1").replace(/^#{1,6}\s+/gm, "").replace(/^---+$/gm, "").replace(/^>\s?/gm, "").replace(/`([^`]+)`/g, "$1").replace(/^\s*[-*+]\s+/gm, " \u2022 ").replace(/^\s*\d+\.\s+/gm, " ").replace(/\n{3,}/g, "\n\n").trim();
|
|
5804
|
+
}
|
|
5805
|
+
function renderMessage(sender, message, audit) {
|
|
5806
|
+
const cleanMsg = stripMarkdown(message);
|
|
5807
|
+
const maxWidth = 70;
|
|
5808
|
+
const lines = wrapText2(cleanMsg, maxWidth - 4);
|
|
5809
|
+
if (sender === "userBob") {
|
|
5810
|
+
const topBar = PURPLE(` \u250C\u2500 UserBob ${"\u2500".repeat(maxWidth - 13)}\u2510`);
|
|
5811
|
+
const bottomBar = PURPLE(` \u2514${"\u2500".repeat(maxWidth - 2)}\u2518`);
|
|
5812
|
+
console.log("");
|
|
5813
|
+
console.log(topBar);
|
|
5814
|
+
for (const line of lines) {
|
|
5815
|
+
const padded = line.padEnd(maxWidth - 4);
|
|
5816
|
+
console.log(PURPLE(" \u2502") + ` ${padded}` + PURPLE(" \u2502"));
|
|
5817
|
+
}
|
|
5818
|
+
console.log(bottomBar);
|
|
5819
|
+
if (audit) {
|
|
5820
|
+
const chips = [];
|
|
5821
|
+
if (audit.satisfactionScore !== void 0) chips.push(CYAN5(`[SAT: ${audit.satisfactionScore}%]`));
|
|
5822
|
+
if (audit.resemblanceScore !== void 0) chips.push(BLUE4(`[RES: ${audit.resemblanceScore}%]`));
|
|
5823
|
+
if (audit.reasoning) chips.push(GRAY5(`[${String(audit.reasoning).slice(0, 50)}...]`));
|
|
5824
|
+
if (chips.length > 0) console.log(" " + chips.join(" "));
|
|
5825
|
+
}
|
|
5826
|
+
} else if (sender === "bob") {
|
|
5827
|
+
const indent = " ";
|
|
5828
|
+
const topBar = BOB_COLOR(`${indent}\u250C${"\u2500".repeat(maxWidth - 12)}\u2500 Bob \u2500\u2510`);
|
|
5829
|
+
const bottomBar = BOB_COLOR(`${indent}\u2514${"\u2500".repeat(maxWidth - 2)}\u2518`);
|
|
5830
|
+
console.log("");
|
|
5831
|
+
console.log(topBar);
|
|
5832
|
+
for (const line of lines) {
|
|
5833
|
+
const padded = line.padEnd(maxWidth - 4);
|
|
5834
|
+
console.log(BOB_COLOR(`${indent}\u2502`) + ` ${padded}` + BOB_COLOR(" \u2502"));
|
|
5835
|
+
}
|
|
5836
|
+
console.log(bottomBar);
|
|
5837
|
+
} else if (sender === "system") {
|
|
5838
|
+
console.log("");
|
|
5839
|
+
console.log(CYAN5(" \u2500\u2500 SYSTEM \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
5840
|
+
console.log(GRAY5(` ${cleanMsg}`));
|
|
5841
|
+
console.log(CYAN5(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
5842
|
+
} else {
|
|
5843
|
+
console.log("");
|
|
5844
|
+
console.log(GRAY5(` [${sender.toUpperCase()}] ${cleanMsg}`));
|
|
5845
|
+
}
|
|
5846
|
+
}
|
|
5847
|
+
function wrapText2(text, maxWidth) {
|
|
5848
|
+
const lines = [];
|
|
5849
|
+
const paragraphs = text.split("\n");
|
|
5850
|
+
for (const paragraph of paragraphs) {
|
|
5851
|
+
if (paragraph.trim() === "") {
|
|
5852
|
+
lines.push("");
|
|
5853
|
+
continue;
|
|
5854
|
+
}
|
|
5855
|
+
const words = paragraph.split(" ");
|
|
5856
|
+
let currentLine = "";
|
|
5857
|
+
for (const word of words) {
|
|
5858
|
+
if ((currentLine + " " + word).trim().length > maxWidth) {
|
|
5859
|
+
if (currentLine) lines.push(currentLine.trim());
|
|
5860
|
+
currentLine = word;
|
|
5861
|
+
} else {
|
|
5862
|
+
currentLine = currentLine ? currentLine + " " + word : word;
|
|
5863
|
+
}
|
|
5864
|
+
}
|
|
5865
|
+
if (currentLine.trim()) lines.push(currentLine.trim());
|
|
5866
|
+
}
|
|
5867
|
+
return lines;
|
|
5868
|
+
}
|
|
5869
|
+
async function handleSlashCommand(input, config, conversationId) {
|
|
5870
|
+
const trimmed = input.trim();
|
|
5871
|
+
if (trimmed === "/status") {
|
|
5872
|
+
try {
|
|
5873
|
+
const response = await callCloudFunction("getCLIConversationMessages", { conversationId, since: null });
|
|
5874
|
+
const state = response?.state || {};
|
|
5875
|
+
console.log("");
|
|
5876
|
+
console.log(AMBER6(" \u2500\u2500\u2500 Current Parameters \u2500\u2500\u2500"));
|
|
5877
|
+
console.log(GRAY5(` Target Satisfaction : ${state.targetSatisfaction ?? "N/A"}`));
|
|
5878
|
+
console.log(GRAY5(` Grading Standard : ${state.gradingStandard ?? "N/A"}`));
|
|
5879
|
+
console.log(GRAY5(` Current Satisfaction: ${state.currentSatisfaction ?? "N/A"}`));
|
|
5880
|
+
console.log(GRAY5(` Stalemate : ${state.stalemateState?.current ?? 0}/${state.stalemateState?.target ?? "\u221E"}`));
|
|
5881
|
+
console.log(GRAY5(` Divergence : ${state.divergenceState?.current ?? 0}/${state.divergenceState?.target ?? "\u221E"}`));
|
|
5882
|
+
console.log(GRAY5(` Status : ${state.simulationStatus ?? "UNKNOWN"}`));
|
|
5883
|
+
console.log(GRAY5(` Active : ${state.userBobActive ?? "UNKNOWN"}`));
|
|
5884
|
+
console.log("");
|
|
5885
|
+
} catch {
|
|
5886
|
+
console.log(RED5(" \u274C Could not fetch conversation state."));
|
|
5887
|
+
}
|
|
5888
|
+
return;
|
|
5889
|
+
}
|
|
5890
|
+
const setMatch = trimmed.match(/^\/set\s+(grading|target|stag|div)\s+(\d+)$/i);
|
|
5891
|
+
if (setMatch) {
|
|
5892
|
+
const param = setMatch[1].toLowerCase();
|
|
5893
|
+
const value = parseInt(setMatch[2], 10);
|
|
5894
|
+
const paramMap = {
|
|
5895
|
+
grading: "gradingStandard",
|
|
5896
|
+
target: "targetSatisfaction",
|
|
5897
|
+
stag: "stalemateZone",
|
|
5898
|
+
div: "divergenceThreshold"
|
|
5899
|
+
};
|
|
5900
|
+
try {
|
|
5901
|
+
await callHTTPFunction("userSimManagerService", {
|
|
5902
|
+
action: "updateParameters",
|
|
5903
|
+
conversationId,
|
|
5904
|
+
uid: config.uid,
|
|
5905
|
+
email: config.email,
|
|
5906
|
+
params: { [paramMap[param]]: value }
|
|
5907
|
+
});
|
|
5908
|
+
console.log(GREEN5(` \u2705 ${param} updated to ${value}`));
|
|
5909
|
+
} catch (e) {
|
|
5910
|
+
console.log(RED5(` \u274C Failed to update ${param}: ${e.message}`));
|
|
5911
|
+
}
|
|
5912
|
+
return;
|
|
5913
|
+
}
|
|
5914
|
+
const injectMatch = trimmed.match(/^\/inject\s+"(.+)"$/);
|
|
5915
|
+
if (injectMatch) {
|
|
5916
|
+
const note = injectMatch[1];
|
|
5917
|
+
try {
|
|
5918
|
+
await callHTTPFunction("userSimManagerService", {
|
|
5919
|
+
action: "injectNote",
|
|
5920
|
+
conversationId,
|
|
5921
|
+
uid: config.uid,
|
|
5922
|
+
email: config.email,
|
|
5923
|
+
note
|
|
5924
|
+
});
|
|
5925
|
+
console.log(GREEN5(` \u2705 Director's note injected.`));
|
|
5926
|
+
} catch (e) {
|
|
5927
|
+
console.log(RED5(` \u274C Failed to inject note: ${e.message}`));
|
|
5928
|
+
}
|
|
5929
|
+
return;
|
|
5930
|
+
}
|
|
5931
|
+
console.log(GRAY5(' Commands: /set grading|target|stag|div <n> /inject "note" /status /abort'));
|
|
5932
|
+
}
|
|
5933
|
+
async function runPlatformSimulation(config, conversationId, mission, params) {
|
|
5934
|
+
await callHTTPFunction("userSimManagerService", {
|
|
5935
|
+
action: "updateParameters",
|
|
5936
|
+
conversationId,
|
|
5937
|
+
uid: config.uid,
|
|
5938
|
+
email: config.email,
|
|
5939
|
+
params: {
|
|
5940
|
+
targetSatisfaction: params.target,
|
|
5941
|
+
gradingStandard: params.grading,
|
|
5942
|
+
stalemateZone: params.stag,
|
|
5943
|
+
divergenceThreshold: params.div
|
|
5944
|
+
}
|
|
5945
|
+
});
|
|
5946
|
+
await callHTTPFunction("userSimManagerService", {
|
|
5947
|
+
action: "injectNote",
|
|
5948
|
+
conversationId,
|
|
5949
|
+
uid: config.uid,
|
|
5950
|
+
email: config.email,
|
|
5951
|
+
note: mission
|
|
5952
|
+
});
|
|
5953
|
+
console.log(GREEN5(" \u2705 Mission injected. Simulation is running."));
|
|
5954
|
+
console.log("");
|
|
5955
|
+
console.log(BORDER10(" \u2500\u2500\u2500 LIVE SIMULATION \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
5956
|
+
console.log(GRAY5(" Messages will stream below as Bob and UserBob interact."));
|
|
5957
|
+
console.log(GRAY5(" You can type commands at any time:"));
|
|
5958
|
+
console.log("");
|
|
5959
|
+
console.log(AMBER6(" /abort") + GRAY5(" \u2014 Stop the simulation immediately"));
|
|
5960
|
+
console.log(AMBER6(" /set target 90") + GRAY5(" \u2014 Update satisfaction target"));
|
|
5961
|
+
console.log(AMBER6(" /set grading 70") + GRAY5(" \u2014 Update Teacher's Curve"));
|
|
5962
|
+
console.log(AMBER6(" /set stag 5") + GRAY5(" \u2014 Update stalemate threshold"));
|
|
5963
|
+
console.log(AMBER6(" /set div 3") + GRAY5(" \u2014 Update divergence threshold"));
|
|
5964
|
+
console.log(AMBER6(' /inject "note"') + GRAY5(" \u2014 Inject a director's note mid-session"));
|
|
5965
|
+
console.log(AMBER6(" /status") + GRAY5(" \u2014 Show current simulation parameters"));
|
|
5966
|
+
console.log("");
|
|
5967
|
+
console.log(BORDER10(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
5968
|
+
console.log("");
|
|
5969
|
+
let running = true;
|
|
5970
|
+
let lastMessageTimestamp = 0;
|
|
5971
|
+
let hudState = { sat: 0, target: params.target, stag: 0, stagTarget: params.stag, div: 0, divTarget: params.div, grading: params.grading };
|
|
5972
|
+
const sigintHandler = async () => {
|
|
5973
|
+
if (!running) return;
|
|
5974
|
+
running = false;
|
|
5975
|
+
console.log("\n");
|
|
5976
|
+
console.log(AMBER6(" \u{1F6D1} Aborting simulation..."));
|
|
5977
|
+
try {
|
|
5978
|
+
await callHTTPFunction("userSimManagerService", {
|
|
5979
|
+
action: "abortMission",
|
|
5980
|
+
conversationId,
|
|
5981
|
+
uid: config.uid,
|
|
5982
|
+
email: config.email
|
|
5983
|
+
});
|
|
5984
|
+
console.log(GREEN5(" \u2705 Simulation aborted."));
|
|
5985
|
+
} catch {
|
|
5986
|
+
}
|
|
5987
|
+
process.exit(0);
|
|
5988
|
+
};
|
|
5989
|
+
process.on("SIGINT", sigintHandler);
|
|
5990
|
+
const rl = readline8.createInterface({ input: process.stdin, output: process.stdout, terminal: true });
|
|
5991
|
+
rl.setPrompt("");
|
|
5992
|
+
rl.on("line", async (line) => {
|
|
5993
|
+
const trimmed = line.trim();
|
|
5994
|
+
if (!trimmed) return;
|
|
5995
|
+
if (trimmed === "/abort" || trimmed === "abort") {
|
|
5996
|
+
running = false;
|
|
5997
|
+
console.log(AMBER6(" \u{1F6D1} Aborting simulation..."));
|
|
5998
|
+
try {
|
|
5999
|
+
await callHTTPFunction("userSimManagerService", {
|
|
6000
|
+
action: "abortMission",
|
|
6001
|
+
conversationId,
|
|
6002
|
+
uid: config.uid,
|
|
6003
|
+
email: config.email
|
|
6004
|
+
});
|
|
6005
|
+
console.log(GREEN5(" \u2705 Simulation aborted."));
|
|
6006
|
+
} catch {
|
|
6007
|
+
}
|
|
6008
|
+
rl.close();
|
|
6009
|
+
process.exit(0);
|
|
6010
|
+
}
|
|
6011
|
+
await handleSlashCommand(trimmed, config, conversationId);
|
|
6012
|
+
});
|
|
6013
|
+
while (running) {
|
|
6014
|
+
await new Promise((r) => setTimeout(r, 3e3));
|
|
6015
|
+
try {
|
|
6016
|
+
const response = await callCloudFunction("getCLIConversationMessages", {
|
|
6017
|
+
conversationId,
|
|
6018
|
+
since: lastMessageTimestamp || null
|
|
6019
|
+
});
|
|
6020
|
+
const messages = response?.messages || [];
|
|
6021
|
+
const state = response?.state || {};
|
|
6022
|
+
for (const msg of messages) {
|
|
6023
|
+
renderMessage(msg.sender, msg.message, msg.simulationAudit);
|
|
6024
|
+
if (msg.timestamp && msg.timestamp > lastMessageTimestamp) {
|
|
6025
|
+
lastMessageTimestamp = msg.timestamp;
|
|
6026
|
+
}
|
|
6027
|
+
}
|
|
6028
|
+
if (state.currentSatisfaction !== void 0) hudState.sat = state.currentSatisfaction;
|
|
6029
|
+
if (state.targetSatisfaction !== void 0) hudState.target = state.targetSatisfaction;
|
|
6030
|
+
if (state.gradingStandard !== void 0) hudState.grading = state.gradingStandard;
|
|
6031
|
+
if (state.stalemateState) {
|
|
6032
|
+
hudState.stag = state.stalemateState.current ?? hudState.stag;
|
|
6033
|
+
hudState.stagTarget = state.stalemateState.target ?? hudState.stagTarget;
|
|
6034
|
+
}
|
|
6035
|
+
if (state.divergenceState) {
|
|
6036
|
+
hudState.div = state.divergenceState.current ?? hudState.div;
|
|
6037
|
+
hudState.divTarget = state.divergenceState.target ?? hudState.divTarget;
|
|
6038
|
+
}
|
|
6039
|
+
if (state.userBobActive === false || state.simulationStatus && state.simulationStatus !== "RUNNING") {
|
|
6040
|
+
if (messages.length > 0) {
|
|
6041
|
+
renderHUD(hudState.sat, hudState.target, hudState.stag, hudState.stagTarget, hudState.div, hudState.divTarget, hudState.grading);
|
|
6042
|
+
}
|
|
6043
|
+
console.log("");
|
|
6044
|
+
console.log(AMBER6(` \u{1F3C1} Simulation ended: ${state.simulationStatus || "INACTIVE"}`));
|
|
6045
|
+
console.log("");
|
|
6046
|
+
running = false;
|
|
6047
|
+
break;
|
|
6048
|
+
}
|
|
6049
|
+
if (messages.length > 0) {
|
|
6050
|
+
renderHUD(hudState.sat, hudState.target, hudState.stag, hudState.stagTarget, hudState.div, hudState.divTarget, hudState.grading);
|
|
6051
|
+
}
|
|
6052
|
+
} catch (e) {
|
|
6053
|
+
console.log(RED5(` \u274C Poll error: ${e.message}`));
|
|
6054
|
+
}
|
|
6055
|
+
}
|
|
6056
|
+
rl.close();
|
|
6057
|
+
process.removeListener("SIGINT", sigintHandler);
|
|
6058
|
+
}
|
|
6059
|
+
async function runLocalSimulation(config, dnaString, mission, params) {
|
|
6060
|
+
writeSessionFile({ active: true, turns: 0, mission });
|
|
6061
|
+
let running = true;
|
|
6062
|
+
let turns = 0;
|
|
6063
|
+
let conversationHistory = [];
|
|
6064
|
+
let sat = 0;
|
|
6065
|
+
let stalemateCurrent = 0;
|
|
6066
|
+
let divergenceCurrent = 0;
|
|
6067
|
+
let lastStatus = "";
|
|
6068
|
+
const sigintHandler = () => {
|
|
6069
|
+
running = false;
|
|
6070
|
+
writeSessionFile({ active: false });
|
|
6071
|
+
clearSessionFile();
|
|
6072
|
+
console.log("\n" + AMBER6(" \u{1F6D1} Simulation stopped."));
|
|
6073
|
+
process.exit(0);
|
|
6074
|
+
};
|
|
6075
|
+
process.on("SIGINT", sigintHandler);
|
|
6076
|
+
const rl = readline8.createInterface({ input: process.stdin, output: process.stdout, terminal: true });
|
|
6077
|
+
rl.setPrompt("");
|
|
6078
|
+
rl.on("line", (line) => {
|
|
6079
|
+
const t = line.trim();
|
|
6080
|
+
if (t === "/abort" || t === "abort") {
|
|
6081
|
+
running = false;
|
|
6082
|
+
writeSessionFile({ active: false });
|
|
6083
|
+
clearSessionFile();
|
|
6084
|
+
console.log(AMBER6(" \u{1F6D1} Simulation stopped."));
|
|
6085
|
+
rl.close();
|
|
6086
|
+
process.exit(0);
|
|
6087
|
+
}
|
|
6088
|
+
if (t.startsWith("/set ")) {
|
|
6089
|
+
const m = t.match(/^\/set\s+(grading|target|stag|div)\s+(\d+)$/i);
|
|
6090
|
+
if (m) {
|
|
6091
|
+
const val = parseInt(m[2], 10);
|
|
6092
|
+
if (m[1] === "grading") params.grading = val;
|
|
6093
|
+
if (m[1] === "target") params.target = val;
|
|
6094
|
+
if (m[1] === "stag") params.stag = val;
|
|
6095
|
+
if (m[1] === "div") params.div = val;
|
|
6096
|
+
console.log(GREEN5(` \u2705 ${m[1]} updated to ${val} (local)`));
|
|
6097
|
+
}
|
|
6098
|
+
}
|
|
6099
|
+
if (t === "/status") {
|
|
6100
|
+
console.log("");
|
|
6101
|
+
console.log(AMBER6(" \u2500\u2500\u2500 Local Sim Parameters \u2500\u2500\u2500"));
|
|
6102
|
+
console.log(GRAY5(` Target: ${params.target} \u2502 Grading: ${params.grading} \u2502 Stag Limit: ${params.stag} \u2502 Div Limit: ${params.div}`));
|
|
6103
|
+
console.log(GRAY5(` Current SAT: ${sat} \u2502 Turns: ${turns} \u2502 Stag: ${stalemateCurrent} \u2502 Div: ${divergenceCurrent}`));
|
|
6104
|
+
console.log("");
|
|
6105
|
+
}
|
|
6106
|
+
});
|
|
6107
|
+
const bobSystem = `You are Bob \u2014 a senior AI engineering consultant. A developer's digital twin (UserBob) is evaluating your work. Respond helpfully and directly to advance the mission. Mission context: ${mission}`;
|
|
6108
|
+
const userBobSystem = dnaString ? `You are a digital twin of a software engineer. You ARE this developer. Your personality, communication style, and engineering philosophy are defined below.
|
|
6109
|
+
|
|
6110
|
+
Mission: ${mission}
|
|
6111
|
+
|
|
6112
|
+
${dnaString}
|
|
6113
|
+
|
|
6114
|
+
After each Bob response, evaluate it 0-100 on how well it advances YOUR mission. Reply with your natural reaction, then append exactly one JSON footer on its own line:
|
|
6115
|
+
{"satisfactionScore": <0-100>, "status": "CONVERGING|STAGNATING|DIVERGING"}` : `You are a digital twin of a software engineer. You have no personal profile loaded \u2014 respond based on the mission context only.
|
|
6116
|
+
|
|
6117
|
+
Mission: ${mission}
|
|
6118
|
+
|
|
6119
|
+
After each Bob response, evaluate it 0-100 on mission alignment. Reply with your reaction, then append exactly one JSON footer on its own line:
|
|
6120
|
+
{"satisfactionScore": <0-100>, "status": "CONVERGING|STAGNATING|DIVERGING"}`;
|
|
6121
|
+
console.log(BORDER10(" \u2500\u2500\u2500 LIVE LOCAL SIMULATION \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
6122
|
+
console.log(GRAY5(" Bob and UserBob will converse autonomously below."));
|
|
6123
|
+
console.log(GRAY5(" Commands:"));
|
|
6124
|
+
console.log(AMBER6(" /abort") + GRAY5(" \u2014 Stop the simulation"));
|
|
6125
|
+
console.log(AMBER6(" /set target 90") + GRAY5(" \u2014 Update satisfaction target"));
|
|
6126
|
+
console.log(AMBER6(" /set grading 70") + GRAY5(" \u2014 Update Teacher's Curve"));
|
|
6127
|
+
console.log(AMBER6(" /set stag 5") + GRAY5(" \u2014 Update stalemate threshold"));
|
|
6128
|
+
console.log(AMBER6(" /set div 3") + GRAY5(" \u2014 Update divergence threshold"));
|
|
6129
|
+
console.log(AMBER6(" /status") + GRAY5(" \u2014 Show current parameters"));
|
|
6130
|
+
console.log(BORDER10(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
6131
|
+
console.log("");
|
|
6132
|
+
const kickstart = `Mission received: "${mission}". Bob, what's your first move?`;
|
|
6133
|
+
console.log(PURPLE(" UserBob > ") + WHITE3(kickstart));
|
|
6134
|
+
conversationHistory.push({ role: "user", content: kickstart });
|
|
6135
|
+
while (running) {
|
|
6136
|
+
const session = readSessionFile();
|
|
6137
|
+
if (!session?.active) {
|
|
6138
|
+
running = false;
|
|
6139
|
+
break;
|
|
6140
|
+
}
|
|
6141
|
+
turns++;
|
|
6142
|
+
try {
|
|
6143
|
+
const bobMessages = [
|
|
6144
|
+
{ role: "system", content: bobSystem },
|
|
6145
|
+
...conversationHistory
|
|
6146
|
+
];
|
|
6147
|
+
const bobResponse = await callLocalModel(config.localEndpoint, bobMessages);
|
|
6148
|
+
console.log(BOB_COLOR(" Bob > ") + WHITE3(bobResponse));
|
|
6149
|
+
conversationHistory.push({ role: "assistant", content: bobResponse });
|
|
6150
|
+
const ubMessages = [
|
|
6151
|
+
{ role: "system", content: userBobSystem },
|
|
6152
|
+
...conversationHistory
|
|
6153
|
+
];
|
|
6154
|
+
const ubResponse = await callLocalModel(config.localEndpoint, ubMessages);
|
|
6155
|
+
const jsonMatch = ubResponse.match(/\{[^}]*"satisfactionScore"[^}]*\}/);
|
|
6156
|
+
const cleanResponse = ubResponse.replace(/\{[^}]*"satisfactionScore"[^}]*\}/, "").trim();
|
|
6157
|
+
console.log(PURPLE(" UserBob > ") + WHITE3(cleanResponse));
|
|
6158
|
+
let auditChips = [];
|
|
6159
|
+
if (jsonMatch) {
|
|
6160
|
+
try {
|
|
6161
|
+
const audit = JSON.parse(jsonMatch[0]);
|
|
6162
|
+
const rawScore = audit.satisfactionScore || 0;
|
|
6163
|
+
sat = Math.round(rawScore * (params.grading / 100));
|
|
6164
|
+
lastStatus = audit.status || "";
|
|
6165
|
+
auditChips = [CYAN5(`[SAT: ${sat}%]`), BLUE4(`[RAW: ${rawScore}]`), GRAY5(`[${lastStatus}]`)];
|
|
6166
|
+
if (lastStatus === "STAGNATING") {
|
|
6167
|
+
stalemateCurrent++;
|
|
6168
|
+
if (params.stag > 0 && stalemateCurrent >= params.stag) {
|
|
6169
|
+
console.log(" " + auditChips.join(" "));
|
|
6170
|
+
renderHUD(sat, params.target, stalemateCurrent, params.stag, divergenceCurrent, params.div, params.grading);
|
|
6171
|
+
console.log(AMBER6(` \u{1F3C1} Stalemate threshold reached (${stalemateCurrent}/${params.stag}). Simulation ended.`));
|
|
6172
|
+
running = false;
|
|
6173
|
+
break;
|
|
6174
|
+
}
|
|
6175
|
+
} else if (lastStatus === "DIVERGING") {
|
|
6176
|
+
divergenceCurrent++;
|
|
6177
|
+
stalemateCurrent = 0;
|
|
6178
|
+
if (params.div > 0 && divergenceCurrent >= params.div) {
|
|
6179
|
+
console.log(" " + auditChips.join(" "));
|
|
6180
|
+
renderHUD(sat, params.target, stalemateCurrent, params.stag, divergenceCurrent, params.div, params.grading);
|
|
6181
|
+
console.log(AMBER6(` \u{1F3C1} Divergence threshold reached (${divergenceCurrent}/${params.div}). Simulation ended.`));
|
|
6182
|
+
running = false;
|
|
6183
|
+
break;
|
|
6184
|
+
}
|
|
6185
|
+
} else if (lastStatus === "CONVERGING") {
|
|
6186
|
+
stalemateCurrent = 0;
|
|
6187
|
+
divergenceCurrent = 0;
|
|
6188
|
+
}
|
|
6189
|
+
} catch {
|
|
6190
|
+
}
|
|
6191
|
+
}
|
|
6192
|
+
if (auditChips.length) console.log(" " + auditChips.join(" "));
|
|
6193
|
+
conversationHistory.push({ role: "user", content: ubResponse });
|
|
6194
|
+
writeSessionFile({ active: true, turns, mission, sat });
|
|
6195
|
+
renderHUD(sat, params.target, stalemateCurrent, params.stag, divergenceCurrent, params.div, params.grading);
|
|
6196
|
+
if (sat >= params.target) {
|
|
6197
|
+
console.log(GREEN5(` \u{1F3AF} Target satisfaction ${params.target}% reached! Mission complete.`));
|
|
6198
|
+
running = false;
|
|
6199
|
+
break;
|
|
6200
|
+
}
|
|
6201
|
+
await new Promise((r) => setTimeout(r, 1e3));
|
|
6202
|
+
} catch (e) {
|
|
6203
|
+
console.log(RED5(` \u274C Local model error: ${e.message}`));
|
|
6204
|
+
console.log(GRAY5(" Retrying in 3 seconds..."));
|
|
6205
|
+
await new Promise((r) => setTimeout(r, 3e3));
|
|
6206
|
+
}
|
|
6207
|
+
}
|
|
6208
|
+
clearSessionFile();
|
|
6209
|
+
rl.close();
|
|
6210
|
+
process.removeListener("SIGINT", sigintHandler);
|
|
6211
|
+
console.log("");
|
|
6212
|
+
console.log(GRAY5(` Session complete. ${turns} turns processed.`));
|
|
6213
|
+
console.log("");
|
|
6214
|
+
}
|
|
6215
|
+
function registerUserBobCommand(program2) {
|
|
6216
|
+
program2.command("userbob [mission...]").description("Launch your UserBob digital twin simulation").option("--local", "Force local Ollama mode (Tier 1)").option("--target <number>", "Satisfaction target (default: 85)", "85").option("--grading <number>", "Teacher's curve grading standard (default: 50)", "50").option("--stag <number>", "Stalemate threshold \u2014 0 = infinite (default: 0)", "0").option("--div <number>", "Divergence threshold \u2014 0 = infinite (default: 0)", "0").option("--resume", "Resume without injecting a new mission note").action(async (missionArgs, options) => {
|
|
6217
|
+
const config = getConfig();
|
|
6218
|
+
const params = {
|
|
6219
|
+
target: parseInt(options.target, 10),
|
|
6220
|
+
grading: parseInt(options.grading, 10),
|
|
6221
|
+
stag: parseInt(options.stag, 10),
|
|
6222
|
+
div: parseInt(options.div, 10)
|
|
6223
|
+
};
|
|
6224
|
+
const usePlatform = !options.local && isAuthenticated();
|
|
6225
|
+
console.log("");
|
|
6226
|
+
console.log(BORDER10(" \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"));
|
|
6227
|
+
console.log(BORDER10(" \u2551") + PURPLE(" \u{1F916} UserBob \u2014 Digital Twin Simulation"));
|
|
6228
|
+
console.log(BORDER10(" \u2551") + GRAY5(` Mode: ${usePlatform ? "Platform (Tier 3)" : "Local Ollama (Tier 1)"}`));
|
|
6229
|
+
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"));
|
|
6230
|
+
console.log("");
|
|
6231
|
+
const dna = buildDNAString();
|
|
6232
|
+
if (dna) {
|
|
6233
|
+
console.log(GREEN5(" \u2705 Behavioral DNA loaded."));
|
|
6234
|
+
} else {
|
|
6235
|
+
console.log(AMBER6(" \u26A0\uFE0F No behavioral profile found."));
|
|
6236
|
+
console.log(GRAY5(" UserBob performs significantly better with your DNA loaded."));
|
|
6237
|
+
console.log("");
|
|
6238
|
+
const rl = readline8.createInterface({ input: process.stdin, output: process.stdout, terminal: true });
|
|
6239
|
+
const answer = await new Promise((resolve2) => rl.question(AMBER6(" Run `bob profile --today` now? (y/n): "), resolve2));
|
|
6240
|
+
rl.close();
|
|
6241
|
+
if (answer.trim().toLowerCase() === "y") {
|
|
6242
|
+
console.log("");
|
|
6243
|
+
console.log(GRAY5(" Run `bob profile --today` in a separate terminal, then re-run `bob userbob`."));
|
|
6244
|
+
process.exit(0);
|
|
6245
|
+
} else {
|
|
6246
|
+
console.log("");
|
|
6247
|
+
console.log(RED5(" \u26A0\uFE0F Running in Generic Mode \u2014 no behavioral profile loaded."));
|
|
6248
|
+
console.log(RED5(" UserBob will respond using project context only."));
|
|
6249
|
+
console.log(RED5(" Responses won't reflect your personal communication style,"));
|
|
6250
|
+
console.log(RED5(" decision patterns, or engineering philosophy."));
|
|
6251
|
+
console.log(GRAY5(" Run `bob profile --today` anytime to unlock full personalization."));
|
|
6252
|
+
console.log("");
|
|
6253
|
+
}
|
|
6254
|
+
}
|
|
6255
|
+
let mission = missionArgs.length > 0 ? missionArgs.join(" ") : "";
|
|
6256
|
+
if (!mission && !options.resume) {
|
|
6257
|
+
const mrl = readline8.createInterface({ input: process.stdin, output: process.stdout, terminal: true });
|
|
6258
|
+
mission = await new Promise((resolve2) => mrl.question(AMBER6(" What's the mission? > "), resolve2));
|
|
6259
|
+
mrl.close();
|
|
6260
|
+
if (!mission.trim()) {
|
|
6261
|
+
console.log(RED5(" \u274C Mission cannot be empty. Exiting."));
|
|
6262
|
+
process.exit(1);
|
|
6263
|
+
}
|
|
6264
|
+
mission = mission.trim();
|
|
6265
|
+
}
|
|
6266
|
+
console.log("");
|
|
6267
|
+
console.log(GRAY5(` Target: ${params.target}% \u2502 Grade: ${params.grading} \u2502 Stag: ${params.stag || "\u221E"} \u2502 Div: ${params.div || "\u221E"}`));
|
|
6268
|
+
console.log("");
|
|
6269
|
+
if (usePlatform) {
|
|
6270
|
+
const conversationId = config.conversationId;
|
|
6271
|
+
if (!conversationId) {
|
|
6272
|
+
console.log(RED5(" \u274C No active conversation. Run `bob conversations join` first."));
|
|
6273
|
+
process.exit(1);
|
|
6274
|
+
}
|
|
6275
|
+
if (options.resume) {
|
|
6276
|
+
console.log(AMBER6(" \u{1F504} Resuming simulation (no new mission note)..."));
|
|
6277
|
+
await callHTTPFunction("userSimManagerService", {
|
|
6278
|
+
action: "resumeMission",
|
|
6279
|
+
conversationId,
|
|
6280
|
+
uid: config.uid,
|
|
6281
|
+
email: config.email
|
|
6282
|
+
});
|
|
6283
|
+
console.log(GREEN5(" \u2705 Simulation resumed. Entering watch mode..."));
|
|
6284
|
+
console.log("");
|
|
6285
|
+
await runPlatformSimulation(config, conversationId, mission || "Resumed session", params);
|
|
6286
|
+
} else {
|
|
6287
|
+
await runPlatformSimulation(config, conversationId, mission, params);
|
|
6288
|
+
}
|
|
6289
|
+
return;
|
|
6290
|
+
}
|
|
6291
|
+
if (!config.localEndpoint) {
|
|
6292
|
+
console.log(RED5(" \u274C No local model configured."));
|
|
6293
|
+
console.log(GRAY5(" Run: bob config set localEndpoint http://127.0.0.1:11434/api/chat"));
|
|
6294
|
+
process.exit(1);
|
|
6295
|
+
}
|
|
6296
|
+
await runLocalSimulation(config, dna, mission, params);
|
|
6297
|
+
});
|
|
6298
|
+
}
|
|
6299
|
+
|
|
6300
|
+
// src/commands/command-center.ts
|
|
6301
|
+
import chalk25 from "chalk";
|
|
6302
|
+
import inquirer from "inquirer";
|
|
6303
|
+
var ORANGE2 = chalk25.hex("#E66F24");
|
|
6304
|
+
var AMBER7 = chalk25.hex("#FFAB00");
|
|
6305
|
+
var GREEN6 = chalk25.hex("#66BB6A");
|
|
6306
|
+
var RED6 = chalk25.hex("#EF5350");
|
|
6307
|
+
var BLUE5 = chalk25.hex("#42A5F5");
|
|
6308
|
+
var PURPLE2 = chalk25.hex("#AB47BC");
|
|
6309
|
+
var CYAN6 = chalk25.hex("#26C6DA");
|
|
6310
|
+
var TEAL = chalk25.hex("#26A69A");
|
|
6311
|
+
var GRAY6 = chalk25.gray;
|
|
6312
|
+
var WHITE4 = chalk25.white;
|
|
6313
|
+
var BORDER11 = chalk25.hex("#455A64");
|
|
6314
|
+
function statusColor(status) {
|
|
6315
|
+
switch (status) {
|
|
6316
|
+
case "queued":
|
|
6317
|
+
return GRAY6;
|
|
6318
|
+
case "awaiting_approval":
|
|
6319
|
+
return AMBER7;
|
|
6320
|
+
case "in_progress":
|
|
6321
|
+
return BLUE5;
|
|
6322
|
+
case "completed":
|
|
6323
|
+
return GREEN6;
|
|
6324
|
+
case "failed":
|
|
6325
|
+
return RED6;
|
|
6326
|
+
case "denied":
|
|
6327
|
+
return RED6;
|
|
6328
|
+
default:
|
|
6329
|
+
return GRAY6;
|
|
6330
|
+
}
|
|
6331
|
+
}
|
|
6332
|
+
function statusLabel(status) {
|
|
6333
|
+
switch (status) {
|
|
6334
|
+
case "queued":
|
|
6335
|
+
return "QUEUED";
|
|
6336
|
+
case "awaiting_approval":
|
|
6337
|
+
return "NEEDS APPROVAL";
|
|
6338
|
+
case "in_progress":
|
|
6339
|
+
return "IN PROGRESS";
|
|
6340
|
+
case "completed":
|
|
6341
|
+
return "COMPLETE";
|
|
6342
|
+
case "failed":
|
|
6343
|
+
return "FAILED";
|
|
6344
|
+
case "denied":
|
|
6345
|
+
return "DENIED";
|
|
6346
|
+
default:
|
|
6347
|
+
return status.toUpperCase();
|
|
6348
|
+
}
|
|
6349
|
+
}
|
|
6350
|
+
function categoryColor(category) {
|
|
6351
|
+
const map = {
|
|
6352
|
+
security: RED6,
|
|
6353
|
+
frontend: BLUE5,
|
|
6354
|
+
backend: GREEN6,
|
|
6355
|
+
cloud_functions: PURPLE2,
|
|
6356
|
+
documentation: TEAL,
|
|
6357
|
+
testing: AMBER7,
|
|
6358
|
+
review: CYAN6,
|
|
6359
|
+
fullstack: ORANGE2
|
|
6360
|
+
};
|
|
6361
|
+
return map[category] || GRAY6;
|
|
6362
|
+
}
|
|
6363
|
+
function confidenceColor(confidence) {
|
|
6364
|
+
if (confidence >= 80) return GREEN6;
|
|
6365
|
+
if (confidence >= 50) return AMBER7;
|
|
6366
|
+
return RED6;
|
|
6367
|
+
}
|
|
6368
|
+
function formatTimestamp(ms) {
|
|
6369
|
+
if (!ms) return "\u2014";
|
|
6370
|
+
return new Date(ms).toLocaleString("en-US", {
|
|
6371
|
+
month: "short",
|
|
6372
|
+
day: "numeric",
|
|
6373
|
+
hour: "numeric",
|
|
6374
|
+
minute: "2-digit",
|
|
6375
|
+
hour12: true
|
|
6376
|
+
});
|
|
6377
|
+
}
|
|
6378
|
+
function stripMarkdown2(text) {
|
|
6379
|
+
return text.replace(/\*\*(.+?)\*\*/g, "$1").replace(/\*(.+?)\*/g, "$1").replace(/^#{1,6}\s+/gm, "").replace(/^---+$/gm, "").replace(/`([^`]+)`/g, "$1").replace(/^\s*[-*+]\s+/gm, " \u2022 ").replace(/\n{3,}/g, "\n\n").trim();
|
|
6380
|
+
}
|
|
6381
|
+
function renderStats(stats) {
|
|
6382
|
+
console.log("");
|
|
6383
|
+
console.log(BORDER11(" \u2500\u2500\u2500 COMMAND CENTER \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
6384
|
+
const parts = [];
|
|
6385
|
+
if (stats.awaiting_approval > 0) parts.push(AMBER7(`${stats.awaiting_approval} PENDING`));
|
|
6386
|
+
if (stats.in_progress > 0) parts.push(BLUE5(`${stats.in_progress} RUNNING`));
|
|
6387
|
+
if (stats.completed > 0) parts.push(GREEN6(`${stats.completed} DONE`));
|
|
6388
|
+
if (stats.failed > 0) parts.push(RED6(`${stats.failed} FAILED`));
|
|
6389
|
+
if (stats.denied > 0) parts.push(GRAY6(`${stats.denied} DENIED`));
|
|
6390
|
+
parts.push(GRAY6(`${stats.total} TOTAL`));
|
|
6391
|
+
console.log(" " + parts.join(GRAY6(" \u2502 ")));
|
|
6392
|
+
console.log(BORDER11(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
6393
|
+
console.log("");
|
|
6394
|
+
}
|
|
6395
|
+
function renderTaskDetail(task) {
|
|
6396
|
+
const catColor = categoryColor(task.request.category);
|
|
6397
|
+
const confColor = confidenceColor(task.request.confidence);
|
|
6398
|
+
const sColor = statusColor(task.outcome.status);
|
|
6399
|
+
console.log("");
|
|
6400
|
+
console.log(BORDER11(" \u250C\u2500 TASK DETAIL \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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"));
|
|
6401
|
+
console.log(BORDER11(" \u2502"));
|
|
6402
|
+
console.log(BORDER11(" \u2502 ") + WHITE4(task.request.description));
|
|
6403
|
+
console.log(BORDER11(" \u2502"));
|
|
6404
|
+
console.log(BORDER11(" \u2502 ") + catColor(`[${task.request.category.toUpperCase()}]`) + " " + WHITE4(`${task.request.taskType.toUpperCase()}`) + " " + confColor(`${task.request.confidence}% confidence`) + " " + sColor(`\u25CF ${statusLabel(task.outcome.status)}`));
|
|
6405
|
+
if (task.request.targetFile) {
|
|
6406
|
+
console.log(BORDER11(" \u2502 ") + GRAY6("File: ") + CYAN6(task.request.targetFile));
|
|
6407
|
+
}
|
|
6408
|
+
console.log(BORDER11(" \u2502"));
|
|
6409
|
+
console.log(BORDER11(" \u2502 ") + GRAY6("Priority: ") + WHITE4(`P${task.priority}`) + GRAY6(" \u2502 Difficulty: ") + WHITE4(task.difficulty.toUpperCase()) + GRAY6(" \u2502 Created: ") + WHITE4(formatTimestamp(task.createdAt)));
|
|
6410
|
+
console.log(BORDER11(" \u2502"));
|
|
6411
|
+
console.log(BORDER11(" \u2502 ") + AMBER7("TRIGGER"));
|
|
6412
|
+
console.log(BORDER11(" \u2502 ") + GRAY6(stripMarkdown2(task.trigger.reasoning).slice(0, 120) + "..."));
|
|
6413
|
+
if (task.trigger.turnSatisfaction !== null) {
|
|
6414
|
+
console.log(BORDER11(" \u2502 ") + GRAY6("SAT at dispatch: ") + CYAN6(`${task.trigger.turnSatisfaction}%`));
|
|
6415
|
+
}
|
|
6416
|
+
if (task.outcome.resultSummary) {
|
|
6417
|
+
console.log(BORDER11(" \u2502"));
|
|
6418
|
+
console.log(BORDER11(" \u2502 ") + GREEN6("RESULT"));
|
|
6419
|
+
console.log(BORDER11(" \u2502 ") + GRAY6(stripMarkdown2(task.outcome.resultSummary).slice(0, 200) + "..."));
|
|
6420
|
+
}
|
|
6421
|
+
if (task.outcome.filesModified && task.outcome.filesModified.length > 0) {
|
|
6422
|
+
console.log(BORDER11(" \u2502"));
|
|
6423
|
+
console.log(BORDER11(" \u2502 ") + AMBER7("FILES MODIFIED"));
|
|
6424
|
+
for (const file of task.outcome.filesModified) {
|
|
6425
|
+
const icon = file.action === "created" ? GREEN6("+") : AMBER7("~");
|
|
6426
|
+
console.log(BORDER11(" \u2502 ") + icon + " " + CYAN6(file.path || file));
|
|
6427
|
+
}
|
|
6428
|
+
}
|
|
6429
|
+
if (task.outcome.error) {
|
|
6430
|
+
console.log(BORDER11(" \u2502"));
|
|
6431
|
+
console.log(BORDER11(" \u2502 ") + RED6("ERROR"));
|
|
6432
|
+
console.log(BORDER11(" \u2502 ") + RED6(task.outcome.error.slice(0, 150)));
|
|
6433
|
+
}
|
|
6434
|
+
if (task.isDenied && task.denyReason) {
|
|
6435
|
+
console.log(BORDER11(" \u2502"));
|
|
6436
|
+
console.log(BORDER11(" \u2502 ") + RED6("DENIED BY: ") + GRAY6(task.deniedBy || "Unknown"));
|
|
6437
|
+
console.log(BORDER11(" \u2502 ") + RED6("REASON: ") + GRAY6(task.denyReason));
|
|
6438
|
+
}
|
|
6439
|
+
if (task.outcome.turnsUsed || task.outcome.tokensConsumed) {
|
|
6440
|
+
console.log(BORDER11(" \u2502"));
|
|
6441
|
+
if (task.outcome.turnsUsed) {
|
|
6442
|
+
console.log(BORDER11(" \u2502 ") + GRAY6(`Turns: ${task.outcome.turnsUsed} \u2502 Tokens: ${task.outcome.tokensConsumed || 0} \u2502 Provider: ${task.outcome.provider || "unknown"}`));
|
|
6443
|
+
}
|
|
6444
|
+
}
|
|
6445
|
+
console.log(BORDER11(" \u2502"));
|
|
6446
|
+
console.log(BORDER11(" \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\u2518"));
|
|
6447
|
+
console.log("");
|
|
6448
|
+
}
|
|
6449
|
+
async function runDecisionStream(conversationId) {
|
|
6450
|
+
console.log("");
|
|
6451
|
+
console.log(ORANGE2(" \u2500\u2500\u2500 DECISION STREAM \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
6452
|
+
console.log(GRAY6(" Live feed of all autonomous decisions. Ctrl+C to exit.\n"));
|
|
6453
|
+
let lastCount = 0;
|
|
6454
|
+
let running = true;
|
|
6455
|
+
process.on("SIGINT", () => {
|
|
6456
|
+
running = false;
|
|
6457
|
+
console.log("\n" + AMBER7(" Stream ended."));
|
|
6458
|
+
process.exit(0);
|
|
6459
|
+
});
|
|
6460
|
+
while (running) {
|
|
6461
|
+
try {
|
|
6462
|
+
const response = await callCloudFunction("getCLIAutonomousTasks", { conversationId });
|
|
6463
|
+
const tasks = response?.tasks || [];
|
|
6464
|
+
if (tasks.length > lastCount) {
|
|
6465
|
+
const newTasks = tasks.slice(0, tasks.length - lastCount);
|
|
6466
|
+
for (const task of newTasks.reverse()) {
|
|
6467
|
+
const sColor = statusColor(task.outcome.status);
|
|
6468
|
+
const catColor = categoryColor(task.request.category);
|
|
6469
|
+
console.log(
|
|
6470
|
+
GRAY6(` ${formatTimestamp(task.createdAt)} `) + catColor(`[${task.request.category.toUpperCase()}]`) + " " + WHITE4(task.request.description.slice(0, 60) + (task.request.description.length > 60 ? "..." : "")) + " " + sColor(`\u25CF ${statusLabel(task.outcome.status)}`)
|
|
6471
|
+
);
|
|
6472
|
+
}
|
|
6473
|
+
lastCount = tasks.length;
|
|
6474
|
+
}
|
|
6475
|
+
} catch (e) {
|
|
6476
|
+
console.log(RED6(` \u274C Poll error: ${e.message}`));
|
|
6477
|
+
}
|
|
6478
|
+
await new Promise((r) => setTimeout(r, 3e3));
|
|
6479
|
+
}
|
|
6480
|
+
}
|
|
6481
|
+
async function runSettings(conversationId) {
|
|
6482
|
+
let response;
|
|
6483
|
+
try {
|
|
6484
|
+
response = await callCloudFunction("getCLIAutonomousTasks", { conversationId });
|
|
6485
|
+
} catch (e) {
|
|
6486
|
+
console.log(RED6(` \u274C Failed to fetch settings: ${e.message}`));
|
|
6487
|
+
return;
|
|
6488
|
+
}
|
|
6489
|
+
const settings = response?.settings || {};
|
|
6490
|
+
console.log("");
|
|
6491
|
+
console.log(ORANGE2(" \u2500\u2500\u2500 AUTONOMY SETTINGS \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
6492
|
+
console.log(GRAY6(` Current threshold: ${settings.autonomousConfidenceThreshold ?? 75}%`));
|
|
6493
|
+
console.log(GRAY6(" Tasks below this confidence level require your approval."));
|
|
6494
|
+
console.log("");
|
|
6495
|
+
const { threshold } = await inquirer.prompt([
|
|
6496
|
+
{
|
|
6497
|
+
type: "number",
|
|
6498
|
+
name: "threshold",
|
|
6499
|
+
message: AMBER7(" Confidence threshold (0-100):"),
|
|
6500
|
+
default: settings.autonomousConfidenceThreshold ?? 75,
|
|
6501
|
+
validate: (val) => val >= 0 && val <= 100 ? true : "Must be 0-100"
|
|
6502
|
+
}
|
|
6503
|
+
]);
|
|
6504
|
+
const categories = ["security", "frontend", "backend", "cloud_functions", "documentation", "testing", "review", "fullstack"];
|
|
6505
|
+
const overrides = { ...settings.autonomousCategoryOverrides || {} };
|
|
6506
|
+
console.log("");
|
|
6507
|
+
console.log(GRAY6(" Category overrides:"));
|
|
6508
|
+
console.log("");
|
|
6509
|
+
for (const cat of categories) {
|
|
6510
|
+
const current = overrides[cat] || "threshold";
|
|
6511
|
+
const { override } = await inquirer.prompt([
|
|
6512
|
+
{
|
|
6513
|
+
type: "select",
|
|
6514
|
+
name: "override",
|
|
6515
|
+
message: categoryColor(cat)(` ${cat.padEnd(20)}`),
|
|
6516
|
+
default: current,
|
|
6517
|
+
choices: [
|
|
6518
|
+
{ name: GRAY6("Use Threshold"), value: "threshold" },
|
|
6519
|
+
{ name: GREEN6("Always Auto-Execute"), value: "autonomous" },
|
|
6520
|
+
{ name: RED6("Always Require Approval"), value: "approval_required" }
|
|
6521
|
+
]
|
|
6522
|
+
}
|
|
6523
|
+
]);
|
|
6524
|
+
if (override === "threshold") {
|
|
6525
|
+
delete overrides[cat];
|
|
6526
|
+
} else {
|
|
6527
|
+
overrides[cat] = override;
|
|
6528
|
+
}
|
|
6529
|
+
}
|
|
6530
|
+
try {
|
|
6531
|
+
await callCloudFunction("updateCLIAutonomySettings", {
|
|
6532
|
+
conversationId,
|
|
6533
|
+
autonomousConfidenceThreshold: threshold,
|
|
6534
|
+
autonomousCategoryOverrides: overrides
|
|
6535
|
+
});
|
|
6536
|
+
console.log("");
|
|
6537
|
+
console.log(GREEN6(" \u2705 Settings saved."));
|
|
6538
|
+
} catch {
|
|
6539
|
+
console.log("");
|
|
6540
|
+
console.log(AMBER7(" \u26A0\uFE0F `updateCLIAutonomySettings` not deployed yet."));
|
|
6541
|
+
console.log(GRAY6(" Update via web app for now, or deploy the CF first."));
|
|
6542
|
+
}
|
|
6543
|
+
console.log("");
|
|
6544
|
+
}
|
|
6545
|
+
async function runTaskBoard(conversationId) {
|
|
6546
|
+
let continueLoop = true;
|
|
6547
|
+
while (continueLoop) {
|
|
6548
|
+
let response;
|
|
6549
|
+
try {
|
|
6550
|
+
response = await callCloudFunction("getCLIAutonomousTasks", { conversationId });
|
|
6551
|
+
} catch (e) {
|
|
6552
|
+
console.log(RED6(` \u274C Failed to fetch tasks: ${e.message}`));
|
|
6553
|
+
return;
|
|
6554
|
+
}
|
|
6555
|
+
const tasks = response?.tasks || [];
|
|
6556
|
+
const stats = response?.stats || {};
|
|
6557
|
+
renderStats(stats);
|
|
6558
|
+
if (tasks.length === 0) {
|
|
6559
|
+
console.log(GRAY6(" No autonomous tasks found for this conversation."));
|
|
6560
|
+
console.log(GRAY6(" Tasks appear here when UserBob dispatches work to Mini Bob."));
|
|
6561
|
+
console.log("");
|
|
6562
|
+
return;
|
|
6563
|
+
}
|
|
6564
|
+
const seen = /* @__PURE__ */ new Map();
|
|
6565
|
+
for (const task of tasks) {
|
|
6566
|
+
const key = task.request.description.slice(0, 80);
|
|
6567
|
+
if (!seen.has(key) || (task.createdAt || 0) > (seen.get(key).createdAt || 0)) {
|
|
6568
|
+
seen.set(key, task);
|
|
6569
|
+
}
|
|
6570
|
+
}
|
|
6571
|
+
const dedupedTasks = Array.from(seen.values());
|
|
6572
|
+
const taskChoices = dedupedTasks.map((task) => {
|
|
6573
|
+
const sColor = statusColor(task.outcome.status);
|
|
6574
|
+
const catColor = categoryColor(task.request.category);
|
|
6575
|
+
const desc = task.request.description.slice(0, 55) + (task.request.description.length > 55 ? "..." : "");
|
|
6576
|
+
return {
|
|
6577
|
+
name: sColor(` \u25CF ${statusLabel(task.outcome.status).padEnd(16)}`) + " " + catColor(`[${task.request.category.padEnd(15)}]`) + " " + WHITE4(desc),
|
|
6578
|
+
value: task.id,
|
|
6579
|
+
short: task.request.description.slice(0, 40)
|
|
6580
|
+
};
|
|
6581
|
+
});
|
|
6582
|
+
taskChoices.push({
|
|
6583
|
+
name: BORDER11(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),
|
|
6584
|
+
value: "__separator__",
|
|
6585
|
+
short: "",
|
|
6586
|
+
disabled: true
|
|
6587
|
+
});
|
|
6588
|
+
taskChoices.push({
|
|
6589
|
+
name: GRAY6(" \u21A9 Exit Command Center"),
|
|
6590
|
+
value: "__exit__",
|
|
6591
|
+
short: "Exit"
|
|
6592
|
+
});
|
|
6593
|
+
const { selectedTaskId } = await inquirer.prompt([
|
|
6594
|
+
{
|
|
6595
|
+
type: "select",
|
|
6596
|
+
name: "selectedTaskId",
|
|
6597
|
+
message: ORANGE2(" Select a task to inspect:"),
|
|
6598
|
+
choices: taskChoices,
|
|
6599
|
+
pageSize: 14
|
|
6600
|
+
}
|
|
6601
|
+
]);
|
|
6602
|
+
if (selectedTaskId === "__exit__" || selectedTaskId === "__separator__") {
|
|
6603
|
+
continueLoop = false;
|
|
6604
|
+
break;
|
|
6605
|
+
}
|
|
6606
|
+
const selectedTask = dedupedTasks.find((t) => t.id === selectedTaskId);
|
|
6607
|
+
if (!selectedTask) continue;
|
|
6608
|
+
renderTaskDetail(selectedTask);
|
|
6609
|
+
const actions = [];
|
|
6610
|
+
if (selectedTask.outcome.status === "awaiting_approval") {
|
|
6611
|
+
actions.push({ name: GREEN6(" \u2705 Approve \u2014 execute this task now"), value: "approve", short: "Approve" });
|
|
6612
|
+
actions.push({ name: RED6(" \u274C Deny \u2014 reject this task"), value: "deny", short: "Deny" });
|
|
6613
|
+
}
|
|
6614
|
+
actions.push({ name: AMBER7(" \u21A9 Back to task list"), value: "back", short: "Back" });
|
|
6615
|
+
actions.push({ name: GRAY6(" \u2715 Exit Command Center"), value: "exit", short: "Exit" });
|
|
6616
|
+
const { action } = await inquirer.prompt([
|
|
6617
|
+
{
|
|
6618
|
+
type: "select",
|
|
6619
|
+
name: "action",
|
|
6620
|
+
message: ORANGE2(" What would you like to do?"),
|
|
6621
|
+
choices: actions
|
|
6622
|
+
}
|
|
6623
|
+
]);
|
|
6624
|
+
if (action === "approve") {
|
|
6625
|
+
const { confirmed } = await inquirer.prompt([
|
|
6626
|
+
{
|
|
6627
|
+
type: "confirm",
|
|
6628
|
+
name: "confirmed",
|
|
6629
|
+
message: AMBER7(" Approve task and execute autonomously?"),
|
|
6630
|
+
default: true
|
|
6631
|
+
}
|
|
6632
|
+
]);
|
|
6633
|
+
if (confirmed) {
|
|
6634
|
+
console.log("");
|
|
6635
|
+
console.log(ORANGE2(" \u2500\u2500\u2500 EXECUTION STREAM \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
6636
|
+
console.log(GRAY6(" Mini Bob is executing. Streaming live logs...\n"));
|
|
6637
|
+
callCloudFunction("approveAutonomousTask", {
|
|
6638
|
+
conversationId,
|
|
6639
|
+
taskId: selectedTask.id,
|
|
6640
|
+
action: "approve"
|
|
6641
|
+
}).then(() => {
|
|
6642
|
+
}).catch((e) => {
|
|
6643
|
+
console.log(RED6(`
|
|
6644
|
+
\u274C Execution error: ${e.message}`));
|
|
6645
|
+
});
|
|
6646
|
+
let running = true;
|
|
6647
|
+
let seenLogIds = /* @__PURE__ */ new Set();
|
|
6648
|
+
let pollErrors = 0;
|
|
6649
|
+
const maxPollErrors = 5;
|
|
6650
|
+
const sigintHandler = () => {
|
|
6651
|
+
running = false;
|
|
6652
|
+
console.log("\n" + AMBER7(" Stream ended. Task continues in background."));
|
|
6653
|
+
process.exit(0);
|
|
6654
|
+
};
|
|
6655
|
+
process.on("SIGINT", sigintHandler);
|
|
6656
|
+
while (running) {
|
|
6657
|
+
await new Promise((r) => setTimeout(r, 2e3));
|
|
6658
|
+
try {
|
|
6659
|
+
const taskResponse = await callCloudFunction("getCLIAutonomousTasks", {
|
|
6660
|
+
conversationId,
|
|
6661
|
+
statusFilter: null
|
|
6662
|
+
});
|
|
6663
|
+
const updatedTask = taskResponse?.tasks?.find((t) => t.id === selectedTask.id);
|
|
6664
|
+
const logResponse = await callCloudFunction("getCLITaskExecutionLog", {
|
|
6665
|
+
conversationId,
|
|
6666
|
+
taskId: selectedTask.id
|
|
6667
|
+
});
|
|
6668
|
+
const logEntries = logResponse?.entries || [];
|
|
6669
|
+
for (const entry of logEntries) {
|
|
6670
|
+
if (seenLogIds.has(entry.id)) continue;
|
|
6671
|
+
seenLogIds.add(entry.id);
|
|
6672
|
+
const stage = entry.stage || "EXECUTION";
|
|
6673
|
+
let prefix;
|
|
6674
|
+
switch (stage) {
|
|
6675
|
+
case "INIT":
|
|
6676
|
+
prefix = CYAN6(" [INIT] ");
|
|
6677
|
+
break;
|
|
6678
|
+
case "TOOL_CALL":
|
|
6679
|
+
prefix = AMBER7(" [TOOL] ");
|
|
6680
|
+
break;
|
|
6681
|
+
case "FALLBACK":
|
|
6682
|
+
prefix = AMBER7(" [FALLBACK] ");
|
|
6683
|
+
break;
|
|
6684
|
+
case "COMPLETE":
|
|
6685
|
+
prefix = GREEN6(" [DONE] ");
|
|
6686
|
+
break;
|
|
6687
|
+
case "ERROR":
|
|
6688
|
+
prefix = RED6(" [ERROR] ");
|
|
6689
|
+
break;
|
|
6690
|
+
case "APPROVED":
|
|
6691
|
+
prefix = GREEN6(" [APPROVED] ");
|
|
6692
|
+
break;
|
|
6693
|
+
case "DENIED":
|
|
6694
|
+
prefix = RED6(" [DENIED] ");
|
|
6695
|
+
break;
|
|
6696
|
+
case "AWAITING_APPROVAL":
|
|
6697
|
+
prefix = AMBER7(" [PENDING] ");
|
|
6698
|
+
break;
|
|
6699
|
+
default:
|
|
6700
|
+
prefix = GRAY6(" [LOG] ");
|
|
6701
|
+
break;
|
|
6702
|
+
}
|
|
6703
|
+
console.log(prefix + WHITE4(entry.text));
|
|
6704
|
+
}
|
|
6705
|
+
if (updatedTask) {
|
|
6706
|
+
const status = updatedTask.outcome.status;
|
|
6707
|
+
if (status === "completed") {
|
|
6708
|
+
console.log("");
|
|
6709
|
+
console.log(GREEN6(" \u2705 Task complete!"));
|
|
6710
|
+
if (updatedTask.outcome.filesModified?.length > 0) {
|
|
6711
|
+
console.log("");
|
|
6712
|
+
console.log(AMBER7(" Files modified:"));
|
|
6713
|
+
for (const file of updatedTask.outcome.filesModified) {
|
|
6714
|
+
const icon = file.action === "created" ? GREEN6("+") : AMBER7("~");
|
|
6715
|
+
console.log(` ${icon} ${CYAN6(file.path || file)}`);
|
|
6716
|
+
}
|
|
6717
|
+
}
|
|
6718
|
+
if (updatedTask.outcome.resultSummary) {
|
|
6719
|
+
console.log("");
|
|
6720
|
+
console.log(AMBER7(" Summary:"));
|
|
6721
|
+
const summary = stripMarkdown2(updatedTask.outcome.resultSummary);
|
|
6722
|
+
const lines = summary.split("\n").slice(0, 8);
|
|
6723
|
+
for (const line of lines) {
|
|
6724
|
+
console.log(GRAY6(` ${line}`));
|
|
6725
|
+
}
|
|
6726
|
+
}
|
|
6727
|
+
if (updatedTask.outcome.turnsUsed) {
|
|
6728
|
+
console.log("");
|
|
6729
|
+
console.log(GRAY6(` Turns: ${updatedTask.outcome.turnsUsed} \u2502 Tokens: ${updatedTask.outcome.tokensConsumed || 0} \u2502 Provider: ${updatedTask.outcome.provider || "unknown"}`));
|
|
6730
|
+
}
|
|
6731
|
+
running = false;
|
|
6732
|
+
} else if (status === "failed") {
|
|
6733
|
+
console.log("");
|
|
6734
|
+
console.log(RED6(` \u274C Task failed: ${updatedTask.outcome.error || "Unknown error"}`));
|
|
6735
|
+
running = false;
|
|
6736
|
+
} else if (status === "denied") {
|
|
6737
|
+
console.log("");
|
|
6738
|
+
console.log(RED6(" \u274C Task was denied."));
|
|
6739
|
+
running = false;
|
|
6740
|
+
}
|
|
6741
|
+
}
|
|
6742
|
+
pollErrors = 0;
|
|
6743
|
+
} catch (e) {
|
|
6744
|
+
pollErrors++;
|
|
6745
|
+
if (pollErrors >= maxPollErrors) {
|
|
6746
|
+
console.log(RED6(` \u274C Lost connection after ${maxPollErrors} errors. Task continues in background.`));
|
|
6747
|
+
running = false;
|
|
6748
|
+
}
|
|
6749
|
+
}
|
|
6750
|
+
}
|
|
6751
|
+
process.removeListener("SIGINT", sigintHandler);
|
|
6752
|
+
console.log("");
|
|
6753
|
+
}
|
|
6754
|
+
} else if (action === "deny") {
|
|
6755
|
+
const { reason } = await inquirer.prompt([
|
|
6756
|
+
{
|
|
6757
|
+
type: "input",
|
|
6758
|
+
name: "reason",
|
|
6759
|
+
message: AMBER7(" Denial reason (optional):"),
|
|
6760
|
+
default: ""
|
|
6761
|
+
}
|
|
6762
|
+
]);
|
|
6763
|
+
try {
|
|
6764
|
+
console.log(GRAY6(" Denying task..."));
|
|
6765
|
+
await callCloudFunction("approveAutonomousTask", {
|
|
6766
|
+
conversationId,
|
|
6767
|
+
taskId: selectedTask.id,
|
|
6768
|
+
action: "deny",
|
|
6769
|
+
reason: reason.trim() || null
|
|
6770
|
+
});
|
|
6771
|
+
console.log("");
|
|
6772
|
+
console.log(RED6(" \u274C Task denied."));
|
|
6773
|
+
console.log("");
|
|
6774
|
+
} catch (e) {
|
|
6775
|
+
console.log(RED6(` \u274C Failed to deny: ${e.message}`));
|
|
6776
|
+
}
|
|
6777
|
+
} else if (action === "exit") {
|
|
6778
|
+
continueLoop = false;
|
|
6779
|
+
break;
|
|
6780
|
+
}
|
|
6781
|
+
}
|
|
6782
|
+
}
|
|
6783
|
+
function registerCommandCenterCommand(program2) {
|
|
6784
|
+
program2.command("command-center").alias("cc").description("Autonomous Command Center \u2014 inspect, approve, and manage UserBob dispatch tasks").option("--stream", "Live decision stream feed").option("--settings", "Configure autonomy threshold and category overrides").action(async (options) => {
|
|
6785
|
+
if (!isAuthenticated()) {
|
|
6786
|
+
console.log("");
|
|
6787
|
+
console.log(RED6(" \u274C Authentication required."));
|
|
6788
|
+
console.log(GRAY6(" Run `bob login` first."));
|
|
6789
|
+
console.log("");
|
|
6790
|
+
process.exit(1);
|
|
6791
|
+
}
|
|
6792
|
+
const config = getConfig();
|
|
6793
|
+
const conversationId = config.conversationId;
|
|
6794
|
+
if (!conversationId) {
|
|
6795
|
+
console.log("");
|
|
6796
|
+
console.log(RED6(" \u274C No active conversation."));
|
|
6797
|
+
console.log(GRAY6(" Run `bob conversations join` first."));
|
|
6798
|
+
console.log("");
|
|
6799
|
+
process.exit(1);
|
|
6800
|
+
}
|
|
6801
|
+
console.log("");
|
|
6802
|
+
console.log(BORDER11(" \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"));
|
|
6803
|
+
console.log(BORDER11(" \u2551") + ORANGE2(" \u25C9 AUTONOMOUS COMMAND CENTER"));
|
|
6804
|
+
console.log(BORDER11(" \u2551") + GRAY6(` Conversation: ${conversationId.slice(0, 24)}...`));
|
|
6805
|
+
console.log(BORDER11(" \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"));
|
|
6806
|
+
if (options.stream) {
|
|
6807
|
+
await runDecisionStream(conversationId);
|
|
6808
|
+
return;
|
|
6809
|
+
}
|
|
6810
|
+
if (options.settings) {
|
|
6811
|
+
await runSettings(conversationId);
|
|
6812
|
+
return;
|
|
6813
|
+
}
|
|
6814
|
+
await runTaskBoard(conversationId);
|
|
6815
|
+
});
|
|
6816
|
+
}
|
|
6817
|
+
|
|
5723
6818
|
// bin/bob.ts
|
|
5724
|
-
var BRAND_PRIMARY11 =
|
|
5725
|
-
var BRAND_SECONDARY15 =
|
|
5726
|
-
var SUCCESS16 =
|
|
5727
|
-
var INFO16 =
|
|
5728
|
-
var MUTED16 =
|
|
5729
|
-
var MODE_CONSULTANT9 =
|
|
6819
|
+
var BRAND_PRIMARY11 = chalk26.hex("#E66F24");
|
|
6820
|
+
var BRAND_SECONDARY15 = chalk26.hex("#FFAB00");
|
|
6821
|
+
var SUCCESS16 = chalk26.hex("#66BB6A");
|
|
6822
|
+
var INFO16 = chalk26.hex("#26C6DA");
|
|
6823
|
+
var MUTED16 = chalk26.hex("#78909C");
|
|
6824
|
+
var MODE_CONSULTANT9 = chalk26.hex("#AB47BC");
|
|
5730
6825
|
var program = new Command();
|
|
5731
6826
|
program.name("bob").description("Bob's CLI \u2014 AI coding assistant and Forge orchestrator").version("0.2.0").helpOption(false).addHelpCommand(false);
|
|
5732
6827
|
program.option("-h, --help", "Print this usage information").on("option:help", () => {
|
|
@@ -5740,7 +6835,7 @@ function printCustomHelp() {
|
|
|
5740
6835
|
console.log("");
|
|
5741
6836
|
console.log(BRAND_PRIMARY11(" \u25C9 Bob's CLI") + MUTED16(" \u2014 Your AI Engineering Partner, In Your Terminal."));
|
|
5742
6837
|
console.log("");
|
|
5743
|
-
console.log(
|
|
6838
|
+
console.log(chalk26.white(" Common commands:"));
|
|
5744
6839
|
console.log("");
|
|
5745
6840
|
console.log(BRAND_SECONDARY15(' bob chat "message"'));
|
|
5746
6841
|
console.log(MUTED16(" Chat with Bob \u2014 code-friendly engineering partner with file awareness."));
|
|
@@ -5751,13 +6846,13 @@ function printCustomHelp() {
|
|
|
5751
6846
|
console.log(BRAND_SECONDARY15(" bob index"));
|
|
5752
6847
|
console.log(MUTED16(" Index your project \u2014 generates summaries and dependency map for context."));
|
|
5753
6848
|
console.log("");
|
|
5754
|
-
console.log(
|
|
6849
|
+
console.log(chalk26.white(" Usage: ") + INFO16("bob <command> [arguments]"));
|
|
5755
6850
|
console.log("");
|
|
5756
|
-
console.log(
|
|
6851
|
+
console.log(chalk26.white(" Global options:"));
|
|
5757
6852
|
console.log(MUTED16(" -h, --help Print this usage information."));
|
|
5758
6853
|
console.log(MUTED16(" -V, --version Output the version number."));
|
|
5759
6854
|
console.log("");
|
|
5760
|
-
console.log(
|
|
6855
|
+
console.log(chalk26.white(" Available commands:"));
|
|
5761
6856
|
console.log("");
|
|
5762
6857
|
console.log(INFO16(" Conversation"));
|
|
5763
6858
|
printCmd("chat [message]", "Chat with Bob \u2014 code-friendly engineering partner");
|
|
@@ -5783,7 +6878,10 @@ function printCustomHelp() {
|
|
|
5783
6878
|
console.log(MODE_CONSULTANT9(" Profile & Identity"));
|
|
5784
6879
|
printCmd("profile", "View your behavioral DNA dashboard");
|
|
5785
6880
|
printCmd("profile --cloud", "Generate cloud-powered daily profile");
|
|
6881
|
+
printCmd("userbob", "Launch your UserBob digital twin");
|
|
5786
6882
|
printCmd("byok", "Manage Bring Your Own Key configuration");
|
|
6883
|
+
printCmd("command-center", "Autonomous Command Center \u2014 inspect and approve tasks");
|
|
6884
|
+
printCmd("cc", "Alias for command-center");
|
|
5787
6885
|
console.log("");
|
|
5788
6886
|
console.log(MUTED16(" Configuration"));
|
|
5789
6887
|
printCmd("config show", "Display current configuration");
|
|
@@ -5792,7 +6890,7 @@ function printCustomHelp() {
|
|
|
5792
6890
|
printCmd("logout", "Sign out and clear stored credentials");
|
|
5793
6891
|
printCmd("whoami", "Show current auth status and project info");
|
|
5794
6892
|
console.log("");
|
|
5795
|
-
console.log(
|
|
6893
|
+
console.log(chalk26.white(" Interactive slash commands ") + MUTED16("(inside a chat/consult session):"));
|
|
5796
6894
|
console.log("");
|
|
5797
6895
|
printCmd("/exit", "End the session");
|
|
5798
6896
|
printCmd("/new", "Start a fresh conversation");
|
|
@@ -5818,20 +6916,20 @@ function printCmd(cmd, desc) {
|
|
|
5818
6916
|
}
|
|
5819
6917
|
program.command("whoami").description("Show current authentication status and configuration").action(() => {
|
|
5820
6918
|
const config = getConfig();
|
|
5821
|
-
const projectName =
|
|
6919
|
+
const projectName = path14.basename(process.cwd());
|
|
5822
6920
|
console.log("");
|
|
5823
|
-
console.log(
|
|
5824
|
-
console.log(
|
|
5825
|
-
console.log(` ${
|
|
5826
|
-
console.log(` ${
|
|
5827
|
-
console.log(` ${
|
|
5828
|
-
console.log(` ${
|
|
5829
|
-
console.log(` ${
|
|
5830
|
-
console.log(` ${
|
|
5831
|
-
console.log(` ${
|
|
6921
|
+
console.log(chalk26.bold(" \u{1F916} Bob's CLI"));
|
|
6922
|
+
console.log(chalk26.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"));
|
|
6923
|
+
console.log(` ${chalk26.cyan("Status:")} ${config.loggedIn ? chalk26.green("Logged in as " + config.email) : "Not logged in"}`);
|
|
6924
|
+
console.log(` ${chalk26.cyan("Tier:")} ${config.tier === "platform" ? "Platform (Tier 3)" : "Local-first (Tier 1)"}`);
|
|
6925
|
+
console.log(` ${chalk26.cyan("Provider:")} ${config.provider || "Not configured"}`);
|
|
6926
|
+
console.log(` ${chalk26.cyan("Mode:")} ${config.personalizationMode ? "Personalized" : config.consultantMode ? "Consultant" : "Standard"}`);
|
|
6927
|
+
console.log(` ${chalk26.cyan("IDRP:")} ${config.idrp ? "Enabled" : "Disabled"}`);
|
|
6928
|
+
console.log(` ${chalk26.cyan("Project:")} ${projectName} (${process.cwd()})`);
|
|
6929
|
+
console.log(` ${chalk26.cyan("Session:")} ${config.conversationId ? config.conversationId.slice(0, 20) + "..." : "None"}`);
|
|
5832
6930
|
console.log("");
|
|
5833
6931
|
if (!config.loggedIn) {
|
|
5834
|
-
console.log(
|
|
6932
|
+
console.log(chalk26.gray(" Run `bob login` to authenticate."));
|
|
5835
6933
|
console.log("");
|
|
5836
6934
|
}
|
|
5837
6935
|
});
|
|
@@ -5850,23 +6948,25 @@ registerAutonomyCommand(program);
|
|
|
5850
6948
|
registerServeCommand(program);
|
|
5851
6949
|
registerRemoteCommand(program);
|
|
5852
6950
|
registerProfileCommand(program);
|
|
6951
|
+
registerUserBobCommand(program);
|
|
6952
|
+
registerCommandCenterCommand(program);
|
|
5853
6953
|
process.on("uncaughtException", (error) => {
|
|
5854
6954
|
console.error("");
|
|
5855
|
-
console.error(
|
|
5856
|
-
console.error(
|
|
6955
|
+
console.error(chalk26.hex("#EF5350")(" \u274C An unexpected error occurred."));
|
|
6956
|
+
console.error(chalk26.hex("#78909C")(` ${error.message || "Unknown error"}`));
|
|
5857
6957
|
console.error("");
|
|
5858
|
-
console.error(
|
|
5859
|
-
console.error(
|
|
6958
|
+
console.error(chalk26.hex("#78909C")(" If this persists, please report it:"));
|
|
6959
|
+
console.error(chalk26.hex("#26C6DA")(" https://github.com/bobsworkshop/bob-cli/issues"));
|
|
5860
6960
|
console.error("");
|
|
5861
6961
|
process.exit(1);
|
|
5862
6962
|
});
|
|
5863
6963
|
process.on("unhandledRejection", (reason) => {
|
|
5864
6964
|
console.error("");
|
|
5865
|
-
console.error(
|
|
5866
|
-
console.error(
|
|
6965
|
+
console.error(chalk26.hex("#EF5350")(" \u274C An unexpected error occurred."));
|
|
6966
|
+
console.error(chalk26.hex("#78909C")(` ${reason?.message || reason || "Unknown error"}`));
|
|
5867
6967
|
console.error("");
|
|
5868
|
-
console.error(
|
|
5869
|
-
console.error(
|
|
6968
|
+
console.error(chalk26.hex("#78909C")(" If this persists, please report it:"));
|
|
6969
|
+
console.error(chalk26.hex("#26C6DA")(" https://github.com/bobsworkshop/bob-cli/issues"));
|
|
5870
6970
|
console.error("");
|
|
5871
6971
|
process.exit(1);
|
|
5872
6972
|
});
|