@braingrid/cli 0.2.34 → 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 CHANGED
@@ -7,6 +7,23 @@ 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
+
10
27
  ## [0.2.34] - 2026-02-05
11
28
 
12
29
  ### 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.34" : "0.0.0-test";
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 {
@@ -2716,8 +2716,58 @@ async function updateClaudeSettings(settingsPath = ".claude/settings.json", scri
2716
2716
  } else {
2717
2717
  mergedPostToolUse = [...existingPostToolUse, ourHookEntry];
2718
2718
  }
2719
+ const ourCreateHookEntry = {
2720
+ matcher: "TaskCreate",
2721
+ hooks: [
2722
+ {
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"
2741
+ }
2742
+ ]
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
+ }
2719
2768
  settings.hooks = {
2720
2769
  ...existingHooks,
2770
+ PreToolUse: mergedPreToolUse,
2721
2771
  PostToolUse: mergedPostToolUse
2722
2772
  };
2723
2773
  const parentDir = path2.dirname(settingsPath);
@@ -3416,28 +3466,41 @@ async function _handleSetup(config2, opts) {
3416
3466
  );
3417
3467
  }
3418
3468
  }
3419
- let hookInstalled = false;
3469
+ let syncHookInstalled = false;
3470
+ let createHookInstalled = false;
3420
3471
  if (config2.name === "Claude Code") {
3421
3472
  try {
3422
- const hookContent = await fetchFileFromGitHub("claude-code/hooks/sync-braingrid-task.sh");
3423
- await installHookScript(hookContent);
3424
- hookInstalled = true;
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;
3425
3488
  } catch (error) {
3426
3489
  console.error(
3427
- chalk8.yellow("\u26A0\uFE0F Failed to install hook script:"),
3490
+ chalk8.yellow("\u26A0\uFE0F Failed to install create hook:"),
3428
3491
  error instanceof Error ? error.message : String(error)
3429
3492
  );
3430
3493
  }
3431
3494
  }
3432
3495
  await copyBraingridReadme();
3433
3496
  const statusLineMessage = statusLineInstalled ? chalk8.dim(" Status line: .claude/statusline.sh\n") : "";
3434
- const hookMessage = hookInstalled ? chalk8.dim(" Hook script: .claude/hooks/sync-braingrid-task.sh\n") : "";
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") : "");
3435
3498
  return {
3436
3499
  success: true,
3437
3500
  message: chalk8.green(`\u2705 ${config2.name} integration installed successfully!
3438
3501
 
3439
3502
  `) + chalk8.dim("Files installed:\n") + chalk8.dim(` Commands: ${result.installed} files
3440
- `) + statusLineMessage + hookMessage + chalk8.dim(` Content injected into: ${config2.injection.targetFile}
3503
+ `) + statusLineMessage + hooksMessage + chalk8.dim(` Content injected into: ${config2.injection.targetFile}
3441
3504
 
3442
3505
  `) + chalk8.dim("Next steps:\n") + chalk8.dim(" 1. Review the integration files\n") + chalk8.dim(` 2. Open ${config2.name}
3443
3506
  `) + chalk8.dim(" 3. Try the /specify or /breakdown commands\n") + chalk8.dim(" 4. Learn more: ") + chalk8.cyan(config2.docsUrl)