@braingrid/cli 0.2.36 → 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 +20 -0
- package/dist/cli.js +75 -48
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,26 @@ 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
|
+
|
|
10
30
|
## [0.2.36] - 2026-02-14
|
|
11
31
|
|
|
12
32
|
### 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",
|
|
@@ -2198,8 +2198,7 @@ async function handleCompletion(shellArg, opts) {
|
|
|
2198
2198
|
}
|
|
2199
2199
|
|
|
2200
2200
|
// src/handlers/init.handlers.ts
|
|
2201
|
-
import { access as access3
|
|
2202
|
-
import path6 from "path";
|
|
2201
|
+
import { access as access3 } from "fs/promises";
|
|
2203
2202
|
import { confirm as confirm2, input, select as select3 } from "@inquirer/prompts";
|
|
2204
2203
|
import chalk10 from "chalk";
|
|
2205
2204
|
|
|
@@ -3033,9 +3032,33 @@ async function canUseGhAutomation() {
|
|
|
3033
3032
|
return isGhAuthenticated();
|
|
3034
3033
|
}
|
|
3035
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
|
+
|
|
3036
3059
|
// src/utils/local-store.ts
|
|
3037
3060
|
import fs3 from "fs";
|
|
3038
|
-
import
|
|
3061
|
+
import path5 from "path";
|
|
3039
3062
|
|
|
3040
3063
|
// src/types/local-project.ts
|
|
3041
3064
|
import { z } from "zod";
|
|
@@ -3065,11 +3088,11 @@ async function getDefaultCwd() {
|
|
|
3065
3088
|
}
|
|
3066
3089
|
async function getBraingridDir(cwd) {
|
|
3067
3090
|
const dir = cwd ?? await getDefaultCwd();
|
|
3068
|
-
return
|
|
3091
|
+
return path5.join(dir, BRAINGRID_DIR);
|
|
3069
3092
|
}
|
|
3070
3093
|
async function getProjectConfigPath(cwd) {
|
|
3071
3094
|
const braingridDir = await getBraingridDir(cwd);
|
|
3072
|
-
return
|
|
3095
|
+
return path5.join(braingridDir, PROJECT_CONFIG_FILE);
|
|
3073
3096
|
}
|
|
3074
3097
|
async function projectConfigExists(cwd) {
|
|
3075
3098
|
const configPath = await getProjectConfigPath(cwd);
|
|
@@ -3286,7 +3309,7 @@ async function checkAndShowUpdateWarning() {
|
|
|
3286
3309
|
|
|
3287
3310
|
// src/handlers/setup.handlers.ts
|
|
3288
3311
|
import * as fs4 from "fs/promises";
|
|
3289
|
-
import * as
|
|
3312
|
+
import * as path6 from "path";
|
|
3290
3313
|
import { select } from "@inquirer/prompts";
|
|
3291
3314
|
import chalk8 from "chalk";
|
|
3292
3315
|
async function fileExists(filePath) {
|
|
@@ -3318,22 +3341,23 @@ async function checkPrerequisites() {
|
|
|
3318
3341
|
}
|
|
3319
3342
|
async function getFileList(sourcePaths, targetPaths) {
|
|
3320
3343
|
const operations = [];
|
|
3321
|
-
async function processDirectory(sourceDir, targetDir) {
|
|
3344
|
+
async function processDirectory(sourceDir, targetDir, dirIndex) {
|
|
3322
3345
|
try {
|
|
3323
3346
|
const items = await listGitHubDirectory(sourceDir);
|
|
3324
3347
|
for (const item of items) {
|
|
3325
3348
|
if (item.type === "file") {
|
|
3326
|
-
const itemTargetPath =
|
|
3349
|
+
const itemTargetPath = path6.join(targetDir, item.name);
|
|
3327
3350
|
const exists = await fileExists(itemTargetPath);
|
|
3328
3351
|
operations.push({
|
|
3329
3352
|
type: "copy",
|
|
3330
3353
|
sourcePath: item.path,
|
|
3331
3354
|
targetPath: itemTargetPath,
|
|
3332
|
-
exists
|
|
3355
|
+
exists,
|
|
3356
|
+
dirIndex
|
|
3333
3357
|
});
|
|
3334
3358
|
} else if (item.type === "dir") {
|
|
3335
|
-
const itemTargetPath =
|
|
3336
|
-
await processDirectory(item.path, itemTargetPath);
|
|
3359
|
+
const itemTargetPath = path6.join(targetDir, item.name);
|
|
3360
|
+
await processDirectory(item.path, itemTargetPath, dirIndex);
|
|
3337
3361
|
}
|
|
3338
3362
|
}
|
|
3339
3363
|
} catch (error) {
|
|
@@ -3344,7 +3368,7 @@ async function getFileList(sourcePaths, targetPaths) {
|
|
|
3344
3368
|
}
|
|
3345
3369
|
}
|
|
3346
3370
|
for (let i = 0; i < sourcePaths.length; i++) {
|
|
3347
|
-
await processDirectory(sourcePaths[i], targetPaths[i]);
|
|
3371
|
+
await processDirectory(sourcePaths[i], targetPaths[i], i);
|
|
3348
3372
|
}
|
|
3349
3373
|
return operations;
|
|
3350
3374
|
}
|
|
@@ -3380,15 +3404,15 @@ async function promptForConflict(filePath) {
|
|
|
3380
3404
|
});
|
|
3381
3405
|
return answer;
|
|
3382
3406
|
}
|
|
3383
|
-
async function installFiles(operations, force) {
|
|
3384
|
-
|
|
3407
|
+
async function installFiles(operations, force, dirCount) {
|
|
3408
|
+
const installedPerDir = new Array(dirCount).fill(0);
|
|
3385
3409
|
let skipped = 0;
|
|
3386
3410
|
let overwriteAll = force;
|
|
3387
3411
|
for (const operation of operations) {
|
|
3388
3412
|
if (operation.exists && !overwriteAll) {
|
|
3389
3413
|
const response = await promptForConflict(operation.targetPath);
|
|
3390
3414
|
if (response === "quit") {
|
|
3391
|
-
return {
|
|
3415
|
+
return { installedPerDir, skipped, cancelled: true };
|
|
3392
3416
|
} else if (response === "skip") {
|
|
3393
3417
|
skipped++;
|
|
3394
3418
|
continue;
|
|
@@ -3398,7 +3422,9 @@ async function installFiles(operations, force) {
|
|
|
3398
3422
|
}
|
|
3399
3423
|
try {
|
|
3400
3424
|
await copyFileFromGitHub(operation.sourcePath, operation.targetPath);
|
|
3401
|
-
|
|
3425
|
+
if (operation.dirIndex !== void 0) {
|
|
3426
|
+
installedPerDir[operation.dirIndex]++;
|
|
3427
|
+
}
|
|
3402
3428
|
} catch (error) {
|
|
3403
3429
|
console.error(
|
|
3404
3430
|
chalk8.red(`Failed to copy ${operation.targetPath}:`),
|
|
@@ -3407,7 +3433,7 @@ async function installFiles(operations, force) {
|
|
|
3407
3433
|
skipped++;
|
|
3408
3434
|
}
|
|
3409
3435
|
}
|
|
3410
|
-
return {
|
|
3436
|
+
return { installedPerDir, skipped, cancelled: false };
|
|
3411
3437
|
}
|
|
3412
3438
|
async function _handleSetup(config2, opts) {
|
|
3413
3439
|
const prerequisiteError = await checkPrerequisites();
|
|
@@ -3435,11 +3461,12 @@ async function _handleSetup(config2, opts) {
|
|
|
3435
3461
|
};
|
|
3436
3462
|
}
|
|
3437
3463
|
const copyOps = operations.filter((op) => op.type === "copy");
|
|
3438
|
-
const result = await installFiles(copyOps, opts.force || false);
|
|
3464
|
+
const result = await installFiles(copyOps, opts.force || false, config2.sourceDirs.length);
|
|
3439
3465
|
if (result.cancelled) {
|
|
3466
|
+
const totalInstalled = result.installedPerDir.reduce((a, b) => a + b, 0);
|
|
3440
3467
|
return {
|
|
3441
3468
|
success: false,
|
|
3442
|
-
message: chalk8.yellow("\u26A0\uFE0F Installation cancelled.\n\n") + chalk8.dim(`Installed: ${
|
|
3469
|
+
message: chalk8.yellow("\u26A0\uFE0F Installation cancelled.\n\n") + chalk8.dim(`Installed: ${totalInstalled}, Skipped: ${result.skipped}`),
|
|
3443
3470
|
code: "CANCELLED"
|
|
3444
3471
|
};
|
|
3445
3472
|
}
|
|
@@ -3456,16 +3483,23 @@ async function _handleSetup(config2, opts) {
|
|
|
3456
3483
|
return {
|
|
3457
3484
|
success: true,
|
|
3458
3485
|
data: {
|
|
3459
|
-
|
|
3486
|
+
installedPerDir: result.installedPerDir,
|
|
3460
3487
|
skipped: result.skipped
|
|
3461
3488
|
}
|
|
3462
3489
|
};
|
|
3463
3490
|
}
|
|
3464
|
-
function buildSuccessMessage(config2,
|
|
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
|
+
}
|
|
3465
3500
|
return chalk8.green(`\u2705 ${config2.name} integration installed successfully!
|
|
3466
3501
|
|
|
3467
|
-
`) + chalk8.dim("Files installed:\n") + chalk8.dim(`
|
|
3468
|
-
`) + extras + chalk8.dim(` Content injected into: ${config2.injection.targetFile}
|
|
3502
|
+
`) + chalk8.dim("Files installed:\n") + dirLines + extras + chalk8.dim(` Content injected into: ${config2.injection.targetFile}
|
|
3469
3503
|
|
|
3470
3504
|
`) + chalk8.dim("Next steps:\n") + chalk8.dim(" 1. Review the integration files\n") + chalk8.dim(` 2. Open ${config2.name}
|
|
3471
3505
|
`) + chalk8.dim(" 3. Try the /specify or /breakdown commands\n") + chalk8.dim(" 4. Learn more: ") + chalk8.cyan(config2.docsUrl);
|
|
@@ -3478,6 +3512,10 @@ async function handleSetupClaudeCode(opts) {
|
|
|
3478
3512
|
name: "Claude Code",
|
|
3479
3513
|
sourceDirs: ["claude-code/commands", "claude-code/skills/braingrid-cli"],
|
|
3480
3514
|
targetDirs: [".claude/commands", ".claude/skills/braingrid-cli"],
|
|
3515
|
+
dirLabels: [
|
|
3516
|
+
{ label: "Commands", countMode: "files" },
|
|
3517
|
+
{ label: "Skills", countMode: "single" }
|
|
3518
|
+
],
|
|
3481
3519
|
injection: {
|
|
3482
3520
|
sourceFile: "claude-code/CLAUDE.md",
|
|
3483
3521
|
targetFile: "CLAUDE.md"
|
|
@@ -3489,7 +3527,10 @@ async function handleSetupClaudeCode(opts) {
|
|
|
3489
3527
|
if (!isSetupResult(setupResult)) {
|
|
3490
3528
|
return setupResult;
|
|
3491
3529
|
}
|
|
3492
|
-
const {
|
|
3530
|
+
const { installedPerDir } = setupResult.data;
|
|
3531
|
+
const displayPerDir = installedPerDir.map(
|
|
3532
|
+
(count, i) => config2.dirLabels[i].countMode === "single" ? Math.min(count, 1) : count
|
|
3533
|
+
);
|
|
3493
3534
|
let statusLineInstalled = false;
|
|
3494
3535
|
try {
|
|
3495
3536
|
const scriptContent = await fetchFileFromGitHub("claude-code/statusline.sh");
|
|
@@ -3535,7 +3576,7 @@ async function handleSetupClaudeCode(opts) {
|
|
|
3535
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") : "");
|
|
3536
3577
|
return {
|
|
3537
3578
|
success: true,
|
|
3538
|
-
message: buildSuccessMessage(config2,
|
|
3579
|
+
message: buildSuccessMessage(config2, displayPerDir, statusLineMessage + hooksMessage)
|
|
3539
3580
|
};
|
|
3540
3581
|
} catch (error) {
|
|
3541
3582
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
@@ -3550,6 +3591,10 @@ async function handleSetupCursor(opts) {
|
|
|
3550
3591
|
name: "Cursor",
|
|
3551
3592
|
sourceDirs: ["cursor/commands", "cursor/rules"],
|
|
3552
3593
|
targetDirs: [".cursor/commands", ".cursor/rules"],
|
|
3594
|
+
dirLabels: [
|
|
3595
|
+
{ label: "Commands", countMode: "files" },
|
|
3596
|
+
{ label: "Rules", countMode: "files" }
|
|
3597
|
+
],
|
|
3553
3598
|
injection: {
|
|
3554
3599
|
sourceFile: "cursor/AGENTS.md",
|
|
3555
3600
|
targetFile: "AGENTS.md"
|
|
@@ -3561,10 +3606,10 @@ async function handleSetupCursor(opts) {
|
|
|
3561
3606
|
if (!isSetupResult(setupResult)) {
|
|
3562
3607
|
return setupResult;
|
|
3563
3608
|
}
|
|
3564
|
-
const {
|
|
3609
|
+
const { installedPerDir } = setupResult.data;
|
|
3565
3610
|
return {
|
|
3566
3611
|
success: true,
|
|
3567
|
-
message: buildSuccessMessage(config2,
|
|
3612
|
+
message: buildSuccessMessage(config2, installedPerDir, "")
|
|
3568
3613
|
};
|
|
3569
3614
|
} catch (error) {
|
|
3570
3615
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
@@ -3729,6 +3774,7 @@ async function handleUpdate(opts) {
|
|
|
3729
3774
|
output += chalk9.green("\u2705 You are already on the latest version!\n");
|
|
3730
3775
|
console.log(output);
|
|
3731
3776
|
await copyBraingridReadme();
|
|
3777
|
+
await addBraingridTempToGitignore();
|
|
3732
3778
|
const setupOutput2 = await promptForIdeUpdates();
|
|
3733
3779
|
return {
|
|
3734
3780
|
success: true,
|
|
@@ -3768,6 +3814,7 @@ async function handleUpdate(opts) {
|
|
|
3768
3814
|
console.log(output);
|
|
3769
3815
|
executeUpdate(packageManager, PACKAGE_NAME);
|
|
3770
3816
|
await copyBraingridReadme();
|
|
3817
|
+
await addBraingridTempToGitignore();
|
|
3771
3818
|
const setupOutput = await promptForIdeUpdates();
|
|
3772
3819
|
return {
|
|
3773
3820
|
success: true,
|
|
@@ -3798,26 +3845,6 @@ async function fileExists2(filePath) {
|
|
|
3798
3845
|
return false;
|
|
3799
3846
|
}
|
|
3800
3847
|
}
|
|
3801
|
-
async function addBraingridTempToGitignore() {
|
|
3802
|
-
const gitRoot = await getGitRoot();
|
|
3803
|
-
if (!gitRoot) return;
|
|
3804
|
-
const gitignorePath = path6.join(gitRoot, ".gitignore");
|
|
3805
|
-
const entry = ".braingrid/temp/";
|
|
3806
|
-
let content = "";
|
|
3807
|
-
try {
|
|
3808
|
-
content = await readFile2(gitignorePath, "utf8");
|
|
3809
|
-
} catch {
|
|
3810
|
-
}
|
|
3811
|
-
const lines = content.split("\n");
|
|
3812
|
-
if (lines.some((line) => line.trim() === entry || line.trim() === ".braingrid/temp")) {
|
|
3813
|
-
return;
|
|
3814
|
-
}
|
|
3815
|
-
const addition = content.endsWith("\n") || content === "" ? "" : "\n";
|
|
3816
|
-
const newContent = `${content}${addition}# BrainGrid temp files
|
|
3817
|
-
${entry}
|
|
3818
|
-
`;
|
|
3819
|
-
await writeFile2(gitignorePath, newContent, "utf8");
|
|
3820
|
-
}
|
|
3821
3848
|
function getServices() {
|
|
3822
3849
|
const config2 = getConfig();
|
|
3823
3850
|
const auth = new BraingridAuth(config2.apiUrl);
|