brainfile 0.19.0 → 0.20.0

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.
Files changed (82) hide show
  1. package/README.md +18 -24
  2. package/dist/__tests__/helpers/mcp-wire.d.ts +45 -0
  3. package/dist/__tests__/helpers/mcp-wire.d.ts.map +1 -0
  4. package/dist/__tests__/helpers/mcp-wire.js +133 -0
  5. package/dist/__tests__/helpers/mcp-wire.js.map +1 -0
  6. package/dist/{chunk-YTFLC673.mjs → chunk-UOCQBEEW.mjs} +1486 -992
  7. package/dist/cli.mjs +916 -776
  8. package/dist/commands/adr.d.ts +1 -9
  9. package/dist/commands/adr.d.ts.map +1 -1
  10. package/dist/commands/adr.js +11 -88
  11. package/dist/commands/adr.js.map +1 -1
  12. package/dist/commands/archive.d.ts +2 -5
  13. package/dist/commands/archive.d.ts.map +1 -1
  14. package/dist/commands/archive.js +43 -11
  15. package/dist/commands/archive.js.map +1 -1
  16. package/dist/commands/brief.d.ts +31 -0
  17. package/dist/commands/brief.d.ts.map +1 -0
  18. package/dist/commands/brief.js +151 -0
  19. package/dist/commands/brief.js.map +1 -0
  20. package/dist/commands/complete.js +1 -1
  21. package/dist/commands/complete.js.map +1 -1
  22. package/dist/commands/log.js +31 -18
  23. package/dist/commands/log.js.map +1 -1
  24. package/dist/commands/mcp.d.ts +9 -0
  25. package/dist/commands/mcp.d.ts.map +1 -1
  26. package/dist/commands/mcp.js +28 -17
  27. package/dist/commands/mcp.js.map +1 -1
  28. package/dist/commands/migrate.js +10 -12
  29. package/dist/commands/migrate.js.map +1 -1
  30. package/dist/mcp/schemas.d.ts +297 -0
  31. package/dist/mcp/schemas.d.ts.map +1 -0
  32. package/dist/mcp/schemas.js +328 -0
  33. package/dist/mcp/schemas.js.map +1 -0
  34. package/dist/mcp/tools/brief_tool.d.ts +14 -0
  35. package/dist/mcp/tools/brief_tool.d.ts.map +1 -0
  36. package/dist/mcp/tools/brief_tool.js +58 -0
  37. package/dist/mcp/tools/brief_tool.js.map +1 -0
  38. package/dist/mcp/tools/contract_tool.d.ts.map +1 -1
  39. package/dist/mcp/tools/contract_tool.js +37 -13
  40. package/dist/mcp/tools/contract_tool.js.map +1 -1
  41. package/dist/mcp/tools/get_task_tool.d.ts.map +1 -1
  42. package/dist/mcp/tools/get_task_tool.js +7 -2
  43. package/dist/mcp/tools/get_task_tool.js.map +1 -1
  44. package/dist/mcp/tools/list_tasks_tool.d.ts.map +1 -1
  45. package/dist/mcp/tools/list_tasks_tool.js +9 -3
  46. package/dist/mcp/tools/list_tasks_tool.js.map +1 -1
  47. package/dist/mcp/tools/search_tool.d.ts.map +1 -1
  48. package/dist/mcp/tools/search_tool.js +17 -4
  49. package/dist/mcp/tools/search_tool.js.map +1 -1
  50. package/dist/mcp/tools/subtask_tool.d.ts.map +1 -1
  51. package/dist/mcp/tools/subtask_tool.js +47 -13
  52. package/dist/mcp/tools/subtask_tool.js.map +1 -1
  53. package/dist/mcp/tools/task_add_tool.d.ts.map +1 -1
  54. package/dist/mcp/tools/task_add_tool.js +7 -2
  55. package/dist/mcp/tools/task_add_tool.js.map +1 -1
  56. package/dist/mcp/tools/task_complete_tool.d.ts.map +1 -1
  57. package/dist/mcp/tools/task_complete_tool.js +34 -6
  58. package/dist/mcp/tools/task_complete_tool.js.map +1 -1
  59. package/dist/mcp/tools/task_delete_tool.d.ts.map +1 -1
  60. package/dist/mcp/tools/task_delete_tool.js +7 -2
  61. package/dist/mcp/tools/task_delete_tool.js.map +1 -1
  62. package/dist/mcp/tools/task_move_tool.d.ts.map +1 -1
  63. package/dist/mcp/tools/task_move_tool.js +15 -6
  64. package/dist/mcp/tools/task_move_tool.js.map +1 -1
  65. package/dist/mcp/tools/task_patch_tool.d.ts.map +1 -1
  66. package/dist/mcp/tools/task_patch_tool.js +18 -6
  67. package/dist/mcp/tools/task_patch_tool.js.map +1 -1
  68. package/dist/schemas/board.json +1 -1
  69. package/dist/tui/theme.d.ts +98 -0
  70. package/dist/tui/theme.d.ts.map +1 -0
  71. package/dist/tui/theme.js +141 -0
  72. package/dist/tui/theme.js.map +1 -0
  73. package/dist/{tui-P56H4X7N.mjs → tui-H3RI22S4.mjs} +1593 -1934
  74. package/dist/utils/cli-error.d.ts +20 -0
  75. package/dist/utils/cli-error.d.ts.map +1 -1
  76. package/dist/utils/cli-error.js +36 -0
  77. package/dist/utils/cli-error.js.map +1 -1
  78. package/package.json +3 -3
  79. package/dist/commands/rules.d.ts +0 -68
  80. package/dist/commands/rules.d.ts.map +0 -1
  81. package/dist/commands/rules.js +0 -322
  82. package/dist/commands/rules.js.map +0 -1
package/dist/cli.mjs CHANGED
@@ -8,6 +8,7 @@ const __dirname = __brainfileDirname(__filename);
8
8
  import {
9
9
  CLIError,
10
10
  ExitCode,
11
+ GLYPHS,
11
12
  V1_UNSUPPORTED_MESSAGE,
12
13
  assertV2Brainfile,
13
14
  authStore,
@@ -40,6 +41,7 @@ import {
40
41
  parseError,
41
42
  parseFailure,
42
43
  probeWorkspaceFormat,
44
+ renderCliError,
43
45
  require_dist,
44
46
  require_source,
45
47
  resolveCliBrainfilePath,
@@ -52,7 +54,7 @@ import {
52
54
  validationError,
53
55
  validationError2,
54
56
  workspaceRootFromBrainfilePath
55
- } from "./chunk-YTFLC673.mjs";
57
+ } from "./chunk-UOCQBEEW.mjs";
56
58
  import {
57
59
  __commonJS,
58
60
  __export,
@@ -1223,7 +1225,7 @@ var require_command = __commonJS({
1223
1225
  "../node_modules/commander/lib/command.js"(exports) {
1224
1226
  var EventEmitter = __require("node:events").EventEmitter;
1225
1227
  var childProcess = __require("node:child_process");
1226
- var path20 = __require("node:path");
1228
+ var path21 = __require("node:path");
1227
1229
  var fs21 = __require("node:fs");
1228
1230
  var process4 = __require("node:process");
1229
1231
  var { Argument: Argument2, humanReadableArgName } = require_argument();
@@ -2236,9 +2238,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
2236
2238
  let launchWithNode = false;
2237
2239
  const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
2238
2240
  function findFile(baseDir, baseName) {
2239
- const localBin = path20.resolve(baseDir, baseName);
2241
+ const localBin = path21.resolve(baseDir, baseName);
2240
2242
  if (fs21.existsSync(localBin)) return localBin;
2241
- if (sourceExt.includes(path20.extname(baseName))) return void 0;
2243
+ if (sourceExt.includes(path21.extname(baseName))) return void 0;
2242
2244
  const foundExt = sourceExt.find(
2243
2245
  (ext) => fs21.existsSync(`${localBin}${ext}`)
2244
2246
  );
@@ -2256,17 +2258,17 @@ Expecting one of '${allowedValues.join("', '")}'`);
2256
2258
  } catch {
2257
2259
  resolvedScriptPath = this._scriptPath;
2258
2260
  }
2259
- executableDir = path20.resolve(
2260
- path20.dirname(resolvedScriptPath),
2261
+ executableDir = path21.resolve(
2262
+ path21.dirname(resolvedScriptPath),
2261
2263
  executableDir
2262
2264
  );
2263
2265
  }
2264
2266
  if (executableDir) {
2265
2267
  let localFile = findFile(executableDir, executableFile);
2266
2268
  if (!localFile && !subcommand._executableFile && this._scriptPath) {
2267
- const legacyName = path20.basename(
2269
+ const legacyName = path21.basename(
2268
2270
  this._scriptPath,
2269
- path20.extname(this._scriptPath)
2271
+ path21.extname(this._scriptPath)
2270
2272
  );
2271
2273
  if (legacyName !== this._name) {
2272
2274
  localFile = findFile(
@@ -2277,7 +2279,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
2277
2279
  }
2278
2280
  executableFile = localFile || executableFile;
2279
2281
  }
2280
- launchWithNode = sourceExt.includes(path20.extname(executableFile));
2282
+ launchWithNode = sourceExt.includes(path21.extname(executableFile));
2281
2283
  let proc;
2282
2284
  if (process4.platform !== "win32") {
2283
2285
  if (launchWithNode) {
@@ -3192,7 +3194,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
3192
3194
  * @return {Command}
3193
3195
  */
3194
3196
  nameFromFilename(filename) {
3195
- this._name = path20.basename(filename, path20.extname(filename));
3197
+ this._name = path21.basename(filename, path21.extname(filename));
3196
3198
  return this;
3197
3199
  }
3198
3200
  /**
@@ -3206,9 +3208,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
3206
3208
  * @param {string} [path]
3207
3209
  * @return {(string|null|Command)}
3208
3210
  */
3209
- executableDir(path21) {
3210
- if (path21 === void 0) return this._executableDir;
3211
- this._executableDir = path21;
3211
+ executableDir(path22) {
3212
+ if (path22 === void 0) return this._executableDir;
3213
+ this._executableDir = path22;
3212
3214
  return this;
3213
3215
  }
3214
3216
  /**
@@ -3491,7 +3493,7 @@ var require_package = __commonJS({
3491
3493
  "package.json"(exports, module) {
3492
3494
  module.exports = {
3493
3495
  name: "brainfile",
3494
- version: "0.19.0",
3496
+ version: "0.20.0",
3495
3497
  description: "Command-line interface for Brainfile task management",
3496
3498
  main: "dist/index.js",
3497
3499
  types: "dist/index.d.ts",
@@ -3528,7 +3530,7 @@ var require_package = __commonJS({
3528
3530
  access: "public"
3529
3531
  },
3530
3532
  dependencies: {
3531
- "@brainfile/core": "^0.19.0",
3533
+ "@brainfile/core": "^0.20.0",
3532
3534
  chalk: "^4.1.2",
3533
3535
  chokidar: "^4.0.3",
3534
3536
  commander: "^14.0.3",
@@ -3563,7 +3565,7 @@ var require_package = __commonJS({
3563
3565
  "dist"
3564
3566
  ],
3565
3567
  engines: {
3566
- node: ">=22"
3568
+ node: ">=20"
3567
3569
  }
3568
3570
  };
3569
3571
  }
@@ -3587,8 +3589,8 @@ var {
3587
3589
  } = import_index.default;
3588
3590
 
3589
3591
  // src/cli.ts
3590
- import { readFileSync as readFileSync10, existsSync as existsSync16 } from "fs";
3591
- import { join as join14, sep as sep2 } from "path";
3592
+ import { readFileSync as readFileSync9, existsSync as existsSync16 } from "fs";
3593
+ import { join as join15, sep as sep2 } from "path";
3592
3594
 
3593
3595
  // src/commands/list.ts
3594
3596
  var import_chalk = __toESM(require_source());
@@ -4648,6 +4650,7 @@ function migrateBrainfileToV2(brainfilePath, options) {
4648
4650
  ...col.completionColumn && { completionColumn: col.completionColumn }
4649
4651
  }));
4650
4652
  delete configBoard.archive;
4653
+ delete configBoard.rules;
4651
4654
  configBoard.schema = "https://brainfile.md/v2/board.json";
4652
4655
  const configContent = import_core10.Brainfile.serialize(configBoard);
4653
4656
  fs5.writeFileSync(brainfilePath, configContent, "utf-8");
@@ -4705,10 +4708,6 @@ function migrateLogsToLedger(options) {
4705
4708
  const taskId = doc.task.id;
4706
4709
  if (existingLedgerIds.has(taskId)) {
4707
4710
  console.log(import_chalk6.default.gray(` Skip: ${taskId} (already in ledger)`));
4708
- if (options.force) {
4709
- fs5.rmSync(doc.filePath, { force: true });
4710
- console.log(import_chalk6.default.gray(` Removed stale log file: ${path7.basename(doc.filePath)}`));
4711
- }
4712
4711
  skippedCount++;
4713
4712
  continue;
4714
4713
  }
@@ -4729,22 +4728,17 @@ function migrateLogsToLedger(options) {
4729
4728
  console.log(import_chalk6.default.yellow(` Conflict: board/${taskId} \u2192 ${newId} (completed task takes precedence)`));
4730
4729
  conflictCount++;
4731
4730
  }
4732
- fs5.rmSync(doc.filePath, { force: true });
4733
- console.log(import_chalk6.default.gray(` Migrated: ${taskId}`));
4731
+ console.log(import_chalk6.default.gray(` Backfilled: ${taskId}`));
4734
4732
  }
4735
4733
  console.log("");
4736
- console.log(import_chalk6.default.green(`Logs-to-ledger migration complete.`));
4737
- console.log(import_chalk6.default.gray(` Migrated to ledger: ${migratedCount}`));
4734
+ console.log(import_chalk6.default.green(`Ledger backfill complete (markdown archives kept).`));
4735
+ console.log(import_chalk6.default.gray(` Backfilled to ledger: ${migratedCount}`));
4738
4736
  if (skippedCount > 0) {
4739
4737
  console.log(import_chalk6.default.gray(` Skipped (in ledger): ${skippedCount}`));
4740
4738
  }
4741
4739
  if (conflictCount > 0) {
4742
4740
  console.log(import_chalk6.default.yellow(` ID conflicts fixed: ${conflictCount} board task(s) renamed`));
4743
4741
  }
4744
- if (skippedCount > 0 && !options.force) {
4745
- console.log("");
4746
- console.log(import_chalk6.default.gray("Tip: Use --force to also remove stale .md files that are already in the ledger."));
4747
- }
4748
4742
  }
4749
4743
  function backupAndRemoveLegacyRoot(rootBrainfilePath, dotDir) {
4750
4744
  const backupPath = uniquePath(path7.join(dotDir, "brainfile.root.legacy.bak"));
@@ -4928,7 +4922,6 @@ var import_core14 = __toESM(require_dist());
4928
4922
  var import_chalk9 = __toESM(require_source());
4929
4923
  import * as fs7 from "fs";
4930
4924
  import * as path10 from "path";
4931
- var import_core15 = __toESM(require_dist());
4932
4925
  async function archiveCommand(options) {
4933
4926
  try {
4934
4927
  if (!options.task && !options.all) {
@@ -4946,7 +4939,14 @@ async function archiveCommand(options) {
4946
4939
  }
4947
4940
  const destination = parsedDest.type;
4948
4941
  if (destination === "local") {
4949
- operationError('Local archive is not supported for v2 boards. Use "brainfile complete" to move tasks to logs/.');
4942
+ if (options.all) {
4943
+ console.log(import_chalk9.default.yellow("Note:") + " --all with local destination has no effect (tasks complete into logs/ via brainfile complete).");
4944
+ return;
4945
+ }
4946
+ if (options.task) {
4947
+ await completeLocalV2(filePath, options.task, options.dryRun);
4948
+ }
4949
+ return;
4950
4950
  }
4951
4951
  if (destination === "github" && !await isGitHubAuthenticated()) {
4952
4952
  console.log(import_chalk9.default.red("\u2717") + " Not authenticated with GitHub.");
@@ -4971,9 +4971,38 @@ async function archiveCommand(options) {
4971
4971
  handleError(error48);
4972
4972
  }
4973
4973
  }
4974
+ function completeLocalV2(filePath, taskId, dryRun) {
4975
+ const dirs = (0, import_core.getV2Dirs)(filePath);
4976
+ const onBoard = (0, import_core.findV2Task)(dirs, taskId, false);
4977
+ if (onBoard && !onBoard.isLog) {
4978
+ if (dryRun) {
4979
+ console.log(import_chalk9.default.yellow("DRY RUN") + " - No changes will be made");
4980
+ console.log("");
4981
+ console.log(`Would complete ${import_chalk9.default.cyan(taskId)} (ledger + logs/${taskId}.md)`);
4982
+ return;
4983
+ }
4984
+ const result = (0, import_core14.completeTaskFile)(onBoard.filePath, dirs.logsDir);
4985
+ if (!result.success) {
4986
+ operationError(result.error || `Failed to complete task: ${taskId}`);
4987
+ return;
4988
+ }
4989
+ console.log(import_chalk9.default.green("Task completed!"));
4990
+ console.log("");
4991
+ console.log(import_chalk9.default.gray(` Task: ${taskId}`));
4992
+ console.log(import_chalk9.default.gray(` Moved to: logs/${taskId}.md`));
4993
+ return;
4994
+ }
4995
+ const inLogs = (0, import_core.findV2Task)(dirs, taskId, true);
4996
+ if (inLogs?.isLog) {
4997
+ console.log(import_chalk9.default.yellow("Already completed.") + ` ${taskId} is in logs/.`);
4998
+ console.log("Export it with: " + import_chalk9.default.cyan(`brainfile archive --task ${taskId} --to github|linear`));
4999
+ return;
5000
+ }
5001
+ operationError(`Task not found: ${taskId}`);
5002
+ }
4974
5003
  async function archiveSingleFromLogsV2(filePath, board, taskId, destination, dryRun, parsedDest) {
4975
5004
  const dirs = (0, import_core.getV2Dirs)(filePath);
4976
- const logPath = path10.join(dirs.logsDir, (0, import_core15.taskFileName)(taskId));
5005
+ const logPath = path10.join(dirs.logsDir, (0, import_core14.taskFileName)(taskId));
4977
5006
  if (!fs7.existsSync(logPath)) {
4978
5007
  operationError(`Task not found in logs: ${taskId}`);
4979
5008
  return;
@@ -5004,7 +5033,7 @@ async function archiveAllFromLogsV2(filePath, board, destination, dryRun) {
5004
5033
  return;
5005
5034
  }
5006
5035
  const dirs = (0, import_core.getV2Dirs)(filePath);
5007
- const logDocs = (0, import_core15.readTasksDir)(dirs.logsDir);
5036
+ const logDocs = (0, import_core14.readTasksDir)(dirs.logsDir);
5008
5037
  if (logDocs.length === 0) {
5009
5038
  console.log("No completed tasks in logs/ to export.");
5010
5039
  return;
@@ -5044,7 +5073,7 @@ async function archiveAllFromLogsV2(filePath, board, destination, dryRun) {
5044
5073
  state: "closed"
5045
5074
  });
5046
5075
  if (result.success) {
5047
- const logPath = path10.join(dirs.logsDir, (0, import_core15.taskFileName)(task.id));
5076
+ const logPath = path10.join(dirs.logsDir, (0, import_core14.taskFileName)(task.id));
5048
5077
  fs7.unlinkSync(logPath);
5049
5078
  console.log(import_chalk9.default.green(" OK") + ` #${result.issueNumber}`);
5050
5079
  successCount++;
@@ -5073,7 +5102,7 @@ async function archiveAllFromLogsV2(filePath, board, destination, dryRun) {
5073
5102
  stateName: "Done"
5074
5103
  });
5075
5104
  if (result.success) {
5076
- const logPath = path10.join(dirs.logsDir, (0, import_core15.taskFileName)(task.id));
5105
+ const logPath = path10.join(dirs.logsDir, (0, import_core14.taskFileName)(task.id));
5077
5106
  fs7.unlinkSync(logPath);
5078
5107
  console.log(import_chalk9.default.green(" OK") + ` ${result.issueId}`);
5079
5108
  successCount++;
@@ -5234,13 +5263,13 @@ async function archiveToLinear(board, task, columnTitle, dryRun, parsedDest) {
5234
5263
  }
5235
5264
 
5236
5265
  // src/commands/restore.ts
5237
- var import_core17 = __toESM(require_dist());
5266
+ var import_core16 = __toESM(require_dist());
5238
5267
  var import_chalk10 = __toESM(require_source());
5239
5268
  import * as fs9 from "fs";
5240
5269
  import * as path12 from "path";
5241
5270
 
5242
5271
  // src/utils/archive.ts
5243
- var import_core16 = __toESM(require_dist());
5272
+ var import_core15 = __toESM(require_dist());
5244
5273
  import * as fs8 from "fs";
5245
5274
  import * as path11 from "path";
5246
5275
  function getArchivePath(filePath) {
@@ -5256,7 +5285,7 @@ function loadArchivedTasks(filePath) {
5256
5285
  }
5257
5286
  try {
5258
5287
  const archiveContent = fs8.readFileSync(archivePath, "utf-8");
5259
- const parseResult = import_core16.Brainfile.parseWithErrors(archiveContent);
5288
+ const parseResult = import_core15.Brainfile.parseWithErrors(archiveContent);
5260
5289
  if (parseResult.board && Array.isArray(parseResult.board.archive)) {
5261
5290
  return { tasks: parseResult.board.archive, archivePath };
5262
5291
  }
@@ -5277,7 +5306,7 @@ function restoreFromArchive(filePath, taskId, toColumnId) {
5277
5306
  let archiveBoard;
5278
5307
  try {
5279
5308
  const archiveContent = fs8.readFileSync(archivePath, "utf-8");
5280
- const parseResult = import_core16.Brainfile.parseWithErrors(archiveContent);
5309
+ const parseResult = import_core15.Brainfile.parseWithErrors(archiveContent);
5281
5310
  if (!parseResult.board) {
5282
5311
  return { success: false, error: "Failed to parse archive file" };
5283
5312
  }
@@ -5295,7 +5324,7 @@ function restoreFromArchive(filePath, taskId, toColumnId) {
5295
5324
  let board;
5296
5325
  try {
5297
5326
  const mainContent = fs8.readFileSync(filePath, "utf-8");
5298
- const parseResult = import_core16.Brainfile.parseWithErrors(mainContent);
5327
+ const parseResult = import_core15.Brainfile.parseWithErrors(mainContent);
5299
5328
  if (!parseResult.board) {
5300
5329
  return { success: false, error: "Failed to parse main brainfile" };
5301
5330
  }
@@ -5313,9 +5342,9 @@ function restoreFromArchive(filePath, taskId, toColumnId) {
5313
5342
  targetColumn.tasks.push(task);
5314
5343
  archiveBoard.archive = archiveBoard.archive.filter((t) => t.id !== taskId);
5315
5344
  try {
5316
- const mainContent = import_core16.Brainfile.serialize(board);
5345
+ const mainContent = import_core15.Brainfile.serialize(board);
5317
5346
  fs8.writeFileSync(filePath, mainContent, "utf-8");
5318
- const archiveContent = import_core16.Brainfile.serialize(archiveBoard);
5347
+ const archiveContent = import_core15.Brainfile.serialize(archiveBoard);
5319
5348
  fs8.writeFileSync(archivePath, archiveContent, "utf-8");
5320
5349
  } catch (err) {
5321
5350
  return {
@@ -5340,7 +5369,7 @@ function restoreCommand(options) {
5340
5369
  fileNotFoundError(filePath);
5341
5370
  }
5342
5371
  const content = fs9.readFileSync(filePath, "utf-8");
5343
- const result = import_core17.Brainfile.parseWithErrors(content);
5372
+ const result = import_core16.Brainfile.parseWithErrors(content);
5344
5373
  if (!result.board) {
5345
5374
  parseError(result.error);
5346
5375
  }
@@ -5362,9 +5391,9 @@ Archived tasks in ${path12.basename(archivePath)}:`));
5362
5391
  });
5363
5392
  process.exit(1);
5364
5393
  }
5365
- let targetColumn = (0, import_core17.findColumnById)(board, options.column);
5394
+ let targetColumn = (0, import_core16.findColumnById)(board, options.column);
5366
5395
  if (!targetColumn) {
5367
- targetColumn = (0, import_core17.findColumnByName)(board, options.column);
5396
+ targetColumn = (0, import_core16.findColumnByName)(board, options.column);
5368
5397
  }
5369
5398
  if (!targetColumn) {
5370
5399
  columnNotFoundError(options.column, board);
@@ -5386,7 +5415,7 @@ Archived tasks in ${path12.basename(archivePath)}:`));
5386
5415
  // src/commands/complete.ts
5387
5416
  var import_chalk11 = __toESM(require_source());
5388
5417
  import * as path13 from "path";
5389
- var import_core18 = __toESM(require_dist());
5418
+ var import_core17 = __toESM(require_dist());
5390
5419
  function assertSafeTaskId2(taskId) {
5391
5420
  const trimmed = taskId.trim();
5392
5421
  if (!trimmed || trimmed !== taskId) {
@@ -5416,7 +5445,7 @@ function completeV2(filePath, taskId, force, logger) {
5416
5445
  }
5417
5446
  const { doc, filePath: taskPath } = found;
5418
5447
  const task = doc.task;
5419
- const result = (0, import_core18.completeTaskFile)(taskPath, dirs.logsDir, { legacyMode: true, force });
5448
+ const result = (0, import_core17.completeTaskFile)(taskPath, dirs.logsDir, { force });
5420
5449
  if (!result.success) {
5421
5450
  if (result.incompleteChildren?.length) {
5422
5451
  logger.warn(import_chalk11.default.yellow(`Epic ${taskId} has incomplete child tasks:`));
@@ -5449,7 +5478,8 @@ function completeV2(filePath, taskId, force, logger) {
5449
5478
  // src/commands/log.ts
5450
5479
  var import_chalk12 = __toESM(require_source());
5451
5480
  import * as fs10 from "fs";
5452
- var import_core19 = __toESM(require_dist());
5481
+ import * as path14 from "path";
5482
+ var import_core18 = __toESM(require_dist());
5453
5483
  function logCommand(options, logger = defaultLogger) {
5454
5484
  const filePath = resolveCliBrainfilePath(options.file);
5455
5485
  if (!fs10.existsSync(filePath)) {
@@ -5495,7 +5525,7 @@ function logNoteCommand(options, logger = defaultLogger) {
5495
5525
  const newLog = existingLog ? `${existingLog}
5496
5526
  ${entry}` : entry;
5497
5527
  const newBody = (0, import_core.composeBody)(existingDesc, newLog);
5498
- (0, import_core19.writeTaskFile)(taskPath, doc.task, newBody);
5528
+ (0, import_core18.writeTaskFile)(taskPath, doc.task, newBody);
5499
5529
  logger.log(import_chalk12.default.green("Log entry added."));
5500
5530
  logger.log(import_chalk12.default.gray(` Task: ${options.task}`));
5501
5531
  logger.log(import_chalk12.default.gray(` ${entry}`));
@@ -5535,13 +5565,13 @@ function viewTaskLog(dirs, taskId, logger) {
5535
5565
  };
5536
5566
  }
5537
5567
  function searchLogs(dirs, query, logger) {
5538
- const logDocs = (0, import_core19.readTasksDir)(dirs.logsDir);
5568
+ const logDocs = (0, import_core18.readTasksDir)(dirs.logsDir);
5539
5569
  const queryLower = query.toLowerCase();
5540
5570
  const matches = [];
5541
5571
  for (const doc of logDocs) {
5542
5572
  const task = doc.task;
5543
5573
  const logContent = (0, import_core.extractLog)(doc.body);
5544
- if ((0, import_core19.scoreTaskDocument)(doc, queryLower) > 0) {
5574
+ if ((0, import_core18.scoreTaskDocument)(doc, queryLower) > 0) {
5545
5575
  let matchContext = "";
5546
5576
  if (task.title.toLowerCase().includes(queryLower)) {
5547
5577
  matchContext = `Title: ${task.title}`;
@@ -5581,39 +5611,132 @@ function searchLogs(dirs, query, logger) {
5581
5611
  };
5582
5612
  }
5583
5613
  function listRecentLogs(dirs, logger) {
5584
- const logDocs = (0, import_core19.readTasksDir)(dirs.logsDir);
5585
- logDocs.sort((a, b) => {
5586
- const aDate = a.task.completedAt || "";
5587
- const bDate = b.task.completedAt || "";
5588
- return bDate.localeCompare(aDate);
5589
- });
5614
+ const logDocs = (0, import_core18.readTasksDir)(dirs.logsDir);
5615
+ const seenIds = new Set(logDocs.map((d) => d.task.id));
5616
+ const entries = logDocs.map((d) => ({ id: d.task.id, title: d.task.title, completedAt: d.task.completedAt }));
5617
+ const ledgerPath = path14.join(dirs.logsDir, "ledger.jsonl");
5618
+ if (fs10.existsSync(ledgerPath)) {
5619
+ for (const line of fs10.readFileSync(ledgerPath, "utf-8").split("\n")) {
5620
+ const trimmed = line.trim();
5621
+ if (!trimmed) continue;
5622
+ try {
5623
+ const record2 = JSON.parse(trimmed);
5624
+ if (record2?.id && !seenIds.has(record2.id)) {
5625
+ seenIds.add(record2.id);
5626
+ entries.push({ id: record2.id, title: record2.title ?? record2.id, completedAt: record2.completedAt, ledgerOnly: true });
5627
+ }
5628
+ } catch {
5629
+ }
5630
+ }
5631
+ }
5632
+ entries.sort((a, b) => (b.completedAt || "").localeCompare(a.completedAt || ""));
5590
5633
  logger.log("");
5591
- logger.log(import_chalk12.default.bold(`Recently completed tasks (${logDocs.length})`));
5634
+ logger.log(import_chalk12.default.bold(`Recently completed tasks (${entries.length})`));
5592
5635
  logger.log(import_chalk12.default.gray("\u2500".repeat(50)));
5593
- if (logDocs.length === 0) {
5636
+ if (entries.length === 0) {
5594
5637
  logger.log(import_chalk12.default.gray(" No completed tasks found."));
5595
5638
  } else {
5596
- for (const doc of logDocs.slice(0, 20)) {
5597
- const task = doc.task;
5598
- logger.log(` ${import_chalk12.default.gray(`[${task.id}]`)} ${import_chalk12.default.white(task.title)}`);
5599
- if (task.completedAt) {
5600
- logger.log(` ${import_chalk12.default.gray("Completed:")} ${task.completedAt}`);
5639
+ for (const entry of entries.slice(0, 20)) {
5640
+ const marker = entry.ledgerOnly ? import_chalk12.default.gray(" (ledger only)") : "";
5641
+ logger.log(` ${import_chalk12.default.gray(`[${entry.id}]`)} ${import_chalk12.default.white(entry.title)}${marker}`);
5642
+ if (entry.completedAt) {
5643
+ logger.log(` ${import_chalk12.default.gray("Completed:")} ${entry.completedAt}`);
5601
5644
  }
5602
5645
  logger.log("");
5603
5646
  }
5604
5647
  }
5605
5648
  return {
5606
5649
  success: true,
5607
- tasks: logDocs.map((doc) => ({
5608
- id: doc.task.id,
5609
- title: doc.task.title,
5610
- completedAt: doc.task.completedAt
5611
- }))
5650
+ tasks: entries.map(({ id, title, completedAt }) => ({ id, title, completedAt }))
5612
5651
  };
5613
5652
  }
5614
5653
 
5615
- // src/commands/search.ts
5654
+ // src/commands/brief.ts
5616
5655
  var import_chalk13 = __toESM(require_source());
5656
+ var import_core19 = __toESM(require_dist());
5657
+ import * as fs11 from "fs";
5658
+ var BRIEF_JSON_VERSION = "0.1";
5659
+ function briefCommand(options, logger = defaultLogger) {
5660
+ try {
5661
+ const result = runBrief(options);
5662
+ if (options.json) {
5663
+ logger.log(JSON.stringify(
5664
+ { version: BRIEF_JSON_VERSION, kind: "brief", data: result },
5665
+ null,
5666
+ 2
5667
+ ));
5668
+ } else {
5669
+ renderBrief(result, logger);
5670
+ }
5671
+ return result;
5672
+ } catch (error48) {
5673
+ if (options.json && error48 instanceof CLIError) {
5674
+ const payload = {
5675
+ version: BRIEF_JSON_VERSION,
5676
+ kind: "error",
5677
+ error: {
5678
+ code: "VALIDATION_ERROR",
5679
+ message: error48.message,
5680
+ ...error48.details ? { field: error48.details } : {}
5681
+ }
5682
+ };
5683
+ process.stderr.write(`${JSON.stringify(payload, null, 2)}
5684
+ `);
5685
+ process.exit(error48.exitCode ?? ExitCode.USER_ERROR);
5686
+ }
5687
+ throw error48;
5688
+ }
5689
+ }
5690
+ function runBrief(options) {
5691
+ const agent = options.agent?.trim();
5692
+ if (!agent) {
5693
+ throw missingRequired("--agent", "brainfile brief --agent <name>");
5694
+ }
5695
+ const filePath = resolveCliBrainfilePath(options.file);
5696
+ if (!fs11.existsSync(filePath)) {
5697
+ throw fileNotFound(filePath);
5698
+ }
5699
+ if (!(0, import_core.isV2)(filePath)) {
5700
+ throw operationFailed(
5701
+ "Brief requires v2 per-task file architecture. Run: brainfile migrate"
5702
+ );
5703
+ }
5704
+ const dirs = (0, import_core.getV2Dirs)(filePath);
5705
+ const peek = options.peek === true;
5706
+ const state = (0, import_core19.readBriefState)(filePath, agent);
5707
+ const result = (0, import_core19.buildBrief)(dirs, agent, { lastBriefAt: state.lastBriefAt });
5708
+ if (!peek) {
5709
+ (0, import_core19.writeBriefState)(filePath, agent, result.generatedAt);
5710
+ }
5711
+ return { ...result, peek };
5712
+ }
5713
+ function renderBrief(result, logger) {
5714
+ const modeLabel = result.mode === "full" ? "first brief" : "since last brief";
5715
+ const header = `${import_chalk13.default.bold(result.agent)} ${import_chalk13.default.gray("\xB7")} ${import_chalk13.default.gray(modeLabel)}`;
5716
+ logger.log(header + (result.peek ? import_chalk13.default.gray(" (peek)") : ""));
5717
+ if ((0, import_core19.isEmptyBrief)(result)) {
5718
+ logger.log("");
5719
+ logger.log(import_chalk13.default.gray(` ${GLYPHS.success} Nothing new since your last brief.`));
5720
+ return;
5721
+ }
5722
+ for (const lane of result.lanes) {
5723
+ if (lane.items.length === 0) continue;
5724
+ logger.log("");
5725
+ logger.log(import_chalk13.default.bold(lane.label));
5726
+ const width = Math.max(...lane.items.map((item) => laneKey(item.taskId).length));
5727
+ for (const item of lane.items) {
5728
+ const key = laneKey(item.taskId).padEnd(width);
5729
+ const prefix = key.trim().length > 0 ? import_chalk13.default.cyan(key) : key;
5730
+ logger.log(` ${prefix} ${item.text} ${import_chalk13.default.gray(`(${item.why})`)}`);
5731
+ }
5732
+ }
5733
+ }
5734
+ function laneKey(taskId) {
5735
+ return taskId ? `${GLYPHS.pointer} ${taskId}` : "";
5736
+ }
5737
+
5738
+ // src/commands/search.ts
5739
+ var import_chalk14 = __toESM(require_source());
5617
5740
  var import_core20 = __toESM(require_dist());
5618
5741
  function searchCommand(options, logger = defaultLogger) {
5619
5742
  if (!options.query) {
@@ -5650,16 +5773,16 @@ function searchV2(filePath, query, column, logger) {
5650
5773
  }
5651
5774
  function displayResults(results, query, logger) {
5652
5775
  logger.log("");
5653
- logger.log(import_chalk13.default.bold(`Search: "${query}" (${results.length} results)`));
5654
- logger.log(import_chalk13.default.gray("\u2500".repeat(50)));
5776
+ logger.log(import_chalk14.default.bold(`Search: "${query}" (${results.length} results)`));
5777
+ logger.log(import_chalk14.default.gray("\u2500".repeat(50)));
5655
5778
  if (results.length === 0) {
5656
- logger.log(import_chalk13.default.gray(" No matching tasks found."));
5779
+ logger.log(import_chalk14.default.gray(" No matching tasks found."));
5657
5780
  } else {
5658
5781
  for (const result of results) {
5659
- const location = result.isLog ? import_chalk13.default.yellow("(completed)") : import_chalk13.default.cyan(result.column || "");
5660
- logger.log(` ${import_chalk13.default.gray(`[${result.id}]`)} ${import_chalk13.default.white(result.title)} ${location}`);
5782
+ const location = result.isLog ? import_chalk14.default.yellow("(completed)") : import_chalk14.default.cyan(result.column || "");
5783
+ logger.log(` ${import_chalk14.default.gray(`[${result.id}]`)} ${import_chalk14.default.white(result.title)} ${location}`);
5661
5784
  if (result.completedAt) {
5662
- logger.log(` ${import_chalk13.default.gray("Completed:")} ${result.completedAt}`);
5785
+ logger.log(` ${import_chalk14.default.gray("Completed:")} ${result.completedAt}`);
5663
5786
  }
5664
5787
  }
5665
5788
  }
@@ -5667,7 +5790,7 @@ function displayResults(results, query, logger) {
5667
5790
  }
5668
5791
 
5669
5792
  // src/commands/auth.ts
5670
- var import_chalk14 = __toESM(require_source());
5793
+ var import_chalk15 = __toESM(require_source());
5671
5794
  import * as readline from "readline";
5672
5795
  async function promptSetDefaultDestination(destination) {
5673
5796
  const currentConfig = getArchiveConfig();
@@ -5680,14 +5803,14 @@ async function promptSetDefaultDestination(destination) {
5680
5803
  });
5681
5804
  const currentDefault = currentConfig.default || "local";
5682
5805
  console.log("");
5683
- console.log(`Current default archive destination: ${import_chalk14.default.cyan(currentDefault)}`);
5806
+ console.log(`Current default archive destination: ${import_chalk15.default.cyan(currentDefault)}`);
5684
5807
  const answer = await new Promise((resolve6) => {
5685
5808
  rl.question(`Set ${destination} as default? [y/N]: `, resolve6);
5686
5809
  });
5687
5810
  rl.close();
5688
5811
  if (answer.toLowerCase() === "y" || answer.toLowerCase() === "yes") {
5689
5812
  setArchiveDefault(destination);
5690
- console.log(import_chalk14.default.green("\u2713") + ` Default archive destination set to ${import_chalk14.default.cyan(destination)}`);
5813
+ console.log(import_chalk15.default.green("\u2713") + ` Default archive destination set to ${import_chalk15.default.cyan(destination)}`);
5691
5814
  }
5692
5815
  }
5693
5816
  async function githubAuthCommand(options) {
@@ -5697,11 +5820,11 @@ async function githubAuthCommand(options) {
5697
5820
  usePAT: !!options.token
5698
5821
  });
5699
5822
  if (!result.success) {
5700
- console.log(import_chalk14.default.red("\u2717") + ` Authentication failed: ${result.error}`);
5823
+ console.log(import_chalk15.default.red("\u2717") + ` Authentication failed: ${result.error}`);
5701
5824
  process.exit(1);
5702
5825
  }
5703
5826
  console.log("");
5704
- console.log(import_chalk14.default.green("GitHub authentication complete!"));
5827
+ console.log(import_chalk15.default.green("GitHub authentication complete!"));
5705
5828
  } catch (error48) {
5706
5829
  handleError(error48);
5707
5830
  }
@@ -5712,10 +5835,10 @@ async function linearAuthCommand(options) {
5712
5835
  console.log("");
5713
5836
  console.log("Linear authentication requires an API key.");
5714
5837
  console.log("");
5715
- console.log(`Get your API key from: ${import_chalk14.default.underline("https://linear.app/settings/api")}`);
5838
+ console.log(`Get your API key from: ${import_chalk15.default.underline("https://linear.app/settings/api")}`);
5716
5839
  console.log("");
5717
5840
  console.log("Then run:");
5718
- console.log(import_chalk14.default.cyan(" brainfile auth linear --token <your-api-key>"));
5841
+ console.log(import_chalk15.default.cyan(" brainfile auth linear --token <your-api-key>"));
5719
5842
  console.log("");
5720
5843
  return;
5721
5844
  }
@@ -5723,23 +5846,23 @@ async function linearAuthCommand(options) {
5723
5846
  token: options.token
5724
5847
  });
5725
5848
  if (!result.success) {
5726
- console.log(import_chalk14.default.red("\u2717") + ` Authentication failed: ${result.error}`);
5849
+ console.log(import_chalk15.default.red("\u2717") + ` Authentication failed: ${result.error}`);
5727
5850
  process.exit(1);
5728
5851
  }
5729
5852
  const teams = await getLinearTeams();
5730
5853
  if (teams.length === 0) {
5731
- console.log(import_chalk14.default.yellow("\u26A0") + " No teams found. You may need to configure a team later.");
5854
+ console.log(import_chalk15.default.yellow("\u26A0") + " No teams found. You may need to configure a team later.");
5732
5855
  } else if (teams.length === 1) {
5733
5856
  const team = teams[0];
5734
5857
  setLinearConfig({ teamId: team.id });
5735
- console.log(import_chalk14.default.green("\u2713") + ` Default team set to ${import_chalk14.default.cyan(team.name)} (${team.key})`);
5858
+ console.log(import_chalk15.default.green("\u2713") + ` Default team set to ${import_chalk15.default.cyan(team.name)} (${team.key})`);
5736
5859
  await promptSetDefaultDestination("linear");
5737
5860
  } else {
5738
5861
  console.log("");
5739
5862
  console.log("Multiple teams found. Select a default team for archiving:");
5740
5863
  console.log("");
5741
5864
  teams.forEach((team, index) => {
5742
- console.log(` ${import_chalk14.default.cyan(index + 1)}) ${team.name} (${team.key})`);
5865
+ console.log(` ${import_chalk15.default.cyan(index + 1)}) ${team.name} (${team.key})`);
5743
5866
  });
5744
5867
  console.log("");
5745
5868
  const rl = readline.createInterface({
@@ -5755,15 +5878,15 @@ async function linearAuthCommand(options) {
5755
5878
  const team = teams[selection - 1];
5756
5879
  setLinearConfig({ teamId: team.id });
5757
5880
  console.log("");
5758
- console.log(import_chalk14.default.green("\u2713") + ` Default team set to ${import_chalk14.default.cyan(team.name)} (${team.key})`);
5881
+ console.log(import_chalk15.default.green("\u2713") + ` Default team set to ${import_chalk15.default.cyan(team.name)} (${team.key})`);
5759
5882
  await promptSetDefaultDestination("linear");
5760
5883
  } else {
5761
- console.log(import_chalk14.default.yellow("\u26A0") + " Invalid selection. You can set a team later with:");
5762
- console.log(import_chalk14.default.cyan(" brainfile config set archive.linear.teamId <team-id>"));
5884
+ console.log(import_chalk15.default.yellow("\u26A0") + " Invalid selection. You can set a team later with:");
5885
+ console.log(import_chalk15.default.cyan(" brainfile config set archive.linear.teamId <team-id>"));
5763
5886
  }
5764
5887
  }
5765
5888
  console.log("");
5766
- console.log(import_chalk14.default.green("Linear authentication complete!"));
5889
+ console.log(import_chalk15.default.green("Linear authentication complete!"));
5767
5890
  } catch (error48) {
5768
5891
  handleError(error48);
5769
5892
  }
@@ -5772,25 +5895,25 @@ async function authStatusCommand() {
5772
5895
  try {
5773
5896
  const status = await authStore.getStatus();
5774
5897
  console.log("");
5775
- console.log(import_chalk14.default.bold("Authentication Status"));
5898
+ console.log(import_chalk15.default.bold("Authentication Status"));
5776
5899
  console.log("");
5777
5900
  if (status.github.authenticated) {
5778
5901
  const source = status.github.source;
5779
- const username = status.github.username ? ` as ${import_chalk14.default.cyan("@" + status.github.username)}` : "";
5902
+ const username = status.github.username ? ` as ${import_chalk15.default.cyan("@" + status.github.username)}` : "";
5780
5903
  console.log(
5781
- import_chalk14.default.green("\u2713") + ` GitHub: authenticated${username} ${import_chalk14.default.gray(`(${source})`)}`
5904
+ import_chalk15.default.green("\u2713") + ` GitHub: authenticated${username} ${import_chalk15.default.gray(`(${source})`)}`
5782
5905
  );
5783
5906
  } else {
5784
- console.log(import_chalk14.default.red("\u2717") + " GitHub: not configured");
5907
+ console.log(import_chalk15.default.red("\u2717") + " GitHub: not configured");
5785
5908
  }
5786
5909
  if (status.linear.authenticated) {
5787
5910
  const source = status.linear.source;
5788
- const username = status.linear.username ? ` as ${import_chalk14.default.cyan(status.linear.username)}` : "";
5911
+ const username = status.linear.username ? ` as ${import_chalk15.default.cyan(status.linear.username)}` : "";
5789
5912
  console.log(
5790
- import_chalk14.default.green("\u2713") + ` Linear: authenticated${username} ${import_chalk14.default.gray(`(${source})`)}`
5913
+ import_chalk15.default.green("\u2713") + ` Linear: authenticated${username} ${import_chalk15.default.gray(`(${source})`)}`
5791
5914
  );
5792
5915
  } else {
5793
- console.log(import_chalk14.default.red("\u2717") + " Linear: not configured");
5916
+ console.log(import_chalk15.default.red("\u2717") + " Linear: not configured");
5794
5917
  }
5795
5918
  console.log("");
5796
5919
  } catch (error48) {
@@ -5802,7 +5925,7 @@ async function authLogoutCommand(provider, options) {
5802
5925
  if (options?.all || provider === "all") {
5803
5926
  await logoutGitHub();
5804
5927
  await logoutLinear();
5805
- console.log(import_chalk14.default.green("\u2713") + " Logged out from all providers");
5928
+ console.log(import_chalk15.default.green("\u2713") + " Logged out from all providers");
5806
5929
  return;
5807
5930
  }
5808
5931
  if (!provider) {
@@ -5817,14 +5940,14 @@ async function authLogoutCommand(provider, options) {
5817
5940
  switch (provider.toLowerCase()) {
5818
5941
  case "github":
5819
5942
  await logoutGitHub();
5820
- console.log(import_chalk14.default.green("\u2713") + " Logged out from GitHub");
5943
+ console.log(import_chalk15.default.green("\u2713") + " Logged out from GitHub");
5821
5944
  break;
5822
5945
  case "linear":
5823
5946
  await logoutLinear();
5824
- console.log(import_chalk14.default.green("\u2713") + " Logged out from Linear");
5947
+ console.log(import_chalk15.default.green("\u2713") + " Logged out from Linear");
5825
5948
  break;
5826
5949
  default:
5827
- console.log(import_chalk14.default.red("\u2717") + ` Unknown provider: ${provider}`);
5950
+ console.log(import_chalk15.default.red("\u2717") + ` Unknown provider: ${provider}`);
5828
5951
  console.log("Valid providers: github, linear");
5829
5952
  process.exit(1);
5830
5953
  }
@@ -5835,7 +5958,7 @@ async function authLogoutCommand(provider, options) {
5835
5958
 
5836
5959
  // src/commands/subtask.ts
5837
5960
  var import_core21 = __toESM(require_dist());
5838
- var import_chalk15 = __toESM(require_source());
5961
+ var import_chalk16 = __toESM(require_source());
5839
5962
  function subtaskCommand(options) {
5840
5963
  try {
5841
5964
  if (!options.task) {
@@ -5859,7 +5982,7 @@ function subtaskCommandV2(options, brainfilePath) {
5859
5982
  const dirs = (0, import_core.getV2Dirs)(brainfilePath);
5860
5983
  const found = (0, import_core.findV2Task)(dirs, options.task, true);
5861
5984
  if (!found) {
5862
- console.error(import_chalk15.default.red(`Error: Task ${options.task} not found`));
5985
+ console.error(import_chalk16.default.red(`Error: Task ${options.task} not found`));
5863
5986
  process.exit(1);
5864
5987
  }
5865
5988
  const { doc, filePath } = found;
@@ -5870,7 +5993,7 @@ function subtaskCommandV2(options, brainfilePath) {
5870
5993
  validationError(result.error || `Failed to add subtask to ${options.task}`);
5871
5994
  }
5872
5995
  const added = result.affected[0];
5873
- console.log(import_chalk15.default.green(`Subtask added: ${added.id} - ${added.title}`));
5996
+ console.log(import_chalk16.default.green(`Subtask added: ${added.id} - ${added.title}`));
5874
5997
  return;
5875
5998
  }
5876
5999
  if (!task.subtasks || task.subtasks.length === 0) {
@@ -5882,7 +6005,7 @@ function subtaskCommandV2(options, brainfilePath) {
5882
6005
  subtaskNotFoundError(options.delete, task);
5883
6006
  }
5884
6007
  const deleted = result.affected[0];
5885
- console.log(import_chalk15.default.green(`Subtask deleted: ${deleted.id} - ${deleted.title}`));
6008
+ console.log(import_chalk16.default.green(`Subtask deleted: ${deleted.id} - ${deleted.title}`));
5886
6009
  return;
5887
6010
  }
5888
6011
  if (options.update) {
@@ -5894,7 +6017,7 @@ function subtaskCommandV2(options, brainfilePath) {
5894
6017
  if (!result.success || !result.affected?.length) {
5895
6018
  subtaskNotFoundError(options.update, task);
5896
6019
  }
5897
- console.log(import_chalk15.default.green(`Subtask updated: ${options.update}
6020
+ console.log(import_chalk16.default.green(`Subtask updated: ${options.update}
5898
6021
  "${oldTitle}" \u2192 "${options.title}"`));
5899
6022
  return;
5900
6023
  }
@@ -5904,7 +6027,7 @@ function subtaskCommandV2(options, brainfilePath) {
5904
6027
  subtaskNotFoundError(options.toggle, task);
5905
6028
  }
5906
6029
  const newStatus = result.affected[0].completed ? "completed" : "incomplete";
5907
- console.log(import_chalk15.default.green(`Subtask ${options.toggle} marked as ${newStatus}`));
6030
+ console.log(import_chalk16.default.green(`Subtask ${options.toggle} marked as ${newStatus}`));
5908
6031
  return;
5909
6032
  }
5910
6033
  }
@@ -6733,10 +6856,10 @@ function mergeDefs(...defs) {
6733
6856
  function cloneDef(schema2) {
6734
6857
  return mergeDefs(schema2._zod.def);
6735
6858
  }
6736
- function getElementAtPath(obj, path20) {
6737
- if (!path20)
6859
+ function getElementAtPath(obj, path21) {
6860
+ if (!path21)
6738
6861
  return obj;
6739
- return path20.reduce((acc, key) => acc?.[key], obj);
6862
+ return path21.reduce((acc, key) => acc?.[key], obj);
6740
6863
  }
6741
6864
  function promiseAllObject(promisesObj) {
6742
6865
  const keys = Object.keys(promisesObj);
@@ -7119,11 +7242,11 @@ function aborted(x, startIndex = 0) {
7119
7242
  }
7120
7243
  return false;
7121
7244
  }
7122
- function prefixIssues(path20, issues) {
7245
+ function prefixIssues(path21, issues) {
7123
7246
  return issues.map((iss) => {
7124
7247
  var _a2;
7125
7248
  (_a2 = iss).path ?? (_a2.path = []);
7126
- iss.path.unshift(path20);
7249
+ iss.path.unshift(path21);
7127
7250
  return iss;
7128
7251
  });
7129
7252
  }
@@ -7306,7 +7429,7 @@ function formatError(error48, mapper = (issue2) => issue2.message) {
7306
7429
  }
7307
7430
  function treeifyError(error48, mapper = (issue2) => issue2.message) {
7308
7431
  const result = { errors: [] };
7309
- const processError = (error49, path20 = []) => {
7432
+ const processError = (error49, path21 = []) => {
7310
7433
  var _a2, _b;
7311
7434
  for (const issue2 of error49.issues) {
7312
7435
  if (issue2.code === "invalid_union" && issue2.errors.length) {
@@ -7316,7 +7439,7 @@ function treeifyError(error48, mapper = (issue2) => issue2.message) {
7316
7439
  } else if (issue2.code === "invalid_element") {
7317
7440
  processError({ issues: issue2.issues }, issue2.path);
7318
7441
  } else {
7319
- const fullpath = [...path20, ...issue2.path];
7442
+ const fullpath = [...path21, ...issue2.path];
7320
7443
  if (fullpath.length === 0) {
7321
7444
  result.errors.push(mapper(issue2));
7322
7445
  continue;
@@ -7348,8 +7471,8 @@ function treeifyError(error48, mapper = (issue2) => issue2.message) {
7348
7471
  }
7349
7472
  function toDotPath(_path) {
7350
7473
  const segs = [];
7351
- const path20 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
7352
- for (const seg of path20) {
7474
+ const path21 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
7475
+ for (const seg of path21) {
7353
7476
  if (typeof seg === "number")
7354
7477
  segs.push(`[${seg}]`);
7355
7478
  else if (typeof seg === "symbol")
@@ -19326,13 +19449,13 @@ function resolveRef(ref, ctx) {
19326
19449
  if (!ref.startsWith("#")) {
19327
19450
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
19328
19451
  }
19329
- const path20 = ref.slice(1).split("/").filter(Boolean);
19330
- if (path20.length === 0) {
19452
+ const path21 = ref.slice(1).split("/").filter(Boolean);
19453
+ if (path21.length === 0) {
19331
19454
  return ctx.rootSchema;
19332
19455
  }
19333
19456
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
19334
- if (path20[0] === defsKey) {
19335
- const key = path20[1];
19457
+ if (path21[0] === defsKey) {
19458
+ const key = path21[1];
19336
19459
  if (!key || !ctx.defs[key]) {
19337
19460
  throw new Error(`Reference not found: ${ref}`);
19338
19461
  }
@@ -23294,9 +23417,9 @@ var rev2026Codec = {
23294
23417
  });
23295
23418
  const parsed = buildSchemas2026().RequestMetaEnvelopeSchema.safeParse(meta3);
23296
23419
  if (!parsed.success) for (const issue2 of parsed.error.issues) {
23297
- const path20 = issue2.path.map(String);
23298
- const key = path20.length > 0 ? path20.join(".") : "_meta";
23299
- if (path20.length === 1 && issues.some((existing) => existing.key === key && existing.problem === "missing")) continue;
23420
+ const path21 = issue2.path.map(String);
23421
+ const key = path21.length > 0 ? path21.join(".") : "_meta";
23422
+ if (path21.length === 1 && issues.some((existing) => existing.key === key && existing.problem === "missing")) continue;
23300
23423
  issues.push({
23301
23424
  key,
23302
23425
  problem: issue2.message
@@ -23617,29 +23740,29 @@ var PERMITTED_X_MCP_HEADER_TYPES = /* @__PURE__ */ new Set([
23617
23740
  function scanXMcpHeaderDeclarations(inputSchema) {
23618
23741
  const declarations = [];
23619
23742
  const seenLower = /* @__PURE__ */ new Map();
23620
- const visit = (node, path20, reachable) => {
23743
+ const visit = (node, path21, reachable) => {
23621
23744
  if (node === null || typeof node !== "object") return void 0;
23622
23745
  const schema2 = node;
23623
23746
  if (X_MCP_HEADER_KEY in schema2) {
23624
- if (!reachable || path20.length === 0) return `${pathName(path20)}: x-mcp-header is only permitted on properties statically reachable via a chain of 'properties' keys (not under items, additionalProperties, oneOf/anyOf/allOf/not, if/then/else, or $ref)`;
23747
+ if (!reachable || path21.length === 0) return `${pathName(path21)}: x-mcp-header is only permitted on properties statically reachable via a chain of 'properties' keys (not under items, additionalProperties, oneOf/anyOf/allOf/not, if/then/else, or $ref)`;
23625
23748
  const raw = schema2[X_MCP_HEADER_KEY];
23626
- if (typeof raw !== "string" || raw.length === 0) return `${pathName(path20)}: x-mcp-header MUST be a non-empty string`;
23627
- if (!RFC9110_TOKEN.test(raw)) return `${pathName(path20)}: x-mcp-header '${raw}' is not a valid RFC 9110 token (no spaces, control characters or HTTP delimiters)`;
23749
+ if (typeof raw !== "string" || raw.length === 0) return `${pathName(path21)}: x-mcp-header MUST be a non-empty string`;
23750
+ if (!RFC9110_TOKEN.test(raw)) return `${pathName(path21)}: x-mcp-header '${raw}' is not a valid RFC 9110 token (no spaces, control characters or HTTP delimiters)`;
23628
23751
  const type2 = typeof schema2.type === "string" ? schema2.type : void 0;
23629
- if (type2 === void 0 || !PERMITTED_X_MCP_HEADER_TYPES.has(type2)) return `${pathName(path20)}: x-mcp-header is only permitted on primitive-typed properties (string, integer, boolean); got ${type2 ?? "<none>"}`;
23752
+ if (type2 === void 0 || !PERMITTED_X_MCP_HEADER_TYPES.has(type2)) return `${pathName(path21)}: x-mcp-header is only permitted on primitive-typed properties (string, integer, boolean); got ${type2 ?? "<none>"}`;
23630
23753
  const lower = raw.toLowerCase();
23631
23754
  const prior = seenLower.get(lower);
23632
23755
  if (prior !== void 0) return `x-mcp-header '${raw}' is not case-insensitively unique (also declared as '${prior}')`;
23633
23756
  seenLower.set(lower, raw);
23634
23757
  declarations.push({
23635
- path: path20,
23758
+ path: path21,
23636
23759
  headerName: raw,
23637
23760
  type: type2
23638
23761
  });
23639
23762
  }
23640
23763
  const properties = schema2.properties;
23641
23764
  if (properties !== null && typeof properties === "object") for (const [key, child] of Object.entries(properties)) {
23642
- const fault$1 = visit(child, [...path20, key], reachable);
23765
+ const fault$1 = visit(child, [...path21, key], reachable);
23643
23766
  if (fault$1 !== void 0) return fault$1;
23644
23767
  }
23645
23768
  for (const k of NON_REACHABLE_SUBSCHEMA_KEYWORDS) {
@@ -23647,7 +23770,7 @@ function scanXMcpHeaderDeclarations(inputSchema) {
23647
23770
  if (sub === void 0) continue;
23648
23771
  const branches = Array.isArray(sub) ? sub : sub !== null && typeof sub === "object" && OBJECT_VALUED_SUBSCHEMA_KEYWORDS.has(k) ? Object.values(sub) : [sub];
23649
23772
  for (const branch of branches) {
23650
- const fault$1 = visit(branch, [...path20, `<${k}>`], false);
23773
+ const fault$1 = visit(branch, [...path21, `<${k}>`], false);
23651
23774
  if (fault$1 !== void 0) return fault$1;
23652
23775
  }
23653
23776
  }
@@ -23687,8 +23810,8 @@ var OBJECT_VALUED_SUBSCHEMA_KEYWORDS = /* @__PURE__ */ new Set([
23687
23810
  "$defs",
23688
23811
  "definitions"
23689
23812
  ]);
23690
- function pathName(path20) {
23691
- return path20.length === 0 ? "<root>" : path20.join(".");
23813
+ function pathName(path21) {
23814
+ return path21.length === 0 ? "<root>" : path21.join(".");
23692
23815
  }
23693
23816
  var HEADER_MISMATCH_ERROR_CODE = -32020;
23694
23817
  var INBOUND_VALIDATION_LADDER = [
@@ -23977,7 +24100,7 @@ var PROPERTY_KEYS_BY_TYPE = {
23977
24100
  array: shapeKeys([UntitledMultiSelectEnumSchemaSchema, TitledMultiSelectEnumSchemaSchema])
23978
24101
  };
23979
24102
  var SUPPORTED_STRING_FORMATS = new Set(StringSchemaSchema.shape.format.unwrap().options);
23980
- function walkProperty(node, path20, vendor, unsupported) {
24103
+ function walkProperty(node, path21, vendor, unsupported) {
23981
24104
  if (!isJsonObject(node)) return node;
23982
24105
  const allowedKeys = typeof node.type === "string" && Object.hasOwn(PROPERTY_KEYS_BY_TYPE, node.type) ? PROPERTY_KEYS_BY_TYPE[node.type] : void 0;
23983
24106
  if (allowedKeys === void 0) return node;
@@ -23985,8 +24108,8 @@ function walkProperty(node, path20, vendor, unsupported) {
23985
24108
  for (const [key, value] of Object.entries(node)) if (allowedKeys.has(key) || isAnnotationOnlyJsonSchemaKeyword(key)) pruned[key] = value;
23986
24109
  else if (key === "pattern" && node.type === "string" && typeof node.format === "string") {
23987
24110
  if (!SUPPORTED_STRING_FORMATS.has(node.format)) pruned[key] = value;
23988
- else if (typeof value !== "string" || !isLibraryFormatPattern(node.format, value, vendor)) unsupported.push(`${path20}.${key}`);
23989
- } else unsupported.push(`${path20}.${key}`);
24111
+ else if (typeof value !== "string" || !isLibraryFormatPattern(node.format, value, vendor)) unsupported.push(`${path21}.${key}`);
24112
+ } else unsupported.push(`${path21}.${key}`);
23990
24113
  return pruned;
23991
24114
  }
23992
24115
  function walkRequestedSchema(converted, vendor) {
@@ -24003,11 +24126,11 @@ function describeUnsupportedProperties(pruned, fallback) {
24003
24126
  const offenders = Object.entries(pruned.properties).filter(([, node]) => !parseSchema(PrimitiveSchemaDefinitionSchema, node).success).map(([name]) => `properties.${name}`);
24004
24127
  return offenders.length > 0 ? offenders.join(", ") : fallback;
24005
24128
  }
24006
- function findDroppedConstraintPaths(original, parsed, path20 = "") {
24007
- if (Array.isArray(original) && Array.isArray(parsed)) return original.flatMap((item, index) => findDroppedConstraintPaths(item, parsed[index], `${path20}[${index}]`));
24129
+ function findDroppedConstraintPaths(original, parsed, path21 = "") {
24130
+ if (Array.isArray(original) && Array.isArray(parsed)) return original.flatMap((item, index) => findDroppedConstraintPaths(item, parsed[index], `${path21}[${index}]`));
24008
24131
  if (!isJsonObject(original) || !isJsonObject(parsed)) return [];
24009
24132
  return Object.entries(original).flatMap(([key, value]) => {
24010
- const childPath = path20 ? `${path20}.${key}` : key;
24133
+ const childPath = path21 ? `${path21}.${key}` : key;
24011
24134
  if (!Object.prototype.hasOwnProperty.call(parsed, key)) return isAnnotationOnlyJsonSchemaKeyword(key) ? [] : [childPath];
24012
24135
  return findDroppedConstraintPaths(value, parsed[key], childPath);
24013
24136
  });
@@ -28085,8 +28208,8 @@ var require_utils = /* @__PURE__ */ __commonJSMin(((exports, module) => {
28085
28208
  for (let i = 0; i < str2.length; i++) if (str2[i] === token) ind++;
28086
28209
  return ind;
28087
28210
  }
28088
- function removeDotSegments(path20) {
28089
- let input = path20;
28211
+ function removeDotSegments(path21) {
28212
+ let input = path21;
28090
28213
  const output = [];
28091
28214
  let nextSlash = -1;
28092
28215
  let len = 0;
@@ -28239,8 +28362,8 @@ var require_schemes = /* @__PURE__ */ __commonJSMin(((exports, module) => {
28239
28362
  wsComponent.secure = void 0;
28240
28363
  }
28241
28364
  if (wsComponent.resourceName) {
28242
- const [path20, query] = wsComponent.resourceName.split("?");
28243
- wsComponent.path = path20 && path20 !== "/" ? path20 : void 0;
28365
+ const [path21, query] = wsComponent.resourceName.split("?");
28366
+ wsComponent.path = path21 && path21 !== "/" ? path21 : void 0;
28244
28367
  wsComponent.query = query;
28245
28368
  wsComponent.resourceName = void 0;
28246
28369
  }
@@ -28889,7 +29012,7 @@ var require_core$3 = /* @__PURE__ */ __commonJSMin(((exports) => {
28889
29012
  } else throw new Error("invalid addKeywords parameters");
28890
29013
  checkKeyword.call(this, keyword, def);
28891
29014
  if (!def) {
28892
- (0, util_1.eachItem)(keyword, (kwd) => addRule2.call(this, kwd));
29015
+ (0, util_1.eachItem)(keyword, (kwd) => addRule.call(this, kwd));
28893
29016
  return this;
28894
29017
  }
28895
29018
  keywordMetaschema.call(this, def);
@@ -28898,7 +29021,7 @@ var require_core$3 = /* @__PURE__ */ __commonJSMin(((exports) => {
28898
29021
  type: (0, dataType_1.getJSONTypes)(def.type),
28899
29022
  schemaType: (0, dataType_1.getJSONTypes)(def.schemaType)
28900
29023
  };
28901
- (0, util_1.eachItem)(keyword, definition.type.length === 0 ? (k) => addRule2.call(this, k, definition) : (k) => definition.type.forEach((t) => addRule2.call(this, k, definition, t)));
29024
+ (0, util_1.eachItem)(keyword, definition.type.length === 0 ? (k) => addRule.call(this, k, definition) : (k) => definition.type.forEach((t) => addRule.call(this, k, definition, t)));
28902
29025
  return this;
28903
29026
  }
28904
29027
  getKeyword(keyword) {
@@ -29063,7 +29186,7 @@ var require_core$3 = /* @__PURE__ */ __commonJSMin(((exports) => {
29063
29186
  if (!def) return;
29064
29187
  if (def.$data && !("code" in def || "validate" in def)) throw new Error('$data keyword must have "code" or "validate" function');
29065
29188
  }
29066
- function addRule2(keyword, definition, dataType) {
29189
+ function addRule(keyword, definition, dataType) {
29067
29190
  var _a2;
29068
29191
  const post = definition === null || definition === void 0 ? void 0 : definition.post;
29069
29192
  if (dataType && post) throw new Error('keyword with "post" flag cannot have "type"');
@@ -34320,18 +34443,18 @@ function toError(value) {
34320
34443
  }
34321
34444
 
34322
34445
  // src/commands/mcp.ts
34323
- var import_core43 = __toESM(require_dist());
34446
+ var import_core44 = __toESM(require_dist());
34324
34447
 
34325
34448
  // src/mcp/helpers.ts
34326
34449
  var import_core29 = __toESM(require_dist());
34327
- import * as fs11 from "fs";
34328
- import * as path14 from "path";
34450
+ import * as fs12 from "fs";
34451
+ import * as path15 from "path";
34329
34452
  function resolveBrainfile(filePath) {
34330
- return path14.resolve(filePath);
34453
+ return path15.resolve(filePath);
34331
34454
  }
34332
34455
  function requireV2(filePath) {
34333
34456
  const resolvedPath = resolveBrainfile(filePath);
34334
- if (!fs11.existsSync(resolvedPath)) {
34457
+ if (!fs12.existsSync(resolvedPath)) {
34335
34458
  return { content: [{ type: "text", text: `Error: File not found: ${resolvedPath}` }], isError: true };
34336
34459
  }
34337
34460
  if (!(0, import_core29.isV2)(resolvedPath)) {
@@ -34353,20 +34476,237 @@ function mcpStructuredError(message, field, value) {
34353
34476
  };
34354
34477
  }
34355
34478
  function findGitRoot(startDir) {
34356
- let currentDir = path14.resolve(startDir);
34357
- const root = path14.parse(currentDir).root;
34479
+ let currentDir = path15.resolve(startDir);
34480
+ const root = path15.parse(currentDir).root;
34358
34481
  while (currentDir !== root) {
34359
- const gitPath = path14.join(currentDir, ".git");
34360
- if (fs11.existsSync(gitPath)) {
34482
+ const gitPath = path15.join(currentDir, ".git");
34483
+ if (fs12.existsSync(gitPath)) {
34361
34484
  return currentDir;
34362
34485
  }
34363
- const parentDir = path14.dirname(currentDir);
34486
+ const parentDir = path15.dirname(currentDir);
34364
34487
  if (parentDir === currentDir) break;
34365
34488
  currentDir = parentDir;
34366
34489
  }
34367
34490
  return null;
34368
34491
  }
34369
34492
 
34493
+ // src/mcp/schemas.ts
34494
+ var taskSummaryShape = {
34495
+ id: external_exports.string(),
34496
+ title: external_exports.string(),
34497
+ priority: external_exports.string().optional(),
34498
+ tags: external_exports.array(external_exports.string()).optional(),
34499
+ assignee: external_exports.string().optional()
34500
+ };
34501
+ var subtaskSchema = external_exports.looseObject({
34502
+ id: external_exports.string(),
34503
+ title: external_exports.string(),
34504
+ completed: external_exports.boolean().optional()
34505
+ });
34506
+ var listTasksOutputSchema = external_exports.object({
34507
+ tasks: external_exports.array(external_exports.object({ ...taskSummaryShape, column: external_exports.string() })),
34508
+ count: external_exports.number()
34509
+ });
34510
+ var getTaskOutputSchema = external_exports.looseObject({
34511
+ id: external_exports.string(),
34512
+ title: external_exports.string(),
34513
+ column: external_exports.string(),
34514
+ type: external_exports.string().optional(),
34515
+ description: external_exports.string().optional(),
34516
+ parentId: external_exports.string().optional(),
34517
+ dependsOn: external_exports.array(external_exports.string()).optional(),
34518
+ blockedBy: external_exports.array(external_exports.string()).optional(),
34519
+ relatedFiles: external_exports.array(external_exports.string()).optional(),
34520
+ assignee: external_exports.string().optional(),
34521
+ tags: external_exports.array(external_exports.string()).optional(),
34522
+ priority: external_exports.string().optional(),
34523
+ dueDate: external_exports.string().optional(),
34524
+ subtasks: external_exports.array(subtaskSchema).optional(),
34525
+ contract: external_exports.looseObject({}).optional(),
34526
+ createdAt: external_exports.string().optional(),
34527
+ updatedAt: external_exports.string().optional(),
34528
+ completedAt: external_exports.string().optional(),
34529
+ position: external_exports.number().optional(),
34530
+ archived: external_exports.boolean().optional()
34531
+ });
34532
+ var searchTaskViewSchema = external_exports.object({
34533
+ id: external_exports.string(),
34534
+ title: external_exports.string(),
34535
+ completedAt: external_exports.string().optional(),
34536
+ isLog: external_exports.boolean(),
34537
+ description: external_exports.string().optional(),
34538
+ log: external_exports.string().optional()
34539
+ });
34540
+ var searchRecentSchema = external_exports.object({
34541
+ logs: external_exports.array(external_exports.object({
34542
+ id: external_exports.string(),
34543
+ title: external_exports.string(),
34544
+ completedAt: external_exports.string().optional()
34545
+ })),
34546
+ count: external_exports.number()
34547
+ });
34548
+ var searchResultsSchema = external_exports.object({
34549
+ results: external_exports.array(external_exports.object({
34550
+ ...taskSummaryShape,
34551
+ column: external_exports.string().optional(),
34552
+ score: external_exports.number(),
34553
+ isLog: external_exports.boolean().optional()
34554
+ })),
34555
+ count: external_exports.number()
34556
+ });
34557
+ var searchOutputSchema = external_exports.union([
34558
+ searchTaskViewSchema,
34559
+ searchRecentSchema,
34560
+ searchResultsSchema
34561
+ ]);
34562
+ var taskAddOutputSchema = external_exports.object({
34563
+ id: external_exports.string(),
34564
+ title: external_exports.string()
34565
+ });
34566
+ var taskMoveOutputSchema = external_exports.object({
34567
+ success: external_exports.boolean(),
34568
+ successCount: external_exports.number(),
34569
+ failureCount: external_exports.number(),
34570
+ results: external_exports.array(external_exports.object({
34571
+ taskId: external_exports.string(),
34572
+ success: external_exports.boolean(),
34573
+ message: external_exports.string().optional(),
34574
+ warning: external_exports.string().optional(),
34575
+ error: external_exports.string().optional()
34576
+ }))
34577
+ });
34578
+ var taskPatchOutputSchema = external_exports.object({
34579
+ success: external_exports.boolean(),
34580
+ successCount: external_exports.number(),
34581
+ failureCount: external_exports.number(),
34582
+ results: external_exports.array(external_exports.object({
34583
+ taskId: external_exports.string(),
34584
+ success: external_exports.boolean(),
34585
+ error: external_exports.string().optional()
34586
+ }))
34587
+ });
34588
+ var taskDeleteOutputSchema = external_exports.object({
34589
+ id: external_exports.string(),
34590
+ deleted: external_exports.literal(true)
34591
+ });
34592
+ var subtaskOutputSchema = external_exports.discriminatedUnion("action", [
34593
+ external_exports.object({
34594
+ action: external_exports.literal("add"),
34595
+ added: external_exports.array(external_exports.object({ id: external_exports.string(), title: external_exports.string() })),
34596
+ count: external_exports.number()
34597
+ }),
34598
+ external_exports.object({
34599
+ action: external_exports.literal("delete"),
34600
+ deleted: external_exports.array(external_exports.string()),
34601
+ missing: external_exports.array(external_exports.string()),
34602
+ count: external_exports.number()
34603
+ }),
34604
+ external_exports.object({
34605
+ action: external_exports.literal("toggle"),
34606
+ updated: external_exports.array(external_exports.object({ id: external_exports.string(), completed: external_exports.boolean().optional() })),
34607
+ count: external_exports.number()
34608
+ }),
34609
+ external_exports.object({
34610
+ action: external_exports.literal("update"),
34611
+ updated: external_exports.array(external_exports.object({ id: external_exports.string(), title: external_exports.string() })),
34612
+ count: external_exports.number()
34613
+ })
34614
+ ]);
34615
+ var deliverableSchema = external_exports.looseObject({
34616
+ // Hand-edited YAML often omits `type` (path-only deliverables). Required here
34617
+ // would fail output validation on a successful validate after the task was
34618
+ // already completed.
34619
+ type: external_exports.string().optional(),
34620
+ path: external_exports.string(),
34621
+ description: external_exports.string().optional()
34622
+ });
34623
+ var contractOutputSchema = external_exports.discriminatedUnion("action", [
34624
+ external_exports.object({
34625
+ action: external_exports.literal("attach"),
34626
+ task: external_exports.string(),
34627
+ status: external_exports.string()
34628
+ }),
34629
+ external_exports.object({
34630
+ action: external_exports.literal("pickup"),
34631
+ task: external_exports.string(),
34632
+ markdown: external_exports.string()
34633
+ }),
34634
+ external_exports.object({
34635
+ action: external_exports.literal("deliver"),
34636
+ task: external_exports.string()
34637
+ }),
34638
+ external_exports.object({
34639
+ action: external_exports.literal("validate"),
34640
+ ok: external_exports.boolean(),
34641
+ status: external_exports.string(),
34642
+ deliverables: external_exports.array(external_exports.looseObject({
34643
+ deliverable: deliverableSchema,
34644
+ ok: external_exports.boolean(),
34645
+ resolvedPath: external_exports.string().optional(),
34646
+ error: external_exports.string().optional()
34647
+ })),
34648
+ commands: external_exports.array(external_exports.looseObject({
34649
+ command: external_exports.string(),
34650
+ exitCode: external_exports.number(),
34651
+ stdout: external_exports.string(),
34652
+ stderr: external_exports.string()
34653
+ })),
34654
+ warnings: external_exports.array(external_exports.looseObject({
34655
+ command: external_exports.string(),
34656
+ message: external_exports.string()
34657
+ }))
34658
+ }),
34659
+ external_exports.object({
34660
+ action: external_exports.literal("graph"),
34661
+ attached: external_exports.array(external_exports.string()),
34662
+ count: external_exports.number(),
34663
+ order: external_exports.array(external_exports.string()),
34664
+ graph: external_exports.string()
34665
+ }),
34666
+ external_exports.object({
34667
+ action: external_exports.literal("activate"),
34668
+ activated: external_exports.array(external_exports.string()),
34669
+ count: external_exports.number()
34670
+ })
34671
+ ]);
34672
+ var taskCompleteOutputSchema = external_exports.discriminatedUnion("destination", [
34673
+ external_exports.object({
34674
+ destination: external_exports.literal("local"),
34675
+ taskId: external_exports.string(),
34676
+ completedAt: external_exports.string().optional(),
34677
+ archived: external_exports.boolean()
34678
+ }),
34679
+ external_exports.object({
34680
+ destination: external_exports.literal("github"),
34681
+ taskId: external_exports.string(),
34682
+ issueNumber: external_exports.number().optional(),
34683
+ issueUrl: external_exports.string().optional()
34684
+ }),
34685
+ external_exports.object({
34686
+ destination: external_exports.literal("linear"),
34687
+ taskId: external_exports.string(),
34688
+ issueId: external_exports.string().optional(),
34689
+ issueUrl: external_exports.string().optional()
34690
+ })
34691
+ ]);
34692
+ var briefOutputSchema = external_exports.object({
34693
+ agent: external_exports.string(),
34694
+ mode: external_exports.enum(["full", "delta"]),
34695
+ generatedAt: external_exports.string(),
34696
+ lastBriefAt: external_exports.string().nullable(),
34697
+ peek: external_exports.boolean(),
34698
+ lanes: external_exports.array(external_exports.object({
34699
+ id: external_exports.string(),
34700
+ label: external_exports.string(),
34701
+ items: external_exports.array(external_exports.object({
34702
+ taskId: external_exports.string().optional(),
34703
+ text: external_exports.string(),
34704
+ why: external_exports.string(),
34705
+ at: external_exports.string().optional()
34706
+ }))
34707
+ }))
34708
+ });
34709
+
34370
34710
  // src/mcp/tools/list_tasks_tool.ts
34371
34711
  function registerListTasksTool(server, defaultFile) {
34372
34712
  server.registerTool(
@@ -34379,7 +34719,8 @@ function registerListTasksTool(server, defaultFile) {
34379
34719
  column: external_exports.string().optional().describe("Filter by column ID or name"),
34380
34720
  tag: external_exports.string().optional().describe("Filter by tag"),
34381
34721
  type: external_exports.string().optional().describe("Filter by document type (e.g., epic, adr, plan). Only returns tasks matching this type.")
34382
- })
34722
+ }),
34723
+ outputSchema: listTasksOutputSchema
34383
34724
  },
34384
34725
  async ({ file: file2, column, tag, type: filterType }) => {
34385
34726
  const filePath = file2 || defaultFile;
@@ -34399,10 +34740,14 @@ function registerListTasksTool(server, defaultFile) {
34399
34740
  const taskType = task.type || "task";
34400
34741
  if (taskType !== filterType) continue;
34401
34742
  }
34402
- tasks.push({ id: task.id, title: task.title, column: col.title, priority: task.priority, tags: task.tags, assignee: task.assignee });
34743
+ tasks.push({ id: task.id, title: task.title, column: col.id, priority: task.priority, tags: task.tags, assignee: task.assignee });
34403
34744
  }
34404
34745
  }
34405
- return { content: [{ type: "text", text: JSON.stringify({ tasks, count: tasks.length }, null, 2) }] };
34746
+ const output = { tasks, count: tasks.length };
34747
+ return {
34748
+ content: [{ type: "text", text: JSON.stringify(output, null, 2) }],
34749
+ structuredContent: output
34750
+ };
34406
34751
  }
34407
34752
  );
34408
34753
  }
@@ -34417,7 +34762,8 @@ function registerGetTaskTool(server, defaultFile) {
34417
34762
  inputSchema: external_exports.object({
34418
34763
  file: external_exports.string().optional().describe("Path to brainfile.md (default: brainfile.md)"),
34419
34764
  task: external_exports.string().describe("Task ID to retrieve")
34420
- })
34765
+ }),
34766
+ outputSchema: getTaskOutputSchema
34421
34767
  },
34422
34768
  async ({ file: file2, task }) => {
34423
34769
  const filePath = file2 || defaultFile;
@@ -34436,7 +34782,10 @@ function registerGetTaskTool(server, defaultFile) {
34436
34782
  column: isLog ? "Completed" : doc.task.column || "unknown",
34437
34783
  ...isLog && { archived: true }
34438
34784
  };
34439
- return { content: [{ type: "text", text: JSON.stringify(output, null, 2) }] };
34785
+ return {
34786
+ content: [{ type: "text", text: JSON.stringify(output, null, 2) }],
34787
+ structuredContent: output
34788
+ };
34440
34789
  }
34441
34790
  );
34442
34791
  }
@@ -34457,7 +34806,8 @@ function registerSearchTool(server, defaultFile) {
34457
34806
  assignee: external_exports.string().optional().describe("Filter by assignee"),
34458
34807
  recent: external_exports.boolean().optional().describe("List recently completed tasks (v2 only)"),
34459
34808
  task: external_exports.string().optional().describe("View a specific task/log entry (v2 only)")
34460
- })
34809
+ }),
34810
+ outputSchema: searchOutputSchema
34461
34811
  },
34462
34812
  async ({ file: file2, query, column, priority, assignee, recent, task }) => {
34463
34813
  const filePath = file2 || defaultFile;
@@ -34469,7 +34819,7 @@ function registerSearchTool(server, defaultFile) {
34469
34819
  if (!found) {
34470
34820
  return { content: [{ type: "text", text: `Error: Task not found: ${task}` }], isError: true };
34471
34821
  }
34472
- const output = {
34822
+ const output2 = {
34473
34823
  id: found.doc.task.id,
34474
34824
  title: found.doc.task.title,
34475
34825
  completedAt: found.doc.task.completedAt,
@@ -34477,7 +34827,10 @@ function registerSearchTool(server, defaultFile) {
34477
34827
  description: (0, import_core.extractDescription)(found.doc.body),
34478
34828
  log: (0, import_core.extractLog)(found.doc.body)
34479
34829
  };
34480
- return { content: [{ type: "text", text: JSON.stringify(output, null, 2) }] };
34830
+ return {
34831
+ content: [{ type: "text", text: JSON.stringify(output2, null, 2) }],
34832
+ structuredContent: output2
34833
+ };
34481
34834
  }
34482
34835
  if (recent) {
34483
34836
  const dirs2 = (0, import_core.getV2Dirs)(filePath);
@@ -34488,7 +34841,11 @@ function registerSearchTool(server, defaultFile) {
34488
34841
  title: doc.task.title,
34489
34842
  completedAt: doc.task.completedAt
34490
34843
  }));
34491
- return { content: [{ type: "text", text: JSON.stringify({ logs, count: logs.length }, null, 2) }] };
34844
+ const output2 = { logs, count: logs.length };
34845
+ return {
34846
+ content: [{ type: "text", text: JSON.stringify(output2, null, 2) }],
34847
+ structuredContent: output2
34848
+ };
34492
34849
  }
34493
34850
  if (!query) {
34494
34851
  return { content: [{ type: "text", text: "Error: query is required unless recent or task is provided" }], isError: true };
@@ -34508,7 +34865,11 @@ function registerSearchTool(server, defaultFile) {
34508
34865
  }
34509
34866
  }
34510
34867
  matches.sort((a, b) => b.score - a.score);
34511
- return { content: [{ type: "text", text: JSON.stringify({ results: matches, count: matches.length }, null, 2) }] };
34868
+ const output = { results: matches, count: matches.length };
34869
+ return {
34870
+ content: [{ type: "text", text: JSON.stringify(output, null, 2) }],
34871
+ structuredContent: output
34872
+ };
34512
34873
  }
34513
34874
  );
34514
34875
  }
@@ -34546,7 +34907,8 @@ function registerTaskAddTool(server, defaultFile) {
34546
34907
  // Aliases (some clients prefer camelCase)
34547
34908
  withContract: external_exports.boolean().optional().describe("Alias of with_contract"),
34548
34909
  validationCommands: external_exports.array(external_exports.string()).optional().describe("Alias of validation_commands")
34549
- })
34910
+ }),
34911
+ outputSchema: taskAddOutputSchema
34550
34912
  },
34551
34913
  async ({
34552
34914
  file: file2,
@@ -34625,7 +34987,10 @@ function registerTaskAddTool(server, defaultFile) {
34625
34987
  if (!result.success || !result.task) {
34626
34988
  return { content: [{ type: "text", text: `Error: ${result.error || "Failed to add task"}` }], isError: true };
34627
34989
  }
34628
- return { content: [{ type: "text", text: `Task added successfully: ${result.task.id} - ${title}` }] };
34990
+ return {
34991
+ content: [{ type: "text", text: `Task added successfully: ${result.task.id} - ${title}` }],
34992
+ structuredContent: { id: result.task.id, title }
34993
+ };
34629
34994
  } catch (e) {
34630
34995
  return { content: [{ type: "text", text: `Error: ${e.message}` }], isError: true };
34631
34996
  }
@@ -34647,7 +35012,8 @@ function registerTaskMoveTool(server, defaultFile) {
34647
35012
  taskId: external_exports.union([external_exports.string(), external_exports.array(external_exports.string())]).optional().describe("Task ID or array of task IDs to move"),
34648
35013
  task: external_exports.string().optional().describe("Alias of taskId for single task move"),
34649
35014
  column: external_exports.string().describe("Target column ID or name")
34650
- })
35015
+ }),
35016
+ outputSchema: taskMoveOutputSchema
34651
35017
  },
34652
35018
  async ({ file: file2, taskId, task, column }) => {
34653
35019
  const filePath = file2 || defaultFile;
@@ -34693,22 +35059,27 @@ function registerTaskMoveTool(server, defaultFile) {
34693
35059
  }
34694
35060
  results.push({ taskId: id, success: true, message, warning: warning?.warning });
34695
35061
  }
35062
+ const successCount = results.filter((r) => r.success).length;
35063
+ const failureCount = results.length - successCount;
35064
+ const output = { success: failureCount === 0, successCount, failureCount, results };
34696
35065
  if (!isBatch) {
34697
35066
  const single = results[0];
34698
35067
  if (!single?.success) {
34699
- return { content: [{ type: "text", text: `Error: ${single?.error || "Move failed"}` }], isError: true };
35068
+ return {
35069
+ content: [{ type: "text", text: `Error: ${single?.error || "Move failed"}` }],
35070
+ structuredContent: output,
35071
+ isError: true
35072
+ };
34700
35073
  }
34701
35074
  let text = single.message || `Task ${taskIds[0]} moved`;
34702
35075
  if (single.warning) text += `
34703
35076
 
34704
35077
  ${single.warning}`;
34705
- return { content: [{ type: "text", text }] };
35078
+ return { content: [{ type: "text", text }], structuredContent: output };
34706
35079
  }
34707
- const successCount = results.filter((r) => r.success).length;
34708
- const failureCount = results.length - successCount;
34709
- const output = { success: failureCount === 0, successCount, failureCount, results };
34710
35080
  return {
34711
35081
  content: [{ type: "text", text: JSON.stringify(output, null, 2) }],
35082
+ structuredContent: output,
34712
35083
  isError: failureCount > 0 && successCount === 0
34713
35084
  };
34714
35085
  }
@@ -34717,7 +35088,7 @@ ${single.warning}`;
34717
35088
 
34718
35089
  // src/mcp/tools/task_patch_tool.ts
34719
35090
  var import_core36 = __toESM(require_dist());
34720
- import * as path15 from "path";
35091
+ import * as path16 from "path";
34721
35092
  function registerTaskPatchTool(server, defaultFile) {
34722
35093
  server.registerTool(
34723
35094
  "task_patch",
@@ -34736,7 +35107,8 @@ function registerTaskPatchTool(server, defaultFile) {
34736
35107
  dueDate: external_exports.string().nullable().optional().describe("New due date (null to remove)"),
34737
35108
  relatedFiles: external_exports.array(external_exports.string()).nullable().optional().describe("Related file paths (null to remove)"),
34738
35109
  parentId: external_exports.string().nullable().optional().describe("Parent task ID (null to remove)")
34739
- })
35110
+ }),
35111
+ outputSchema: taskPatchOutputSchema
34740
35112
  },
34741
35113
  async ({ file: file2, taskId, task, title, description, priority, tags, assignee, dueDate, relatedFiles, parentId }) => {
34742
35114
  const filePath = file2 || defaultFile;
@@ -34752,7 +35124,7 @@ function registerTaskPatchTool(server, defaultFile) {
34752
35124
  const dirs = (0, import_core.getV2Dirs)(filePath);
34753
35125
  const results = [];
34754
35126
  for (const id of taskIds) {
34755
- const taskPath = path15.join(dirs.boardDir, (0, import_core36.taskFileName)(id));
35127
+ const taskPath = path16.join(dirs.boardDir, (0, import_core36.taskFileName)(id));
34756
35128
  const doc = (0, import_core36.readTaskFile)(taskPath);
34757
35129
  if (!doc) {
34758
35130
  results.push({ taskId: id, success: false, error: "Task not found" });
@@ -34775,18 +35147,26 @@ function registerTaskPatchTool(server, defaultFile) {
34775
35147
  }
34776
35148
  results.push({ taskId: id, success: true });
34777
35149
  }
35150
+ const successCount = results.filter((r) => r.success).length;
35151
+ const failureCount = results.length - successCount;
35152
+ const output = { success: failureCount === 0, successCount, failureCount, results };
34778
35153
  if (!isBatch) {
34779
35154
  const single = results[0];
34780
35155
  if (!single?.success) {
34781
- return { content: [{ type: "text", text: `Error: ${single?.error || "Task not found"}` }], isError: true };
35156
+ return {
35157
+ content: [{ type: "text", text: `Error: ${single?.error || "Task not found"}` }],
35158
+ structuredContent: output,
35159
+ isError: true
35160
+ };
34782
35161
  }
34783
- return { content: [{ type: "text", text: `Task ${taskIds[0]} updated successfully` }] };
35162
+ return {
35163
+ content: [{ type: "text", text: `Task ${taskIds[0]} updated successfully` }],
35164
+ structuredContent: output
35165
+ };
34784
35166
  }
34785
- const successCount = results.filter((r) => r.success).length;
34786
- const failureCount = results.length - successCount;
34787
- const output = { success: failureCount === 0, successCount, failureCount, results };
34788
35167
  return {
34789
35168
  content: [{ type: "text", text: JSON.stringify(output, null, 2) }],
35169
+ structuredContent: output,
34790
35170
  isError: failureCount > 0 && successCount === 0
34791
35171
  };
34792
35172
  }
@@ -34794,7 +35174,7 @@ function registerTaskPatchTool(server, defaultFile) {
34794
35174
  }
34795
35175
 
34796
35176
  // src/mcp/tools/task_delete_tool.ts
34797
- import * as fs12 from "fs";
35177
+ import * as fs13 from "fs";
34798
35178
  function registerTaskDeleteTool(server, defaultFile) {
34799
35179
  server.registerTool(
34800
35180
  "task_delete",
@@ -34804,7 +35184,8 @@ function registerTaskDeleteTool(server, defaultFile) {
34804
35184
  inputSchema: external_exports.object({
34805
35185
  file: external_exports.string().optional().describe("Path to brainfile.md (default: brainfile.md)"),
34806
35186
  task: external_exports.string().describe("Task ID to delete")
34807
- })
35187
+ }),
35188
+ outputSchema: taskDeleteOutputSchema
34808
35189
  },
34809
35190
  async ({ file: file2, task }) => {
34810
35191
  const filePath = file2 || defaultFile;
@@ -34816,8 +35197,11 @@ function registerTaskDeleteTool(server, defaultFile) {
34816
35197
  return { content: [{ type: "text", text: `Error: Task not found: ${task}` }], isError: true };
34817
35198
  }
34818
35199
  try {
34819
- fs12.unlinkSync(found.filePath);
34820
- return { content: [{ type: "text", text: `Task ${task} deleted successfully` }] };
35200
+ fs13.unlinkSync(found.filePath);
35201
+ return {
35202
+ content: [{ type: "text", text: `Task ${task} deleted successfully` }],
35203
+ structuredContent: { id: task, deleted: true }
35204
+ };
34821
35205
  } catch (e) {
34822
35206
  return { content: [{ type: "text", text: `Error: ${e.message}` }], isError: true };
34823
35207
  }
@@ -34843,7 +35227,8 @@ function registerSubtaskTool(server, defaultFile) {
34843
35227
  titles: external_exports.array(external_exports.string()).optional().describe("Optional titles for batch update action"),
34844
35228
  completed: external_exports.boolean().optional().describe("For toggle action: set explicit completed state (true/false) instead of flipping"),
34845
35229
  all: external_exports.boolean().optional().describe("For toggle/delete action: target all subtasks in the task")
34846
- })
35230
+ }),
35231
+ outputSchema: subtaskOutputSchema
34847
35232
  },
34848
35233
  async ({ action, file: file2, task, subtask, subtasks, title, titles, completed, all }) => {
34849
35234
  const filePath = file2 || defaultFile;
@@ -34879,10 +35264,17 @@ function registerSubtaskTool(server, defaultFile) {
34879
35264
  return { content: [{ type: "text", text: `Error: ${result.error || "Failed to add subtasks"}` }], isError: true };
34880
35265
  }
34881
35266
  const added = result.affected.map((st) => ({ id: st.id, title: st.title }));
35267
+ const output = { action: "add", added, count: added.length };
34882
35268
  if (added.length === 1) {
34883
- return { content: [{ type: "text", text: `Subtask added: ${added[0].id} - ${added[0].title}` }] };
35269
+ return {
35270
+ content: [{ type: "text", text: `Subtask added: ${added[0].id} - ${added[0].title}` }],
35271
+ structuredContent: output
35272
+ };
34884
35273
  }
34885
- return { content: [{ type: "text", text: JSON.stringify({ added, count: added.length }, null, 2) }] };
35274
+ return {
35275
+ content: [{ type: "text", text: JSON.stringify(output, null, 2) }],
35276
+ structuredContent: output
35277
+ };
34886
35278
  }
34887
35279
  if (action === "delete") {
34888
35280
  const dirs = (0, import_core.getV2Dirs)(filePath);
@@ -34903,10 +35295,17 @@ function registerSubtaskTool(server, defaultFile) {
34903
35295
  }
34904
35296
  const deleted = result.affected.map((st) => st.id);
34905
35297
  const missing = result.missing ?? [];
35298
+ const output = { action: "delete", deleted, missing, count: deleted.length };
34906
35299
  if (!useAll && deleted.length === 1 && missing.length === 0) {
34907
- return { content: [{ type: "text", text: `Subtask ${deleted[0]} deleted successfully` }] };
35300
+ return {
35301
+ content: [{ type: "text", text: `Subtask ${deleted[0]} deleted successfully` }],
35302
+ structuredContent: output
35303
+ };
34908
35304
  }
34909
- return { content: [{ type: "text", text: JSON.stringify({ deleted, missing, count: deleted.length }, null, 2) }] };
35305
+ return {
35306
+ content: [{ type: "text", text: JSON.stringify(output, null, 2) }],
35307
+ structuredContent: output
35308
+ };
34910
35309
  }
34911
35310
  if (action === "toggle") {
34912
35311
  const dirs = (0, import_core.getV2Dirs)(filePath);
@@ -34926,11 +35325,18 @@ function registerSubtaskTool(server, defaultFile) {
34926
35325
  return { content: [{ type: "text", text: `Error: ${result.error || "Failed to toggle subtasks"}` }], isError: true };
34927
35326
  }
34928
35327
  const updated = result.affected.map((st) => ({ id: st.id, completed: st.completed }));
35328
+ const output = { action: "toggle", updated, count: updated.length };
34929
35329
  if (!useAll && updated.length === 1) {
34930
35330
  const status = updated[0].completed ? "completed" : "incomplete";
34931
- return { content: [{ type: "text", text: `Subtask ${updated[0].id} marked as ${status}` }] };
35331
+ return {
35332
+ content: [{ type: "text", text: `Subtask ${updated[0].id} marked as ${status}` }],
35333
+ structuredContent: output
35334
+ };
34932
35335
  }
34933
- return { content: [{ type: "text", text: JSON.stringify({ updated, count: updated.length }, null, 2) }] };
35336
+ return {
35337
+ content: [{ type: "text", text: JSON.stringify(output, null, 2) }],
35338
+ structuredContent: output
35339
+ };
34934
35340
  }
34935
35341
  if (action === "update") {
34936
35342
  const targetIds = listParam;
@@ -34958,10 +35364,17 @@ function registerSubtaskTool(server, defaultFile) {
34958
35364
  return { content: [{ type: "text", text: `Error: ${result.error || "Failed to update subtasks"}` }], isError: true };
34959
35365
  }
34960
35366
  const updated = result.affected.map((st) => ({ id: st.id, title: st.title }));
35367
+ const output = { action: "update", updated, count: updated.length };
34961
35368
  if (updated.length === 1) {
34962
- return { content: [{ type: "text", text: `Subtask ${updated[0].id} updated to "${updated[0].title}"` }] };
35369
+ return {
35370
+ content: [{ type: "text", text: `Subtask ${updated[0].id} updated to "${updated[0].title}"` }],
35371
+ structuredContent: output
35372
+ };
34963
35373
  }
34964
- return { content: [{ type: "text", text: JSON.stringify({ updated, count: updated.length }, null, 2) }] };
35374
+ return {
35375
+ content: [{ type: "text", text: JSON.stringify(output, null, 2) }],
35376
+ structuredContent: output
35377
+ };
34965
35378
  }
34966
35379
  return { content: [{ type: "text", text: `Error: Unknown action: ${action}` }], isError: true };
34967
35380
  }
@@ -34973,8 +35386,8 @@ var import_core41 = __toESM(require_dist());
34973
35386
 
34974
35387
  // src/lib/contractRunner.ts
34975
35388
  var import_core38 = __toESM(require_dist());
34976
- import * as fs13 from "fs";
34977
- import * as path16 from "path";
35389
+ import * as fs14 from "fs";
35390
+ import * as path17 from "path";
34978
35391
  import { spawnSync } from "child_process";
34979
35392
  function detectDirectoryChangeWarning(command) {
34980
35393
  const patterns = [
@@ -34993,7 +35406,7 @@ function detectDirectoryChangeWarning(command) {
34993
35406
  return void 0;
34994
35407
  }
34995
35408
  function requireV2Runner(resolvedFilePath) {
34996
- if (!fs13.existsSync(resolvedFilePath)) {
35409
+ if (!fs14.existsSync(resolvedFilePath)) {
34997
35410
  return { error: `File not found: ${resolvedFilePath}` };
34998
35411
  }
34999
35412
  if (!(0, import_core.isV2)(resolvedFilePath)) {
@@ -35137,9 +35550,9 @@ function validateContractV2(ctx, resolvedFilePath) {
35137
35550
  if (!taskDoc) return { error: `Failed to read task file: ${taskPath}` };
35138
35551
  if (!taskDoc.task.contract) return { error: `Task ${ctx.taskId} has no contract` };
35139
35552
  const contract = taskDoc.task.contract;
35140
- const brainfileAbs = path16.resolve(resolvedFilePath);
35141
- const brainfileDir = path16.dirname(brainfileAbs);
35142
- const baseDir = path16.basename(brainfileDir) === ".brainfile" ? path16.dirname(brainfileDir) : brainfileDir;
35553
+ const brainfileAbs = path17.resolve(resolvedFilePath);
35554
+ const brainfileDir = path17.dirname(brainfileAbs);
35555
+ const baseDir = path17.basename(brainfileDir) === ".brainfile" ? path17.dirname(brainfileDir) : brainfileDir;
35143
35556
  const deliverables = contract.deliverables ?? [];
35144
35557
  const deliverableChecks = [];
35145
35558
  for (const d of deliverables) {
@@ -35152,8 +35565,8 @@ function validateContractV2(ctx, resolvedFilePath) {
35152
35565
  deliverableChecks.push({ deliverable: d, ok: false, error: normalized.error });
35153
35566
  continue;
35154
35567
  }
35155
- const resolved = path16.isAbsolute(normalized.value) ? normalized.value : path16.join(baseDir, normalized.value);
35156
- if (!fs13.existsSync(resolved)) {
35568
+ const resolved = path17.isAbsolute(normalized.value) ? normalized.value : path17.join(baseDir, normalized.value);
35569
+ if (!fs14.existsSync(resolved)) {
35157
35570
  deliverableChecks.push({ deliverable: d, ok: false, resolvedPath: resolved, error: "File not found" });
35158
35571
  } else {
35159
35572
  deliverableChecks.push({ deliverable: d, ok: true, resolvedPath: resolved });
@@ -35621,9 +36034,9 @@ function contractActivateCommand(options, logger = defaultLogger) {
35621
36034
  // src/mcp/tools/contract.ts
35622
36035
  function toDeliverableSpec(deliverable) {
35623
36036
  const type2 = deliverable.type.trim();
35624
- const path20 = deliverable.path.trim();
36037
+ const path21 = deliverable.path.trim();
35625
36038
  const description = deliverable.description?.trim();
35626
- return description ? `${type2}:${path20}:${description}` : `${type2}:${path20}`;
36039
+ return description ? `${type2}:${path21}:${description}` : `${type2}:${path21}`;
35627
36040
  }
35628
36041
  function executeContractGraphMcpAction(options) {
35629
36042
  const logger = new MemoryLogger();
@@ -35683,7 +36096,8 @@ function registerContractTool(server, defaultFile) {
35683
36096
  dependsOn: external_exports.array(external_exports.string()).optional()
35684
36097
  })).optional().describe("graph only: array of contract graph task specs"),
35685
36098
  activate: external_exports.boolean().optional().describe("graph only: when true, attached contracts start in ready instead of draft")
35686
- })
36099
+ }),
36100
+ outputSchema: contractOutputSchema
35687
36101
  },
35688
36102
  async ({ action, file: file2, task, parentId, ready: attachReady, deliverables, validation_commands, constraints, tasks, activate }) => {
35689
36103
  const filePath = file2 || defaultFile;
@@ -35708,7 +36122,11 @@ function registerContractTool(server, defaultFile) {
35708
36122
  if (!result.success || !result.task) {
35709
36123
  return { content: [{ type: "text", text: `Error: ${result.error || "Failed to attach contract"}` }], isError: true };
35710
36124
  }
35711
- return { content: [{ type: "text", text: `Contract attached (${result.task.contract.status}): ${task}` }] };
36125
+ const status = result.task.contract.status;
36126
+ return {
36127
+ content: [{ type: "text", text: `Contract attached (${status}): ${task}` }],
36128
+ structuredContent: { action: "attach", task, status }
36129
+ };
35712
36130
  } catch (e) {
35713
36131
  return { content: [{ type: "text", text: `Error: ${e.message}` }], isError: true };
35714
36132
  }
@@ -35721,7 +36139,10 @@ function registerContractTool(server, defaultFile) {
35721
36139
  if ("error" in result) {
35722
36140
  return { content: [{ type: "text", text: `Error: ${result.error}` }], isError: true };
35723
36141
  }
35724
- return { content: [{ type: "text", text: result.markdown }] };
36142
+ return {
36143
+ content: [{ type: "text", text: result.markdown }],
36144
+ structuredContent: { action: "pickup", task, markdown: result.markdown }
36145
+ };
35725
36146
  }
35726
36147
  if (action === "deliver") {
35727
36148
  if (!task) {
@@ -35731,7 +36152,10 @@ function registerContractTool(server, defaultFile) {
35731
36152
  if ("error" in result) {
35732
36153
  return { content: [{ type: "text", text: `Error: ${result.error}` }], isError: true };
35733
36154
  }
35734
- return { content: [{ type: "text", text: `Contract delivered: ${task}` }] };
36155
+ return {
36156
+ content: [{ type: "text", text: `Contract delivered: ${task}` }],
36157
+ structuredContent: { action: "deliver", task }
36158
+ };
35735
36159
  }
35736
36160
  if (action === "validate") {
35737
36161
  if (!task) {
@@ -35742,6 +36166,7 @@ function registerContractTool(server, defaultFile) {
35742
36166
  return { content: [{ type: "text", text: `Error: ${result.error}` }], isError: true };
35743
36167
  }
35744
36168
  const output = {
36169
+ action: "validate",
35745
36170
  ok: result.ok,
35746
36171
  status: result.ok ? "done" : "failed",
35747
36172
  deliverables: result.deliverableChecks,
@@ -35750,7 +36175,7 @@ function registerContractTool(server, defaultFile) {
35750
36175
  };
35751
36176
  return {
35752
36177
  content: [{ type: "text", text: JSON.stringify(output, null, 2) }],
35753
- isError: !result.ok
36178
+ structuredContent: output
35754
36179
  };
35755
36180
  }
35756
36181
  if (action === "graph") {
@@ -35763,13 +36188,16 @@ function registerContractTool(server, defaultFile) {
35763
36188
  tasks,
35764
36189
  activate
35765
36190
  });
36191
+ const output = {
36192
+ action: "graph",
36193
+ attached: result.attached,
36194
+ count: result.count,
36195
+ order: result.order,
36196
+ graph: result.graph
36197
+ };
35766
36198
  return {
35767
- content: [{ type: "text", text: JSON.stringify({
35768
- attached: result.attached,
35769
- count: result.count,
35770
- order: result.order,
35771
- graph: result.graph
35772
- }, null, 2) }]
36199
+ content: [{ type: "text", text: JSON.stringify(output, null, 2) }],
36200
+ structuredContent: output
35773
36201
  };
35774
36202
  } catch (error48) {
35775
36203
  return {
@@ -35799,8 +36227,11 @@ function registerContractTool(server, defaultFile) {
35799
36227
  } else {
35800
36228
  activated.push(...(0, import_core41.activateTaskContractsByParent)(dirs.boardDir, parentId).activated);
35801
36229
  }
35802
- const output = { activated, count: activated.length };
35803
- return { content: [{ type: "text", text: JSON.stringify(output, null, 2) }] };
36230
+ const output = { action: "activate", activated, count: activated.length };
36231
+ return {
36232
+ content: [{ type: "text", text: JSON.stringify(output, null, 2) }],
36233
+ structuredContent: output
36234
+ };
35804
36235
  }
35805
36236
  return { content: [{ type: "text", text: `Error: Unknown action: ${action}` }], isError: true };
35806
36237
  }
@@ -35809,7 +36240,7 @@ function registerContractTool(server, defaultFile) {
35809
36240
 
35810
36241
  // src/mcp/tools/task_complete_tool.ts
35811
36242
  var import_core42 = __toESM(require_dist());
35812
- import * as fs14 from "fs";
36243
+ import * as fs15 from "fs";
35813
36244
  function registerTaskCompleteTool(server, defaultFile) {
35814
36245
  server.registerTool(
35815
36246
  "task_complete",
@@ -35820,7 +36251,8 @@ function registerTaskCompleteTool(server, defaultFile) {
35820
36251
  file: external_exports.string().optional().describe("Path to brainfile.md (default: brainfile.md)"),
35821
36252
  task: external_exports.string().describe("Task ID to complete"),
35822
36253
  destination: external_exports.enum(["local", "github", "linear"]).optional().describe("Optional archive destination. If omitted, performs normal completion flow.")
35823
- })
36254
+ }),
36255
+ outputSchema: taskCompleteOutputSchema
35824
36256
  },
35825
36257
  async ({ file: file2, task, destination }) => {
35826
36258
  const filePath = file2 || defaultFile;
@@ -35834,7 +36266,13 @@ function registerTaskCompleteTool(server, defaultFile) {
35834
36266
  }, info: () => {
35835
36267
  } });
35836
36268
  return {
35837
- content: [{ type: "text", text: `Task ${task} completed at ${result.completedAt}` }]
36269
+ content: [{ type: "text", text: `Task ${task} completed at ${result.completedAt}` }],
36270
+ structuredContent: {
36271
+ destination: "local",
36272
+ taskId: task,
36273
+ completedAt: result.completedAt,
36274
+ archived: false
36275
+ }
35838
36276
  };
35839
36277
  }
35840
36278
  if (destination === "local") {
@@ -35843,7 +36281,7 @@ function registerTaskCompleteTool(server, defaultFile) {
35843
36281
  if (!found2) {
35844
36282
  return { content: [{ type: "text", text: `Error: Task not found: ${task}` }], isError: true };
35845
36283
  }
35846
- const result = (0, import_core42.completeTaskFile)(found2.filePath, dirs2.logsDir, { legacyMode: true });
36284
+ const result = (0, import_core42.completeTaskFile)(found2.filePath, dirs2.logsDir, {});
35847
36285
  if (!result.success) {
35848
36286
  const detail = result.incompleteChildren?.length ? `
35849
36287
  Incomplete children:
@@ -35853,7 +36291,10 @@ ${result.incompleteChildren.map((c) => ` - ${c.id}: ${c.title}`).join("\n")}` :
35853
36291
  isError: true
35854
36292
  };
35855
36293
  }
35856
- return { content: [{ type: "text", text: `Task ${task} archived to logs/` }] };
36294
+ return {
36295
+ content: [{ type: "text", text: `Task ${task} archived to logs/` }],
36296
+ structuredContent: { destination: "local", taskId: task, archived: true }
36297
+ };
35857
36298
  }
35858
36299
  const dirs = (0, import_core.getV2Dirs)(filePath);
35859
36300
  const found = (0, import_core.findV2Task)(dirs, task);
@@ -35888,10 +36329,18 @@ ${result.incompleteChildren.map((c) => ` - ${c.id}: ${c.title}`).join("\n")}` :
35888
36329
  if (!ghResult.success) {
35889
36330
  return { content: [{ type: "text", text: `Error creating GitHub issue: ${ghResult.error}` }], isError: true };
35890
36331
  }
35891
- fs14.unlinkSync(found.filePath);
35892
- return { content: [{ type: "text", text: `Task ${task} archived to GitHub Issue #${ghResult.issueNumber} (closed)
36332
+ fs15.unlinkSync(found.filePath);
36333
+ return {
36334
+ content: [{ type: "text", text: `Task ${task} archived to GitHub Issue #${ghResult.issueNumber} (closed)
35893
36335
 
35894
- View: ${ghResult.issueUrl}` }] };
36336
+ View: ${ghResult.issueUrl}` }],
36337
+ structuredContent: {
36338
+ destination: "github",
36339
+ taskId: task,
36340
+ issueNumber: ghResult.issueNumber,
36341
+ issueUrl: ghResult.issueUrl
36342
+ }
36343
+ };
35895
36344
  }
35896
36345
  if (!await isLinearAuthenticated()) {
35897
36346
  return { content: [{ type: "text", text: "Error: Not authenticated with Linear." }], isError: true };
@@ -35932,10 +36381,18 @@ ${teamList}` }], isError: true };
35932
36381
  if (!linearResult.success) {
35933
36382
  return { content: [{ type: "text", text: `Error creating Linear issue: ${linearResult.error}` }], isError: true };
35934
36383
  }
35935
- fs14.unlinkSync(found.filePath);
35936
- return { content: [{ type: "text", text: `Task ${task} archived to Linear Issue ${linearResult.issueId} (Done)
36384
+ fs15.unlinkSync(found.filePath);
36385
+ return {
36386
+ content: [{ type: "text", text: `Task ${task} archived to Linear Issue ${linearResult.issueId} (Done)
35937
36387
 
35938
- View: ${linearResult.issueUrl}` }] };
36388
+ View: ${linearResult.issueUrl}` }],
36389
+ structuredContent: {
36390
+ destination: "linear",
36391
+ taskId: task,
36392
+ issueId: linearResult.issueId,
36393
+ issueUrl: linearResult.issueUrl
36394
+ }
36395
+ };
35939
36396
  } catch (e) {
35940
36397
  return { content: [{ type: "text", text: `Error: ${e.message}` }], isError: true };
35941
36398
  }
@@ -35943,8 +36400,69 @@ View: ${linearResult.issueUrl}` }] };
35943
36400
  );
35944
36401
  }
35945
36402
 
36403
+ // src/mcp/tools/brief_tool.ts
36404
+ var import_core43 = __toESM(require_dist());
36405
+ function registerBriefTool(server, defaultFile) {
36406
+ server.registerTool(
36407
+ "brief",
36408
+ {
36409
+ title: "Agent Brief",
36410
+ description: "Get a per-agent brief: what changed on the board since this agent last checked in. First call returns a full orientation; later calls return only what is new.",
36411
+ inputSchema: external_exports.object({
36412
+ file: external_exports.string().optional().describe("Path to brainfile.md (default: brainfile.md)"),
36413
+ agent: external_exports.string().describe("Agent identifier (required \u2014 brief state is per-agent)"),
36414
+ peek: external_exports.boolean().optional().describe("Read the brief without marking it as seen")
36415
+ }),
36416
+ outputSchema: briefOutputSchema
36417
+ },
36418
+ async ({ file: file2, agent, peek }) => {
36419
+ const filePath = file2 || defaultFile;
36420
+ const guard = requireV2(filePath);
36421
+ if (guard) return guard;
36422
+ const agentName = agent?.trim();
36423
+ if (!agentName) {
36424
+ return {
36425
+ content: [{ type: "text", text: "Error: agent is required" }],
36426
+ isError: true
36427
+ };
36428
+ }
36429
+ const resolvedPath = resolveBrainfile(filePath);
36430
+ const dirs = (0, import_core.getV2Dirs)(resolvedPath);
36431
+ const isPeek = peek === true;
36432
+ const state = (0, import_core43.readBriefState)(resolvedPath, agentName);
36433
+ const result = (0, import_core43.buildBrief)(dirs, agentName, { lastBriefAt: state.lastBriefAt });
36434
+ if (!isPeek) {
36435
+ (0, import_core43.writeBriefState)(resolvedPath, agentName, result.generatedAt);
36436
+ }
36437
+ const output = { ...result, peek: isPeek };
36438
+ return {
36439
+ content: [{ type: "text", text: JSON.stringify(output, null, 2) }],
36440
+ structuredContent: output
36441
+ };
36442
+ }
36443
+ );
36444
+ }
36445
+
35946
36446
  // src/commands/mcp.ts
35947
36447
  var cliVersion = require_package().version;
36448
+ function createBrainfileMcpServer(defaultFile) {
36449
+ const server = new McpServer({
36450
+ name: "brainfile",
36451
+ version: cliVersion
36452
+ });
36453
+ registerListTasksTool(server, defaultFile);
36454
+ registerGetTaskTool(server, defaultFile);
36455
+ registerSearchTool(server, defaultFile);
36456
+ registerTaskAddTool(server, defaultFile);
36457
+ registerTaskMoveTool(server, defaultFile);
36458
+ registerTaskPatchTool(server, defaultFile);
36459
+ registerTaskDeleteTool(server, defaultFile);
36460
+ registerSubtaskTool(server, defaultFile);
36461
+ registerContractTool(server, defaultFile);
36462
+ registerTaskCompleteTool(server, defaultFile);
36463
+ registerBriefTool(server, defaultFile);
36464
+ return server;
36465
+ }
35948
36466
  async function mcpCommand(options) {
35949
36467
  let defaultFile = options.file;
35950
36468
  if (defaultFile === "brainfile.md") {
@@ -35952,13 +36470,13 @@ async function mcpCommand(options) {
35952
36470
  if (workspacePaths) {
35953
36471
  const paths = workspacePaths.split(":").filter(Boolean);
35954
36472
  for (const wsPath of paths) {
35955
- const found = (0, import_core43.findBrainfile)(wsPath);
36473
+ const found = (0, import_core44.findBrainfile)(wsPath);
35956
36474
  if (found) {
35957
36475
  defaultFile = found.absolutePath;
35958
36476
  console.error(`[brainfile-mcp] Found in workspace: ${defaultFile}`);
35959
36477
  break;
35960
36478
  }
35961
- const discovered = (0, import_core43.findNearestBrainfile)(wsPath);
36479
+ const discovered = (0, import_core44.findNearestBrainfile)(wsPath);
35962
36480
  if (discovered) {
35963
36481
  defaultFile = discovered.absolutePath;
35964
36482
  console.error(`[brainfile-mcp] Discovered in workspace: ${defaultFile}`);
@@ -35969,12 +36487,12 @@ async function mcpCommand(options) {
35969
36487
  if (defaultFile === "brainfile.md") {
35970
36488
  const gitRoot = findGitRoot(process.cwd());
35971
36489
  if (gitRoot) {
35972
- const found = (0, import_core43.findBrainfile)(gitRoot);
36490
+ const found = (0, import_core44.findBrainfile)(gitRoot);
35973
36491
  if (found) {
35974
36492
  defaultFile = found.absolutePath;
35975
36493
  console.error(`[brainfile-mcp] Found from git root: ${defaultFile}`);
35976
36494
  } else {
35977
- const discovered = (0, import_core43.findNearestBrainfile)(gitRoot);
36495
+ const discovered = (0, import_core44.findNearestBrainfile)(gitRoot);
35978
36496
  if (discovered) {
35979
36497
  defaultFile = discovered.absolutePath;
35980
36498
  console.error(`[brainfile-mcp] Discovered from git root: ${defaultFile}`);
@@ -35983,46 +36501,30 @@ async function mcpCommand(options) {
35983
36501
  }
35984
36502
  }
35985
36503
  if (defaultFile === "brainfile.md") {
35986
- const found = (0, import_core43.findBrainfile)();
36504
+ const found = (0, import_core44.findBrainfile)();
35987
36505
  if (found) {
35988
36506
  defaultFile = found.absolutePath;
35989
36507
  console.error(`[brainfile-mcp] Auto-discovered: ${defaultFile}`);
35990
36508
  } else {
35991
- defaultFile = (0, import_core43.resolveBrainfilePath)({ filePath: "brainfile.md", startDir: process.cwd() });
36509
+ defaultFile = (0, import_core44.resolveBrainfilePath)({ filePath: "brainfile.md", startDir: process.cwd() });
35992
36510
  console.error(`[brainfile-mcp] No brainfile found, using: ${defaultFile}`);
35993
36511
  }
35994
36512
  }
35995
36513
  } else {
35996
- defaultFile = (0, import_core43.resolveBrainfilePath)({ filePath: defaultFile, startDir: process.cwd() });
36514
+ defaultFile = (0, import_core44.resolveBrainfilePath)({ filePath: defaultFile, startDir: process.cwd() });
35997
36515
  console.error(`[brainfile-mcp] Using specified file: ${defaultFile}`);
35998
36516
  }
35999
36517
  assertV2Brainfile(defaultFile);
36000
- serveStdio(() => {
36001
- const server = new McpServer({
36002
- name: "brainfile",
36003
- version: cliVersion
36004
- });
36005
- registerListTasksTool(server, defaultFile);
36006
- registerGetTaskTool(server, defaultFile);
36007
- registerSearchTool(server, defaultFile);
36008
- registerTaskAddTool(server, defaultFile);
36009
- registerTaskMoveTool(server, defaultFile);
36010
- registerTaskPatchTool(server, defaultFile);
36011
- registerTaskDeleteTool(server, defaultFile);
36012
- registerSubtaskTool(server, defaultFile);
36013
- registerContractTool(server, defaultFile);
36014
- registerTaskCompleteTool(server, defaultFile);
36015
- return server;
36016
- });
36518
+ serveStdio(() => createBrainfileMcpServer(defaultFile));
36017
36519
  }
36018
36520
 
36019
36521
  // src/commands/hooks.ts
36020
- var import_chalk16 = __toESM(require_source());
36522
+ var import_chalk17 = __toESM(require_source());
36021
36523
  import * as readline2 from "readline";
36022
36524
 
36023
36525
  // src/utils/hook-settings.ts
36024
- import * as fs15 from "fs";
36025
- import * as path17 from "path";
36526
+ import * as fs16 from "fs";
36527
+ import * as path18 from "path";
36026
36528
  import * as os from "os";
36027
36529
  function resolveHomeDir() {
36028
36530
  const home = process.env.HOME?.trim();
@@ -36033,44 +36535,44 @@ function getSettingsPath(tool, scope) {
36033
36535
  if (scope === "user") {
36034
36536
  switch (tool) {
36035
36537
  case "claude-code":
36036
- return path17.join(homeDir, ".claude", "settings.json");
36538
+ return path18.join(homeDir, ".claude", "settings.json");
36037
36539
  case "cursor":
36038
- return path17.join(homeDir, ".cursor", "hooks.json");
36540
+ return path18.join(homeDir, ".cursor", "hooks.json");
36039
36541
  case "cline":
36040
- return path17.join(homeDir, "Documents", "Cline", "Rules", "Hooks");
36542
+ return path18.join(homeDir, "Documents", "Cline", "Rules", "Hooks");
36041
36543
  }
36042
36544
  } else {
36043
36545
  switch (tool) {
36044
36546
  case "claude-code":
36045
- return path17.join(process.cwd(), ".claude", "settings.json");
36547
+ return path18.join(process.cwd(), ".claude", "settings.json");
36046
36548
  case "cursor":
36047
- return path17.join(process.cwd(), ".cursor", "hooks.json");
36549
+ return path18.join(process.cwd(), ".cursor", "hooks.json");
36048
36550
  case "cline":
36049
- return path17.join(process.cwd(), ".clinerules", "hooks");
36551
+ return path18.join(process.cwd(), ".clinerules", "hooks");
36050
36552
  }
36051
36553
  }
36052
36554
  }
36053
36555
  function readToolSettings(tool, scope) {
36054
36556
  const settingsPath = getSettingsPath(tool, scope);
36055
36557
  if (tool === "cline") {
36056
- if (!fs15.existsSync(settingsPath)) {
36558
+ if (!fs16.existsSync(settingsPath)) {
36057
36559
  return { hooks: {} };
36058
36560
  }
36059
36561
  const hooks = {};
36060
36562
  const hookFiles = ["PostToolUse", "UserPromptSubmit", "TaskStart"];
36061
36563
  for (const hookFile of hookFiles) {
36062
- const hookPath = path17.join(settingsPath, hookFile);
36063
- if (fs15.existsSync(hookPath)) {
36564
+ const hookPath = path18.join(settingsPath, hookFile);
36565
+ if (fs16.existsSync(hookPath)) {
36064
36566
  hooks[hookFile] = true;
36065
36567
  }
36066
36568
  }
36067
36569
  return { hooks };
36068
36570
  }
36069
- if (!fs15.existsSync(settingsPath)) {
36571
+ if (!fs16.existsSync(settingsPath)) {
36070
36572
  return tool === "cursor" ? { version: 1, hooks: {} } : {};
36071
36573
  }
36072
36574
  try {
36073
- const content = fs15.readFileSync(settingsPath, "utf-8");
36575
+ const content = fs16.readFileSync(settingsPath, "utf-8");
36074
36576
  return JSON.parse(content);
36075
36577
  } catch {
36076
36578
  return tool === "cursor" ? { version: 1, hooks: {} } : {};
@@ -36078,11 +36580,11 @@ function readToolSettings(tool, scope) {
36078
36580
  }
36079
36581
  function writeToolSettings(tool, scope, settings) {
36080
36582
  const settingsPath = getSettingsPath(tool, scope);
36081
- const settingsDir = path17.dirname(settingsPath);
36082
- if (!fs15.existsSync(settingsDir)) {
36083
- fs15.mkdirSync(settingsDir, { recursive: true });
36583
+ const settingsDir = path18.dirname(settingsPath);
36584
+ if (!fs16.existsSync(settingsDir)) {
36585
+ fs16.mkdirSync(settingsDir, { recursive: true });
36084
36586
  }
36085
- fs15.writeFileSync(settingsPath, JSON.stringify(settings, null, 2), "utf-8");
36587
+ fs16.writeFileSync(settingsPath, JSON.stringify(settings, null, 2), "utf-8");
36086
36588
  }
36087
36589
  function createClineHookScript(hookType) {
36088
36590
  const commandMap = {
@@ -36132,8 +36634,8 @@ function installBrainfileHooks(tool, scope) {
36132
36634
  const settings = readToolSettings(tool, scope);
36133
36635
  if (tool === "cline") {
36134
36636
  const hooksDir = getSettingsPath(tool, scope);
36135
- if (!fs15.existsSync(hooksDir)) {
36136
- fs15.mkdirSync(hooksDir, { recursive: true });
36637
+ if (!fs16.existsSync(hooksDir)) {
36638
+ fs16.mkdirSync(hooksDir, { recursive: true });
36137
36639
  }
36138
36640
  const hookTypes = [
36139
36641
  "PostToolUse",
@@ -36141,9 +36643,9 @@ function installBrainfileHooks(tool, scope) {
36141
36643
  "TaskStart"
36142
36644
  ];
36143
36645
  for (const hookType of hookTypes) {
36144
- const hookPath = path17.join(hooksDir, hookType);
36646
+ const hookPath = path18.join(hooksDir, hookType);
36145
36647
  const scriptContent = createClineHookScript(hookType);
36146
- fs15.writeFileSync(hookPath, scriptContent, { mode: 493 });
36648
+ fs16.writeFileSync(hookPath, scriptContent, { mode: 493 });
36147
36649
  }
36148
36650
  return;
36149
36651
  }
@@ -36233,16 +36735,16 @@ function installBrainfileHooks(tool, scope) {
36233
36735
  function uninstallBrainfileHooks(tool, scope) {
36234
36736
  if (tool === "cline") {
36235
36737
  const hooksDir = getSettingsPath(tool, scope);
36236
- if (!fs15.existsSync(hooksDir)) {
36738
+ if (!fs16.existsSync(hooksDir)) {
36237
36739
  return;
36238
36740
  }
36239
36741
  const hookFiles = ["PostToolUse", "UserPromptSubmit", "TaskStart"];
36240
36742
  for (const hookFile of hookFiles) {
36241
- const hookPath = path17.join(hooksDir, hookFile);
36242
- if (fs15.existsSync(hookPath)) {
36243
- const content = fs15.readFileSync(hookPath, "utf-8");
36743
+ const hookPath = path18.join(hooksDir, hookFile);
36744
+ if (fs16.existsSync(hookPath)) {
36745
+ const content = fs16.readFileSync(hookPath, "utf-8");
36244
36746
  if (content.includes("brainfile hooks")) {
36245
- fs15.unlinkSync(hookPath);
36747
+ fs16.unlinkSync(hookPath);
36246
36748
  }
36247
36749
  }
36248
36750
  }
@@ -36289,15 +36791,15 @@ function uninstallBrainfileHooks(tool, scope) {
36289
36791
  }
36290
36792
  function areBrainfileHooksInstalled(tool, scope) {
36291
36793
  const settingsPath = getSettingsPath(tool, scope);
36292
- if (!fs15.existsSync(settingsPath)) {
36794
+ if (!fs16.existsSync(settingsPath)) {
36293
36795
  return false;
36294
36796
  }
36295
36797
  if (tool === "cline") {
36296
36798
  const hookFiles = ["PostToolUse", "UserPromptSubmit", "TaskStart"];
36297
36799
  for (const hookFile of hookFiles) {
36298
- const hookPath = path17.join(settingsPath, hookFile);
36299
- if (fs15.existsSync(hookPath)) {
36300
- const content = fs15.readFileSync(hookPath, "utf-8");
36800
+ const hookPath = path18.join(settingsPath, hookFile);
36801
+ if (fs16.existsSync(hookPath)) {
36802
+ const content = fs16.readFileSync(hookPath, "utf-8");
36301
36803
  if (content.includes("brainfile hooks")) {
36302
36804
  return true;
36303
36805
  }
@@ -36336,16 +36838,16 @@ function areBrainfileHooksInstalled(tool, scope) {
36336
36838
  }
36337
36839
 
36338
36840
  // src/utils/brainfile-finder.ts
36339
- var import_core44 = __toESM(require_dist());
36340
- import * as fs16 from "fs";
36841
+ var import_core45 = __toESM(require_dist());
36842
+ import * as fs17 from "fs";
36341
36843
  function findBrainfile2() {
36342
- const found = (0, import_core44.findBrainfile)(process.cwd());
36844
+ const found = (0, import_core45.findBrainfile)(process.cwd());
36343
36845
  if (found && found.kind !== "dotdir") return null;
36344
36846
  return found?.absolutePath ?? null;
36345
36847
  }
36346
36848
  function getFileAgeMinutes(filePath) {
36347
36849
  try {
36348
- const stats = fs16.statSync(filePath);
36850
+ const stats = fs17.statSync(filePath);
36349
36851
  const now = Date.now();
36350
36852
  const modifiedTime = stats.mtimeMs;
36351
36853
  const ageMs = now - modifiedTime;
@@ -36526,8 +37028,8 @@ async function sessionStartCommand(logger = defaultLogger, stdin) {
36526
37028
  function installCommand(options, logger = defaultLogger) {
36527
37029
  const tool = options.tool;
36528
37030
  if (tool !== "claude-code" && tool !== "cursor" && tool !== "cline") {
36529
- logger.error(import_chalk16.default.red(`Error: Unknown tool '${options.tool}'`));
36530
- logger.log(import_chalk16.default.gray("Supported tools: claude-code, cursor, cline"));
37031
+ logger.error(import_chalk17.default.red(`Error: Unknown tool '${options.tool}'`));
37032
+ logger.log(import_chalk17.default.gray("Supported tools: claude-code, cursor, cline"));
36531
37033
  throw new CLIError(`Unknown tool '${options.tool}'`, ExitCode.USER_ERROR);
36532
37034
  }
36533
37035
  try {
@@ -36540,35 +37042,35 @@ function installCommand(options, logger = defaultLogger) {
36540
37042
  }
36541
37043
  const settingsPath = getSettingsPath(tool, options.scope);
36542
37044
  const toolName = tool === "claude-code" ? "Claude Code" : tool === "cursor" ? "Cursor" : "Cline";
36543
- logger.log(import_chalk16.default.green(`\u2705 Brainfile hooks installed for ${toolName}!`));
36544
- logger.log(import_chalk16.default.gray(` Settings: ${settingsPath}`));
36545
- logger.log(import_chalk16.default.gray(` Scope: ${options.scope}`));
37045
+ logger.log(import_chalk17.default.green(`\u2705 Brainfile hooks installed for ${toolName}!`));
37046
+ logger.log(import_chalk17.default.gray(` Settings: ${settingsPath}`));
37047
+ logger.log(import_chalk17.default.gray(` Scope: ${options.scope}`));
36546
37048
  logger.log("");
36547
- logger.log(import_chalk16.default.white("Hooks configured:"));
37049
+ logger.log(import_chalk17.default.white("Hooks configured:"));
36548
37050
  if (tool === "claude-code") {
36549
- logger.log(import_chalk16.default.gray(" \u2022 PostToolUse \u2192 brainfile hooks after-edit"));
36550
- logger.log(import_chalk16.default.gray(" \u2022 UserPromptSubmit \u2192 brainfile hooks before-prompt"));
36551
- logger.log(import_chalk16.default.gray(" \u2022 SessionStart \u2192 brainfile hooks session-start"));
37051
+ logger.log(import_chalk17.default.gray(" \u2022 PostToolUse \u2192 brainfile hooks after-edit"));
37052
+ logger.log(import_chalk17.default.gray(" \u2022 UserPromptSubmit \u2192 brainfile hooks before-prompt"));
37053
+ logger.log(import_chalk17.default.gray(" \u2022 SessionStart \u2192 brainfile hooks session-start"));
36552
37054
  } else if (tool === "cursor") {
36553
- logger.log(import_chalk16.default.gray(" \u2022 afterFileEdit \u2192 brainfile hooks after-edit"));
36554
- logger.log(import_chalk16.default.gray(" \u2022 beforeSubmitPrompt \u2192 brainfile hooks before-prompt"));
36555
- logger.log(import_chalk16.default.gray(" \u2022 stop \u2192 brainfile hooks session-start"));
37055
+ logger.log(import_chalk17.default.gray(" \u2022 afterFileEdit \u2192 brainfile hooks after-edit"));
37056
+ logger.log(import_chalk17.default.gray(" \u2022 beforeSubmitPrompt \u2192 brainfile hooks before-prompt"));
37057
+ logger.log(import_chalk17.default.gray(" \u2022 stop \u2192 brainfile hooks session-start"));
36556
37058
  } else if (tool === "cline") {
36557
- logger.log(import_chalk16.default.gray(" \u2022 PostToolUse \u2192 brainfile hooks after-edit"));
36558
- logger.log(import_chalk16.default.gray(" \u2022 UserPromptSubmit \u2192 brainfile hooks before-prompt"));
36559
- logger.log(import_chalk16.default.gray(" \u2022 TaskStart \u2192 brainfile hooks session-start"));
37059
+ logger.log(import_chalk17.default.gray(" \u2022 PostToolUse \u2192 brainfile hooks after-edit"));
37060
+ logger.log(import_chalk17.default.gray(" \u2022 UserPromptSubmit \u2192 brainfile hooks before-prompt"));
37061
+ logger.log(import_chalk17.default.gray(" \u2022 TaskStart \u2192 brainfile hooks session-start"));
36560
37062
  }
36561
37063
  logger.log("");
36562
- logger.log(import_chalk16.default.gray("Next steps:"));
36563
- logger.log(import_chalk16.default.gray(` 1. Restart ${toolName} to activate hooks`));
36564
- logger.log(import_chalk16.default.gray(" 2. Edit files and watch for brainfile reminders"));
36565
- logger.log(import_chalk16.default.gray(" 3. Run 'brainfile hooks list' to verify installation"));
37064
+ logger.log(import_chalk17.default.gray("Next steps:"));
37065
+ logger.log(import_chalk17.default.gray(` 1. Restart ${toolName} to activate hooks`));
37066
+ logger.log(import_chalk17.default.gray(" 2. Edit files and watch for brainfile reminders"));
37067
+ logger.log(import_chalk17.default.gray(" 3. Run 'brainfile hooks list' to verify installation"));
36566
37068
  }
36567
37069
  function uninstallCommand(options, logger = defaultLogger) {
36568
37070
  const tool = options.tool;
36569
37071
  if (tool !== "claude-code" && tool !== "cursor" && tool !== "cline") {
36570
- logger.error(import_chalk16.default.red(`Error: Unknown tool '${options.tool}'`));
36571
- logger.log(import_chalk16.default.gray("Supported tools: claude-code, cursor, cline"));
37072
+ logger.error(import_chalk17.default.red(`Error: Unknown tool '${options.tool}'`));
37073
+ logger.log(import_chalk17.default.gray("Supported tools: claude-code, cursor, cline"));
36572
37074
  throw new CLIError(`Unknown tool '${options.tool}'`, ExitCode.USER_ERROR);
36573
37075
  }
36574
37076
  const scopes = options.scope === "all" ? ["user", "project"] : [options.scope];
@@ -36579,69 +37081,69 @@ function uninstallCommand(options, logger = defaultLogger) {
36579
37081
  uninstallBrainfileHooks(tool, scope);
36580
37082
  removed = true;
36581
37083
  const settingsPath = getSettingsPath(tool, scope);
36582
- logger.log(import_chalk16.default.green(`\u2713 Removed brainfile hooks from ${scope} settings`));
36583
- logger.log(import_chalk16.default.gray(` ${settingsPath}`));
37084
+ logger.log(import_chalk17.default.green(`\u2713 Removed brainfile hooks from ${scope} settings`));
37085
+ logger.log(import_chalk17.default.gray(` ${settingsPath}`));
36584
37086
  } catch (error48) {
36585
37087
  logger.warn(`Failed to remove hooks from ${scope}: ${error48 instanceof Error ? error48.message : String(error48)}`);
36586
37088
  }
36587
37089
  }
36588
37090
  }
36589
37091
  if (!removed) {
36590
- logger.log(import_chalk16.default.yellow("No brainfile hooks found to remove"));
37092
+ logger.log(import_chalk17.default.yellow("No brainfile hooks found to remove"));
36591
37093
  }
36592
37094
  }
36593
37095
  function listCommand2(options, logger = defaultLogger) {
36594
37096
  const tools = options.tool ? [options.tool] : ["claude-code", "cursor", "cline"];
36595
- logger.log(import_chalk16.default.bold.white("\nBrainfile Hooks Status\n"));
37097
+ logger.log(import_chalk17.default.bold.white("\nBrainfile Hooks Status\n"));
36596
37098
  for (const tool of tools) {
36597
37099
  const toolName = tool === "claude-code" ? "Claude Code" : tool === "cursor" ? "Cursor" : "Cline";
36598
- logger.log(import_chalk16.default.cyan(toolName + ":"));
37100
+ logger.log(import_chalk17.default.cyan(toolName + ":"));
36599
37101
  try {
36600
37102
  const userInstalled = areBrainfileHooksInstalled(tool, "user");
36601
37103
  const userPath = getSettingsPath(tool, "user");
36602
- logger.log(import_chalk16.default.white(` User scope (${userPath}):`));
37104
+ logger.log(import_chalk17.default.white(` User scope (${userPath}):`));
36603
37105
  if (userInstalled) {
36604
37106
  if (tool === "cline") {
36605
- logger.log(import_chalk16.default.green(" \u2713 PostToolUse \u2192 brainfile hooks after-edit"));
36606
- logger.log(import_chalk16.default.green(" \u2713 UserPromptSubmit \u2192 brainfile hooks before-prompt"));
36607
- logger.log(import_chalk16.default.green(" \u2713 TaskStart \u2192 brainfile hooks session-start"));
37107
+ logger.log(import_chalk17.default.green(" \u2713 PostToolUse \u2192 brainfile hooks after-edit"));
37108
+ logger.log(import_chalk17.default.green(" \u2713 UserPromptSubmit \u2192 brainfile hooks before-prompt"));
37109
+ logger.log(import_chalk17.default.green(" \u2713 TaskStart \u2192 brainfile hooks session-start"));
36608
37110
  } else {
36609
- logger.log(import_chalk16.default.green(" \u2713 PostToolUse/afterFileEdit \u2192 brainfile hooks after-edit"));
36610
- logger.log(import_chalk16.default.green(" \u2713 UserPromptSubmit/beforeSubmitPrompt \u2192 brainfile hooks before-prompt"));
36611
- logger.log(import_chalk16.default.green(" \u2713 SessionStart/stop \u2192 brainfile hooks session-start"));
37111
+ logger.log(import_chalk17.default.green(" \u2713 PostToolUse/afterFileEdit \u2192 brainfile hooks after-edit"));
37112
+ logger.log(import_chalk17.default.green(" \u2713 UserPromptSubmit/beforeSubmitPrompt \u2192 brainfile hooks before-prompt"));
37113
+ logger.log(import_chalk17.default.green(" \u2713 SessionStart/stop \u2192 brainfile hooks session-start"));
36612
37114
  }
36613
37115
  } else {
36614
- logger.log(import_chalk16.default.gray(" \u2717 Not installed"));
37116
+ logger.log(import_chalk17.default.gray(" \u2717 Not installed"));
36615
37117
  }
36616
37118
  } catch (error48) {
36617
- logger.log(import_chalk16.default.gray(` Error checking user scope: ${error48}`));
37119
+ logger.log(import_chalk17.default.gray(` Error checking user scope: ${error48}`));
36618
37120
  }
36619
37121
  try {
36620
37122
  const projectInstalled = areBrainfileHooksInstalled(tool, "project");
36621
37123
  const projectPath = getSettingsPath(tool, "project");
36622
- logger.log(import_chalk16.default.white(` Project scope (${projectPath}):`));
37124
+ logger.log(import_chalk17.default.white(` Project scope (${projectPath}):`));
36623
37125
  if (projectInstalled) {
36624
37126
  if (tool === "cline") {
36625
- logger.log(import_chalk16.default.green(" \u2713 PostToolUse \u2192 brainfile hooks after-edit"));
36626
- logger.log(import_chalk16.default.green(" \u2713 UserPromptSubmit \u2192 brainfile hooks before-prompt"));
36627
- logger.log(import_chalk16.default.green(" \u2713 TaskStart \u2192 brainfile hooks session-start"));
37127
+ logger.log(import_chalk17.default.green(" \u2713 PostToolUse \u2192 brainfile hooks after-edit"));
37128
+ logger.log(import_chalk17.default.green(" \u2713 UserPromptSubmit \u2192 brainfile hooks before-prompt"));
37129
+ logger.log(import_chalk17.default.green(" \u2713 TaskStart \u2192 brainfile hooks session-start"));
36628
37130
  } else {
36629
- logger.log(import_chalk16.default.green(" \u2713 PostToolUse/afterFileEdit \u2192 brainfile hooks after-edit"));
36630
- logger.log(import_chalk16.default.green(" \u2713 UserPromptSubmit/beforeSubmitPrompt \u2192 brainfile hooks before-prompt"));
36631
- logger.log(import_chalk16.default.green(" \u2713 SessionStart/stop \u2192 brainfile hooks session-start"));
37131
+ logger.log(import_chalk17.default.green(" \u2713 PostToolUse/afterFileEdit \u2192 brainfile hooks after-edit"));
37132
+ logger.log(import_chalk17.default.green(" \u2713 UserPromptSubmit/beforeSubmitPrompt \u2192 brainfile hooks before-prompt"));
37133
+ logger.log(import_chalk17.default.green(" \u2713 SessionStart/stop \u2192 brainfile hooks session-start"));
36632
37134
  }
36633
37135
  } else {
36634
- logger.log(import_chalk16.default.gray(" \u2717 Not installed"));
37136
+ logger.log(import_chalk17.default.gray(" \u2717 Not installed"));
36635
37137
  }
36636
37138
  } catch (error48) {
36637
- logger.log(import_chalk16.default.gray(` Error checking project scope: ${error48}`));
37139
+ logger.log(import_chalk17.default.gray(` Error checking project scope: ${error48}`));
36638
37140
  }
36639
37141
  logger.log("");
36640
37142
  }
36641
37143
  }
36642
37144
 
36643
37145
  // src/commands/config.ts
36644
- var import_chalk17 = __toESM(require_source());
37146
+ var import_chalk18 = __toESM(require_source());
36645
37147
  function configCommand(action, options) {
36646
37148
  switch (action) {
36647
37149
  case "list":
@@ -36649,18 +37151,18 @@ function configCommand(action, options) {
36649
37151
  break;
36650
37152
  case "get":
36651
37153
  if (!options.key) {
36652
- console.log(import_chalk17.default.red("Error:") + " Missing key. Usage: brainfile config get <key>");
37154
+ console.log(import_chalk18.default.red("Error:") + " Missing key. Usage: brainfile config get <key>");
36653
37155
  process.exit(1);
36654
37156
  }
36655
37157
  getConfigValue(options.key);
36656
37158
  break;
36657
37159
  case "set":
36658
37160
  if (!options.key) {
36659
- console.log(import_chalk17.default.red("Error:") + " Missing key. Usage: brainfile config set <key> <value>");
37161
+ console.log(import_chalk18.default.red("Error:") + " Missing key. Usage: brainfile config set <key> <value>");
36660
37162
  process.exit(1);
36661
37163
  }
36662
37164
  if (options.value === void 0) {
36663
- console.log(import_chalk17.default.red("Error:") + " Missing value. Usage: brainfile config set <key> <value>");
37165
+ console.log(import_chalk18.default.red("Error:") + " Missing value. Usage: brainfile config set <key> <value>");
36664
37166
  process.exit(1);
36665
37167
  }
36666
37168
  setConfigValue(options.key, options.value);
@@ -36669,7 +37171,7 @@ function configCommand(action, options) {
36669
37171
  showConfigPath();
36670
37172
  break;
36671
37173
  default:
36672
- console.log(import_chalk17.default.red("Error:") + ` Unknown action: ${action}`);
37174
+ console.log(import_chalk18.default.red("Error:") + ` Unknown action: ${action}`);
36673
37175
  console.log("");
36674
37176
  console.log("Available actions:");
36675
37177
  console.log(" list Show all config values");
@@ -36682,29 +37184,29 @@ function configCommand(action, options) {
36682
37184
  function listConfig() {
36683
37185
  const config2 = loadConfig();
36684
37186
  if (Object.keys(config2).length === 0) {
36685
- console.log(import_chalk17.default.gray("No configuration set."));
37187
+ console.log(import_chalk18.default.gray("No configuration set."));
36686
37188
  console.log("");
36687
- console.log("Use " + import_chalk17.default.cyan("brainfile config set <key> <value>") + " to set values.");
37189
+ console.log("Use " + import_chalk18.default.cyan("brainfile config set <key> <value>") + " to set values.");
36688
37190
  console.log("");
36689
37191
  showAvailableKeys();
36690
37192
  return;
36691
37193
  }
36692
- console.log(import_chalk17.default.bold("Brainfile Configuration"));
37194
+ console.log(import_chalk18.default.bold("Brainfile Configuration"));
36693
37195
  console.log("");
36694
37196
  const flattened = flattenConfig(config2);
36695
37197
  const maxKeyLen = Math.max(...Object.keys(flattened).map((k) => k.length));
36696
37198
  for (const [key, value] of Object.entries(flattened)) {
36697
37199
  const displayValue = Array.isArray(value) ? value.join(", ") : String(value);
36698
- console.log(` ${import_chalk17.default.cyan(key.padEnd(maxKeyLen))} ${displayValue}`);
37200
+ console.log(` ${import_chalk18.default.cyan(key.padEnd(maxKeyLen))} ${displayValue}`);
36699
37201
  }
36700
37202
  console.log("");
36701
- console.log(import_chalk17.default.gray(`Config file: ${getConfigPath()}`));
37203
+ console.log(import_chalk18.default.gray(`Config file: ${getConfigPath()}`));
36702
37204
  }
36703
37205
  function getConfigValue(key) {
36704
37206
  const config2 = loadConfig();
36705
37207
  const value = getNestedValue(config2, key);
36706
37208
  if (value === void 0) {
36707
- console.log(import_chalk17.default.gray("(not set)"));
37209
+ console.log(import_chalk18.default.gray("(not set)"));
36708
37210
  return;
36709
37211
  }
36710
37212
  if (Array.isArray(value)) {
@@ -36718,7 +37220,7 @@ function getConfigValue(key) {
36718
37220
  function setConfigValue(key, value) {
36719
37221
  const config2 = loadConfig();
36720
37222
  if (!isValidKey(key)) {
36721
- console.log(import_chalk17.default.red("Error:") + ` Invalid key: ${key}`);
37223
+ console.log(import_chalk18.default.red("Error:") + ` Invalid key: ${key}`);
36722
37224
  console.log("");
36723
37225
  showAvailableKeys();
36724
37226
  process.exit(1);
@@ -36726,7 +37228,7 @@ function setConfigValue(key, value) {
36726
37228
  const parsedValue = parseValue(key, value);
36727
37229
  setNestedValue(config2, key, parsedValue);
36728
37230
  saveConfig(config2);
36729
- console.log(import_chalk17.default.green("") + ` Set ${import_chalk17.default.cyan(key)} = ${formatValue(parsedValue)}`);
37231
+ console.log(import_chalk18.default.green("") + ` Set ${import_chalk18.default.cyan(key)} = ${formatValue(parsedValue)}`);
36730
37232
  }
36731
37233
  function showConfigPath() {
36732
37234
  console.log(getConfigPath());
@@ -36744,12 +37246,12 @@ function isValidKey(key) {
36744
37246
  }
36745
37247
  function showAvailableKeys() {
36746
37248
  console.log("Available keys:");
36747
- console.log(" " + import_chalk17.default.cyan("archive.default") + " Default archive destination (local, github, linear)");
36748
- console.log(" " + import_chalk17.default.cyan("archive.github.owner") + " GitHub repository owner");
36749
- console.log(" " + import_chalk17.default.cyan("archive.github.repo") + " GitHub repository name");
36750
- console.log(" " + import_chalk17.default.cyan("archive.github.labels") + " Comma-separated labels for GitHub issues");
36751
- console.log(" " + import_chalk17.default.cyan("archive.linear.teamId") + " Linear team ID");
36752
- console.log(" " + import_chalk17.default.cyan("archive.linear.projectId") + " Linear project ID");
37249
+ console.log(" " + import_chalk18.default.cyan("archive.default") + " Default archive destination (local, github, linear)");
37250
+ console.log(" " + import_chalk18.default.cyan("archive.github.owner") + " GitHub repository owner");
37251
+ console.log(" " + import_chalk18.default.cyan("archive.github.repo") + " GitHub repository name");
37252
+ console.log(" " + import_chalk18.default.cyan("archive.github.labels") + " Comma-separated labels for GitHub issues");
37253
+ console.log(" " + import_chalk18.default.cyan("archive.linear.teamId") + " Linear team ID");
37254
+ console.log(" " + import_chalk18.default.cyan("archive.linear.projectId") + " Linear project ID");
36753
37255
  }
36754
37256
  function parseValue(key, value) {
36755
37257
  if (key === "archive.github.labels") {
@@ -36758,7 +37260,7 @@ function parseValue(key, value) {
36758
37260
  if (key === "archive.default") {
36759
37261
  const valid = ["local", "github", "linear"];
36760
37262
  if (!valid.includes(value)) {
36761
- console.log(import_chalk17.default.red("Error:") + ` Invalid value for archive.default. Must be one of: ${valid.join(", ")}`);
37263
+ console.log(import_chalk18.default.red("Error:") + ` Invalid value for archive.default. Must be one of: ${valid.join(", ")}`);
36762
37264
  process.exit(1);
36763
37265
  }
36764
37266
  }
@@ -36770,8 +37272,8 @@ function formatValue(value) {
36770
37272
  }
36771
37273
  return value;
36772
37274
  }
36773
- function getNestedValue(obj, path20) {
36774
- const parts = path20.split(".");
37275
+ function getNestedValue(obj, path21) {
37276
+ const parts = path21.split(".");
36775
37277
  let current = obj;
36776
37278
  for (const part of parts) {
36777
37279
  if (current === void 0 || current === null) {
@@ -36781,8 +37283,8 @@ function getNestedValue(obj, path20) {
36781
37283
  }
36782
37284
  return current;
36783
37285
  }
36784
- function setNestedValue(obj, path20, value) {
36785
- const parts = path20.split(".");
37286
+ function setNestedValue(obj, path21, value) {
37287
+ const parts = path21.split(".");
36786
37288
  let current = obj;
36787
37289
  for (let i = 0; i < parts.length - 1; i++) {
36788
37290
  const part = parts[i];
@@ -36807,21 +37309,21 @@ function flattenConfig(obj, prefix = "") {
36807
37309
  }
36808
37310
 
36809
37311
  // src/commands/schema.ts
36810
- var import_chalk18 = __toESM(require_source());
36811
- import * as fs17 from "fs";
36812
- import * as path18 from "path";
37312
+ var import_chalk19 = __toESM(require_source());
37313
+ import * as fs18 from "fs";
37314
+ import * as path19 from "path";
36813
37315
  var SCHEMA_BASE_URL = "https://brainfile.md/v1";
36814
37316
  var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
36815
37317
  function getBundledSchemasDir() {
36816
- const distPath = path18.join(__dirname, "..", "schemas");
36817
- const srcPath = path18.join(__dirname, "..", "..", "src", "schemas");
36818
- if (fs17.existsSync(distPath)) {
37318
+ const distPath = path19.join(__dirname, "..", "schemas");
37319
+ const srcPath = path19.join(__dirname, "..", "..", "src", "schemas");
37320
+ if (fs18.existsSync(distPath)) {
36819
37321
  return distPath;
36820
37322
  }
36821
- if (fs17.existsSync(srcPath)) {
37323
+ if (fs18.existsSync(srcPath)) {
36822
37324
  return srcPath;
36823
37325
  }
36824
- return path18.join(__dirname, "schemas");
37326
+ return path19.join(__dirname, "schemas");
36825
37327
  }
36826
37328
  var BUNDLED_SCHEMAS = [
36827
37329
  {
@@ -36873,17 +37375,17 @@ function schemaListCommand(options, logger) {
36873
37375
  logger.log(JSON.stringify(output, null, 2));
36874
37376
  } else {
36875
37377
  logger.log("");
36876
- logger.log(import_chalk18.default.bold("Available schemas:"));
37378
+ logger.log(import_chalk19.default.bold("Available schemas:"));
36877
37379
  logger.log("");
36878
37380
  const maxIdLen = Math.max(...BUNDLED_SCHEMAS.map((s) => s.id.length));
36879
37381
  for (const schema2 of BUNDLED_SCHEMAS) {
36880
37382
  logger.log(
36881
- ` ${import_chalk18.default.cyan(schema2.id.padEnd(maxIdLen))} ${import_chalk18.default.gray("-")} ${schema2.description}`
37383
+ ` ${import_chalk19.default.cyan(schema2.id.padEnd(maxIdLen))} ${import_chalk19.default.gray("-")} ${schema2.description}`
36882
37384
  );
36883
37385
  }
36884
37386
  logger.log("");
36885
- logger.log(import_chalk18.default.gray("Run: brainfile schema <name> to view schema"));
36886
- logger.log(import_chalk18.default.gray("Run: brainfile schema update to check for updates"));
37387
+ logger.log(import_chalk19.default.gray("Run: brainfile schema <name> to view schema"));
37388
+ logger.log(import_chalk19.default.gray("Run: brainfile schema update to check for updates"));
36887
37389
  }
36888
37390
  checkAutoUpdate(logger);
36889
37391
  return {
@@ -36903,23 +37405,23 @@ function schemaShowCommand(name, options, logger) {
36903
37405
  );
36904
37406
  }
36905
37407
  const schemasDir = getBundledSchemasDir();
36906
- const schemaPath = path18.join(schemasDir, schemaInfo.file);
36907
- if (!fs17.existsSync(schemaPath)) {
37408
+ const schemaPath = path19.join(schemasDir, schemaInfo.file);
37409
+ if (!fs18.existsSync(schemaPath)) {
36908
37410
  throw new CLIError(
36909
37411
  `Schema file not found: ${schemaInfo.file}`,
36910
37412
  ExitCode.USER_ERROR,
36911
37413
  `Expected at: ${schemaPath}`
36912
37414
  );
36913
37415
  }
36914
- const content = fs17.readFileSync(schemaPath, "utf-8");
37416
+ const content = fs18.readFileSync(schemaPath, "utf-8");
36915
37417
  const schema2 = JSON.parse(content);
36916
37418
  if (options.json) {
36917
37419
  logger.log(JSON.stringify(schema2, null, 2));
36918
37420
  } else {
36919
37421
  logger.log("");
36920
- logger.log(import_chalk18.default.bold(`Schema: ${name}`));
36921
- logger.log(import_chalk18.default.gray(`Description: ${schemaInfo.description}`));
36922
- logger.log(import_chalk18.default.gray(`Source: ${SCHEMA_BASE_URL}/${schemaInfo.file}`));
37422
+ logger.log(import_chalk19.default.bold(`Schema: ${name}`));
37423
+ logger.log(import_chalk19.default.gray(`Description: ${schemaInfo.description}`));
37424
+ logger.log(import_chalk19.default.gray(`Source: ${SCHEMA_BASE_URL}/${schemaInfo.file}`));
36923
37425
  logger.log("");
36924
37426
  logger.log(JSON.stringify(schema2, null, 2));
36925
37427
  }
@@ -36943,7 +37445,7 @@ function schemaUpdateCommand(options, logger = defaultLogger) {
36943
37445
  void schemaUpdateAsync(options, logger).catch((error48) => {
36944
37446
  const message = error48 instanceof Error ? error48.message : String(error48);
36945
37447
  if (!options.json) {
36946
- logger.log(import_chalk18.default.red(`Schema update failed: ${message}`));
37448
+ logger.log(import_chalk19.default.red(`Schema update failed: ${message}`));
36947
37449
  }
36948
37450
  });
36949
37451
  return {
@@ -36956,14 +37458,14 @@ function schemaUpdateCommand(options, logger = defaultLogger) {
36956
37458
  async function schemaUpdateAsync(options, logger) {
36957
37459
  if (!options.json) {
36958
37460
  logger.log("");
36959
- logger.log(import_chalk18.default.bold("Checking for schema updates..."));
37461
+ logger.log(import_chalk19.default.bold("Checking for schema updates..."));
36960
37462
  logger.log("");
36961
37463
  }
36962
37464
  const schemasDir = getBundledSchemasDir();
36963
37465
  const results = [];
36964
37466
  for (const schemaInfo of BUNDLED_SCHEMAS) {
36965
37467
  const url2 = `${SCHEMA_BASE_URL}/${schemaInfo.file}`;
36966
- const localPath = path18.join(schemasDir, schemaInfo.file);
37468
+ const localPath = path19.join(schemasDir, schemaInfo.file);
36967
37469
  try {
36968
37470
  const response = await fetchWithTimeout(url2, 1e4);
36969
37471
  if (!response.ok) {
@@ -36977,14 +37479,14 @@ async function schemaUpdateAsync(options, logger) {
36977
37479
  const remoteContent = await response.text();
36978
37480
  const remoteSchema = JSON.parse(remoteContent);
36979
37481
  let localSchema = null;
36980
- if (fs17.existsSync(localPath)) {
36981
- const localContent = fs17.readFileSync(localPath, "utf-8");
37482
+ if (fs18.existsSync(localPath)) {
37483
+ const localContent = fs18.readFileSync(localPath, "utf-8");
36982
37484
  localSchema = JSON.parse(localContent);
36983
37485
  }
36984
37486
  const remoteStr = JSON.stringify(remoteSchema);
36985
37487
  const localStr = localSchema ? JSON.stringify(localSchema) : "";
36986
37488
  if (remoteStr !== localStr) {
36987
- fs17.writeFileSync(localPath, JSON.stringify(remoteSchema, null, 2));
37489
+ fs18.writeFileSync(localPath, JSON.stringify(remoteSchema, null, 2));
36988
37490
  results.push({ id: schemaInfo.id, status: "updated" });
36989
37491
  } else {
36990
37492
  results.push({ id: schemaInfo.id, status: "current" });
@@ -37000,21 +37502,21 @@ async function schemaUpdateAsync(options, logger) {
37000
37502
  } else {
37001
37503
  for (const result of results) {
37002
37504
  if (result.status === "updated") {
37003
- logger.log(` ${import_chalk18.default.green("+")} ${import_chalk18.default.cyan(result.id)} - Updated`);
37505
+ logger.log(` ${import_chalk19.default.green("+")} ${import_chalk19.default.cyan(result.id)} - Updated`);
37004
37506
  } else if (result.status === "current") {
37005
- logger.log(` ${import_chalk18.default.gray("=")} ${import_chalk18.default.cyan(result.id)} - Up to date`);
37507
+ logger.log(` ${import_chalk19.default.gray("=")} ${import_chalk19.default.cyan(result.id)} - Up to date`);
37006
37508
  } else {
37007
37509
  logger.log(
37008
- ` ${import_chalk18.default.red("!")} ${import_chalk18.default.cyan(result.id)} - Error: ${result.error}`
37510
+ ` ${import_chalk19.default.red("!")} ${import_chalk19.default.cyan(result.id)} - Error: ${result.error}`
37009
37511
  );
37010
37512
  }
37011
37513
  }
37012
37514
  const updatedCount = results.filter((r) => r.status === "updated").length;
37013
37515
  logger.log("");
37014
37516
  if (updatedCount > 0) {
37015
- logger.log(import_chalk18.default.green(`Updated ${updatedCount} schema(s)`));
37517
+ logger.log(import_chalk19.default.green(`Updated ${updatedCount} schema(s)`));
37016
37518
  } else {
37017
- logger.log(import_chalk18.default.gray("All schemas are up to date"));
37519
+ logger.log(import_chalk19.default.gray("All schemas are up to date"));
37018
37520
  }
37019
37521
  }
37020
37522
  }
@@ -37053,7 +37555,7 @@ async function checkForUpdatesAsync(logger) {
37053
37555
  let hasUpdates = false;
37054
37556
  for (const schemaInfo of BUNDLED_SCHEMAS) {
37055
37557
  const url2 = `${SCHEMA_BASE_URL}/${schemaInfo.file}`;
37056
- const localPath = path18.join(schemasDir, schemaInfo.file);
37558
+ const localPath = path19.join(schemasDir, schemaInfo.file);
37057
37559
  try {
37058
37560
  const response = await fetchWithTimeout(url2, 5e3);
37059
37561
  if (!response.ok) {
@@ -37061,8 +37563,8 @@ async function checkForUpdatesAsync(logger) {
37061
37563
  }
37062
37564
  const remoteContent = await response.text();
37063
37565
  const remoteSchema = JSON.parse(remoteContent);
37064
- if (fs17.existsSync(localPath)) {
37065
- const localContent = fs17.readFileSync(localPath, "utf-8");
37566
+ if (fs18.existsSync(localPath)) {
37567
+ const localContent = fs18.readFileSync(localPath, "utf-8");
37066
37568
  const localSchema = JSON.parse(localContent);
37067
37569
  const remoteStr = JSON.stringify(remoteSchema);
37068
37570
  const localStr = JSON.stringify(localSchema);
@@ -37078,295 +37580,20 @@ async function checkForUpdatesAsync(logger) {
37078
37580
  if (hasUpdates) {
37079
37581
  logger.log("");
37080
37582
  logger.log(
37081
- import_chalk18.default.yellow("Hint: ") + "Schema updates available. Run " + import_chalk18.default.cyan("brainfile schema update") + " to update."
37583
+ import_chalk19.default.yellow("Hint: ") + "Schema updates available. Run " + import_chalk19.default.cyan("brainfile schema update") + " to update."
37082
37584
  );
37083
37585
  }
37084
37586
  }
37085
37587
 
37086
- // src/commands/rules.ts
37087
- var import_chalk19 = __toESM(require_source());
37088
- var import_core45 = __toESM(require_dist());
37089
- import * as fs18 from "fs";
37090
- var VALID_CATEGORIES = ["always", "never", "prefer", "context"];
37091
- var CATEGORY_DESCRIPTIONS = {
37092
- always: "Rules that must always be followed",
37093
- never: "Things that should never be done",
37094
- prefer: "Preferred approaches when applicable",
37095
- context: "Context-specific guidelines"
37096
- };
37097
- var RULES_COMMAND_HELP = `
37098
- Examples:
37099
- brainfile rules # List all rules
37100
- brainfile rules list # List all rules
37101
- brainfile rules list --category always # List only 'always' rules
37102
- brainfile rules add always "Use TypeScript strict mode"
37103
- brainfile rules add never "Commit .env files"
37104
- brainfile rules add prefer "Functional over imperative style"
37105
- brainfile rules add context "For API routes, use Express middleware"
37106
- brainfile rules delete always 1 # Delete rule with id=1 from 'always'
37107
- brainfile rules --json # Output as JSON
37108
-
37109
- Categories:
37110
- always - ${CATEGORY_DESCRIPTIONS.always}
37111
- never - ${CATEGORY_DESCRIPTIONS.never}
37112
- prefer - ${CATEGORY_DESCRIPTIONS.prefer}
37113
- context - ${CATEGORY_DESCRIPTIONS.context}
37114
-
37115
- Notes:
37116
- - Rules are stored in brainfile.md frontmatter under the 'rules' field
37117
- - Each rule has an auto-generated numeric ID within its category
37118
- - Rules provide guidance to AI agents working on the project
37119
- `.trimEnd();
37120
- function validateCategory(category) {
37121
- const lower = category.toLowerCase();
37122
- if (!VALID_CATEGORIES.includes(lower)) {
37123
- throw new CLIError(
37124
- `Invalid category: ${category}`,
37125
- ExitCode.USER_ERROR,
37126
- `Valid categories: ${VALID_CATEGORIES.join(", ")}`
37127
- );
37128
- }
37129
- return lower;
37130
- }
37131
- function readBoardFromFile(filePath) {
37132
- const resolvedPath = resolveCliBrainfilePath(filePath);
37133
- if (!fs18.existsSync(resolvedPath)) {
37134
- throw new CLIError(
37135
- `File not found: ${resolvedPath}`,
37136
- ExitCode.USER_ERROR
37137
- );
37138
- }
37139
- const content = fs18.readFileSync(resolvedPath, "utf-8");
37140
- const result = import_core45.Brainfile.parseWithErrors(content);
37141
- if (!result.board) {
37142
- throw new CLIError(
37143
- "Failed to parse brainfile",
37144
- ExitCode.USER_ERROR,
37145
- result.error
37146
- );
37147
- }
37148
- return { board: result.board, resolvedPath };
37149
- }
37150
- function writeBoard(filePath, board) {
37151
- const content = import_core45.Brainfile.serialize(board);
37152
- fs18.writeFileSync(filePath, content, "utf-8");
37153
- }
37154
- function countRules(rules) {
37155
- if (!rules) return 0;
37156
- return (rules.always?.length || 0) + (rules.never?.length || 0) + (rules.prefer?.length || 0) + (rules.context?.length || 0);
37157
- }
37158
- function rulesListCommand(options, logger = defaultLogger) {
37159
- const { board } = readBoardFromFile(options.file);
37160
- const rules = board.rules || {};
37161
- const totalCount = countRules(rules);
37162
- let displayRules = rules;
37163
- if (options.category) {
37164
- const cat = validateCategory(options.category);
37165
- displayRules = { [cat]: rules[cat] || [] };
37166
- }
37167
- if (options.json) {
37168
- const output = {
37169
- rules: displayRules,
37170
- totalCount: options.category ? displayRules[options.category]?.length || 0 : totalCount
37171
- };
37172
- logger.log(JSON.stringify(output, null, 2));
37173
- } else {
37174
- if (totalCount === 0) {
37175
- logger.log("");
37176
- logger.log(import_chalk19.default.gray("No rules defined."));
37177
- logger.log(import_chalk19.default.gray('Run: brainfile rules add <category> "<rule text>"'));
37178
- } else {
37179
- logger.log("");
37180
- logger.log(import_chalk19.default.bold("Project Rules"));
37181
- logger.log("");
37182
- for (const category of VALID_CATEGORIES) {
37183
- if (options.category && options.category !== category) continue;
37184
- const categoryRules = displayRules[category];
37185
- if (!categoryRules || categoryRules.length === 0) continue;
37186
- const categoryColor = getCategoryColor(category);
37187
- logger.log(categoryColor(`${category.toUpperCase()}:`));
37188
- for (const rule of categoryRules) {
37189
- logger.log(` ${import_chalk19.default.gray(`[${rule.id}]`)} ${rule.rule}`);
37190
- }
37191
- logger.log("");
37192
- }
37193
- }
37194
- }
37195
- return {
37196
- success: true,
37197
- action: "list",
37198
- rules: displayRules,
37199
- totalCount
37200
- };
37201
- }
37202
- function getCategoryColor(category) {
37203
- switch (category) {
37204
- case "always":
37205
- return import_chalk19.default.green;
37206
- case "never":
37207
- return import_chalk19.default.red;
37208
- case "prefer":
37209
- return import_chalk19.default.blue;
37210
- case "context":
37211
- return import_chalk19.default.yellow;
37212
- }
37213
- }
37214
- function rulesAddCommand(options, logger = defaultLogger) {
37215
- const category = validateCategory(options.category);
37216
- const { board, resolvedPath } = readBoardFromFile(options.file);
37217
- const result = (0, import_core45.addRule)(board, category, options.text);
37218
- if (!result.success || !result.board) {
37219
- throw new CLIError(
37220
- result.error || "Failed to add rule",
37221
- ExitCode.USER_ERROR
37222
- );
37223
- }
37224
- writeBoard(resolvedPath, result.board);
37225
- const newRules = result.board.rules?.[category] || [];
37226
- const newRule = newRules[newRules.length - 1];
37227
- if (options.json) {
37228
- const output = {
37229
- success: true,
37230
- action: "add",
37231
- category,
37232
- rule: newRule
37233
- };
37234
- logger.log(JSON.stringify(output, null, 2));
37235
- } else {
37236
- const categoryColor = getCategoryColor(category);
37237
- logger.log("");
37238
- logger.log(
37239
- `${import_chalk19.default.green("+")} Added ${categoryColor(category)} rule: ${import_chalk19.default.gray(`[${newRule.id}]`)} ${newRule.rule}`
37240
- );
37241
- }
37242
- return {
37243
- success: true,
37244
- action: "add",
37245
- category,
37246
- rule: newRule
37247
- };
37248
- }
37249
- function rulesDeleteCommand(options, logger = defaultLogger) {
37250
- const category = validateCategory(options.category);
37251
- const { board, resolvedPath } = readBoardFromFile(options.file);
37252
- const existingRules = board.rules?.[category] || [];
37253
- const ruleToDelete = existingRules.find((r) => r.id === options.id);
37254
- if (!ruleToDelete) {
37255
- throw new CLIError(
37256
- `Rule ${options.id} not found in ${category}`,
37257
- ExitCode.USER_ERROR,
37258
- existingRules.length > 0 ? `Available IDs in ${category}: ${existingRules.map((r) => r.id).join(", ")}` : `No rules in ${category} category`
37259
- );
37260
- }
37261
- const result = (0, import_core45.deleteRule)(board, category, options.id);
37262
- if (!result.success || !result.board) {
37263
- throw new CLIError(
37264
- result.error || "Failed to delete rule",
37265
- ExitCode.USER_ERROR
37266
- );
37267
- }
37268
- writeBoard(resolvedPath, result.board);
37269
- if (options.json) {
37270
- const output = {
37271
- success: true,
37272
- action: "delete",
37273
- category,
37274
- id: options.id,
37275
- deletedRule: ruleToDelete
37276
- };
37277
- logger.log(JSON.stringify(output, null, 2));
37278
- } else {
37279
- const categoryColor = getCategoryColor(category);
37280
- logger.log("");
37281
- logger.log(
37282
- `${import_chalk19.default.red("-")} Deleted ${categoryColor(category)} rule: ${import_chalk19.default.gray(`[${options.id}]`)} ${ruleToDelete.rule}`
37283
- );
37284
- }
37285
- return {
37286
- success: true,
37287
- action: "delete",
37288
- category,
37289
- id: options.id
37290
- };
37291
- }
37292
- function rulesCommand(action, args, options, logger = defaultLogger) {
37293
- const effectiveAction = action || "list";
37294
- switch (effectiveAction) {
37295
- case "list":
37296
- return rulesListCommand(
37297
- {
37298
- file: options.file,
37299
- json: options.json,
37300
- category: options.category
37301
- },
37302
- logger
37303
- );
37304
- case "add": {
37305
- if (args.length < 2) {
37306
- throw new CLIError(
37307
- "Usage: brainfile rules add <category> <text>",
37308
- ExitCode.USER_ERROR,
37309
- `Categories: ${VALID_CATEGORIES.join(", ")}`
37310
- );
37311
- }
37312
- const [category, ...textParts] = args;
37313
- const text = textParts.join(" ");
37314
- return rulesAddCommand(
37315
- {
37316
- file: options.file,
37317
- category,
37318
- text,
37319
- json: options.json
37320
- },
37321
- logger
37322
- );
37323
- }
37324
- case "delete": {
37325
- if (args.length < 2) {
37326
- throw new CLIError(
37327
- "Usage: brainfile rules delete <category> <id>",
37328
- ExitCode.USER_ERROR,
37329
- `Categories: ${VALID_CATEGORIES.join(", ")}`
37330
- );
37331
- }
37332
- const [category, idStr] = args;
37333
- const id = parseInt(idStr, 10);
37334
- if (isNaN(id)) {
37335
- throw new CLIError(
37336
- `Invalid rule ID: ${idStr}`,
37337
- ExitCode.USER_ERROR,
37338
- "Rule ID must be a number"
37339
- );
37340
- }
37341
- return rulesDeleteCommand(
37342
- {
37343
- file: options.file,
37344
- category,
37345
- id,
37346
- json: options.json
37347
- },
37348
- logger
37349
- );
37350
- }
37351
- default:
37352
- throw new CLIError(
37353
- `Unknown action: ${effectiveAction}`,
37354
- ExitCode.USER_ERROR,
37355
- "Valid actions: list, add, delete"
37356
- );
37357
- }
37358
- }
37359
-
37360
37588
  // src/commands/adr.ts
37361
37589
  var import_chalk20 = __toESM(require_source());
37362
37590
  var import_core46 = __toESM(require_dist());
37363
37591
  import * as fs19 from "fs";
37364
- import * as path19 from "path";
37365
- var VALID_CATEGORIES2 = ["prefer", "always", "never", "context"];
37592
+ import * as path20 from "path";
37366
37593
  var ADR_COMMAND_HELP = `
37367
37594
  Examples:
37368
- brainfile adr promote -t adr-1 --category prefer
37369
- brainfile adr promote -t adr-12 --category always -f .brainfile/brainfile.md
37595
+ brainfile adr promote -t adr-1
37596
+ brainfile adr promote -t adr-12 -f .brainfile/brainfile.md
37370
37597
  `.trimEnd();
37371
37598
  function assertSafeTaskId3(taskId) {
37372
37599
  const trimmed = taskId.trim();
@@ -37376,86 +37603,21 @@ function assertSafeTaskId3(taskId) {
37376
37603
  if (taskId === "." || taskId === "..") {
37377
37604
  throw operationFailed(`Invalid task ID: ${taskId}`);
37378
37605
  }
37379
- if (path19.isAbsolute(taskId) || /[\\/]/.test(taskId)) {
37606
+ if (path20.isAbsolute(taskId) || /[\\/]/.test(taskId)) {
37380
37607
  throw operationFailed(`Invalid task ID: ${taskId}`);
37381
37608
  }
37382
37609
  }
37383
- function validateCategory2(category) {
37384
- if (!category) {
37385
- throw missingRequired(
37386
- "--category",
37387
- "brainfile adr promote --task <adr-id> --category <prefer|always|never|context> [--file <path>]"
37388
- );
37389
- }
37390
- const normalized = category.trim().toLowerCase();
37391
- if (!VALID_CATEGORIES2.includes(normalized)) {
37392
- throw new CLIError(
37393
- `Invalid category: ${category}`,
37394
- ExitCode.USER_ERROR,
37395
- `Valid categories: ${VALID_CATEGORIES2.join(", ")}`
37396
- );
37397
- }
37398
- return normalized;
37399
- }
37400
- function readBoard(filePath) {
37401
- const resolvedPath = resolveCliBrainfilePath(filePath);
37402
- if (!fs19.existsSync(resolvedPath)) {
37403
- throw fileNotFound(resolvedPath);
37404
- }
37405
- const content = fs19.readFileSync(resolvedPath, "utf-8");
37406
- const parsed = import_core46.Brainfile.parseWithErrors(content);
37407
- if (!parsed.board) {
37408
- throw operationFailed(parsed.error || "Failed to parse brainfile");
37409
- }
37410
- return { board: parsed.board, resolvedPath };
37411
- }
37412
- function writeBoard2(filePath, board) {
37413
- fs19.writeFileSync(filePath, import_core46.Brainfile.serialize(board), "utf-8");
37414
- }
37415
- function extractRuleText(title) {
37416
- const trimmed = title.trim();
37417
- const withoutAdrPrefix = trimmed.replace(/^ADR-\d+\s*:\s*/i, "").trim();
37418
- return withoutAdrPrefix.length > 0 ? withoutAdrPrefix : trimmed;
37419
- }
37420
- function parseRuleId(id) {
37421
- if (typeof id === "number" && Number.isFinite(id)) {
37422
- return id;
37423
- }
37424
- if (typeof id === "string") {
37425
- const prefixed = id.match(/^rule-(\d+)$/i);
37426
- if (prefixed) {
37427
- return Number(prefixed[1]);
37428
- }
37429
- const numeric = id.match(/^(\d+)$/);
37430
- if (numeric) {
37431
- return Number(numeric[1]);
37432
- }
37433
- }
37434
- return null;
37435
- }
37436
- function getNextRuleId(rules) {
37437
- if (!rules) return 1;
37438
- let maxId = 0;
37439
- for (const categoryRules of Object.values(rules)) {
37440
- if (!Array.isArray(categoryRules)) continue;
37441
- for (const rule of categoryRules) {
37442
- const parsedId = parseRuleId(rule?.id);
37443
- if (parsedId !== null) {
37444
- maxId = Math.max(maxId, parsedId);
37445
- }
37446
- }
37447
- }
37448
- return maxId + 1;
37449
- }
37450
37610
  function adrPromoteCommand(options, logger = defaultLogger) {
37451
37611
  if (!options.task) {
37452
37612
  throw missingRequired(
37453
37613
  "--task",
37454
- "brainfile adr promote --task <adr-id> --category <prefer|always|never|context> [--file <path>]"
37614
+ "brainfile adr promote --task <adr-id> [--file <path>]"
37455
37615
  );
37456
37616
  }
37457
- const category = validateCategory2(options.category);
37458
- const { board, resolvedPath } = readBoard(options.file);
37617
+ const resolvedPath = resolveCliBrainfilePath(options.file);
37618
+ if (!fs19.existsSync(resolvedPath)) {
37619
+ throw fileNotFound(resolvedPath);
37620
+ }
37459
37621
  if (!(0, import_core.isV2)(resolvedPath)) {
37460
37622
  throw operationFailed("adr promote requires v2 per-task file architecture. Run: brainfile migrate");
37461
37623
  }
@@ -37472,19 +37634,6 @@ function adrPromoteCommand(options, logger = defaultLogger) {
37472
37634
  `Only ADRs can be promoted. ${task.id} has type "${task.type || "unknown"}".`
37473
37635
  );
37474
37636
  }
37475
- const ruleText = extractRuleText(task.title);
37476
- const ruleId = getNextRuleId(board.rules);
37477
- const promotedRule = {
37478
- id: ruleId,
37479
- rule: ruleText,
37480
- source: task.id
37481
- };
37482
- if (!board.rules || typeof board.rules !== "object" || Array.isArray(board.rules)) {
37483
- board.rules = {};
37484
- }
37485
- const categoryRules = Array.isArray(board.rules[category]) ? board.rules[category] : [];
37486
- board.rules[category] = [...categoryRules, promotedRule];
37487
- writeBoard2(resolvedPath, board);
37488
37637
  const completedAt = (/* @__PURE__ */ new Date()).toISOString();
37489
37638
  const promotedTask = {
37490
37639
  ...task,
@@ -37494,7 +37643,7 @@ function adrPromoteCommand(options, logger = defaultLogger) {
37494
37643
  delete promotedTask.column;
37495
37644
  delete promotedTask.position;
37496
37645
  fs19.mkdirSync(dirs.logsDir, { recursive: true });
37497
- const logPath = path19.join(dirs.logsDir, (0, import_core46.taskFileName)(task.id));
37646
+ const logPath = path20.join(dirs.logsDir, (0, import_core46.taskFileName)(task.id));
37498
37647
  if (fs19.existsSync(logPath)) {
37499
37648
  throw operationFailed(`Log already exists for ADR: ${task.id}`);
37500
37649
  }
@@ -37503,15 +37652,12 @@ function adrPromoteCommand(options, logger = defaultLogger) {
37503
37652
  logger.log(import_chalk20.default.green("ADR promoted!"));
37504
37653
  logger.log("");
37505
37654
  logger.log(import_chalk20.default.gray(` ADR: ${task.id} - ${task.title}`));
37506
- logger.log(import_chalk20.default.gray(` Category: ${category}`));
37507
- logger.log(import_chalk20.default.gray(` Rule: ${promotedRule.rule}`));
37508
- logger.log(import_chalk20.default.gray(` Source: ${promotedRule.source}`));
37655
+ logger.log(import_chalk20.default.gray(` Status: promoted`));
37509
37656
  logger.log(import_chalk20.default.gray(` Moved to: logs/${task.id}.md`));
37510
37657
  return {
37511
37658
  success: true,
37512
37659
  taskId: task.id,
37513
- category,
37514
- rule: promotedRule,
37660
+ title: task.title,
37515
37661
  completedAt
37516
37662
  };
37517
37663
  }
@@ -40916,13 +41062,13 @@ function typesAddCommand(options, logger = defaultLogger) {
40916
41062
 
40917
41063
  // src/cli.ts
40918
41064
  async function runTui(options) {
40919
- const { tuiCommand } = await import("./tui-P56H4X7N.mjs");
41065
+ const { tuiCommand } = await import("./tui-H3RI22S4.mjs");
40920
41066
  return tuiCommand(options);
40921
41067
  }
40922
41068
  var packageJson = JSON.parse(
40923
- readFileSync10(join14(__dirname, "..", "package.json"), "utf8")
41069
+ readFileSync9(join15(__dirname, "..", "package.json"), "utf8")
40924
41070
  );
40925
- var SUBCOMMANDS = ["init", "migrate", "list", "show", "add", "move", "patch", "delete", "archive", "restore", "complete", "log", "note", "search", "subtask", "template", "lint", "tui", "hooks", "mcp", "auth", "config", "contract", "schema", "rules", "adr", "plan", "types", "help"];
41071
+ var SUBCOMMANDS = ["init", "migrate", "list", "show", "add", "move", "patch", "delete", "archive", "restore", "complete", "log", "note", "brief", "search", "subtask", "template", "lint", "tui", "hooks", "mcp", "auth", "config", "contract", "schema", "adr", "plan", "types", "help"];
40926
41072
  function shouldLaunchTUI() {
40927
41073
  const args = process.argv.slice(2);
40928
41074
  if (args.length === 0) {
@@ -40983,7 +41129,7 @@ Brainfile file resolution (when you don't pass --file):
40983
41129
  4) .bb.md
40984
41130
  `.trimEnd());
40985
41131
  program2.command("init").description("Initialize a new .brainfile/brainfile.md in the current directory").option("-f, --file <path>", "Path to brainfile file", ".brainfile/brainfile.md").option("--force", "Overwrite existing file").action(initCommand);
40986
- program2.command("migrate").description("Migrate legacy brainfile layouts to v2 (.brainfile/brainfile.md + board/ + logs/)").option("--dir <path>", "Directory containing legacy brainfile files (default: cwd)").option("--force", "Overwrite existing migration outputs (task files/backups)").option("--v2", "Deprecated alias; migration now targets v2 by default").option("--logs-to-ledger", "Migrate logs/*.md files into ledger.jsonl (resolves ID conflicts with board)").action(migrateCommand);
41132
+ program2.command("migrate").description("Migrate legacy brainfile layouts to v2 (.brainfile/brainfile.md + board/ + logs/)").option("--dir <path>", "Directory containing legacy brainfile files (default: cwd)").option("--force", "Overwrite existing migration outputs (task files/backups)").option("--v2", "Deprecated alias; migration now targets v2 by default").option("--logs-to-ledger", "Backfill logs/*.md into ledger.jsonl, keeping the markdown archives (resolves ID conflicts with board)").action(migrateCommand);
40987
41133
  const listCmd = program2.command("list").description("List tasks").option("-f, --file <path>", "Path to brainfile file (auto-detect by default)", "brainfile.md").option("-c, --column <name>", "Filter by column").option("-t, --tag <name>", "Filter by tag").option("--parent <id>", "Filter by parent task ID (parentId)").option("--contract <status>", "Filter by contract status (ready|in_progress|delivered|done|failed)").action((options) => {
40988
41134
  listCommand(options);
40989
41135
  });
@@ -41019,7 +41165,7 @@ ${LIST_COMMAND_HELP}`);
41019
41165
  ${ADD_COMMAND_HELP}`);
41020
41166
  program2.command("patch").description("Update task fields (partial update)").option("-f, --file <path>", "Path to brainfile file (auto-detect by default)", "brainfile.md").option("-t, --task <id>", "Task ID to update (required)").option("--title <text>", "New task title").option("-d, --description <text>", "New task description").option("-p, --priority <level>", 'Priority (low, medium, high, critical, or "none" to remove)').option("--tags <tags>", "Comma-separated tags (replaces existing)").option("--assignee <name>", "Assignee name").option("--due-date <date>", "Due date (YYYY-MM-DD)").option("--clear-tags", "Remove all tags").option("--clear-assignee", "Remove assignee").option("--clear-due-date", "Remove due date").option("--clear-priority", "Remove priority").action(patchCommand);
41021
41167
  program2.command("delete").description("Delete a task permanently").option("-f, --file <path>", "Path to brainfile file (auto-detect by default)", "brainfile.md").option("-t, --task <id>", "Task ID to delete (required)").option("--force", "Confirm deletion (required)").action(deleteCommand);
41022
- program2.command("archive").description("Archive a task (locally or to GitHub/Linear)").option("-f, --file <path>", "Path to brainfile file (auto-detect by default)", "brainfile.md").option("-t, --task <id>", "Task ID to archive").option("--to <destination>", "Archive destination: local, github, or linear").option("--all", "Archive all tasks from local archive to external service").option("--dry-run", "Preview what would be created without making changes").action(archiveCommand);
41168
+ program2.command("archive").description("Complete a task locally, or export a completed task to GitHub/Linear").option("-f, --file <path>", "Path to brainfile file (auto-detect by default)", "brainfile.md").option("-t, --task <id>", "Task ID to archive").option("--to <destination>", "Archive destination: local, github, or linear").option("--all", "Archive all tasks from local archive to external service").option("--dry-run", "Preview what would be created without making changes").action(archiveCommand);
41023
41169
  program2.command("restore").description("Restore a task from the archive").option("-f, --file <path>", "Path to brainfile file (auto-detect by default)", "brainfile.md").option("-t, --task <id>", "Task ID to restore (required)").option("-c, --column <name>", "Target column name or ID (required)").action(restoreCommand);
41024
41170
  program2.command("complete").description("Complete a task (moves it to logs/)").option("-f, --file <path>", "Path to brainfile file (auto-detect by default)", "brainfile.md").option("-t, --task <id>", "Task ID to complete (required)").option("--force", "Force epic completion even if child tasks are still active").action((options) => {
41025
41171
  completeCommand(options);
@@ -41030,6 +41176,9 @@ ${ADD_COMMAND_HELP}`);
41030
41176
  program2.command("note").description("Append a timestamped note to a task log").option("-f, --file <path>", "Path to brainfile file (auto-detect by default)", "brainfile.md").option("-t, --task <id>", "Task ID to add note to (required)").option("--agent <name>", "Agent name for attribution").argument("[message]", "Log message to append").action((message, options) => {
41031
41177
  logNoteCommand({ ...options, message });
41032
41178
  });
41179
+ program2.command("brief").description("Per-agent brief: what changed since this agent last checked in").option("-f, --file <path>", "Path to brainfile file (auto-detect by default)", "brainfile.md").option("--agent <name>", "Agent identifier (required \u2014 brief state is per-agent)").option("--peek", "Read the brief without marking it as seen").option("--json", "Output as JSON").action((options) => {
41180
+ briefCommand(options);
41181
+ });
41033
41182
  program2.command("search").description("Search across active tasks and completed logs").option("-f, --file <path>", "Path to brainfile file (auto-detect by default)", "brainfile.md").option("-c, --column <name>", "Filter by column").argument("<query>", "Search query").action((query, options) => {
41034
41183
  searchCommand({ ...options, query });
41035
41184
  });
@@ -41043,7 +41192,7 @@ ${ADD_COMMAND_HELP}`);
41043
41192
  program2.command("lint").description("Validate and auto-fix brainfile.md syntax").option("-f, --file <path>", "Path to brainfile file (auto-detect by default)", "brainfile.md").option("--fix", "Automatically fix issues when possible").option("--check", "Exit with error code if issues found (for CI/CD)").action((options) => {
41044
41193
  lintCommand(options);
41045
41194
  });
41046
- program2.command("tui").description("Launch interactive Terminal UI for task management").option("-f, --file <path>", "Path to brainfile file (auto-detect by default)", "brainfile.md").action((options) => {
41195
+ program2.command("tui").description("Launch the interactive board").option("-f, --file <path>", "Path to the board file (auto-detected by default)", "brainfile.md").action((options) => {
41047
41196
  void runTui(options).catch((error48) => {
41048
41197
  console.error(error48 instanceof Error ? error48.message : String(error48));
41049
41198
  process.exit(1);
@@ -41144,19 +41293,10 @@ ${PLAN_COMMAND_HELP}`);
41144
41293
  });
41145
41294
  schemaCmd.addHelpText("after", `
41146
41295
  ${SCHEMA_COMMAND_HELP}`);
41147
- const rulesCmd = program2.command("rules [action]").description("Manage project rules (always, never, prefer, context)").option("-f, --file <path>", "Path to brainfile file (auto-detect by default)", "brainfile.md").option("--json", "Output in JSON format").option("--category <category>", "Filter by category (for list)").argument("[args...]", "Arguments for the action (category, text/id)").action((action, args, options) => {
41148
- rulesCommand(action, args, {
41149
- file: options.file,
41150
- json: options.json,
41151
- category: options.category
41152
- });
41153
- });
41154
- rulesCmd.addHelpText("after", `
41155
- ${RULES_COMMAND_HELP}`);
41156
41296
  const adrCmd = program2.command("adr").description("Manage Architectural Decision Records (ADR) lifecycle");
41157
41297
  adrCmd.addHelpText("after", `
41158
41298
  ${ADR_COMMAND_HELP}`);
41159
- adrCmd.command("promote").description("Promote an ADR into a project rule and move it to logs/").option("-f, --file <path>", "Path to brainfile file (auto-detect by default)", "brainfile.md").option("-t, --task <id>", "ADR task ID to promote (required)").option("--category <category>", "Rule category (prefer|always|never|context)").action((options) => {
41299
+ adrCmd.command("promote").description("Mark an ADR accepted and move it to logs/").option("-f, --file <path>", "Path to brainfile file (auto-detect by default)", "brainfile.md").option("-t, --task <id>", "ADR task ID to promote (required)").action((options) => {
41160
41300
  adrPromoteCommand(options);
41161
41301
  });
41162
41302
  program2.command("types [action]").description("Inspect and manage board document types").option("-f, --file <path>", "Path to brainfile file (auto-detect by default)", "brainfile.md").option("--json", "Output in JSON format (list action)").option("--id-prefix <prefix>", "ID prefix to use for the type (add action; default: type name)").option("--completable <bool>", "Whether this type can be completed (add action; default: true)").option("--schema <url>", "Optional schema URL/path for this type (add action)").argument("[name]", "Type name (required for add action)").action((action, name, options) => {
@@ -41181,7 +41321,7 @@ ${ADR_COMMAND_HELP}`);
41181
41321
  }
41182
41322
  throw new Error(`Unknown types action: ${action}`);
41183
41323
  });
41184
- program2.parse();
41324
+ program2.parseAsync().catch(renderCliError);
41185
41325
  }
41186
41326
  /*! Bundled license information:
41187
41327