@braingrid/cli 0.2.34 → 0.2.36
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 +32 -0
- package/dist/cli.js +252 -133
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,38 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.2.36] - 2026-02-14
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **Add `.braingrid/temp/` to `.gitignore` during init**
|
|
15
|
+
- `braingrid init` now automatically appends `.braingrid/temp/` to the project's `.gitignore`
|
|
16
|
+
- Prevents session-specific temp files (e.g., acceptance criteria checklists) from being committed
|
|
17
|
+
- Skips if the entry already exists; creates `.gitignore` if none exists
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- **Always offer Claude Code setup updates during init**
|
|
22
|
+
- `braingrid init` now prompts to update Claude Code integration even when it's already installed
|
|
23
|
+
- Ensures users get the latest slash commands, skills, and status line configuration
|
|
24
|
+
|
|
25
|
+
## [0.2.35] - 2026-02-14
|
|
26
|
+
|
|
27
|
+
### Added
|
|
28
|
+
|
|
29
|
+
- **Acceptance criteria extraction in `/build` skill**
|
|
30
|
+
- `/build` now parses the `## Acceptance Criteria` section from requirement content
|
|
31
|
+
- Extracts all criteria into a flat `[]` checklist at `.braingrid/temp/REQ-{id}-acceptance-criteria.md`
|
|
32
|
+
- Handles both flat and sub-sectioned (`###` headings) criteria formats
|
|
33
|
+
- Strips markdown bold formatting and collapses multi-line criteria
|
|
34
|
+
- Added `Write` to allowed tools in build command
|
|
35
|
+
|
|
36
|
+
### Fixed
|
|
37
|
+
|
|
38
|
+
- **Add `create-braingrid-task.sh` to Claude Code sync script**
|
|
39
|
+
- The setup handler was fetching this hook from GitHub but the sync script never pushed it
|
|
40
|
+
- Without this fix, `braingrid setup claude-code` would fail to install the TaskCreate hook
|
|
41
|
+
|
|
10
42
|
## [0.2.34] - 2026-02-05
|
|
11
43
|
|
|
12
44
|
### Fixed
|
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.36" : "0.0.0-test";
|
|
226
226
|
var PRODUCTION_CONFIG = {
|
|
227
227
|
apiUrl: "https://app.braingrid.ai",
|
|
228
228
|
workosAuthUrl: "https://auth.braingrid.ai",
|
|
@@ -2198,7 +2198,8 @@ async function handleCompletion(shellArg, opts) {
|
|
|
2198
2198
|
}
|
|
2199
2199
|
|
|
2200
2200
|
// src/handlers/init.handlers.ts
|
|
2201
|
-
import { access as access3 } from "fs/promises";
|
|
2201
|
+
import { access as access3, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
|
|
2202
|
+
import path6 from "path";
|
|
2202
2203
|
import { confirm as confirm2, input, select as select3 } from "@inquirer/prompts";
|
|
2203
2204
|
import chalk10 from "chalk";
|
|
2204
2205
|
|
|
@@ -2543,17 +2544,17 @@ function parseGitHubError(error) {
|
|
|
2543
2544
|
}
|
|
2544
2545
|
return errorMessage;
|
|
2545
2546
|
}
|
|
2546
|
-
async function fetchFileFromGitHub(
|
|
2547
|
+
async function fetchFileFromGitHub(path7) {
|
|
2547
2548
|
return withRetry(async () => {
|
|
2548
2549
|
try {
|
|
2549
|
-
const command = `gh api repos/${GITHUB_OWNER}/${GITHUB_REPO}/contents/${
|
|
2550
|
+
const command = `gh api repos/${GITHUB_OWNER}/${GITHUB_REPO}/contents/${path7}`;
|
|
2550
2551
|
const { stdout } = await execAsync(command);
|
|
2551
2552
|
const response = JSON.parse(stdout);
|
|
2552
2553
|
if (response.type !== "file") {
|
|
2553
|
-
throw new Error(`Path ${
|
|
2554
|
+
throw new Error(`Path ${path7} is not a file`);
|
|
2554
2555
|
}
|
|
2555
2556
|
if (!response.content || !response.encoding) {
|
|
2556
|
-
throw new Error(`No content found for file ${
|
|
2557
|
+
throw new Error(`No content found for file ${path7}`);
|
|
2557
2558
|
}
|
|
2558
2559
|
if (response.encoding !== "base64") {
|
|
2559
2560
|
throw new Error(`Unexpected encoding: ${response.encoding}`);
|
|
@@ -2562,18 +2563,18 @@ async function fetchFileFromGitHub(path6) {
|
|
|
2562
2563
|
return content;
|
|
2563
2564
|
} catch (error) {
|
|
2564
2565
|
const parsedError = parseGitHubError(error);
|
|
2565
|
-
throw new Error(`Failed to fetch file ${
|
|
2566
|
+
throw new Error(`Failed to fetch file ${path7}: ${parsedError}`);
|
|
2566
2567
|
}
|
|
2567
2568
|
});
|
|
2568
2569
|
}
|
|
2569
|
-
async function listGitHubDirectory(
|
|
2570
|
+
async function listGitHubDirectory(path7) {
|
|
2570
2571
|
return withRetry(async () => {
|
|
2571
2572
|
try {
|
|
2572
|
-
const command = `gh api repos/${GITHUB_OWNER}/${GITHUB_REPO}/contents/${
|
|
2573
|
+
const command = `gh api repos/${GITHUB_OWNER}/${GITHUB_REPO}/contents/${path7}`;
|
|
2573
2574
|
const { stdout } = await execAsync(command);
|
|
2574
2575
|
const response = JSON.parse(stdout);
|
|
2575
2576
|
if (!Array.isArray(response)) {
|
|
2576
|
-
throw new Error(`Path ${
|
|
2577
|
+
throw new Error(`Path ${path7} is not a directory`);
|
|
2577
2578
|
}
|
|
2578
2579
|
return response.map((item) => ({
|
|
2579
2580
|
name: item.name,
|
|
@@ -2582,7 +2583,7 @@ async function listGitHubDirectory(path6) {
|
|
|
2582
2583
|
}));
|
|
2583
2584
|
} catch (error) {
|
|
2584
2585
|
const parsedError = parseGitHubError(error);
|
|
2585
|
-
throw new Error(`Failed to list directory ${
|
|
2586
|
+
throw new Error(`Failed to list directory ${path7}: ${parsedError}`);
|
|
2586
2587
|
}
|
|
2587
2588
|
});
|
|
2588
2589
|
}
|
|
@@ -2679,7 +2680,7 @@ async function copyBraingridReadme(targetPath = ".braingrid/README.md") {
|
|
|
2679
2680
|
return false;
|
|
2680
2681
|
}
|
|
2681
2682
|
}
|
|
2682
|
-
async function updateClaudeSettings(settingsPath = ".claude/settings.json", scriptPath2 = ".claude/statusline.sh", hookScriptPath = ".claude/hooks/sync-braingrid-task.sh") {
|
|
2683
|
+
async function updateClaudeSettings(settingsPath = ".claude/settings.json", scriptPath2 = ".claude/statusline.sh", hookScriptPath = ".claude/hooks/sync-braingrid-task.sh", createHookScriptPath = ".claude/hooks/create-braingrid-task.sh") {
|
|
2683
2684
|
try {
|
|
2684
2685
|
let settings = {};
|
|
2685
2686
|
try {
|
|
@@ -2716,8 +2717,58 @@ async function updateClaudeSettings(settingsPath = ".claude/settings.json", scri
|
|
|
2716
2717
|
} else {
|
|
2717
2718
|
mergedPostToolUse = [...existingPostToolUse, ourHookEntry];
|
|
2718
2719
|
}
|
|
2720
|
+
const ourCreateHookEntry = {
|
|
2721
|
+
matcher: "TaskCreate",
|
|
2722
|
+
hooks: [
|
|
2723
|
+
{
|
|
2724
|
+
type: "command",
|
|
2725
|
+
command: createHookScriptPath,
|
|
2726
|
+
timeout: 1e4
|
|
2727
|
+
}
|
|
2728
|
+
]
|
|
2729
|
+
};
|
|
2730
|
+
const taskCreatePostIdx = mergedPostToolUse.findIndex((e) => e.matcher === "TaskCreate");
|
|
2731
|
+
if (taskCreatePostIdx >= 0) {
|
|
2732
|
+
mergedPostToolUse[taskCreatePostIdx] = ourCreateHookEntry;
|
|
2733
|
+
} else {
|
|
2734
|
+
mergedPostToolUse = [...mergedPostToolUse, ourCreateHookEntry];
|
|
2735
|
+
}
|
|
2736
|
+
const ourPreToolUseEntry = {
|
|
2737
|
+
matcher: "TaskCreate",
|
|
2738
|
+
hooks: [
|
|
2739
|
+
{
|
|
2740
|
+
type: "prompt",
|
|
2741
|
+
prompt: "Use sequential task numbering with conventional commit naming for task subjects. The subject MUST follow the format 'TASK N: type: description' where N is the next sequential number (1, 2, 3, ...) based on existing tasks in the current session. Valid types: feat, fix, docs, style, refactor, perf, test, chore. Optional scope in parentheses after type is allowed. Examples: 'TASK 1: feat: add user login', 'TASK 2: fix: resolve null pointer', 'TASK 3: feat(auth): add OAuth support'. Reject if the subject does not follow this convention. $ARGUMENTS"
|
|
2742
|
+
}
|
|
2743
|
+
]
|
|
2744
|
+
};
|
|
2745
|
+
const existingPreToolUse = Array.isArray(existingHooks.PreToolUse) ? existingHooks.PreToolUse : [];
|
|
2746
|
+
const taskCreateIdx = existingPreToolUse.findIndex((e) => e.matcher === "TaskCreate");
|
|
2747
|
+
let mergedPreToolUse;
|
|
2748
|
+
if (taskCreateIdx >= 0) {
|
|
2749
|
+
mergedPreToolUse = [...existingPreToolUse];
|
|
2750
|
+
mergedPreToolUse[taskCreateIdx] = ourPreToolUseEntry;
|
|
2751
|
+
} else {
|
|
2752
|
+
mergedPreToolUse = [...existingPreToolUse, ourPreToolUseEntry];
|
|
2753
|
+
}
|
|
2754
|
+
const ourPreToolUseTaskUpdateEntry = {
|
|
2755
|
+
matcher: "TaskUpdate",
|
|
2756
|
+
hooks: [
|
|
2757
|
+
{
|
|
2758
|
+
type: "prompt",
|
|
2759
|
+
prompt: "Before completing a task, you MUST validate and commit. If status is being set to 'completed':\n\n1. Run the project's linter, test suite, and type checker (detect from project config \u2014 e.g. package.json, Makefile, pyproject.toml, Cargo.toml, etc.).\n2. If any check fails, DO NOT complete \u2014 fix first.\n3. Stage relevant files with git add (specific paths, not -A).\n4. Commit using the conventional commit part of the subject as the message (e.g. 'feat: add user login').\n5. Get the short hash: git rev-parse --short HEAD\n6. Update the task subject to: 'TASK N (HASH): type: description' (e.g. 'TASK 1 (abc1234): feat: add user login').\n7. Only then mark completed.\n\nFor other status changes, proceed normally.\n\n$ARGUMENTS"
|
|
2760
|
+
}
|
|
2761
|
+
]
|
|
2762
|
+
};
|
|
2763
|
+
const taskUpdatePreIdx = mergedPreToolUse.findIndex((e) => e.matcher === "TaskUpdate");
|
|
2764
|
+
if (taskUpdatePreIdx >= 0) {
|
|
2765
|
+
mergedPreToolUse[taskUpdatePreIdx] = ourPreToolUseTaskUpdateEntry;
|
|
2766
|
+
} else {
|
|
2767
|
+
mergedPreToolUse = [...mergedPreToolUse, ourPreToolUseTaskUpdateEntry];
|
|
2768
|
+
}
|
|
2719
2769
|
settings.hooks = {
|
|
2720
2770
|
...existingHooks,
|
|
2771
|
+
PreToolUse: mergedPreToolUse,
|
|
2721
2772
|
PostToolUse: mergedPostToolUse
|
|
2722
2773
|
};
|
|
2723
2774
|
const parentDir = path2.dirname(settingsPath);
|
|
@@ -3359,88 +3410,132 @@ async function installFiles(operations, force) {
|
|
|
3359
3410
|
return { installed, skipped, cancelled: false };
|
|
3360
3411
|
}
|
|
3361
3412
|
async function _handleSetup(config2, opts) {
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
console.log(chalk8.bold(`\u{1F680} Setting up ${config2.name} integration...
|
|
3413
|
+
const prerequisiteError = await checkPrerequisites();
|
|
3414
|
+
if (prerequisiteError) {
|
|
3415
|
+
return prerequisiteError;
|
|
3416
|
+
}
|
|
3417
|
+
console.log(chalk8.bold(`\u{1F680} Setting up ${config2.name} integration...
|
|
3368
3418
|
`));
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3419
|
+
const operations = await getFileList(config2.sourceDirs, config2.targetDirs);
|
|
3420
|
+
const injectionFileExists = await fileExists(config2.injection.targetFile);
|
|
3421
|
+
operations.push({
|
|
3422
|
+
type: "inject",
|
|
3423
|
+
sourcePath: config2.injection.sourceFile,
|
|
3424
|
+
targetPath: config2.injection.targetFile,
|
|
3425
|
+
exists: injectionFileExists
|
|
3426
|
+
});
|
|
3427
|
+
displayInstallationPlan(
|
|
3428
|
+
operations.filter((op) => op.type === "copy"),
|
|
3429
|
+
config2.injection.targetFile
|
|
3430
|
+
);
|
|
3431
|
+
if (opts.dryRun) {
|
|
3432
|
+
return {
|
|
3433
|
+
success: true,
|
|
3434
|
+
message: chalk8.green("\u2705 Dry-run complete. No files were modified.\n\n") + chalk8.dim(`Would install ${operations.length} files.`)
|
|
3435
|
+
};
|
|
3436
|
+
}
|
|
3437
|
+
const copyOps = operations.filter((op) => op.type === "copy");
|
|
3438
|
+
const result = await installFiles(copyOps, opts.force || false);
|
|
3439
|
+
if (result.cancelled) {
|
|
3440
|
+
return {
|
|
3441
|
+
success: false,
|
|
3442
|
+
message: chalk8.yellow("\u26A0\uFE0F Installation cancelled.\n\n") + chalk8.dim(`Installed: ${result.installed}, Skipped: ${result.skipped}`),
|
|
3443
|
+
code: "CANCELLED"
|
|
3444
|
+
};
|
|
3445
|
+
}
|
|
3446
|
+
try {
|
|
3447
|
+
const content = await fetchFileFromGitHub(config2.injection.sourceFile);
|
|
3448
|
+
await injectContentIntoFile(config2.injection.targetFile, content);
|
|
3449
|
+
} catch (error) {
|
|
3450
|
+
console.error(
|
|
3451
|
+
chalk8.red(`Failed to inject content into ${config2.injection.targetFile}:`),
|
|
3452
|
+
error instanceof Error ? error.message : String(error)
|
|
3380
3453
|
);
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3454
|
+
}
|
|
3455
|
+
await copyBraingridReadme();
|
|
3456
|
+
return {
|
|
3457
|
+
success: true,
|
|
3458
|
+
data: {
|
|
3459
|
+
installed: result.installed,
|
|
3460
|
+
skipped: result.skipped
|
|
3386
3461
|
}
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3462
|
+
};
|
|
3463
|
+
}
|
|
3464
|
+
function buildSuccessMessage(config2, installed, extras) {
|
|
3465
|
+
return chalk8.green(`\u2705 ${config2.name} integration installed successfully!
|
|
3466
|
+
|
|
3467
|
+
`) + chalk8.dim("Files installed:\n") + chalk8.dim(` Commands: ${installed} files
|
|
3468
|
+
`) + extras + chalk8.dim(` Content injected into: ${config2.injection.targetFile}
|
|
3469
|
+
|
|
3470
|
+
`) + chalk8.dim("Next steps:\n") + chalk8.dim(" 1. Review the integration files\n") + chalk8.dim(` 2. Open ${config2.name}
|
|
3471
|
+
`) + chalk8.dim(" 3. Try the /specify or /breakdown commands\n") + chalk8.dim(" 4. Learn more: ") + chalk8.cyan(config2.docsUrl);
|
|
3472
|
+
}
|
|
3473
|
+
function isSetupResult(result) {
|
|
3474
|
+
return "data" in result && result.success === true && !("message" in result);
|
|
3475
|
+
}
|
|
3476
|
+
async function handleSetupClaudeCode(opts) {
|
|
3477
|
+
const config2 = {
|
|
3478
|
+
name: "Claude Code",
|
|
3479
|
+
sourceDirs: ["claude-code/commands", "claude-code/skills/braingrid-cli"],
|
|
3480
|
+
targetDirs: [".claude/commands", ".claude/skills/braingrid-cli"],
|
|
3481
|
+
injection: {
|
|
3482
|
+
sourceFile: "claude-code/CLAUDE.md",
|
|
3483
|
+
targetFile: "CLAUDE.md"
|
|
3484
|
+
},
|
|
3485
|
+
docsUrl: "https://docs.braingrid.ai/claude-code"
|
|
3486
|
+
};
|
|
3487
|
+
try {
|
|
3488
|
+
const setupResult = await _handleSetup(config2, opts);
|
|
3489
|
+
if (!isSetupResult(setupResult)) {
|
|
3490
|
+
return setupResult;
|
|
3395
3491
|
}
|
|
3492
|
+
const { installed } = setupResult.data;
|
|
3493
|
+
let statusLineInstalled = false;
|
|
3396
3494
|
try {
|
|
3397
|
-
const
|
|
3398
|
-
await
|
|
3495
|
+
const scriptContent = await fetchFileFromGitHub("claude-code/statusline.sh");
|
|
3496
|
+
await installStatusLineScript(scriptContent);
|
|
3497
|
+
statusLineInstalled = true;
|
|
3399
3498
|
} catch (error) {
|
|
3400
3499
|
console.error(
|
|
3401
|
-
chalk8.
|
|
3500
|
+
chalk8.yellow("\u26A0\uFE0F Failed to install status line script:"),
|
|
3402
3501
|
error instanceof Error ? error.message : String(error)
|
|
3403
3502
|
);
|
|
3404
3503
|
}
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
} catch (error) {
|
|
3413
|
-
console.error(
|
|
3414
|
-
chalk8.yellow("\u26A0\uFE0F Failed to install status line script:"),
|
|
3415
|
-
error instanceof Error ? error.message : String(error)
|
|
3416
|
-
);
|
|
3417
|
-
}
|
|
3504
|
+
try {
|
|
3505
|
+
await updateClaudeSettings();
|
|
3506
|
+
} catch (error) {
|
|
3507
|
+
console.error(
|
|
3508
|
+
chalk8.yellow("\u26A0\uFE0F Failed to update Claude settings:"),
|
|
3509
|
+
error instanceof Error ? error.message : String(error)
|
|
3510
|
+
);
|
|
3418
3511
|
}
|
|
3419
|
-
let
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3512
|
+
let syncHookInstalled = false;
|
|
3513
|
+
try {
|
|
3514
|
+
const syncContent = await fetchFileFromGitHub("claude-code/hooks/sync-braingrid-task.sh");
|
|
3515
|
+
await installHookScript(syncContent);
|
|
3516
|
+
syncHookInstalled = true;
|
|
3517
|
+
} catch (error) {
|
|
3518
|
+
console.error(
|
|
3519
|
+
chalk8.yellow("\u26A0\uFE0F Failed to install sync hook:"),
|
|
3520
|
+
error instanceof Error ? error.message : String(error)
|
|
3521
|
+
);
|
|
3522
|
+
}
|
|
3523
|
+
let createHookInstalled = false;
|
|
3524
|
+
try {
|
|
3525
|
+
const createContent = await fetchFileFromGitHub("claude-code/hooks/create-braingrid-task.sh");
|
|
3526
|
+
await installHookScript(createContent, ".claude/hooks/create-braingrid-task.sh");
|
|
3527
|
+
createHookInstalled = true;
|
|
3528
|
+
} catch (error) {
|
|
3529
|
+
console.error(
|
|
3530
|
+
chalk8.yellow("\u26A0\uFE0F Failed to install create hook:"),
|
|
3531
|
+
error instanceof Error ? error.message : String(error)
|
|
3532
|
+
);
|
|
3431
3533
|
}
|
|
3432
|
-
await copyBraingridReadme();
|
|
3433
3534
|
const statusLineMessage = statusLineInstalled ? chalk8.dim(" Status line: .claude/statusline.sh\n") : "";
|
|
3434
|
-
const
|
|
3535
|
+
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") : "");
|
|
3435
3536
|
return {
|
|
3436
3537
|
success: true,
|
|
3437
|
-
message:
|
|
3438
|
-
|
|
3439
|
-
`) + chalk8.dim("Files installed:\n") + chalk8.dim(` Commands: ${result.installed} files
|
|
3440
|
-
`) + statusLineMessage + hookMessage + chalk8.dim(` Content injected into: ${config2.injection.targetFile}
|
|
3441
|
-
|
|
3442
|
-
`) + chalk8.dim("Next steps:\n") + chalk8.dim(" 1. Review the integration files\n") + chalk8.dim(` 2. Open ${config2.name}
|
|
3443
|
-
`) + chalk8.dim(" 3. Try the /specify or /breakdown commands\n") + chalk8.dim(" 4. Learn more: ") + chalk8.cyan(config2.docsUrl)
|
|
3538
|
+
message: buildSuccessMessage(config2, installed, statusLineMessage + hooksMessage)
|
|
3444
3539
|
};
|
|
3445
3540
|
} catch (error) {
|
|
3446
3541
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
@@ -3450,19 +3545,6 @@ async function _handleSetup(config2, opts) {
|
|
|
3450
3545
|
};
|
|
3451
3546
|
}
|
|
3452
3547
|
}
|
|
3453
|
-
async function handleSetupClaudeCode(opts) {
|
|
3454
|
-
const config2 = {
|
|
3455
|
-
name: "Claude Code",
|
|
3456
|
-
sourceDirs: ["claude-code/commands", "claude-code/skills/braingrid-cli"],
|
|
3457
|
-
targetDirs: [".claude/commands", ".claude/skills/braingrid-cli"],
|
|
3458
|
-
injection: {
|
|
3459
|
-
sourceFile: "claude-code/CLAUDE.md",
|
|
3460
|
-
targetFile: "CLAUDE.md"
|
|
3461
|
-
},
|
|
3462
|
-
docsUrl: "https://docs.braingrid.ai/claude-code"
|
|
3463
|
-
};
|
|
3464
|
-
return _handleSetup(config2, opts);
|
|
3465
|
-
}
|
|
3466
3548
|
async function handleSetupCursor(opts) {
|
|
3467
3549
|
const config2 = {
|
|
3468
3550
|
name: "Cursor",
|
|
@@ -3474,7 +3556,23 @@ async function handleSetupCursor(opts) {
|
|
|
3474
3556
|
},
|
|
3475
3557
|
docsUrl: "https://docs.braingrid.ai/cursor"
|
|
3476
3558
|
};
|
|
3477
|
-
|
|
3559
|
+
try {
|
|
3560
|
+
const setupResult = await _handleSetup(config2, opts);
|
|
3561
|
+
if (!isSetupResult(setupResult)) {
|
|
3562
|
+
return setupResult;
|
|
3563
|
+
}
|
|
3564
|
+
const { installed } = setupResult.data;
|
|
3565
|
+
return {
|
|
3566
|
+
success: true,
|
|
3567
|
+
message: buildSuccessMessage(config2, installed, "")
|
|
3568
|
+
};
|
|
3569
|
+
} catch (error) {
|
|
3570
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
3571
|
+
return {
|
|
3572
|
+
success: false,
|
|
3573
|
+
message: chalk8.red(`\u274C Setup failed: ${errorMessage}`)
|
|
3574
|
+
};
|
|
3575
|
+
}
|
|
3478
3576
|
}
|
|
3479
3577
|
|
|
3480
3578
|
// src/handlers/update.handlers.ts
|
|
@@ -3700,6 +3798,26 @@ async function fileExists2(filePath) {
|
|
|
3700
3798
|
return false;
|
|
3701
3799
|
}
|
|
3702
3800
|
}
|
|
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
|
+
}
|
|
3703
3821
|
function getServices() {
|
|
3704
3822
|
const config2 = getConfig();
|
|
3705
3823
|
const auth = new BraingridAuth(config2.apiUrl);
|
|
@@ -4227,6 +4345,7 @@ async function handleInit(opts) {
|
|
|
4227
4345
|
created_at: project2.created_at
|
|
4228
4346
|
};
|
|
4229
4347
|
await saveProjectConfig(localConfig);
|
|
4348
|
+
await addBraingridTempToGitignore();
|
|
4230
4349
|
await copyBraingridReadme();
|
|
4231
4350
|
console.log(
|
|
4232
4351
|
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"
|
|
@@ -4234,62 +4353,62 @@ async function handleInit(opts) {
|
|
|
4234
4353
|
const installedIDEs = await detectInstalledIDEs();
|
|
4235
4354
|
if (installedIDEs.claudeCode) {
|
|
4236
4355
|
const claudeSetupExists = await fileExists2(".claude/commands/specify.md");
|
|
4237
|
-
|
|
4356
|
+
console.log("");
|
|
4357
|
+
const setupClaude = await confirm2({
|
|
4358
|
+
message: claudeSetupExists ? "Claude Code detected. Update BrainGrid integration?" : "Claude Code detected. Install BrainGrid integration? (slash commands, skills, status line)",
|
|
4359
|
+
default: true
|
|
4360
|
+
});
|
|
4361
|
+
if (setupClaude) {
|
|
4238
4362
|
console.log("");
|
|
4239
|
-
|
|
4240
|
-
|
|
4241
|
-
|
|
4242
|
-
|
|
4243
|
-
|
|
4244
|
-
|
|
4245
|
-
|
|
4246
|
-
|
|
4247
|
-
if (result.success) {
|
|
4248
|
-
console.log(result.message);
|
|
4249
|
-
} else {
|
|
4250
|
-
console.log(chalk10.yellow("\u26A0\uFE0F Claude Code setup was not completed."));
|
|
4251
|
-
console.log(
|
|
4252
|
-
chalk10.dim("You can run ") + chalk10.cyan("braingrid setup claude-code") + chalk10.dim(" later.")
|
|
4253
|
-
);
|
|
4254
|
-
}
|
|
4255
|
-
} catch {
|
|
4256
|
-
console.log(chalk10.yellow("\u26A0\uFE0F Claude Code setup encountered an error."));
|
|
4363
|
+
try {
|
|
4364
|
+
const result = await handleSetupClaudeCode({
|
|
4365
|
+
force: claudeSetupExists
|
|
4366
|
+
});
|
|
4367
|
+
if (result.success) {
|
|
4368
|
+
console.log(result.message);
|
|
4369
|
+
} else {
|
|
4370
|
+
console.log(chalk10.yellow("\u26A0\uFE0F Claude Code setup was not completed."));
|
|
4257
4371
|
console.log(
|
|
4258
4372
|
chalk10.dim("You can run ") + chalk10.cyan("braingrid setup claude-code") + chalk10.dim(" later.")
|
|
4259
4373
|
);
|
|
4260
4374
|
}
|
|
4261
|
-
|
|
4375
|
+
} catch {
|
|
4376
|
+
console.log(chalk10.yellow("\u26A0\uFE0F Claude Code setup encountered an error."));
|
|
4377
|
+
console.log(
|
|
4378
|
+
chalk10.dim("You can run ") + chalk10.cyan("braingrid setup claude-code") + chalk10.dim(" later.")
|
|
4379
|
+
);
|
|
4262
4380
|
}
|
|
4381
|
+
console.log("");
|
|
4263
4382
|
}
|
|
4264
4383
|
}
|
|
4265
4384
|
if (installedIDEs.cursor) {
|
|
4266
4385
|
const cursorSetupExists = await fileExists2(".cursor/commands/specify.md");
|
|
4267
|
-
|
|
4386
|
+
console.log("");
|
|
4387
|
+
const setupCursor = await confirm2({
|
|
4388
|
+
message: cursorSetupExists ? "Cursor detected. Update BrainGrid integration?" : "Cursor detected. Install BrainGrid integration? (slash commands, rules, context)",
|
|
4389
|
+
default: true
|
|
4390
|
+
});
|
|
4391
|
+
if (setupCursor) {
|
|
4268
4392
|
console.log("");
|
|
4269
|
-
|
|
4270
|
-
|
|
4271
|
-
|
|
4272
|
-
|
|
4273
|
-
|
|
4274
|
-
|
|
4275
|
-
|
|
4276
|
-
|
|
4277
|
-
if (result.success) {
|
|
4278
|
-
console.log(result.message);
|
|
4279
|
-
} else {
|
|
4280
|
-
console.log(chalk10.yellow("\u26A0\uFE0F Cursor setup was not completed."));
|
|
4281
|
-
console.log(
|
|
4282
|
-
chalk10.dim("You can run ") + chalk10.cyan("braingrid setup cursor") + chalk10.dim(" later.")
|
|
4283
|
-
);
|
|
4284
|
-
}
|
|
4285
|
-
} catch {
|
|
4286
|
-
console.log(chalk10.yellow("\u26A0\uFE0F Cursor setup encountered an error."));
|
|
4393
|
+
try {
|
|
4394
|
+
const result = await handleSetupCursor({
|
|
4395
|
+
force: cursorSetupExists
|
|
4396
|
+
});
|
|
4397
|
+
if (result.success) {
|
|
4398
|
+
console.log(result.message);
|
|
4399
|
+
} else {
|
|
4400
|
+
console.log(chalk10.yellow("\u26A0\uFE0F Cursor setup was not completed."));
|
|
4287
4401
|
console.log(
|
|
4288
4402
|
chalk10.dim("You can run ") + chalk10.cyan("braingrid setup cursor") + chalk10.dim(" later.")
|
|
4289
4403
|
);
|
|
4290
4404
|
}
|
|
4291
|
-
|
|
4405
|
+
} catch {
|
|
4406
|
+
console.log(chalk10.yellow("\u26A0\uFE0F Cursor setup encountered an error."));
|
|
4407
|
+
console.log(
|
|
4408
|
+
chalk10.dim("You can run ") + chalk10.cyan("braingrid setup cursor") + chalk10.dim(" later.")
|
|
4409
|
+
);
|
|
4292
4410
|
}
|
|
4411
|
+
console.log("");
|
|
4293
4412
|
}
|
|
4294
4413
|
}
|
|
4295
4414
|
return {
|