@braingrid/cli 0.2.33 → 0.2.35
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 +28 -0
- package/dist/cli.js +100 -23
- package/dist/cli.js.map +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,34 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.2.35] - 2026-02-14
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **Acceptance criteria extraction in `/build` skill**
|
|
15
|
+
- `/build` now parses the `## Acceptance Criteria` section from requirement content
|
|
16
|
+
- Extracts all criteria into a flat `[]` checklist at `.braingrid/temp/REQ-{id}-acceptance-criteria.md`
|
|
17
|
+
- Handles both flat and sub-sectioned (`###` headings) criteria formats
|
|
18
|
+
- Strips markdown bold formatting and collapses multi-line criteria
|
|
19
|
+
- Added `Write` to allowed tools in build command
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
|
|
23
|
+
- **Add `create-braingrid-task.sh` to Claude Code sync script**
|
|
24
|
+
- The setup handler was fetching this hook from GitHub but the sync script never pushed it
|
|
25
|
+
- Without this fix, `braingrid setup claude-code` would fail to install the TaskCreate hook
|
|
26
|
+
|
|
27
|
+
## [0.2.34] - 2026-02-05
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
|
|
31
|
+
- **Deep merge `.claude/settings.json` during setup**
|
|
32
|
+
- `updateClaudeSettings()` now deep-merges statusLine and hooks instead of overwriting
|
|
33
|
+
- Preserves user customizations (extra pipes, additional hooks, other event types)
|
|
34
|
+
- Updates TaskUpdate hook in-place without duplicating entries
|
|
35
|
+
- Gracefully handles malformed hooks (non-object) and PostToolUse (non-array)
|
|
36
|
+
- Removed stale `| bunx cc-safety-net --statusline` pipe from default settings
|
|
37
|
+
|
|
10
38
|
## [0.2.33] - 2026-02-03
|
|
11
39
|
|
|
12
40
|
### Changed
|
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.35" : "0.0.0-test";
|
|
226
226
|
var PRODUCTION_CONFIG = {
|
|
227
227
|
apiUrl: "https://app.braingrid.ai",
|
|
228
228
|
workosAuthUrl: "https://auth.braingrid.ai",
|
|
@@ -2679,7 +2679,7 @@ async function copyBraingridReadme(targetPath = ".braingrid/README.md") {
|
|
|
2679
2679
|
return false;
|
|
2680
2680
|
}
|
|
2681
2681
|
}
|
|
2682
|
-
async function updateClaudeSettings(settingsPath = ".claude/settings.json", scriptPath2 = ".claude/statusline.sh", hookScriptPath = ".claude/hooks/sync-braingrid-task.sh") {
|
|
2682
|
+
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
2683
|
try {
|
|
2684
2684
|
let settings = {};
|
|
2685
2685
|
try {
|
|
@@ -2687,25 +2687,89 @@ async function updateClaudeSettings(settingsPath = ".claude/settings.json", scri
|
|
|
2687
2687
|
settings = JSON.parse(content2);
|
|
2688
2688
|
} catch {
|
|
2689
2689
|
}
|
|
2690
|
-
settings.statusLine
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2690
|
+
const existingStatusLine = settings.statusLine;
|
|
2691
|
+
if (existingStatusLine?.command?.includes(scriptPath2)) {
|
|
2692
|
+
} else {
|
|
2693
|
+
settings.statusLine = {
|
|
2694
|
+
type: "command",
|
|
2695
|
+
command: scriptPath2,
|
|
2696
|
+
padding: 0
|
|
2697
|
+
};
|
|
2698
|
+
}
|
|
2699
|
+
const ourHookEntry = {
|
|
2700
|
+
matcher: "TaskUpdate",
|
|
2701
|
+
hooks: [
|
|
2702
|
+
{
|
|
2703
|
+
type: "command",
|
|
2704
|
+
command: hookScriptPath,
|
|
2705
|
+
timeout: 1e4
|
|
2706
|
+
}
|
|
2707
|
+
]
|
|
2694
2708
|
};
|
|
2695
|
-
settings.hooks
|
|
2696
|
-
|
|
2709
|
+
const existingHooks = settings.hooks && typeof settings.hooks === "object" && !Array.isArray(settings.hooks) ? settings.hooks : {};
|
|
2710
|
+
const existingPostToolUse = Array.isArray(existingHooks.PostToolUse) ? existingHooks.PostToolUse : [];
|
|
2711
|
+
const taskUpdateIdx = existingPostToolUse.findIndex((e) => e.matcher === "TaskUpdate");
|
|
2712
|
+
let mergedPostToolUse;
|
|
2713
|
+
if (taskUpdateIdx >= 0) {
|
|
2714
|
+
mergedPostToolUse = [...existingPostToolUse];
|
|
2715
|
+
mergedPostToolUse[taskUpdateIdx] = ourHookEntry;
|
|
2716
|
+
} else {
|
|
2717
|
+
mergedPostToolUse = [...existingPostToolUse, ourHookEntry];
|
|
2718
|
+
}
|
|
2719
|
+
const ourCreateHookEntry = {
|
|
2720
|
+
matcher: "TaskCreate",
|
|
2721
|
+
hooks: [
|
|
2697
2722
|
{
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2723
|
+
type: "command",
|
|
2724
|
+
command: createHookScriptPath,
|
|
2725
|
+
timeout: 1e4
|
|
2726
|
+
}
|
|
2727
|
+
]
|
|
2728
|
+
};
|
|
2729
|
+
const taskCreatePostIdx = mergedPostToolUse.findIndex((e) => e.matcher === "TaskCreate");
|
|
2730
|
+
if (taskCreatePostIdx >= 0) {
|
|
2731
|
+
mergedPostToolUse[taskCreatePostIdx] = ourCreateHookEntry;
|
|
2732
|
+
} else {
|
|
2733
|
+
mergedPostToolUse = [...mergedPostToolUse, ourCreateHookEntry];
|
|
2734
|
+
}
|
|
2735
|
+
const ourPreToolUseEntry = {
|
|
2736
|
+
matcher: "TaskCreate",
|
|
2737
|
+
hooks: [
|
|
2738
|
+
{
|
|
2739
|
+
type: "prompt",
|
|
2740
|
+
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"
|
|
2706
2741
|
}
|
|
2707
2742
|
]
|
|
2708
2743
|
};
|
|
2744
|
+
const existingPreToolUse = Array.isArray(existingHooks.PreToolUse) ? existingHooks.PreToolUse : [];
|
|
2745
|
+
const taskCreateIdx = existingPreToolUse.findIndex((e) => e.matcher === "TaskCreate");
|
|
2746
|
+
let mergedPreToolUse;
|
|
2747
|
+
if (taskCreateIdx >= 0) {
|
|
2748
|
+
mergedPreToolUse = [...existingPreToolUse];
|
|
2749
|
+
mergedPreToolUse[taskCreateIdx] = ourPreToolUseEntry;
|
|
2750
|
+
} else {
|
|
2751
|
+
mergedPreToolUse = [...existingPreToolUse, ourPreToolUseEntry];
|
|
2752
|
+
}
|
|
2753
|
+
const ourPreToolUseTaskUpdateEntry = {
|
|
2754
|
+
matcher: "TaskUpdate",
|
|
2755
|
+
hooks: [
|
|
2756
|
+
{
|
|
2757
|
+
type: "prompt",
|
|
2758
|
+
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"
|
|
2759
|
+
}
|
|
2760
|
+
]
|
|
2761
|
+
};
|
|
2762
|
+
const taskUpdatePreIdx = mergedPreToolUse.findIndex((e) => e.matcher === "TaskUpdate");
|
|
2763
|
+
if (taskUpdatePreIdx >= 0) {
|
|
2764
|
+
mergedPreToolUse[taskUpdatePreIdx] = ourPreToolUseTaskUpdateEntry;
|
|
2765
|
+
} else {
|
|
2766
|
+
mergedPreToolUse = [...mergedPreToolUse, ourPreToolUseTaskUpdateEntry];
|
|
2767
|
+
}
|
|
2768
|
+
settings.hooks = {
|
|
2769
|
+
...existingHooks,
|
|
2770
|
+
PreToolUse: mergedPreToolUse,
|
|
2771
|
+
PostToolUse: mergedPostToolUse
|
|
2772
|
+
};
|
|
2709
2773
|
const parentDir = path2.dirname(settingsPath);
|
|
2710
2774
|
await fs2.mkdir(parentDir, { recursive: true });
|
|
2711
2775
|
const content = JSON.stringify(settings, null, " ");
|
|
@@ -3402,28 +3466,41 @@ async function _handleSetup(config2, opts) {
|
|
|
3402
3466
|
);
|
|
3403
3467
|
}
|
|
3404
3468
|
}
|
|
3405
|
-
let
|
|
3469
|
+
let syncHookInstalled = false;
|
|
3470
|
+
let createHookInstalled = false;
|
|
3406
3471
|
if (config2.name === "Claude Code") {
|
|
3407
3472
|
try {
|
|
3408
|
-
const
|
|
3409
|
-
await installHookScript(
|
|
3410
|
-
|
|
3473
|
+
const syncContent = await fetchFileFromGitHub("claude-code/hooks/sync-braingrid-task.sh");
|
|
3474
|
+
await installHookScript(syncContent);
|
|
3475
|
+
syncHookInstalled = true;
|
|
3476
|
+
} catch (error) {
|
|
3477
|
+
console.error(
|
|
3478
|
+
chalk8.yellow("\u26A0\uFE0F Failed to install sync hook:"),
|
|
3479
|
+
error instanceof Error ? error.message : String(error)
|
|
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;
|
|
3411
3488
|
} catch (error) {
|
|
3412
3489
|
console.error(
|
|
3413
|
-
chalk8.yellow("\u26A0\uFE0F Failed to install hook
|
|
3490
|
+
chalk8.yellow("\u26A0\uFE0F Failed to install create hook:"),
|
|
3414
3491
|
error instanceof Error ? error.message : String(error)
|
|
3415
3492
|
);
|
|
3416
3493
|
}
|
|
3417
3494
|
}
|
|
3418
3495
|
await copyBraingridReadme();
|
|
3419
3496
|
const statusLineMessage = statusLineInstalled ? chalk8.dim(" Status line: .claude/statusline.sh\n") : "";
|
|
3420
|
-
const
|
|
3497
|
+
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") : "");
|
|
3421
3498
|
return {
|
|
3422
3499
|
success: true,
|
|
3423
3500
|
message: chalk8.green(`\u2705 ${config2.name} integration installed successfully!
|
|
3424
3501
|
|
|
3425
3502
|
`) + chalk8.dim("Files installed:\n") + chalk8.dim(` Commands: ${result.installed} files
|
|
3426
|
-
`) + statusLineMessage +
|
|
3503
|
+
`) + statusLineMessage + hooksMessage + chalk8.dim(` Content injected into: ${config2.injection.targetFile}
|
|
3427
3504
|
|
|
3428
3505
|
`) + chalk8.dim("Next steps:\n") + chalk8.dim(" 1. Review the integration files\n") + chalk8.dim(` 2. Open ${config2.name}
|
|
3429
3506
|
`) + chalk8.dim(" 3. Try the /specify or /breakdown commands\n") + chalk8.dim(" 4. Learn more: ") + chalk8.cyan(config2.docsUrl)
|