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.
- package/README.md +18 -24
- package/dist/__tests__/helpers/mcp-wire.d.ts +45 -0
- package/dist/__tests__/helpers/mcp-wire.d.ts.map +1 -0
- package/dist/__tests__/helpers/mcp-wire.js +133 -0
- package/dist/__tests__/helpers/mcp-wire.js.map +1 -0
- package/dist/{chunk-YTFLC673.mjs → chunk-UOCQBEEW.mjs} +1486 -992
- package/dist/cli.mjs +916 -776
- package/dist/commands/adr.d.ts +1 -9
- package/dist/commands/adr.d.ts.map +1 -1
- package/dist/commands/adr.js +11 -88
- package/dist/commands/adr.js.map +1 -1
- package/dist/commands/archive.d.ts +2 -5
- package/dist/commands/archive.d.ts.map +1 -1
- package/dist/commands/archive.js +43 -11
- package/dist/commands/archive.js.map +1 -1
- package/dist/commands/brief.d.ts +31 -0
- package/dist/commands/brief.d.ts.map +1 -0
- package/dist/commands/brief.js +151 -0
- package/dist/commands/brief.js.map +1 -0
- package/dist/commands/complete.js +1 -1
- package/dist/commands/complete.js.map +1 -1
- package/dist/commands/log.js +31 -18
- package/dist/commands/log.js.map +1 -1
- package/dist/commands/mcp.d.ts +9 -0
- package/dist/commands/mcp.d.ts.map +1 -1
- package/dist/commands/mcp.js +28 -17
- package/dist/commands/mcp.js.map +1 -1
- package/dist/commands/migrate.js +10 -12
- package/dist/commands/migrate.js.map +1 -1
- package/dist/mcp/schemas.d.ts +297 -0
- package/dist/mcp/schemas.d.ts.map +1 -0
- package/dist/mcp/schemas.js +328 -0
- package/dist/mcp/schemas.js.map +1 -0
- package/dist/mcp/tools/brief_tool.d.ts +14 -0
- package/dist/mcp/tools/brief_tool.d.ts.map +1 -0
- package/dist/mcp/tools/brief_tool.js +58 -0
- package/dist/mcp/tools/brief_tool.js.map +1 -0
- package/dist/mcp/tools/contract_tool.d.ts.map +1 -1
- package/dist/mcp/tools/contract_tool.js +37 -13
- package/dist/mcp/tools/contract_tool.js.map +1 -1
- package/dist/mcp/tools/get_task_tool.d.ts.map +1 -1
- package/dist/mcp/tools/get_task_tool.js +7 -2
- package/dist/mcp/tools/get_task_tool.js.map +1 -1
- package/dist/mcp/tools/list_tasks_tool.d.ts.map +1 -1
- package/dist/mcp/tools/list_tasks_tool.js +9 -3
- package/dist/mcp/tools/list_tasks_tool.js.map +1 -1
- package/dist/mcp/tools/search_tool.d.ts.map +1 -1
- package/dist/mcp/tools/search_tool.js +17 -4
- package/dist/mcp/tools/search_tool.js.map +1 -1
- package/dist/mcp/tools/subtask_tool.d.ts.map +1 -1
- package/dist/mcp/tools/subtask_tool.js +47 -13
- package/dist/mcp/tools/subtask_tool.js.map +1 -1
- package/dist/mcp/tools/task_add_tool.d.ts.map +1 -1
- package/dist/mcp/tools/task_add_tool.js +7 -2
- package/dist/mcp/tools/task_add_tool.js.map +1 -1
- package/dist/mcp/tools/task_complete_tool.d.ts.map +1 -1
- package/dist/mcp/tools/task_complete_tool.js +34 -6
- package/dist/mcp/tools/task_complete_tool.js.map +1 -1
- package/dist/mcp/tools/task_delete_tool.d.ts.map +1 -1
- package/dist/mcp/tools/task_delete_tool.js +7 -2
- package/dist/mcp/tools/task_delete_tool.js.map +1 -1
- package/dist/mcp/tools/task_move_tool.d.ts.map +1 -1
- package/dist/mcp/tools/task_move_tool.js +15 -6
- package/dist/mcp/tools/task_move_tool.js.map +1 -1
- package/dist/mcp/tools/task_patch_tool.d.ts.map +1 -1
- package/dist/mcp/tools/task_patch_tool.js +18 -6
- package/dist/mcp/tools/task_patch_tool.js.map +1 -1
- package/dist/schemas/board.json +1 -1
- package/dist/tui/theme.d.ts +98 -0
- package/dist/tui/theme.d.ts.map +1 -0
- package/dist/tui/theme.js +141 -0
- package/dist/tui/theme.js.map +1 -0
- package/dist/{tui-P56H4X7N.mjs → tui-H3RI22S4.mjs} +1593 -1934
- package/dist/utils/cli-error.d.ts +20 -0
- package/dist/utils/cli-error.d.ts.map +1 -1
- package/dist/utils/cli-error.js +36 -0
- package/dist/utils/cli-error.js.map +1 -1
- package/package.json +3 -3
- package/dist/commands/rules.d.ts +0 -68
- package/dist/commands/rules.d.ts.map +0 -1
- package/dist/commands/rules.js +0 -322
- 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-
|
|
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
|
|
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 =
|
|
2241
|
+
const localBin = path21.resolve(baseDir, baseName);
|
|
2240
2242
|
if (fs21.existsSync(localBin)) return localBin;
|
|
2241
|
-
if (sourceExt.includes(
|
|
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 =
|
|
2260
|
-
|
|
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 =
|
|
2269
|
+
const legacyName = path21.basename(
|
|
2268
2270
|
this._scriptPath,
|
|
2269
|
-
|
|
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(
|
|
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 =
|
|
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(
|
|
3210
|
-
if (
|
|
3211
|
-
this._executableDir =
|
|
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.
|
|
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.
|
|
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: ">=
|
|
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
|
|
3591
|
-
import { join as
|
|
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
|
-
|
|
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(`
|
|
4737
|
-
console.log(import_chalk6.default.gray(`
|
|
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
|
-
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
5345
|
+
const mainContent = import_core15.Brainfile.serialize(board);
|
|
5317
5346
|
fs8.writeFileSync(filePath, mainContent, "utf-8");
|
|
5318
|
-
const archiveContent =
|
|
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 =
|
|
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,
|
|
5394
|
+
let targetColumn = (0, import_core16.findColumnById)(board, options.column);
|
|
5366
5395
|
if (!targetColumn) {
|
|
5367
|
-
targetColumn = (0,
|
|
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
|
|
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,
|
|
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
|
-
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
5585
|
-
logDocs.
|
|
5586
|
-
|
|
5587
|
-
|
|
5588
|
-
|
|
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 (${
|
|
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 (
|
|
5636
|
+
if (entries.length === 0) {
|
|
5594
5637
|
logger.log(import_chalk12.default.gray(" No completed tasks found."));
|
|
5595
5638
|
} else {
|
|
5596
|
-
for (const
|
|
5597
|
-
const
|
|
5598
|
-
logger.log(` ${import_chalk12.default.gray(`[${
|
|
5599
|
-
if (
|
|
5600
|
-
logger.log(` ${import_chalk12.default.gray("Completed:")} ${
|
|
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:
|
|
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/
|
|
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(
|
|
5654
|
-
logger.log(
|
|
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(
|
|
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 ?
|
|
5660
|
-
logger.log(` ${
|
|
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(` ${
|
|
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
|
|
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: ${
|
|
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(
|
|
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(
|
|
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(
|
|
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: ${
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(` ${
|
|
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(
|
|
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(
|
|
5762
|
-
console.log(
|
|
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(
|
|
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(
|
|
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 ${
|
|
5902
|
+
const username = status.github.username ? ` as ${import_chalk15.default.cyan("@" + status.github.username)}` : "";
|
|
5780
5903
|
console.log(
|
|
5781
|
-
|
|
5904
|
+
import_chalk15.default.green("\u2713") + ` GitHub: authenticated${username} ${import_chalk15.default.gray(`(${source})`)}`
|
|
5782
5905
|
);
|
|
5783
5906
|
} else {
|
|
5784
|
-
console.log(
|
|
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 ${
|
|
5911
|
+
const username = status.linear.username ? ` as ${import_chalk15.default.cyan(status.linear.username)}` : "";
|
|
5789
5912
|
console.log(
|
|
5790
|
-
|
|
5913
|
+
import_chalk15.default.green("\u2713") + ` Linear: authenticated${username} ${import_chalk15.default.gray(`(${source})`)}`
|
|
5791
5914
|
);
|
|
5792
5915
|
} else {
|
|
5793
|
-
console.log(
|
|
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(
|
|
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(
|
|
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(
|
|
5947
|
+
console.log(import_chalk15.default.green("\u2713") + " Logged out from Linear");
|
|
5825
5948
|
break;
|
|
5826
5949
|
default:
|
|
5827
|
-
console.log(
|
|
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
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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,
|
|
6737
|
-
if (!
|
|
6859
|
+
function getElementAtPath(obj, path21) {
|
|
6860
|
+
if (!path21)
|
|
6738
6861
|
return obj;
|
|
6739
|
-
return
|
|
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(
|
|
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(
|
|
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,
|
|
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 = [...
|
|
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
|
|
7352
|
-
for (const seg of
|
|
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
|
|
19330
|
-
if (
|
|
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 (
|
|
19335
|
-
const key =
|
|
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
|
|
23298
|
-
const key =
|
|
23299
|
-
if (
|
|
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,
|
|
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 ||
|
|
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(
|
|
23627
|
-
if (!RFC9110_TOKEN.test(raw)) return `${pathName(
|
|
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(
|
|
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:
|
|
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, [...
|
|
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, [...
|
|
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(
|
|
23691
|
-
return
|
|
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,
|
|
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(`${
|
|
23989
|
-
} else unsupported.push(`${
|
|
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,
|
|
24007
|
-
if (Array.isArray(original) && Array.isArray(parsed)) return original.flatMap((item, index) => findDroppedConstraintPaths(item, parsed[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 =
|
|
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(
|
|
28089
|
-
let input =
|
|
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 [
|
|
28243
|
-
wsComponent.path =
|
|
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) =>
|
|
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) =>
|
|
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
|
|
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
|
|
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
|
|
34328
|
-
import * as
|
|
34450
|
+
import * as fs12 from "fs";
|
|
34451
|
+
import * as path15 from "path";
|
|
34329
34452
|
function resolveBrainfile(filePath) {
|
|
34330
|
-
return
|
|
34453
|
+
return path15.resolve(filePath);
|
|
34331
34454
|
}
|
|
34332
34455
|
function requireV2(filePath) {
|
|
34333
34456
|
const resolvedPath = resolveBrainfile(filePath);
|
|
34334
|
-
if (!
|
|
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 =
|
|
34357
|
-
const root =
|
|
34479
|
+
let currentDir = path15.resolve(startDir);
|
|
34480
|
+
const root = path15.parse(currentDir).root;
|
|
34358
34481
|
while (currentDir !== root) {
|
|
34359
|
-
const gitPath =
|
|
34360
|
-
if (
|
|
34482
|
+
const gitPath = path15.join(currentDir, ".git");
|
|
34483
|
+
if (fs12.existsSync(gitPath)) {
|
|
34361
34484
|
return currentDir;
|
|
34362
34485
|
}
|
|
34363
|
-
const parentDir =
|
|
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.
|
|
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
|
-
|
|
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 {
|
|
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
|
|
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 {
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|
|
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 {
|
|
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
|
|
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 =
|
|
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 {
|
|
35156
|
+
return {
|
|
35157
|
+
content: [{ type: "text", text: `Error: ${single?.error || "Task not found"}` }],
|
|
35158
|
+
structuredContent: output,
|
|
35159
|
+
isError: true
|
|
35160
|
+
};
|
|
34782
35161
|
}
|
|
34783
|
-
return {
|
|
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
|
|
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
|
-
|
|
34820
|
-
return {
|
|
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 {
|
|
35269
|
+
return {
|
|
35270
|
+
content: [{ type: "text", text: `Subtask added: ${added[0].id} - ${added[0].title}` }],
|
|
35271
|
+
structuredContent: output
|
|
35272
|
+
};
|
|
34884
35273
|
}
|
|
34885
|
-
return {
|
|
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 {
|
|
35300
|
+
return {
|
|
35301
|
+
content: [{ type: "text", text: `Subtask ${deleted[0]} deleted successfully` }],
|
|
35302
|
+
structuredContent: output
|
|
35303
|
+
};
|
|
34908
35304
|
}
|
|
34909
|
-
return {
|
|
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 {
|
|
35331
|
+
return {
|
|
35332
|
+
content: [{ type: "text", text: `Subtask ${updated[0].id} marked as ${status}` }],
|
|
35333
|
+
structuredContent: output
|
|
35334
|
+
};
|
|
34932
35335
|
}
|
|
34933
|
-
return {
|
|
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 {
|
|
35369
|
+
return {
|
|
35370
|
+
content: [{ type: "text", text: `Subtask ${updated[0].id} updated to "${updated[0].title}"` }],
|
|
35371
|
+
structuredContent: output
|
|
35372
|
+
};
|
|
34963
35373
|
}
|
|
34964
|
-
return {
|
|
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
|
|
34977
|
-
import * as
|
|
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 (!
|
|
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 =
|
|
35141
|
-
const brainfileDir =
|
|
35142
|
-
const baseDir =
|
|
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 =
|
|
35156
|
-
if (!
|
|
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
|
|
36037
|
+
const path21 = deliverable.path.trim();
|
|
35625
36038
|
const description = deliverable.description?.trim();
|
|
35626
|
-
return description ? `${type2}:${
|
|
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
|
-
|
|
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 {
|
|
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 {
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|
|
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
|
|
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, {
|
|
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 {
|
|
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
|
-
|
|
35892
|
-
return {
|
|
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
|
-
|
|
35936
|
-
return {
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
|
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
|
|
36025
|
-
import * as
|
|
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
|
|
36538
|
+
return path18.join(homeDir, ".claude", "settings.json");
|
|
36037
36539
|
case "cursor":
|
|
36038
|
-
return
|
|
36540
|
+
return path18.join(homeDir, ".cursor", "hooks.json");
|
|
36039
36541
|
case "cline":
|
|
36040
|
-
return
|
|
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
|
|
36547
|
+
return path18.join(process.cwd(), ".claude", "settings.json");
|
|
36046
36548
|
case "cursor":
|
|
36047
|
-
return
|
|
36549
|
+
return path18.join(process.cwd(), ".cursor", "hooks.json");
|
|
36048
36550
|
case "cline":
|
|
36049
|
-
return
|
|
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 (!
|
|
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 =
|
|
36063
|
-
if (
|
|
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 (!
|
|
36571
|
+
if (!fs16.existsSync(settingsPath)) {
|
|
36070
36572
|
return tool === "cursor" ? { version: 1, hooks: {} } : {};
|
|
36071
36573
|
}
|
|
36072
36574
|
try {
|
|
36073
|
-
const content =
|
|
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 =
|
|
36082
|
-
if (!
|
|
36083
|
-
|
|
36583
|
+
const settingsDir = path18.dirname(settingsPath);
|
|
36584
|
+
if (!fs16.existsSync(settingsDir)) {
|
|
36585
|
+
fs16.mkdirSync(settingsDir, { recursive: true });
|
|
36084
36586
|
}
|
|
36085
|
-
|
|
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 (!
|
|
36136
|
-
|
|
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 =
|
|
36646
|
+
const hookPath = path18.join(hooksDir, hookType);
|
|
36145
36647
|
const scriptContent = createClineHookScript(hookType);
|
|
36146
|
-
|
|
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 (!
|
|
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 =
|
|
36242
|
-
if (
|
|
36243
|
-
const content =
|
|
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
|
-
|
|
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 (!
|
|
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 =
|
|
36299
|
-
if (
|
|
36300
|
-
const content =
|
|
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
|
|
36340
|
-
import * as
|
|
36841
|
+
var import_core45 = __toESM(require_dist());
|
|
36842
|
+
import * as fs17 from "fs";
|
|
36341
36843
|
function findBrainfile2() {
|
|
36342
|
-
const found = (0,
|
|
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 =
|
|
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(
|
|
36530
|
-
logger.log(
|
|
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(
|
|
36544
|
-
logger.log(
|
|
36545
|
-
logger.log(
|
|
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(
|
|
37049
|
+
logger.log(import_chalk17.default.white("Hooks configured:"));
|
|
36548
37050
|
if (tool === "claude-code") {
|
|
36549
|
-
logger.log(
|
|
36550
|
-
logger.log(
|
|
36551
|
-
logger.log(
|
|
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(
|
|
36554
|
-
logger.log(
|
|
36555
|
-
logger.log(
|
|
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(
|
|
36558
|
-
logger.log(
|
|
36559
|
-
logger.log(
|
|
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(
|
|
36563
|
-
logger.log(
|
|
36564
|
-
logger.log(
|
|
36565
|
-
logger.log(
|
|
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(
|
|
36571
|
-
logger.log(
|
|
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(
|
|
36583
|
-
logger.log(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
37104
|
+
logger.log(import_chalk17.default.white(` User scope (${userPath}):`));
|
|
36603
37105
|
if (userInstalled) {
|
|
36604
37106
|
if (tool === "cline") {
|
|
36605
|
-
logger.log(
|
|
36606
|
-
logger.log(
|
|
36607
|
-
logger.log(
|
|
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(
|
|
36610
|
-
logger.log(
|
|
36611
|
-
logger.log(
|
|
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(
|
|
37116
|
+
logger.log(import_chalk17.default.gray(" \u2717 Not installed"));
|
|
36615
37117
|
}
|
|
36616
37118
|
} catch (error48) {
|
|
36617
|
-
logger.log(
|
|
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(
|
|
37124
|
+
logger.log(import_chalk17.default.white(` Project scope (${projectPath}):`));
|
|
36623
37125
|
if (projectInstalled) {
|
|
36624
37126
|
if (tool === "cline") {
|
|
36625
|
-
logger.log(
|
|
36626
|
-
logger.log(
|
|
36627
|
-
logger.log(
|
|
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(
|
|
36630
|
-
logger.log(
|
|
36631
|
-
logger.log(
|
|
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(
|
|
37136
|
+
logger.log(import_chalk17.default.gray(" \u2717 Not installed"));
|
|
36635
37137
|
}
|
|
36636
37138
|
} catch (error48) {
|
|
36637
|
-
logger.log(
|
|
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
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
37187
|
+
console.log(import_chalk18.default.gray("No configuration set."));
|
|
36686
37188
|
console.log("");
|
|
36687
|
-
console.log("Use " +
|
|
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(
|
|
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(` ${
|
|
37200
|
+
console.log(` ${import_chalk18.default.cyan(key.padEnd(maxKeyLen))} ${displayValue}`);
|
|
36699
37201
|
}
|
|
36700
37202
|
console.log("");
|
|
36701
|
-
console.log(
|
|
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(
|
|
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(
|
|
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(
|
|
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(" " +
|
|
36748
|
-
console.log(" " +
|
|
36749
|
-
console.log(" " +
|
|
36750
|
-
console.log(" " +
|
|
36751
|
-
console.log(" " +
|
|
36752
|
-
console.log(" " +
|
|
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(
|
|
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,
|
|
36774
|
-
const parts =
|
|
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,
|
|
36785
|
-
const parts =
|
|
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
|
|
36811
|
-
import * as
|
|
36812
|
-
import * as
|
|
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 =
|
|
36817
|
-
const srcPath =
|
|
36818
|
-
if (
|
|
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 (
|
|
37323
|
+
if (fs18.existsSync(srcPath)) {
|
|
36822
37324
|
return srcPath;
|
|
36823
37325
|
}
|
|
36824
|
-
return
|
|
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(
|
|
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
|
-
` ${
|
|
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(
|
|
36886
|
-
logger.log(
|
|
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 =
|
|
36907
|
-
if (!
|
|
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 =
|
|
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(
|
|
36921
|
-
logger.log(
|
|
36922
|
-
logger.log(
|
|
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(
|
|
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(
|
|
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 =
|
|
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 (
|
|
36981
|
-
const localContent =
|
|
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
|
-
|
|
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(` ${
|
|
37505
|
+
logger.log(` ${import_chalk19.default.green("+")} ${import_chalk19.default.cyan(result.id)} - Updated`);
|
|
37004
37506
|
} else if (result.status === "current") {
|
|
37005
|
-
logger.log(` ${
|
|
37507
|
+
logger.log(` ${import_chalk19.default.gray("=")} ${import_chalk19.default.cyan(result.id)} - Up to date`);
|
|
37006
37508
|
} else {
|
|
37007
37509
|
logger.log(
|
|
37008
|
-
` ${
|
|
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(
|
|
37517
|
+
logger.log(import_chalk19.default.green(`Updated ${updatedCount} schema(s)`));
|
|
37016
37518
|
} else {
|
|
37017
|
-
logger.log(
|
|
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 =
|
|
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 (
|
|
37065
|
-
const localContent =
|
|
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
|
-
|
|
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
|
|
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
|
|
37369
|
-
brainfile adr promote -t adr-12
|
|
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 (
|
|
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>
|
|
37614
|
+
"brainfile adr promote --task <adr-id> [--file <path>]"
|
|
37455
37615
|
);
|
|
37456
37616
|
}
|
|
37457
|
-
const
|
|
37458
|
-
|
|
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 =
|
|
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(`
|
|
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
|
-
|
|
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-
|
|
41065
|
+
const { tuiCommand } = await import("./tui-H3RI22S4.mjs");
|
|
40920
41066
|
return tuiCommand(options);
|
|
40921
41067
|
}
|
|
40922
41068
|
var packageJson = JSON.parse(
|
|
40923
|
-
|
|
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", "
|
|
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", "
|
|
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("
|
|
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
|
|
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("
|
|
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.
|
|
41324
|
+
program2.parseAsync().catch(renderCliError);
|
|
41185
41325
|
}
|
|
41186
41326
|
/*! Bundled license information:
|
|
41187
41327
|
|