@braingrid/cli 0.2.35 → 0.2.37
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 +35 -0
- package/dist/cli.js +239 -156
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,41 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.2.37] - 2026-02-14
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **Add `.braingrid/temp/` to `.gitignore` during update**
|
|
15
|
+
- `braingrid update` now ensures `.braingrid/temp/` is in `.gitignore` (previously only `init` did this)
|
|
16
|
+
- Runs in both the "already up-to-date" and "after successful update" code paths
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- **Per-directory install counts in setup success message**
|
|
21
|
+
- Setup success message now shows "Commands: 4" and "Skills: 1" instead of "Commands: 6 files"
|
|
22
|
+
- Each skill directory counts as 1 skill regardless of supporting files inside it
|
|
23
|
+
- Cursor setup shows separate "Commands" and "Rules" counts
|
|
24
|
+
|
|
25
|
+
### Refactored
|
|
26
|
+
|
|
27
|
+
- **Extract `addBraingridTempToGitignore` to shared utility**
|
|
28
|
+
- Moved from `init.handlers.ts` to `src/utils/gitignore.ts` for reuse across handlers
|
|
29
|
+
|
|
30
|
+
## [0.2.36] - 2026-02-14
|
|
31
|
+
|
|
32
|
+
### Added
|
|
33
|
+
|
|
34
|
+
- **Add `.braingrid/temp/` to `.gitignore` during init**
|
|
35
|
+
- `braingrid init` now automatically appends `.braingrid/temp/` to the project's `.gitignore`
|
|
36
|
+
- Prevents session-specific temp files (e.g., acceptance criteria checklists) from being committed
|
|
37
|
+
- Skips if the entry already exists; creates `.gitignore` if none exists
|
|
38
|
+
|
|
39
|
+
### Fixed
|
|
40
|
+
|
|
41
|
+
- **Always offer Claude Code setup updates during init**
|
|
42
|
+
- `braingrid init` now prompts to update Claude Code integration even when it's already installed
|
|
43
|
+
- Ensures users get the latest slash commands, skills, and status line configuration
|
|
44
|
+
|
|
10
45
|
## [0.2.35] - 2026-02-14
|
|
11
46
|
|
|
12
47
|
### Added
|
package/dist/cli.js
CHANGED
|
@@ -222,7 +222,7 @@ async function axiosWithRetry(config2, options) {
|
|
|
222
222
|
|
|
223
223
|
// src/build-config.ts
|
|
224
224
|
var BUILD_ENV = true ? "production" : process.env.NODE_ENV === "test" ? "development" : "production";
|
|
225
|
-
var CLI_VERSION = true ? "0.2.
|
|
225
|
+
var CLI_VERSION = true ? "0.2.37" : "0.0.0-test";
|
|
226
226
|
var PRODUCTION_CONFIG = {
|
|
227
227
|
apiUrl: "https://app.braingrid.ai",
|
|
228
228
|
workosAuthUrl: "https://auth.braingrid.ai",
|
|
@@ -2543,17 +2543,17 @@ function parseGitHubError(error) {
|
|
|
2543
2543
|
}
|
|
2544
2544
|
return errorMessage;
|
|
2545
2545
|
}
|
|
2546
|
-
async function fetchFileFromGitHub(
|
|
2546
|
+
async function fetchFileFromGitHub(path7) {
|
|
2547
2547
|
return withRetry(async () => {
|
|
2548
2548
|
try {
|
|
2549
|
-
const command = `gh api repos/${GITHUB_OWNER}/${GITHUB_REPO}/contents/${
|
|
2549
|
+
const command = `gh api repos/${GITHUB_OWNER}/${GITHUB_REPO}/contents/${path7}`;
|
|
2550
2550
|
const { stdout } = await execAsync(command);
|
|
2551
2551
|
const response = JSON.parse(stdout);
|
|
2552
2552
|
if (response.type !== "file") {
|
|
2553
|
-
throw new Error(`Path ${
|
|
2553
|
+
throw new Error(`Path ${path7} is not a file`);
|
|
2554
2554
|
}
|
|
2555
2555
|
if (!response.content || !response.encoding) {
|
|
2556
|
-
throw new Error(`No content found for file ${
|
|
2556
|
+
throw new Error(`No content found for file ${path7}`);
|
|
2557
2557
|
}
|
|
2558
2558
|
if (response.encoding !== "base64") {
|
|
2559
2559
|
throw new Error(`Unexpected encoding: ${response.encoding}`);
|
|
@@ -2562,18 +2562,18 @@ async function fetchFileFromGitHub(path6) {
|
|
|
2562
2562
|
return content;
|
|
2563
2563
|
} catch (error) {
|
|
2564
2564
|
const parsedError = parseGitHubError(error);
|
|
2565
|
-
throw new Error(`Failed to fetch file ${
|
|
2565
|
+
throw new Error(`Failed to fetch file ${path7}: ${parsedError}`);
|
|
2566
2566
|
}
|
|
2567
2567
|
});
|
|
2568
2568
|
}
|
|
2569
|
-
async function listGitHubDirectory(
|
|
2569
|
+
async function listGitHubDirectory(path7) {
|
|
2570
2570
|
return withRetry(async () => {
|
|
2571
2571
|
try {
|
|
2572
|
-
const command = `gh api repos/${GITHUB_OWNER}/${GITHUB_REPO}/contents/${
|
|
2572
|
+
const command = `gh api repos/${GITHUB_OWNER}/${GITHUB_REPO}/contents/${path7}`;
|
|
2573
2573
|
const { stdout } = await execAsync(command);
|
|
2574
2574
|
const response = JSON.parse(stdout);
|
|
2575
2575
|
if (!Array.isArray(response)) {
|
|
2576
|
-
throw new Error(`Path ${
|
|
2576
|
+
throw new Error(`Path ${path7} is not a directory`);
|
|
2577
2577
|
}
|
|
2578
2578
|
return response.map((item) => ({
|
|
2579
2579
|
name: item.name,
|
|
@@ -2582,7 +2582,7 @@ async function listGitHubDirectory(path6) {
|
|
|
2582
2582
|
}));
|
|
2583
2583
|
} catch (error) {
|
|
2584
2584
|
const parsedError = parseGitHubError(error);
|
|
2585
|
-
throw new Error(`Failed to list directory ${
|
|
2585
|
+
throw new Error(`Failed to list directory ${path7}: ${parsedError}`);
|
|
2586
2586
|
}
|
|
2587
2587
|
});
|
|
2588
2588
|
}
|
|
@@ -3032,9 +3032,33 @@ async function canUseGhAutomation() {
|
|
|
3032
3032
|
return isGhAuthenticated();
|
|
3033
3033
|
}
|
|
3034
3034
|
|
|
3035
|
+
// src/utils/gitignore.ts
|
|
3036
|
+
import { readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
|
|
3037
|
+
import path4 from "path";
|
|
3038
|
+
async function addBraingridTempToGitignore() {
|
|
3039
|
+
const gitRoot = await getGitRoot();
|
|
3040
|
+
if (!gitRoot) return;
|
|
3041
|
+
const gitignorePath = path4.join(gitRoot, ".gitignore");
|
|
3042
|
+
const entry = ".braingrid/temp/";
|
|
3043
|
+
let content = "";
|
|
3044
|
+
try {
|
|
3045
|
+
content = await readFile2(gitignorePath, "utf8");
|
|
3046
|
+
} catch {
|
|
3047
|
+
}
|
|
3048
|
+
const lines = content.split("\n");
|
|
3049
|
+
if (lines.some((line) => line.trim() === entry || line.trim() === ".braingrid/temp")) {
|
|
3050
|
+
return;
|
|
3051
|
+
}
|
|
3052
|
+
const addition = content.endsWith("\n") || content === "" ? "" : "\n";
|
|
3053
|
+
const newContent = `${content}${addition}# BrainGrid temp files
|
|
3054
|
+
${entry}
|
|
3055
|
+
`;
|
|
3056
|
+
await writeFile2(gitignorePath, newContent, "utf8");
|
|
3057
|
+
}
|
|
3058
|
+
|
|
3035
3059
|
// src/utils/local-store.ts
|
|
3036
3060
|
import fs3 from "fs";
|
|
3037
|
-
import
|
|
3061
|
+
import path5 from "path";
|
|
3038
3062
|
|
|
3039
3063
|
// src/types/local-project.ts
|
|
3040
3064
|
import { z } from "zod";
|
|
@@ -3064,11 +3088,11 @@ async function getDefaultCwd() {
|
|
|
3064
3088
|
}
|
|
3065
3089
|
async function getBraingridDir(cwd) {
|
|
3066
3090
|
const dir = cwd ?? await getDefaultCwd();
|
|
3067
|
-
return
|
|
3091
|
+
return path5.join(dir, BRAINGRID_DIR);
|
|
3068
3092
|
}
|
|
3069
3093
|
async function getProjectConfigPath(cwd) {
|
|
3070
3094
|
const braingridDir = await getBraingridDir(cwd);
|
|
3071
|
-
return
|
|
3095
|
+
return path5.join(braingridDir, PROJECT_CONFIG_FILE);
|
|
3072
3096
|
}
|
|
3073
3097
|
async function projectConfigExists(cwd) {
|
|
3074
3098
|
const configPath = await getProjectConfigPath(cwd);
|
|
@@ -3285,7 +3309,7 @@ async function checkAndShowUpdateWarning() {
|
|
|
3285
3309
|
|
|
3286
3310
|
// src/handlers/setup.handlers.ts
|
|
3287
3311
|
import * as fs4 from "fs/promises";
|
|
3288
|
-
import * as
|
|
3312
|
+
import * as path6 from "path";
|
|
3289
3313
|
import { select } from "@inquirer/prompts";
|
|
3290
3314
|
import chalk8 from "chalk";
|
|
3291
3315
|
async function fileExists(filePath) {
|
|
@@ -3317,22 +3341,23 @@ async function checkPrerequisites() {
|
|
|
3317
3341
|
}
|
|
3318
3342
|
async function getFileList(sourcePaths, targetPaths) {
|
|
3319
3343
|
const operations = [];
|
|
3320
|
-
async function processDirectory(sourceDir, targetDir) {
|
|
3344
|
+
async function processDirectory(sourceDir, targetDir, dirIndex) {
|
|
3321
3345
|
try {
|
|
3322
3346
|
const items = await listGitHubDirectory(sourceDir);
|
|
3323
3347
|
for (const item of items) {
|
|
3324
3348
|
if (item.type === "file") {
|
|
3325
|
-
const itemTargetPath =
|
|
3349
|
+
const itemTargetPath = path6.join(targetDir, item.name);
|
|
3326
3350
|
const exists = await fileExists(itemTargetPath);
|
|
3327
3351
|
operations.push({
|
|
3328
3352
|
type: "copy",
|
|
3329
3353
|
sourcePath: item.path,
|
|
3330
3354
|
targetPath: itemTargetPath,
|
|
3331
|
-
exists
|
|
3355
|
+
exists,
|
|
3356
|
+
dirIndex
|
|
3332
3357
|
});
|
|
3333
3358
|
} else if (item.type === "dir") {
|
|
3334
|
-
const itemTargetPath =
|
|
3335
|
-
await processDirectory(item.path, itemTargetPath);
|
|
3359
|
+
const itemTargetPath = path6.join(targetDir, item.name);
|
|
3360
|
+
await processDirectory(item.path, itemTargetPath, dirIndex);
|
|
3336
3361
|
}
|
|
3337
3362
|
}
|
|
3338
3363
|
} catch (error) {
|
|
@@ -3343,7 +3368,7 @@ async function getFileList(sourcePaths, targetPaths) {
|
|
|
3343
3368
|
}
|
|
3344
3369
|
}
|
|
3345
3370
|
for (let i = 0; i < sourcePaths.length; i++) {
|
|
3346
|
-
await processDirectory(sourcePaths[i], targetPaths[i]);
|
|
3371
|
+
await processDirectory(sourcePaths[i], targetPaths[i], i);
|
|
3347
3372
|
}
|
|
3348
3373
|
return operations;
|
|
3349
3374
|
}
|
|
@@ -3379,15 +3404,15 @@ async function promptForConflict(filePath) {
|
|
|
3379
3404
|
});
|
|
3380
3405
|
return answer;
|
|
3381
3406
|
}
|
|
3382
|
-
async function installFiles(operations, force) {
|
|
3383
|
-
|
|
3407
|
+
async function installFiles(operations, force, dirCount) {
|
|
3408
|
+
const installedPerDir = new Array(dirCount).fill(0);
|
|
3384
3409
|
let skipped = 0;
|
|
3385
3410
|
let overwriteAll = force;
|
|
3386
3411
|
for (const operation of operations) {
|
|
3387
3412
|
if (operation.exists && !overwriteAll) {
|
|
3388
3413
|
const response = await promptForConflict(operation.targetPath);
|
|
3389
3414
|
if (response === "quit") {
|
|
3390
|
-
return {
|
|
3415
|
+
return { installedPerDir, skipped, cancelled: true };
|
|
3391
3416
|
} else if (response === "skip") {
|
|
3392
3417
|
skipped++;
|
|
3393
3418
|
continue;
|
|
@@ -3397,7 +3422,9 @@ async function installFiles(operations, force) {
|
|
|
3397
3422
|
}
|
|
3398
3423
|
try {
|
|
3399
3424
|
await copyFileFromGitHub(operation.sourcePath, operation.targetPath);
|
|
3400
|
-
|
|
3425
|
+
if (operation.dirIndex !== void 0) {
|
|
3426
|
+
installedPerDir[operation.dirIndex]++;
|
|
3427
|
+
}
|
|
3401
3428
|
} catch (error) {
|
|
3402
3429
|
console.error(
|
|
3403
3430
|
chalk8.red(`Failed to copy ${operation.targetPath}:`),
|
|
@@ -3406,104 +3433,150 @@ async function installFiles(operations, force) {
|
|
|
3406
3433
|
skipped++;
|
|
3407
3434
|
}
|
|
3408
3435
|
}
|
|
3409
|
-
return {
|
|
3436
|
+
return { installedPerDir, skipped, cancelled: false };
|
|
3410
3437
|
}
|
|
3411
3438
|
async function _handleSetup(config2, opts) {
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
console.log(chalk8.bold(`\u{1F680} Setting up ${config2.name} integration...
|
|
3439
|
+
const prerequisiteError = await checkPrerequisites();
|
|
3440
|
+
if (prerequisiteError) {
|
|
3441
|
+
return prerequisiteError;
|
|
3442
|
+
}
|
|
3443
|
+
console.log(chalk8.bold(`\u{1F680} Setting up ${config2.name} integration...
|
|
3418
3444
|
`));
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3445
|
+
const operations = await getFileList(config2.sourceDirs, config2.targetDirs);
|
|
3446
|
+
const injectionFileExists = await fileExists(config2.injection.targetFile);
|
|
3447
|
+
operations.push({
|
|
3448
|
+
type: "inject",
|
|
3449
|
+
sourcePath: config2.injection.sourceFile,
|
|
3450
|
+
targetPath: config2.injection.targetFile,
|
|
3451
|
+
exists: injectionFileExists
|
|
3452
|
+
});
|
|
3453
|
+
displayInstallationPlan(
|
|
3454
|
+
operations.filter((op) => op.type === "copy"),
|
|
3455
|
+
config2.injection.targetFile
|
|
3456
|
+
);
|
|
3457
|
+
if (opts.dryRun) {
|
|
3458
|
+
return {
|
|
3459
|
+
success: true,
|
|
3460
|
+
message: chalk8.green("\u2705 Dry-run complete. No files were modified.\n\n") + chalk8.dim(`Would install ${operations.length} files.`)
|
|
3461
|
+
};
|
|
3462
|
+
}
|
|
3463
|
+
const copyOps = operations.filter((op) => op.type === "copy");
|
|
3464
|
+
const result = await installFiles(copyOps, opts.force || false, config2.sourceDirs.length);
|
|
3465
|
+
if (result.cancelled) {
|
|
3466
|
+
const totalInstalled = result.installedPerDir.reduce((a, b) => a + b, 0);
|
|
3467
|
+
return {
|
|
3468
|
+
success: false,
|
|
3469
|
+
message: chalk8.yellow("\u26A0\uFE0F Installation cancelled.\n\n") + chalk8.dim(`Installed: ${totalInstalled}, Skipped: ${result.skipped}`),
|
|
3470
|
+
code: "CANCELLED"
|
|
3471
|
+
};
|
|
3472
|
+
}
|
|
3473
|
+
try {
|
|
3474
|
+
const content = await fetchFileFromGitHub(config2.injection.sourceFile);
|
|
3475
|
+
await injectContentIntoFile(config2.injection.targetFile, content);
|
|
3476
|
+
} catch (error) {
|
|
3477
|
+
console.error(
|
|
3478
|
+
chalk8.red(`Failed to inject content into ${config2.injection.targetFile}:`),
|
|
3479
|
+
error instanceof Error ? error.message : String(error)
|
|
3430
3480
|
);
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3481
|
+
}
|
|
3482
|
+
await copyBraingridReadme();
|
|
3483
|
+
return {
|
|
3484
|
+
success: true,
|
|
3485
|
+
data: {
|
|
3486
|
+
installedPerDir: result.installedPerDir,
|
|
3487
|
+
skipped: result.skipped
|
|
3436
3488
|
}
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3489
|
+
};
|
|
3490
|
+
}
|
|
3491
|
+
function buildSuccessMessage(config2, installedPerDir, extras) {
|
|
3492
|
+
let dirLines = "";
|
|
3493
|
+
for (let i = 0; i < config2.dirLabels.length; i++) {
|
|
3494
|
+
const count = installedPerDir[i] ?? 0;
|
|
3495
|
+
if (count === 0) continue;
|
|
3496
|
+
const { label } = config2.dirLabels[i];
|
|
3497
|
+
dirLines += chalk8.dim(` ${label}: ${count}
|
|
3498
|
+
`);
|
|
3499
|
+
}
|
|
3500
|
+
return chalk8.green(`\u2705 ${config2.name} integration installed successfully!
|
|
3501
|
+
|
|
3502
|
+
`) + chalk8.dim("Files installed:\n") + dirLines + extras + chalk8.dim(` Content injected into: ${config2.injection.targetFile}
|
|
3503
|
+
|
|
3504
|
+
`) + chalk8.dim("Next steps:\n") + chalk8.dim(" 1. Review the integration files\n") + chalk8.dim(` 2. Open ${config2.name}
|
|
3505
|
+
`) + chalk8.dim(" 3. Try the /specify or /breakdown commands\n") + chalk8.dim(" 4. Learn more: ") + chalk8.cyan(config2.docsUrl);
|
|
3506
|
+
}
|
|
3507
|
+
function isSetupResult(result) {
|
|
3508
|
+
return "data" in result && result.success === true && !("message" in result);
|
|
3509
|
+
}
|
|
3510
|
+
async function handleSetupClaudeCode(opts) {
|
|
3511
|
+
const config2 = {
|
|
3512
|
+
name: "Claude Code",
|
|
3513
|
+
sourceDirs: ["claude-code/commands", "claude-code/skills/braingrid-cli"],
|
|
3514
|
+
targetDirs: [".claude/commands", ".claude/skills/braingrid-cli"],
|
|
3515
|
+
dirLabels: [
|
|
3516
|
+
{ label: "Commands", countMode: "files" },
|
|
3517
|
+
{ label: "Skills", countMode: "single" }
|
|
3518
|
+
],
|
|
3519
|
+
injection: {
|
|
3520
|
+
sourceFile: "claude-code/CLAUDE.md",
|
|
3521
|
+
targetFile: "CLAUDE.md"
|
|
3522
|
+
},
|
|
3523
|
+
docsUrl: "https://docs.braingrid.ai/claude-code"
|
|
3524
|
+
};
|
|
3525
|
+
try {
|
|
3526
|
+
const setupResult = await _handleSetup(config2, opts);
|
|
3527
|
+
if (!isSetupResult(setupResult)) {
|
|
3528
|
+
return setupResult;
|
|
3445
3529
|
}
|
|
3530
|
+
const { installedPerDir } = setupResult.data;
|
|
3531
|
+
const displayPerDir = installedPerDir.map(
|
|
3532
|
+
(count, i) => config2.dirLabels[i].countMode === "single" ? Math.min(count, 1) : count
|
|
3533
|
+
);
|
|
3534
|
+
let statusLineInstalled = false;
|
|
3446
3535
|
try {
|
|
3447
|
-
const
|
|
3448
|
-
await
|
|
3536
|
+
const scriptContent = await fetchFileFromGitHub("claude-code/statusline.sh");
|
|
3537
|
+
await installStatusLineScript(scriptContent);
|
|
3538
|
+
statusLineInstalled = true;
|
|
3449
3539
|
} catch (error) {
|
|
3450
3540
|
console.error(
|
|
3451
|
-
chalk8.
|
|
3541
|
+
chalk8.yellow("\u26A0\uFE0F Failed to install status line script:"),
|
|
3452
3542
|
error instanceof Error ? error.message : String(error)
|
|
3453
3543
|
);
|
|
3454
3544
|
}
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
} catch (error) {
|
|
3463
|
-
console.error(
|
|
3464
|
-
chalk8.yellow("\u26A0\uFE0F Failed to install status line script:"),
|
|
3465
|
-
error instanceof Error ? error.message : String(error)
|
|
3466
|
-
);
|
|
3467
|
-
}
|
|
3545
|
+
try {
|
|
3546
|
+
await updateClaudeSettings();
|
|
3547
|
+
} catch (error) {
|
|
3548
|
+
console.error(
|
|
3549
|
+
chalk8.yellow("\u26A0\uFE0F Failed to update Claude settings:"),
|
|
3550
|
+
error instanceof Error ? error.message : String(error)
|
|
3551
|
+
);
|
|
3468
3552
|
}
|
|
3469
3553
|
let syncHookInstalled = false;
|
|
3554
|
+
try {
|
|
3555
|
+
const syncContent = await fetchFileFromGitHub("claude-code/hooks/sync-braingrid-task.sh");
|
|
3556
|
+
await installHookScript(syncContent);
|
|
3557
|
+
syncHookInstalled = true;
|
|
3558
|
+
} catch (error) {
|
|
3559
|
+
console.error(
|
|
3560
|
+
chalk8.yellow("\u26A0\uFE0F Failed to install sync hook:"),
|
|
3561
|
+
error instanceof Error ? error.message : String(error)
|
|
3562
|
+
);
|
|
3563
|
+
}
|
|
3470
3564
|
let createHookInstalled = false;
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
);
|
|
3481
|
-
}
|
|
3482
|
-
try {
|
|
3483
|
-
const createContent = await fetchFileFromGitHub(
|
|
3484
|
-
"claude-code/hooks/create-braingrid-task.sh"
|
|
3485
|
-
);
|
|
3486
|
-
await installHookScript(createContent, ".claude/hooks/create-braingrid-task.sh");
|
|
3487
|
-
createHookInstalled = true;
|
|
3488
|
-
} catch (error) {
|
|
3489
|
-
console.error(
|
|
3490
|
-
chalk8.yellow("\u26A0\uFE0F Failed to install create hook:"),
|
|
3491
|
-
error instanceof Error ? error.message : String(error)
|
|
3492
|
-
);
|
|
3493
|
-
}
|
|
3565
|
+
try {
|
|
3566
|
+
const createContent = await fetchFileFromGitHub("claude-code/hooks/create-braingrid-task.sh");
|
|
3567
|
+
await installHookScript(createContent, ".claude/hooks/create-braingrid-task.sh");
|
|
3568
|
+
createHookInstalled = true;
|
|
3569
|
+
} catch (error) {
|
|
3570
|
+
console.error(
|
|
3571
|
+
chalk8.yellow("\u26A0\uFE0F Failed to install create hook:"),
|
|
3572
|
+
error instanceof Error ? error.message : String(error)
|
|
3573
|
+
);
|
|
3494
3574
|
}
|
|
3495
|
-
await copyBraingridReadme();
|
|
3496
3575
|
const statusLineMessage = statusLineInstalled ? chalk8.dim(" Status line: .claude/statusline.sh\n") : "";
|
|
3497
3576
|
const hooksMessage = (syncHookInstalled ? chalk8.dim(" Hook script: .claude/hooks/sync-braingrid-task.sh\n") : "") + (createHookInstalled ? chalk8.dim(" Hook script: .claude/hooks/create-braingrid-task.sh\n") : "");
|
|
3498
3577
|
return {
|
|
3499
3578
|
success: true,
|
|
3500
|
-
message:
|
|
3501
|
-
|
|
3502
|
-
`) + chalk8.dim("Files installed:\n") + chalk8.dim(` Commands: ${result.installed} files
|
|
3503
|
-
`) + statusLineMessage + hooksMessage + chalk8.dim(` Content injected into: ${config2.injection.targetFile}
|
|
3504
|
-
|
|
3505
|
-
`) + chalk8.dim("Next steps:\n") + chalk8.dim(" 1. Review the integration files\n") + chalk8.dim(` 2. Open ${config2.name}
|
|
3506
|
-
`) + chalk8.dim(" 3. Try the /specify or /breakdown commands\n") + chalk8.dim(" 4. Learn more: ") + chalk8.cyan(config2.docsUrl)
|
|
3579
|
+
message: buildSuccessMessage(config2, displayPerDir, statusLineMessage + hooksMessage)
|
|
3507
3580
|
};
|
|
3508
3581
|
} catch (error) {
|
|
3509
3582
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
@@ -3513,31 +3586,38 @@ async function _handleSetup(config2, opts) {
|
|
|
3513
3586
|
};
|
|
3514
3587
|
}
|
|
3515
3588
|
}
|
|
3516
|
-
async function handleSetupClaudeCode(opts) {
|
|
3517
|
-
const config2 = {
|
|
3518
|
-
name: "Claude Code",
|
|
3519
|
-
sourceDirs: ["claude-code/commands", "claude-code/skills/braingrid-cli"],
|
|
3520
|
-
targetDirs: [".claude/commands", ".claude/skills/braingrid-cli"],
|
|
3521
|
-
injection: {
|
|
3522
|
-
sourceFile: "claude-code/CLAUDE.md",
|
|
3523
|
-
targetFile: "CLAUDE.md"
|
|
3524
|
-
},
|
|
3525
|
-
docsUrl: "https://docs.braingrid.ai/claude-code"
|
|
3526
|
-
};
|
|
3527
|
-
return _handleSetup(config2, opts);
|
|
3528
|
-
}
|
|
3529
3589
|
async function handleSetupCursor(opts) {
|
|
3530
3590
|
const config2 = {
|
|
3531
3591
|
name: "Cursor",
|
|
3532
3592
|
sourceDirs: ["cursor/commands", "cursor/rules"],
|
|
3533
3593
|
targetDirs: [".cursor/commands", ".cursor/rules"],
|
|
3594
|
+
dirLabels: [
|
|
3595
|
+
{ label: "Commands", countMode: "files" },
|
|
3596
|
+
{ label: "Rules", countMode: "files" }
|
|
3597
|
+
],
|
|
3534
3598
|
injection: {
|
|
3535
3599
|
sourceFile: "cursor/AGENTS.md",
|
|
3536
3600
|
targetFile: "AGENTS.md"
|
|
3537
3601
|
},
|
|
3538
3602
|
docsUrl: "https://docs.braingrid.ai/cursor"
|
|
3539
3603
|
};
|
|
3540
|
-
|
|
3604
|
+
try {
|
|
3605
|
+
const setupResult = await _handleSetup(config2, opts);
|
|
3606
|
+
if (!isSetupResult(setupResult)) {
|
|
3607
|
+
return setupResult;
|
|
3608
|
+
}
|
|
3609
|
+
const { installedPerDir } = setupResult.data;
|
|
3610
|
+
return {
|
|
3611
|
+
success: true,
|
|
3612
|
+
message: buildSuccessMessage(config2, installedPerDir, "")
|
|
3613
|
+
};
|
|
3614
|
+
} catch (error) {
|
|
3615
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
3616
|
+
return {
|
|
3617
|
+
success: false,
|
|
3618
|
+
message: chalk8.red(`\u274C Setup failed: ${errorMessage}`)
|
|
3619
|
+
};
|
|
3620
|
+
}
|
|
3541
3621
|
}
|
|
3542
3622
|
|
|
3543
3623
|
// src/handlers/update.handlers.ts
|
|
@@ -3694,6 +3774,7 @@ async function handleUpdate(opts) {
|
|
|
3694
3774
|
output += chalk9.green("\u2705 You are already on the latest version!\n");
|
|
3695
3775
|
console.log(output);
|
|
3696
3776
|
await copyBraingridReadme();
|
|
3777
|
+
await addBraingridTempToGitignore();
|
|
3697
3778
|
const setupOutput2 = await promptForIdeUpdates();
|
|
3698
3779
|
return {
|
|
3699
3780
|
success: true,
|
|
@@ -3733,6 +3814,7 @@ async function handleUpdate(opts) {
|
|
|
3733
3814
|
console.log(output);
|
|
3734
3815
|
executeUpdate(packageManager, PACKAGE_NAME);
|
|
3735
3816
|
await copyBraingridReadme();
|
|
3817
|
+
await addBraingridTempToGitignore();
|
|
3736
3818
|
const setupOutput = await promptForIdeUpdates();
|
|
3737
3819
|
return {
|
|
3738
3820
|
success: true,
|
|
@@ -4290,6 +4372,7 @@ async function handleInit(opts) {
|
|
|
4290
4372
|
created_at: project2.created_at
|
|
4291
4373
|
};
|
|
4292
4374
|
await saveProjectConfig(localConfig);
|
|
4375
|
+
await addBraingridTempToGitignore();
|
|
4293
4376
|
await copyBraingridReadme();
|
|
4294
4377
|
console.log(
|
|
4295
4378
|
chalk10.green("\u2705 Repository initialized successfully!\n\n") + chalk10.dim("Project: ") + chalk10.cyan(project2.name) + chalk10.dim(` (${project2.short_id})`) + "\n" + chalk10.dim("Config: ") + chalk10.gray(".braingrid/project.json") + "\n"
|
|
@@ -4297,62 +4380,62 @@ async function handleInit(opts) {
|
|
|
4297
4380
|
const installedIDEs = await detectInstalledIDEs();
|
|
4298
4381
|
if (installedIDEs.claudeCode) {
|
|
4299
4382
|
const claudeSetupExists = await fileExists2(".claude/commands/specify.md");
|
|
4300
|
-
|
|
4383
|
+
console.log("");
|
|
4384
|
+
const setupClaude = await confirm2({
|
|
4385
|
+
message: claudeSetupExists ? "Claude Code detected. Update BrainGrid integration?" : "Claude Code detected. Install BrainGrid integration? (slash commands, skills, status line)",
|
|
4386
|
+
default: true
|
|
4387
|
+
});
|
|
4388
|
+
if (setupClaude) {
|
|
4301
4389
|
console.log("");
|
|
4302
|
-
|
|
4303
|
-
|
|
4304
|
-
|
|
4305
|
-
|
|
4306
|
-
|
|
4307
|
-
|
|
4308
|
-
|
|
4309
|
-
|
|
4310
|
-
if (result.success) {
|
|
4311
|
-
console.log(result.message);
|
|
4312
|
-
} else {
|
|
4313
|
-
console.log(chalk10.yellow("\u26A0\uFE0F Claude Code setup was not completed."));
|
|
4314
|
-
console.log(
|
|
4315
|
-
chalk10.dim("You can run ") + chalk10.cyan("braingrid setup claude-code") + chalk10.dim(" later.")
|
|
4316
|
-
);
|
|
4317
|
-
}
|
|
4318
|
-
} catch {
|
|
4319
|
-
console.log(chalk10.yellow("\u26A0\uFE0F Claude Code setup encountered an error."));
|
|
4390
|
+
try {
|
|
4391
|
+
const result = await handleSetupClaudeCode({
|
|
4392
|
+
force: claudeSetupExists
|
|
4393
|
+
});
|
|
4394
|
+
if (result.success) {
|
|
4395
|
+
console.log(result.message);
|
|
4396
|
+
} else {
|
|
4397
|
+
console.log(chalk10.yellow("\u26A0\uFE0F Claude Code setup was not completed."));
|
|
4320
4398
|
console.log(
|
|
4321
4399
|
chalk10.dim("You can run ") + chalk10.cyan("braingrid setup claude-code") + chalk10.dim(" later.")
|
|
4322
4400
|
);
|
|
4323
4401
|
}
|
|
4324
|
-
|
|
4402
|
+
} catch {
|
|
4403
|
+
console.log(chalk10.yellow("\u26A0\uFE0F Claude Code setup encountered an error."));
|
|
4404
|
+
console.log(
|
|
4405
|
+
chalk10.dim("You can run ") + chalk10.cyan("braingrid setup claude-code") + chalk10.dim(" later.")
|
|
4406
|
+
);
|
|
4325
4407
|
}
|
|
4408
|
+
console.log("");
|
|
4326
4409
|
}
|
|
4327
4410
|
}
|
|
4328
4411
|
if (installedIDEs.cursor) {
|
|
4329
4412
|
const cursorSetupExists = await fileExists2(".cursor/commands/specify.md");
|
|
4330
|
-
|
|
4413
|
+
console.log("");
|
|
4414
|
+
const setupCursor = await confirm2({
|
|
4415
|
+
message: cursorSetupExists ? "Cursor detected. Update BrainGrid integration?" : "Cursor detected. Install BrainGrid integration? (slash commands, rules, context)",
|
|
4416
|
+
default: true
|
|
4417
|
+
});
|
|
4418
|
+
if (setupCursor) {
|
|
4331
4419
|
console.log("");
|
|
4332
|
-
|
|
4333
|
-
|
|
4334
|
-
|
|
4335
|
-
|
|
4336
|
-
|
|
4337
|
-
|
|
4338
|
-
|
|
4339
|
-
|
|
4340
|
-
if (result.success) {
|
|
4341
|
-
console.log(result.message);
|
|
4342
|
-
} else {
|
|
4343
|
-
console.log(chalk10.yellow("\u26A0\uFE0F Cursor setup was not completed."));
|
|
4344
|
-
console.log(
|
|
4345
|
-
chalk10.dim("You can run ") + chalk10.cyan("braingrid setup cursor") + chalk10.dim(" later.")
|
|
4346
|
-
);
|
|
4347
|
-
}
|
|
4348
|
-
} catch {
|
|
4349
|
-
console.log(chalk10.yellow("\u26A0\uFE0F Cursor setup encountered an error."));
|
|
4420
|
+
try {
|
|
4421
|
+
const result = await handleSetupCursor({
|
|
4422
|
+
force: cursorSetupExists
|
|
4423
|
+
});
|
|
4424
|
+
if (result.success) {
|
|
4425
|
+
console.log(result.message);
|
|
4426
|
+
} else {
|
|
4427
|
+
console.log(chalk10.yellow("\u26A0\uFE0F Cursor setup was not completed."));
|
|
4350
4428
|
console.log(
|
|
4351
4429
|
chalk10.dim("You can run ") + chalk10.cyan("braingrid setup cursor") + chalk10.dim(" later.")
|
|
4352
4430
|
);
|
|
4353
4431
|
}
|
|
4354
|
-
|
|
4432
|
+
} catch {
|
|
4433
|
+
console.log(chalk10.yellow("\u26A0\uFE0F Cursor setup encountered an error."));
|
|
4434
|
+
console.log(
|
|
4435
|
+
chalk10.dim("You can run ") + chalk10.cyan("braingrid setup cursor") + chalk10.dim(" later.")
|
|
4436
|
+
);
|
|
4355
4437
|
}
|
|
4438
|
+
console.log("");
|
|
4356
4439
|
}
|
|
4357
4440
|
}
|
|
4358
4441
|
return {
|