@braingrid/cli 0.2.52 → 0.2.54
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/CHANGELOG.md +49 -0
- package/dist/{chunk-S3D6P3QC.js → chunk-NEDKD2DE.js} +7 -1
- package/dist/{chunk-S3D6P3QC.js.map → chunk-NEDKD2DE.js.map} +1 -1
- package/dist/cli.js +1021 -521
- package/dist/cli.js.map +1 -1
- package/dist/{gh-installer-DWTEK33P.js → gh-installer-T7E7SYVI.js} +2 -2
- package/package.json +1 -1
- /package/dist/{gh-installer-DWTEK33P.js.map → gh-installer-T7E7SYVI.js.map} +0 -0
package/dist/cli.js
CHANGED
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
isGhInstalled,
|
|
12
12
|
isHomebrewInstalled,
|
|
13
13
|
isWingetAvailable
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-NEDKD2DE.js";
|
|
15
15
|
|
|
16
16
|
// src/cli.ts
|
|
17
17
|
import { existsSync } from "fs";
|
|
@@ -228,7 +228,7 @@ async function axiosWithRetry(config2, options) {
|
|
|
228
228
|
|
|
229
229
|
// src/build-config.ts
|
|
230
230
|
var BUILD_ENV = true ? "production" : process.env.NODE_ENV === "test" ? "development" : "production";
|
|
231
|
-
var CLI_VERSION = true ? "0.2.
|
|
231
|
+
var CLI_VERSION = true ? "0.2.54" : "0.0.0-test";
|
|
232
232
|
var PRODUCTION_CONFIG = {
|
|
233
233
|
apiUrl: "https://app.braingrid.ai",
|
|
234
234
|
workosAuthUrl: "https://auth.braingrid.ai",
|
|
@@ -312,42 +312,6 @@ function getConfig() {
|
|
|
312
312
|
};
|
|
313
313
|
}
|
|
314
314
|
|
|
315
|
-
// src/utils/jwt.ts
|
|
316
|
-
function decodeJWT(token) {
|
|
317
|
-
try {
|
|
318
|
-
const parts = token.split(".");
|
|
319
|
-
if (parts.length !== 3) {
|
|
320
|
-
return null;
|
|
321
|
-
}
|
|
322
|
-
const payload = parts[1];
|
|
323
|
-
const base64 = payload.replace(/-/g, "+").replace(/_/g, "/");
|
|
324
|
-
const padded = base64 + "==".substring(0, (4 - base64.length % 4) % 4);
|
|
325
|
-
const decoded = Buffer.from(padded, "base64").toString("utf-8");
|
|
326
|
-
return JSON.parse(decoded);
|
|
327
|
-
} catch {
|
|
328
|
-
return null;
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
function isJWTExpired(token) {
|
|
332
|
-
const payload = decodeJWT(token);
|
|
333
|
-
if (!payload || !payload.exp) {
|
|
334
|
-
return true;
|
|
335
|
-
}
|
|
336
|
-
return Date.now() >= payload.exp * 1e3;
|
|
337
|
-
}
|
|
338
|
-
function extractUserFromJWT(payload) {
|
|
339
|
-
return {
|
|
340
|
-
id: payload.sub || "",
|
|
341
|
-
email: payload.email || "",
|
|
342
|
-
// Try different naming conventions for first name
|
|
343
|
-
firstName: payload.firstName || payload.first_name || payload.given_name || (payload.name ? payload.name.split(" ")[0] : ""),
|
|
344
|
-
// Try different naming conventions for last name
|
|
345
|
-
lastName: payload.lastName || payload.last_name || payload.family_name || (payload.name ? payload.name.split(" ").slice(1).join(" ") : ""),
|
|
346
|
-
// Try different naming conventions for organization
|
|
347
|
-
organizationId: payload.organizationId || payload.organization_id || payload.org_id || payload.org || "default"
|
|
348
|
-
};
|
|
349
|
-
}
|
|
350
|
-
|
|
351
315
|
// src/utils/logger.ts
|
|
352
316
|
import fs from "fs";
|
|
353
317
|
import os from "os";
|
|
@@ -356,7 +320,7 @@ var Logger = class {
|
|
|
356
320
|
constructor(config2 = {}) {
|
|
357
321
|
const isDebugMode = process.env.DEBUG === "true";
|
|
358
322
|
this.config = {
|
|
359
|
-
logToFile:
|
|
323
|
+
logToFile: true,
|
|
360
324
|
logLevel: isDebugMode ? "debug" : "info",
|
|
361
325
|
// Set log level to debug when DEBUG=true
|
|
362
326
|
maxFileSize: 10 * 1024 * 1024,
|
|
@@ -368,7 +332,11 @@ var Logger = class {
|
|
|
368
332
|
};
|
|
369
333
|
this.logFilePath = this.config.logFilePath || path.join(os.homedir(), ".braingrid", "logs", "braingrid-cli.log");
|
|
370
334
|
if (this.config.logToFile) {
|
|
371
|
-
|
|
335
|
+
try {
|
|
336
|
+
this.ensureLogDirectory();
|
|
337
|
+
} catch {
|
|
338
|
+
this.config.logToFile = false;
|
|
339
|
+
}
|
|
372
340
|
}
|
|
373
341
|
}
|
|
374
342
|
ensureLogDirectory() {
|
|
@@ -385,8 +353,9 @@ var Logger = class {
|
|
|
385
353
|
const timestamp = entry.timestamp.toISOString();
|
|
386
354
|
const level = entry.level.toUpperCase().padEnd(5);
|
|
387
355
|
const type = entry.type.toUpperCase().padEnd(7);
|
|
356
|
+
const sessionStr = this.config.sessionId ? ` [${this.config.sessionId}]` : "";
|
|
388
357
|
const contextStr = entry.context ? ` | ${JSON.stringify(entry.context)}` : "";
|
|
389
|
-
return `[${timestamp}] ${level} [${type}] ${entry.message}${contextStr}`;
|
|
358
|
+
return `[${timestamp}] ${level}${sessionStr} [${type}] ${entry.message}${contextStr}`;
|
|
390
359
|
}
|
|
391
360
|
writeToFile(entry) {
|
|
392
361
|
if (!this.config.logToFile) return;
|
|
@@ -508,6 +477,45 @@ function getLogger(config2) {
|
|
|
508
477
|
return loggerInstance;
|
|
509
478
|
}
|
|
510
479
|
|
|
480
|
+
// src/utils/jwt.ts
|
|
481
|
+
function decodeJWT(token) {
|
|
482
|
+
try {
|
|
483
|
+
const parts = token.split(".");
|
|
484
|
+
if (parts.length !== 3) {
|
|
485
|
+
return null;
|
|
486
|
+
}
|
|
487
|
+
const payload = parts[1];
|
|
488
|
+
const base64 = payload.replace(/-/g, "+").replace(/_/g, "/");
|
|
489
|
+
const padded = base64 + "==".substring(0, (4 - base64.length % 4) % 4);
|
|
490
|
+
const decoded = Buffer.from(padded, "base64").toString("utf-8");
|
|
491
|
+
return JSON.parse(decoded);
|
|
492
|
+
} catch (error) {
|
|
493
|
+
getLogger().debug("[JWT] Failed to decode token", {
|
|
494
|
+
error: error instanceof Error ? error.message : String(error)
|
|
495
|
+
});
|
|
496
|
+
return null;
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
function isJWTExpired(token) {
|
|
500
|
+
const payload = decodeJWT(token);
|
|
501
|
+
if (!payload || !payload.exp) {
|
|
502
|
+
return true;
|
|
503
|
+
}
|
|
504
|
+
return Date.now() >= payload.exp * 1e3;
|
|
505
|
+
}
|
|
506
|
+
function extractUserFromJWT(payload) {
|
|
507
|
+
return {
|
|
508
|
+
id: payload.sub || "",
|
|
509
|
+
email: payload.email || "",
|
|
510
|
+
// Try different naming conventions for first name
|
|
511
|
+
firstName: payload.firstName || payload.first_name || payload.given_name || (payload.name ? payload.name.split(" ")[0] : ""),
|
|
512
|
+
// Try different naming conventions for last name
|
|
513
|
+
lastName: payload.lastName || payload.last_name || payload.family_name || (payload.name ? payload.name.split(" ").slice(1).join(" ") : ""),
|
|
514
|
+
// Try different naming conventions for organization
|
|
515
|
+
organizationId: payload.organizationId || payload.organization_id || payload.org_id || payload.org || "default"
|
|
516
|
+
};
|
|
517
|
+
}
|
|
518
|
+
|
|
511
519
|
// src/services/credential-store.ts
|
|
512
520
|
import crypto from "crypto";
|
|
513
521
|
import Conf from "conf";
|
|
@@ -1164,11 +1172,17 @@ var BraingridAuth = class {
|
|
|
1164
1172
|
}
|
|
1165
1173
|
}
|
|
1166
1174
|
return true;
|
|
1167
|
-
} catch {
|
|
1175
|
+
} catch (error) {
|
|
1176
|
+
this.logger.debug("[AUTH] isAuthenticated error", {
|
|
1177
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1178
|
+
});
|
|
1168
1179
|
try {
|
|
1169
1180
|
const session = await this.getStoredSession();
|
|
1170
1181
|
return session !== null && !this.isSessionExpired(session);
|
|
1171
|
-
} catch {
|
|
1182
|
+
} catch (error2) {
|
|
1183
|
+
this.logger.debug("[AUTH] isAuthenticated fallback failed", {
|
|
1184
|
+
error: error2 instanceof Error ? error2.message : String(error2)
|
|
1185
|
+
});
|
|
1172
1186
|
return false;
|
|
1173
1187
|
}
|
|
1174
1188
|
}
|
|
@@ -1204,7 +1218,10 @@ var BraingridAuth = class {
|
|
|
1204
1218
|
session.user = user;
|
|
1205
1219
|
await this.storeSession(session);
|
|
1206
1220
|
return user;
|
|
1207
|
-
} catch {
|
|
1221
|
+
} catch (error) {
|
|
1222
|
+
this.logger.debug("[AUTH] getCurrentUser failed", {
|
|
1223
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1224
|
+
});
|
|
1208
1225
|
return null;
|
|
1209
1226
|
}
|
|
1210
1227
|
}
|
|
@@ -1212,7 +1229,10 @@ var BraingridAuth = class {
|
|
|
1212
1229
|
try {
|
|
1213
1230
|
const session = await this.getStoredSession();
|
|
1214
1231
|
return session !== null && !this.isSessionExpired(session);
|
|
1215
|
-
} catch {
|
|
1232
|
+
} catch (error) {
|
|
1233
|
+
this.logger.debug("[AUTH] hasStoredSession failed", {
|
|
1234
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1235
|
+
});
|
|
1216
1236
|
return false;
|
|
1217
1237
|
}
|
|
1218
1238
|
}
|
|
@@ -1233,7 +1253,10 @@ var BraingridAuth = class {
|
|
|
1233
1253
|
updated_at: new Date(session.updated_at),
|
|
1234
1254
|
login_time: session.login_time ? new Date(session.login_time) : void 0
|
|
1235
1255
|
};
|
|
1236
|
-
} catch {
|
|
1256
|
+
} catch (error) {
|
|
1257
|
+
this.logger.debug("[AUTH] Failed to parse stored session", {
|
|
1258
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1259
|
+
});
|
|
1237
1260
|
return null;
|
|
1238
1261
|
}
|
|
1239
1262
|
}
|
|
@@ -1576,7 +1599,10 @@ var BraingridAuth = class {
|
|
|
1576
1599
|
async getStoredGitHubToken() {
|
|
1577
1600
|
try {
|
|
1578
1601
|
return await credentialStore.getPassword(KEYCHAIN_SERVICE, GITHUB_KEYCHAIN_ACCOUNT);
|
|
1579
|
-
} catch {
|
|
1602
|
+
} catch (error) {
|
|
1603
|
+
this.logger.debug("[AUTH] Failed to retrieve GitHub token", {
|
|
1604
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1605
|
+
});
|
|
1580
1606
|
return null;
|
|
1581
1607
|
}
|
|
1582
1608
|
}
|
|
@@ -1613,7 +1639,10 @@ var BraingridAuth = class {
|
|
|
1613
1639
|
user,
|
|
1614
1640
|
scopes
|
|
1615
1641
|
};
|
|
1616
|
-
} catch {
|
|
1642
|
+
} catch (error) {
|
|
1643
|
+
this.logger.debug("[AUTH] GitHub token validation failed", {
|
|
1644
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1645
|
+
});
|
|
1617
1646
|
return { valid: false };
|
|
1618
1647
|
}
|
|
1619
1648
|
}
|
|
@@ -1822,9 +1851,25 @@ function formatError(error, context) {
|
|
|
1822
1851
|
return formatAxiosError(error, context);
|
|
1823
1852
|
}
|
|
1824
1853
|
if (error instanceof Error) {
|
|
1825
|
-
|
|
1854
|
+
const logger4 = getLogger();
|
|
1855
|
+
if (error.stack) {
|
|
1856
|
+
logger4.debug("[ERROR] Stack trace", { stack: error.stack });
|
|
1857
|
+
}
|
|
1858
|
+
const prefix2 = context ? `Error ${context}: ` : "";
|
|
1859
|
+
return chalk2.red(`\u274C ${prefix2}${error.message}`);
|
|
1860
|
+
}
|
|
1861
|
+
if (error && typeof error === "object") {
|
|
1862
|
+
try {
|
|
1863
|
+
const serialized = JSON.stringify(error);
|
|
1864
|
+
if (serialized !== "{}") {
|
|
1865
|
+
const prefix2 = context ? `Error ${context}: ` : "";
|
|
1866
|
+
return chalk2.red(`\u274C ${prefix2}${serialized}`);
|
|
1867
|
+
}
|
|
1868
|
+
} catch {
|
|
1869
|
+
}
|
|
1826
1870
|
}
|
|
1827
|
-
|
|
1871
|
+
const prefix = context ? `Error ${context}: ` : "";
|
|
1872
|
+
return chalk2.red(`\u274C ${prefix}${String(error)}`);
|
|
1828
1873
|
}
|
|
1829
1874
|
function isAxiosError(error) {
|
|
1830
1875
|
return typeof error === "object" && error !== null && "isAxiosError" in error && error.isAxiosError === true;
|
|
@@ -2331,7 +2376,7 @@ async function handleCompletion(shellArg, opts) {
|
|
|
2331
2376
|
// src/handlers/init.handlers.ts
|
|
2332
2377
|
import { access as access3 } from "fs/promises";
|
|
2333
2378
|
import { confirm as confirm2, input, select as select3 } from "@inquirer/prompts";
|
|
2334
|
-
import
|
|
2379
|
+
import chalk12 from "chalk";
|
|
2335
2380
|
|
|
2336
2381
|
// src/utils/axios-with-auth.ts
|
|
2337
2382
|
import axios4 from "axios";
|
|
@@ -2629,9 +2674,56 @@ var ProjectService = class {
|
|
|
2629
2674
|
};
|
|
2630
2675
|
|
|
2631
2676
|
// src/services/setup-service.ts
|
|
2632
|
-
import * as
|
|
2633
|
-
import * as
|
|
2677
|
+
import * as fs3 from "fs/promises";
|
|
2678
|
+
import * as path3 from "path";
|
|
2634
2679
|
import axios5 from "axios";
|
|
2680
|
+
|
|
2681
|
+
// src/utils/cli-logger.ts
|
|
2682
|
+
import * as fs2 from "fs";
|
|
2683
|
+
import * as path2 from "path";
|
|
2684
|
+
var logFile = null;
|
|
2685
|
+
var logStream = null;
|
|
2686
|
+
function initLogger(logPath) {
|
|
2687
|
+
if (logStream) {
|
|
2688
|
+
logStream.end();
|
|
2689
|
+
}
|
|
2690
|
+
const dir = path2.dirname(logPath);
|
|
2691
|
+
fs2.mkdirSync(dir, { recursive: true });
|
|
2692
|
+
logFile = logPath;
|
|
2693
|
+
logStream = fs2.createWriteStream(logPath, { flags: "a" });
|
|
2694
|
+
}
|
|
2695
|
+
function log(level, component, event, details = "") {
|
|
2696
|
+
if (!logStream) return;
|
|
2697
|
+
const now = /* @__PURE__ */ new Date();
|
|
2698
|
+
const ts = now.toLocaleTimeString("en-US", { hour12: false });
|
|
2699
|
+
const line = `${ts} [${level.padEnd(5)}] ${component} | ${event} | ${details}
|
|
2700
|
+
`;
|
|
2701
|
+
logStream.write(line);
|
|
2702
|
+
}
|
|
2703
|
+
function closeLogger() {
|
|
2704
|
+
logStream?.end();
|
|
2705
|
+
logStream = null;
|
|
2706
|
+
logFile = null;
|
|
2707
|
+
}
|
|
2708
|
+
function flushLogger() {
|
|
2709
|
+
return new Promise((resolve2) => {
|
|
2710
|
+
if (!logStream) {
|
|
2711
|
+
resolve2();
|
|
2712
|
+
return;
|
|
2713
|
+
}
|
|
2714
|
+
if (logStream.writableLength === 0) {
|
|
2715
|
+
resolve2();
|
|
2716
|
+
return;
|
|
2717
|
+
}
|
|
2718
|
+
const timeout = setTimeout(resolve2, 1e3);
|
|
2719
|
+
logStream.once("drain", () => {
|
|
2720
|
+
clearTimeout(timeout);
|
|
2721
|
+
resolve2();
|
|
2722
|
+
});
|
|
2723
|
+
});
|
|
2724
|
+
}
|
|
2725
|
+
|
|
2726
|
+
// src/services/setup-service.ts
|
|
2635
2727
|
var GITHUB_OWNER = "BrainGridAI";
|
|
2636
2728
|
var GITHUB_REPO = "braingrid";
|
|
2637
2729
|
var GITHUB_API_BASE = `https://api.github.com/repos/${GITHUB_OWNER}/${GITHUB_REPO}/contents`;
|
|
@@ -2664,6 +2756,12 @@ async function githubGet(url) {
|
|
|
2664
2756
|
return await axios5.get(url, { headers });
|
|
2665
2757
|
} catch (error) {
|
|
2666
2758
|
if (axios5.isAxiosError(error) && error.response?.status === 401 && cachedHeaders?.Authorization) {
|
|
2759
|
+
log(
|
|
2760
|
+
"WARN",
|
|
2761
|
+
"github-api",
|
|
2762
|
+
"auth_fallback",
|
|
2763
|
+
"dropping expired token, retrying unauthenticated"
|
|
2764
|
+
);
|
|
2667
2765
|
delete cachedHeaders.Authorization;
|
|
2668
2766
|
return axios5.get(url, { headers: cachedHeaders });
|
|
2669
2767
|
}
|
|
@@ -2683,6 +2781,7 @@ async function withRetry(fn, retries = MAX_RETRIES, delay = INITIAL_RETRY_DELAY)
|
|
|
2683
2781
|
if (!isNetworkError) {
|
|
2684
2782
|
throw error;
|
|
2685
2783
|
}
|
|
2784
|
+
log("WARN", "github-api", "retry", `attempt=${MAX_RETRIES - retries + 1} delay=${delay}ms`);
|
|
2686
2785
|
await new Promise((resolve2) => setTimeout(resolve2, delay));
|
|
2687
2786
|
return withRetry(fn, retries - 1, delay * 2);
|
|
2688
2787
|
}
|
|
@@ -2712,6 +2811,7 @@ function parseGitHubError(error) {
|
|
|
2712
2811
|
}
|
|
2713
2812
|
async function fetchFileFromGitHub(filePath) {
|
|
2714
2813
|
return withRetry(async () => {
|
|
2814
|
+
log("INFO", "github-api", "fetch", `path=${filePath}`);
|
|
2715
2815
|
try {
|
|
2716
2816
|
const { data: response } = await githubGet(
|
|
2717
2817
|
`${GITHUB_API_BASE}/${filePath}`
|
|
@@ -2768,9 +2868,9 @@ async function listGitHubDirectory(dirPath) {
|
|
|
2768
2868
|
async function copyFileFromGitHub(sourcePath, targetPath) {
|
|
2769
2869
|
try {
|
|
2770
2870
|
const content = await fetchFileFromGitHub(sourcePath);
|
|
2771
|
-
const parentDir =
|
|
2772
|
-
await
|
|
2773
|
-
await
|
|
2871
|
+
const parentDir = path3.dirname(targetPath);
|
|
2872
|
+
await fs3.mkdir(parentDir, { recursive: true });
|
|
2873
|
+
await fs3.writeFile(targetPath, content, { encoding: "utf8", mode: 420 });
|
|
2774
2874
|
} catch (error) {
|
|
2775
2875
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2776
2876
|
throw new Error(`Failed to copy file ${sourcePath} to ${targetPath}: ${errorMessage}`);
|
|
@@ -2781,7 +2881,7 @@ async function injectContentIntoFile(targetPath, content) {
|
|
|
2781
2881
|
let fileContent;
|
|
2782
2882
|
let fileExists3 = false;
|
|
2783
2883
|
try {
|
|
2784
|
-
fileContent = await
|
|
2884
|
+
fileContent = await fs3.readFile(targetPath, "utf8");
|
|
2785
2885
|
fileExists3 = true;
|
|
2786
2886
|
} catch {
|
|
2787
2887
|
fileContent = "";
|
|
@@ -2803,7 +2903,7 @@ async function injectContentIntoFile(targetPath, content) {
|
|
|
2803
2903
|
const newContent = `${before}${BEGIN_MARKER}
|
|
2804
2904
|
${cleanContent}
|
|
2805
2905
|
${END_MARKER}${after}`;
|
|
2806
|
-
await
|
|
2906
|
+
await fs3.writeFile(targetPath, newContent, { encoding: "utf8" });
|
|
2807
2907
|
} else {
|
|
2808
2908
|
const newContent = `${fileContent}
|
|
2809
2909
|
|
|
@@ -2811,16 +2911,16 @@ ${BEGIN_MARKER}
|
|
|
2811
2911
|
${cleanContent}
|
|
2812
2912
|
${END_MARKER}
|
|
2813
2913
|
`;
|
|
2814
|
-
await
|
|
2914
|
+
await fs3.writeFile(targetPath, newContent, { encoding: "utf8" });
|
|
2815
2915
|
}
|
|
2816
2916
|
} else {
|
|
2817
|
-
const parentDir =
|
|
2818
|
-
await
|
|
2917
|
+
const parentDir = path3.dirname(targetPath);
|
|
2918
|
+
await fs3.mkdir(parentDir, { recursive: true });
|
|
2819
2919
|
const newContent = `${BEGIN_MARKER}
|
|
2820
2920
|
${cleanContent}
|
|
2821
2921
|
${END_MARKER}
|
|
2822
2922
|
`;
|
|
2823
|
-
await
|
|
2923
|
+
await fs3.writeFile(targetPath, newContent, { encoding: "utf8", mode: 420 });
|
|
2824
2924
|
}
|
|
2825
2925
|
} catch (error) {
|
|
2826
2926
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
@@ -2829,9 +2929,9 @@ ${END_MARKER}
|
|
|
2829
2929
|
}
|
|
2830
2930
|
async function installStatusLineScript(scriptContent, targetPath = ".claude/statusline.sh") {
|
|
2831
2931
|
try {
|
|
2832
|
-
const parentDir =
|
|
2833
|
-
await
|
|
2834
|
-
await
|
|
2932
|
+
const parentDir = path3.dirname(targetPath);
|
|
2933
|
+
await fs3.mkdir(parentDir, { recursive: true });
|
|
2934
|
+
await fs3.writeFile(targetPath, scriptContent, { encoding: "utf8", mode: 493 });
|
|
2835
2935
|
} catch (error) {
|
|
2836
2936
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2837
2937
|
throw new Error(`Failed to install status line script to ${targetPath}: ${errorMessage}`);
|
|
@@ -2839,9 +2939,9 @@ async function installStatusLineScript(scriptContent, targetPath = ".claude/stat
|
|
|
2839
2939
|
}
|
|
2840
2940
|
async function installHookScript(scriptContent, targetPath) {
|
|
2841
2941
|
try {
|
|
2842
|
-
const parentDir =
|
|
2843
|
-
await
|
|
2844
|
-
await
|
|
2942
|
+
const parentDir = path3.dirname(targetPath);
|
|
2943
|
+
await fs3.mkdir(parentDir, { recursive: true });
|
|
2944
|
+
await fs3.writeFile(targetPath, scriptContent, { encoding: "utf8", mode: 493 });
|
|
2845
2945
|
} catch (error) {
|
|
2846
2946
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2847
2947
|
throw new Error(`Failed to install hook script to ${targetPath}: ${errorMessage}`);
|
|
@@ -2850,11 +2950,17 @@ async function installHookScript(scriptContent, targetPath) {
|
|
|
2850
2950
|
async function copyBraingridReadme(targetPath = ".braingrid/README.md") {
|
|
2851
2951
|
try {
|
|
2852
2952
|
const content = await fetchFileFromGitHub("claude-code/README.md");
|
|
2853
|
-
const parentDir =
|
|
2854
|
-
await
|
|
2855
|
-
await
|
|
2953
|
+
const parentDir = path3.dirname(targetPath);
|
|
2954
|
+
await fs3.mkdir(parentDir, { recursive: true });
|
|
2955
|
+
await fs3.writeFile(targetPath, content, { encoding: "utf8", mode: 420 });
|
|
2856
2956
|
return true;
|
|
2857
|
-
} catch {
|
|
2957
|
+
} catch (error) {
|
|
2958
|
+
log(
|
|
2959
|
+
"WARN",
|
|
2960
|
+
"setup",
|
|
2961
|
+
"readme_failed",
|
|
2962
|
+
`error=${error instanceof Error ? error.message : String(error)}`
|
|
2963
|
+
);
|
|
2858
2964
|
return false;
|
|
2859
2965
|
}
|
|
2860
2966
|
}
|
|
@@ -2920,7 +3026,7 @@ async function updateClaudeSettings(settingsPath = ".claude/settings.json", stat
|
|
|
2920
3026
|
try {
|
|
2921
3027
|
let settings = {};
|
|
2922
3028
|
try {
|
|
2923
|
-
const content2 = await
|
|
3029
|
+
const content2 = await fs3.readFile(settingsPath, "utf8");
|
|
2924
3030
|
settings = JSON.parse(content2);
|
|
2925
3031
|
} catch {
|
|
2926
3032
|
}
|
|
@@ -2966,10 +3072,10 @@ async function updateClaudeSettings(settingsPath = ".claude/settings.json", stat
|
|
|
2966
3072
|
...existingHooks,
|
|
2967
3073
|
...mergedByType
|
|
2968
3074
|
};
|
|
2969
|
-
const parentDir =
|
|
2970
|
-
await
|
|
3075
|
+
const parentDir = path3.dirname(settingsPath);
|
|
3076
|
+
await fs3.mkdir(parentDir, { recursive: true });
|
|
2971
3077
|
const content = JSON.stringify(settings, null, " ");
|
|
2972
|
-
await
|
|
3078
|
+
await fs3.writeFile(settingsPath, content, { encoding: "utf8" });
|
|
2973
3079
|
} catch (error) {
|
|
2974
3080
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2975
3081
|
throw new Error(`Failed to update Claude settings at ${settingsPath}: ${errorMessage}`);
|
|
@@ -3177,7 +3283,7 @@ function getManualInstallInstructions() {
|
|
|
3177
3283
|
|
|
3178
3284
|
// src/utils/github-repo.ts
|
|
3179
3285
|
import { exec as exec3 } from "child_process";
|
|
3180
|
-
import
|
|
3286
|
+
import path4 from "path";
|
|
3181
3287
|
import { promisify as promisify3 } from "util";
|
|
3182
3288
|
var execAsync4 = promisify3(exec3);
|
|
3183
3289
|
async function initGitRepo() {
|
|
@@ -3197,7 +3303,7 @@ async function isGhAuthenticated() {
|
|
|
3197
3303
|
}
|
|
3198
3304
|
}
|
|
3199
3305
|
function getCurrentDirectoryName() {
|
|
3200
|
-
return
|
|
3306
|
+
return path4.basename(process.cwd());
|
|
3201
3307
|
}
|
|
3202
3308
|
async function createGitHubRepoWithGh(name, isPrivate) {
|
|
3203
3309
|
try {
|
|
@@ -3220,7 +3326,7 @@ async function createGitHubRepoWithGh(name, isPrivate) {
|
|
|
3220
3326
|
}
|
|
3221
3327
|
}
|
|
3222
3328
|
async function canUseGhAutomation() {
|
|
3223
|
-
const { isGhInstalled: isGhInstalled2 } = await import("./gh-installer-
|
|
3329
|
+
const { isGhInstalled: isGhInstalled2 } = await import("./gh-installer-T7E7SYVI.js");
|
|
3224
3330
|
const ghInstalled = await isGhInstalled2();
|
|
3225
3331
|
if (!ghInstalled) {
|
|
3226
3332
|
return false;
|
|
@@ -3230,11 +3336,11 @@ async function canUseGhAutomation() {
|
|
|
3230
3336
|
|
|
3231
3337
|
// src/utils/gitignore.ts
|
|
3232
3338
|
import { readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
|
|
3233
|
-
import
|
|
3339
|
+
import path5 from "path";
|
|
3234
3340
|
async function addBraingridTempToGitignore() {
|
|
3235
3341
|
const gitRoot = await getGitRoot();
|
|
3236
3342
|
if (!gitRoot) return;
|
|
3237
|
-
const gitignorePath =
|
|
3343
|
+
const gitignorePath = path5.join(gitRoot, ".gitignore");
|
|
3238
3344
|
const entry = ".braingrid/temp/";
|
|
3239
3345
|
let content = "";
|
|
3240
3346
|
try {
|
|
@@ -3252,9 +3358,195 @@ ${entry}
|
|
|
3252
3358
|
await writeFile2(gitignorePath, newContent, "utf8");
|
|
3253
3359
|
}
|
|
3254
3360
|
|
|
3361
|
+
// src/utils/jq-installer.ts
|
|
3362
|
+
import { exec as exec4 } from "child_process";
|
|
3363
|
+
import { promisify as promisify4 } from "util";
|
|
3364
|
+
import chalk7 from "chalk";
|
|
3365
|
+
var execAsync5 = promisify4(exec4);
|
|
3366
|
+
async function isJqInstalled() {
|
|
3367
|
+
return isCliInstalled("jq");
|
|
3368
|
+
}
|
|
3369
|
+
async function getJqVersion() {
|
|
3370
|
+
return getCliVersion("jq", "--version", (output) => {
|
|
3371
|
+
const match = output.match(/jq-([\d.]+)/);
|
|
3372
|
+
return match ? match[1] : output.trim();
|
|
3373
|
+
});
|
|
3374
|
+
}
|
|
3375
|
+
async function installViaHomebrew2() {
|
|
3376
|
+
console.log(chalk7.blue("\u{1F4E6} Installing jq via Homebrew..."));
|
|
3377
|
+
try {
|
|
3378
|
+
await execAsync5("brew install jq", {
|
|
3379
|
+
timeout: 3e5
|
|
3380
|
+
// 5 minutes
|
|
3381
|
+
});
|
|
3382
|
+
const version = await getJqVersion();
|
|
3383
|
+
if (!version) {
|
|
3384
|
+
return {
|
|
3385
|
+
success: false,
|
|
3386
|
+
message: chalk7.red("\u274C jq installation completed but jq command not found")
|
|
3387
|
+
};
|
|
3388
|
+
}
|
|
3389
|
+
return {
|
|
3390
|
+
success: true,
|
|
3391
|
+
message: chalk7.green(`\u2705 jq installed successfully (version ${version})!`),
|
|
3392
|
+
version
|
|
3393
|
+
};
|
|
3394
|
+
} catch (error) {
|
|
3395
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
3396
|
+
return {
|
|
3397
|
+
success: false,
|
|
3398
|
+
message: chalk7.red("\u274C Failed to install jq via Homebrew\n\n") + chalk7.dim("Error: ") + errorMsg + "\n\n" + chalk7.dim("Please install jq manually from: ") + chalk7.cyan("https://jqlang.github.io/jq/download/")
|
|
3399
|
+
};
|
|
3400
|
+
}
|
|
3401
|
+
}
|
|
3402
|
+
async function installViaWebi() {
|
|
3403
|
+
console.log(chalk7.blue("\u{1F4E6} Installing jq via Webi..."));
|
|
3404
|
+
console.log(chalk7.dim("This will install jq to ~/.local/bin/\n"));
|
|
3405
|
+
try {
|
|
3406
|
+
await execAsync5("curl -sS https://webi.sh/jq | sh", {
|
|
3407
|
+
timeout: 3e5
|
|
3408
|
+
// 5 minutes
|
|
3409
|
+
});
|
|
3410
|
+
const version = await getJqVersion();
|
|
3411
|
+
if (!version) {
|
|
3412
|
+
return {
|
|
3413
|
+
success: true,
|
|
3414
|
+
message: chalk7.green("\u2705 jq installed successfully!\n") + chalk7.dim(
|
|
3415
|
+
"Note: You may need to restart your terminal for the jq command to be available."
|
|
3416
|
+
)
|
|
3417
|
+
};
|
|
3418
|
+
}
|
|
3419
|
+
return {
|
|
3420
|
+
success: true,
|
|
3421
|
+
message: chalk7.green(`\u2705 jq installed successfully (version ${version})!`),
|
|
3422
|
+
version
|
|
3423
|
+
};
|
|
3424
|
+
} catch (error) {
|
|
3425
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
3426
|
+
return {
|
|
3427
|
+
success: false,
|
|
3428
|
+
message: chalk7.red("\u274C Failed to install jq via Webi\n\n") + chalk7.dim("Error: ") + errorMsg + "\n\n" + chalk7.dim("Please install jq manually from: ") + chalk7.cyan("https://jqlang.github.io/jq/download/")
|
|
3429
|
+
};
|
|
3430
|
+
}
|
|
3431
|
+
}
|
|
3432
|
+
async function installJqMacOS() {
|
|
3433
|
+
const hasHomebrew = await isHomebrewInstalled();
|
|
3434
|
+
if (hasHomebrew) {
|
|
3435
|
+
return installViaHomebrew2();
|
|
3436
|
+
}
|
|
3437
|
+
return installViaWebi();
|
|
3438
|
+
}
|
|
3439
|
+
async function installJqWindows() {
|
|
3440
|
+
const hasWinget = await isWingetAvailable();
|
|
3441
|
+
if (!hasWinget) {
|
|
3442
|
+
return {
|
|
3443
|
+
success: false,
|
|
3444
|
+
message: chalk7.red("\u274C winget is not available on this system\n\n") + chalk7.dim("winget is included in Windows 10 (version 1809+) and Windows 11.\n") + chalk7.dim("Please install jq manually from: ") + chalk7.cyan("https://jqlang.github.io/jq/download/")
|
|
3445
|
+
};
|
|
3446
|
+
}
|
|
3447
|
+
console.log(chalk7.blue("\u{1F4E6} Installing jq via winget..."));
|
|
3448
|
+
try {
|
|
3449
|
+
await execAsync5("winget install --id jqlang.jq --silent", {
|
|
3450
|
+
timeout: 3e5
|
|
3451
|
+
// 5 minutes
|
|
3452
|
+
});
|
|
3453
|
+
const version = await getJqVersion();
|
|
3454
|
+
if (!version) {
|
|
3455
|
+
return {
|
|
3456
|
+
success: false,
|
|
3457
|
+
message: chalk7.red("\u274C jq installation completed but jq command not found")
|
|
3458
|
+
};
|
|
3459
|
+
}
|
|
3460
|
+
return {
|
|
3461
|
+
success: true,
|
|
3462
|
+
message: chalk7.green(`\u2705 jq installed successfully (version ${version})!`),
|
|
3463
|
+
version
|
|
3464
|
+
};
|
|
3465
|
+
} catch (error) {
|
|
3466
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
3467
|
+
return {
|
|
3468
|
+
success: false,
|
|
3469
|
+
message: chalk7.red("\u274C Failed to install jq via winget\n\n") + chalk7.dim("Error: ") + errorMsg + "\n\n" + chalk7.dim("Please install jq manually from: ") + chalk7.cyan("https://jqlang.github.io/jq/download/")
|
|
3470
|
+
};
|
|
3471
|
+
}
|
|
3472
|
+
}
|
|
3473
|
+
async function installJqLinux() {
|
|
3474
|
+
const packageManager = await detectLinuxPackageManager();
|
|
3475
|
+
if (!packageManager) {
|
|
3476
|
+
return {
|
|
3477
|
+
success: false,
|
|
3478
|
+
message: chalk7.red("\u274C Could not detect a supported package manager\n\n") + chalk7.dim("Supported package managers: apt, dnf, yum, pacman\n") + chalk7.dim("Please install jq manually from: ") + chalk7.cyan("https://jqlang.github.io/jq/download/")
|
|
3479
|
+
};
|
|
3480
|
+
}
|
|
3481
|
+
console.log(chalk7.blue(`\u{1F4E6} Installing jq via ${packageManager.name}...`));
|
|
3482
|
+
console.log(chalk7.dim("This may prompt for your sudo password.\n"));
|
|
3483
|
+
try {
|
|
3484
|
+
let installCommand;
|
|
3485
|
+
switch (packageManager.name) {
|
|
3486
|
+
case "apt":
|
|
3487
|
+
installCommand = "sudo apt update && sudo apt install jq -y";
|
|
3488
|
+
break;
|
|
3489
|
+
case "dnf":
|
|
3490
|
+
installCommand = "sudo dnf install jq -y";
|
|
3491
|
+
break;
|
|
3492
|
+
case "yum":
|
|
3493
|
+
installCommand = "sudo yum install jq -y";
|
|
3494
|
+
break;
|
|
3495
|
+
case "pacman":
|
|
3496
|
+
installCommand = "sudo pacman -S --noconfirm jq";
|
|
3497
|
+
break;
|
|
3498
|
+
default:
|
|
3499
|
+
return {
|
|
3500
|
+
success: false,
|
|
3501
|
+
message: chalk7.red(`\u274C Unsupported package manager: ${packageManager.name}`)
|
|
3502
|
+
};
|
|
3503
|
+
}
|
|
3504
|
+
await execAsync5(installCommand, {
|
|
3505
|
+
timeout: 3e5
|
|
3506
|
+
// 5 minutes
|
|
3507
|
+
});
|
|
3508
|
+
const version = await getJqVersion();
|
|
3509
|
+
if (!version) {
|
|
3510
|
+
return {
|
|
3511
|
+
success: false,
|
|
3512
|
+
message: chalk7.red("\u274C jq installation completed but jq command not found")
|
|
3513
|
+
};
|
|
3514
|
+
}
|
|
3515
|
+
return {
|
|
3516
|
+
success: true,
|
|
3517
|
+
message: chalk7.green(`\u2705 jq installed successfully (version ${version})!`),
|
|
3518
|
+
version
|
|
3519
|
+
};
|
|
3520
|
+
} catch (error) {
|
|
3521
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
3522
|
+
return {
|
|
3523
|
+
success: false,
|
|
3524
|
+
message: chalk7.red("\u274C Failed to install jq\n\n") + chalk7.dim("Error: ") + errorMsg + "\n\n" + chalk7.dim("Please install jq manually from: ") + chalk7.cyan("https://jqlang.github.io/jq/download/")
|
|
3525
|
+
};
|
|
3526
|
+
}
|
|
3527
|
+
}
|
|
3528
|
+
async function installJq() {
|
|
3529
|
+
const platform = process.platform;
|
|
3530
|
+
switch (platform) {
|
|
3531
|
+
case "darwin":
|
|
3532
|
+
return installJqMacOS();
|
|
3533
|
+
case "win32":
|
|
3534
|
+
return installJqWindows();
|
|
3535
|
+
case "linux":
|
|
3536
|
+
return installJqLinux();
|
|
3537
|
+
default:
|
|
3538
|
+
return {
|
|
3539
|
+
success: false,
|
|
3540
|
+
message: chalk7.red(`\u274C Unsupported platform: ${platform}
|
|
3541
|
+
|
|
3542
|
+
`) + chalk7.dim("Please install jq manually from: ") + chalk7.cyan("https://jqlang.github.io/jq/download/")
|
|
3543
|
+
};
|
|
3544
|
+
}
|
|
3545
|
+
}
|
|
3546
|
+
|
|
3255
3547
|
// src/utils/local-store.ts
|
|
3256
|
-
import
|
|
3257
|
-
import
|
|
3548
|
+
import fs4 from "fs";
|
|
3549
|
+
import path6 from "path";
|
|
3258
3550
|
|
|
3259
3551
|
// src/types/local-project.ts
|
|
3260
3552
|
import { z } from "zod";
|
|
@@ -3284,28 +3576,28 @@ async function getDefaultCwd() {
|
|
|
3284
3576
|
}
|
|
3285
3577
|
async function getBraingridDir(cwd) {
|
|
3286
3578
|
const dir = cwd ?? await getDefaultCwd();
|
|
3287
|
-
return
|
|
3579
|
+
return path6.join(dir, BRAINGRID_DIR);
|
|
3288
3580
|
}
|
|
3289
3581
|
async function getProjectConfigPath(cwd) {
|
|
3290
3582
|
const braingridDir = await getBraingridDir(cwd);
|
|
3291
|
-
return
|
|
3583
|
+
return path6.join(braingridDir, PROJECT_CONFIG_FILE);
|
|
3292
3584
|
}
|
|
3293
3585
|
async function projectConfigExists(cwd) {
|
|
3294
3586
|
const configPath = await getProjectConfigPath(cwd);
|
|
3295
|
-
return
|
|
3587
|
+
return fs4.existsSync(configPath);
|
|
3296
3588
|
}
|
|
3297
3589
|
async function ensureBraingridDir(cwd) {
|
|
3298
3590
|
const dir = await getBraingridDir(cwd);
|
|
3299
|
-
if (!
|
|
3300
|
-
|
|
3591
|
+
if (!fs4.existsSync(dir)) {
|
|
3592
|
+
fs4.mkdirSync(dir, { recursive: true });
|
|
3301
3593
|
}
|
|
3302
3594
|
}
|
|
3303
3595
|
async function loadProjectConfig(cwd) {
|
|
3304
3596
|
const configPath = await getProjectConfigPath(cwd);
|
|
3305
|
-
if (!
|
|
3597
|
+
if (!fs4.existsSync(configPath)) {
|
|
3306
3598
|
throw new Error(`Project not initialized. Run 'braingrid init' to initialize this repository.`);
|
|
3307
3599
|
}
|
|
3308
|
-
const content =
|
|
3600
|
+
const content = fs4.readFileSync(configPath, "utf8");
|
|
3309
3601
|
const data = JSON.parse(content);
|
|
3310
3602
|
return LocalProjectConfigSchema.parse(data);
|
|
3311
3603
|
}
|
|
@@ -3313,7 +3605,7 @@ async function saveProjectConfig(config2, cwd) {
|
|
|
3313
3605
|
await ensureBraingridDir(cwd);
|
|
3314
3606
|
const configPath = await getProjectConfigPath(cwd);
|
|
3315
3607
|
const content = JSON.stringify(config2, null, 2);
|
|
3316
|
-
|
|
3608
|
+
fs4.writeFileSync(configPath, content, "utf8");
|
|
3317
3609
|
}
|
|
3318
3610
|
async function getLocalProjectId(cwd) {
|
|
3319
3611
|
try {
|
|
@@ -3358,7 +3650,7 @@ async function getRepositoryId(repositoryService, owner, name) {
|
|
|
3358
3650
|
}
|
|
3359
3651
|
|
|
3360
3652
|
// src/utils/spinner.ts
|
|
3361
|
-
import
|
|
3653
|
+
import chalk8 from "chalk";
|
|
3362
3654
|
var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
3363
3655
|
async function waitWithSpinner(message, checkFn, intervalMs = 3e3, maxAttempts = 60) {
|
|
3364
3656
|
let frameIndex = 0;
|
|
@@ -3366,7 +3658,7 @@ async function waitWithSpinner(message, checkFn, intervalMs = 3e3, maxAttempts =
|
|
|
3366
3658
|
const updateSpinner = () => {
|
|
3367
3659
|
const frame = SPINNER_FRAMES[frameIndex];
|
|
3368
3660
|
frameIndex = (frameIndex + 1) % SPINNER_FRAMES.length;
|
|
3369
|
-
process.stdout.write(`\r${
|
|
3661
|
+
process.stdout.write(`\r${chalk8.cyan(frame)} ${message}...`);
|
|
3370
3662
|
};
|
|
3371
3663
|
spinnerInterval = setInterval(updateSpinner, 100);
|
|
3372
3664
|
try {
|
|
@@ -3390,7 +3682,7 @@ async function waitWithSpinner(message, checkFn, intervalMs = 3e3, maxAttempts =
|
|
|
3390
3682
|
throw error;
|
|
3391
3683
|
}
|
|
3392
3684
|
}
|
|
3393
|
-
function showSpinner(message, color =
|
|
3685
|
+
function showSpinner(message, color = chalk8.cyan) {
|
|
3394
3686
|
let frameIndex = 0;
|
|
3395
3687
|
const interval = setInterval(() => {
|
|
3396
3688
|
const frame = SPINNER_FRAMES[frameIndex];
|
|
@@ -3404,7 +3696,7 @@ function showSpinner(message, color = chalk7.cyan) {
|
|
|
3404
3696
|
}
|
|
3405
3697
|
|
|
3406
3698
|
// src/utils/update-checker.ts
|
|
3407
|
-
import
|
|
3699
|
+
import chalk9 from "chalk";
|
|
3408
3700
|
|
|
3409
3701
|
// src/utils/version.ts
|
|
3410
3702
|
import axios6 from "axios";
|
|
@@ -3496,7 +3788,7 @@ async function checkAndShowUpdateWarning() {
|
|
|
3496
3788
|
try {
|
|
3497
3789
|
const { available, currentVersion, latestVersion } = await isUpdateAvailable();
|
|
3498
3790
|
if (available && latestVersion) {
|
|
3499
|
-
const warning = "\n" +
|
|
3791
|
+
const warning = "\n" + chalk9.yellow(`\u26A0\uFE0F Update available: ${currentVersion} \u2192 ${latestVersion}`) + "\n" + chalk9.dim(` Run \`${getUpdateCommand()}\` to update`) + "\n";
|
|
3500
3792
|
console.log(warning);
|
|
3501
3793
|
}
|
|
3502
3794
|
} catch {
|
|
@@ -3504,13 +3796,13 @@ async function checkAndShowUpdateWarning() {
|
|
|
3504
3796
|
}
|
|
3505
3797
|
|
|
3506
3798
|
// src/handlers/setup.handlers.ts
|
|
3507
|
-
import * as
|
|
3508
|
-
import * as
|
|
3799
|
+
import * as fs5 from "fs/promises";
|
|
3800
|
+
import * as path7 from "path";
|
|
3509
3801
|
import { select } from "@inquirer/prompts";
|
|
3510
|
-
import
|
|
3802
|
+
import chalk10 from "chalk";
|
|
3511
3803
|
async function fileExists(filePath) {
|
|
3512
3804
|
try {
|
|
3513
|
-
await
|
|
3805
|
+
await fs5.access(filePath);
|
|
3514
3806
|
return true;
|
|
3515
3807
|
} catch {
|
|
3516
3808
|
return false;
|
|
@@ -3523,7 +3815,7 @@ async function getFileList(sourcePaths, targetPaths) {
|
|
|
3523
3815
|
const items = await listGitHubDirectory(sourceDir);
|
|
3524
3816
|
for (const item of items) {
|
|
3525
3817
|
if (item.type === "file") {
|
|
3526
|
-
const itemTargetPath =
|
|
3818
|
+
const itemTargetPath = path7.join(targetDir, item.name);
|
|
3527
3819
|
const exists = await fileExists(itemTargetPath);
|
|
3528
3820
|
operations.push({
|
|
3529
3821
|
type: "copy",
|
|
@@ -3533,13 +3825,19 @@ async function getFileList(sourcePaths, targetPaths) {
|
|
|
3533
3825
|
dirIndex
|
|
3534
3826
|
});
|
|
3535
3827
|
} else if (item.type === "dir") {
|
|
3536
|
-
const itemTargetPath =
|
|
3828
|
+
const itemTargetPath = path7.join(targetDir, item.name);
|
|
3537
3829
|
await processDirectory(item.path, itemTargetPath, dirIndex);
|
|
3538
3830
|
}
|
|
3539
3831
|
}
|
|
3540
3832
|
} catch (error) {
|
|
3833
|
+
log(
|
|
3834
|
+
"WARN",
|
|
3835
|
+
"setup",
|
|
3836
|
+
"list_dir_failed",
|
|
3837
|
+
`source=${sourceDir} error=${error instanceof Error ? error.message : String(error)}`
|
|
3838
|
+
);
|
|
3541
3839
|
console.warn(
|
|
3542
|
-
|
|
3840
|
+
chalk10.yellow(`\u26A0\uFE0F Could not list directory: ${sourceDir}`),
|
|
3543
3841
|
error instanceof Error ? error.message : String(error)
|
|
3544
3842
|
);
|
|
3545
3843
|
}
|
|
@@ -3550,28 +3848,28 @@ async function getFileList(sourcePaths, targetPaths) {
|
|
|
3550
3848
|
return operations;
|
|
3551
3849
|
}
|
|
3552
3850
|
function displayInstallationPlan(operations, injectionFile) {
|
|
3553
|
-
console.log(
|
|
3554
|
-
console.log(
|
|
3555
|
-
console.log(
|
|
3851
|
+
console.log(chalk10.bold("\n\u{1F4CB} Installation Plan:\n"));
|
|
3852
|
+
console.log(chalk10.cyan(" Content Injection:"));
|
|
3853
|
+
console.log(chalk10.dim(` ${injectionFile}`));
|
|
3556
3854
|
const newFiles = operations.filter((op) => !op.exists);
|
|
3557
3855
|
const existingFiles = operations.filter((op) => op.exists);
|
|
3558
3856
|
if (newFiles.length > 0) {
|
|
3559
|
-
console.log(
|
|
3857
|
+
console.log(chalk10.cyan("\n New Files:"));
|
|
3560
3858
|
for (const op of newFiles) {
|
|
3561
|
-
console.log(
|
|
3859
|
+
console.log(chalk10.dim(` ${op.targetPath}`));
|
|
3562
3860
|
}
|
|
3563
3861
|
}
|
|
3564
3862
|
if (existingFiles.length > 0) {
|
|
3565
|
-
console.log(
|
|
3863
|
+
console.log(chalk10.yellow("\n Existing Files (will prompt):"));
|
|
3566
3864
|
for (const op of existingFiles) {
|
|
3567
|
-
console.log(
|
|
3865
|
+
console.log(chalk10.dim(` ${op.targetPath}`));
|
|
3568
3866
|
}
|
|
3569
3867
|
}
|
|
3570
3868
|
console.log("");
|
|
3571
3869
|
}
|
|
3572
3870
|
async function promptForConflict(filePath) {
|
|
3573
3871
|
const answer = await select({
|
|
3574
|
-
message:
|
|
3872
|
+
message: chalk10.yellow(`File exists: ${filePath}`),
|
|
3575
3873
|
choices: [
|
|
3576
3874
|
{ name: "[A]ll - Overwrite all remaining", value: "all" },
|
|
3577
3875
|
{ name: "[O]verwrite - Replace this file", value: "overwrite" },
|
|
@@ -3591,6 +3889,7 @@ async function installFiles(operations, force, dirCount) {
|
|
|
3591
3889
|
if (response === "quit") {
|
|
3592
3890
|
return { installedPerDir, skipped, cancelled: true };
|
|
3593
3891
|
} else if (response === "skip") {
|
|
3892
|
+
log("INFO", "setup", "skip_file", `target=${operation.targetPath} reason=user_skip`);
|
|
3594
3893
|
skipped++;
|
|
3595
3894
|
continue;
|
|
3596
3895
|
} else if (response === "all") {
|
|
@@ -3599,12 +3898,19 @@ async function installFiles(operations, force, dirCount) {
|
|
|
3599
3898
|
}
|
|
3600
3899
|
try {
|
|
3601
3900
|
await copyFileFromGitHub(operation.sourcePath, operation.targetPath);
|
|
3901
|
+
log("INFO", "setup", "copy_file", `target=${operation.targetPath}`);
|
|
3602
3902
|
if (operation.dirIndex !== void 0) {
|
|
3603
3903
|
installedPerDir[operation.dirIndex]++;
|
|
3604
3904
|
}
|
|
3605
3905
|
} catch (error) {
|
|
3906
|
+
log(
|
|
3907
|
+
"ERROR",
|
|
3908
|
+
"setup",
|
|
3909
|
+
"copy_file_failed",
|
|
3910
|
+
`target=${operation.targetPath} error=${error instanceof Error ? error.message : String(error)}`
|
|
3911
|
+
);
|
|
3606
3912
|
console.error(
|
|
3607
|
-
|
|
3913
|
+
chalk10.red(`Failed to copy ${operation.targetPath}:`),
|
|
3608
3914
|
error instanceof Error ? error.message : String(error)
|
|
3609
3915
|
);
|
|
3610
3916
|
skipped++;
|
|
@@ -3613,7 +3919,14 @@ async function installFiles(operations, force, dirCount) {
|
|
|
3613
3919
|
return { installedPerDir, skipped, cancelled: false };
|
|
3614
3920
|
}
|
|
3615
3921
|
async function _handleSetup(config2, opts) {
|
|
3616
|
-
|
|
3922
|
+
initLogger(".braingrid/temp/setup-debug.log");
|
|
3923
|
+
log(
|
|
3924
|
+
"INFO",
|
|
3925
|
+
"setup",
|
|
3926
|
+
"start",
|
|
3927
|
+
`integration=${config2.name} force=${opts.force} dry_run=${opts.dryRun}`
|
|
3928
|
+
);
|
|
3929
|
+
console.log(chalk10.bold(`\u{1F680} Setting up ${config2.name} integration...
|
|
3617
3930
|
`));
|
|
3618
3931
|
const operations = await getFileList(config2.sourceDirs, config2.targetDirs);
|
|
3619
3932
|
const injectionFileExists = await fileExists(config2.injection.targetFile);
|
|
@@ -3630,7 +3943,7 @@ async function _handleSetup(config2, opts) {
|
|
|
3630
3943
|
if (opts.dryRun) {
|
|
3631
3944
|
return {
|
|
3632
3945
|
success: true,
|
|
3633
|
-
message:
|
|
3946
|
+
message: chalk10.green("\u2705 Dry-run complete. No files were modified.\n\n") + chalk10.dim(`Would install ${operations.length} files.`)
|
|
3634
3947
|
};
|
|
3635
3948
|
}
|
|
3636
3949
|
const copyOps = operations.filter((op) => op.type === "copy");
|
|
@@ -3639,20 +3952,30 @@ async function _handleSetup(config2, opts) {
|
|
|
3639
3952
|
const totalInstalled = result.installedPerDir.reduce((a, b) => a + b, 0);
|
|
3640
3953
|
return {
|
|
3641
3954
|
success: false,
|
|
3642
|
-
message:
|
|
3955
|
+
message: chalk10.yellow("\u26A0\uFE0F Installation cancelled.\n\n") + chalk10.dim(`Installed: ${totalInstalled}, Skipped: ${result.skipped}`),
|
|
3643
3956
|
code: "CANCELLED"
|
|
3644
3957
|
};
|
|
3645
3958
|
}
|
|
3646
3959
|
try {
|
|
3647
3960
|
const content = await fetchFileFromGitHub(config2.injection.sourceFile);
|
|
3648
3961
|
await injectContentIntoFile(config2.injection.targetFile, content);
|
|
3962
|
+
log("INFO", "setup", "inject", `target=${config2.injection.targetFile}`);
|
|
3649
3963
|
} catch (error) {
|
|
3964
|
+
log(
|
|
3965
|
+
"ERROR",
|
|
3966
|
+
"setup",
|
|
3967
|
+
"inject_failed",
|
|
3968
|
+
`target=${config2.injection.targetFile} error=${error instanceof Error ? error.message : String(error)}`
|
|
3969
|
+
);
|
|
3650
3970
|
console.error(
|
|
3651
|
-
|
|
3971
|
+
chalk10.red(`Failed to inject content into ${config2.injection.targetFile}:`),
|
|
3652
3972
|
error instanceof Error ? error.message : String(error)
|
|
3653
3973
|
);
|
|
3654
3974
|
}
|
|
3655
|
-
await copyBraingridReadme();
|
|
3975
|
+
const readmeCopied = await copyBraingridReadme();
|
|
3976
|
+
if (readmeCopied) {
|
|
3977
|
+
log("INFO", "setup", "readme", "path=.braingrid/README.md");
|
|
3978
|
+
}
|
|
3656
3979
|
return {
|
|
3657
3980
|
success: true,
|
|
3658
3981
|
data: {
|
|
@@ -3667,14 +3990,14 @@ function buildSuccessMessage(config2, installedPerDir, extras) {
|
|
|
3667
3990
|
const count = installedPerDir[i] ?? 0;
|
|
3668
3991
|
if (count === 0) continue;
|
|
3669
3992
|
const { label } = config2.dirLabels[i];
|
|
3670
|
-
dirLines +=
|
|
3993
|
+
dirLines += chalk10.dim(` ${label}: ${count}
|
|
3671
3994
|
`);
|
|
3672
3995
|
}
|
|
3673
|
-
return
|
|
3996
|
+
return chalk10.green(`\u2705 ${config2.name} integration installed successfully!
|
|
3674
3997
|
|
|
3675
|
-
`) +
|
|
3998
|
+
`) + chalk10.dim("Files installed:\n") + dirLines + extras + chalk10.dim(` Content injected into: ${config2.injection.targetFile}
|
|
3676
3999
|
|
|
3677
|
-
`) +
|
|
4000
|
+
`) + chalk10.dim("Next, try:\n") + chalk10.dim(" /build REQ-X \u2192 build a requirement\n") + chalk10.dim(' /specify "add two-factor auth" \u2192 specify a requirement\n') + chalk10.dim(" Learn more: ") + chalk10.cyan(config2.docsUrl);
|
|
3678
4001
|
}
|
|
3679
4002
|
function isSetupResult(result) {
|
|
3680
4003
|
return "data" in result && result.success === true && !("message" in result);
|
|
@@ -3720,17 +4043,31 @@ async function handleSetupClaudeCode(opts) {
|
|
|
3720
4043
|
const scriptContent = await fetchFileFromGitHub("claude-code/statusline.sh");
|
|
3721
4044
|
await installStatusLineScript(scriptContent);
|
|
3722
4045
|
statusLineInstalled = true;
|
|
4046
|
+
log("INFO", "setup", "statusline", "path=.claude/statusline.sh");
|
|
3723
4047
|
} catch (error) {
|
|
4048
|
+
log(
|
|
4049
|
+
"ERROR",
|
|
4050
|
+
"setup",
|
|
4051
|
+
"statusline_failed",
|
|
4052
|
+
`error=${error instanceof Error ? error.message : String(error)}`
|
|
4053
|
+
);
|
|
3724
4054
|
console.error(
|
|
3725
|
-
|
|
4055
|
+
chalk10.yellow("\u26A0\uFE0F Failed to install status line script:"),
|
|
3726
4056
|
error instanceof Error ? error.message : String(error)
|
|
3727
4057
|
);
|
|
3728
4058
|
}
|
|
3729
4059
|
try {
|
|
3730
4060
|
await updateClaudeSettings();
|
|
4061
|
+
log("INFO", "setup", "settings", "path=.claude/settings.json");
|
|
3731
4062
|
} catch (error) {
|
|
4063
|
+
log(
|
|
4064
|
+
"ERROR",
|
|
4065
|
+
"setup",
|
|
4066
|
+
"settings_failed",
|
|
4067
|
+
`error=${error instanceof Error ? error.message : String(error)}`
|
|
4068
|
+
);
|
|
3732
4069
|
console.error(
|
|
3733
|
-
|
|
4070
|
+
chalk10.yellow("\u26A0\uFE0F Failed to update Claude settings:"),
|
|
3734
4071
|
error instanceof Error ? error.message : String(error)
|
|
3735
4072
|
);
|
|
3736
4073
|
}
|
|
@@ -3746,25 +4083,44 @@ async function handleSetupClaudeCode(opts) {
|
|
|
3746
4083
|
const content = await fetchFileFromGitHub(`claude-code/hooks/${script}`);
|
|
3747
4084
|
await installHookScript(content, `.claude/hooks/${script}`);
|
|
3748
4085
|
installedHooks.push(script);
|
|
4086
|
+
log("INFO", "setup", "hook", `script=${script}`);
|
|
3749
4087
|
} catch (error) {
|
|
4088
|
+
log(
|
|
4089
|
+
"ERROR",
|
|
4090
|
+
"setup",
|
|
4091
|
+
"hook_failed",
|
|
4092
|
+
`script=${script} error=${error instanceof Error ? error.message : String(error)}`
|
|
4093
|
+
);
|
|
3750
4094
|
console.error(
|
|
3751
|
-
|
|
4095
|
+
chalk10.yellow(`\u26A0\uFE0F Failed to install hook ${script}:`),
|
|
3752
4096
|
error instanceof Error ? error.message : String(error)
|
|
3753
4097
|
);
|
|
3754
4098
|
}
|
|
3755
4099
|
}
|
|
3756
|
-
const statusLineMessage = statusLineInstalled ?
|
|
3757
|
-
const hooksMessage = installedHooks.map((s) =>
|
|
4100
|
+
const statusLineMessage = statusLineInstalled ? chalk10.dim(" Status line: .claude/statusline.sh\n") : "";
|
|
4101
|
+
const hooksMessage = installedHooks.map((s) => chalk10.dim(` Hook script: .claude/hooks/${s}
|
|
3758
4102
|
`)).join("");
|
|
4103
|
+
const totalInstalled = displayPerDir.reduce((a, b) => a + b, 0);
|
|
4104
|
+
log(
|
|
4105
|
+
"INFO",
|
|
4106
|
+
"setup",
|
|
4107
|
+
"complete",
|
|
4108
|
+
`installed=${totalInstalled} skipped=${setupResult.data.skipped} hooks=${installedHooks.length}`
|
|
4109
|
+
);
|
|
4110
|
+
await flushLogger();
|
|
4111
|
+
closeLogger();
|
|
3759
4112
|
return {
|
|
3760
4113
|
success: true,
|
|
3761
4114
|
message: buildSuccessMessage(config2, displayPerDir, statusLineMessage + hooksMessage)
|
|
3762
4115
|
};
|
|
3763
4116
|
} catch (error) {
|
|
3764
4117
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4118
|
+
log("ERROR", "setup", "fatal", `error=${errorMessage}`);
|
|
4119
|
+
await flushLogger();
|
|
4120
|
+
closeLogger();
|
|
3765
4121
|
return {
|
|
3766
4122
|
success: false,
|
|
3767
|
-
message:
|
|
4123
|
+
message: chalk10.red(`\u274C Setup failed: ${errorMessage}`)
|
|
3768
4124
|
};
|
|
3769
4125
|
}
|
|
3770
4126
|
}
|
|
@@ -3788,16 +4144,23 @@ async function handleSetupCursor(opts) {
|
|
|
3788
4144
|
if (!isSetupResult(setupResult)) {
|
|
3789
4145
|
return setupResult;
|
|
3790
4146
|
}
|
|
3791
|
-
const { installedPerDir } = setupResult.data;
|
|
4147
|
+
const { installedPerDir, skipped } = setupResult.data;
|
|
4148
|
+
const totalInstalled = installedPerDir.reduce((a, b) => a + b, 0);
|
|
4149
|
+
log("INFO", "setup", "complete", `installed=${totalInstalled} skipped=${skipped}`);
|
|
4150
|
+
await flushLogger();
|
|
4151
|
+
closeLogger();
|
|
3792
4152
|
return {
|
|
3793
4153
|
success: true,
|
|
3794
4154
|
message: buildSuccessMessage(config2, installedPerDir, "")
|
|
3795
4155
|
};
|
|
3796
4156
|
} catch (error) {
|
|
3797
4157
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4158
|
+
log("ERROR", "setup", "fatal", `error=${errorMessage}`);
|
|
4159
|
+
await flushLogger();
|
|
4160
|
+
closeLogger();
|
|
3798
4161
|
return {
|
|
3799
4162
|
success: false,
|
|
3800
|
-
message:
|
|
4163
|
+
message: chalk10.red(`\u274C Setup failed: ${errorMessage}`)
|
|
3801
4164
|
};
|
|
3802
4165
|
}
|
|
3803
4166
|
}
|
|
@@ -3805,7 +4168,7 @@ async function handleSetupCursor(opts) {
|
|
|
3805
4168
|
// src/handlers/update.handlers.ts
|
|
3806
4169
|
import { execSync as execSync3 } from "child_process";
|
|
3807
4170
|
import { confirm } from "@inquirer/prompts";
|
|
3808
|
-
import
|
|
4171
|
+
import chalk11 from "chalk";
|
|
3809
4172
|
|
|
3810
4173
|
// src/utils/package-manager.ts
|
|
3811
4174
|
import { execSync as execSync2 } from "child_process";
|
|
@@ -3921,7 +4284,7 @@ function runSetupSubprocess(subcommand) {
|
|
|
3921
4284
|
return output.toString();
|
|
3922
4285
|
} catch (error) {
|
|
3923
4286
|
const msg = error instanceof Error ? error.message : String(error);
|
|
3924
|
-
return
|
|
4287
|
+
return chalk11.yellow(`\u26A0\uFE0F Setup ${subcommand} failed: ${msg}`);
|
|
3925
4288
|
}
|
|
3926
4289
|
}
|
|
3927
4290
|
async function promptForIdeUpdates() {
|
|
@@ -3954,17 +4317,17 @@ ${runSetupSubprocess("cursor")}`;
|
|
|
3954
4317
|
async function handleUpdate(opts) {
|
|
3955
4318
|
try {
|
|
3956
4319
|
const currentVersion = getCurrentVersion();
|
|
3957
|
-
let output =
|
|
3958
|
-
output += `${
|
|
4320
|
+
let output = chalk11.bold.cyan("\n\u{1F504} BrainGrid CLI Update\n\n");
|
|
4321
|
+
output += `${chalk11.bold("Current version:")} ${currentVersion}
|
|
3959
4322
|
`;
|
|
3960
|
-
output +=
|
|
4323
|
+
output += chalk11.dim("Checking for updates...\n");
|
|
3961
4324
|
const latestVersion = await getLatestVersion();
|
|
3962
|
-
output += `${
|
|
4325
|
+
output += `${chalk11.bold("Latest version:")} ${latestVersion}
|
|
3963
4326
|
|
|
3964
4327
|
`;
|
|
3965
4328
|
const comparison = compareVersions(currentVersion, latestVersion);
|
|
3966
4329
|
if (comparison === 0) {
|
|
3967
|
-
output +=
|
|
4330
|
+
output += chalk11.green("\u2705 You are already on the latest version!\n");
|
|
3968
4331
|
console.log(output);
|
|
3969
4332
|
await copyBraingridReadme();
|
|
3970
4333
|
await addBraingridTempToGitignore();
|
|
@@ -3976,32 +4339,32 @@ async function handleUpdate(opts) {
|
|
|
3976
4339
|
};
|
|
3977
4340
|
}
|
|
3978
4341
|
if (comparison > 0) {
|
|
3979
|
-
output +=
|
|
3980
|
-
output +=
|
|
4342
|
+
output += chalk11.yellow("\u26A0\uFE0F You are on a newer version than what is published.\n");
|
|
4343
|
+
output += chalk11.dim(" This is expected if you are developing locally.\n");
|
|
3981
4344
|
return {
|
|
3982
4345
|
success: true,
|
|
3983
4346
|
message: output,
|
|
3984
4347
|
data: { currentVersion, latestVersion, upToDate: false }
|
|
3985
4348
|
};
|
|
3986
4349
|
}
|
|
3987
|
-
output +=
|
|
4350
|
+
output += chalk11.yellow(`\u2B06\uFE0F Update available: ${currentVersion} \u2192 ${latestVersion}
|
|
3988
4351
|
|
|
3989
4352
|
`);
|
|
3990
4353
|
if (opts.check) {
|
|
3991
|
-
output +=
|
|
4354
|
+
output += chalk11.dim("Run ") + chalk11.cyan("braingrid update") + chalk11.dim(" to update\n");
|
|
3992
4355
|
return {
|
|
3993
4356
|
success: true,
|
|
3994
4357
|
message: output,
|
|
3995
4358
|
data: { currentVersion, latestVersion, upToDate: false }
|
|
3996
4359
|
};
|
|
3997
4360
|
}
|
|
3998
|
-
output +=
|
|
4361
|
+
output += chalk11.dim("Detecting package manager...\n");
|
|
3999
4362
|
const packageManager = await detectPackageManager(PACKAGE_NAME);
|
|
4000
|
-
output += `${
|
|
4363
|
+
output += `${chalk11.bold("Package manager:")} ${packageManager}
|
|
4001
4364
|
|
|
4002
4365
|
`;
|
|
4003
4366
|
const updateCommand = getUpdateCommand2(packageManager, PACKAGE_NAME);
|
|
4004
|
-
output += `${
|
|
4367
|
+
output += `${chalk11.dim("Running: ") + chalk11.cyan(updateCommand)}
|
|
4005
4368
|
|
|
4006
4369
|
`;
|
|
4007
4370
|
console.log(output);
|
|
@@ -4011,7 +4374,7 @@ async function handleUpdate(opts) {
|
|
|
4011
4374
|
const setupOutput = await promptForIdeUpdates();
|
|
4012
4375
|
return {
|
|
4013
4376
|
success: true,
|
|
4014
|
-
message:
|
|
4377
|
+
message: chalk11.green("\n\u2705 Successfully updated BrainGrid CLI!\n") + setupOutput,
|
|
4015
4378
|
data: { currentVersion, latestVersion, packageManager }
|
|
4016
4379
|
};
|
|
4017
4380
|
} catch (error) {
|
|
@@ -4049,12 +4412,12 @@ function getServices() {
|
|
|
4049
4412
|
function promptToAddOrganization(owner, webUrl) {
|
|
4050
4413
|
return {
|
|
4051
4414
|
success: false,
|
|
4052
|
-
message:
|
|
4415
|
+
message: chalk12.yellow("\u26A0\uFE0F No projects found for this repository.\n\n") + chalk12.dim(`Repository: ${owner}/*
|
|
4053
4416
|
|
|
4054
|
-
`) +
|
|
4417
|
+
`) + chalk12.dim(`You have GitHub connected, but not for the "${owner}" organization.
|
|
4055
4418
|
|
|
4056
|
-
`) +
|
|
4057
|
-
`) +
|
|
4419
|
+
`) + chalk12.dim("To connect ") + chalk12.cyan(owner) + chalk12.dim(":\n") + chalk12.dim(" 1. Visit: ") + chalk12.cyan(`${webUrl}/integrations`) + chalk12.dim("\n") + chalk12.dim(' 2. Click "Add GitHub Organization"\n') + chalk12.dim(` 3. Select "${owner}"
|
|
4420
|
+
`) + chalk12.dim(" 4. Run ") + chalk12.cyan("braingrid init") + chalk12.dim(" again")
|
|
4058
4421
|
};
|
|
4059
4422
|
}
|
|
4060
4423
|
async function promptToCreateProject(gitInfo, projectService, repositoryService) {
|
|
@@ -4062,20 +4425,20 @@ async function promptToCreateProject(gitInfo, projectService, repositoryService)
|
|
|
4062
4425
|
if (!gitInfo.owner || !gitInfo.name) {
|
|
4063
4426
|
return {
|
|
4064
4427
|
success: false,
|
|
4065
|
-
message:
|
|
4428
|
+
message: chalk12.red("\u274C Repository information is incomplete")
|
|
4066
4429
|
};
|
|
4067
4430
|
}
|
|
4068
4431
|
const repositoryId = await getRepositoryId(repositoryService, gitInfo.owner, gitInfo.name);
|
|
4069
4432
|
if (!repositoryId) {
|
|
4070
4433
|
return {
|
|
4071
4434
|
success: false,
|
|
4072
|
-
message:
|
|
4435
|
+
message: chalk12.yellow("\u26A0\uFE0F Repository accessible but could not retrieve details.\n\n") + chalk12.dim(`Repository: ${gitInfo.owner}/${gitInfo.name}
|
|
4073
4436
|
|
|
4074
|
-
`) +
|
|
4437
|
+
`) + chalk12.dim("Please try again or create a project manually at: ") + chalk12.cyan(webUrl)
|
|
4075
4438
|
};
|
|
4076
4439
|
}
|
|
4077
4440
|
console.log(
|
|
4078
|
-
|
|
4441
|
+
chalk12.yellow("\u26A0\uFE0F Repository accessible but no project exists.\n\n") + chalk12.dim(`Repository: ${gitInfo.owner}/${gitInfo.name}
|
|
4079
4442
|
`)
|
|
4080
4443
|
);
|
|
4081
4444
|
const shouldCreate = await confirm2({
|
|
@@ -4085,7 +4448,7 @@ async function promptToCreateProject(gitInfo, projectService, repositoryService)
|
|
|
4085
4448
|
if (!shouldCreate) {
|
|
4086
4449
|
return {
|
|
4087
4450
|
success: false,
|
|
4088
|
-
message:
|
|
4451
|
+
message: chalk12.dim("\nProject creation cancelled.\n\n") + chalk12.dim("Create a project at ") + chalk12.cyan(webUrl) + chalk12.dim(" and link it to this repository, or use:\n") + chalk12.cyan(
|
|
4089
4452
|
`braingrid project create --name "${gitInfo.name}" --repositories "${gitInfo.owner}/${gitInfo.name}"`
|
|
4090
4453
|
)
|
|
4091
4454
|
};
|
|
@@ -4096,9 +4459,9 @@ async function promptToCreateProject(gitInfo, projectService, repositoryService)
|
|
|
4096
4459
|
description: `Project for ${gitInfo.owner}/${gitInfo.name}`,
|
|
4097
4460
|
repository_id: repositoryId
|
|
4098
4461
|
});
|
|
4099
|
-
console.log(
|
|
4462
|
+
console.log(chalk12.green(`
|
|
4100
4463
|
\u2705 Created project ${project2.short_id}: ${project2.name}`));
|
|
4101
|
-
console.log(
|
|
4464
|
+
console.log(chalk12.green(`\u2705 Linked repository ${gitInfo.owner}/${gitInfo.name}
|
|
4102
4465
|
`));
|
|
4103
4466
|
return { success: true, message: "", data: project2 };
|
|
4104
4467
|
} catch (error) {
|
|
@@ -4112,18 +4475,18 @@ async function promptToGrantRepositoryAccess(gitInfo, webUrl, repositoryService,
|
|
|
4112
4475
|
if (!gitInfo.owner || !gitInfo.name) {
|
|
4113
4476
|
return {
|
|
4114
4477
|
success: false,
|
|
4115
|
-
message:
|
|
4478
|
+
message: chalk12.red("\u274C Repository information is incomplete")
|
|
4116
4479
|
};
|
|
4117
4480
|
}
|
|
4118
4481
|
const owner = gitInfo.owner;
|
|
4119
4482
|
const name = gitInfo.name;
|
|
4120
4483
|
console.log(
|
|
4121
|
-
|
|
4484
|
+
chalk12.yellow("\u26A0\uFE0F Repository found but BrainGrid needs access.\n\n") + chalk12.dim(`Repository: ${owner}/${name}
|
|
4122
4485
|
|
|
4123
|
-
`) +
|
|
4486
|
+
`) + chalk12.dim("Please grant BrainGrid access to this repository:\n") + chalk12.dim(" 1. Visit: ") + chalk12.cyan(`${webUrl}/integrations`) + chalk12.dim("\n") + chalk12.dim(
|
|
4124
4487
|
` 2. Click on your "${owner}" installation "Add/Remove" to grant BrainGrid access to your repository
|
|
4125
4488
|
`
|
|
4126
|
-
) +
|
|
4489
|
+
) + chalk12.dim(` 3. Select "${name}" and save
|
|
4127
4490
|
|
|
4128
4491
|
`)
|
|
4129
4492
|
);
|
|
@@ -4136,10 +4499,10 @@ async function promptToGrantRepositoryAccess(gitInfo, webUrl, repositoryService,
|
|
|
4136
4499
|
if (!accessGranted) {
|
|
4137
4500
|
return {
|
|
4138
4501
|
success: false,
|
|
4139
|
-
message:
|
|
4502
|
+
message: chalk12.yellow("\n\u26A0\uFE0F Repository access not detected within 3 minutes.\n\n") + chalk12.dim("Please grant access at: ") + chalk12.cyan(`${webUrl}/integrations`) + chalk12.dim(" and run ") + chalk12.cyan("braingrid init") + chalk12.dim(" again.")
|
|
4140
4503
|
};
|
|
4141
4504
|
}
|
|
4142
|
-
console.log(
|
|
4505
|
+
console.log(chalk12.green("\u2705 Repository access granted!\n"));
|
|
4143
4506
|
return promptToCreateProject(gitInfo, projectService, repositoryService);
|
|
4144
4507
|
}
|
|
4145
4508
|
async function handleNoProjectForRepository(owner, name, gitInfo, githubService, repositoryService, projectService, config2) {
|
|
@@ -4147,16 +4510,22 @@ async function handleNoProjectForRepository(owner, name, gitInfo, githubService,
|
|
|
4147
4510
|
try {
|
|
4148
4511
|
const installationsResponse = await githubService.listInstallations({ limit: 100 });
|
|
4149
4512
|
allInstallations = installationsResponse.installations;
|
|
4150
|
-
} catch {
|
|
4513
|
+
} catch (error) {
|
|
4514
|
+
log(
|
|
4515
|
+
"WARN",
|
|
4516
|
+
"init",
|
|
4517
|
+
"list_installations_failed",
|
|
4518
|
+
`error=${error instanceof Error ? error.message : String(error)}`
|
|
4519
|
+
);
|
|
4151
4520
|
allInstallations = [];
|
|
4152
4521
|
}
|
|
4153
4522
|
const webUrl = config2.getWebAppUrl();
|
|
4154
4523
|
if (allInstallations.length === 0) {
|
|
4155
4524
|
return {
|
|
4156
4525
|
success: false,
|
|
4157
|
-
message:
|
|
4526
|
+
message: chalk12.yellow("\u26A0\uFE0F No projects found for this repository.\n\n") + chalk12.dim(`Repository: ${owner}/${name}
|
|
4158
4527
|
|
|
4159
|
-
`) +
|
|
4528
|
+
`) + chalk12.dim("It looks like you haven't connected your GitHub account yet.\n") + chalk12.dim("Please connect GitHub at: ") + chalk12.cyan(`${webUrl}/integrations`) + chalk12.dim("\n\nOnce connected, create a project and link it to this repository.")
|
|
4160
4529
|
};
|
|
4161
4530
|
}
|
|
4162
4531
|
const ownerInstallation = findInstallationForOwner(owner, allInstallations);
|
|
@@ -4172,28 +4541,28 @@ async function handleNoProjectForRepository(owner, name, gitInfo, githubService,
|
|
|
4172
4541
|
function showSetupInstructions(scenario) {
|
|
4173
4542
|
let message = "";
|
|
4174
4543
|
if (scenario === "no-git") {
|
|
4175
|
-
message +=
|
|
4176
|
-
message +=
|
|
4177
|
-
message +=
|
|
4544
|
+
message += chalk12.dim("To initialize BrainGrid locally:\n\n");
|
|
4545
|
+
message += chalk12.dim(" 1. Initialize git:\n");
|
|
4546
|
+
message += chalk12.cyan(" git init\n\n");
|
|
4178
4547
|
} else {
|
|
4179
|
-
message +=
|
|
4548
|
+
message += chalk12.dim("To connect to GitHub:\n\n");
|
|
4180
4549
|
}
|
|
4181
|
-
message +=
|
|
4182
|
-
message +=
|
|
4183
|
-
message +=
|
|
4184
|
-
message +=
|
|
4550
|
+
message += chalk12.dim(" 2. Create GitHub repository:\n");
|
|
4551
|
+
message += chalk12.dim(" \u2022 Install GitHub CLI: ") + chalk12.cyan("https://cli.github.com\n");
|
|
4552
|
+
message += chalk12.dim(" Then: ") + chalk12.cyan("gh repo create --private --source=.\n");
|
|
4553
|
+
message += chalk12.dim(" \u2022 Or manually: ") + chalk12.cyan("https://github.com/new\n\n");
|
|
4185
4554
|
if (scenario === "no-git") {
|
|
4186
|
-
message +=
|
|
4555
|
+
message += chalk12.dim(" 3. Run: ") + chalk12.cyan("braingrid init\n\n");
|
|
4187
4556
|
} else {
|
|
4188
|
-
message +=
|
|
4189
|
-
message +=
|
|
4190
|
-
message +=
|
|
4557
|
+
message += chalk12.dim(" 3. Add remote and run init:\n");
|
|
4558
|
+
message += chalk12.cyan(" git remote add origin <url>\n");
|
|
4559
|
+
message += chalk12.cyan(" braingrid init\n\n");
|
|
4191
4560
|
}
|
|
4192
|
-
message +=
|
|
4193
|
-
message +=
|
|
4194
|
-
message +=
|
|
4195
|
-
message +=
|
|
4196
|
-
message += `${
|
|
4561
|
+
message += chalk12.bold("Or use BrainGrid without local initialization:\n\n");
|
|
4562
|
+
message += chalk12.dim(" All commands support the --project flag:\n");
|
|
4563
|
+
message += chalk12.cyan(" braingrid requirement list --project PROJ-123\n");
|
|
4564
|
+
message += chalk12.cyan(' braingrid task create --project PROJ-123 --title "Task"\n\n');
|
|
4565
|
+
message += `${chalk12.dim(" Note: Without local init, you must specify --project for each command.")}
|
|
4197
4566
|
`;
|
|
4198
4567
|
return message;
|
|
4199
4568
|
}
|
|
@@ -4210,10 +4579,10 @@ async function handleNoGitRepository() {
|
|
|
4210
4579
|
if (!gitInitSuccess) {
|
|
4211
4580
|
return {
|
|
4212
4581
|
success: false,
|
|
4213
|
-
message:
|
|
4582
|
+
message: chalk12.red("\u274C Failed to initialize git repository")
|
|
4214
4583
|
};
|
|
4215
4584
|
}
|
|
4216
|
-
console.log(
|
|
4585
|
+
console.log(chalk12.green("\u2705 Initialized git repository"));
|
|
4217
4586
|
const dirName = getCurrentDirectoryName();
|
|
4218
4587
|
const isPrivate = await select3({
|
|
4219
4588
|
message: "Repository visibility:",
|
|
@@ -4227,16 +4596,16 @@ async function handleNoGitRepository() {
|
|
|
4227
4596
|
message: "Repository name:",
|
|
4228
4597
|
default: dirName
|
|
4229
4598
|
});
|
|
4230
|
-
console.log(
|
|
4599
|
+
console.log(chalk12.dim("\nCreating repository...\n"));
|
|
4231
4600
|
const repo = await createGitHubRepoWithGh(repoName, isPrivate);
|
|
4232
4601
|
if (!repo) {
|
|
4233
4602
|
return {
|
|
4234
4603
|
success: false,
|
|
4235
|
-
message:
|
|
4604
|
+
message: chalk12.red("\u274C Failed to create GitHub repository\n\n") + showSetupInstructions("no-git")
|
|
4236
4605
|
};
|
|
4237
4606
|
}
|
|
4238
|
-
console.log(
|
|
4239
|
-
console.log(
|
|
4607
|
+
console.log(chalk12.green(`\u2705 Created repository: ${repo.url}`));
|
|
4608
|
+
console.log(chalk12.green("\u2705 Added remote origin\n"));
|
|
4240
4609
|
return {
|
|
4241
4610
|
success: true,
|
|
4242
4611
|
message: "continue-init",
|
|
@@ -4247,7 +4616,7 @@ async function handleNoGitRepository() {
|
|
|
4247
4616
|
}
|
|
4248
4617
|
return {
|
|
4249
4618
|
success: false,
|
|
4250
|
-
message:
|
|
4619
|
+
message: chalk12.yellow("\u26A0\uFE0F This directory is not a git repository.\n\n") + showSetupInstructions("no-git")
|
|
4251
4620
|
};
|
|
4252
4621
|
}
|
|
4253
4622
|
async function handleNoGitRemote() {
|
|
@@ -4271,16 +4640,16 @@ async function handleNoGitRemote() {
|
|
|
4271
4640
|
message: "Repository name:",
|
|
4272
4641
|
default: dirName
|
|
4273
4642
|
});
|
|
4274
|
-
console.log(
|
|
4643
|
+
console.log(chalk12.dim("\nCreating repository...\n"));
|
|
4275
4644
|
const repo = await createGitHubRepoWithGh(repoName, isPrivate);
|
|
4276
4645
|
if (!repo) {
|
|
4277
4646
|
return {
|
|
4278
4647
|
success: false,
|
|
4279
|
-
message:
|
|
4648
|
+
message: chalk12.red("\u274C Failed to create GitHub repository\n\n") + showSetupInstructions("no-remote")
|
|
4280
4649
|
};
|
|
4281
4650
|
}
|
|
4282
|
-
console.log(
|
|
4283
|
-
console.log(
|
|
4651
|
+
console.log(chalk12.green(`\u2705 Created repository: ${repo.url}`));
|
|
4652
|
+
console.log(chalk12.green("\u2705 Added remote origin\n"));
|
|
4284
4653
|
return {
|
|
4285
4654
|
success: true,
|
|
4286
4655
|
message: "continue-init",
|
|
@@ -4291,16 +4660,19 @@ async function handleNoGitRemote() {
|
|
|
4291
4660
|
}
|
|
4292
4661
|
return {
|
|
4293
4662
|
success: false,
|
|
4294
|
-
message:
|
|
4663
|
+
message: chalk12.yellow("\u26A0\uFE0F Git repository detected but no GitHub remote configured.\n\n") + showSetupInstructions("no-remote")
|
|
4295
4664
|
};
|
|
4296
4665
|
}
|
|
4297
4666
|
async function handleInit(opts) {
|
|
4298
4667
|
try {
|
|
4668
|
+
const initStartTime = Date.now();
|
|
4669
|
+
initLogger(".braingrid/temp/init-debug.log");
|
|
4670
|
+
log("INFO", "init", "start", `force=${opts.force} project=${opts.project || "auto-detect"}`);
|
|
4299
4671
|
const updateInfo = await isUpdateAvailable();
|
|
4300
4672
|
if (updateInfo.available && updateInfo.latestVersion) {
|
|
4301
4673
|
console.log(
|
|
4302
|
-
|
|
4303
|
-
\u26A0\uFE0F A new version of BrainGrid CLI is available: `) +
|
|
4674
|
+
chalk12.yellow(`
|
|
4675
|
+
\u26A0\uFE0F A new version of BrainGrid CLI is available: `) + chalk12.dim(`${updateInfo.currentVersion} \u2192 `) + chalk12.green(updateInfo.latestVersion) + "\n"
|
|
4304
4676
|
);
|
|
4305
4677
|
const shouldUpdate = await confirm2({
|
|
4306
4678
|
message: "Would you like to update now?",
|
|
@@ -4311,11 +4683,17 @@ async function handleInit(opts) {
|
|
|
4311
4683
|
console.log(result.message);
|
|
4312
4684
|
return {
|
|
4313
4685
|
success: true,
|
|
4314
|
-
message:
|
|
4686
|
+
message: chalk12.dim("\nRun `braingrid init` again after the update completes.")
|
|
4315
4687
|
};
|
|
4316
4688
|
}
|
|
4317
4689
|
console.log();
|
|
4318
4690
|
}
|
|
4691
|
+
log(
|
|
4692
|
+
"INFO",
|
|
4693
|
+
"init",
|
|
4694
|
+
"update_check",
|
|
4695
|
+
`available=${updateInfo.available} current=${updateInfo.currentVersion}${updateInfo.latestVersion ? ` latest=${updateInfo.latestVersion}` : ""}`
|
|
4696
|
+
);
|
|
4319
4697
|
const config2 = getConfig();
|
|
4320
4698
|
const { projectService, githubService, repositoryService, auth } = getServices();
|
|
4321
4699
|
if (!await isGitInstalled()) {
|
|
@@ -4326,7 +4704,7 @@ async function handleInit(opts) {
|
|
|
4326
4704
|
if (!shouldInstall) {
|
|
4327
4705
|
return {
|
|
4328
4706
|
success: false,
|
|
4329
|
-
message:
|
|
4707
|
+
message: chalk12.yellow("\u26A0\uFE0F Git installation cancelled.\n\n") + getManualInstallInstructions()
|
|
4330
4708
|
};
|
|
4331
4709
|
}
|
|
4332
4710
|
console.log();
|
|
@@ -4340,16 +4718,18 @@ async function handleInit(opts) {
|
|
|
4340
4718
|
console.log(installResult.message);
|
|
4341
4719
|
console.log();
|
|
4342
4720
|
if (!await isGitInstalled()) {
|
|
4721
|
+
log("ERROR", "init", "git_check", "installed=false post_install=true");
|
|
4343
4722
|
return {
|
|
4344
4723
|
success: false,
|
|
4345
|
-
message:
|
|
4724
|
+
message: chalk12.red("\u274C Git installation completed but git command not found\n\n") + chalk12.dim("You may need to restart your terminal or add Git to your PATH.\n") + getManualInstallInstructions()
|
|
4346
4725
|
};
|
|
4347
4726
|
}
|
|
4348
4727
|
}
|
|
4728
|
+
log("INFO", "init", "git_check", "installed=true");
|
|
4349
4729
|
if (!await isGhInstalled()) {
|
|
4350
|
-
console.log(
|
|
4730
|
+
console.log(chalk12.blue("\n\u{1F4A1} GitHub CLI is highly recommended for working with BrainGrid."));
|
|
4351
4731
|
console.log(
|
|
4352
|
-
|
|
4732
|
+
chalk12.dim(" It enables seamless GitHub integration and repository management.\n")
|
|
4353
4733
|
);
|
|
4354
4734
|
const shouldInstallGh = await confirm2({
|
|
4355
4735
|
message: "Would you like to install GitHub CLI now?",
|
|
@@ -4363,30 +4743,60 @@ async function handleInit(opts) {
|
|
|
4363
4743
|
console.log();
|
|
4364
4744
|
} else {
|
|
4365
4745
|
console.log(ghInstallResult.message);
|
|
4366
|
-
console.log(
|
|
4746
|
+
console.log(chalk12.dim("You can install it manually later.\n"));
|
|
4747
|
+
}
|
|
4748
|
+
} else {
|
|
4749
|
+
console.log(chalk12.dim("Skipping GitHub CLI installation.\n"));
|
|
4750
|
+
}
|
|
4751
|
+
}
|
|
4752
|
+
log("INFO", "init", "gh_check", `installed=${await isGhInstalled()}`);
|
|
4753
|
+
if (!await isJqInstalled()) {
|
|
4754
|
+
console.log(chalk12.blue("\n\u{1F4A1} jq is required for BrainGrid hooks to parse JSON data."));
|
|
4755
|
+
console.log(chalk12.dim(" All IDE integrations (Claude Code, Cursor) use jq.\n"));
|
|
4756
|
+
const shouldInstallJq = await confirm2({
|
|
4757
|
+
message: "Would you like to install jq now?",
|
|
4758
|
+
default: true
|
|
4759
|
+
});
|
|
4760
|
+
if (shouldInstallJq) {
|
|
4761
|
+
console.log();
|
|
4762
|
+
const jqInstallResult = await installJq();
|
|
4763
|
+
if (jqInstallResult.success) {
|
|
4764
|
+
console.log(jqInstallResult.message);
|
|
4765
|
+
console.log();
|
|
4766
|
+
} else {
|
|
4767
|
+
console.log(jqInstallResult.message);
|
|
4768
|
+
console.log(chalk12.dim("You can install it manually later.\n"));
|
|
4367
4769
|
}
|
|
4368
4770
|
} else {
|
|
4369
|
-
console.log(
|
|
4771
|
+
console.log(chalk12.dim("Skipping jq installation.\n"));
|
|
4370
4772
|
}
|
|
4371
4773
|
}
|
|
4774
|
+
log("INFO", "init", "jq_check", `installed=${await isJqInstalled()}`);
|
|
4372
4775
|
if (await projectConfigExists() && !opts.force) {
|
|
4373
4776
|
try {
|
|
4374
4777
|
const existing = await loadProjectConfig();
|
|
4375
4778
|
return {
|
|
4376
4779
|
success: false,
|
|
4377
|
-
message:
|
|
4378
|
-
`) +
|
|
4780
|
+
message: chalk12.yellow("\u26A0\uFE0F Already initialized.\n\n") + chalk12.dim(`Project: ${existing.project_name} (${existing.project_short_id})
|
|
4781
|
+
`) + chalk12.dim(`Repository: ${existing.repository?.full_name || "N/A"}
|
|
4379
4782
|
|
|
4380
|
-
`) +
|
|
4783
|
+
`) + chalk12.dim("Use --force to reinitialize")
|
|
4381
4784
|
};
|
|
4382
|
-
} catch {
|
|
4785
|
+
} catch (error) {
|
|
4786
|
+
log(
|
|
4787
|
+
"ERROR",
|
|
4788
|
+
"init",
|
|
4789
|
+
"config_load",
|
|
4790
|
+
`error=${error instanceof Error ? error.message : String(error)}`
|
|
4791
|
+
);
|
|
4383
4792
|
return {
|
|
4384
4793
|
success: false,
|
|
4385
|
-
message:
|
|
4794
|
+
message: chalk12.yellow("\u26A0\uFE0F Invalid project configuration found.\n") + chalk12.dim("Use --force to reinitialize")
|
|
4386
4795
|
};
|
|
4387
4796
|
}
|
|
4388
4797
|
}
|
|
4389
4798
|
const isAuthenticated = await auth.isAuthenticated();
|
|
4799
|
+
log("INFO", "init", "auth_check", `authenticated=${isAuthenticated}`);
|
|
4390
4800
|
if (!isAuthenticated) {
|
|
4391
4801
|
const shouldLogin = await confirm2({
|
|
4392
4802
|
message: "You need to be authenticated. Would you like to log in / sign up now?",
|
|
@@ -4395,21 +4805,24 @@ async function handleInit(opts) {
|
|
|
4395
4805
|
if (!shouldLogin) {
|
|
4396
4806
|
return {
|
|
4397
4807
|
success: false,
|
|
4398
|
-
message:
|
|
4808
|
+
message: chalk12.yellow("\u26A0\uFE0F Authentication required.\n") + chalk12.dim("Run ") + chalk12.cyan("braingrid login") + chalk12.dim(" when you're ready to authenticate.")
|
|
4399
4809
|
};
|
|
4400
4810
|
}
|
|
4811
|
+
log("WARN", "init", "login_required", "prompted=true");
|
|
4401
4812
|
console.log();
|
|
4402
4813
|
const loginResult = await handleLogin();
|
|
4403
4814
|
if (!loginResult.success) {
|
|
4815
|
+
log("ERROR", "init", "login_failed", "error=login handler returned failure");
|
|
4404
4816
|
return {
|
|
4405
4817
|
success: false,
|
|
4406
|
-
message:
|
|
4818
|
+
message: chalk12.red("\u274C Login failed.\n") + chalk12.dim("Please try running ") + chalk12.cyan("braingrid login") + chalk12.dim(" again.")
|
|
4407
4819
|
};
|
|
4408
4820
|
}
|
|
4409
4821
|
if (!await auth.isAuthenticated()) {
|
|
4822
|
+
log("ERROR", "init", "login_failed", "error=login not completed");
|
|
4410
4823
|
return {
|
|
4411
4824
|
success: false,
|
|
4412
|
-
message:
|
|
4825
|
+
message: chalk12.red("\u274C Login was not completed.\n") + chalk12.dim("Please try running ") + chalk12.cyan("braingrid login") + chalk12.dim(" again.")
|
|
4413
4826
|
};
|
|
4414
4827
|
}
|
|
4415
4828
|
console.log();
|
|
@@ -4418,39 +4831,45 @@ async function handleInit(opts) {
|
|
|
4418
4831
|
if (!session) {
|
|
4419
4832
|
return {
|
|
4420
4833
|
success: false,
|
|
4421
|
-
message:
|
|
4834
|
+
message: chalk12.red("\u274C No session found. Please run `braingrid login` first.")
|
|
4422
4835
|
};
|
|
4423
4836
|
}
|
|
4424
4837
|
if (session.organization_id === "default") {
|
|
4425
|
-
console.log(
|
|
4838
|
+
console.log(chalk12.yellow("\u26A0\uFE0F Organization ID not set, validating session...\n"));
|
|
4426
4839
|
const isValid = await auth.isAuthenticated();
|
|
4427
4840
|
if (!isValid) {
|
|
4428
4841
|
return {
|
|
4429
4842
|
success: false,
|
|
4430
|
-
message:
|
|
4843
|
+
message: chalk12.red("\u274C Session validation failed. Please run `braingrid login` again.")
|
|
4431
4844
|
};
|
|
4432
4845
|
}
|
|
4433
4846
|
const updatedSession = await auth.getStoredSession();
|
|
4434
4847
|
if (!updatedSession || updatedSession.organization_id === "default") {
|
|
4435
4848
|
return {
|
|
4436
4849
|
success: false,
|
|
4437
|
-
message:
|
|
4850
|
+
message: chalk12.red("\u274C Unable to retrieve organization information.\n\n") + chalk12.dim("This may indicate an issue with your account setup.\n") + chalk12.dim("Please contact support or try logging in again with ") + chalk12.cyan("braingrid logout") + chalk12.dim(" and ") + chalk12.cyan("braingrid login")
|
|
4438
4851
|
};
|
|
4439
4852
|
}
|
|
4440
4853
|
Object.assign(session, updatedSession);
|
|
4441
|
-
console.log(
|
|
4854
|
+
console.log(chalk12.green("\u2705 Organization ID updated successfully\n"));
|
|
4442
4855
|
}
|
|
4443
4856
|
let gitInfo = await getGitRepositoryInfo();
|
|
4444
4857
|
let project2;
|
|
4445
4858
|
if (opts.project) {
|
|
4446
4859
|
try {
|
|
4447
4860
|
project2 = await projectService.getProject(opts.project);
|
|
4448
|
-
} catch {
|
|
4861
|
+
} catch (error) {
|
|
4862
|
+
log(
|
|
4863
|
+
"ERROR",
|
|
4864
|
+
"init",
|
|
4865
|
+
"project_fetch",
|
|
4866
|
+
`project=${opts.project} error=${error instanceof Error ? error.message : String(error)}`
|
|
4867
|
+
);
|
|
4449
4868
|
return {
|
|
4450
4869
|
success: false,
|
|
4451
|
-
message:
|
|
4870
|
+
message: chalk12.red(`\u274C Project not found: ${opts.project}
|
|
4452
4871
|
|
|
4453
|
-
`) +
|
|
4872
|
+
`) + chalk12.dim("Make sure the project ID is correct and you have access to it.")
|
|
4454
4873
|
};
|
|
4455
4874
|
}
|
|
4456
4875
|
} else {
|
|
@@ -4461,7 +4880,7 @@ async function handleInit(opts) {
|
|
|
4461
4880
|
if (!gitInfo || !gitInfo.owner || !gitInfo.name) {
|
|
4462
4881
|
return {
|
|
4463
4882
|
success: false,
|
|
4464
|
-
message:
|
|
4883
|
+
message: chalk12.red("\u274C Failed to get repository information after setup")
|
|
4465
4884
|
};
|
|
4466
4885
|
}
|
|
4467
4886
|
} else {
|
|
@@ -4475,7 +4894,7 @@ async function handleInit(opts) {
|
|
|
4475
4894
|
if (!gitInfo || !gitInfo.owner || !gitInfo.name) {
|
|
4476
4895
|
return {
|
|
4477
4896
|
success: false,
|
|
4478
|
-
message:
|
|
4897
|
+
message: chalk12.red("\u274C Failed to get repository information after setup")
|
|
4479
4898
|
};
|
|
4480
4899
|
}
|
|
4481
4900
|
} else {
|
|
@@ -4485,7 +4904,7 @@ async function handleInit(opts) {
|
|
|
4485
4904
|
if (!gitInfo) {
|
|
4486
4905
|
return {
|
|
4487
4906
|
success: false,
|
|
4488
|
-
message:
|
|
4907
|
+
message: chalk12.red("\u274C Repository information is missing")
|
|
4489
4908
|
};
|
|
4490
4909
|
}
|
|
4491
4910
|
const owner = gitInfo.owner;
|
|
@@ -4493,7 +4912,7 @@ async function handleInit(opts) {
|
|
|
4493
4912
|
if (!owner || !name) {
|
|
4494
4913
|
return {
|
|
4495
4914
|
success: false,
|
|
4496
|
-
message:
|
|
4915
|
+
message: chalk12.red("\u274C Repository information is incomplete")
|
|
4497
4916
|
};
|
|
4498
4917
|
}
|
|
4499
4918
|
let response;
|
|
@@ -4532,8 +4951,9 @@ async function handleInit(opts) {
|
|
|
4532
4951
|
}
|
|
4533
4952
|
project2 = response.projects[0];
|
|
4534
4953
|
}
|
|
4535
|
-
|
|
4536
|
-
|
|
4954
|
+
log("INFO", "init", "project_found", `id=${project2.short_id} name=${project2.name}`);
|
|
4955
|
+
const projectInfo = chalk12.bold("\n\u{1F4E6} BrainGrid Project Found\n\n") + chalk12.dim("Project: ") + chalk12.cyan(project2.name) + "\n" + chalk12.dim("ID: ") + chalk12.gray(project2.short_id) + "\n" + (project2.description ? `${chalk12.dim("Description: ") + chalk12.gray(project2.description)}
|
|
4956
|
+
` : "") + chalk12.dim("Repository: ") + chalk12.gray(project2.repository?.full_name || "N/A") + "\n\n";
|
|
4537
4957
|
console.log(projectInfo);
|
|
4538
4958
|
if (!opts.force) {
|
|
4539
4959
|
const shouldInit = await confirm2({
|
|
@@ -4543,7 +4963,7 @@ async function handleInit(opts) {
|
|
|
4543
4963
|
if (!shouldInit) {
|
|
4544
4964
|
return {
|
|
4545
4965
|
success: false,
|
|
4546
|
-
message:
|
|
4966
|
+
message: chalk12.yellow("Initialization cancelled.")
|
|
4547
4967
|
};
|
|
4548
4968
|
}
|
|
4549
4969
|
}
|
|
@@ -4565,12 +4985,19 @@ async function handleInit(opts) {
|
|
|
4565
4985
|
created_at: project2.created_at
|
|
4566
4986
|
};
|
|
4567
4987
|
await saveProjectConfig(localConfig);
|
|
4988
|
+
log("INFO", "init", "config_saved", "path=.braingrid/project.json");
|
|
4568
4989
|
await addBraingridTempToGitignore();
|
|
4569
4990
|
await copyBraingridReadme();
|
|
4570
4991
|
console.log(
|
|
4571
|
-
|
|
4992
|
+
chalk12.green("\u2705 Repository initialized successfully!\n\n") + chalk12.dim("Project: ") + chalk12.cyan(project2.name) + chalk12.dim(` (${project2.short_id})`) + "\n" + chalk12.dim("Config: ") + chalk12.gray(".braingrid/project.json") + "\n"
|
|
4572
4993
|
);
|
|
4573
4994
|
const installedIDEs = await detectInstalledIDEs();
|
|
4995
|
+
log(
|
|
4996
|
+
"INFO",
|
|
4997
|
+
"init",
|
|
4998
|
+
"ide_detect",
|
|
4999
|
+
`claude_code=${installedIDEs.claudeCode} cursor=${installedIDEs.cursor}`
|
|
5000
|
+
);
|
|
4574
5001
|
if (installedIDEs.claudeCode) {
|
|
4575
5002
|
const claudeSetupExists = await fileExists2(".claude/commands/specify.md");
|
|
4576
5003
|
console.log("");
|
|
@@ -4579,6 +5006,7 @@ async function handleInit(opts) {
|
|
|
4579
5006
|
default: true
|
|
4580
5007
|
});
|
|
4581
5008
|
if (setupClaude) {
|
|
5009
|
+
log("INFO", "init", "setup_claude", `force=${claudeSetupExists}`);
|
|
4582
5010
|
console.log("");
|
|
4583
5011
|
try {
|
|
4584
5012
|
const result = await handleSetupClaudeCode({
|
|
@@ -4587,15 +5015,21 @@ async function handleInit(opts) {
|
|
|
4587
5015
|
if (result.success) {
|
|
4588
5016
|
console.log(result.message);
|
|
4589
5017
|
} else {
|
|
4590
|
-
console.log(
|
|
5018
|
+
console.log(chalk12.yellow("\u26A0\uFE0F Claude Code setup was not completed."));
|
|
4591
5019
|
console.log(
|
|
4592
|
-
|
|
5020
|
+
chalk12.dim("You can run ") + chalk12.cyan("braingrid setup claude-code") + chalk12.dim(" later.")
|
|
4593
5021
|
);
|
|
4594
5022
|
}
|
|
4595
|
-
} catch {
|
|
4596
|
-
|
|
5023
|
+
} catch (error) {
|
|
5024
|
+
log(
|
|
5025
|
+
"ERROR",
|
|
5026
|
+
"init",
|
|
5027
|
+
"setup_claude_failed",
|
|
5028
|
+
`error=${error instanceof Error ? error.message : String(error)}`
|
|
5029
|
+
);
|
|
5030
|
+
console.log(chalk12.yellow("\u26A0\uFE0F Claude Code setup encountered an error."));
|
|
4597
5031
|
console.log(
|
|
4598
|
-
|
|
5032
|
+
chalk12.dim("You can run ") + chalk12.cyan("braingrid setup claude-code") + chalk12.dim(" later.")
|
|
4599
5033
|
);
|
|
4600
5034
|
}
|
|
4601
5035
|
console.log("");
|
|
@@ -4609,6 +5043,7 @@ async function handleInit(opts) {
|
|
|
4609
5043
|
default: true
|
|
4610
5044
|
});
|
|
4611
5045
|
if (setupCursor) {
|
|
5046
|
+
log("INFO", "init", "setup_cursor", `force=${cursorSetupExists}`);
|
|
4612
5047
|
console.log("");
|
|
4613
5048
|
try {
|
|
4614
5049
|
const result = await handleSetupCursor({
|
|
@@ -4617,28 +5052,44 @@ async function handleInit(opts) {
|
|
|
4617
5052
|
if (result.success) {
|
|
4618
5053
|
console.log(result.message);
|
|
4619
5054
|
} else {
|
|
4620
|
-
console.log(
|
|
5055
|
+
console.log(chalk12.yellow("\u26A0\uFE0F Cursor setup was not completed."));
|
|
4621
5056
|
console.log(
|
|
4622
|
-
|
|
5057
|
+
chalk12.dim("You can run ") + chalk12.cyan("braingrid setup cursor") + chalk12.dim(" later.")
|
|
4623
5058
|
);
|
|
4624
5059
|
}
|
|
4625
|
-
} catch {
|
|
4626
|
-
|
|
5060
|
+
} catch (error) {
|
|
5061
|
+
log(
|
|
5062
|
+
"ERROR",
|
|
5063
|
+
"init",
|
|
5064
|
+
"setup_cursor_failed",
|
|
5065
|
+
`error=${error instanceof Error ? error.message : String(error)}`
|
|
5066
|
+
);
|
|
5067
|
+
console.log(chalk12.yellow("\u26A0\uFE0F Cursor setup encountered an error."));
|
|
4627
5068
|
console.log(
|
|
4628
|
-
|
|
5069
|
+
chalk12.dim("You can run ") + chalk12.cyan("braingrid setup cursor") + chalk12.dim(" later.")
|
|
4629
5070
|
);
|
|
4630
5071
|
}
|
|
4631
5072
|
console.log("");
|
|
4632
5073
|
}
|
|
4633
5074
|
}
|
|
5075
|
+
const initDuration = Math.round((Date.now() - initStartTime) / 1e3);
|
|
5076
|
+
log("INFO", "init", "complete", `project=${project2.short_id} duration=${initDuration}s`);
|
|
5077
|
+
await flushLogger();
|
|
5078
|
+
closeLogger();
|
|
4634
5079
|
return {
|
|
4635
5080
|
success: true,
|
|
4636
|
-
message:
|
|
4637
|
-
"You can now use project-scoped commands without specifying a project ID."
|
|
4638
|
-
),
|
|
5081
|
+
message: chalk12.dim("You can now use project-scoped commands without specifying a project ID.") + "\n" + chalk12.dim("Debug log: .braingrid/temp/init-debug.log"),
|
|
4639
5082
|
data: localConfig
|
|
4640
5083
|
};
|
|
4641
5084
|
} catch (error) {
|
|
5085
|
+
log(
|
|
5086
|
+
"ERROR",
|
|
5087
|
+
"init",
|
|
5088
|
+
"fatal",
|
|
5089
|
+
`error=${error instanceof Error ? error.message : String(error)}`
|
|
5090
|
+
);
|
|
5091
|
+
await flushLogger();
|
|
5092
|
+
closeLogger();
|
|
4642
5093
|
return {
|
|
4643
5094
|
success: false,
|
|
4644
5095
|
message: formatError(error, "initializing repository")
|
|
@@ -4647,10 +5098,10 @@ async function handleInit(opts) {
|
|
|
4647
5098
|
}
|
|
4648
5099
|
|
|
4649
5100
|
// src/handlers/project.handlers.ts
|
|
4650
|
-
import
|
|
5101
|
+
import chalk15 from "chalk";
|
|
4651
5102
|
|
|
4652
5103
|
// src/utils/formatting.ts
|
|
4653
|
-
import
|
|
5104
|
+
import chalk13 from "chalk";
|
|
4654
5105
|
|
|
4655
5106
|
// src/utils/id-normalization.ts
|
|
4656
5107
|
function normalizeId(prefix, input2) {
|
|
@@ -4692,7 +5143,7 @@ function getWebUiUrl(apiUrl) {
|
|
|
4692
5143
|
}
|
|
4693
5144
|
function formatTasksListOutput(tasks, format, verbose, options) {
|
|
4694
5145
|
if (tasks.length === 0) {
|
|
4695
|
-
return
|
|
5146
|
+
return chalk13.yellow("No tasks found.");
|
|
4696
5147
|
}
|
|
4697
5148
|
switch (format) {
|
|
4698
5149
|
case "json":
|
|
@@ -4722,7 +5173,7 @@ function formatTasksListOutput(tasks, format, verbose, options) {
|
|
|
4722
5173
|
}
|
|
4723
5174
|
}
|
|
4724
5175
|
function formatTasksListTable(tasks) {
|
|
4725
|
-
let output =
|
|
5176
|
+
let output = chalk13.bold(`\u{1F4CB} Tasks (${tasks.length})
|
|
4726
5177
|
|
|
4727
5178
|
`);
|
|
4728
5179
|
output += "ID Short ID Status Title Assigned To Blocked\n";
|
|
@@ -4742,58 +5193,58 @@ function formatTasksListTable(tasks) {
|
|
|
4742
5193
|
return output;
|
|
4743
5194
|
}
|
|
4744
5195
|
function formatTasksListVerbose(tasks, options) {
|
|
4745
|
-
let output =
|
|
5196
|
+
let output = chalk13.bold(`\u{1F4CB} Tasks (${tasks.length})
|
|
4746
5197
|
|
|
4747
5198
|
`);
|
|
4748
|
-
const divider =
|
|
5199
|
+
const divider = chalk13.gray("\u2500".repeat(50));
|
|
4749
5200
|
for (let i = 0; i < tasks.length; i++) {
|
|
4750
5201
|
const task2 = tasks[i];
|
|
4751
5202
|
const statusEmoji = getTaskStatusEmoji(task2.status);
|
|
4752
|
-
output += `${statusEmoji} ${
|
|
5203
|
+
output += `${statusEmoji} ${chalk13.bold(task2.title)}
|
|
4753
5204
|
`;
|
|
4754
|
-
output += `${
|
|
5205
|
+
output += `${chalk13.bold("Short ID:")} TASK-${task2.number}
|
|
4755
5206
|
`;
|
|
4756
|
-
output += `${
|
|
5207
|
+
output += `${chalk13.bold("ID:")} ${task2.id}
|
|
4757
5208
|
`;
|
|
4758
5209
|
if (options?.apiUrl && options?.requirementId) {
|
|
4759
5210
|
const webUiUrl = getWebUiUrl(options.apiUrl);
|
|
4760
|
-
output += `${
|
|
5211
|
+
output += `${chalk13.bold("URL:")} ${webUiUrl}/requirements/overview?id=${options.requirementId}&tab=tasks
|
|
4761
5212
|
`;
|
|
4762
5213
|
}
|
|
4763
|
-
output += `${
|
|
5214
|
+
output += `${chalk13.bold("Project:")} ${options?.projectShortId || "N/A"}
|
|
4764
5215
|
`;
|
|
4765
|
-
output += `${
|
|
5216
|
+
output += `${chalk13.bold("Requirement:")} ${options?.requirementShortId || "N/A"}
|
|
4766
5217
|
`;
|
|
4767
|
-
output += `${
|
|
5218
|
+
output += `${chalk13.bold("Status:")} ${task2.status}
|
|
4768
5219
|
`;
|
|
4769
5220
|
if (task2.assigned_to) {
|
|
4770
|
-
output += `${
|
|
5221
|
+
output += `${chalk13.bold("Assigned to:")} ${task2.assigned_to}
|
|
4771
5222
|
`;
|
|
4772
5223
|
} else {
|
|
4773
|
-
output += `${
|
|
5224
|
+
output += `${chalk13.bold("Assigned to:")} Unassigned
|
|
4774
5225
|
`;
|
|
4775
5226
|
}
|
|
4776
5227
|
if (task2.created_at) {
|
|
4777
|
-
output += `${
|
|
5228
|
+
output += `${chalk13.bold("Created:")} ${new Date(task2.created_at).toLocaleString()}
|
|
4778
5229
|
`;
|
|
4779
5230
|
}
|
|
4780
5231
|
if (task2.updated_at) {
|
|
4781
|
-
output += `${
|
|
5232
|
+
output += `${chalk13.bold("Updated:")} ${new Date(task2.updated_at).toLocaleString()}
|
|
4782
5233
|
`;
|
|
4783
5234
|
}
|
|
4784
5235
|
if (task2.started_at) {
|
|
4785
|
-
output += `${
|
|
5236
|
+
output += `${chalk13.bold("Started:")} ${new Date(task2.started_at).toLocaleString()}
|
|
4786
5237
|
`;
|
|
4787
5238
|
}
|
|
4788
5239
|
if (task2.finished_at) {
|
|
4789
|
-
output += `${
|
|
5240
|
+
output += `${chalk13.bold("Finished:")} ${new Date(task2.finished_at).toLocaleString()}
|
|
4790
5241
|
`;
|
|
4791
5242
|
}
|
|
4792
5243
|
if (task2.content) {
|
|
4793
5244
|
output += `
|
|
4794
5245
|
${divider}
|
|
4795
5246
|
`;
|
|
4796
|
-
output += `${
|
|
5247
|
+
output += `${chalk13.bold("Content:")}
|
|
4797
5248
|
${task2.content}
|
|
4798
5249
|
`;
|
|
4799
5250
|
output += `${divider}
|
|
@@ -4801,12 +5252,12 @@ ${task2.content}
|
|
|
4801
5252
|
}
|
|
4802
5253
|
if (task2.blocked_by && task2.blocked_by.length > 0) {
|
|
4803
5254
|
output += `
|
|
4804
|
-
${
|
|
5255
|
+
${chalk13.bold("Blocked by:")} ${task2.blocked_by.join(", ")}
|
|
4805
5256
|
`;
|
|
4806
5257
|
}
|
|
4807
5258
|
if (i < tasks.length - 1) {
|
|
4808
5259
|
output += `
|
|
4809
|
-
${
|
|
5260
|
+
${chalk13.gray("\u2550".repeat(50))}
|
|
4810
5261
|
|
|
4811
5262
|
`;
|
|
4812
5263
|
}
|
|
@@ -5019,57 +5470,57 @@ function getRequirementStatusEmoji(status) {
|
|
|
5019
5470
|
}
|
|
5020
5471
|
function formatRequirementOutput(requirement2, options) {
|
|
5021
5472
|
const statusEmoji = getRequirementStatusEmoji(requirement2.status);
|
|
5022
|
-
const divider =
|
|
5473
|
+
const divider = chalk13.gray("\u2500".repeat(50));
|
|
5023
5474
|
let message = "";
|
|
5024
5475
|
if (options?.successMessage) {
|
|
5025
|
-
message +=
|
|
5476
|
+
message += chalk13.green(`\u2705 ${options.successMessage}
|
|
5026
5477
|
|
|
5027
5478
|
`);
|
|
5028
5479
|
}
|
|
5029
|
-
message += `${statusEmoji} ${
|
|
5480
|
+
message += `${statusEmoji} ${chalk13.bold(requirement2.name)}
|
|
5030
5481
|
|
|
5031
5482
|
`;
|
|
5032
|
-
message += `${
|
|
5483
|
+
message += `${chalk13.bold("Short ID:")} ${requirement2.short_id || "N/A"}
|
|
5033
5484
|
`;
|
|
5034
|
-
message += `${
|
|
5485
|
+
message += `${chalk13.bold("ID:")} ${requirement2.id}
|
|
5035
5486
|
`;
|
|
5036
5487
|
if (options?.apiUrl) {
|
|
5037
5488
|
const webUiUrl = getWebUiUrl(options.apiUrl);
|
|
5038
|
-
message += `${
|
|
5489
|
+
message += `${chalk13.bold("URL:")} ${webUiUrl}/requirements/overview?id=${requirement2.id}
|
|
5039
5490
|
`;
|
|
5040
5491
|
}
|
|
5041
|
-
message += `${
|
|
5492
|
+
message += `${chalk13.bold("Project:")} ${options?.projectShortId || "N/A"}
|
|
5042
5493
|
`;
|
|
5043
5494
|
if (requirement2.branch) {
|
|
5044
|
-
message += `${
|
|
5495
|
+
message += `${chalk13.bold("Branch:")} ${requirement2.branch}
|
|
5045
5496
|
`;
|
|
5046
5497
|
}
|
|
5047
|
-
message += `${
|
|
5498
|
+
message += `${chalk13.bold("Status:")} ${requirement2.status}
|
|
5048
5499
|
`;
|
|
5049
5500
|
if (requirement2.tags && requirement2.tags.length > 0) {
|
|
5050
5501
|
const tagNames = requirement2.tags.map((tag) => tag.name).join(", ");
|
|
5051
|
-
message += `${
|
|
5502
|
+
message += `${chalk13.bold("Tags:")} ${tagNames}
|
|
5052
5503
|
`;
|
|
5053
5504
|
}
|
|
5054
5505
|
if (requirement2.assignee) {
|
|
5055
5506
|
const assigneeName = requirement2.assignee.first_name || requirement2.assignee.last_name ? `${requirement2.assignee.first_name || ""} ${requirement2.assignee.last_name || ""}`.trim() : requirement2.assignee.email;
|
|
5056
|
-
message += `${
|
|
5507
|
+
message += `${chalk13.bold("Assigned to:")} ${assigneeName} (${requirement2.assignee.email})
|
|
5057
5508
|
`;
|
|
5058
5509
|
} else {
|
|
5059
|
-
message += `${
|
|
5510
|
+
message += `${chalk13.bold("Assigned to:")} Unassigned
|
|
5060
5511
|
`;
|
|
5061
5512
|
}
|
|
5062
|
-
message += `${
|
|
5513
|
+
message += `${chalk13.bold("Created:")} ${new Date(requirement2.created_at).toLocaleString()}
|
|
5063
5514
|
`;
|
|
5064
5515
|
if (options?.showUpdated) {
|
|
5065
|
-
message += `${
|
|
5516
|
+
message += `${chalk13.bold("Updated:")} ${new Date(requirement2.updated_at).toLocaleString()}
|
|
5066
5517
|
`;
|
|
5067
5518
|
}
|
|
5068
5519
|
if (options?.showDescription && requirement2.description) {
|
|
5069
5520
|
message += `
|
|
5070
5521
|
${divider}
|
|
5071
5522
|
`;
|
|
5072
|
-
message += `${
|
|
5523
|
+
message += `${chalk13.bold("Description:")}
|
|
5073
5524
|
${requirement2.description}
|
|
5074
5525
|
`;
|
|
5075
5526
|
message += `${divider}
|
|
@@ -5079,7 +5530,7 @@ ${requirement2.description}
|
|
|
5079
5530
|
message += `
|
|
5080
5531
|
${divider}
|
|
5081
5532
|
`;
|
|
5082
|
-
message += `${
|
|
5533
|
+
message += `${chalk13.bold("Content:")}
|
|
5083
5534
|
${requirement2.content}
|
|
5084
5535
|
`;
|
|
5085
5536
|
message += `${divider}
|
|
@@ -5087,7 +5538,7 @@ ${requirement2.content}
|
|
|
5087
5538
|
}
|
|
5088
5539
|
if (options?.showTaskList) {
|
|
5089
5540
|
message += `
|
|
5090
|
-
${
|
|
5541
|
+
${chalk13.bold("Tasks:")}
|
|
5091
5542
|
`;
|
|
5092
5543
|
if (requirement2.tasks && requirement2.tasks.length > 0) {
|
|
5093
5544
|
for (const task2 of requirement2.tasks) {
|
|
@@ -5103,7 +5554,7 @@ ${chalk12.bold("Tasks:")}
|
|
|
5103
5554
|
const completedTasks = tasks.filter((task2) => task2.status === "COMPLETED").length;
|
|
5104
5555
|
const totalTasks = tasks.length;
|
|
5105
5556
|
message += `
|
|
5106
|
-
${
|
|
5557
|
+
${chalk13.bold("Tasks:")} ${completedTasks}/${totalTasks} completed
|
|
5107
5558
|
`;
|
|
5108
5559
|
message += `${divider}
|
|
5109
5560
|
`;
|
|
@@ -5126,59 +5577,59 @@ function getTaskStatusEmoji(status) {
|
|
|
5126
5577
|
}
|
|
5127
5578
|
function formatTaskOutput(task2, options) {
|
|
5128
5579
|
const statusEmoji = getTaskStatusEmoji(task2.status);
|
|
5129
|
-
const divider =
|
|
5580
|
+
const divider = chalk13.gray("\u2500".repeat(50));
|
|
5130
5581
|
let message = "";
|
|
5131
5582
|
if (options?.successMessage) {
|
|
5132
|
-
message +=
|
|
5583
|
+
message += chalk13.green(`\u2705 ${options.successMessage}
|
|
5133
5584
|
|
|
5134
5585
|
`);
|
|
5135
5586
|
}
|
|
5136
|
-
message += `${statusEmoji} ${
|
|
5587
|
+
message += `${statusEmoji} ${chalk13.bold(task2.title)}
|
|
5137
5588
|
|
|
5138
5589
|
`;
|
|
5139
|
-
message += `${
|
|
5590
|
+
message += `${chalk13.bold("Short ID:")} TASK-${task2.number}
|
|
5140
5591
|
`;
|
|
5141
|
-
message += `${
|
|
5592
|
+
message += `${chalk13.bold("ID:")} ${task2.id}
|
|
5142
5593
|
`;
|
|
5143
5594
|
if (options?.apiUrl && options?.requirementId) {
|
|
5144
5595
|
const webUiUrl = getWebUiUrl(options.apiUrl);
|
|
5145
|
-
message += `${
|
|
5596
|
+
message += `${chalk13.bold("URL:")} ${webUiUrl}/requirements/overview?id=${options.requirementId}&tab=tasks
|
|
5146
5597
|
`;
|
|
5147
5598
|
}
|
|
5148
|
-
message += `${
|
|
5599
|
+
message += `${chalk13.bold("Project:")} ${options?.projectShortId || "N/A"}
|
|
5149
5600
|
`;
|
|
5150
|
-
message += `${
|
|
5601
|
+
message += `${chalk13.bold("Requirement:")} ${options?.requirementShortId || "N/A"}
|
|
5151
5602
|
`;
|
|
5152
|
-
message += `${
|
|
5603
|
+
message += `${chalk13.bold("Status:")} ${task2.status}
|
|
5153
5604
|
`;
|
|
5154
5605
|
if (task2.assigned_to) {
|
|
5155
|
-
message += `${
|
|
5606
|
+
message += `${chalk13.bold("Assigned to:")} ${task2.assigned_to}
|
|
5156
5607
|
`;
|
|
5157
5608
|
} else {
|
|
5158
|
-
message += `${
|
|
5609
|
+
message += `${chalk13.bold("Assigned to:")} Unassigned
|
|
5159
5610
|
`;
|
|
5160
5611
|
}
|
|
5161
5612
|
if (task2.created_at) {
|
|
5162
|
-
message += `${
|
|
5613
|
+
message += `${chalk13.bold("Created:")} ${new Date(task2.created_at).toLocaleString()}
|
|
5163
5614
|
`;
|
|
5164
5615
|
}
|
|
5165
5616
|
if (task2.updated_at) {
|
|
5166
|
-
message += `${
|
|
5617
|
+
message += `${chalk13.bold("Updated:")} ${new Date(task2.updated_at).toLocaleString()}
|
|
5167
5618
|
`;
|
|
5168
5619
|
}
|
|
5169
5620
|
if (task2.started_at) {
|
|
5170
|
-
message += `${
|
|
5621
|
+
message += `${chalk13.bold("Started:")} ${new Date(task2.started_at).toLocaleString()}
|
|
5171
5622
|
`;
|
|
5172
5623
|
}
|
|
5173
5624
|
if (task2.finished_at) {
|
|
5174
|
-
message += `${
|
|
5625
|
+
message += `${chalk13.bold("Finished:")} ${new Date(task2.finished_at).toLocaleString()}
|
|
5175
5626
|
`;
|
|
5176
5627
|
}
|
|
5177
5628
|
if (options?.showContent && task2.content) {
|
|
5178
5629
|
message += `
|
|
5179
5630
|
${divider}
|
|
5180
5631
|
`;
|
|
5181
|
-
message += `${
|
|
5632
|
+
message += `${chalk13.bold("Content:")}
|
|
5182
5633
|
${task2.content}
|
|
5183
5634
|
`;
|
|
5184
5635
|
message += `${divider}
|
|
@@ -5186,7 +5637,7 @@ ${task2.content}
|
|
|
5186
5637
|
}
|
|
5187
5638
|
if (task2.blocked_by && task2.blocked_by.length > 0) {
|
|
5188
5639
|
message += `
|
|
5189
|
-
${
|
|
5640
|
+
${chalk13.bold("Blocked by:")} ${task2.blocked_by.join(", ")}
|
|
5190
5641
|
`;
|
|
5191
5642
|
}
|
|
5192
5643
|
return message;
|
|
@@ -5769,9 +6220,9 @@ function formatProjectShowXml(project2) {
|
|
|
5769
6220
|
}
|
|
5770
6221
|
function formatRequirementTreeView(requirements, tasksMap) {
|
|
5771
6222
|
if (requirements.length === 0) {
|
|
5772
|
-
return
|
|
6223
|
+
return chalk13.yellow("No requirements found.\n");
|
|
5773
6224
|
}
|
|
5774
|
-
let output =
|
|
6225
|
+
let output = chalk13.bold.cyan(`\u{1F4CB} Requirements (Tree View)
|
|
5775
6226
|
|
|
5776
6227
|
`);
|
|
5777
6228
|
for (let i = 0; i < requirements.length; i++) {
|
|
@@ -5784,12 +6235,12 @@ function formatRequirementTreeView(requirements, tasksMap) {
|
|
|
5784
6235
|
if (shortId.startsWith("REQ-REQ-")) {
|
|
5785
6236
|
shortId = shortId.replace("REQ-REQ-", "REQ-");
|
|
5786
6237
|
}
|
|
5787
|
-
const branchInfo = req.branch ?
|
|
5788
|
-
output += `${reqPrefix}${
|
|
6238
|
+
const branchInfo = req.branch ? chalk13.gray(` (branch: ${req.branch})`) : "";
|
|
6239
|
+
output += `${reqPrefix}${chalk13.bold(shortId)} ${reqStatusEmoji} ${req.name}${branchInfo}
|
|
5789
6240
|
`;
|
|
5790
6241
|
const tasks = tasksMap.get(req.id) || [];
|
|
5791
6242
|
if (tasks.length === 0) {
|
|
5792
|
-
output += `${childPrefix}\u2514\u2500\u2500 ${
|
|
6243
|
+
output += `${childPrefix}\u2514\u2500\u2500 ${chalk13.gray("(no tasks yet)")}
|
|
5793
6244
|
`;
|
|
5794
6245
|
} else {
|
|
5795
6246
|
for (let j = 0; j < tasks.length; j++) {
|
|
@@ -5797,7 +6248,7 @@ function formatRequirementTreeView(requirements, tasksMap) {
|
|
|
5797
6248
|
const isLastTask = j === tasks.length - 1;
|
|
5798
6249
|
const taskPrefix = isLastTask ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
|
|
5799
6250
|
const taskStatusEmoji = getRequirementStatusEmoji(task2.status);
|
|
5800
|
-
const blockedInfo = task2.blocked_by && task2.blocked_by.length > 0 ?
|
|
6251
|
+
const blockedInfo = task2.blocked_by && task2.blocked_by.length > 0 ? chalk13.yellow(` [Blocked by: ${task2.blocked_by.length}]`) : "";
|
|
5801
6252
|
output += `${childPrefix}${taskPrefix}${task2.number} ${taskStatusEmoji} ${task2.title}${blockedInfo}
|
|
5802
6253
|
`;
|
|
5803
6254
|
}
|
|
@@ -5812,7 +6263,7 @@ function parseProjectId(input2) {
|
|
|
5812
6263
|
}
|
|
5813
6264
|
|
|
5814
6265
|
// src/utils/workspace-manager.ts
|
|
5815
|
-
import
|
|
6266
|
+
import chalk14 from "chalk";
|
|
5816
6267
|
var WorkspaceManager = class {
|
|
5817
6268
|
constructor() {
|
|
5818
6269
|
this.activeRequirementId = null;
|
|
@@ -5852,13 +6303,13 @@ var WorkspaceManager = class {
|
|
|
5852
6303
|
const msg = error instanceof Error ? error.message : String(error);
|
|
5853
6304
|
return {
|
|
5854
6305
|
success: false,
|
|
5855
|
-
error:
|
|
6306
|
+
error: chalk14.red("\u274C Found .braingrid/project.json but failed to load it:") + "\n" + chalk14.dim(` ${msg}`) + "\n\n" + chalk14.dim("Try re-initializing with:\n") + chalk14.cyan(" braingrid init --force")
|
|
5856
6307
|
};
|
|
5857
6308
|
}
|
|
5858
6309
|
}
|
|
5859
6310
|
return {
|
|
5860
6311
|
success: false,
|
|
5861
|
-
error:
|
|
6312
|
+
error: chalk14.red("\u274C No project specified and no local project found.") + "\n\n" + chalk14.dim("To initialize this workspace, run:\n") + chalk14.cyan(" braingrid init") + "\n\n" + chalk14.dim("Or specify a project explicitly:\n") + chalk14.cyan(" braingrid <command> -p PROJ-123")
|
|
5862
6313
|
};
|
|
5863
6314
|
}
|
|
5864
6315
|
/**
|
|
@@ -5892,9 +6343,9 @@ var WorkspaceManager = class {
|
|
|
5892
6343
|
}
|
|
5893
6344
|
return {
|
|
5894
6345
|
success: false,
|
|
5895
|
-
error:
|
|
6346
|
+
error: chalk14.red("\u274C No requirement specified.") + "\n\n" + chalk14.dim("Unable to auto-detect requirement from branch name.\n") + chalk14.dim("Please provide a requirement ID explicitly:\n") + chalk14.cyan(" braingrid requirement show REQ-123") + "\n" + chalk14.dim(" or\n") + chalk14.cyan(" braingrid task list -r REQ-123") + "\n\n" + chalk14.dim(
|
|
5896
6347
|
"Tip: Name your branch with a requirement ID (e.g., feature/REQ-123-description)\n"
|
|
5897
|
-
) +
|
|
6348
|
+
) + chalk14.dim("to enable auto-detection.")
|
|
5898
6349
|
};
|
|
5899
6350
|
}
|
|
5900
6351
|
/**
|
|
@@ -5953,19 +6404,19 @@ async function handleProjectList(opts) {
|
|
|
5953
6404
|
if (!isAuthenticated) {
|
|
5954
6405
|
return {
|
|
5955
6406
|
success: false,
|
|
5956
|
-
message:
|
|
6407
|
+
message: chalk15.red("\u274C Not authenticated. Please run `braingrid login` first.")
|
|
5957
6408
|
};
|
|
5958
6409
|
}
|
|
5959
6410
|
const format = opts.format || "table";
|
|
5960
6411
|
if (!["table", "json", "xml", "markdown"].includes(format)) {
|
|
5961
6412
|
return {
|
|
5962
6413
|
success: false,
|
|
5963
|
-
message:
|
|
6414
|
+
message: chalk15.red(
|
|
5964
6415
|
`\u274C Invalid format: ${format}. Supported formats: table, json, xml, markdown`
|
|
5965
6416
|
)
|
|
5966
6417
|
};
|
|
5967
6418
|
}
|
|
5968
|
-
const stop = showSpinner("Loading projects",
|
|
6419
|
+
const stop = showSpinner("Loading projects", chalk15.gray);
|
|
5969
6420
|
try {
|
|
5970
6421
|
const response = await projectService.listProjects({
|
|
5971
6422
|
page: opts.page ? parseInt(opts.page, 10) : 1,
|
|
@@ -5992,11 +6443,11 @@ async function handleProjectList(opts) {
|
|
|
5992
6443
|
if (response.projects.length === 0) {
|
|
5993
6444
|
return {
|
|
5994
6445
|
success: true,
|
|
5995
|
-
message:
|
|
6446
|
+
message: chalk15.yellow("No projects found."),
|
|
5996
6447
|
data: response
|
|
5997
6448
|
};
|
|
5998
6449
|
}
|
|
5999
|
-
output =
|
|
6450
|
+
output = chalk15.bold("\u{1F4CB} Projects\n\n");
|
|
6000
6451
|
output += "ID Short ID Name Created\n";
|
|
6001
6452
|
output += `${"\u2500".repeat(100)}
|
|
6002
6453
|
`;
|
|
@@ -6035,21 +6486,21 @@ async function handleProjectShow(opts) {
|
|
|
6035
6486
|
if (!isAuthenticated) {
|
|
6036
6487
|
return {
|
|
6037
6488
|
success: false,
|
|
6038
|
-
message:
|
|
6489
|
+
message: chalk15.red("\u274C Not authenticated. Please run `braingrid login` first.")
|
|
6039
6490
|
};
|
|
6040
6491
|
}
|
|
6041
6492
|
const format = opts.format || "table";
|
|
6042
6493
|
if (!["table", "json", "xml", "markdown"].includes(format)) {
|
|
6043
6494
|
return {
|
|
6044
6495
|
success: false,
|
|
6045
|
-
message:
|
|
6496
|
+
message: chalk15.red(
|
|
6046
6497
|
`\u274C Invalid format: ${format}. Supported formats: table, json, xml, markdown`
|
|
6047
6498
|
)
|
|
6048
6499
|
};
|
|
6049
6500
|
}
|
|
6050
6501
|
if (opts.id) {
|
|
6051
6502
|
const normalizedId = parseProjectId(opts.id);
|
|
6052
|
-
const stop2 = showSpinner("Loading project",
|
|
6503
|
+
const stop2 = showSpinner("Loading project", chalk15.gray);
|
|
6053
6504
|
try {
|
|
6054
6505
|
const project2 = await projectService.getProject(normalizedId);
|
|
6055
6506
|
stop2();
|
|
@@ -6068,23 +6519,23 @@ async function handleProjectShow(opts) {
|
|
|
6068
6519
|
break;
|
|
6069
6520
|
}
|
|
6070
6521
|
default: {
|
|
6071
|
-
output =
|
|
6522
|
+
output = chalk15.bold(`
|
|
6072
6523
|
\u{1F4C1} Project: ${project2.name}
|
|
6073
6524
|
|
|
6074
6525
|
`);
|
|
6075
|
-
output += `${
|
|
6526
|
+
output += `${chalk15.bold("ID:")} ${project2.id}
|
|
6076
6527
|
`;
|
|
6077
|
-
output += `${
|
|
6528
|
+
output += `${chalk15.bold("Short ID:")} ${project2.short_id}
|
|
6078
6529
|
`;
|
|
6079
|
-
output += `${
|
|
6530
|
+
output += `${chalk15.bold("Name:")} ${project2.name}
|
|
6080
6531
|
`;
|
|
6081
6532
|
if (project2.description) {
|
|
6082
|
-
output += `${
|
|
6533
|
+
output += `${chalk15.bold("Description:")} ${project2.description}
|
|
6083
6534
|
`;
|
|
6084
6535
|
}
|
|
6085
|
-
output += `${
|
|
6536
|
+
output += `${chalk15.bold("Created:")} ${new Date(project2.created_at).toLocaleString()}
|
|
6086
6537
|
`;
|
|
6087
|
-
output += `${
|
|
6538
|
+
output += `${chalk15.bold("Updated:")} ${new Date(project2.updated_at).toLocaleString()}
|
|
6088
6539
|
`;
|
|
6089
6540
|
break;
|
|
6090
6541
|
}
|
|
@@ -6104,13 +6555,13 @@ async function handleProjectShow(opts) {
|
|
|
6104
6555
|
if (parts.length !== 2) {
|
|
6105
6556
|
return {
|
|
6106
6557
|
success: false,
|
|
6107
|
-
message:
|
|
6558
|
+
message: chalk15.red(
|
|
6108
6559
|
"\u274C Invalid repository format. Use: --repository owner/name (e.g., --repository microsoft/vscode)"
|
|
6109
6560
|
)
|
|
6110
6561
|
};
|
|
6111
6562
|
}
|
|
6112
6563
|
const [repositoryOwner, repositoryName] = parts;
|
|
6113
|
-
const stop2 = showSpinner("Loading projects",
|
|
6564
|
+
const stop2 = showSpinner("Loading projects", chalk15.gray);
|
|
6114
6565
|
try {
|
|
6115
6566
|
const response = await projectService.listProjects({
|
|
6116
6567
|
repository_owner: repositoryOwner,
|
|
@@ -6136,14 +6587,14 @@ async function handleProjectShow(opts) {
|
|
|
6136
6587
|
break;
|
|
6137
6588
|
}
|
|
6138
6589
|
default: {
|
|
6139
|
-
const repoDisplay =
|
|
6590
|
+
const repoDisplay = chalk15.cyan(`${repositoryOwner}/${repositoryName}`);
|
|
6140
6591
|
if (response.projects.length === 0) {
|
|
6141
6592
|
return {
|
|
6142
6593
|
success: true,
|
|
6143
|
-
message:
|
|
6594
|
+
message: chalk15.yellow(`No projects found for repository ${repoDisplay}`)
|
|
6144
6595
|
};
|
|
6145
6596
|
}
|
|
6146
|
-
output =
|
|
6597
|
+
output = chalk15.bold(`\u{1F4CB} Projects for ${repoDisplay}
|
|
6147
6598
|
|
|
6148
6599
|
`);
|
|
6149
6600
|
output += "ID Short ID Name Created\n";
|
|
@@ -6178,7 +6629,7 @@ async function handleProjectShow(opts) {
|
|
|
6178
6629
|
message: workspace.error
|
|
6179
6630
|
};
|
|
6180
6631
|
}
|
|
6181
|
-
const stop = showSpinner("Loading project",
|
|
6632
|
+
const stop = showSpinner("Loading project", chalk15.gray);
|
|
6182
6633
|
try {
|
|
6183
6634
|
const project2 = await projectService.getProject(workspace.projectId);
|
|
6184
6635
|
stop();
|
|
@@ -6197,23 +6648,23 @@ async function handleProjectShow(opts) {
|
|
|
6197
6648
|
break;
|
|
6198
6649
|
}
|
|
6199
6650
|
default: {
|
|
6200
|
-
output =
|
|
6651
|
+
output = chalk15.bold(`
|
|
6201
6652
|
\u{1F4C1} Project: ${project2.name}
|
|
6202
6653
|
|
|
6203
6654
|
`);
|
|
6204
|
-
output += `${
|
|
6655
|
+
output += `${chalk15.bold("ID:")} ${project2.id}
|
|
6205
6656
|
`;
|
|
6206
|
-
output += `${
|
|
6657
|
+
output += `${chalk15.bold("Short ID:")} ${project2.short_id}
|
|
6207
6658
|
`;
|
|
6208
|
-
output += `${
|
|
6659
|
+
output += `${chalk15.bold("Name:")} ${project2.name}
|
|
6209
6660
|
`;
|
|
6210
6661
|
if (project2.description) {
|
|
6211
|
-
output += `${
|
|
6662
|
+
output += `${chalk15.bold("Description:")} ${project2.description}
|
|
6212
6663
|
`;
|
|
6213
6664
|
}
|
|
6214
|
-
output += `${
|
|
6665
|
+
output += `${chalk15.bold("Created:")} ${new Date(project2.created_at).toLocaleString()}
|
|
6215
6666
|
`;
|
|
6216
|
-
output += `${
|
|
6667
|
+
output += `${chalk15.bold("Updated:")} ${new Date(project2.updated_at).toLocaleString()}
|
|
6217
6668
|
`;
|
|
6218
6669
|
break;
|
|
6219
6670
|
}
|
|
@@ -6241,7 +6692,7 @@ async function handleProjectCreate(opts) {
|
|
|
6241
6692
|
if (!isAuthenticated) {
|
|
6242
6693
|
return {
|
|
6243
6694
|
success: false,
|
|
6244
|
-
message:
|
|
6695
|
+
message: chalk15.red("\u274C Not authenticated. Please run `braingrid login` first.")
|
|
6245
6696
|
};
|
|
6246
6697
|
}
|
|
6247
6698
|
let repositoryId;
|
|
@@ -6252,21 +6703,21 @@ async function handleProjectCreate(opts) {
|
|
|
6252
6703
|
if (parts.length !== 2) {
|
|
6253
6704
|
return {
|
|
6254
6705
|
success: false,
|
|
6255
|
-
message:
|
|
6706
|
+
message: chalk15.red(
|
|
6256
6707
|
"\u274C Invalid repository format. Use: --repository owner/name (e.g., microsoft/vscode)"
|
|
6257
6708
|
)
|
|
6258
6709
|
};
|
|
6259
6710
|
}
|
|
6260
6711
|
const [owner, name] = parts;
|
|
6261
6712
|
const repositoryService = new RepositoryService(config2.apiUrl, auth);
|
|
6262
|
-
const stop2 = showSpinner(`Looking up repository ${opts.repository}`,
|
|
6713
|
+
const stop2 = showSpinner(`Looking up repository ${opts.repository}`, chalk15.gray);
|
|
6263
6714
|
try {
|
|
6264
6715
|
const repoId = await getRepositoryId(repositoryService, owner, name);
|
|
6265
6716
|
stop2();
|
|
6266
6717
|
if (!repoId) {
|
|
6267
6718
|
return {
|
|
6268
6719
|
success: false,
|
|
6269
|
-
message:
|
|
6720
|
+
message: chalk15.red(
|
|
6270
6721
|
`\u274C Repository '${opts.repository}' not found. Please check the name or ensure you have access.`
|
|
6271
6722
|
)
|
|
6272
6723
|
};
|
|
@@ -6276,7 +6727,7 @@ async function handleProjectCreate(opts) {
|
|
|
6276
6727
|
stop2();
|
|
6277
6728
|
}
|
|
6278
6729
|
}
|
|
6279
|
-
const stop = showSpinner("Creating project",
|
|
6730
|
+
const stop = showSpinner("Creating project", chalk15.gray);
|
|
6280
6731
|
try {
|
|
6281
6732
|
const project2 = await projectService.createProject({
|
|
6282
6733
|
name: opts.name,
|
|
@@ -6284,9 +6735,9 @@ async function handleProjectCreate(opts) {
|
|
|
6284
6735
|
repository_id: repositoryId
|
|
6285
6736
|
});
|
|
6286
6737
|
stop();
|
|
6287
|
-
let message =
|
|
6738
|
+
let message = chalk15.green(`\u2705 Created project ${project2.short_id}: ${project2.name}`);
|
|
6288
6739
|
if (opts.repository && repositoryId) {
|
|
6289
|
-
message +=
|
|
6740
|
+
message += chalk15.gray(` (linked to ${opts.repository})`);
|
|
6290
6741
|
}
|
|
6291
6742
|
return {
|
|
6292
6743
|
success: true,
|
|
@@ -6310,19 +6761,19 @@ async function handleProjectUpdate(id, opts) {
|
|
|
6310
6761
|
if (!isAuthenticated) {
|
|
6311
6762
|
return {
|
|
6312
6763
|
success: false,
|
|
6313
|
-
message:
|
|
6764
|
+
message: chalk15.red("\u274C Not authenticated. Please run `braingrid login` first.")
|
|
6314
6765
|
};
|
|
6315
6766
|
}
|
|
6316
6767
|
if (!opts.name && !opts.description) {
|
|
6317
6768
|
return {
|
|
6318
6769
|
success: false,
|
|
6319
|
-
message:
|
|
6770
|
+
message: chalk15.red(
|
|
6320
6771
|
"\u274C Please provide at least one field to update (--name or --description)"
|
|
6321
6772
|
)
|
|
6322
6773
|
};
|
|
6323
6774
|
}
|
|
6324
6775
|
const normalizedId = parseProjectId(id);
|
|
6325
|
-
const stop = showSpinner("Updating project",
|
|
6776
|
+
const stop = showSpinner("Updating project", chalk15.gray);
|
|
6326
6777
|
try {
|
|
6327
6778
|
const project2 = await projectService.updateProject(normalizedId, {
|
|
6328
6779
|
name: opts.name,
|
|
@@ -6331,7 +6782,7 @@ async function handleProjectUpdate(id, opts) {
|
|
|
6331
6782
|
stop();
|
|
6332
6783
|
return {
|
|
6333
6784
|
success: true,
|
|
6334
|
-
message:
|
|
6785
|
+
message: chalk15.green(`\u2705 Updated project ${project2.short_id}: ${project2.name}`),
|
|
6335
6786
|
data: project2
|
|
6336
6787
|
};
|
|
6337
6788
|
} finally {
|
|
@@ -6351,25 +6802,25 @@ async function handleProjectDelete(id, opts) {
|
|
|
6351
6802
|
if (!isAuthenticated) {
|
|
6352
6803
|
return {
|
|
6353
6804
|
success: false,
|
|
6354
|
-
message:
|
|
6805
|
+
message: chalk15.red("\u274C Not authenticated. Please run `braingrid login` first.")
|
|
6355
6806
|
};
|
|
6356
6807
|
}
|
|
6357
6808
|
if (!opts.force) {
|
|
6358
6809
|
return {
|
|
6359
6810
|
success: false,
|
|
6360
|
-
message:
|
|
6811
|
+
message: chalk15.yellow(
|
|
6361
6812
|
"\u26A0\uFE0F Deleting a project is permanent. Use --force to confirm deletion."
|
|
6362
6813
|
)
|
|
6363
6814
|
};
|
|
6364
6815
|
}
|
|
6365
6816
|
const normalizedId = parseProjectId(id);
|
|
6366
|
-
const stop = showSpinner("Deleting project",
|
|
6817
|
+
const stop = showSpinner("Deleting project", chalk15.gray);
|
|
6367
6818
|
try {
|
|
6368
6819
|
await projectService.deleteProject(normalizedId);
|
|
6369
6820
|
stop();
|
|
6370
6821
|
return {
|
|
6371
6822
|
success: true,
|
|
6372
|
-
message:
|
|
6823
|
+
message: chalk15.green(`\u2705 Deleted project ${id}`)
|
|
6373
6824
|
};
|
|
6374
6825
|
} finally {
|
|
6375
6826
|
stop();
|
|
@@ -6383,7 +6834,7 @@ async function handleProjectDelete(id, opts) {
|
|
|
6383
6834
|
}
|
|
6384
6835
|
|
|
6385
6836
|
// src/handlers/requirement.handlers.ts
|
|
6386
|
-
import
|
|
6837
|
+
import chalk16 from "chalk";
|
|
6387
6838
|
|
|
6388
6839
|
// src/services/requirement-service.ts
|
|
6389
6840
|
var RequirementService = class {
|
|
@@ -6533,14 +6984,14 @@ async function handleRequirementList(opts) {
|
|
|
6533
6984
|
if (!isAuthenticated) {
|
|
6534
6985
|
return {
|
|
6535
6986
|
success: false,
|
|
6536
|
-
message:
|
|
6987
|
+
message: chalk16.red("\u274C Not authenticated. Please run `braingrid login` first.")
|
|
6537
6988
|
};
|
|
6538
6989
|
}
|
|
6539
6990
|
const format = opts.format || "table";
|
|
6540
6991
|
if (!opts.tree && !["table", "json", "xml", "markdown"].includes(format)) {
|
|
6541
6992
|
return {
|
|
6542
6993
|
success: false,
|
|
6543
|
-
message:
|
|
6994
|
+
message: chalk16.red(
|
|
6544
6995
|
`\u274C Invalid format: ${format}. Supported formats: table, json, xml, markdown`
|
|
6545
6996
|
)
|
|
6546
6997
|
};
|
|
@@ -6553,7 +7004,7 @@ async function handleRequirementList(opts) {
|
|
|
6553
7004
|
};
|
|
6554
7005
|
}
|
|
6555
7006
|
const projectId = workspace.projectId;
|
|
6556
|
-
stopSpinner = showSpinner("Loading requirements",
|
|
7007
|
+
stopSpinner = showSpinner("Loading requirements", chalk16.gray);
|
|
6557
7008
|
const response = await requirementService.listProjectRequirements(projectId, {
|
|
6558
7009
|
status: opts.status,
|
|
6559
7010
|
page: opts.page ? parseInt(opts.page, 10) : 1,
|
|
@@ -6565,10 +7016,10 @@ async function handleRequirementList(opts) {
|
|
|
6565
7016
|
if (response.requirements.length === 0) {
|
|
6566
7017
|
return {
|
|
6567
7018
|
success: true,
|
|
6568
|
-
message:
|
|
7019
|
+
message: chalk16.yellow("No requirements found.")
|
|
6569
7020
|
};
|
|
6570
7021
|
}
|
|
6571
|
-
stopSpinner = showSpinner("Loading tasks for tree view",
|
|
7022
|
+
stopSpinner = showSpinner("Loading tasks for tree view", chalk16.gray);
|
|
6572
7023
|
const tasksMap = /* @__PURE__ */ new Map();
|
|
6573
7024
|
const taskPromises = response.requirements.map(async (req) => {
|
|
6574
7025
|
try {
|
|
@@ -6591,7 +7042,7 @@ Page ${response.pagination.page} of ${Math.ceil(response.pagination.total / resp
|
|
|
6591
7042
|
finalOutput += ` (${response.pagination.total} total)`;
|
|
6592
7043
|
if (response.pagination?.has_more) {
|
|
6593
7044
|
finalOutput += `
|
|
6594
|
-
${
|
|
7045
|
+
${chalk16.yellow("\u26A0\uFE0F More requirements exist. Use --limit to see more.")}`;
|
|
6595
7046
|
}
|
|
6596
7047
|
return {
|
|
6597
7048
|
success: true,
|
|
@@ -6617,10 +7068,10 @@ ${chalk15.yellow("\u26A0\uFE0F More requirements exist. Use --limit to see more.
|
|
|
6617
7068
|
if (response.requirements.length === 0) {
|
|
6618
7069
|
return {
|
|
6619
7070
|
success: true,
|
|
6620
|
-
message:
|
|
7071
|
+
message: chalk16.yellow("No requirements found.")
|
|
6621
7072
|
};
|
|
6622
7073
|
}
|
|
6623
|
-
output =
|
|
7074
|
+
output = chalk16.bold("\u{1F4CB} Requirements\n\n");
|
|
6624
7075
|
output += "Short ID Status Name Branch Progress\n";
|
|
6625
7076
|
output += `${"\u2500".repeat(90)}
|
|
6626
7077
|
`;
|
|
@@ -6642,7 +7093,7 @@ ${chalk15.yellow("\u26A0\uFE0F More requirements exist. Use --limit to see more.
|
|
|
6642
7093
|
}
|
|
6643
7094
|
if (response.pagination?.has_more) {
|
|
6644
7095
|
output += `
|
|
6645
|
-
${
|
|
7096
|
+
${chalk16.yellow("\u26A0\uFE0F More requirements exist. Use --limit to see more.")}`;
|
|
6646
7097
|
}
|
|
6647
7098
|
return {
|
|
6648
7099
|
success: true,
|
|
@@ -6667,14 +7118,14 @@ async function handleRequirementShow(opts) {
|
|
|
6667
7118
|
if (!isAuthenticated) {
|
|
6668
7119
|
return {
|
|
6669
7120
|
success: false,
|
|
6670
|
-
message:
|
|
7121
|
+
message: chalk16.red("\u274C Not authenticated. Please run `braingrid login` first.")
|
|
6671
7122
|
};
|
|
6672
7123
|
}
|
|
6673
7124
|
const format = opts?.format || "table";
|
|
6674
7125
|
if (!["table", "json", "xml", "markdown"].includes(format)) {
|
|
6675
7126
|
return {
|
|
6676
7127
|
success: false,
|
|
6677
|
-
message:
|
|
7128
|
+
message: chalk16.red(
|
|
6678
7129
|
`\u274C Invalid format: ${format}. Supported formats: table, json, xml, markdown`
|
|
6679
7130
|
)
|
|
6680
7131
|
};
|
|
@@ -6696,7 +7147,7 @@ async function handleRequirementShow(opts) {
|
|
|
6696
7147
|
}
|
|
6697
7148
|
const projectId = workspace.projectId;
|
|
6698
7149
|
const normalizedId = normalizeRequirementId(requirementId);
|
|
6699
|
-
stopSpinner = showSpinner("Loading requirement",
|
|
7150
|
+
stopSpinner = showSpinner("Loading requirement", chalk16.gray);
|
|
6700
7151
|
const requirement2 = await requirementService.getProjectRequirement(projectId, normalizedId);
|
|
6701
7152
|
stopSpinner();
|
|
6702
7153
|
stopSpinner = null;
|
|
@@ -6752,7 +7203,7 @@ async function handleRequirementCreate(opts) {
|
|
|
6752
7203
|
if (!isAuthenticated) {
|
|
6753
7204
|
return {
|
|
6754
7205
|
success: false,
|
|
6755
|
-
message:
|
|
7206
|
+
message: chalk16.red("\u274C Not authenticated. Please run `braingrid login` first.")
|
|
6756
7207
|
};
|
|
6757
7208
|
}
|
|
6758
7209
|
const workspace = await workspaceManager.getProject(opts.project);
|
|
@@ -6769,7 +7220,7 @@ async function handleRequirementCreate(opts) {
|
|
|
6769
7220
|
if (emailRegex.test(opts.assignedTo)) {
|
|
6770
7221
|
return {
|
|
6771
7222
|
success: false,
|
|
6772
|
-
message:
|
|
7223
|
+
message: chalk16.red(
|
|
6773
7224
|
"\u274C Email addresses are not supported for --assigned-to yet.\nPlease use the user UUID instead.\nEmail lookup will be supported when the API provides a user lookup endpoint."
|
|
6774
7225
|
)
|
|
6775
7226
|
};
|
|
@@ -6777,7 +7228,7 @@ async function handleRequirementCreate(opts) {
|
|
|
6777
7228
|
if (!uuidRegex.test(opts.assignedTo)) {
|
|
6778
7229
|
return {
|
|
6779
7230
|
success: false,
|
|
6780
|
-
message:
|
|
7231
|
+
message: chalk16.red(
|
|
6781
7232
|
'\u274C Invalid UUID format for --assigned-to.\nPlease provide a valid user UUID (e.g., "550e8400-e29b-41d4-a716-446655440000")'
|
|
6782
7233
|
)
|
|
6783
7234
|
};
|
|
@@ -6789,12 +7240,12 @@ async function handleRequirementCreate(opts) {
|
|
|
6789
7240
|
if (!tagResult.valid) {
|
|
6790
7241
|
return {
|
|
6791
7242
|
success: false,
|
|
6792
|
-
message:
|
|
7243
|
+
message: chalk16.red(`\u274C ${tagResult.error}`)
|
|
6793
7244
|
};
|
|
6794
7245
|
}
|
|
6795
7246
|
validatedTags = tagResult.tags;
|
|
6796
7247
|
}
|
|
6797
|
-
stopSpinner = showSpinner("Creating requirement",
|
|
7248
|
+
stopSpinner = showSpinner("Creating requirement", chalk16.gray);
|
|
6798
7249
|
const requirement2 = await requirementService.createProjectRequirement(projectId, {
|
|
6799
7250
|
name: opts.name,
|
|
6800
7251
|
content: opts.content || null,
|
|
@@ -6828,7 +7279,7 @@ async function handleRequirementCreate(opts) {
|
|
|
6828
7279
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
6829
7280
|
return {
|
|
6830
7281
|
success: false,
|
|
6831
|
-
message:
|
|
7282
|
+
message: chalk16.red(`\u274C Error creating requirement: ${errorMessage}`)
|
|
6832
7283
|
};
|
|
6833
7284
|
}
|
|
6834
7285
|
}
|
|
@@ -6840,14 +7291,14 @@ async function handleRequirementSpecify(opts) {
|
|
|
6840
7291
|
if (!isAuthenticated) {
|
|
6841
7292
|
return {
|
|
6842
7293
|
success: false,
|
|
6843
|
-
message:
|
|
7294
|
+
message: chalk16.red("\u274C Not authenticated. Please run `braingrid login` first.")
|
|
6844
7295
|
};
|
|
6845
7296
|
}
|
|
6846
7297
|
const format = opts.format || "table";
|
|
6847
7298
|
if (!["table", "json", "xml", "markdown"].includes(format)) {
|
|
6848
7299
|
return {
|
|
6849
7300
|
success: false,
|
|
6850
|
-
message:
|
|
7301
|
+
message: chalk16.red(
|
|
6851
7302
|
`\u274C Invalid format: ${format}. Supported formats: table, json, xml, markdown`
|
|
6852
7303
|
)
|
|
6853
7304
|
};
|
|
@@ -6863,13 +7314,13 @@ async function handleRequirementSpecify(opts) {
|
|
|
6863
7314
|
if (opts.prompt.length < 10) {
|
|
6864
7315
|
return {
|
|
6865
7316
|
success: false,
|
|
6866
|
-
message:
|
|
7317
|
+
message: chalk16.red("\u274C Prompt must be at least 10 characters long")
|
|
6867
7318
|
};
|
|
6868
7319
|
}
|
|
6869
7320
|
if (opts.prompt.length > 5e3) {
|
|
6870
7321
|
return {
|
|
6871
7322
|
success: false,
|
|
6872
|
-
message:
|
|
7323
|
+
message: chalk16.red("\u274C Prompt must be no more than 5000 characters long")
|
|
6873
7324
|
};
|
|
6874
7325
|
}
|
|
6875
7326
|
let validatedTags;
|
|
@@ -6878,7 +7329,7 @@ async function handleRequirementSpecify(opts) {
|
|
|
6878
7329
|
if (!tagResult.valid) {
|
|
6879
7330
|
return {
|
|
6880
7331
|
success: false,
|
|
6881
|
-
message:
|
|
7332
|
+
message: chalk16.red(`\u274C ${tagResult.error}`)
|
|
6882
7333
|
};
|
|
6883
7334
|
}
|
|
6884
7335
|
validatedTags = tagResult.tags;
|
|
@@ -6941,13 +7392,13 @@ async function handleRequirementUpdate(opts) {
|
|
|
6941
7392
|
if (!isAuthenticated) {
|
|
6942
7393
|
return {
|
|
6943
7394
|
success: false,
|
|
6944
|
-
message:
|
|
7395
|
+
message: chalk16.red("\u274C Not authenticated. Please run `braingrid login` first.")
|
|
6945
7396
|
};
|
|
6946
7397
|
}
|
|
6947
7398
|
if (!opts.status && !opts.name && !opts.content) {
|
|
6948
7399
|
return {
|
|
6949
7400
|
success: false,
|
|
6950
|
-
message:
|
|
7401
|
+
message: chalk16.red(
|
|
6951
7402
|
"\u274C Please provide at least one field to update (--status, --name, or --content)"
|
|
6952
7403
|
)
|
|
6953
7404
|
};
|
|
@@ -6969,7 +7420,7 @@ async function handleRequirementUpdate(opts) {
|
|
|
6969
7420
|
}
|
|
6970
7421
|
const projectId = workspace.projectId;
|
|
6971
7422
|
const normalizedId = normalizeRequirementId(requirementId);
|
|
6972
|
-
stopSpinner = showSpinner("Updating requirement",
|
|
7423
|
+
stopSpinner = showSpinner("Updating requirement", chalk16.gray);
|
|
6973
7424
|
const requirement2 = await requirementService.updateProjectRequirement(projectId, normalizedId, {
|
|
6974
7425
|
status: opts.status,
|
|
6975
7426
|
name: opts.name,
|
|
@@ -6979,7 +7430,7 @@ async function handleRequirementUpdate(opts) {
|
|
|
6979
7430
|
stopSpinner = null;
|
|
6980
7431
|
return {
|
|
6981
7432
|
success: true,
|
|
6982
|
-
message:
|
|
7433
|
+
message: chalk16.green(`\u2705 Updated requirement ${requirement2.short_id}: ${requirement2.name}`),
|
|
6983
7434
|
data: requirement2
|
|
6984
7435
|
};
|
|
6985
7436
|
} catch (error) {
|
|
@@ -7000,13 +7451,13 @@ async function handleRequirementDelete(opts) {
|
|
|
7000
7451
|
if (!isAuthenticated) {
|
|
7001
7452
|
return {
|
|
7002
7453
|
success: false,
|
|
7003
|
-
message:
|
|
7454
|
+
message: chalk16.red("\u274C Not authenticated. Please run `braingrid login` first.")
|
|
7004
7455
|
};
|
|
7005
7456
|
}
|
|
7006
7457
|
if (!opts.force) {
|
|
7007
7458
|
return {
|
|
7008
7459
|
success: false,
|
|
7009
|
-
message:
|
|
7460
|
+
message: chalk16.yellow(
|
|
7010
7461
|
"\u26A0\uFE0F Deleting a requirement is permanent. Use --force to confirm deletion."
|
|
7011
7462
|
)
|
|
7012
7463
|
};
|
|
@@ -7028,13 +7479,13 @@ async function handleRequirementDelete(opts) {
|
|
|
7028
7479
|
}
|
|
7029
7480
|
const projectId = workspace.projectId;
|
|
7030
7481
|
const normalizedId = normalizeRequirementId(requirementId);
|
|
7031
|
-
stopSpinner = showSpinner("Deleting requirement",
|
|
7482
|
+
stopSpinner = showSpinner("Deleting requirement", chalk16.gray);
|
|
7032
7483
|
await requirementService.deleteProjectRequirement(projectId, normalizedId);
|
|
7033
7484
|
stopSpinner();
|
|
7034
7485
|
stopSpinner = null;
|
|
7035
7486
|
return {
|
|
7036
7487
|
success: true,
|
|
7037
|
-
message:
|
|
7488
|
+
message: chalk16.green(`\u2705 Deleted requirement ${requirementId}`)
|
|
7038
7489
|
};
|
|
7039
7490
|
} catch (error) {
|
|
7040
7491
|
if (stopSpinner) {
|
|
@@ -7054,14 +7505,14 @@ async function handleRequirementBreakdown(opts) {
|
|
|
7054
7505
|
if (!isAuthenticated) {
|
|
7055
7506
|
return {
|
|
7056
7507
|
success: false,
|
|
7057
|
-
message:
|
|
7508
|
+
message: chalk16.red("\u274C Not authenticated. Please run `braingrid login` first.")
|
|
7058
7509
|
};
|
|
7059
7510
|
}
|
|
7060
7511
|
const format = opts.format || "table";
|
|
7061
7512
|
if (!["table", "json", "xml", "markdown"].includes(format)) {
|
|
7062
7513
|
return {
|
|
7063
7514
|
success: false,
|
|
7064
|
-
message:
|
|
7515
|
+
message: chalk16.red(
|
|
7065
7516
|
`\u274C Invalid format: ${format}. Supported formats: table, json, xml, markdown`
|
|
7066
7517
|
)
|
|
7067
7518
|
};
|
|
@@ -7083,7 +7534,7 @@ async function handleRequirementBreakdown(opts) {
|
|
|
7083
7534
|
}
|
|
7084
7535
|
const projectId = workspace.projectId;
|
|
7085
7536
|
const normalizedId = normalizeRequirementId(requirementId);
|
|
7086
|
-
stop = showSpinner("Checking requirement...",
|
|
7537
|
+
stop = showSpinner("Checking requirement...", chalk16.gray);
|
|
7087
7538
|
const requirement2 = await requirementService.getProjectRequirement(projectId, normalizedId);
|
|
7088
7539
|
stop();
|
|
7089
7540
|
const taskCount = requirement2.tasks?.length ?? requirement2.task_progress?.total ?? 0;
|
|
@@ -7091,13 +7542,13 @@ async function handleRequirementBreakdown(opts) {
|
|
|
7091
7542
|
const shortId = requirement2.short_id || normalizedId;
|
|
7092
7543
|
return {
|
|
7093
7544
|
success: false,
|
|
7094
|
-
message:
|
|
7545
|
+
message: chalk16.red(
|
|
7095
7546
|
`\u274C Cannot breakdown requirement ${shortId} - it already has ${taskCount} task(s)
|
|
7096
7547
|
|
|
7097
7548
|
`
|
|
7098
|
-
) + "The breakdown command is only for initial task generation from requirements.\nOnce tasks exist, you should manage them individually.\n\n" +
|
|
7549
|
+
) + "The breakdown command is only for initial task generation from requirements.\nOnce tasks exist, you should manage them individually.\n\n" + chalk16.bold("To add more tasks to this requirement:\n") + chalk16.cyan(` braingrid task create -r ${shortId} --title "Task title"
|
|
7099
7550
|
|
|
7100
|
-
`) +
|
|
7551
|
+
`) + chalk16.bold("To view existing tasks:\n") + chalk16.cyan(` braingrid task list -r ${shortId}`)
|
|
7101
7552
|
};
|
|
7102
7553
|
}
|
|
7103
7554
|
stop = showSpinner("Breaking down requirement into tasks...");
|
|
@@ -7118,7 +7569,7 @@ async function handleRequirementBreakdown(opts) {
|
|
|
7118
7569
|
break;
|
|
7119
7570
|
}
|
|
7120
7571
|
default: {
|
|
7121
|
-
output =
|
|
7572
|
+
output = chalk16.green(
|
|
7122
7573
|
`\u2705 Generated ${response.tasks.length} tasks for ${response.requirement_short_id}
|
|
7123
7574
|
|
|
7124
7575
|
`
|
|
@@ -7156,7 +7607,7 @@ async function handleRequirementBreakdown(opts) {
|
|
|
7156
7607
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
7157
7608
|
return {
|
|
7158
7609
|
success: false,
|
|
7159
|
-
message:
|
|
7610
|
+
message: chalk16.red(`\u274C Error breaking down requirement: ${errorMessage}`)
|
|
7160
7611
|
};
|
|
7161
7612
|
}
|
|
7162
7613
|
}
|
|
@@ -7168,14 +7619,14 @@ async function handleRequirementBuild(opts) {
|
|
|
7168
7619
|
if (!isAuthenticated) {
|
|
7169
7620
|
return {
|
|
7170
7621
|
success: false,
|
|
7171
|
-
message:
|
|
7622
|
+
message: chalk16.red("\u274C Not authenticated. Please run `braingrid login` first.")
|
|
7172
7623
|
};
|
|
7173
7624
|
}
|
|
7174
7625
|
const format = opts.format || "markdown";
|
|
7175
7626
|
if (!["markdown", "json", "xml"].includes(format)) {
|
|
7176
7627
|
return {
|
|
7177
7628
|
success: false,
|
|
7178
|
-
message:
|
|
7629
|
+
message: chalk16.red("\u274C Invalid format. Must be one of: markdown, json, xml")
|
|
7179
7630
|
};
|
|
7180
7631
|
}
|
|
7181
7632
|
const requirementResult = await workspaceManager.getRequirement(opts.id);
|
|
@@ -7195,7 +7646,7 @@ async function handleRequirementBuild(opts) {
|
|
|
7195
7646
|
}
|
|
7196
7647
|
const projectId = workspace.projectId;
|
|
7197
7648
|
const normalizedId = normalizeRequirementId(requirementId);
|
|
7198
|
-
stopSpinner = showSpinner("Building requirement with tasks",
|
|
7649
|
+
stopSpinner = showSpinner("Building requirement with tasks", chalk16.gray);
|
|
7199
7650
|
const requirement2 = await requirementService.getProjectRequirement(projectId, normalizedId);
|
|
7200
7651
|
stopSpinner();
|
|
7201
7652
|
stopSpinner = null;
|
|
@@ -7243,14 +7694,14 @@ async function handleCreateGitBranch(opts) {
|
|
|
7243
7694
|
if (!isAuthenticated) {
|
|
7244
7695
|
return {
|
|
7245
7696
|
success: false,
|
|
7246
|
-
message:
|
|
7697
|
+
message: chalk16.red("\u274C Not authenticated. Please run `braingrid login` first.")
|
|
7247
7698
|
};
|
|
7248
7699
|
}
|
|
7249
7700
|
const format = opts.format || "table";
|
|
7250
7701
|
if (!["table", "json", "markdown"].includes(format)) {
|
|
7251
7702
|
return {
|
|
7252
7703
|
success: false,
|
|
7253
|
-
message:
|
|
7704
|
+
message: chalk16.red(
|
|
7254
7705
|
`\u274C Invalid format: ${format}. Supported formats: table, json, markdown`
|
|
7255
7706
|
)
|
|
7256
7707
|
};
|
|
@@ -7278,7 +7729,7 @@ async function handleCreateGitBranch(opts) {
|
|
|
7278
7729
|
if (!user) {
|
|
7279
7730
|
return {
|
|
7280
7731
|
success: false,
|
|
7281
|
-
message:
|
|
7732
|
+
message: chalk16.red("\u274C Could not get current user for branch name generation")
|
|
7282
7733
|
};
|
|
7283
7734
|
}
|
|
7284
7735
|
const requirement2 = await requirementService.getProjectRequirement(projectId, normalizedId);
|
|
@@ -7287,7 +7738,7 @@ async function handleCreateGitBranch(opts) {
|
|
|
7287
7738
|
const sluggedName = slugify(requirement2.name);
|
|
7288
7739
|
branchName = `${username}/${reqShortId}-${sluggedName}`;
|
|
7289
7740
|
}
|
|
7290
|
-
stopSpinner = showSpinner("Creating GitHub branch...",
|
|
7741
|
+
stopSpinner = showSpinner("Creating GitHub branch...", chalk16.gray);
|
|
7291
7742
|
const response = await requirementService.createGitBranch(projectId, normalizedId, {
|
|
7292
7743
|
branchName,
|
|
7293
7744
|
baseBranch: opts.base
|
|
@@ -7319,13 +7770,13 @@ git fetch origin && git checkout ${response.branch.name}
|
|
|
7319
7770
|
break;
|
|
7320
7771
|
}
|
|
7321
7772
|
default: {
|
|
7322
|
-
output =
|
|
7773
|
+
output = chalk16.green(`\u2705 Created branch: ${response.branch.name}
|
|
7323
7774
|
|
|
7324
7775
|
`);
|
|
7325
|
-
output += `${
|
|
7776
|
+
output += `${chalk16.bold("SHA:")} ${response.branch.sha.substring(0, 7)}
|
|
7326
7777
|
`;
|
|
7327
7778
|
output += `
|
|
7328
|
-
${
|
|
7779
|
+
${chalk16.dim("To checkout:")} git fetch origin && git checkout ${response.branch.name}`;
|
|
7329
7780
|
break;
|
|
7330
7781
|
}
|
|
7331
7782
|
}
|
|
@@ -7351,7 +7802,7 @@ async function handleReviewAcceptance(opts) {
|
|
|
7351
7802
|
if (!isAuthenticated) {
|
|
7352
7803
|
return {
|
|
7353
7804
|
success: false,
|
|
7354
|
-
message:
|
|
7805
|
+
message: chalk16.red("\u274C Not authenticated. Please run `braingrid login` first.")
|
|
7355
7806
|
};
|
|
7356
7807
|
}
|
|
7357
7808
|
const requirementResult = await workspaceManager.getRequirement(opts.id);
|
|
@@ -7373,9 +7824,9 @@ async function handleReviewAcceptance(opts) {
|
|
|
7373
7824
|
const normalizedId = normalizeRequirementId(requirementId);
|
|
7374
7825
|
let prNumber = opts.pr;
|
|
7375
7826
|
if (!prNumber) {
|
|
7376
|
-
const { execAsync:
|
|
7827
|
+
const { execAsync: execAsync6 } = await import("./command-execution-HEGDCOLO.js");
|
|
7377
7828
|
try {
|
|
7378
|
-
const { stdout } = await
|
|
7829
|
+
const { stdout } = await execAsync6("gh pr view --json number -q .number");
|
|
7379
7830
|
const detectedPrNumber = stdout.trim();
|
|
7380
7831
|
if (!detectedPrNumber) {
|
|
7381
7832
|
throw new Error("No PR number returned from gh CLI");
|
|
@@ -7384,13 +7835,13 @@ async function handleReviewAcceptance(opts) {
|
|
|
7384
7835
|
} catch {
|
|
7385
7836
|
return {
|
|
7386
7837
|
success: false,
|
|
7387
|
-
message:
|
|
7838
|
+
message: chalk16.red(
|
|
7388
7839
|
"\u274C No PR specified and could not detect PR for current branch.\n Use --pr <number> or ensure you have an open PR for this branch."
|
|
7389
7840
|
)
|
|
7390
7841
|
};
|
|
7391
7842
|
}
|
|
7392
7843
|
}
|
|
7393
|
-
console.log(
|
|
7844
|
+
console.log(chalk16.bold("\n\u{1F50D} AI Code Review\n"));
|
|
7394
7845
|
let fullOutput = "";
|
|
7395
7846
|
await requirementService.reviewAcceptance(
|
|
7396
7847
|
projectId,
|
|
@@ -7423,14 +7874,14 @@ async function handleRequirementTagList(opts) {
|
|
|
7423
7874
|
if (!isAuthenticated) {
|
|
7424
7875
|
return {
|
|
7425
7876
|
success: false,
|
|
7426
|
-
message:
|
|
7877
|
+
message: chalk16.red("\u274C Not authenticated. Please run `braingrid login` first.")
|
|
7427
7878
|
};
|
|
7428
7879
|
}
|
|
7429
7880
|
const format = opts.format || "table";
|
|
7430
7881
|
if (!["table", "json", "xml", "markdown"].includes(format)) {
|
|
7431
7882
|
return {
|
|
7432
7883
|
success: false,
|
|
7433
|
-
message:
|
|
7884
|
+
message: chalk16.red(
|
|
7434
7885
|
`\u274C Invalid format: ${format}. Supported formats: table, json, xml, markdown`
|
|
7435
7886
|
)
|
|
7436
7887
|
};
|
|
@@ -7452,7 +7903,7 @@ async function handleRequirementTagList(opts) {
|
|
|
7452
7903
|
}
|
|
7453
7904
|
const projectId = workspace.projectId;
|
|
7454
7905
|
const normalizedId = normalizeRequirementId(requirementId);
|
|
7455
|
-
stopSpinner = showSpinner("Loading tags",
|
|
7906
|
+
stopSpinner = showSpinner("Loading tags", chalk16.gray);
|
|
7456
7907
|
const tags = await requirementService.listRequirementTags(projectId, normalizedId);
|
|
7457
7908
|
stopSpinner();
|
|
7458
7909
|
stopSpinner = null;
|
|
@@ -7499,11 +7950,11 @@ async function handleRequirementTagList(opts) {
|
|
|
7499
7950
|
if (tags.length === 0) {
|
|
7500
7951
|
return {
|
|
7501
7952
|
success: true,
|
|
7502
|
-
message:
|
|
7953
|
+
message: chalk16.yellow(`No tags found for ${normalizedId}.`),
|
|
7503
7954
|
data: tags
|
|
7504
7955
|
};
|
|
7505
7956
|
}
|
|
7506
|
-
output =
|
|
7957
|
+
output = chalk16.bold(`\u{1F3F7}\uFE0F Tags for ${normalizedId}
|
|
7507
7958
|
|
|
7508
7959
|
`);
|
|
7509
7960
|
output += "Name Color\n";
|
|
@@ -7544,20 +7995,20 @@ async function handleRequirementTagAdd(opts) {
|
|
|
7544
7995
|
if (!isAuthenticated) {
|
|
7545
7996
|
return {
|
|
7546
7997
|
success: false,
|
|
7547
|
-
message:
|
|
7998
|
+
message: chalk16.red("\u274C Not authenticated. Please run `braingrid login` first.")
|
|
7548
7999
|
};
|
|
7549
8000
|
}
|
|
7550
8001
|
if (!opts.name || opts.name.trim().length === 0) {
|
|
7551
8002
|
return {
|
|
7552
8003
|
success: false,
|
|
7553
|
-
message:
|
|
8004
|
+
message: chalk16.red("\u274C Tag name is required")
|
|
7554
8005
|
};
|
|
7555
8006
|
}
|
|
7556
8007
|
const colorResult = validateHexColor(opts.color);
|
|
7557
8008
|
if (!colorResult.valid) {
|
|
7558
8009
|
return {
|
|
7559
8010
|
success: false,
|
|
7560
|
-
message:
|
|
8011
|
+
message: chalk16.red(`\u274C ${colorResult.error}`)
|
|
7561
8012
|
};
|
|
7562
8013
|
}
|
|
7563
8014
|
const requirementResult = await workspaceManager.getRequirement(opts.requirementId);
|
|
@@ -7577,7 +8028,7 @@ async function handleRequirementTagAdd(opts) {
|
|
|
7577
8028
|
}
|
|
7578
8029
|
const projectId = workspace.projectId;
|
|
7579
8030
|
const normalizedId = normalizeRequirementId(requirementId);
|
|
7580
|
-
stopSpinner = showSpinner("Adding tag",
|
|
8031
|
+
stopSpinner = showSpinner("Adding tag", chalk16.gray);
|
|
7581
8032
|
const response = await requirementService.addRequirementTag(projectId, normalizedId, {
|
|
7582
8033
|
name: opts.name.trim(),
|
|
7583
8034
|
color: opts.color
|
|
@@ -7586,7 +8037,7 @@ async function handleRequirementTagAdd(opts) {
|
|
|
7586
8037
|
stopSpinner = null;
|
|
7587
8038
|
return {
|
|
7588
8039
|
success: true,
|
|
7589
|
-
message:
|
|
8040
|
+
message: chalk16.green(
|
|
7590
8041
|
`\u2705 Added tag "${response.tag.name}" (${response.tag.color}) to ${normalizedId}`
|
|
7591
8042
|
),
|
|
7592
8043
|
data: response
|
|
@@ -7602,13 +8053,13 @@ async function handleRequirementTagAdd(opts) {
|
|
|
7602
8053
|
if (errorData?.code === "MAX_TAGS_EXCEEDED") {
|
|
7603
8054
|
return {
|
|
7604
8055
|
success: false,
|
|
7605
|
-
message:
|
|
8056
|
+
message: chalk16.red("\u274C Maximum 5 tags allowed per requirement")
|
|
7606
8057
|
};
|
|
7607
8058
|
}
|
|
7608
8059
|
if (errorData?.code === "TAG_ALREADY_ASSOCIATED") {
|
|
7609
8060
|
return {
|
|
7610
8061
|
success: false,
|
|
7611
|
-
message:
|
|
8062
|
+
message: chalk16.red(`\u274C Tag "${opts.name}" is already associated with this requirement`)
|
|
7612
8063
|
};
|
|
7613
8064
|
}
|
|
7614
8065
|
}
|
|
@@ -7630,13 +8081,13 @@ async function handleRequirementTagRemove(opts) {
|
|
|
7630
8081
|
if (!isAuthenticated) {
|
|
7631
8082
|
return {
|
|
7632
8083
|
success: false,
|
|
7633
|
-
message:
|
|
8084
|
+
message: chalk16.red("\u274C Not authenticated. Please run `braingrid login` first.")
|
|
7634
8085
|
};
|
|
7635
8086
|
}
|
|
7636
8087
|
if (!opts.name || opts.name.trim().length === 0) {
|
|
7637
8088
|
return {
|
|
7638
8089
|
success: false,
|
|
7639
|
-
message:
|
|
8090
|
+
message: chalk16.red("\u274C Tag name is required")
|
|
7640
8091
|
};
|
|
7641
8092
|
}
|
|
7642
8093
|
const requirementResult = await workspaceManager.getRequirement(opts.requirementId);
|
|
@@ -7656,13 +8107,13 @@ async function handleRequirementTagRemove(opts) {
|
|
|
7656
8107
|
}
|
|
7657
8108
|
const projectId = workspace.projectId;
|
|
7658
8109
|
const normalizedId = normalizeRequirementId(requirementId);
|
|
7659
|
-
stopSpinner = showSpinner("Removing tag",
|
|
8110
|
+
stopSpinner = showSpinner("Removing tag", chalk16.gray);
|
|
7660
8111
|
await requirementService.removeRequirementTag(projectId, normalizedId, opts.name.trim());
|
|
7661
8112
|
stopSpinner();
|
|
7662
8113
|
stopSpinner = null;
|
|
7663
8114
|
return {
|
|
7664
8115
|
success: true,
|
|
7665
|
-
message:
|
|
8116
|
+
message: chalk16.green(`\u2705 Removed tag "${opts.name}" from ${normalizedId}`)
|
|
7666
8117
|
};
|
|
7667
8118
|
} catch (error) {
|
|
7668
8119
|
if (stopSpinner) {
|
|
@@ -7675,13 +8126,13 @@ async function handleRequirementTagRemove(opts) {
|
|
|
7675
8126
|
if (errorData?.code === "TAG_NOT_FOUND") {
|
|
7676
8127
|
return {
|
|
7677
8128
|
success: false,
|
|
7678
|
-
message:
|
|
8129
|
+
message: chalk16.red(`\u274C Tag "${opts.name}" not found`)
|
|
7679
8130
|
};
|
|
7680
8131
|
}
|
|
7681
8132
|
if (errorData?.code === "ASSOCIATION_NOT_FOUND") {
|
|
7682
8133
|
return {
|
|
7683
8134
|
success: false,
|
|
7684
|
-
message:
|
|
8135
|
+
message: chalk16.red(`\u274C Tag "${opts.name}" is not associated with this requirement`)
|
|
7685
8136
|
};
|
|
7686
8137
|
}
|
|
7687
8138
|
}
|
|
@@ -7697,88 +8148,136 @@ async function handleRequirementTagRemove(opts) {
|
|
|
7697
8148
|
}
|
|
7698
8149
|
|
|
7699
8150
|
// src/handlers/status.handlers.ts
|
|
7700
|
-
import
|
|
8151
|
+
import fs6 from "fs";
|
|
8152
|
+
import path8 from "path";
|
|
8153
|
+
import chalk17 from "chalk";
|
|
7701
8154
|
function getAuth2() {
|
|
7702
8155
|
const config2 = getConfig();
|
|
7703
8156
|
return new BraingridAuth(config2.apiUrl);
|
|
7704
8157
|
}
|
|
8158
|
+
function formatFileSize(bytes) {
|
|
8159
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
8160
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
8161
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
8162
|
+
}
|
|
8163
|
+
function getDebugLogsSection() {
|
|
8164
|
+
let section = chalk17.bold("Debug Logs\n");
|
|
8165
|
+
section += `${chalk17.dim("\u2500".repeat(50))}
|
|
8166
|
+
`;
|
|
8167
|
+
const logger4 = getLogger();
|
|
8168
|
+
const cliLogPath = logger4.getLogFilePath();
|
|
8169
|
+
const cliLogSize = logger4.getLogFileSize();
|
|
8170
|
+
if (cliLogSize > 0) {
|
|
8171
|
+
section += `${chalk17.bold("CLI Log:")} ${cliLogPath} (${formatFileSize(cliLogSize * 1024 * 1024)})
|
|
8172
|
+
`;
|
|
8173
|
+
} else {
|
|
8174
|
+
section += `${chalk17.bold("CLI Log:")} ${chalk17.dim("none")}
|
|
8175
|
+
`;
|
|
8176
|
+
}
|
|
8177
|
+
try {
|
|
8178
|
+
const tempDir = ".braingrid/temp";
|
|
8179
|
+
if (fs6.existsSync(tempDir)) {
|
|
8180
|
+
const hookLogFiles = fs6.readdirSync(tempDir).filter((f) => f.endsWith("-debug.log") || f === "build-debug.log");
|
|
8181
|
+
if (hookLogFiles.length > 0) {
|
|
8182
|
+
for (const file of hookLogFiles) {
|
|
8183
|
+
const filePath = path8.join(tempDir, file);
|
|
8184
|
+
const stats = fs6.statSync(filePath);
|
|
8185
|
+
section += `${chalk17.bold("Hook Log:")} ${filePath} (${formatFileSize(stats.size)})
|
|
8186
|
+
`;
|
|
8187
|
+
}
|
|
8188
|
+
} else {
|
|
8189
|
+
section += `${chalk17.bold("Hook Logs:")} ${chalk17.dim("none")}
|
|
8190
|
+
`;
|
|
8191
|
+
}
|
|
8192
|
+
} else {
|
|
8193
|
+
section += `${chalk17.bold("Hook Logs:")} ${chalk17.dim("none")}
|
|
8194
|
+
`;
|
|
8195
|
+
}
|
|
8196
|
+
} catch {
|
|
8197
|
+
section += `${chalk17.bold("Hook Logs:")} ${chalk17.dim("unable to check")}
|
|
8198
|
+
`;
|
|
8199
|
+
}
|
|
8200
|
+
section += "\n";
|
|
8201
|
+
return section;
|
|
8202
|
+
}
|
|
7705
8203
|
async function handleStatus() {
|
|
7706
8204
|
try {
|
|
7707
8205
|
const config2 = getConfig();
|
|
7708
8206
|
const auth = getAuth2();
|
|
7709
|
-
const stop = showSpinner("Checking status",
|
|
8207
|
+
const stop = showSpinner("Checking status", chalk17.gray);
|
|
7710
8208
|
const isAuthenticated = await auth.isAuthenticated(true);
|
|
7711
8209
|
stop();
|
|
7712
|
-
let output =
|
|
7713
|
-
output +=
|
|
7714
|
-
output += `${
|
|
8210
|
+
let output = chalk17.bold.cyan("\n\u{1F9E0} BrainGrid CLI Status\n\n");
|
|
8211
|
+
output += chalk17.bold("Authentication\n");
|
|
8212
|
+
output += `${chalk17.dim("\u2500".repeat(50))}
|
|
7715
8213
|
`;
|
|
7716
8214
|
if (!isAuthenticated) {
|
|
7717
|
-
output +=
|
|
7718
|
-
output +=
|
|
7719
|
-
output +=
|
|
7720
|
-
output += `${
|
|
8215
|
+
output += chalk17.red("\u274C Not authenticated\n");
|
|
8216
|
+
output += chalk17.dim(" Run ") + chalk17.cyan("braingrid login") + chalk17.dim(" to sign in\n\n");
|
|
8217
|
+
output += chalk17.bold("Git Repository\n");
|
|
8218
|
+
output += `${chalk17.dim("\u2500".repeat(50))}
|
|
7721
8219
|
`;
|
|
7722
8220
|
const gitInfo2 = await getGitRepositoryInfo();
|
|
7723
8221
|
if (!gitInfo2 || !gitInfo2.isRepo) {
|
|
7724
|
-
output +=
|
|
8222
|
+
output += chalk17.yellow("\u26A0\uFE0F Not in a git repository\n\n");
|
|
7725
8223
|
} else {
|
|
7726
|
-
output +=
|
|
8224
|
+
output += chalk17.green("\u2705 Repository detected!\n");
|
|
7727
8225
|
if (gitInfo2.owner && gitInfo2.name) {
|
|
7728
|
-
output += `${
|
|
8226
|
+
output += `${chalk17.bold("Owner:")} ${gitInfo2.owner}
|
|
7729
8227
|
`;
|
|
7730
|
-
output += `${
|
|
8228
|
+
output += `${chalk17.bold("Name:")} ${gitInfo2.name}
|
|
7731
8229
|
`;
|
|
7732
8230
|
} else if (gitInfo2.remoteUrl) {
|
|
7733
|
-
output += `${
|
|
8231
|
+
output += `${chalk17.bold("Remote:")} ${gitInfo2.remoteUrl}
|
|
7734
8232
|
`;
|
|
7735
8233
|
} else {
|
|
7736
|
-
output +=
|
|
8234
|
+
output += chalk17.yellow("\u26A0\uFE0F Local repository (no remote)\n");
|
|
7737
8235
|
}
|
|
7738
8236
|
if (gitInfo2.branch) {
|
|
7739
|
-
output += `${
|
|
8237
|
+
output += `${chalk17.bold("Branch:")} ${gitInfo2.branch}
|
|
7740
8238
|
`;
|
|
7741
8239
|
}
|
|
7742
8240
|
if (gitInfo2.userName || gitInfo2.userEmail) {
|
|
7743
8241
|
const userDisplay = gitInfo2.userName && gitInfo2.userEmail ? `${gitInfo2.userName} <${gitInfo2.userEmail}>` : gitInfo2.userName || gitInfo2.userEmail;
|
|
7744
|
-
output += `${
|
|
8242
|
+
output += `${chalk17.bold("Git User:")} ${userDisplay}
|
|
7745
8243
|
`;
|
|
7746
8244
|
} else {
|
|
7747
|
-
output += `${
|
|
8245
|
+
output += `${chalk17.bold("Git User:")} ` + chalk17.yellow("\u26A0\uFE0F Not configured (run git config --global user.name/email)\n");
|
|
7748
8246
|
}
|
|
7749
8247
|
output += "\n";
|
|
7750
8248
|
}
|
|
7751
|
-
output +=
|
|
7752
|
-
output += `${
|
|
8249
|
+
output += chalk17.bold("Installed Coding Tools\n");
|
|
8250
|
+
output += `${chalk17.dim("\u2500".repeat(50))}
|
|
7753
8251
|
`;
|
|
7754
8252
|
const cliToolsUnauthenticated = await checkInstalledCliTools();
|
|
7755
8253
|
const installedTools2 = cliToolsUnauthenticated.filter((tool) => tool.installed);
|
|
7756
8254
|
if (installedTools2.length === 0) {
|
|
7757
|
-
output +=
|
|
8255
|
+
output += chalk17.yellow("\u26A0\uFE0F No coding tools detected\n\n");
|
|
7758
8256
|
} else {
|
|
7759
8257
|
installedTools2.forEach((tool) => {
|
|
7760
|
-
const versionInfo = tool.version ?
|
|
7761
|
-
output += `${
|
|
8258
|
+
const versionInfo = tool.version ? chalk17.dim(` (${tool.version})`) : "";
|
|
8259
|
+
output += `${chalk17.green("\u2705")} ${tool.name}${versionInfo}
|
|
7762
8260
|
`;
|
|
7763
8261
|
});
|
|
7764
8262
|
output += "\n";
|
|
7765
8263
|
}
|
|
7766
|
-
output +=
|
|
7767
|
-
output += `${
|
|
8264
|
+
output += chalk17.bold("Configuration\n");
|
|
8265
|
+
output += `${chalk17.dim("\u2500".repeat(50))}
|
|
7768
8266
|
`;
|
|
7769
|
-
output += `${
|
|
8267
|
+
output += `${chalk17.bold("API URL:")} ${config2.apiUrl}
|
|
7770
8268
|
`;
|
|
7771
|
-
output += `${
|
|
8269
|
+
output += `${chalk17.bold("OAuth Client:")} ${config2.oauthClientId}
|
|
7772
8270
|
`;
|
|
7773
|
-
output += `${
|
|
8271
|
+
output += `${chalk17.bold("Redirect URI:")} http://127.0.0.1:34536/callback
|
|
7774
8272
|
`;
|
|
7775
|
-
output += `${
|
|
8273
|
+
output += `${chalk17.bold("Config Path:")} ${credentialStore.getStoragePath()}
|
|
7776
8274
|
|
|
7777
8275
|
`;
|
|
7778
|
-
output +=
|
|
7779
|
-
output +=
|
|
8276
|
+
output += getDebugLogsSection();
|
|
8277
|
+
output += chalk17.bold("Overall Status\n");
|
|
8278
|
+
output += `${chalk17.dim("\u2500".repeat(50))}
|
|
7780
8279
|
`;
|
|
7781
|
-
output +=
|
|
8280
|
+
output += chalk17.yellow("\u26A0\uFE0F Setup required - please authenticate\n");
|
|
7782
8281
|
return {
|
|
7783
8282
|
success: true,
|
|
7784
8283
|
message: output,
|
|
@@ -7787,94 +8286,95 @@ async function handleStatus() {
|
|
|
7787
8286
|
}
|
|
7788
8287
|
const session = await auth.getStoredSession();
|
|
7789
8288
|
if (!session) {
|
|
7790
|
-
output +=
|
|
8289
|
+
output += chalk17.red("\u274C Session not found\n\n");
|
|
7791
8290
|
return {
|
|
7792
8291
|
success: true,
|
|
7793
8292
|
message: output,
|
|
7794
8293
|
data: { authenticated: false }
|
|
7795
8294
|
};
|
|
7796
8295
|
}
|
|
7797
|
-
output +=
|
|
8296
|
+
output += chalk17.green("\u2705 Authenticated\n");
|
|
7798
8297
|
const userName = `${session.user.firstName || ""} ${session.user.lastName || ""}`.trim();
|
|
7799
8298
|
if (userName) {
|
|
7800
|
-
output += `${
|
|
8299
|
+
output += `${chalk17.bold("Name:")} ${userName}
|
|
7801
8300
|
`;
|
|
7802
8301
|
}
|
|
7803
|
-
output += `${
|
|
8302
|
+
output += `${chalk17.bold("Email:")} ${session.user.email}
|
|
7804
8303
|
`;
|
|
7805
|
-
output += `${
|
|
8304
|
+
output += `${chalk17.bold("User ID:")} ${session.user.id}
|
|
7806
8305
|
`;
|
|
7807
|
-
output += `${
|
|
8306
|
+
output += `${chalk17.bold("Organization:")} ${session.organization_id}
|
|
7808
8307
|
`;
|
|
7809
8308
|
const sessionAge = Math.floor((Date.now() - session.created_at.getTime()) / 1e3 / 60);
|
|
7810
|
-
output += `${
|
|
8309
|
+
output += `${chalk17.bold("Session Age:")} ${sessionAge} minutes
|
|
7811
8310
|
`;
|
|
7812
|
-
output += `${
|
|
8311
|
+
output += `${chalk17.bold("API URL:")} ${config2.apiUrl}
|
|
7813
8312
|
`;
|
|
7814
|
-
output += `${
|
|
8313
|
+
output += `${chalk17.bold("Config Path:")} ${credentialStore.getStoragePath()}
|
|
7815
8314
|
|
|
7816
8315
|
`;
|
|
7817
|
-
output +=
|
|
7818
|
-
output += `${
|
|
8316
|
+
output += chalk17.bold("Git Repository\n");
|
|
8317
|
+
output += `${chalk17.dim("\u2500".repeat(50))}
|
|
7819
8318
|
`;
|
|
7820
8319
|
const gitInfo = await getGitRepositoryInfo();
|
|
7821
8320
|
if (!gitInfo || !gitInfo.isRepo) {
|
|
7822
|
-
output +=
|
|
8321
|
+
output += chalk17.yellow("\u26A0\uFE0F Not in a git repository\n\n");
|
|
7823
8322
|
} else {
|
|
7824
|
-
output +=
|
|
8323
|
+
output += chalk17.green("\u2705 Repository detected!\n");
|
|
7825
8324
|
if (gitInfo.owner && gitInfo.name) {
|
|
7826
|
-
output += `${
|
|
8325
|
+
output += `${chalk17.bold("Owner:")} ${gitInfo.owner}
|
|
7827
8326
|
`;
|
|
7828
|
-
output += `${
|
|
8327
|
+
output += `${chalk17.bold("Name:")} ${gitInfo.name}
|
|
7829
8328
|
`;
|
|
7830
8329
|
} else if (gitInfo.remoteUrl) {
|
|
7831
|
-
output += `${
|
|
8330
|
+
output += `${chalk17.bold("Remote:")} ${gitInfo.remoteUrl}
|
|
7832
8331
|
`;
|
|
7833
8332
|
} else {
|
|
7834
|
-
output +=
|
|
8333
|
+
output += chalk17.yellow("\u26A0\uFE0F Local repository (no remote)\n");
|
|
7835
8334
|
}
|
|
7836
8335
|
if (gitInfo.branch) {
|
|
7837
|
-
output += `${
|
|
8336
|
+
output += `${chalk17.bold("Branch:")} ${gitInfo.branch}
|
|
7838
8337
|
`;
|
|
7839
8338
|
}
|
|
7840
8339
|
if (gitInfo.userName || gitInfo.userEmail) {
|
|
7841
8340
|
const userDisplay = gitInfo.userName && gitInfo.userEmail ? `${gitInfo.userName} <${gitInfo.userEmail}>` : gitInfo.userName || gitInfo.userEmail;
|
|
7842
|
-
output += `${
|
|
8341
|
+
output += `${chalk17.bold("Git User:")} ${userDisplay}
|
|
7843
8342
|
`;
|
|
7844
8343
|
} else {
|
|
7845
|
-
output += `${
|
|
8344
|
+
output += `${chalk17.bold("Git User:")} ` + chalk17.yellow("\u26A0\uFE0F Not configured (run git config --global user.name/email)\n");
|
|
7846
8345
|
}
|
|
7847
8346
|
output += "\n";
|
|
7848
8347
|
}
|
|
7849
|
-
output +=
|
|
7850
|
-
output += `${
|
|
8348
|
+
output += chalk17.bold("Installed Coding Tools\n");
|
|
8349
|
+
output += `${chalk17.dim("\u2500".repeat(50))}
|
|
7851
8350
|
`;
|
|
7852
8351
|
const cliTools = await checkInstalledCliTools();
|
|
7853
8352
|
const installedTools = cliTools.filter((tool) => tool.installed);
|
|
7854
8353
|
if (installedTools.length === 0) {
|
|
7855
|
-
output +=
|
|
8354
|
+
output += chalk17.yellow("\u26A0\uFE0F No coding tools detected\n\n");
|
|
7856
8355
|
} else {
|
|
7857
8356
|
installedTools.forEach((tool) => {
|
|
7858
|
-
const versionInfo = tool.version ?
|
|
7859
|
-
output += `${
|
|
8357
|
+
const versionInfo = tool.version ? chalk17.dim(` (${tool.version})`) : "";
|
|
8358
|
+
output += `${chalk17.green("\u2705")} ${tool.name}${versionInfo}
|
|
7860
8359
|
`;
|
|
7861
8360
|
});
|
|
7862
8361
|
output += "\n";
|
|
7863
8362
|
}
|
|
7864
|
-
output +=
|
|
7865
|
-
output += `${
|
|
8363
|
+
output += chalk17.bold("Configuration\n");
|
|
8364
|
+
output += `${chalk17.dim("\u2500".repeat(50))}
|
|
7866
8365
|
`;
|
|
7867
|
-
output += `${
|
|
8366
|
+
output += `${chalk17.bold("OAuth Client:")} ${config2.oauthClientId}
|
|
7868
8367
|
`;
|
|
7869
|
-
output += `${
|
|
8368
|
+
output += `${chalk17.bold("Redirect URI:")} http://127.0.0.1:34536/callback
|
|
7870
8369
|
`;
|
|
7871
|
-
output += `${
|
|
8370
|
+
output += `${chalk17.bold("Auth Server:")} ${config2.getWorkOSAuthUrl()}
|
|
7872
8371
|
|
|
7873
8372
|
`;
|
|
7874
|
-
output +=
|
|
7875
|
-
output +=
|
|
8373
|
+
output += getDebugLogsSection();
|
|
8374
|
+
output += chalk17.bold("Overall Status\n");
|
|
8375
|
+
output += `${chalk17.dim("\u2500".repeat(50))}
|
|
7876
8376
|
`;
|
|
7877
|
-
output +=
|
|
8377
|
+
output += chalk17.green("\u2705 Ready to use BrainGrid CLI\n");
|
|
7878
8378
|
return {
|
|
7879
8379
|
success: true,
|
|
7880
8380
|
message: output,
|
|
@@ -7889,7 +8389,7 @@ async function handleStatus() {
|
|
|
7889
8389
|
}
|
|
7890
8390
|
|
|
7891
8391
|
// src/handlers/task.handlers.ts
|
|
7892
|
-
import
|
|
8392
|
+
import chalk18 from "chalk";
|
|
7893
8393
|
|
|
7894
8394
|
// src/services/task-service.ts
|
|
7895
8395
|
var TaskService = class {
|
|
@@ -7993,7 +8493,7 @@ async function handleTaskList(opts) {
|
|
|
7993
8493
|
if (!isAuthenticated) {
|
|
7994
8494
|
return {
|
|
7995
8495
|
success: false,
|
|
7996
|
-
message:
|
|
8496
|
+
message: chalk18.red("\u274C Not authenticated. Please run `braingrid login` first.")
|
|
7997
8497
|
};
|
|
7998
8498
|
}
|
|
7999
8499
|
const workspace = await workspaceManager.getProject(opts.project);
|
|
@@ -8013,7 +8513,7 @@ async function handleTaskList(opts) {
|
|
|
8013
8513
|
}
|
|
8014
8514
|
const requirementId = requirementResult.requirementId;
|
|
8015
8515
|
if (opts.format !== "json" && opts.format !== "xml") {
|
|
8016
|
-
stopSpinner = showSpinner("Loading tasks",
|
|
8516
|
+
stopSpinner = showSpinner("Loading tasks", chalk18.gray);
|
|
8017
8517
|
}
|
|
8018
8518
|
const response = await taskService.listTasks(projectId, requirementId, {
|
|
8019
8519
|
page: opts.page ? parseInt(opts.page, 10) : 1,
|
|
@@ -8026,7 +8526,7 @@ async function handleTaskList(opts) {
|
|
|
8026
8526
|
if (response.tasks.length === 0) {
|
|
8027
8527
|
return {
|
|
8028
8528
|
success: true,
|
|
8029
|
-
message:
|
|
8529
|
+
message: chalk18.yellow("No tasks found."),
|
|
8030
8530
|
data: response
|
|
8031
8531
|
};
|
|
8032
8532
|
}
|
|
@@ -8042,7 +8542,7 @@ async function handleTaskList(opts) {
|
|
|
8042
8542
|
});
|
|
8043
8543
|
if (response.pagination?.has_more) {
|
|
8044
8544
|
output += `
|
|
8045
|
-
${
|
|
8545
|
+
${chalk18.yellow("\u26A0\uFE0F More tasks exist. Use --limit to see more.")}`;
|
|
8046
8546
|
}
|
|
8047
8547
|
return {
|
|
8048
8548
|
success: true,
|
|
@@ -8067,7 +8567,7 @@ async function handleTaskSummary(opts) {
|
|
|
8067
8567
|
if (!isAuthenticated) {
|
|
8068
8568
|
return {
|
|
8069
8569
|
success: false,
|
|
8070
|
-
message:
|
|
8570
|
+
message: chalk18.red("\u274C Not authenticated. Please run `braingrid login` first.")
|
|
8071
8571
|
};
|
|
8072
8572
|
}
|
|
8073
8573
|
const workspace = await workspaceManager.getProject(opts.project);
|
|
@@ -8086,7 +8586,7 @@ async function handleTaskSummary(opts) {
|
|
|
8086
8586
|
};
|
|
8087
8587
|
}
|
|
8088
8588
|
const requirementId = requirementResult.requirementId;
|
|
8089
|
-
stopSpinner = showSpinner("Loading tasks",
|
|
8589
|
+
stopSpinner = showSpinner("Loading tasks", chalk18.gray);
|
|
8090
8590
|
const response = await taskService.listTasks(projectId, requirementId, {
|
|
8091
8591
|
page: opts.page ? parseInt(opts.page, 10) : 1,
|
|
8092
8592
|
limit: opts.limit ? parseInt(opts.limit, 10) : 20
|
|
@@ -8096,7 +8596,7 @@ async function handleTaskSummary(opts) {
|
|
|
8096
8596
|
if (response.tasks.length === 0) {
|
|
8097
8597
|
return {
|
|
8098
8598
|
success: true,
|
|
8099
|
-
message:
|
|
8599
|
+
message: chalk18.yellow("No tasks found."),
|
|
8100
8600
|
data: response
|
|
8101
8601
|
};
|
|
8102
8602
|
}
|
|
@@ -8111,7 +8611,7 @@ async function handleTaskSummary(opts) {
|
|
|
8111
8611
|
});
|
|
8112
8612
|
if (response.pagination?.has_more) {
|
|
8113
8613
|
output += `
|
|
8114
|
-
${
|
|
8614
|
+
${chalk18.yellow("\u26A0\uFE0F More tasks exist. Use --limit to see more.")}`;
|
|
8115
8615
|
}
|
|
8116
8616
|
return {
|
|
8117
8617
|
success: true,
|
|
@@ -8136,14 +8636,14 @@ async function handleTaskShow(id, opts) {
|
|
|
8136
8636
|
if (!isAuthenticated) {
|
|
8137
8637
|
return {
|
|
8138
8638
|
success: false,
|
|
8139
|
-
message:
|
|
8639
|
+
message: chalk18.red("\u274C Not authenticated. Please run `braingrid login` first.")
|
|
8140
8640
|
};
|
|
8141
8641
|
}
|
|
8142
8642
|
const format = opts?.format || "markdown";
|
|
8143
8643
|
if (!["markdown", "json", "xml", "table"].includes(format)) {
|
|
8144
8644
|
return {
|
|
8145
8645
|
success: false,
|
|
8146
|
-
message:
|
|
8646
|
+
message: chalk18.red(
|
|
8147
8647
|
`\u274C Invalid format: ${format}. Supported formats: markdown, json, xml, table`
|
|
8148
8648
|
)
|
|
8149
8649
|
};
|
|
@@ -8173,14 +8673,14 @@ async function handleTaskShow(id, opts) {
|
|
|
8173
8673
|
if (!currentTask.success) {
|
|
8174
8674
|
return {
|
|
8175
8675
|
success: false,
|
|
8176
|
-
message:
|
|
8676
|
+
message: chalk18.red(`\u274C ${currentTask.error}`)
|
|
8177
8677
|
};
|
|
8178
8678
|
}
|
|
8179
8679
|
taskId = currentTask.taskId;
|
|
8180
8680
|
taskWarning = currentTask.warning;
|
|
8181
8681
|
}
|
|
8182
8682
|
const config2 = getConfig();
|
|
8183
|
-
stopSpinner = showSpinner("Loading task",
|
|
8683
|
+
stopSpinner = showSpinner("Loading task", chalk18.gray);
|
|
8184
8684
|
const task2 = await taskService.getTask(projectId, requirementId, taskId);
|
|
8185
8685
|
stopSpinner();
|
|
8186
8686
|
stopSpinner = null;
|
|
@@ -8213,7 +8713,7 @@ async function handleTaskShow(id, opts) {
|
|
|
8213
8713
|
}
|
|
8214
8714
|
if (taskWarning) {
|
|
8215
8715
|
output += `
|
|
8216
|
-
${
|
|
8716
|
+
${chalk18.yellow(`\u26A0\uFE0F ${taskWarning}`)}`;
|
|
8217
8717
|
}
|
|
8218
8718
|
return {
|
|
8219
8719
|
success: true,
|
|
@@ -8238,7 +8738,7 @@ async function handleTaskCreate(opts) {
|
|
|
8238
8738
|
if (!isAuthenticated) {
|
|
8239
8739
|
return {
|
|
8240
8740
|
success: false,
|
|
8241
|
-
message:
|
|
8741
|
+
message: chalk18.red("\u274C Not authenticated. Please run `braingrid login` first.")
|
|
8242
8742
|
};
|
|
8243
8743
|
}
|
|
8244
8744
|
const workspace = await workspaceManager.getProject(opts.project);
|
|
@@ -8260,7 +8760,7 @@ async function handleTaskCreate(opts) {
|
|
|
8260
8760
|
const projectShortId = projectId;
|
|
8261
8761
|
const requirementShortId = opts.requirement || requirementId;
|
|
8262
8762
|
const config2 = getConfig();
|
|
8263
|
-
stopSpinner = showSpinner("Creating task",
|
|
8763
|
+
stopSpinner = showSpinner("Creating task", chalk18.gray);
|
|
8264
8764
|
const task2 = await taskService.createTask(projectId, requirementId, {
|
|
8265
8765
|
title: opts.title,
|
|
8266
8766
|
content: opts.content,
|
|
@@ -8299,13 +8799,13 @@ async function handleTaskUpdate(id, opts) {
|
|
|
8299
8799
|
if (!isAuthenticated) {
|
|
8300
8800
|
return {
|
|
8301
8801
|
success: false,
|
|
8302
|
-
message:
|
|
8802
|
+
message: chalk18.red("\u274C Not authenticated. Please run `braingrid login` first.")
|
|
8303
8803
|
};
|
|
8304
8804
|
}
|
|
8305
8805
|
if (!opts?.status && !opts?.title && !opts?.externalId) {
|
|
8306
8806
|
return {
|
|
8307
8807
|
success: false,
|
|
8308
|
-
message:
|
|
8808
|
+
message: chalk18.red(
|
|
8309
8809
|
"\u274C Please provide at least one field to update (--status, --title, or --external-id)"
|
|
8310
8810
|
)
|
|
8311
8811
|
};
|
|
@@ -8335,14 +8835,14 @@ async function handleTaskUpdate(id, opts) {
|
|
|
8335
8835
|
if (!currentTask.success) {
|
|
8336
8836
|
return {
|
|
8337
8837
|
success: false,
|
|
8338
|
-
message:
|
|
8838
|
+
message: chalk18.red(`\u274C ${currentTask.error}`)
|
|
8339
8839
|
};
|
|
8340
8840
|
}
|
|
8341
8841
|
taskId = currentTask.taskId;
|
|
8342
8842
|
taskWarning = currentTask.warning;
|
|
8343
8843
|
}
|
|
8344
8844
|
const config2 = getConfig();
|
|
8345
|
-
stopSpinner = showSpinner("Updating task",
|
|
8845
|
+
stopSpinner = showSpinner("Updating task", chalk18.gray);
|
|
8346
8846
|
const task2 = await taskService.updateTask(projectId, requirementId, taskId, {
|
|
8347
8847
|
status: opts?.status,
|
|
8348
8848
|
title: opts?.title,
|
|
@@ -8360,7 +8860,7 @@ async function handleTaskUpdate(id, opts) {
|
|
|
8360
8860
|
});
|
|
8361
8861
|
if (taskWarning) {
|
|
8362
8862
|
output += `
|
|
8363
|
-
${
|
|
8863
|
+
${chalk18.yellow(`\u26A0\uFE0F ${taskWarning}`)}`;
|
|
8364
8864
|
}
|
|
8365
8865
|
return {
|
|
8366
8866
|
success: true,
|
|
@@ -8385,13 +8885,13 @@ async function handleTaskDelete(id, opts) {
|
|
|
8385
8885
|
if (!isAuthenticated) {
|
|
8386
8886
|
return {
|
|
8387
8887
|
success: false,
|
|
8388
|
-
message:
|
|
8888
|
+
message: chalk18.red("\u274C Not authenticated. Please run `braingrid login` first.")
|
|
8389
8889
|
};
|
|
8390
8890
|
}
|
|
8391
8891
|
if (!opts.force) {
|
|
8392
8892
|
return {
|
|
8393
8893
|
success: false,
|
|
8394
|
-
message:
|
|
8894
|
+
message: chalk18.yellow("\u26A0\uFE0F Deleting a task is permanent. Use --force to confirm deletion.")
|
|
8395
8895
|
};
|
|
8396
8896
|
}
|
|
8397
8897
|
const workspace = await workspaceManager.getProject(opts.project);
|
|
@@ -8411,13 +8911,13 @@ async function handleTaskDelete(id, opts) {
|
|
|
8411
8911
|
}
|
|
8412
8912
|
const requirementId = requirementResult.requirementId;
|
|
8413
8913
|
const taskId = normalizeTaskId(id);
|
|
8414
|
-
stopSpinner = showSpinner("Deleting task",
|
|
8914
|
+
stopSpinner = showSpinner("Deleting task", chalk18.gray);
|
|
8415
8915
|
await taskService.deleteTask(projectId, requirementId, taskId);
|
|
8416
8916
|
stopSpinner();
|
|
8417
8917
|
stopSpinner = null;
|
|
8418
8918
|
return {
|
|
8419
8919
|
success: true,
|
|
8420
|
-
message:
|
|
8920
|
+
message: chalk18.green(`\u2705 Deleted task ${id}`)
|
|
8421
8921
|
};
|
|
8422
8922
|
} catch (error) {
|
|
8423
8923
|
if (stopSpinner) {
|
|
@@ -8437,14 +8937,14 @@ async function handleTaskSpecify(opts) {
|
|
|
8437
8937
|
if (!isAuthenticated) {
|
|
8438
8938
|
return {
|
|
8439
8939
|
success: false,
|
|
8440
|
-
message:
|
|
8940
|
+
message: chalk18.red("\u274C Not authenticated. Please run `braingrid login` first.")
|
|
8441
8941
|
};
|
|
8442
8942
|
}
|
|
8443
8943
|
const format = opts.format || "table";
|
|
8444
8944
|
if (!["table", "json", "markdown"].includes(format)) {
|
|
8445
8945
|
return {
|
|
8446
8946
|
success: false,
|
|
8447
|
-
message:
|
|
8947
|
+
message: chalk18.red(
|
|
8448
8948
|
`\u274C Invalid format: ${format}. Supported formats: table, json, markdown`
|
|
8449
8949
|
)
|
|
8450
8950
|
};
|
|
@@ -8468,16 +8968,16 @@ async function handleTaskSpecify(opts) {
|
|
|
8468
8968
|
if (opts.prompt.length < 10) {
|
|
8469
8969
|
return {
|
|
8470
8970
|
success: false,
|
|
8471
|
-
message:
|
|
8971
|
+
message: chalk18.red("\u274C Prompt must be at least 10 characters long")
|
|
8472
8972
|
};
|
|
8473
8973
|
}
|
|
8474
8974
|
if (opts.prompt.length > 5e3) {
|
|
8475
8975
|
return {
|
|
8476
8976
|
success: false,
|
|
8477
|
-
message:
|
|
8977
|
+
message: chalk18.red("\u274C Prompt must be no more than 5000 characters long")
|
|
8478
8978
|
};
|
|
8479
8979
|
}
|
|
8480
|
-
stopSpinner = showSpinner("Creating task from prompt...",
|
|
8980
|
+
stopSpinner = showSpinner("Creating task from prompt...", chalk18.gray);
|
|
8481
8981
|
const response = await taskService.specifyTask(projectId, requirementId, {
|
|
8482
8982
|
prompt: opts.prompt
|
|
8483
8983
|
});
|
|
@@ -8517,10 +9017,10 @@ async function handleTaskSpecify(opts) {
|
|
|
8517
9017
|
function formatTaskSpecifyTable(response) {
|
|
8518
9018
|
const task2 = response.task;
|
|
8519
9019
|
const lines = [];
|
|
8520
|
-
lines.push(
|
|
9020
|
+
lines.push(chalk18.green(`\u2705 Created task ${response.requirement_short_id}/TASK-${task2.number}`));
|
|
8521
9021
|
lines.push("");
|
|
8522
|
-
lines.push(`${
|
|
8523
|
-
lines.push(`${
|
|
9022
|
+
lines.push(`${chalk18.bold("Title:")} ${task2.title}`);
|
|
9023
|
+
lines.push(`${chalk18.bold("Status:")} ${task2.status}`);
|
|
8524
9024
|
return lines.join("\n");
|
|
8525
9025
|
}
|
|
8526
9026
|
function formatTaskSpecifyMarkdown(response, _apiUrl) {
|