@bobsworkshop/cli 0.1.0 → 0.1.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/dist/bin/{analyse-auto-WQUK5YPO.js → analyse-auto-KKWLMLHZ.js} +4 -5
- package/dist/bin/{analyse-results-FIDS4635.js → analyse-results-N5QLJNND.js} +2 -2
- package/dist/bin/bob.js +1 -0
- package/dist/bin/chunk-WEHSNZKO.js +880 -0
- package/package.json +11 -5
- package/bin/bob.ts +0 -74
- package/dist/bin/analyse-auto-AAWSETKY.js +0 -540
- package/dist/bin/analyse-auto-FQ62GYPV.js +0 -533
- package/dist/bin/analyse-auto-JAD24IQ5.js +0 -511
- package/dist/bin/analyse-auto-R4MZA7SX.js +0 -511
- package/dist/bin/analyse-auto-V3SH4MS7.js +0 -524
- package/dist/bin/analyse-auto-WVAY6467.js +0 -280
- package/dist/bin/analyse-results-3NSD6MAY.js +0 -363
- package/dist/bin/analyse-results-B7LONUXU.js +0 -265
- package/dist/bin/analyse-results-E6NBAVDB.js +0 -265
- package/dist/bin/analyse-results-LMGVKAUX.js +0 -342
- package/dist/bin/analyse-results-LSMLUEIB.js +0 -338
- package/dist/bin/analyse-results-MOCLBCD7.js +0 -326
- package/dist/bin/analyse-results-NLAEAOOP.js +0 -10
- package/dist/bin/analyse-results-PYQIKWYL.js +0 -9
- package/dist/bin/analyse-results-R3MG5H7G.js +0 -329
- package/dist/bin/analyse-results-UYZZSBHB.js +0 -9
- package/dist/bin/analyse-results-YYGHIK2Q.js +0 -9
- package/dist/bin/analysis-tracker-N5VANTLH.js +0 -12
- package/dist/bin/chunk-3RSDDQE2.js +0 -420
- package/dist/bin/chunk-6KWC4HDO.js +0 -97
- package/dist/bin/chunk-6W7WDF4Q.js +0 -589
- package/dist/bin/chunk-7CXM3RLM.js +0 -287
- package/dist/bin/chunk-FGYL6SWO.js +0 -465
- package/dist/bin/chunk-J4BSKFCW.js +0 -624
- package/dist/bin/chunk-KWOQFI6L.js +0 -287
- package/dist/bin/chunk-OOGLZ2QB.js +0 -322
- package/dist/bin/chunk-TEVQLSGD.js +0 -287
- package/dist/bin/chunk-VUS7R7SO.js +0 -479
|
@@ -1,420 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
callCloudFunction,
|
|
3
|
-
callLocalModel,
|
|
4
|
-
proposeAndWriteFile,
|
|
5
|
-
readFileContent
|
|
6
|
-
} from "./chunk-FGYL6SWO.js";
|
|
7
|
-
|
|
8
|
-
// src/commands/analyse-results.ts
|
|
9
|
-
import chalk from "chalk";
|
|
10
|
-
import inquirer from "inquirer";
|
|
11
|
-
import * as fs2 from "fs";
|
|
12
|
-
import * as path2 from "path";
|
|
13
|
-
|
|
14
|
-
// src/core/analysis-tracker.ts
|
|
15
|
-
import * as fs from "fs";
|
|
16
|
-
import * as path from "path";
|
|
17
|
-
var BOB_DIR = path.join(process.env.HOME || process.env.USERPROFILE || "", ".bob");
|
|
18
|
-
function getResultsDir() {
|
|
19
|
-
const projectName = path.basename(process.cwd());
|
|
20
|
-
return path.join(BOB_DIR, "projects", projectName, "analysis", "results");
|
|
21
|
-
}
|
|
22
|
-
function getAnalysisPath() {
|
|
23
|
-
return path.join(getResultsDir(), "analysis.json");
|
|
24
|
-
}
|
|
25
|
-
function getStatusLogPath() {
|
|
26
|
-
return path.join(getResultsDir(), "status-log.json");
|
|
27
|
-
}
|
|
28
|
-
function markSuggestionStatus(filePath, suggestionIndex, category, status, metadata) {
|
|
29
|
-
const analysisPath = getAnalysisPath();
|
|
30
|
-
const logPath = getStatusLogPath();
|
|
31
|
-
if (!fs.existsSync(analysisPath)) return;
|
|
32
|
-
const allResults = JSON.parse(fs.readFileSync(analysisPath, "utf-8"));
|
|
33
|
-
if (allResults[filePath] && allResults[filePath][category]) {
|
|
34
|
-
const items = allResults[filePath][category];
|
|
35
|
-
if (items[suggestionIndex]) {
|
|
36
|
-
items[suggestionIndex].status = status;
|
|
37
|
-
items[suggestionIndex].statusUpdatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
fs.writeFileSync(analysisPath, JSON.stringify(allResults, null, 2));
|
|
41
|
-
let log = [];
|
|
42
|
-
if (fs.existsSync(logPath)) {
|
|
43
|
-
try {
|
|
44
|
-
log = JSON.parse(fs.readFileSync(logPath, "utf-8"));
|
|
45
|
-
} catch {
|
|
46
|
-
log = [];
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
log.push({
|
|
50
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
51
|
-
filePath,
|
|
52
|
-
category,
|
|
53
|
-
suggestionIndex,
|
|
54
|
-
action: status,
|
|
55
|
-
confidence: metadata?.confidence || null,
|
|
56
|
-
reason: metadata?.reason || null,
|
|
57
|
-
implementedBy: metadata?.implementedBy || "minibob",
|
|
58
|
-
previousStatus: "pending"
|
|
59
|
-
});
|
|
60
|
-
fs.writeFileSync(logPath, JSON.stringify(log, null, 2));
|
|
61
|
-
}
|
|
62
|
-
function markSuggestionById(id, category, status, metadata) {
|
|
63
|
-
const analysisPath = getAnalysisPath();
|
|
64
|
-
if (!fs.existsSync(analysisPath)) return;
|
|
65
|
-
const allResults = JSON.parse(fs.readFileSync(analysisPath, "utf-8"));
|
|
66
|
-
for (const [filePath, fileResults] of Object.entries(allResults)) {
|
|
67
|
-
const items = fileResults[category];
|
|
68
|
-
if (!items) continue;
|
|
69
|
-
for (let i = 0; i < items.length; i++) {
|
|
70
|
-
const itemId = `${filePath.replace(/[\/\\]/g, "_")}_${i}`;
|
|
71
|
-
if (itemId === id) {
|
|
72
|
-
markSuggestionStatus(filePath, i, category, status, metadata);
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// src/commands/analyse-results.ts
|
|
80
|
-
var RED = chalk.hex("#EF5350");
|
|
81
|
-
var PURPLE = chalk.hex("#AB47BC");
|
|
82
|
-
var BLUE = chalk.hex("#42A5F5");
|
|
83
|
-
var TEAL = chalk.hex("#26A69A");
|
|
84
|
-
var AMBER = chalk.hex("#FFAB00");
|
|
85
|
-
var GRAY = chalk.gray;
|
|
86
|
-
var BORDER = chalk.hex("#455A64");
|
|
87
|
-
var PRIORITY_COLORS = {
|
|
88
|
-
"critical": chalk.bgHex("#B71C1C").white,
|
|
89
|
-
"high": chalk.hex("#FF6D00"),
|
|
90
|
-
"medium": chalk.hex("#FFA726"),
|
|
91
|
-
"low": chalk.hex("#66BB6A")
|
|
92
|
-
};
|
|
93
|
-
var CATEGORY_COLORS = {
|
|
94
|
-
"bugs": RED,
|
|
95
|
-
"features": PURPLE,
|
|
96
|
-
"improvements": BLUE,
|
|
97
|
-
"upgrades": TEAL
|
|
98
|
-
};
|
|
99
|
-
async function showInteractiveResults(config, category, sort, search) {
|
|
100
|
-
let allSuggestions = [];
|
|
101
|
-
if (config.tier === "platform" && config.provider !== "local" && config.loggedIn && config.conversationId) {
|
|
102
|
-
try {
|
|
103
|
-
const result = await callCloudFunction("getCLIAnalysisResults", {
|
|
104
|
-
conversationId: config.conversationId,
|
|
105
|
-
category,
|
|
106
|
-
sort: sort || "priority",
|
|
107
|
-
search: search || null
|
|
108
|
-
});
|
|
109
|
-
allSuggestions = result?.suggestions || [];
|
|
110
|
-
} catch (error) {
|
|
111
|
-
console.log(chalk.red(` \u274C ${error.message}`));
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
114
|
-
} else {
|
|
115
|
-
allSuggestions = loadLocalSuggestions(category);
|
|
116
|
-
}
|
|
117
|
-
if (search) {
|
|
118
|
-
const query = search.toLowerCase();
|
|
119
|
-
allSuggestions = allSuggestions.filter(
|
|
120
|
-
(s) => (s.description || "").toLowerCase().includes(query) || (s.title || "").toLowerCase().includes(query) || (s.filePath || "").toLowerCase().includes(query)
|
|
121
|
-
);
|
|
122
|
-
}
|
|
123
|
-
sortSuggestions(allSuggestions, sort || "priority");
|
|
124
|
-
if (allSuggestions.length === 0) {
|
|
125
|
-
console.log("");
|
|
126
|
-
console.log(chalk.green(" \u2705 No items found. Clean!"));
|
|
127
|
-
console.log("");
|
|
128
|
-
return;
|
|
129
|
-
}
|
|
130
|
-
const color = CATEGORY_COLORS[category] || GRAY;
|
|
131
|
-
let running = true;
|
|
132
|
-
let displaySuggestions = [...allSuggestions];
|
|
133
|
-
let currentSort = sort || "priority";
|
|
134
|
-
while (running) {
|
|
135
|
-
console.log("");
|
|
136
|
-
console.log(color(` \u25C6 ${category.toUpperCase()} (${displaySuggestions.length} items) | Sort: ${currentSort}`));
|
|
137
|
-
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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
138
|
-
console.log("");
|
|
139
|
-
const choices = [];
|
|
140
|
-
choices.push({
|
|
141
|
-
name: chalk.cyan(" \u{1F500} Toggle sort"),
|
|
142
|
-
value: "__sort__",
|
|
143
|
-
short: "Sort"
|
|
144
|
-
});
|
|
145
|
-
choices.push(new inquirer.Separator(GRAY(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")));
|
|
146
|
-
for (let idx = 0; idx < displaySuggestions.length; idx++) {
|
|
147
|
-
const item = displaySuggestions[idx];
|
|
148
|
-
const pColor = PRIORITY_COLORS[item.priority?.toLowerCase()] || GRAY;
|
|
149
|
-
const priorityLabel = (item.priority || "MEDIUM").toUpperCase().padEnd(9);
|
|
150
|
-
const filePath = (item.filePath || "unknown").split("/").pop() || "unknown";
|
|
151
|
-
const desc = (item.description || item.title || "No description").slice(0, 42);
|
|
152
|
-
const displayName = `${pColor(priorityLabel)} ${chalk.cyan(filePath.padEnd(18))} ${chalk.white(desc)}`;
|
|
153
|
-
choices.push({
|
|
154
|
-
name: displayName,
|
|
155
|
-
value: idx,
|
|
156
|
-
short: item.title || item.description?.slice(0, 30) || "Item",
|
|
157
|
-
description: `${item.priority} ${item.filePath} ${item.title} ${item.description}`
|
|
158
|
-
});
|
|
159
|
-
}
|
|
160
|
-
choices.push(new inquirer.Separator(GRAY(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")));
|
|
161
|
-
choices.push({
|
|
162
|
-
name: chalk.gray(" \u2190 Quit"),
|
|
163
|
-
value: "__quit__",
|
|
164
|
-
short: "Quit"
|
|
165
|
-
});
|
|
166
|
-
const { selected } = await inquirer.prompt([
|
|
167
|
-
{
|
|
168
|
-
type: "search",
|
|
169
|
-
name: "selected",
|
|
170
|
-
message: color(`Search ${category} (type to filter):`),
|
|
171
|
-
source: (input) => {
|
|
172
|
-
if (!input) return choices;
|
|
173
|
-
const query = input.toLowerCase();
|
|
174
|
-
const filtered = choices.filter((c) => {
|
|
175
|
-
if (c.type === "separator") return true;
|
|
176
|
-
if (c.value === "__sort__" || c.value === "__quit__") return true;
|
|
177
|
-
const searchable = c.description?.toLowerCase() || "";
|
|
178
|
-
return searchable.includes(query);
|
|
179
|
-
});
|
|
180
|
-
return filtered;
|
|
181
|
-
},
|
|
182
|
-
pageSize: 12
|
|
183
|
-
}
|
|
184
|
-
]);
|
|
185
|
-
if (selected === "__quit__") {
|
|
186
|
-
running = false;
|
|
187
|
-
break;
|
|
188
|
-
}
|
|
189
|
-
if (selected === "__sort__") {
|
|
190
|
-
currentSort = currentSort === "priority" ? "file" : "priority";
|
|
191
|
-
sortSuggestions(displaySuggestions, currentSort);
|
|
192
|
-
console.log(chalk.cyan(` Sort changed to: ${currentSort}`));
|
|
193
|
-
continue;
|
|
194
|
-
}
|
|
195
|
-
if (typeof selected === "number") {
|
|
196
|
-
const item = displaySuggestions[selected];
|
|
197
|
-
const action = await showExpandedView(item, category);
|
|
198
|
-
if (action === "implement") {
|
|
199
|
-
await handleImplement(item, config, category);
|
|
200
|
-
displaySuggestions.splice(selected, 1);
|
|
201
|
-
const originalIdx = allSuggestions.findIndex((s) => s.id === item.id);
|
|
202
|
-
if (originalIdx !== -1) allSuggestions.splice(originalIdx, 1);
|
|
203
|
-
} else if (action === "dismiss") {
|
|
204
|
-
if (item.id) {
|
|
205
|
-
markSuggestionById(item.id, category, "dismissed", {
|
|
206
|
-
reason: "User dismissed from CLI",
|
|
207
|
-
implementedBy: "user"
|
|
208
|
-
});
|
|
209
|
-
}
|
|
210
|
-
displaySuggestions.splice(selected, 1);
|
|
211
|
-
const originalIdx = allSuggestions.findIndex((s) => s.id === item.id);
|
|
212
|
-
if (originalIdx !== -1) allSuggestions.splice(originalIdx, 1);
|
|
213
|
-
console.log(chalk.gray(" \u23ED\uFE0F Dismissed and logged."));
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
async function showExpandedView(item, category) {
|
|
219
|
-
const color = CATEGORY_COLORS[category] || GRAY;
|
|
220
|
-
const pColor = PRIORITY_COLORS[item.priority?.toLowerCase()] || GRAY;
|
|
221
|
-
console.log("");
|
|
222
|
-
console.log(color(" \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"));
|
|
223
|
-
console.log(color(" \u2551 ") + pColor(`${(item.priority || "MEDIUM").toUpperCase()} ${category.toUpperCase().slice(0, -1)}`));
|
|
224
|
-
console.log(color(" \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"));
|
|
225
|
-
console.log(color(" \u2551") + chalk.gray(" File: ") + chalk.cyan(item.filePath || "unknown"));
|
|
226
|
-
console.log(color(" \u2551") + chalk.gray(" Priority: ") + pColor((item.priority || "medium").toUpperCase()));
|
|
227
|
-
console.log(color(" \u2551"));
|
|
228
|
-
console.log(color(" \u2551") + chalk.gray(" Title:"));
|
|
229
|
-
console.log(color(" \u2551") + chalk.white.bold(` ${item.title || "No title"}`));
|
|
230
|
-
console.log(color(" \u2551"));
|
|
231
|
-
console.log(color(" \u2551") + chalk.gray(" Description:"));
|
|
232
|
-
const descLines = wrapText(item.description || "No description", 54);
|
|
233
|
-
for (const line of descLines) {
|
|
234
|
-
console.log(color(" \u2551") + chalk.white(` ${line}`));
|
|
235
|
-
}
|
|
236
|
-
if (item.implementation) {
|
|
237
|
-
console.log(color(" \u2551"));
|
|
238
|
-
console.log(color(" \u2551") + chalk.gray(" Implementation:"));
|
|
239
|
-
const implLines = wrapText(item.implementation, 54);
|
|
240
|
-
for (const line of implLines) {
|
|
241
|
-
console.log(color(" \u2551") + chalk.white(` ${line}`));
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
console.log(color(" \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"));
|
|
245
|
-
console.log("");
|
|
246
|
-
const { action } = await inquirer.prompt([
|
|
247
|
-
{
|
|
248
|
-
type: "select",
|
|
249
|
-
name: "action",
|
|
250
|
-
message: "What do you want to do?",
|
|
251
|
-
choices: [
|
|
252
|
-
{ name: chalk.green(" \u{1F527} Implement this fix"), value: "implement" },
|
|
253
|
-
{ name: chalk.red(" \u{1F5D1}\uFE0F Dismiss"), value: "dismiss" },
|
|
254
|
-
{ name: chalk.gray(" \u2190 Back to list"), value: "back" }
|
|
255
|
-
]
|
|
256
|
-
}
|
|
257
|
-
]);
|
|
258
|
-
return action;
|
|
259
|
-
}
|
|
260
|
-
async function handleImplement(item, config, category) {
|
|
261
|
-
console.log("");
|
|
262
|
-
console.log(chalk.cyan(" \u{1F527} Implementing fix..."));
|
|
263
|
-
console.log("");
|
|
264
|
-
if (config.provider === "local" && config.localEndpoint) {
|
|
265
|
-
const fileContent = readFileContent(item.filePath);
|
|
266
|
-
if (!fileContent) {
|
|
267
|
-
console.log(chalk.red(` \u274C Could not read file: ${item.filePath}`));
|
|
268
|
-
return;
|
|
269
|
-
}
|
|
270
|
-
const prompt = `You are MiniBob \u2014 a junior engineer making SURGICAL code fixes under strict supervision.
|
|
271
|
-
|
|
272
|
-
CURRENT FILE: ${item.filePath}
|
|
273
|
-
${fileContent}
|
|
274
|
-
|
|
275
|
-
CHANGE TO IMPLEMENT:
|
|
276
|
-
Title: ${item.title}
|
|
277
|
-
Description: ${item.description}
|
|
278
|
-
Implementation Instructions: ${item.implementation || "Apply the fix described above."}
|
|
279
|
-
|
|
280
|
-
RULES (CRITICAL \u2014 VIOLATION = REJECTED):
|
|
281
|
-
- Return ONLY valid source code. No markdown, no code fences, no \`\`\`, no explanation text.
|
|
282
|
-
- Start the FIRST line with: // File: ${item.filePath}
|
|
283
|
-
- PRESERVE ALL existing imports exactly as they are. Do NOT add, remove, or reorder imports.
|
|
284
|
-
- PRESERVE ALL existing exports exactly as they are. Do NOT rename exported functions or classes.
|
|
285
|
-
- PRESERVE the existing code structure, indentation, patterns, and naming conventions.
|
|
286
|
-
- Make the MINIMUM change necessary to implement the fix. Touch NOTHING else.
|
|
287
|
-
- Do NOT refactor, reorganize, or "improve" unrelated code.
|
|
288
|
-
- Do NOT add comments explaining what you changed.
|
|
289
|
-
- Do NOT wrap the response in markdown code blocks.
|
|
290
|
-
- The output must be valid TypeScript/JavaScript that compiles without errors.
|
|
291
|
-
- If you are unsure about a change, return the file UNCHANGED rather than risk breaking it.
|
|
292
|
-
|
|
293
|
-
Return the complete file content now:`;
|
|
294
|
-
try {
|
|
295
|
-
const messages = [
|
|
296
|
-
{ role: "system", content: "You are MiniBob, a junior engineer making SURGICAL fixes. Return ONLY valid source code. NO markdown. NO code fences. NO explanation. Start with // File: comment. Make the ABSOLUTE MINIMUM change needed. Do NOT restructure, refactor, or touch ANYTHING beyond the specific fix. If unsure, return the file unchanged." },
|
|
297
|
-
{ role: "user", content: prompt }
|
|
298
|
-
];
|
|
299
|
-
const response = await callLocalModel(config.localEndpoint, messages);
|
|
300
|
-
const lines = response.split("\n");
|
|
301
|
-
const firstLine = lines[0].trim();
|
|
302
|
-
let newContent;
|
|
303
|
-
if (firstLine.match(/^\/\/\s*(File:)?\s*/)) {
|
|
304
|
-
newContent = lines.slice(1).join("\n").trim();
|
|
305
|
-
} else {
|
|
306
|
-
newContent = response.trim();
|
|
307
|
-
}
|
|
308
|
-
if (newContent.includes("```") || newContent.includes("## ") || newContent.startsWith("Here") || newContent.startsWith("I have") || newContent.startsWith("Sure")) {
|
|
309
|
-
console.log(chalk.yellow(" \u26A0\uFE0F MiniBob returned explanation instead of code. Fix rejected."));
|
|
310
|
-
return;
|
|
311
|
-
}
|
|
312
|
-
if (newContent.length < fileContent.length * 0.5) {
|
|
313
|
-
console.log(chalk.yellow(` \u26A0\uFE0F MiniBob's output is ${Math.round(newContent.length / fileContent.length * 100)}% of original size. Rejecting.`));
|
|
314
|
-
return;
|
|
315
|
-
}
|
|
316
|
-
const originalExports = fileContent.match(/export\s+(function|class|const|interface|type|async\s+function)\s+\w+/g) || [];
|
|
317
|
-
for (const exp of originalExports) {
|
|
318
|
-
const exportName = exp.split(/\s+/).pop();
|
|
319
|
-
if (!newContent.includes(exportName)) {
|
|
320
|
-
console.log(chalk.yellow(` \u26A0\uFE0F MiniBob removed export "${exportName}". Rejecting.`));
|
|
321
|
-
return;
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
await proposeAndWriteFile({
|
|
325
|
-
filePath: item.filePath,
|
|
326
|
-
content: newContent,
|
|
327
|
-
isNew: false
|
|
328
|
-
});
|
|
329
|
-
if (item.id) {
|
|
330
|
-
markSuggestionById(item.id, category, "implemented", {
|
|
331
|
-
reason: "User approved implementation from CLI",
|
|
332
|
-
implementedBy: "minibob"
|
|
333
|
-
});
|
|
334
|
-
}
|
|
335
|
-
} catch (error) {
|
|
336
|
-
console.log(chalk.red(` \u274C Implementation failed: ${error.message}`));
|
|
337
|
-
}
|
|
338
|
-
} else if (config.loggedIn && config.conversationId) {
|
|
339
|
-
try {
|
|
340
|
-
const result = await callCloudFunction("implementSuggestion", {
|
|
341
|
-
conversationId: config.conversationId,
|
|
342
|
-
filePath: item.filePath,
|
|
343
|
-
suggestionId: item.id || "unknown",
|
|
344
|
-
category,
|
|
345
|
-
jobId: `cli_impl_${Date.now()}`
|
|
346
|
-
});
|
|
347
|
-
if (result?.success) {
|
|
348
|
-
console.log(chalk.green(` \u2705 ${result.message}`));
|
|
349
|
-
if (item.id) {
|
|
350
|
-
markSuggestionById(item.id, category, "implemented", {
|
|
351
|
-
reason: "Platform implementation",
|
|
352
|
-
implementedBy: "platform"
|
|
353
|
-
});
|
|
354
|
-
}
|
|
355
|
-
} else {
|
|
356
|
-
console.log(chalk.red(" \u274C Implementation failed on platform."));
|
|
357
|
-
}
|
|
358
|
-
} catch (error) {
|
|
359
|
-
console.log(chalk.red(` \u274C ${error.message}`));
|
|
360
|
-
}
|
|
361
|
-
} else {
|
|
362
|
-
console.log(chalk.red(" \u274C No provider configured for implementation."));
|
|
363
|
-
}
|
|
364
|
-
console.log("");
|
|
365
|
-
}
|
|
366
|
-
function sortSuggestions(suggestions, method) {
|
|
367
|
-
if (method === "file") {
|
|
368
|
-
suggestions.sort((a, b) => (a.filePath || "").localeCompare(b.filePath || ""));
|
|
369
|
-
} else {
|
|
370
|
-
const priorityMap = { "critical": 0, "high": 1, "medium": 2, "low": 3 };
|
|
371
|
-
suggestions.sort((a, b) => {
|
|
372
|
-
const pA = priorityMap[a.priority?.toLowerCase()] ?? 99;
|
|
373
|
-
const pB = priorityMap[b.priority?.toLowerCase()] ?? 99;
|
|
374
|
-
return pA - pB;
|
|
375
|
-
});
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
function loadLocalSuggestions(category) {
|
|
379
|
-
const cwd = process.cwd();
|
|
380
|
-
const projectName = path2.basename(cwd);
|
|
381
|
-
const homeDir = process.env.HOME || process.env.USERPROFILE || "";
|
|
382
|
-
const analysisPath = path2.join(homeDir, ".bob", "projects", projectName, "analysis", "results", "analysis.json");
|
|
383
|
-
if (!fs2.existsSync(analysisPath)) return [];
|
|
384
|
-
const allResults = JSON.parse(fs2.readFileSync(analysisPath, "utf-8"));
|
|
385
|
-
const suggestions = [];
|
|
386
|
-
for (const [filePath, fileResults] of Object.entries(allResults)) {
|
|
387
|
-
const items = fileResults[category] || [];
|
|
388
|
-
items.forEach((item, idx) => {
|
|
389
|
-
if (!item.status || item.status === "pending") {
|
|
390
|
-
suggestions.push({
|
|
391
|
-
...item,
|
|
392
|
-
filePath,
|
|
393
|
-
id: `${filePath.replace(/[\/\\]/g, "_")}_${idx}`
|
|
394
|
-
});
|
|
395
|
-
}
|
|
396
|
-
});
|
|
397
|
-
}
|
|
398
|
-
return suggestions;
|
|
399
|
-
}
|
|
400
|
-
function wrapText(text, maxWidth) {
|
|
401
|
-
const words = text.split(" ");
|
|
402
|
-
const lines = [];
|
|
403
|
-
let currentLine = "";
|
|
404
|
-
for (const word of words) {
|
|
405
|
-
if (currentLine.length + word.length + 1 > maxWidth) {
|
|
406
|
-
lines.push(currentLine);
|
|
407
|
-
currentLine = word;
|
|
408
|
-
} else {
|
|
409
|
-
currentLine += (currentLine ? " " : "") + word;
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
if (currentLine) lines.push(currentLine);
|
|
413
|
-
return lines;
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
export {
|
|
417
|
-
markSuggestionStatus,
|
|
418
|
-
showInteractiveResults,
|
|
419
|
-
loadLocalSuggestions
|
|
420
|
-
};
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
// src/core/analysis-tracker.ts
|
|
2
|
-
import * as fs from "fs";
|
|
3
|
-
import * as path from "path";
|
|
4
|
-
var BOB_DIR = path.join(process.env.HOME || process.env.USERPROFILE || "", ".bob");
|
|
5
|
-
function getResultsDir() {
|
|
6
|
-
const projectName = path.basename(process.cwd());
|
|
7
|
-
return path.join(BOB_DIR, "projects", projectName, "analysis", "results");
|
|
8
|
-
}
|
|
9
|
-
function getAnalysisPath() {
|
|
10
|
-
return path.join(getResultsDir(), "analysis.json");
|
|
11
|
-
}
|
|
12
|
-
function getStatusLogPath() {
|
|
13
|
-
return path.join(getResultsDir(), "status-log.json");
|
|
14
|
-
}
|
|
15
|
-
function markSuggestionStatus(filePath, suggestionIndex, category, status, metadata) {
|
|
16
|
-
const analysisPath = getAnalysisPath();
|
|
17
|
-
const logPath = getStatusLogPath();
|
|
18
|
-
if (!fs.existsSync(analysisPath)) return;
|
|
19
|
-
const allResults = JSON.parse(fs.readFileSync(analysisPath, "utf-8"));
|
|
20
|
-
if (allResults[filePath] && allResults[filePath][category]) {
|
|
21
|
-
const items = allResults[filePath][category];
|
|
22
|
-
if (items[suggestionIndex]) {
|
|
23
|
-
items[suggestionIndex].status = status;
|
|
24
|
-
items[suggestionIndex].statusUpdatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
fs.writeFileSync(analysisPath, JSON.stringify(allResults, null, 2));
|
|
28
|
-
let log = [];
|
|
29
|
-
if (fs.existsSync(logPath)) {
|
|
30
|
-
try {
|
|
31
|
-
log = JSON.parse(fs.readFileSync(logPath, "utf-8"));
|
|
32
|
-
} catch {
|
|
33
|
-
log = [];
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
log.push({
|
|
37
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
38
|
-
filePath,
|
|
39
|
-
category,
|
|
40
|
-
suggestionIndex,
|
|
41
|
-
action: status,
|
|
42
|
-
confidence: metadata?.confidence || null,
|
|
43
|
-
reason: metadata?.reason || null,
|
|
44
|
-
implementedBy: metadata?.implementedBy || "minibob",
|
|
45
|
-
previousStatus: "pending"
|
|
46
|
-
});
|
|
47
|
-
fs.writeFileSync(logPath, JSON.stringify(log, null, 2));
|
|
48
|
-
}
|
|
49
|
-
function markSuggestionById(id, category, status, metadata) {
|
|
50
|
-
const analysisPath = getAnalysisPath();
|
|
51
|
-
if (!fs.existsSync(analysisPath)) return;
|
|
52
|
-
const allResults = JSON.parse(fs.readFileSync(analysisPath, "utf-8"));
|
|
53
|
-
for (const [filePath, fileResults] of Object.entries(allResults)) {
|
|
54
|
-
const items = fileResults[category];
|
|
55
|
-
if (!items) continue;
|
|
56
|
-
for (let i = 0; i < items.length; i++) {
|
|
57
|
-
const itemId = `${filePath.replace(/[\/\\]/g, "_")}_${i}`;
|
|
58
|
-
if (itemId === id) {
|
|
59
|
-
markSuggestionStatus(filePath, i, category, status, metadata);
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
function getStatusLog() {
|
|
66
|
-
const logPath = getStatusLogPath();
|
|
67
|
-
if (!fs.existsSync(logPath)) return [];
|
|
68
|
-
try {
|
|
69
|
-
return JSON.parse(fs.readFileSync(logPath, "utf-8"));
|
|
70
|
-
} catch {
|
|
71
|
-
return [];
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
function getStatusSummary() {
|
|
75
|
-
const analysisPath = getAnalysisPath();
|
|
76
|
-
if (!fs.existsSync(analysisPath)) return { implemented: 0, dismissed: 0, pending: 0 };
|
|
77
|
-
const allResults = JSON.parse(fs.readFileSync(analysisPath, "utf-8"));
|
|
78
|
-
let implemented = 0, dismissed = 0, pending = 0;
|
|
79
|
-
for (const fileResults of Object.values(allResults)) {
|
|
80
|
-
for (const category of ["bugs", "features", "improvements", "upgrades"]) {
|
|
81
|
-
const items = fileResults[category] || [];
|
|
82
|
-
for (const item of items) {
|
|
83
|
-
if (item.status === "implemented") implemented++;
|
|
84
|
-
else if (item.status === "dismissed") dismissed++;
|
|
85
|
-
else pending++;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
return { implemented, dismissed, pending };
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
export {
|
|
93
|
-
markSuggestionStatus,
|
|
94
|
-
markSuggestionById,
|
|
95
|
-
getStatusLog,
|
|
96
|
-
getStatusSummary
|
|
97
|
-
};
|