@braingrid/cli 0.2.5 → 0.2.6
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 +36 -0
- package/dist/{chunk-KNVWIF3L.js → chunk-RAV3B4UY.js} +59 -7
- package/dist/chunk-RAV3B4UY.js.map +1 -0
- package/dist/cli.js +820 -744
- package/dist/cli.js.map +1 -1
- package/dist/{gh-installer-B65ZOOC4.js → gh-installer-LNR25TO7.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-KNVWIF3L.js.map +0 -1
- /package/dist/{gh-installer-B65ZOOC4.js.map → gh-installer-LNR25TO7.js.map} +0 -0
package/dist/cli.js
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
isGhInstalled,
|
|
9
9
|
isHomebrewInstalled,
|
|
10
10
|
isWingetAvailable
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-RAV3B4UY.js";
|
|
12
12
|
|
|
13
13
|
// src/cli.ts
|
|
14
14
|
import { Command } from "commander";
|
|
@@ -422,7 +422,7 @@ import axios3, { AxiosError as AxiosError2 } from "axios";
|
|
|
422
422
|
|
|
423
423
|
// src/build-config.ts
|
|
424
424
|
var BUILD_ENV = true ? "production" : process.env.NODE_ENV === "test" ? "development" : "production";
|
|
425
|
-
var CLI_VERSION = true ? "0.2.
|
|
425
|
+
var CLI_VERSION = true ? "0.2.6" : "0.0.0-test";
|
|
426
426
|
var PRODUCTION_CONFIG = {
|
|
427
427
|
apiUrl: "https://app.braingrid.ai",
|
|
428
428
|
workosAuthUrl: "https://sensitive-harvest-60.authkit.app",
|
|
@@ -4956,18 +4956,14 @@ async function handleStatus() {
|
|
|
4956
4956
|
output += chalk10.bold("Installed Coding Tools\n");
|
|
4957
4957
|
output += chalk10.dim("\u2500".repeat(50)) + "\n";
|
|
4958
4958
|
const cliToolsUnauthenticated = await checkInstalledCliTools();
|
|
4959
|
-
|
|
4959
|
+
const installedTools2 = cliToolsUnauthenticated.filter((tool) => tool.installed);
|
|
4960
|
+
if (installedTools2.length === 0) {
|
|
4960
4961
|
output += chalk10.yellow("\u26A0\uFE0F No coding tools detected\n\n");
|
|
4961
4962
|
} else {
|
|
4962
|
-
|
|
4963
|
-
|
|
4964
|
-
|
|
4965
|
-
output += `${chalk10.green("\u2705")} ${tool.name}${versionInfo}
|
|
4966
|
-
`;
|
|
4967
|
-
} else {
|
|
4968
|
-
output += `${chalk10.dim("\u25CB")} ${tool.name} ${chalk10.dim("(not installed)")}
|
|
4963
|
+
installedTools2.forEach((tool) => {
|
|
4964
|
+
const versionInfo = tool.version ? chalk10.dim(` (${tool.version})`) : "";
|
|
4965
|
+
output += `${chalk10.green("\u2705")} ${tool.name}${versionInfo}
|
|
4969
4966
|
`;
|
|
4970
|
-
}
|
|
4971
4967
|
});
|
|
4972
4968
|
output += "\n";
|
|
4973
4969
|
}
|
|
@@ -5054,18 +5050,14 @@ async function handleStatus() {
|
|
|
5054
5050
|
output += chalk10.bold("Installed Coding Tools\n");
|
|
5055
5051
|
output += chalk10.dim("\u2500".repeat(50)) + "\n";
|
|
5056
5052
|
const cliTools = await checkInstalledCliTools();
|
|
5057
|
-
|
|
5053
|
+
const installedTools = cliTools.filter((tool) => tool.installed);
|
|
5054
|
+
if (installedTools.length === 0) {
|
|
5058
5055
|
output += chalk10.yellow("\u26A0\uFE0F No coding tools detected\n\n");
|
|
5059
5056
|
} else {
|
|
5060
|
-
|
|
5061
|
-
|
|
5062
|
-
|
|
5063
|
-
output += `${chalk10.green("\u2705")} ${tool.name}${versionInfo}
|
|
5064
|
-
`;
|
|
5065
|
-
} else {
|
|
5066
|
-
output += `${chalk10.dim("\u25CB")} ${tool.name} ${chalk10.dim("(not installed)")}
|
|
5057
|
+
installedTools.forEach((tool) => {
|
|
5058
|
+
const versionInfo = tool.version ? chalk10.dim(` (${tool.version})`) : "";
|
|
5059
|
+
output += `${chalk10.green("\u2705")} ${tool.name}${versionInfo}
|
|
5067
5060
|
`;
|
|
5068
|
-
}
|
|
5069
5061
|
});
|
|
5070
5062
|
output += "\n";
|
|
5071
5063
|
}
|
|
@@ -5095,8 +5087,8 @@ async function handleStatus() {
|
|
|
5095
5087
|
}
|
|
5096
5088
|
|
|
5097
5089
|
// src/handlers/init.handlers.ts
|
|
5098
|
-
import
|
|
5099
|
-
import { confirm, select, input } from "@inquirer/prompts";
|
|
5090
|
+
import chalk13 from "chalk";
|
|
5091
|
+
import { confirm, select as select2, input } from "@inquirer/prompts";
|
|
5100
5092
|
|
|
5101
5093
|
// src/services/internal/github-service.ts
|
|
5102
5094
|
var GitHubService = class {
|
|
@@ -5363,108 +5355,551 @@ function getManualInstallInstructions() {
|
|
|
5363
5355
|
}
|
|
5364
5356
|
}
|
|
5365
5357
|
|
|
5366
|
-
// src/
|
|
5367
|
-
import
|
|
5358
|
+
// src/handlers/setup.handlers.ts
|
|
5359
|
+
import chalk12 from "chalk";
|
|
5360
|
+
import { select } from "@inquirer/prompts";
|
|
5361
|
+
import * as path4 from "path";
|
|
5362
|
+
import * as fs4 from "fs/promises";
|
|
5363
|
+
|
|
5364
|
+
// src/utils/command-execution.ts
|
|
5365
|
+
import { exec as exec3, spawn } from "child_process";
|
|
5368
5366
|
import { promisify as promisify3 } from "util";
|
|
5369
|
-
|
|
5370
|
-
|
|
5371
|
-
|
|
5367
|
+
var execAsyncReal = promisify3(exec3);
|
|
5368
|
+
async function execAsync3(command, options, isTestMode = false, mockExecHandler) {
|
|
5369
|
+
if (isTestMode && mockExecHandler) {
|
|
5370
|
+
return mockExecHandler(command);
|
|
5371
|
+
}
|
|
5372
|
+
const defaultOptions = {
|
|
5373
|
+
maxBuffer: 1024 * 1024 * 10,
|
|
5374
|
+
// 10MB default
|
|
5375
|
+
timeout: 3e5,
|
|
5376
|
+
// 5 minutes
|
|
5377
|
+
...options
|
|
5378
|
+
};
|
|
5379
|
+
if (command.includes("claude")) {
|
|
5380
|
+
defaultOptions.maxBuffer = 1024 * 1024 * 50;
|
|
5381
|
+
defaultOptions.timeout = 6e5;
|
|
5382
|
+
}
|
|
5383
|
+
return execAsyncReal(command, defaultOptions);
|
|
5384
|
+
}
|
|
5385
|
+
|
|
5386
|
+
// src/services/setup-service.ts
|
|
5387
|
+
import * as fs3 from "fs/promises";
|
|
5388
|
+
import * as path3 from "path";
|
|
5389
|
+
var GITHUB_OWNER = "BrainGridAI";
|
|
5390
|
+
var GITHUB_REPO = "braingrid";
|
|
5391
|
+
var MAX_RETRIES = 3;
|
|
5392
|
+
var INITIAL_RETRY_DELAY = 100;
|
|
5393
|
+
var BEGIN_MARKER = "<!-- BEGIN BRAINGRID INTEGRATION -->";
|
|
5394
|
+
var END_MARKER = "<!-- END BRAINGRID INTEGRATION -->";
|
|
5395
|
+
async function withRetry(fn, retries = MAX_RETRIES, delay = INITIAL_RETRY_DELAY) {
|
|
5372
5396
|
try {
|
|
5373
|
-
await
|
|
5374
|
-
|
|
5375
|
-
|
|
5376
|
-
|
|
5397
|
+
return await fn();
|
|
5398
|
+
} catch (error) {
|
|
5399
|
+
if (retries === 0) {
|
|
5400
|
+
throw error;
|
|
5401
|
+
}
|
|
5402
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
5403
|
+
const isNetworkError = errorMessage.includes("ECONNRESET") || errorMessage.includes("ETIMEDOUT") || errorMessage.includes("ENOTFOUND") || errorMessage.includes("network");
|
|
5404
|
+
if (!isNetworkError) {
|
|
5405
|
+
throw error;
|
|
5406
|
+
}
|
|
5407
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
5408
|
+
return withRetry(fn, retries - 1, delay * 2);
|
|
5377
5409
|
}
|
|
5378
5410
|
}
|
|
5379
|
-
|
|
5411
|
+
function parseGitHubError(error) {
|
|
5412
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
5413
|
+
if (errorMessage.includes("404") || errorMessage.includes("Not Found")) {
|
|
5414
|
+
return "File or directory not found in BrainGrid repository";
|
|
5415
|
+
}
|
|
5416
|
+
if (errorMessage.includes("403") || errorMessage.includes("rate limit")) {
|
|
5417
|
+
return "GitHub API rate limit exceeded. Please wait a few minutes and try again.\nCheck rate limit status: gh api rate_limit";
|
|
5418
|
+
}
|
|
5419
|
+
if (errorMessage.includes("401") || errorMessage.includes("Unauthorized")) {
|
|
5420
|
+
return "GitHub CLI is not authenticated. Run: gh auth login";
|
|
5421
|
+
}
|
|
5380
5422
|
try {
|
|
5381
|
-
|
|
5382
|
-
|
|
5423
|
+
const match = errorMessage.match(/\{.*\}/s);
|
|
5424
|
+
if (match) {
|
|
5425
|
+
const errorData = JSON.parse(match[0]);
|
|
5426
|
+
return errorData.message || errorMessage;
|
|
5427
|
+
}
|
|
5383
5428
|
} catch {
|
|
5384
|
-
return false;
|
|
5385
5429
|
}
|
|
5430
|
+
return errorMessage;
|
|
5386
5431
|
}
|
|
5387
|
-
function
|
|
5388
|
-
return
|
|
5432
|
+
async function fetchFileFromGitHub(path6) {
|
|
5433
|
+
return withRetry(async () => {
|
|
5434
|
+
try {
|
|
5435
|
+
const command = `gh api repos/${GITHUB_OWNER}/${GITHUB_REPO}/contents/${path6}`;
|
|
5436
|
+
const { stdout } = await execAsync3(command);
|
|
5437
|
+
const response = JSON.parse(stdout);
|
|
5438
|
+
if (response.type !== "file") {
|
|
5439
|
+
throw new Error(`Path ${path6} is not a file`);
|
|
5440
|
+
}
|
|
5441
|
+
if (!response.content || !response.encoding) {
|
|
5442
|
+
throw new Error(`No content found for file ${path6}`);
|
|
5443
|
+
}
|
|
5444
|
+
if (response.encoding !== "base64") {
|
|
5445
|
+
throw new Error(`Unexpected encoding: ${response.encoding}`);
|
|
5446
|
+
}
|
|
5447
|
+
const content = Buffer.from(response.content, "base64").toString("utf8");
|
|
5448
|
+
return content;
|
|
5449
|
+
} catch (error) {
|
|
5450
|
+
const parsedError = parseGitHubError(error);
|
|
5451
|
+
throw new Error(`Failed to fetch file ${path6}: ${parsedError}`);
|
|
5452
|
+
}
|
|
5453
|
+
});
|
|
5389
5454
|
}
|
|
5390
|
-
async function
|
|
5455
|
+
async function listGitHubDirectory(path6) {
|
|
5456
|
+
return withRetry(async () => {
|
|
5457
|
+
try {
|
|
5458
|
+
const command = `gh api repos/${GITHUB_OWNER}/${GITHUB_REPO}/contents/${path6}`;
|
|
5459
|
+
const { stdout } = await execAsync3(command);
|
|
5460
|
+
const response = JSON.parse(stdout);
|
|
5461
|
+
if (!Array.isArray(response)) {
|
|
5462
|
+
throw new Error(`Path ${path6} is not a directory`);
|
|
5463
|
+
}
|
|
5464
|
+
return response.map((item) => ({
|
|
5465
|
+
name: item.name,
|
|
5466
|
+
type: item.type,
|
|
5467
|
+
path: item.path
|
|
5468
|
+
}));
|
|
5469
|
+
} catch (error) {
|
|
5470
|
+
const parsedError = parseGitHubError(error);
|
|
5471
|
+
throw new Error(`Failed to list directory ${path6}: ${parsedError}`);
|
|
5472
|
+
}
|
|
5473
|
+
});
|
|
5474
|
+
}
|
|
5475
|
+
async function copyFileFromGitHub(sourcePath, targetPath) {
|
|
5391
5476
|
try {
|
|
5392
|
-
const
|
|
5393
|
-
const
|
|
5394
|
-
|
|
5395
|
-
|
|
5396
|
-
|
|
5397
|
-
const
|
|
5398
|
-
|
|
5399
|
-
|
|
5477
|
+
const content = await fetchFileFromGitHub(sourcePath);
|
|
5478
|
+
const parentDir = path3.dirname(targetPath);
|
|
5479
|
+
await fs3.mkdir(parentDir, { recursive: true });
|
|
5480
|
+
await fs3.writeFile(targetPath, content, { encoding: "utf8", mode: 420 });
|
|
5481
|
+
} catch (error) {
|
|
5482
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
5483
|
+
throw new Error(`Failed to copy file ${sourcePath} to ${targetPath}: ${errorMessage}`);
|
|
5484
|
+
}
|
|
5485
|
+
}
|
|
5486
|
+
async function injectContentIntoFile(targetPath, content) {
|
|
5487
|
+
try {
|
|
5488
|
+
let fileContent;
|
|
5489
|
+
let fileExists3 = false;
|
|
5490
|
+
try {
|
|
5491
|
+
fileContent = await fs3.readFile(targetPath, "utf8");
|
|
5492
|
+
fileExists3 = true;
|
|
5493
|
+
} catch {
|
|
5494
|
+
fileContent = "";
|
|
5400
5495
|
}
|
|
5401
|
-
|
|
5402
|
-
|
|
5403
|
-
|
|
5404
|
-
|
|
5405
|
-
|
|
5406
|
-
|
|
5407
|
-
|
|
5496
|
+
if (fileExists3) {
|
|
5497
|
+
const beginIndex = fileContent.indexOf(BEGIN_MARKER);
|
|
5498
|
+
const endIndex = fileContent.indexOf(END_MARKER);
|
|
5499
|
+
if (beginIndex !== -1 && endIndex !== -1 && endIndex > beginIndex) {
|
|
5500
|
+
const before = fileContent.substring(0, beginIndex);
|
|
5501
|
+
const after = fileContent.substring(endIndex + END_MARKER.length);
|
|
5502
|
+
const newContent = `${before}${BEGIN_MARKER}
|
|
5503
|
+
${content}
|
|
5504
|
+
${END_MARKER}${after}`;
|
|
5505
|
+
await fs3.writeFile(targetPath, newContent, { encoding: "utf8" });
|
|
5506
|
+
} else {
|
|
5507
|
+
const newContent = `${fileContent}
|
|
5508
|
+
|
|
5509
|
+
${BEGIN_MARKER}
|
|
5510
|
+
${content}
|
|
5511
|
+
${END_MARKER}
|
|
5512
|
+
`;
|
|
5513
|
+
await fs3.writeFile(targetPath, newContent, { encoding: "utf8" });
|
|
5514
|
+
}
|
|
5515
|
+
} else {
|
|
5516
|
+
const parentDir = path3.dirname(targetPath);
|
|
5517
|
+
await fs3.mkdir(parentDir, { recursive: true });
|
|
5518
|
+
const newContent = `${BEGIN_MARKER}
|
|
5519
|
+
${content}
|
|
5520
|
+
${END_MARKER}
|
|
5521
|
+
`;
|
|
5522
|
+
await fs3.writeFile(targetPath, newContent, { encoding: "utf8", mode: 420 });
|
|
5523
|
+
}
|
|
5524
|
+
} catch (error) {
|
|
5525
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
5526
|
+
throw new Error(`Failed to inject content into ${targetPath}: ${errorMessage}`);
|
|
5408
5527
|
}
|
|
5409
5528
|
}
|
|
5410
|
-
async function
|
|
5411
|
-
|
|
5412
|
-
|
|
5413
|
-
|
|
5414
|
-
|
|
5529
|
+
async function installStatusLineScript(scriptContent, targetPath = ".claude/statusline.sh") {
|
|
5530
|
+
try {
|
|
5531
|
+
const parentDir = path3.dirname(targetPath);
|
|
5532
|
+
await fs3.mkdir(parentDir, { recursive: true });
|
|
5533
|
+
await fs3.writeFile(targetPath, scriptContent, { encoding: "utf8", mode: 493 });
|
|
5534
|
+
} catch (error) {
|
|
5535
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
5536
|
+
throw new Error(`Failed to install status line script to ${targetPath}: ${errorMessage}`);
|
|
5415
5537
|
}
|
|
5416
|
-
return isGhAuthenticated();
|
|
5417
5538
|
}
|
|
5418
|
-
|
|
5419
|
-
|
|
5420
|
-
|
|
5421
|
-
|
|
5422
|
-
|
|
5423
|
-
|
|
5424
|
-
|
|
5425
|
-
|
|
5426
|
-
|
|
5539
|
+
async function updateClaudeSettings(settingsPath = ".claude/settings.json", scriptPath = ".claude/statusline.sh") {
|
|
5540
|
+
try {
|
|
5541
|
+
let settings = {};
|
|
5542
|
+
try {
|
|
5543
|
+
const content2 = await fs3.readFile(settingsPath, "utf8");
|
|
5544
|
+
settings = JSON.parse(content2);
|
|
5545
|
+
} catch {
|
|
5546
|
+
}
|
|
5547
|
+
settings.statusLine = {
|
|
5548
|
+
type: "command",
|
|
5549
|
+
command: scriptPath,
|
|
5550
|
+
padding: 0
|
|
5551
|
+
};
|
|
5552
|
+
const parentDir = path3.dirname(settingsPath);
|
|
5553
|
+
await fs3.mkdir(parentDir, { recursive: true });
|
|
5554
|
+
const content = JSON.stringify(settings, null, 2);
|
|
5555
|
+
await fs3.writeFile(settingsPath, content, { encoding: "utf8" });
|
|
5556
|
+
} catch (error) {
|
|
5557
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
5558
|
+
throw new Error(`Failed to update Claude settings at ${settingsPath}: ${errorMessage}`);
|
|
5559
|
+
}
|
|
5427
5560
|
}
|
|
5428
|
-
function promptToAddOrganization(owner, webUrl) {
|
|
5429
|
-
return {
|
|
5430
|
-
success: false,
|
|
5431
|
-
message: chalk12.yellow("\u26A0\uFE0F No projects found for this repository.\n\n") + chalk12.dim(`Repository: ${owner}/*
|
|
5432
|
-
|
|
5433
|
-
`) + chalk12.dim(`You have GitHub connected, but not for the "${owner}" organization.
|
|
5434
5561
|
|
|
5435
|
-
|
|
5436
|
-
|
|
5437
|
-
|
|
5562
|
+
// src/handlers/setup.handlers.ts
|
|
5563
|
+
async function fileExists(filePath) {
|
|
5564
|
+
try {
|
|
5565
|
+
await fs4.access(filePath);
|
|
5566
|
+
return true;
|
|
5567
|
+
} catch {
|
|
5568
|
+
return false;
|
|
5569
|
+
}
|
|
5438
5570
|
}
|
|
5439
|
-
async function
|
|
5440
|
-
|
|
5441
|
-
|
|
5571
|
+
async function checkPrerequisites() {
|
|
5572
|
+
try {
|
|
5573
|
+
await execAsync3("gh --version");
|
|
5574
|
+
} catch {
|
|
5442
5575
|
return {
|
|
5443
5576
|
success: false,
|
|
5444
|
-
message: chalk12.red("\u274C
|
|
5577
|
+
message: chalk12.red("\u274C GitHub CLI is not installed.\n\n") + chalk12.dim("Install instructions:\n") + chalk12.dim(" macOS: ") + chalk12.cyan("brew install gh") + chalk12.dim("\n") + chalk12.dim(" Windows: ") + chalk12.cyan("winget install GitHub.CLI") + chalk12.dim("\n") + chalk12.dim(" Linux: See ") + chalk12.cyan("https://cli.github.com/manual/installation") + chalk12.dim("\n\n") + chalk12.dim("After installing, run: ") + chalk12.cyan("gh auth login")
|
|
5445
5578
|
};
|
|
5446
5579
|
}
|
|
5447
|
-
|
|
5448
|
-
|
|
5580
|
+
try {
|
|
5581
|
+
await execAsync3("gh auth status");
|
|
5582
|
+
} catch {
|
|
5449
5583
|
return {
|
|
5450
5584
|
success: false,
|
|
5451
|
-
message: chalk12.
|
|
5452
|
-
|
|
5453
|
-
`) + chalk12.dim("Please try again or create a project manually at: ") + chalk12.cyan(webUrl)
|
|
5585
|
+
message: chalk12.red("\u274C Not authenticated with GitHub CLI.\n\n") + chalk12.dim("Please run: ") + chalk12.cyan("gh auth login")
|
|
5454
5586
|
};
|
|
5455
5587
|
}
|
|
5456
|
-
|
|
5457
|
-
|
|
5458
|
-
|
|
5459
|
-
|
|
5460
|
-
|
|
5461
|
-
|
|
5462
|
-
|
|
5463
|
-
|
|
5588
|
+
return null;
|
|
5589
|
+
}
|
|
5590
|
+
async function getFileList(sourcePaths, targetPaths) {
|
|
5591
|
+
const operations = [];
|
|
5592
|
+
for (let i = 0; i < sourcePaths.length; i++) {
|
|
5593
|
+
const sourcePath = sourcePaths[i];
|
|
5594
|
+
const targetPath = targetPaths[i];
|
|
5595
|
+
try {
|
|
5596
|
+
const items = await listGitHubDirectory(sourcePath);
|
|
5597
|
+
for (const item of items) {
|
|
5598
|
+
if (item.type === "file") {
|
|
5599
|
+
const itemTargetPath = path4.join(targetPath, item.name);
|
|
5600
|
+
const exists = await fileExists(itemTargetPath);
|
|
5601
|
+
operations.push({
|
|
5602
|
+
type: "copy",
|
|
5603
|
+
sourcePath: item.path,
|
|
5604
|
+
targetPath: itemTargetPath,
|
|
5605
|
+
exists
|
|
5606
|
+
});
|
|
5607
|
+
}
|
|
5608
|
+
}
|
|
5609
|
+
} catch (error) {
|
|
5610
|
+
console.warn(
|
|
5611
|
+
chalk12.yellow(`\u26A0\uFE0F Could not list directory: ${sourcePath}`),
|
|
5612
|
+
error instanceof Error ? error.message : String(error)
|
|
5613
|
+
);
|
|
5614
|
+
}
|
|
5615
|
+
}
|
|
5616
|
+
return operations;
|
|
5617
|
+
}
|
|
5618
|
+
function displayInstallationPlan(operations, injectionFile) {
|
|
5619
|
+
console.log(chalk12.bold("\n\u{1F4CB} Installation Plan:\n"));
|
|
5620
|
+
console.log(chalk12.cyan(" Content Injection:"));
|
|
5621
|
+
console.log(chalk12.dim(` ${injectionFile}`));
|
|
5622
|
+
const newFiles = operations.filter((op) => !op.exists);
|
|
5623
|
+
const existingFiles = operations.filter((op) => op.exists);
|
|
5624
|
+
if (newFiles.length > 0) {
|
|
5625
|
+
console.log(chalk12.cyan("\n New Files:"));
|
|
5626
|
+
for (const op of newFiles) {
|
|
5627
|
+
console.log(chalk12.dim(` ${op.targetPath}`));
|
|
5628
|
+
}
|
|
5629
|
+
}
|
|
5630
|
+
if (existingFiles.length > 0) {
|
|
5631
|
+
console.log(chalk12.yellow("\n Existing Files (will prompt):"));
|
|
5632
|
+
for (const op of existingFiles) {
|
|
5633
|
+
console.log(chalk12.dim(` ${op.targetPath}`));
|
|
5634
|
+
}
|
|
5635
|
+
}
|
|
5636
|
+
console.log("");
|
|
5637
|
+
}
|
|
5638
|
+
async function promptForConflict(filePath) {
|
|
5639
|
+
const answer = await select({
|
|
5640
|
+
message: chalk12.yellow(`File exists: ${filePath}`),
|
|
5641
|
+
choices: [
|
|
5642
|
+
{ name: "[O]verwrite - Replace this file", value: "overwrite" },
|
|
5643
|
+
{ name: "[S]kip - Keep existing file", value: "skip" },
|
|
5644
|
+
{ name: "[A]ll - Overwrite all remaining", value: "all" },
|
|
5645
|
+
{ name: "[Q]uit - Cancel installation", value: "quit" }
|
|
5646
|
+
]
|
|
5647
|
+
});
|
|
5648
|
+
return answer;
|
|
5649
|
+
}
|
|
5650
|
+
async function installFiles(operations, force) {
|
|
5651
|
+
let installed = 0;
|
|
5652
|
+
let skipped = 0;
|
|
5653
|
+
let overwriteAll = force;
|
|
5654
|
+
for (const operation of operations) {
|
|
5655
|
+
if (operation.exists && !overwriteAll) {
|
|
5656
|
+
const response = await promptForConflict(operation.targetPath);
|
|
5657
|
+
if (response === "quit") {
|
|
5658
|
+
return { installed, skipped, cancelled: true };
|
|
5659
|
+
} else if (response === "skip") {
|
|
5660
|
+
skipped++;
|
|
5661
|
+
continue;
|
|
5662
|
+
} else if (response === "all") {
|
|
5663
|
+
overwriteAll = true;
|
|
5664
|
+
}
|
|
5665
|
+
}
|
|
5666
|
+
try {
|
|
5667
|
+
await copyFileFromGitHub(operation.sourcePath, operation.targetPath);
|
|
5668
|
+
installed++;
|
|
5669
|
+
} catch (error) {
|
|
5670
|
+
console.error(
|
|
5671
|
+
chalk12.red(`Failed to copy ${operation.targetPath}:`),
|
|
5672
|
+
error instanceof Error ? error.message : String(error)
|
|
5673
|
+
);
|
|
5674
|
+
skipped++;
|
|
5675
|
+
}
|
|
5676
|
+
}
|
|
5677
|
+
return { installed, skipped, cancelled: false };
|
|
5678
|
+
}
|
|
5679
|
+
async function _handleSetup(config, opts) {
|
|
5680
|
+
try {
|
|
5681
|
+
const prerequisiteError = await checkPrerequisites();
|
|
5682
|
+
if (prerequisiteError) {
|
|
5683
|
+
return prerequisiteError;
|
|
5684
|
+
}
|
|
5685
|
+
console.log(chalk12.bold(`\u{1F680} Setting up ${config.name} integration...
|
|
5686
|
+
`));
|
|
5687
|
+
const operations = await getFileList(config.sourceDirs, config.targetDirs);
|
|
5688
|
+
const injectionFileExists = await fileExists(config.injection.targetFile);
|
|
5689
|
+
operations.push({
|
|
5690
|
+
type: "inject",
|
|
5691
|
+
sourcePath: config.injection.sourceFile,
|
|
5692
|
+
targetPath: config.injection.targetFile,
|
|
5693
|
+
exists: injectionFileExists
|
|
5694
|
+
});
|
|
5695
|
+
displayInstallationPlan(
|
|
5696
|
+
operations.filter((op) => op.type === "copy"),
|
|
5697
|
+
config.injection.targetFile
|
|
5698
|
+
);
|
|
5699
|
+
if (opts.dryRun) {
|
|
5700
|
+
return {
|
|
5701
|
+
success: true,
|
|
5702
|
+
message: chalk12.green("\u2705 Dry-run complete. No files were modified.\n\n") + chalk12.dim(`Would install ${operations.length} files.`)
|
|
5703
|
+
};
|
|
5704
|
+
}
|
|
5705
|
+
const copyOps = operations.filter((op) => op.type === "copy");
|
|
5706
|
+
const result = await installFiles(copyOps, opts.force || false);
|
|
5707
|
+
if (result.cancelled) {
|
|
5708
|
+
return {
|
|
5709
|
+
success: false,
|
|
5710
|
+
message: chalk12.yellow("\u26A0\uFE0F Installation cancelled.\n\n") + chalk12.dim(`Installed: ${result.installed}, Skipped: ${result.skipped}`),
|
|
5711
|
+
code: "CANCELLED"
|
|
5712
|
+
};
|
|
5713
|
+
}
|
|
5714
|
+
try {
|
|
5715
|
+
const content = await fetchFileFromGitHub(config.injection.sourceFile);
|
|
5716
|
+
await injectContentIntoFile(config.injection.targetFile, content);
|
|
5717
|
+
} catch (error) {
|
|
5718
|
+
console.error(
|
|
5719
|
+
chalk12.red(`Failed to inject content into ${config.injection.targetFile}:`),
|
|
5720
|
+
error instanceof Error ? error.message : String(error)
|
|
5721
|
+
);
|
|
5722
|
+
}
|
|
5723
|
+
let statusLineInstalled = false;
|
|
5724
|
+
if (config.name === "Claude Code") {
|
|
5725
|
+
try {
|
|
5726
|
+
const scriptContent = await fetchFileFromGitHub("claude-code/statusline.sh");
|
|
5727
|
+
await installStatusLineScript(scriptContent);
|
|
5728
|
+
await updateClaudeSettings();
|
|
5729
|
+
statusLineInstalled = true;
|
|
5730
|
+
} catch (error) {
|
|
5731
|
+
console.error(
|
|
5732
|
+
chalk12.yellow("\u26A0\uFE0F Failed to install status line script:"),
|
|
5733
|
+
error instanceof Error ? error.message : String(error)
|
|
5734
|
+
);
|
|
5735
|
+
}
|
|
5736
|
+
}
|
|
5737
|
+
const statusLineMessage = statusLineInstalled ? chalk12.dim(" Status line: .claude/statusline.sh\n") : "";
|
|
5738
|
+
return {
|
|
5739
|
+
success: true,
|
|
5740
|
+
message: chalk12.green(`\u2705 ${config.name} integration installed successfully!
|
|
5741
|
+
|
|
5742
|
+
`) + chalk12.dim("Files installed:\n") + chalk12.dim(` Commands: ${result.installed} files
|
|
5743
|
+
`) + statusLineMessage + chalk12.dim(` Content injected into: ${config.injection.targetFile}
|
|
5744
|
+
|
|
5745
|
+
`) + chalk12.dim("Next steps:\n") + chalk12.dim(" 1. Review the integration files\n") + chalk12.dim(` 2. Open ${config.name}
|
|
5746
|
+
`) + chalk12.dim(" 3. Try the /specify or /breakdown commands\n") + chalk12.dim(" 4. Learn more: ") + chalk12.cyan(config.docsUrl)
|
|
5747
|
+
};
|
|
5748
|
+
} catch (error) {
|
|
5749
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
5750
|
+
return {
|
|
5751
|
+
success: false,
|
|
5752
|
+
message: chalk12.red(`\u274C Setup failed: ${errorMessage}`)
|
|
5753
|
+
};
|
|
5754
|
+
}
|
|
5755
|
+
}
|
|
5756
|
+
async function handleSetupClaudeCode(opts) {
|
|
5757
|
+
const config = {
|
|
5758
|
+
name: "Claude Code",
|
|
5759
|
+
sourceDirs: ["claude-code/commands", "claude-code/skills"],
|
|
5760
|
+
targetDirs: [".claude/commands", ".claude/skills/braingrid-cli"],
|
|
5761
|
+
injection: {
|
|
5762
|
+
sourceFile: "claude-code/CLAUDE.md",
|
|
5763
|
+
targetFile: "CLAUDE.md"
|
|
5764
|
+
},
|
|
5765
|
+
docsUrl: "https://braingrid.ai/docs/claude-code"
|
|
5766
|
+
};
|
|
5767
|
+
return _handleSetup(config, opts);
|
|
5768
|
+
}
|
|
5769
|
+
async function handleSetupCursor(opts) {
|
|
5770
|
+
const config = {
|
|
5771
|
+
name: "Cursor",
|
|
5772
|
+
sourceDirs: ["cursor/commands", "cursor/rules"],
|
|
5773
|
+
targetDirs: [".cursor/commands", ".cursor/rules"],
|
|
5774
|
+
injection: {
|
|
5775
|
+
sourceFile: "cursor/AGENTS.md",
|
|
5776
|
+
targetFile: "AGENTS.md"
|
|
5777
|
+
},
|
|
5778
|
+
docsUrl: "https://braingrid.ai/docs/cursor"
|
|
5779
|
+
};
|
|
5780
|
+
return _handleSetup(config, opts);
|
|
5781
|
+
}
|
|
5782
|
+
|
|
5783
|
+
// src/handlers/init.handlers.ts
|
|
5784
|
+
import { access as access3 } from "fs/promises";
|
|
5785
|
+
|
|
5786
|
+
// src/utils/github-repo.ts
|
|
5787
|
+
import { exec as exec4 } from "child_process";
|
|
5788
|
+
import { promisify as promisify4 } from "util";
|
|
5789
|
+
import path5 from "path";
|
|
5790
|
+
var execAsync4 = promisify4(exec4);
|
|
5791
|
+
async function initGitRepo() {
|
|
5792
|
+
try {
|
|
5793
|
+
await execAsync4("git init");
|
|
5794
|
+
return true;
|
|
5795
|
+
} catch {
|
|
5796
|
+
return false;
|
|
5797
|
+
}
|
|
5798
|
+
}
|
|
5799
|
+
async function isGhAuthenticated() {
|
|
5800
|
+
try {
|
|
5801
|
+
await execAsync4("gh auth status");
|
|
5802
|
+
return true;
|
|
5803
|
+
} catch {
|
|
5804
|
+
return false;
|
|
5805
|
+
}
|
|
5806
|
+
}
|
|
5807
|
+
function getCurrentDirectoryName() {
|
|
5808
|
+
return path5.basename(process.cwd());
|
|
5809
|
+
}
|
|
5810
|
+
async function createGitHubRepoWithGh(name, isPrivate) {
|
|
5811
|
+
try {
|
|
5812
|
+
const visibility = isPrivate ? "--private" : "--public";
|
|
5813
|
+
const { stdout } = await execAsync4(
|
|
5814
|
+
`gh repo create ${name} ${visibility} --source=. --remote=origin`,
|
|
5815
|
+
{ timeout: 6e4 }
|
|
5816
|
+
);
|
|
5817
|
+
const urlMatch = stdout.match(/https:\/\/github\.com\/([^/]+)\/([^/\s]+)/);
|
|
5818
|
+
if (!urlMatch) {
|
|
5819
|
+
return null;
|
|
5820
|
+
}
|
|
5821
|
+
return {
|
|
5822
|
+
owner: urlMatch[1],
|
|
5823
|
+
name: urlMatch[2],
|
|
5824
|
+
url: urlMatch[0]
|
|
5825
|
+
};
|
|
5826
|
+
} catch {
|
|
5827
|
+
return null;
|
|
5828
|
+
}
|
|
5829
|
+
}
|
|
5830
|
+
async function canUseGhAutomation() {
|
|
5831
|
+
const { isGhInstalled: isGhInstalled2 } = await import("./gh-installer-LNR25TO7.js");
|
|
5832
|
+
const ghInstalled = await isGhInstalled2();
|
|
5833
|
+
if (!ghInstalled) {
|
|
5834
|
+
return false;
|
|
5835
|
+
}
|
|
5836
|
+
return isGhAuthenticated();
|
|
5837
|
+
}
|
|
5838
|
+
|
|
5839
|
+
// src/handlers/init.handlers.ts
|
|
5840
|
+
async function detectInstalledIDEs() {
|
|
5841
|
+
const tools = await checkInstalledCliTools();
|
|
5842
|
+
return {
|
|
5843
|
+
claudeCode: tools.find((t) => t.name === "Claude Code")?.installed || false,
|
|
5844
|
+
cursor: tools.find((t) => t.name === "Cursor")?.installed || false
|
|
5845
|
+
};
|
|
5846
|
+
}
|
|
5847
|
+
async function fileExists2(filePath) {
|
|
5848
|
+
try {
|
|
5849
|
+
await access3(filePath);
|
|
5850
|
+
return true;
|
|
5851
|
+
} catch {
|
|
5852
|
+
return false;
|
|
5853
|
+
}
|
|
5854
|
+
}
|
|
5855
|
+
function getServices4() {
|
|
5856
|
+
const config = getConfig();
|
|
5857
|
+
const auth = new BraingridAuth(config.apiUrl);
|
|
5858
|
+
const projectService = new ProjectService(config.apiUrl, auth);
|
|
5859
|
+
const githubService = new GitHubService(config.apiUrl, auth);
|
|
5860
|
+
const repositoryService = new RepositoryService(config.apiUrl, auth);
|
|
5861
|
+
return { projectService, githubService, repositoryService, auth };
|
|
5862
|
+
}
|
|
5863
|
+
function promptToAddOrganization(owner, webUrl) {
|
|
5864
|
+
return {
|
|
5865
|
+
success: false,
|
|
5866
|
+
message: chalk13.yellow("\u26A0\uFE0F No projects found for this repository.\n\n") + chalk13.dim(`Repository: ${owner}/*
|
|
5867
|
+
|
|
5868
|
+
`) + chalk13.dim(`You have GitHub connected, but not for the "${owner}" organization.
|
|
5869
|
+
|
|
5870
|
+
`) + chalk13.dim("To connect ") + chalk13.cyan(owner) + chalk13.dim(":\n") + chalk13.dim(" 1. Visit: ") + chalk13.cyan(`${webUrl}/integrations`) + chalk13.dim("\n") + chalk13.dim(' 2. Click "Add GitHub Organization"\n') + chalk13.dim(` 3. Select "${owner}"
|
|
5871
|
+
`) + chalk13.dim(" 4. Run ") + chalk13.cyan("braingrid init") + chalk13.dim(" again")
|
|
5872
|
+
};
|
|
5873
|
+
}
|
|
5874
|
+
async function promptToCreateProject(gitInfo, projectService, repositoryService) {
|
|
5875
|
+
const webUrl = getConfig().getWebAppUrl();
|
|
5876
|
+
if (!gitInfo.owner || !gitInfo.name) {
|
|
5877
|
+
return {
|
|
5878
|
+
success: false,
|
|
5879
|
+
message: chalk13.red("\u274C Repository information is incomplete")
|
|
5880
|
+
};
|
|
5881
|
+
}
|
|
5882
|
+
const repositoryId = await getRepositoryId(repositoryService, gitInfo.owner, gitInfo.name);
|
|
5883
|
+
if (!repositoryId) {
|
|
5884
|
+
return {
|
|
5885
|
+
success: false,
|
|
5886
|
+
message: chalk13.yellow("\u26A0\uFE0F Repository accessible but could not retrieve details.\n\n") + chalk13.dim(`Repository: ${gitInfo.owner}/${gitInfo.name}
|
|
5887
|
+
|
|
5888
|
+
`) + chalk13.dim("Please try again or create a project manually at: ") + chalk13.cyan(webUrl)
|
|
5889
|
+
};
|
|
5890
|
+
}
|
|
5891
|
+
console.log(
|
|
5892
|
+
chalk13.yellow("\u26A0\uFE0F Repository accessible but no project exists.\n\n") + chalk13.dim(`Repository: ${gitInfo.owner}/${gitInfo.name}
|
|
5893
|
+
`)
|
|
5894
|
+
);
|
|
5895
|
+
const shouldCreate = await confirm({
|
|
5896
|
+
message: `Would you like to create a project for ${gitInfo.name} now?`,
|
|
5897
|
+
default: true
|
|
5898
|
+
});
|
|
5464
5899
|
if (!shouldCreate) {
|
|
5465
5900
|
return {
|
|
5466
5901
|
success: false,
|
|
5467
|
-
message:
|
|
5902
|
+
message: chalk13.dim("\nProject creation cancelled.\n\n") + chalk13.dim("Create a project at ") + chalk13.cyan(webUrl) + chalk13.dim(" and link it to this repository, or use:\n") + chalk13.cyan(
|
|
5468
5903
|
`braingrid project create --name "${gitInfo.name}" --repositories "${gitInfo.owner}/${gitInfo.name}"`
|
|
5469
5904
|
)
|
|
5470
5905
|
};
|
|
@@ -5475,9 +5910,9 @@ async function promptToCreateProject(gitInfo, projectService, repositoryService)
|
|
|
5475
5910
|
description: `Project for ${gitInfo.owner}/${gitInfo.name}`,
|
|
5476
5911
|
repository_id: repositoryId
|
|
5477
5912
|
});
|
|
5478
|
-
console.log(
|
|
5913
|
+
console.log(chalk13.green(`
|
|
5479
5914
|
\u2705 Created project ${project2.short_id}: ${project2.name}`));
|
|
5480
|
-
console.log(
|
|
5915
|
+
console.log(chalk13.green(`\u2705 Linked repository ${gitInfo.owner}/${gitInfo.name}
|
|
5481
5916
|
`));
|
|
5482
5917
|
return { success: true, message: "", data: project2 };
|
|
5483
5918
|
} catch (error) {
|
|
@@ -5491,18 +5926,18 @@ async function promptToGrantRepositoryAccess(gitInfo, webUrl, repositoryService,
|
|
|
5491
5926
|
if (!gitInfo.owner || !gitInfo.name) {
|
|
5492
5927
|
return {
|
|
5493
5928
|
success: false,
|
|
5494
|
-
message:
|
|
5929
|
+
message: chalk13.red("\u274C Repository information is incomplete")
|
|
5495
5930
|
};
|
|
5496
5931
|
}
|
|
5497
5932
|
const owner = gitInfo.owner;
|
|
5498
5933
|
const name = gitInfo.name;
|
|
5499
5934
|
console.log(
|
|
5500
|
-
|
|
5935
|
+
chalk13.yellow("\u26A0\uFE0F Repository found but BrainGrid needs access.\n\n") + chalk13.dim(`Repository: ${owner}/${name}
|
|
5501
5936
|
|
|
5502
|
-
`) +
|
|
5937
|
+
`) + chalk13.dim("Please grant BrainGrid access to this repository:\n") + chalk13.dim(" 1. Visit: ") + chalk13.cyan(`${webUrl}/integrations`) + chalk13.dim("\n") + chalk13.dim(
|
|
5503
5938
|
` 2. Click on your "${owner}" installation "Add/Remove" to grant BrainGrid access to your repository
|
|
5504
5939
|
`
|
|
5505
|
-
) +
|
|
5940
|
+
) + chalk13.dim(` 3. Select "${name}" and save
|
|
5506
5941
|
|
|
5507
5942
|
`)
|
|
5508
5943
|
);
|
|
@@ -5515,10 +5950,10 @@ async function promptToGrantRepositoryAccess(gitInfo, webUrl, repositoryService,
|
|
|
5515
5950
|
if (!accessGranted) {
|
|
5516
5951
|
return {
|
|
5517
5952
|
success: false,
|
|
5518
|
-
message:
|
|
5953
|
+
message: chalk13.yellow("\n\u26A0\uFE0F Repository access not detected within 3 minutes.\n\n") + chalk13.dim("Please grant access at: ") + chalk13.cyan(`${webUrl}/integrations`) + chalk13.dim(" and run ") + chalk13.cyan("braingrid init") + chalk13.dim(" again.")
|
|
5519
5954
|
};
|
|
5520
5955
|
}
|
|
5521
|
-
console.log(
|
|
5956
|
+
console.log(chalk13.green("\u2705 Repository access granted!\n"));
|
|
5522
5957
|
return promptToCreateProject(gitInfo, projectService, repositoryService);
|
|
5523
5958
|
}
|
|
5524
5959
|
async function handleNoProjectForRepository(owner, name, gitInfo, githubService, repositoryService, projectService, config) {
|
|
@@ -5533,9 +5968,9 @@ async function handleNoProjectForRepository(owner, name, gitInfo, githubService,
|
|
|
5533
5968
|
if (allInstallations.length === 0) {
|
|
5534
5969
|
return {
|
|
5535
5970
|
success: false,
|
|
5536
|
-
message:
|
|
5971
|
+
message: chalk13.yellow("\u26A0\uFE0F No projects found for this repository.\n\n") + chalk13.dim(`Repository: ${owner}/${name}
|
|
5537
5972
|
|
|
5538
|
-
`) +
|
|
5973
|
+
`) + chalk13.dim("It looks like you haven't connected your GitHub account yet.\n") + chalk13.dim("Please connect GitHub at: ") + chalk13.cyan(`${webUrl}/integrations`) + chalk13.dim("\n\nOnce connected, create a project and link it to this repository.")
|
|
5539
5974
|
};
|
|
5540
5975
|
}
|
|
5541
5976
|
const ownerInstallation = findInstallationForOwner(owner, allInstallations);
|
|
@@ -5551,28 +5986,28 @@ async function handleNoProjectForRepository(owner, name, gitInfo, githubService,
|
|
|
5551
5986
|
function showSetupInstructions(scenario) {
|
|
5552
5987
|
let message = "";
|
|
5553
5988
|
if (scenario === "no-git") {
|
|
5554
|
-
message +=
|
|
5555
|
-
message +=
|
|
5556
|
-
message +=
|
|
5989
|
+
message += chalk13.dim("To initialize BrainGrid locally:\n\n");
|
|
5990
|
+
message += chalk13.dim(" 1. Initialize git:\n");
|
|
5991
|
+
message += chalk13.cyan(" git init\n\n");
|
|
5557
5992
|
} else {
|
|
5558
|
-
message +=
|
|
5993
|
+
message += chalk13.dim("To connect to GitHub:\n\n");
|
|
5559
5994
|
}
|
|
5560
|
-
message +=
|
|
5561
|
-
message +=
|
|
5562
|
-
message +=
|
|
5563
|
-
message +=
|
|
5995
|
+
message += chalk13.dim(" 2. Create GitHub repository:\n");
|
|
5996
|
+
message += chalk13.dim(" \u2022 Install GitHub CLI: ") + chalk13.cyan("https://cli.github.com\n");
|
|
5997
|
+
message += chalk13.dim(" Then: ") + chalk13.cyan("gh repo create --private --source=.\n");
|
|
5998
|
+
message += chalk13.dim(" \u2022 Or manually: ") + chalk13.cyan("https://github.com/new\n\n");
|
|
5564
5999
|
if (scenario === "no-git") {
|
|
5565
|
-
message +=
|
|
6000
|
+
message += chalk13.dim(" 3. Run: ") + chalk13.cyan("braingrid init\n\n");
|
|
5566
6001
|
} else {
|
|
5567
|
-
message +=
|
|
5568
|
-
message +=
|
|
5569
|
-
message +=
|
|
5570
|
-
}
|
|
5571
|
-
message +=
|
|
5572
|
-
message +=
|
|
5573
|
-
message +=
|
|
5574
|
-
message +=
|
|
5575
|
-
message +=
|
|
6002
|
+
message += chalk13.dim(" 3. Add remote and run init:\n");
|
|
6003
|
+
message += chalk13.cyan(" git remote add origin <url>\n");
|
|
6004
|
+
message += chalk13.cyan(" braingrid init\n\n");
|
|
6005
|
+
}
|
|
6006
|
+
message += chalk13.bold("Or use BrainGrid without local initialization:\n\n");
|
|
6007
|
+
message += chalk13.dim(" All commands support the --project flag:\n");
|
|
6008
|
+
message += chalk13.cyan(" braingrid requirement list --project PROJ-123\n");
|
|
6009
|
+
message += chalk13.cyan(' braingrid task create --project PROJ-123 --title "Task"\n\n');
|
|
6010
|
+
message += chalk13.dim(" Note: Without local init, you must specify --project for each command.") + "\n";
|
|
5576
6011
|
return message;
|
|
5577
6012
|
}
|
|
5578
6013
|
async function handleNoGitRepository() {
|
|
@@ -5588,12 +6023,12 @@ async function handleNoGitRepository() {
|
|
|
5588
6023
|
if (!gitInitSuccess) {
|
|
5589
6024
|
return {
|
|
5590
6025
|
success: false,
|
|
5591
|
-
message:
|
|
6026
|
+
message: chalk13.red("\u274C Failed to initialize git repository")
|
|
5592
6027
|
};
|
|
5593
6028
|
}
|
|
5594
|
-
console.log(
|
|
6029
|
+
console.log(chalk13.green("\u2705 Initialized git repository"));
|
|
5595
6030
|
const dirName = getCurrentDirectoryName();
|
|
5596
|
-
const isPrivate = await
|
|
6031
|
+
const isPrivate = await select2({
|
|
5597
6032
|
message: "Repository visibility:",
|
|
5598
6033
|
choices: [
|
|
5599
6034
|
{ value: true, name: "Private" },
|
|
@@ -5605,16 +6040,16 @@ async function handleNoGitRepository() {
|
|
|
5605
6040
|
message: "Repository name:",
|
|
5606
6041
|
default: dirName
|
|
5607
6042
|
});
|
|
5608
|
-
console.log(
|
|
6043
|
+
console.log(chalk13.dim("\nCreating repository...\n"));
|
|
5609
6044
|
const repo = await createGitHubRepoWithGh(repoName, isPrivate);
|
|
5610
6045
|
if (!repo) {
|
|
5611
6046
|
return {
|
|
5612
6047
|
success: false,
|
|
5613
|
-
message:
|
|
6048
|
+
message: chalk13.red("\u274C Failed to create GitHub repository\n\n") + showSetupInstructions("no-git")
|
|
5614
6049
|
};
|
|
5615
6050
|
}
|
|
5616
|
-
console.log(
|
|
5617
|
-
console.log(
|
|
6051
|
+
console.log(chalk13.green(`\u2705 Created repository: ${repo.url}`));
|
|
6052
|
+
console.log(chalk13.green("\u2705 Added remote origin\n"));
|
|
5618
6053
|
return {
|
|
5619
6054
|
success: true,
|
|
5620
6055
|
message: "continue-init",
|
|
@@ -5625,7 +6060,7 @@ async function handleNoGitRepository() {
|
|
|
5625
6060
|
}
|
|
5626
6061
|
return {
|
|
5627
6062
|
success: false,
|
|
5628
|
-
message:
|
|
6063
|
+
message: chalk13.yellow("\u26A0\uFE0F This directory is not a git repository.\n\n") + showSetupInstructions("no-git")
|
|
5629
6064
|
};
|
|
5630
6065
|
}
|
|
5631
6066
|
async function handleNoGitRemote() {
|
|
@@ -5637,7 +6072,7 @@ async function handleNoGitRemote() {
|
|
|
5637
6072
|
});
|
|
5638
6073
|
if (shouldCreate) {
|
|
5639
6074
|
const dirName = getCurrentDirectoryName();
|
|
5640
|
-
const isPrivate = await
|
|
6075
|
+
const isPrivate = await select2({
|
|
5641
6076
|
message: "Repository visibility:",
|
|
5642
6077
|
choices: [
|
|
5643
6078
|
{ value: true, name: "Private" },
|
|
@@ -5649,16 +6084,16 @@ async function handleNoGitRemote() {
|
|
|
5649
6084
|
message: "Repository name:",
|
|
5650
6085
|
default: dirName
|
|
5651
6086
|
});
|
|
5652
|
-
console.log(
|
|
6087
|
+
console.log(chalk13.dim("\nCreating repository...\n"));
|
|
5653
6088
|
const repo = await createGitHubRepoWithGh(repoName, isPrivate);
|
|
5654
6089
|
if (!repo) {
|
|
5655
6090
|
return {
|
|
5656
6091
|
success: false,
|
|
5657
|
-
message:
|
|
6092
|
+
message: chalk13.red("\u274C Failed to create GitHub repository\n\n") + showSetupInstructions("no-remote")
|
|
5658
6093
|
};
|
|
5659
6094
|
}
|
|
5660
|
-
console.log(
|
|
5661
|
-
console.log(
|
|
6095
|
+
console.log(chalk13.green(`\u2705 Created repository: ${repo.url}`));
|
|
6096
|
+
console.log(chalk13.green("\u2705 Added remote origin\n"));
|
|
5662
6097
|
return {
|
|
5663
6098
|
success: true,
|
|
5664
6099
|
message: "continue-init",
|
|
@@ -5669,7 +6104,7 @@ async function handleNoGitRemote() {
|
|
|
5669
6104
|
}
|
|
5670
6105
|
return {
|
|
5671
6106
|
success: false,
|
|
5672
|
-
message:
|
|
6107
|
+
message: chalk13.yellow("\u26A0\uFE0F Git repository detected but no GitHub remote configured.\n\n") + showSetupInstructions("no-remote")
|
|
5673
6108
|
};
|
|
5674
6109
|
}
|
|
5675
6110
|
async function handleInit(opts) {
|
|
@@ -5684,7 +6119,7 @@ async function handleInit(opts) {
|
|
|
5684
6119
|
if (!shouldInstall) {
|
|
5685
6120
|
return {
|
|
5686
6121
|
success: false,
|
|
5687
|
-
message:
|
|
6122
|
+
message: chalk13.yellow("\u26A0\uFE0F Git installation cancelled.\n\n") + getManualInstallInstructions()
|
|
5688
6123
|
};
|
|
5689
6124
|
}
|
|
5690
6125
|
console.log();
|
|
@@ -5700,14 +6135,14 @@ async function handleInit(opts) {
|
|
|
5700
6135
|
if (!await isGitInstalled()) {
|
|
5701
6136
|
return {
|
|
5702
6137
|
success: false,
|
|
5703
|
-
message:
|
|
6138
|
+
message: chalk13.red("\u274C Git installation completed but git command not found\n\n") + chalk13.dim("You may need to restart your terminal or add Git to your PATH.\n") + getManualInstallInstructions()
|
|
5704
6139
|
};
|
|
5705
6140
|
}
|
|
5706
6141
|
}
|
|
5707
6142
|
if (!await isGhInstalled()) {
|
|
5708
|
-
console.log(
|
|
6143
|
+
console.log(chalk13.blue("\n\u{1F4A1} GitHub CLI is highly recommended for working with BrainGrid."));
|
|
5709
6144
|
console.log(
|
|
5710
|
-
|
|
6145
|
+
chalk13.dim(" It enables seamless GitHub integration and repository management.\n")
|
|
5711
6146
|
);
|
|
5712
6147
|
const shouldInstallGh = await confirm({
|
|
5713
6148
|
message: "Would you like to install GitHub CLI now?",
|
|
@@ -5721,10 +6156,10 @@ async function handleInit(opts) {
|
|
|
5721
6156
|
console.log();
|
|
5722
6157
|
} else {
|
|
5723
6158
|
console.log(ghInstallResult.message);
|
|
5724
|
-
console.log(
|
|
6159
|
+
console.log(chalk13.dim("You can install it manually later.\n"));
|
|
5725
6160
|
}
|
|
5726
6161
|
} else {
|
|
5727
|
-
console.log(
|
|
6162
|
+
console.log(chalk13.dim("Skipping GitHub CLI installation.\n"));
|
|
5728
6163
|
}
|
|
5729
6164
|
}
|
|
5730
6165
|
if (await projectConfigExists() && !opts.force) {
|
|
@@ -5732,15 +6167,15 @@ async function handleInit(opts) {
|
|
|
5732
6167
|
const existing = await loadProjectConfig();
|
|
5733
6168
|
return {
|
|
5734
6169
|
success: false,
|
|
5735
|
-
message:
|
|
5736
|
-
`) +
|
|
6170
|
+
message: chalk13.yellow("\u26A0\uFE0F Already initialized.\n\n") + chalk13.dim(`Project: ${existing.project_name} (${existing.project_short_id})
|
|
6171
|
+
`) + chalk13.dim(`Repository: ${existing.repository?.full_name || "N/A"}
|
|
5737
6172
|
|
|
5738
|
-
`) +
|
|
6173
|
+
`) + chalk13.dim("Use --force to reinitialize")
|
|
5739
6174
|
};
|
|
5740
6175
|
} catch {
|
|
5741
6176
|
return {
|
|
5742
6177
|
success: false,
|
|
5743
|
-
message:
|
|
6178
|
+
message: chalk13.yellow("\u26A0\uFE0F Invalid project configuration found.\n") + chalk13.dim("Use --force to reinitialize")
|
|
5744
6179
|
};
|
|
5745
6180
|
}
|
|
5746
6181
|
}
|
|
@@ -5753,7 +6188,7 @@ async function handleInit(opts) {
|
|
|
5753
6188
|
if (!shouldLogin) {
|
|
5754
6189
|
return {
|
|
5755
6190
|
success: false,
|
|
5756
|
-
message:
|
|
6191
|
+
message: chalk13.yellow("\u26A0\uFE0F Authentication required.\n") + chalk13.dim("Run ") + chalk13.cyan("braingrid login") + chalk13.dim(" when you're ready to authenticate.")
|
|
5757
6192
|
};
|
|
5758
6193
|
}
|
|
5759
6194
|
console.log();
|
|
@@ -5761,13 +6196,13 @@ async function handleInit(opts) {
|
|
|
5761
6196
|
if (!loginResult.success) {
|
|
5762
6197
|
return {
|
|
5763
6198
|
success: false,
|
|
5764
|
-
message:
|
|
6199
|
+
message: chalk13.red("\u274C Login failed.\n") + chalk13.dim("Please try running ") + chalk13.cyan("braingrid login") + chalk13.dim(" again.")
|
|
5765
6200
|
};
|
|
5766
6201
|
}
|
|
5767
6202
|
if (!await auth.isAuthenticated()) {
|
|
5768
6203
|
return {
|
|
5769
6204
|
success: false,
|
|
5770
|
-
message:
|
|
6205
|
+
message: chalk13.red("\u274C Login was not completed.\n") + chalk13.dim("Please try running ") + chalk13.cyan("braingrid login") + chalk13.dim(" again.")
|
|
5771
6206
|
};
|
|
5772
6207
|
}
|
|
5773
6208
|
console.log();
|
|
@@ -5776,7 +6211,7 @@ async function handleInit(opts) {
|
|
|
5776
6211
|
if (!session) {
|
|
5777
6212
|
return {
|
|
5778
6213
|
success: false,
|
|
5779
|
-
message:
|
|
6214
|
+
message: chalk13.red("\u274C No session found. Please run `braingrid login` first.")
|
|
5780
6215
|
};
|
|
5781
6216
|
}
|
|
5782
6217
|
let gitInfo = await getGitRepositoryInfo();
|
|
@@ -5787,9 +6222,9 @@ async function handleInit(opts) {
|
|
|
5787
6222
|
} catch {
|
|
5788
6223
|
return {
|
|
5789
6224
|
success: false,
|
|
5790
|
-
message:
|
|
6225
|
+
message: chalk13.red(`\u274C Project not found: ${opts.project}
|
|
5791
6226
|
|
|
5792
|
-
`) +
|
|
6227
|
+
`) + chalk13.dim("Make sure the project ID is correct and you have access to it.")
|
|
5793
6228
|
};
|
|
5794
6229
|
}
|
|
5795
6230
|
} else {
|
|
@@ -5800,7 +6235,7 @@ async function handleInit(opts) {
|
|
|
5800
6235
|
if (!gitInfo || !gitInfo.owner || !gitInfo.name) {
|
|
5801
6236
|
return {
|
|
5802
6237
|
success: false,
|
|
5803
|
-
message:
|
|
6238
|
+
message: chalk13.red("\u274C Failed to get repository information after setup")
|
|
5804
6239
|
};
|
|
5805
6240
|
}
|
|
5806
6241
|
} else {
|
|
@@ -5814,7 +6249,7 @@ async function handleInit(opts) {
|
|
|
5814
6249
|
if (!gitInfo || !gitInfo.owner || !gitInfo.name) {
|
|
5815
6250
|
return {
|
|
5816
6251
|
success: false,
|
|
5817
|
-
message:
|
|
6252
|
+
message: chalk13.red("\u274C Failed to get repository information after setup")
|
|
5818
6253
|
};
|
|
5819
6254
|
}
|
|
5820
6255
|
} else {
|
|
@@ -5824,7 +6259,7 @@ async function handleInit(opts) {
|
|
|
5824
6259
|
if (!gitInfo) {
|
|
5825
6260
|
return {
|
|
5826
6261
|
success: false,
|
|
5827
|
-
message:
|
|
6262
|
+
message: chalk13.red("\u274C Repository information is missing")
|
|
5828
6263
|
};
|
|
5829
6264
|
}
|
|
5830
6265
|
const owner = gitInfo.owner;
|
|
@@ -5832,7 +6267,7 @@ async function handleInit(opts) {
|
|
|
5832
6267
|
if (!owner || !name) {
|
|
5833
6268
|
return {
|
|
5834
6269
|
success: false,
|
|
5835
|
-
message:
|
|
6270
|
+
message: chalk13.red("\u274C Repository information is incomplete")
|
|
5836
6271
|
};
|
|
5837
6272
|
}
|
|
5838
6273
|
let response;
|
|
@@ -5871,7 +6306,7 @@ async function handleInit(opts) {
|
|
|
5871
6306
|
}
|
|
5872
6307
|
project2 = response.projects[0];
|
|
5873
6308
|
}
|
|
5874
|
-
const projectInfo =
|
|
6309
|
+
const projectInfo = chalk13.bold("\n\u{1F4E6} BrainGrid Project Found\n\n") + chalk13.dim("Project: ") + chalk13.cyan(project2.name) + "\n" + chalk13.dim("ID: ") + chalk13.gray(project2.short_id) + "\n" + (project2.description ? chalk13.dim("Description: ") + chalk13.gray(project2.description) + "\n" : "") + chalk13.dim("Repository: ") + chalk13.gray(project2.repository?.full_name || "N/A") + "\n\n";
|
|
5875
6310
|
console.log(projectInfo);
|
|
5876
6311
|
if (!opts.force) {
|
|
5877
6312
|
const shouldInit = await confirm({
|
|
@@ -5881,7 +6316,7 @@ async function handleInit(opts) {
|
|
|
5881
6316
|
if (!shouldInit) {
|
|
5882
6317
|
return {
|
|
5883
6318
|
success: false,
|
|
5884
|
-
message:
|
|
6319
|
+
message: chalk13.yellow("Initialization cancelled.")
|
|
5885
6320
|
};
|
|
5886
6321
|
}
|
|
5887
6322
|
}
|
|
@@ -5892,651 +6327,292 @@ async function handleInit(opts) {
|
|
|
5892
6327
|
project_name: project2.name,
|
|
5893
6328
|
project_description: project2.description || null,
|
|
5894
6329
|
repository: project2.repository ? {
|
|
5895
|
-
id: project2.repository.id,
|
|
5896
|
-
owner: project2.repository.full_name.split("/")[0],
|
|
5897
|
-
// Parse owner from API full_name
|
|
5898
|
-
name: project2.repository.name,
|
|
5899
|
-
// Use API name, not git name
|
|
5900
|
-
full_name: project2.repository.full_name,
|
|
5901
|
-
url: gitInfo?.remoteUrl || void 0
|
|
5902
|
-
} : null,
|
|
5903
|
-
created_at: project2.created_at
|
|
5904
|
-
};
|
|
5905
|
-
await saveProjectConfig(localConfig);
|
|
5906
|
-
|
|
5907
|
-
|
|
5908
|
-
|
|
5909
|
-
|
|
5910
|
-
|
|
5911
|
-
|
|
5912
|
-
|
|
5913
|
-
|
|
5914
|
-
|
|
5915
|
-
|
|
5916
|
-
|
|
5917
|
-
}
|
|
5918
|
-
|
|
5919
|
-
|
|
5920
|
-
|
|
5921
|
-
|
|
5922
|
-
|
|
5923
|
-
|
|
5924
|
-
|
|
5925
|
-
|
|
5926
|
-
|
|
5927
|
-
|
|
5928
|
-
|
|
5929
|
-
|
|
5930
|
-
|
|
5931
|
-
|
|
5932
|
-
|
|
5933
|
-
|
|
5934
|
-
|
|
5935
|
-
|
|
5936
|
-
|
|
5937
|
-
|
|
5938
|
-
|
|
5939
|
-
command = `npm list -g ${packageName} 2>&1`;
|
|
5940
|
-
break;
|
|
5941
|
-
}
|
|
5942
|
-
case "pnpm": {
|
|
5943
|
-
command = `pnpm list -g ${packageName} 2>&1`;
|
|
5944
|
-
break;
|
|
5945
|
-
}
|
|
5946
|
-
case "yarn": {
|
|
5947
|
-
command = `yarn global list 2>&1`;
|
|
5948
|
-
break;
|
|
5949
|
-
}
|
|
5950
|
-
}
|
|
5951
|
-
const output = execSync(command, {
|
|
5952
|
-
encoding: "utf-8",
|
|
5953
|
-
timeout: 5e3
|
|
5954
|
-
});
|
|
5955
|
-
if (pm === "npm" || pm === "pnpm") {
|
|
5956
|
-
return output.includes(packageName) && !output.includes("(empty)");
|
|
5957
|
-
}
|
|
5958
|
-
if (pm === "yarn") {
|
|
5959
|
-
return output.includes(packageName);
|
|
5960
|
-
}
|
|
5961
|
-
return false;
|
|
5962
|
-
} catch {
|
|
5963
|
-
return false;
|
|
5964
|
-
}
|
|
5965
|
-
}
|
|
5966
|
-
async function getPackageManagerInfo(packageName) {
|
|
5967
|
-
const packageManagers = ["npm", "pnpm", "yarn"];
|
|
5968
|
-
return packageManagers.map((pm) => {
|
|
5969
|
-
const installed = isPackageManagerInstalled(pm);
|
|
5970
|
-
return {
|
|
5971
|
-
name: pm,
|
|
5972
|
-
installed,
|
|
5973
|
-
hasPackage: installed ? checkGlobalInstallation(pm, packageName) : false
|
|
5974
|
-
};
|
|
5975
|
-
});
|
|
5976
|
-
}
|
|
5977
|
-
async function detectPackageManager(packageName) {
|
|
5978
|
-
const pmInfo = await getPackageManagerInfo(packageName);
|
|
5979
|
-
const installedWith = pmInfo.find((pm) => pm.hasPackage);
|
|
5980
|
-
if (installedWith) {
|
|
5981
|
-
return installedWith.name;
|
|
5982
|
-
}
|
|
5983
|
-
const availablePMs = pmInfo.filter((pm) => pm.installed);
|
|
5984
|
-
if (availablePMs.length === 0) {
|
|
5985
|
-
throw new Error("No package manager found. Please install npm, pnpm, or yarn.");
|
|
5986
|
-
}
|
|
5987
|
-
if (availablePMs.length === 1) {
|
|
5988
|
-
return availablePMs[0].name;
|
|
5989
|
-
}
|
|
5990
|
-
const selected = await select2({
|
|
5991
|
-
message: "Unable to detect which package manager was used. Please select one:",
|
|
5992
|
-
choices: availablePMs.map((pm) => ({
|
|
5993
|
-
name: pm.name,
|
|
5994
|
-
value: pm.name
|
|
5995
|
-
}))
|
|
5996
|
-
});
|
|
5997
|
-
return selected;
|
|
5998
|
-
}
|
|
5999
|
-
function getUpdateCommand(pm, packageName) {
|
|
6000
|
-
switch (pm) {
|
|
6001
|
-
case "npm": {
|
|
6002
|
-
return `npm install -g ${packageName}@latest`;
|
|
6003
|
-
}
|
|
6004
|
-
case "pnpm": {
|
|
6005
|
-
return `pnpm add -g ${packageName}@latest`;
|
|
6006
|
-
}
|
|
6007
|
-
case "yarn": {
|
|
6008
|
-
return `yarn global add ${packageName}@latest`;
|
|
6009
|
-
}
|
|
6010
|
-
}
|
|
6011
|
-
}
|
|
6012
|
-
function executeUpdate(pm, packageName) {
|
|
6013
|
-
const command = getUpdateCommand(pm, packageName);
|
|
6014
|
-
try {
|
|
6015
|
-
execSync(command, {
|
|
6016
|
-
stdio: "inherit",
|
|
6017
|
-
timeout: 12e4
|
|
6018
|
-
// 2 minutes timeout
|
|
6019
|
-
});
|
|
6020
|
-
} catch (error) {
|
|
6021
|
-
throw new Error(
|
|
6022
|
-
`Failed to update package: ${error instanceof Error ? error.message : String(error)}`
|
|
6023
|
-
);
|
|
6024
|
-
}
|
|
6025
|
-
}
|
|
6026
|
-
|
|
6027
|
-
// src/handlers/update.handlers.ts
|
|
6028
|
-
var PACKAGE_NAME = "@braingrid/cli";
|
|
6029
|
-
var NPM_REGISTRY_URL = `https://registry.npmjs.org/${PACKAGE_NAME}`;
|
|
6030
|
-
function getCurrentVersion() {
|
|
6031
|
-
return CLI_VERSION;
|
|
6032
|
-
}
|
|
6033
|
-
async function getLatestVersion() {
|
|
6034
|
-
try {
|
|
6035
|
-
const response = await axios5.get(NPM_REGISTRY_URL, {
|
|
6036
|
-
timeout: 1e4
|
|
6037
|
-
});
|
|
6038
|
-
return response.data["dist-tags"].latest;
|
|
6039
|
-
} catch (error) {
|
|
6040
|
-
throw new Error(`Failed to fetch latest version: ${formatError(error)}`);
|
|
6041
|
-
}
|
|
6042
|
-
}
|
|
6043
|
-
function compareVersions(v1, v2) {
|
|
6044
|
-
const v1Parts = v1.split(".").map(Number);
|
|
6045
|
-
const v2Parts = v2.split(".").map(Number);
|
|
6046
|
-
for (let i = 0; i < Math.max(v1Parts.length, v2Parts.length); i++) {
|
|
6047
|
-
const v1Part = v1Parts[i] || 0;
|
|
6048
|
-
const v2Part = v2Parts[i] || 0;
|
|
6049
|
-
if (v1Part < v2Part) return -1;
|
|
6050
|
-
if (v1Part > v2Part) return 1;
|
|
6051
|
-
}
|
|
6052
|
-
return 0;
|
|
6053
|
-
}
|
|
6054
|
-
async function handleUpdate(opts) {
|
|
6055
|
-
try {
|
|
6056
|
-
const currentVersion = getCurrentVersion();
|
|
6057
|
-
let output = chalk13.bold.cyan("\n\u{1F504} BrainGrid CLI Update\n\n");
|
|
6058
|
-
output += `${chalk13.bold("Current version:")} ${currentVersion}
|
|
6059
|
-
`;
|
|
6060
|
-
output += chalk13.dim("Checking for updates...\n");
|
|
6061
|
-
const latestVersion = await getLatestVersion();
|
|
6062
|
-
output += `${chalk13.bold("Latest version:")} ${latestVersion}
|
|
6063
|
-
|
|
6064
|
-
`;
|
|
6065
|
-
const comparison = compareVersions(currentVersion, latestVersion);
|
|
6066
|
-
if (comparison === 0) {
|
|
6067
|
-
output += chalk13.green("\u2705 You are already on the latest version!\n");
|
|
6068
|
-
return {
|
|
6069
|
-
success: true,
|
|
6070
|
-
message: output,
|
|
6071
|
-
data: { currentVersion, latestVersion, upToDate: true }
|
|
6072
|
-
};
|
|
6073
|
-
}
|
|
6074
|
-
if (comparison > 0) {
|
|
6075
|
-
output += chalk13.yellow("\u26A0\uFE0F You are on a newer version than what is published.\n");
|
|
6076
|
-
output += chalk13.dim(" This is expected if you are developing locally.\n");
|
|
6077
|
-
return {
|
|
6078
|
-
success: true,
|
|
6079
|
-
message: output,
|
|
6080
|
-
data: { currentVersion, latestVersion, upToDate: false }
|
|
6081
|
-
};
|
|
6330
|
+
id: project2.repository.id,
|
|
6331
|
+
owner: project2.repository.full_name.split("/")[0],
|
|
6332
|
+
// Parse owner from API full_name
|
|
6333
|
+
name: project2.repository.name,
|
|
6334
|
+
// Use API name, not git name
|
|
6335
|
+
full_name: project2.repository.full_name,
|
|
6336
|
+
url: gitInfo?.remoteUrl || void 0
|
|
6337
|
+
} : null,
|
|
6338
|
+
created_at: project2.created_at
|
|
6339
|
+
};
|
|
6340
|
+
await saveProjectConfig(localConfig);
|
|
6341
|
+
console.log(
|
|
6342
|
+
chalk13.green("\u2705 Repository initialized successfully!\n\n") + chalk13.dim("Project: ") + chalk13.cyan(project2.name) + chalk13.dim(` (${project2.short_id})`) + "\n" + chalk13.dim("Config: ") + chalk13.gray(".braingrid/project.json") + "\n"
|
|
6343
|
+
);
|
|
6344
|
+
const installedIDEs = await detectInstalledIDEs();
|
|
6345
|
+
if (installedIDEs.claudeCode) {
|
|
6346
|
+
const claudeSetupExists = await fileExists2(".claude/commands/specify.md");
|
|
6347
|
+
if (!claudeSetupExists) {
|
|
6348
|
+
console.log("");
|
|
6349
|
+
const setupClaude = await confirm({
|
|
6350
|
+
message: "Claude Code detected. Install BrainGrid integration? (slash commands, skills, status line)",
|
|
6351
|
+
default: true
|
|
6352
|
+
});
|
|
6353
|
+
if (setupClaude) {
|
|
6354
|
+
console.log("");
|
|
6355
|
+
try {
|
|
6356
|
+
const result = await handleSetupClaudeCode({ force: false });
|
|
6357
|
+
if (result.success) {
|
|
6358
|
+
console.log(result.message);
|
|
6359
|
+
} else {
|
|
6360
|
+
console.log(chalk13.yellow("\u26A0\uFE0F Claude Code setup was not completed."));
|
|
6361
|
+
console.log(
|
|
6362
|
+
chalk13.dim("You can run ") + chalk13.cyan("braingrid setup claude-code") + chalk13.dim(" later.")
|
|
6363
|
+
);
|
|
6364
|
+
}
|
|
6365
|
+
} catch {
|
|
6366
|
+
console.log(chalk13.yellow("\u26A0\uFE0F Claude Code setup encountered an error."));
|
|
6367
|
+
console.log(
|
|
6368
|
+
chalk13.dim("You can run ") + chalk13.cyan("braingrid setup claude-code") + chalk13.dim(" later.")
|
|
6369
|
+
);
|
|
6370
|
+
}
|
|
6371
|
+
console.log("");
|
|
6372
|
+
}
|
|
6373
|
+
}
|
|
6082
6374
|
}
|
|
6083
|
-
|
|
6084
|
-
|
|
6085
|
-
|
|
6086
|
-
|
|
6087
|
-
|
|
6088
|
-
|
|
6089
|
-
|
|
6090
|
-
|
|
6091
|
-
|
|
6092
|
-
|
|
6375
|
+
if (installedIDEs.cursor) {
|
|
6376
|
+
const cursorSetupExists = await fileExists2(".cursor/commands/specify.md");
|
|
6377
|
+
if (!cursorSetupExists) {
|
|
6378
|
+
console.log("");
|
|
6379
|
+
const setupCursor = await confirm({
|
|
6380
|
+
message: "Cursor detected. Install BrainGrid integration? (slash commands, rules, context)",
|
|
6381
|
+
default: true
|
|
6382
|
+
});
|
|
6383
|
+
if (setupCursor) {
|
|
6384
|
+
console.log("");
|
|
6385
|
+
try {
|
|
6386
|
+
const result = await handleSetupCursor({ force: false });
|
|
6387
|
+
if (result.success) {
|
|
6388
|
+
console.log(result.message);
|
|
6389
|
+
} else {
|
|
6390
|
+
console.log(chalk13.yellow("\u26A0\uFE0F Cursor setup was not completed."));
|
|
6391
|
+
console.log(
|
|
6392
|
+
chalk13.dim("You can run ") + chalk13.cyan("braingrid setup cursor") + chalk13.dim(" later.")
|
|
6393
|
+
);
|
|
6394
|
+
}
|
|
6395
|
+
} catch {
|
|
6396
|
+
console.log(chalk13.yellow("\u26A0\uFE0F Cursor setup encountered an error."));
|
|
6397
|
+
console.log(
|
|
6398
|
+
chalk13.dim("You can run ") + chalk13.cyan("braingrid setup cursor") + chalk13.dim(" later.")
|
|
6399
|
+
);
|
|
6400
|
+
}
|
|
6401
|
+
console.log("");
|
|
6402
|
+
}
|
|
6403
|
+
}
|
|
6093
6404
|
}
|
|
6094
|
-
output += chalk13.dim("Detecting package manager...\n");
|
|
6095
|
-
const packageManager = await detectPackageManager(PACKAGE_NAME);
|
|
6096
|
-
output += `${chalk13.bold("Package manager:")} ${packageManager}
|
|
6097
|
-
|
|
6098
|
-
`;
|
|
6099
|
-
const updateCommand = getUpdateCommand(packageManager, PACKAGE_NAME);
|
|
6100
|
-
output += chalk13.dim("Running: ") + chalk13.cyan(updateCommand) + "\n\n";
|
|
6101
|
-
console.log(output);
|
|
6102
|
-
executeUpdate(packageManager, PACKAGE_NAME);
|
|
6103
6405
|
return {
|
|
6104
6406
|
success: true,
|
|
6105
|
-
message: chalk13.
|
|
6106
|
-
|
|
6407
|
+
message: chalk13.dim(
|
|
6408
|
+
"You can now use project-scoped commands without specifying a project ID."
|
|
6409
|
+
),
|
|
6410
|
+
data: localConfig
|
|
6107
6411
|
};
|
|
6108
6412
|
} catch (error) {
|
|
6109
6413
|
return {
|
|
6110
6414
|
success: false,
|
|
6111
|
-
message: formatError(error)
|
|
6415
|
+
message: formatError(error, "initializing repository")
|
|
6112
6416
|
};
|
|
6113
6417
|
}
|
|
6114
6418
|
}
|
|
6115
6419
|
|
|
6116
|
-
// src/handlers/
|
|
6420
|
+
// src/handlers/update.handlers.ts
|
|
6117
6421
|
import chalk14 from "chalk";
|
|
6118
|
-
import
|
|
6119
|
-
import * as path5 from "path";
|
|
6120
|
-
import * as fs4 from "fs/promises";
|
|
6121
|
-
|
|
6122
|
-
// src/utils/command-execution.ts
|
|
6123
|
-
import { exec as exec4, spawn } from "child_process";
|
|
6124
|
-
import { promisify as promisify4 } from "util";
|
|
6125
|
-
var execAsyncReal = promisify4(exec4);
|
|
6126
|
-
async function execAsync4(command, options, isTestMode = false, mockExecHandler) {
|
|
6127
|
-
if (isTestMode && mockExecHandler) {
|
|
6128
|
-
return mockExecHandler(command);
|
|
6129
|
-
}
|
|
6130
|
-
const defaultOptions = {
|
|
6131
|
-
maxBuffer: 1024 * 1024 * 10,
|
|
6132
|
-
// 10MB default
|
|
6133
|
-
timeout: 3e5,
|
|
6134
|
-
// 5 minutes
|
|
6135
|
-
...options
|
|
6136
|
-
};
|
|
6137
|
-
if (command.includes("claude")) {
|
|
6138
|
-
defaultOptions.maxBuffer = 1024 * 1024 * 50;
|
|
6139
|
-
defaultOptions.timeout = 6e5;
|
|
6140
|
-
}
|
|
6141
|
-
return execAsyncReal(command, defaultOptions);
|
|
6142
|
-
}
|
|
6422
|
+
import axios5 from "axios";
|
|
6143
6423
|
|
|
6144
|
-
// src/
|
|
6145
|
-
import
|
|
6146
|
-
import
|
|
6147
|
-
|
|
6148
|
-
var GITHUB_REPO = "braingrid";
|
|
6149
|
-
var MAX_RETRIES = 3;
|
|
6150
|
-
var INITIAL_RETRY_DELAY = 100;
|
|
6151
|
-
var BEGIN_MARKER = "<!-- BEGIN BRAINGRID INTEGRATION -->";
|
|
6152
|
-
var END_MARKER = "<!-- END BRAINGRID INTEGRATION -->";
|
|
6153
|
-
async function withRetry(fn, retries = MAX_RETRIES, delay = INITIAL_RETRY_DELAY) {
|
|
6154
|
-
try {
|
|
6155
|
-
return await fn();
|
|
6156
|
-
} catch (error) {
|
|
6157
|
-
if (retries === 0) {
|
|
6158
|
-
throw error;
|
|
6159
|
-
}
|
|
6160
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
6161
|
-
const isNetworkError = errorMessage.includes("ECONNRESET") || errorMessage.includes("ETIMEDOUT") || errorMessage.includes("ENOTFOUND") || errorMessage.includes("network");
|
|
6162
|
-
if (!isNetworkError) {
|
|
6163
|
-
throw error;
|
|
6164
|
-
}
|
|
6165
|
-
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
6166
|
-
return withRetry(fn, retries - 1, delay * 2);
|
|
6167
|
-
}
|
|
6168
|
-
}
|
|
6169
|
-
function parseGitHubError(error) {
|
|
6170
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
6171
|
-
if (errorMessage.includes("404") || errorMessage.includes("Not Found")) {
|
|
6172
|
-
return "File or directory not found in BrainGrid repository";
|
|
6173
|
-
}
|
|
6174
|
-
if (errorMessage.includes("403") || errorMessage.includes("rate limit")) {
|
|
6175
|
-
return "GitHub API rate limit exceeded. Please wait a few minutes and try again.\nCheck rate limit status: gh api rate_limit";
|
|
6176
|
-
}
|
|
6177
|
-
if (errorMessage.includes("401") || errorMessage.includes("Unauthorized")) {
|
|
6178
|
-
return "GitHub CLI is not authenticated. Run: gh auth login";
|
|
6179
|
-
}
|
|
6424
|
+
// src/utils/package-manager.ts
|
|
6425
|
+
import { execSync } from "child_process";
|
|
6426
|
+
import { select as select3 } from "@inquirer/prompts";
|
|
6427
|
+
function isPackageManagerInstalled(pm) {
|
|
6180
6428
|
try {
|
|
6181
|
-
|
|
6182
|
-
|
|
6183
|
-
const errorData = JSON.parse(match[0]);
|
|
6184
|
-
return errorData.message || errorMessage;
|
|
6185
|
-
}
|
|
6429
|
+
execSync(`which ${pm}`, { stdio: "ignore", timeout: 2e3 });
|
|
6430
|
+
return true;
|
|
6186
6431
|
} catch {
|
|
6432
|
+
return false;
|
|
6187
6433
|
}
|
|
6188
|
-
return errorMessage;
|
|
6189
6434
|
}
|
|
6190
|
-
|
|
6191
|
-
|
|
6192
|
-
|
|
6193
|
-
|
|
6194
|
-
|
|
6195
|
-
|
|
6196
|
-
|
|
6197
|
-
throw new Error(`Path ${path6} is not a file`);
|
|
6198
|
-
}
|
|
6199
|
-
if (!response.content || !response.encoding) {
|
|
6200
|
-
throw new Error(`No content found for file ${path6}`);
|
|
6435
|
+
function checkGlobalInstallation(pm, packageName) {
|
|
6436
|
+
try {
|
|
6437
|
+
let command;
|
|
6438
|
+
switch (pm) {
|
|
6439
|
+
case "npm": {
|
|
6440
|
+
command = `npm list -g ${packageName} 2>&1`;
|
|
6441
|
+
break;
|
|
6201
6442
|
}
|
|
6202
|
-
|
|
6203
|
-
|
|
6443
|
+
case "pnpm": {
|
|
6444
|
+
command = `pnpm list -g ${packageName} 2>&1`;
|
|
6445
|
+
break;
|
|
6204
6446
|
}
|
|
6205
|
-
|
|
6206
|
-
|
|
6207
|
-
|
|
6208
|
-
const parsedError = parseGitHubError(error);
|
|
6209
|
-
throw new Error(`Failed to fetch file ${path6}: ${parsedError}`);
|
|
6210
|
-
}
|
|
6211
|
-
});
|
|
6212
|
-
}
|
|
6213
|
-
async function listGitHubDirectory(path6) {
|
|
6214
|
-
return withRetry(async () => {
|
|
6215
|
-
try {
|
|
6216
|
-
const command = `gh api repos/${GITHUB_OWNER}/${GITHUB_REPO}/contents/${path6}`;
|
|
6217
|
-
const { stdout } = await execAsync4(command);
|
|
6218
|
-
const response = JSON.parse(stdout);
|
|
6219
|
-
if (!Array.isArray(response)) {
|
|
6220
|
-
throw new Error(`Path ${path6} is not a directory`);
|
|
6447
|
+
case "yarn": {
|
|
6448
|
+
command = `yarn global list 2>&1`;
|
|
6449
|
+
break;
|
|
6221
6450
|
}
|
|
6222
|
-
return response.map((item) => ({
|
|
6223
|
-
name: item.name,
|
|
6224
|
-
type: item.type,
|
|
6225
|
-
path: item.path
|
|
6226
|
-
}));
|
|
6227
|
-
} catch (error) {
|
|
6228
|
-
const parsedError = parseGitHubError(error);
|
|
6229
|
-
throw new Error(`Failed to list directory ${path6}: ${parsedError}`);
|
|
6230
|
-
}
|
|
6231
|
-
});
|
|
6232
|
-
}
|
|
6233
|
-
async function copyFileFromGitHub(sourcePath, targetPath) {
|
|
6234
|
-
try {
|
|
6235
|
-
const content = await fetchFileFromGitHub(sourcePath);
|
|
6236
|
-
const parentDir = path4.dirname(targetPath);
|
|
6237
|
-
await fs3.mkdir(parentDir, { recursive: true });
|
|
6238
|
-
await fs3.writeFile(targetPath, content, { encoding: "utf8", mode: 420 });
|
|
6239
|
-
} catch (error) {
|
|
6240
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
6241
|
-
throw new Error(`Failed to copy file ${sourcePath} to ${targetPath}: ${errorMessage}`);
|
|
6242
|
-
}
|
|
6243
|
-
}
|
|
6244
|
-
async function injectContentIntoFile(targetPath, content) {
|
|
6245
|
-
try {
|
|
6246
|
-
let fileContent;
|
|
6247
|
-
let fileExists2 = false;
|
|
6248
|
-
try {
|
|
6249
|
-
fileContent = await fs3.readFile(targetPath, "utf8");
|
|
6250
|
-
fileExists2 = true;
|
|
6251
|
-
} catch {
|
|
6252
|
-
fileContent = "";
|
|
6253
6451
|
}
|
|
6254
|
-
|
|
6255
|
-
|
|
6256
|
-
|
|
6257
|
-
|
|
6258
|
-
|
|
6259
|
-
|
|
6260
|
-
const newContent = `${before}${BEGIN_MARKER}
|
|
6261
|
-
${content}
|
|
6262
|
-
${END_MARKER}${after}`;
|
|
6263
|
-
await fs3.writeFile(targetPath, newContent, { encoding: "utf8" });
|
|
6264
|
-
} else {
|
|
6265
|
-
const newContent = `${fileContent}
|
|
6266
|
-
|
|
6267
|
-
${BEGIN_MARKER}
|
|
6268
|
-
${content}
|
|
6269
|
-
${END_MARKER}
|
|
6270
|
-
`;
|
|
6271
|
-
await fs3.writeFile(targetPath, newContent, { encoding: "utf8" });
|
|
6272
|
-
}
|
|
6273
|
-
} else {
|
|
6274
|
-
const parentDir = path4.dirname(targetPath);
|
|
6275
|
-
await fs3.mkdir(parentDir, { recursive: true });
|
|
6276
|
-
const newContent = `${BEGIN_MARKER}
|
|
6277
|
-
${content}
|
|
6278
|
-
${END_MARKER}
|
|
6279
|
-
`;
|
|
6280
|
-
await fs3.writeFile(targetPath, newContent, { encoding: "utf8", mode: 420 });
|
|
6452
|
+
const output = execSync(command, {
|
|
6453
|
+
encoding: "utf-8",
|
|
6454
|
+
timeout: 5e3
|
|
6455
|
+
});
|
|
6456
|
+
if (pm === "npm" || pm === "pnpm") {
|
|
6457
|
+
return output.includes(packageName) && !output.includes("(empty)");
|
|
6281
6458
|
}
|
|
6282
|
-
|
|
6283
|
-
|
|
6284
|
-
throw new Error(`Failed to inject content into ${targetPath}: ${errorMessage}`);
|
|
6285
|
-
}
|
|
6286
|
-
}
|
|
6287
|
-
async function installStatusLineScript(scriptContent, targetPath = ".claude/statusline.sh") {
|
|
6288
|
-
try {
|
|
6289
|
-
const parentDir = path4.dirname(targetPath);
|
|
6290
|
-
await fs3.mkdir(parentDir, { recursive: true });
|
|
6291
|
-
await fs3.writeFile(targetPath, scriptContent, { encoding: "utf8", mode: 493 });
|
|
6292
|
-
} catch (error) {
|
|
6293
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
6294
|
-
throw new Error(`Failed to install status line script to ${targetPath}: ${errorMessage}`);
|
|
6295
|
-
}
|
|
6296
|
-
}
|
|
6297
|
-
async function updateClaudeSettings(settingsPath = ".claude/settings.json", scriptPath = ".claude/statusline.sh") {
|
|
6298
|
-
try {
|
|
6299
|
-
let settings = {};
|
|
6300
|
-
try {
|
|
6301
|
-
const content2 = await fs3.readFile(settingsPath, "utf8");
|
|
6302
|
-
settings = JSON.parse(content2);
|
|
6303
|
-
} catch {
|
|
6459
|
+
if (pm === "yarn") {
|
|
6460
|
+
return output.includes(packageName);
|
|
6304
6461
|
}
|
|
6305
|
-
|
|
6306
|
-
type: "command",
|
|
6307
|
-
command: scriptPath,
|
|
6308
|
-
padding: 0
|
|
6309
|
-
};
|
|
6310
|
-
const parentDir = path4.dirname(settingsPath);
|
|
6311
|
-
await fs3.mkdir(parentDir, { recursive: true });
|
|
6312
|
-
const content = JSON.stringify(settings, null, 2);
|
|
6313
|
-
await fs3.writeFile(settingsPath, content, { encoding: "utf8" });
|
|
6314
|
-
} catch (error) {
|
|
6315
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
6316
|
-
throw new Error(`Failed to update Claude settings at ${settingsPath}: ${errorMessage}`);
|
|
6317
|
-
}
|
|
6318
|
-
}
|
|
6319
|
-
|
|
6320
|
-
// src/handlers/setup.handlers.ts
|
|
6321
|
-
async function fileExists(filePath) {
|
|
6322
|
-
try {
|
|
6323
|
-
await fs4.access(filePath);
|
|
6324
|
-
return true;
|
|
6462
|
+
return false;
|
|
6325
6463
|
} catch {
|
|
6326
6464
|
return false;
|
|
6327
6465
|
}
|
|
6328
6466
|
}
|
|
6329
|
-
async function
|
|
6330
|
-
|
|
6331
|
-
|
|
6332
|
-
|
|
6333
|
-
return {
|
|
6334
|
-
success: false,
|
|
6335
|
-
message: chalk14.red("\u274C GitHub CLI is not installed.\n\n") + chalk14.dim("Install instructions:\n") + chalk14.dim(" macOS: ") + chalk14.cyan("brew install gh") + chalk14.dim("\n") + chalk14.dim(" Windows: ") + chalk14.cyan("winget install GitHub.CLI") + chalk14.dim("\n") + chalk14.dim(" Linux: See ") + chalk14.cyan("https://cli.github.com/manual/installation") + chalk14.dim("\n\n") + chalk14.dim("After installing, run: ") + chalk14.cyan("gh auth login")
|
|
6336
|
-
};
|
|
6337
|
-
}
|
|
6338
|
-
try {
|
|
6339
|
-
await execAsync4("gh auth status");
|
|
6340
|
-
} catch {
|
|
6467
|
+
async function getPackageManagerInfo(packageName) {
|
|
6468
|
+
const packageManagers = ["npm", "pnpm", "yarn"];
|
|
6469
|
+
return packageManagers.map((pm) => {
|
|
6470
|
+
const installed = isPackageManagerInstalled(pm);
|
|
6341
6471
|
return {
|
|
6342
|
-
|
|
6343
|
-
|
|
6472
|
+
name: pm,
|
|
6473
|
+
installed,
|
|
6474
|
+
hasPackage: installed ? checkGlobalInstallation(pm, packageName) : false
|
|
6344
6475
|
};
|
|
6345
|
-
}
|
|
6346
|
-
return null;
|
|
6476
|
+
});
|
|
6347
6477
|
}
|
|
6348
|
-
async function
|
|
6349
|
-
const
|
|
6350
|
-
|
|
6351
|
-
|
|
6352
|
-
|
|
6353
|
-
try {
|
|
6354
|
-
const items = await listGitHubDirectory(sourcePath);
|
|
6355
|
-
for (const item of items) {
|
|
6356
|
-
if (item.type === "file") {
|
|
6357
|
-
const itemTargetPath = path5.join(targetPath, item.name);
|
|
6358
|
-
const exists = await fileExists(itemTargetPath);
|
|
6359
|
-
operations.push({
|
|
6360
|
-
type: "copy",
|
|
6361
|
-
sourcePath: item.path,
|
|
6362
|
-
targetPath: itemTargetPath,
|
|
6363
|
-
exists
|
|
6364
|
-
});
|
|
6365
|
-
}
|
|
6366
|
-
}
|
|
6367
|
-
} catch (error) {
|
|
6368
|
-
console.warn(
|
|
6369
|
-
chalk14.yellow(`\u26A0\uFE0F Could not list directory: ${sourcePath}`),
|
|
6370
|
-
error instanceof Error ? error.message : String(error)
|
|
6371
|
-
);
|
|
6372
|
-
}
|
|
6478
|
+
async function detectPackageManager(packageName) {
|
|
6479
|
+
const pmInfo = await getPackageManagerInfo(packageName);
|
|
6480
|
+
const installedWith = pmInfo.find((pm) => pm.hasPackage);
|
|
6481
|
+
if (installedWith) {
|
|
6482
|
+
return installedWith.name;
|
|
6373
6483
|
}
|
|
6374
|
-
|
|
6375
|
-
|
|
6376
|
-
|
|
6377
|
-
console.log(chalk14.bold("\n\u{1F4CB} Installation Plan:\n"));
|
|
6378
|
-
console.log(chalk14.cyan(" Content Injection:"));
|
|
6379
|
-
console.log(chalk14.dim(` ${injectionFile}`));
|
|
6380
|
-
const newFiles = operations.filter((op) => !op.exists);
|
|
6381
|
-
const existingFiles = operations.filter((op) => op.exists);
|
|
6382
|
-
if (newFiles.length > 0) {
|
|
6383
|
-
console.log(chalk14.cyan("\n New Files:"));
|
|
6384
|
-
for (const op of newFiles) {
|
|
6385
|
-
console.log(chalk14.dim(` ${op.targetPath}`));
|
|
6386
|
-
}
|
|
6484
|
+
const availablePMs = pmInfo.filter((pm) => pm.installed);
|
|
6485
|
+
if (availablePMs.length === 0) {
|
|
6486
|
+
throw new Error("No package manager found. Please install npm, pnpm, or yarn.");
|
|
6387
6487
|
}
|
|
6388
|
-
if (
|
|
6389
|
-
|
|
6390
|
-
for (const op of existingFiles) {
|
|
6391
|
-
console.log(chalk14.dim(` ${op.targetPath}`));
|
|
6392
|
-
}
|
|
6488
|
+
if (availablePMs.length === 1) {
|
|
6489
|
+
return availablePMs[0].name;
|
|
6393
6490
|
}
|
|
6394
|
-
|
|
6395
|
-
|
|
6396
|
-
|
|
6397
|
-
|
|
6398
|
-
|
|
6399
|
-
|
|
6400
|
-
{ name: "[O]verwrite - Replace this file", value: "overwrite" },
|
|
6401
|
-
{ name: "[S]kip - Keep existing file", value: "skip" },
|
|
6402
|
-
{ name: "[A]ll - Overwrite all remaining", value: "all" },
|
|
6403
|
-
{ name: "[Q]uit - Cancel installation", value: "quit" }
|
|
6404
|
-
]
|
|
6491
|
+
const selected = await select3({
|
|
6492
|
+
message: "Unable to detect which package manager was used. Please select one:",
|
|
6493
|
+
choices: availablePMs.map((pm) => ({
|
|
6494
|
+
name: pm.name,
|
|
6495
|
+
value: pm.name
|
|
6496
|
+
}))
|
|
6405
6497
|
});
|
|
6406
|
-
return
|
|
6498
|
+
return selected;
|
|
6407
6499
|
}
|
|
6408
|
-
|
|
6409
|
-
|
|
6410
|
-
|
|
6411
|
-
|
|
6412
|
-
for (const operation of operations) {
|
|
6413
|
-
if (operation.exists && !overwriteAll) {
|
|
6414
|
-
const response = await promptForConflict(operation.targetPath);
|
|
6415
|
-
if (response === "quit") {
|
|
6416
|
-
return { installed, skipped, cancelled: true };
|
|
6417
|
-
} else if (response === "skip") {
|
|
6418
|
-
skipped++;
|
|
6419
|
-
continue;
|
|
6420
|
-
} else if (response === "all") {
|
|
6421
|
-
overwriteAll = true;
|
|
6422
|
-
}
|
|
6500
|
+
function getUpdateCommand(pm, packageName) {
|
|
6501
|
+
switch (pm) {
|
|
6502
|
+
case "npm": {
|
|
6503
|
+
return `npm install -g ${packageName}@latest`;
|
|
6423
6504
|
}
|
|
6424
|
-
|
|
6425
|
-
|
|
6426
|
-
|
|
6427
|
-
|
|
6428
|
-
|
|
6429
|
-
chalk14.red(`Failed to copy ${operation.targetPath}:`),
|
|
6430
|
-
error instanceof Error ? error.message : String(error)
|
|
6431
|
-
);
|
|
6432
|
-
skipped++;
|
|
6505
|
+
case "pnpm": {
|
|
6506
|
+
return `pnpm add -g ${packageName}@latest`;
|
|
6507
|
+
}
|
|
6508
|
+
case "yarn": {
|
|
6509
|
+
return `yarn global add ${packageName}@latest`;
|
|
6433
6510
|
}
|
|
6434
6511
|
}
|
|
6435
|
-
return { installed, skipped, cancelled: false };
|
|
6436
6512
|
}
|
|
6437
|
-
|
|
6513
|
+
function executeUpdate(pm, packageName) {
|
|
6514
|
+
const command = getUpdateCommand(pm, packageName);
|
|
6438
6515
|
try {
|
|
6439
|
-
|
|
6440
|
-
|
|
6441
|
-
|
|
6442
|
-
|
|
6443
|
-
console.log(chalk14.bold(`\u{1F680} Setting up ${config.name} integration...
|
|
6444
|
-
`));
|
|
6445
|
-
const operations = await getFileList(config.sourceDirs, config.targetDirs);
|
|
6446
|
-
const injectionFileExists = await fileExists(config.injection.targetFile);
|
|
6447
|
-
operations.push({
|
|
6448
|
-
type: "inject",
|
|
6449
|
-
sourcePath: config.injection.sourceFile,
|
|
6450
|
-
targetPath: config.injection.targetFile,
|
|
6451
|
-
exists: injectionFileExists
|
|
6516
|
+
execSync(command, {
|
|
6517
|
+
stdio: "inherit",
|
|
6518
|
+
timeout: 12e4
|
|
6519
|
+
// 2 minutes timeout
|
|
6452
6520
|
});
|
|
6453
|
-
|
|
6454
|
-
|
|
6455
|
-
|
|
6521
|
+
} catch (error) {
|
|
6522
|
+
throw new Error(
|
|
6523
|
+
`Failed to update package: ${error instanceof Error ? error.message : String(error)}`
|
|
6456
6524
|
);
|
|
6457
|
-
|
|
6525
|
+
}
|
|
6526
|
+
}
|
|
6527
|
+
|
|
6528
|
+
// src/handlers/update.handlers.ts
|
|
6529
|
+
var PACKAGE_NAME = "@braingrid/cli";
|
|
6530
|
+
var NPM_REGISTRY_URL = `https://registry.npmjs.org/${PACKAGE_NAME}`;
|
|
6531
|
+
function getCurrentVersion() {
|
|
6532
|
+
return CLI_VERSION;
|
|
6533
|
+
}
|
|
6534
|
+
async function getLatestVersion() {
|
|
6535
|
+
try {
|
|
6536
|
+
const response = await axios5.get(NPM_REGISTRY_URL, {
|
|
6537
|
+
timeout: 1e4
|
|
6538
|
+
});
|
|
6539
|
+
return response.data["dist-tags"].latest;
|
|
6540
|
+
} catch (error) {
|
|
6541
|
+
throw new Error(`Failed to fetch latest version: ${formatError(error)}`);
|
|
6542
|
+
}
|
|
6543
|
+
}
|
|
6544
|
+
function compareVersions(v1, v2) {
|
|
6545
|
+
const v1Parts = v1.split(".").map(Number);
|
|
6546
|
+
const v2Parts = v2.split(".").map(Number);
|
|
6547
|
+
for (let i = 0; i < Math.max(v1Parts.length, v2Parts.length); i++) {
|
|
6548
|
+
const v1Part = v1Parts[i] || 0;
|
|
6549
|
+
const v2Part = v2Parts[i] || 0;
|
|
6550
|
+
if (v1Part < v2Part) return -1;
|
|
6551
|
+
if (v1Part > v2Part) return 1;
|
|
6552
|
+
}
|
|
6553
|
+
return 0;
|
|
6554
|
+
}
|
|
6555
|
+
async function handleUpdate(opts) {
|
|
6556
|
+
try {
|
|
6557
|
+
const currentVersion = getCurrentVersion();
|
|
6558
|
+
let output = chalk14.bold.cyan("\n\u{1F504} BrainGrid CLI Update\n\n");
|
|
6559
|
+
output += `${chalk14.bold("Current version:")} ${currentVersion}
|
|
6560
|
+
`;
|
|
6561
|
+
output += chalk14.dim("Checking for updates...\n");
|
|
6562
|
+
const latestVersion = await getLatestVersion();
|
|
6563
|
+
output += `${chalk14.bold("Latest version:")} ${latestVersion}
|
|
6564
|
+
|
|
6565
|
+
`;
|
|
6566
|
+
const comparison = compareVersions(currentVersion, latestVersion);
|
|
6567
|
+
if (comparison === 0) {
|
|
6568
|
+
output += chalk14.green("\u2705 You are already on the latest version!\n");
|
|
6458
6569
|
return {
|
|
6459
6570
|
success: true,
|
|
6460
|
-
message:
|
|
6571
|
+
message: output,
|
|
6572
|
+
data: { currentVersion, latestVersion, upToDate: true }
|
|
6461
6573
|
};
|
|
6462
6574
|
}
|
|
6463
|
-
|
|
6464
|
-
|
|
6465
|
-
|
|
6575
|
+
if (comparison > 0) {
|
|
6576
|
+
output += chalk14.yellow("\u26A0\uFE0F You are on a newer version than what is published.\n");
|
|
6577
|
+
output += chalk14.dim(" This is expected if you are developing locally.\n");
|
|
6466
6578
|
return {
|
|
6467
|
-
success:
|
|
6468
|
-
message:
|
|
6469
|
-
|
|
6579
|
+
success: true,
|
|
6580
|
+
message: output,
|
|
6581
|
+
data: { currentVersion, latestVersion, upToDate: false }
|
|
6470
6582
|
};
|
|
6471
6583
|
}
|
|
6472
|
-
|
|
6473
|
-
|
|
6474
|
-
|
|
6475
|
-
|
|
6476
|
-
|
|
6477
|
-
|
|
6478
|
-
|
|
6479
|
-
|
|
6480
|
-
|
|
6481
|
-
|
|
6482
|
-
if (config.name === "Claude Code") {
|
|
6483
|
-
try {
|
|
6484
|
-
const scriptContent = await fetchFileFromGitHub("claude-code/statusline.sh");
|
|
6485
|
-
await installStatusLineScript(scriptContent);
|
|
6486
|
-
await updateClaudeSettings();
|
|
6487
|
-
statusLineInstalled = true;
|
|
6488
|
-
} catch (error) {
|
|
6489
|
-
console.error(
|
|
6490
|
-
chalk14.yellow("\u26A0\uFE0F Failed to install status line script:"),
|
|
6491
|
-
error instanceof Error ? error.message : String(error)
|
|
6492
|
-
);
|
|
6493
|
-
}
|
|
6584
|
+
output += chalk14.yellow(`\u2B06\uFE0F Update available: ${currentVersion} \u2192 ${latestVersion}
|
|
6585
|
+
|
|
6586
|
+
`);
|
|
6587
|
+
if (opts.check) {
|
|
6588
|
+
output += chalk14.dim("Run ") + chalk14.cyan("braingrid update") + chalk14.dim(" to update\n");
|
|
6589
|
+
return {
|
|
6590
|
+
success: true,
|
|
6591
|
+
message: output,
|
|
6592
|
+
data: { currentVersion, latestVersion, upToDate: false }
|
|
6593
|
+
};
|
|
6494
6594
|
}
|
|
6495
|
-
|
|
6595
|
+
output += chalk14.dim("Detecting package manager...\n");
|
|
6596
|
+
const packageManager = await detectPackageManager(PACKAGE_NAME);
|
|
6597
|
+
output += `${chalk14.bold("Package manager:")} ${packageManager}
|
|
6598
|
+
|
|
6599
|
+
`;
|
|
6600
|
+
const updateCommand = getUpdateCommand(packageManager, PACKAGE_NAME);
|
|
6601
|
+
output += chalk14.dim("Running: ") + chalk14.cyan(updateCommand) + "\n\n";
|
|
6602
|
+
console.log(output);
|
|
6603
|
+
executeUpdate(packageManager, PACKAGE_NAME);
|
|
6496
6604
|
return {
|
|
6497
6605
|
success: true,
|
|
6498
|
-
message: chalk14.green(
|
|
6499
|
-
|
|
6500
|
-
`) + chalk14.dim("Files installed:\n") + chalk14.dim(` Commands: ${result.installed} files
|
|
6501
|
-
`) + statusLineMessage + chalk14.dim(` Content injected into: ${config.injection.targetFile}
|
|
6502
|
-
|
|
6503
|
-
`) + chalk14.dim("Next steps:\n") + chalk14.dim(" 1. Review the integration files\n") + chalk14.dim(` 2. Open ${config.name}
|
|
6504
|
-
`) + chalk14.dim(" 3. Try the /specify or /breakdown commands\n") + chalk14.dim(" 4. Learn more: ") + chalk14.cyan(config.docsUrl)
|
|
6606
|
+
message: chalk14.green("\n\u2705 Successfully updated BrainGrid CLI!\n"),
|
|
6607
|
+
data: { currentVersion, latestVersion, packageManager }
|
|
6505
6608
|
};
|
|
6506
6609
|
} catch (error) {
|
|
6507
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
6508
6610
|
return {
|
|
6509
6611
|
success: false,
|
|
6510
|
-
message:
|
|
6612
|
+
message: formatError(error)
|
|
6511
6613
|
};
|
|
6512
6614
|
}
|
|
6513
6615
|
}
|
|
6514
|
-
async function handleSetupClaudeCode(opts) {
|
|
6515
|
-
const config = {
|
|
6516
|
-
name: "Claude Code",
|
|
6517
|
-
sourceDirs: ["claude-code/commands", "claude-code/skills"],
|
|
6518
|
-
targetDirs: [".claude/commands", ".claude/skills/braingrid-cli"],
|
|
6519
|
-
injection: {
|
|
6520
|
-
sourceFile: "claude-code/CLAUDE.md",
|
|
6521
|
-
targetFile: "CLAUDE.md"
|
|
6522
|
-
},
|
|
6523
|
-
docsUrl: "https://braingrid.ai/docs/claude-code"
|
|
6524
|
-
};
|
|
6525
|
-
return _handleSetup(config, opts);
|
|
6526
|
-
}
|
|
6527
|
-
async function handleSetupCursor(opts) {
|
|
6528
|
-
const config = {
|
|
6529
|
-
name: "Cursor",
|
|
6530
|
-
sourceDirs: ["cursor/commands", "cursor/rules"],
|
|
6531
|
-
targetDirs: [".cursor/commands", ".cursor/rules"],
|
|
6532
|
-
injection: {
|
|
6533
|
-
sourceFile: "cursor/AGENTS.md",
|
|
6534
|
-
targetFile: "AGENTS.md"
|
|
6535
|
-
},
|
|
6536
|
-
docsUrl: "https://braingrid.ai/docs/cursor"
|
|
6537
|
-
};
|
|
6538
|
-
return _handleSetup(config, opts);
|
|
6539
|
-
}
|
|
6540
6616
|
|
|
6541
6617
|
// src/cli.ts
|
|
6542
6618
|
var require2 = createRequire(import.meta.url);
|