brainfile 0.18.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 +222 -0
- package/dist/__tests__/helpers/v2.d.ts +12 -0
- package/dist/__tests__/helpers/v2.d.ts.map +1 -0
- package/dist/__tests__/helpers/v2.js +73 -0
- package/dist/__tests__/helpers/v2.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +480 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/add.d.ts +34 -0
- package/dist/commands/add.d.ts.map +1 -0
- package/dist/commands/add.js +255 -0
- package/dist/commands/add.js.map +1 -0
- package/dist/commands/adr.d.ts +22 -0
- package/dist/commands/adr.d.ts.map +1 -0
- package/dist/commands/adr.js +195 -0
- package/dist/commands/adr.js.map +1 -0
- package/dist/commands/archive.d.ts +20 -0
- package/dist/commands/archive.d.ts.map +1 -0
- package/dist/commands/archive.js +590 -0
- package/dist/commands/archive.js.map +1 -0
- package/dist/commands/auth.d.ts +26 -0
- package/dist/commands/auth.d.ts.map +1 -0
- package/dist/commands/auth.js +245 -0
- package/dist/commands/auth.js.map +1 -0
- package/dist/commands/complete.d.ts +30 -0
- package/dist/commands/complete.d.ts.map +1 -0
- package/dist/commands/complete.js +267 -0
- package/dist/commands/complete.js.map +1 -0
- package/dist/commands/config.d.ts +29 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +210 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/contract.d.ts +84 -0
- package/dist/commands/contract.d.ts.map +1 -0
- package/dist/commands/contract.js +715 -0
- package/dist/commands/contract.js.map +1 -0
- package/dist/commands/delete.d.ts +8 -0
- package/dist/commands/delete.d.ts.map +1 -0
- package/dist/commands/delete.js +139 -0
- package/dist/commands/delete.js.map +1 -0
- package/dist/commands/hooks.d.ts +35 -0
- package/dist/commands/hooks.d.ts.map +1 -0
- package/dist/commands/hooks.js +324 -0
- package/dist/commands/hooks.js.map +1 -0
- package/dist/commands/init.d.ts +7 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +118 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/lint.d.ts +16 -0
- package/dist/commands/lint.d.ts.map +1 -0
- package/dist/commands/lint.js +123 -0
- package/dist/commands/lint.js.map +1 -0
- package/dist/commands/list.d.ts +20 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/list.js +175 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/log.d.ts +52 -0
- package/dist/commands/log.d.ts.map +1 -0
- package/dist/commands/log.js +246 -0
- package/dist/commands/log.js.map +1 -0
- package/dist/commands/mcp.d.ts +3 -0
- package/dist/commands/mcp.d.ts.map +1 -0
- package/dist/commands/mcp.js +96 -0
- package/dist/commands/mcp.js.map +1 -0
- package/dist/commands/migrate.d.ts +16 -0
- package/dist/commands/migrate.d.ts.map +1 -0
- package/dist/commands/migrate.js +388 -0
- package/dist/commands/migrate.js.map +1 -0
- package/dist/commands/move.d.ts +16 -0
- package/dist/commands/move.d.ts.map +1 -0
- package/dist/commands/move.js +225 -0
- package/dist/commands/move.js.map +1 -0
- package/dist/commands/patch.d.ts +17 -0
- package/dist/commands/patch.d.ts.map +1 -0
- package/dist/commands/patch.js +226 -0
- package/dist/commands/patch.js.map +1 -0
- package/dist/commands/restore.d.ts +8 -0
- package/dist/commands/restore.d.ts.map +1 -0
- package/dist/commands/restore.js +111 -0
- package/dist/commands/restore.js.map +1 -0
- package/dist/commands/rules.d.ts +68 -0
- package/dist/commands/rules.d.ts.map +1 -0
- package/dist/commands/rules.js +322 -0
- package/dist/commands/rules.js.map +1 -0
- package/dist/commands/schema.d.ts +31 -0
- package/dist/commands/schema.d.ts.map +1 -0
- package/dist/commands/schema.js +374 -0
- package/dist/commands/schema.js.map +1 -0
- package/dist/commands/search.d.ts +33 -0
- package/dist/commands/search.d.ts.map +1 -0
- package/dist/commands/search.js +209 -0
- package/dist/commands/search.js.map +1 -0
- package/dist/commands/show.d.ts +20 -0
- package/dist/commands/show.d.ts.map +1 -0
- package/dist/commands/show.js +272 -0
- package/dist/commands/show.js.map +1 -0
- package/dist/commands/subtask.d.ts +12 -0
- package/dist/commands/subtask.d.ts.map +1 -0
- package/dist/commands/subtask.js +212 -0
- package/dist/commands/subtask.js.map +1 -0
- package/dist/commands/template.d.ts +17 -0
- package/dist/commands/template.d.ts.map +1 -0
- package/dist/commands/template.js +149 -0
- package/dist/commands/template.js.map +1 -0
- package/dist/commands/tui.d.ts +6 -0
- package/dist/commands/tui.d.ts.map +1 -0
- package/dist/commands/tui.js +79 -0
- package/dist/commands/tui.js.map +1 -0
- package/dist/commands/types.d.ts +40 -0
- package/dist/commands/types.d.ts.map +1 -0
- package/dist/commands/types.js +242 -0
- package/dist/commands/types.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +29 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/contractRunner.d.ts +59 -0
- package/dist/lib/contractRunner.d.ts.map +1 -0
- package/dist/lib/contractRunner.js +511 -0
- package/dist/lib/contractRunner.js.map +1 -0
- package/dist/mcp/helpers.d.ts +32 -0
- package/dist/mcp/helpers.d.ts.map +1 -0
- package/dist/mcp/helpers.js +125 -0
- package/dist/mcp/helpers.js.map +1 -0
- package/dist/mcp/tools/contract.d.ts +25 -0
- package/dist/mcp/tools/contract.d.ts.map +1 -0
- package/dist/mcp/tools/contract.js +33 -0
- package/dist/mcp/tools/contract.js.map +1 -0
- package/dist/mcp/tools/contract_tool.d.ts +3 -0
- package/dist/mcp/tools/contract_tool.d.ts.map +1 -0
- package/dist/mcp/tools/contract_tool.js +328 -0
- package/dist/mcp/tools/contract_tool.js.map +1 -0
- package/dist/mcp/tools/get_task_tool.d.ts +3 -0
- package/dist/mcp/tools/get_task_tool.d.ts.map +1 -0
- package/dist/mcp/tools/get_task_tool.js +55 -0
- package/dist/mcp/tools/get_task_tool.js.map +1 -0
- package/dist/mcp/tools/list_tasks_tool.d.ts +3 -0
- package/dist/mcp/tools/list_tasks_tool.d.ts.map +1 -0
- package/dist/mcp/tools/list_tasks_tool.js +82 -0
- package/dist/mcp/tools/list_tasks_tool.js.map +1 -0
- package/dist/mcp/tools/search_tool.d.ts +3 -0
- package/dist/mcp/tools/search_tool.d.ts.map +1 -0
- package/dist/mcp/tools/search_tool.js +155 -0
- package/dist/mcp/tools/search_tool.js.map +1 -0
- package/dist/mcp/tools/subtask_tool.d.ts +3 -0
- package/dist/mcp/tools/subtask_tool.d.ts.map +1 -0
- package/dist/mcp/tools/subtask_tool.js +321 -0
- package/dist/mcp/tools/subtask_tool.js.map +1 -0
- package/dist/mcp/tools/task_add_tool.d.ts +3 -0
- package/dist/mcp/tools/task_add_tool.d.ts.map +1 -0
- package/dist/mcp/tools/task_add_tool.js +196 -0
- package/dist/mcp/tools/task_add_tool.js.map +1 -0
- package/dist/mcp/tools/task_complete_tool.d.ts +3 -0
- package/dist/mcp/tools/task_complete_tool.d.ts.map +1 -0
- package/dist/mcp/tools/task_complete_tool.js +265 -0
- package/dist/mcp/tools/task_complete_tool.js.map +1 -0
- package/dist/mcp/tools/task_delete_tool.d.ts +3 -0
- package/dist/mcp/tools/task_delete_tool.d.ts.map +1 -0
- package/dist/mcp/tools/task_delete_tool.js +87 -0
- package/dist/mcp/tools/task_delete_tool.js.map +1 -0
- package/dist/mcp/tools/task_move_tool.d.ts +3 -0
- package/dist/mcp/tools/task_move_tool.d.ts.map +1 -0
- package/dist/mcp/tools/task_move_tool.js +187 -0
- package/dist/mcp/tools/task_move_tool.js.map +1 -0
- package/dist/mcp/tools/task_patch_tool.d.ts +3 -0
- package/dist/mcp/tools/task_patch_tool.d.ts.map +1 -0
- package/dist/mcp/tools/task_patch_tool.js +189 -0
- package/dist/mcp/tools/task_patch_tool.js.map +1 -0
- package/dist/schemas/base.json +159 -0
- package/dist/schemas/board.json +290 -0
- package/dist/tui/BrainfileTUI.d.ts +4 -0
- package/dist/tui/BrainfileTUI.d.ts.map +1 -0
- package/dist/tui/BrainfileTUI.js +272 -0
- package/dist/tui/BrainfileTUI.js.map +1 -0
- package/dist/tui/actions.d.ts +84 -0
- package/dist/tui/actions.d.ts.map +1 -0
- package/dist/tui/actions.js +1251 -0
- package/dist/tui/actions.js.map +1 -0
- package/dist/tui/components/ColumnTabs.d.ts +9 -0
- package/dist/tui/components/ColumnTabs.d.ts.map +1 -0
- package/dist/tui/components/ColumnTabs.js +34 -0
- package/dist/tui/components/ColumnTabs.js.map +1 -0
- package/dist/tui/components/Header.d.ts +15 -0
- package/dist/tui/components/Header.d.ts.map +1 -0
- package/dist/tui/components/Header.js +48 -0
- package/dist/tui/components/Header.js.map +1 -0
- package/dist/tui/components/HelpOverlay.d.ts +9 -0
- package/dist/tui/components/HelpOverlay.d.ts.map +1 -0
- package/dist/tui/components/HelpOverlay.js +116 -0
- package/dist/tui/components/HelpOverlay.js.map +1 -0
- package/dist/tui/components/LogsPanel.d.ts +16 -0
- package/dist/tui/components/LogsPanel.d.ts.map +1 -0
- package/dist/tui/components/LogsPanel.js +115 -0
- package/dist/tui/components/LogsPanel.js.map +1 -0
- package/dist/tui/components/MainPanelTabs.d.ts +10 -0
- package/dist/tui/components/MainPanelTabs.d.ts.map +1 -0
- package/dist/tui/components/MainPanelTabs.js +56 -0
- package/dist/tui/components/MainPanelTabs.js.map +1 -0
- package/dist/tui/components/ManualBorder.d.ts +42 -0
- package/dist/tui/components/ManualBorder.d.ts.map +1 -0
- package/dist/tui/components/ManualBorder.js +56 -0
- package/dist/tui/components/ManualBorder.js.map +1 -0
- package/dist/tui/components/Overlays.d.ts +41 -0
- package/dist/tui/components/Overlays.d.ts.map +1 -0
- package/dist/tui/components/Overlays.js +127 -0
- package/dist/tui/components/Overlays.js.map +1 -0
- package/dist/tui/components/ProgressBar.d.ts +8 -0
- package/dist/tui/components/ProgressBar.d.ts.map +1 -0
- package/dist/tui/components/ProgressBar.js +35 -0
- package/dist/tui/components/ProgressBar.js.map +1 -0
- package/dist/tui/components/RulesPanel.d.ts +25 -0
- package/dist/tui/components/RulesPanel.d.ts.map +1 -0
- package/dist/tui/components/RulesPanel.js +139 -0
- package/dist/tui/components/RulesPanel.js.map +1 -0
- package/dist/tui/components/SearchBar.d.ts +8 -0
- package/dist/tui/components/SearchBar.d.ts.map +1 -0
- package/dist/tui/components/SearchBar.js +38 -0
- package/dist/tui/components/SearchBar.js.map +1 -0
- package/dist/tui/components/StackedTaskList.d.ts +27 -0
- package/dist/tui/components/StackedTaskList.d.ts.map +1 -0
- package/dist/tui/components/StackedTaskList.js +120 -0
- package/dist/tui/components/StackedTaskList.js.map +1 -0
- package/dist/tui/components/StatusBar.d.ts +14 -0
- package/dist/tui/components/StatusBar.d.ts.map +1 -0
- package/dist/tui/components/StatusBar.js +42 -0
- package/dist/tui/components/StatusBar.js.map +1 -0
- package/dist/tui/components/TaskCard.d.ts +25 -0
- package/dist/tui/components/TaskCard.d.ts.map +1 -0
- package/dist/tui/components/TaskCard.js +221 -0
- package/dist/tui/components/TaskCard.js.map +1 -0
- package/dist/tui/components/TaskCardMeasure.d.ts +31 -0
- package/dist/tui/components/TaskCardMeasure.d.ts.map +1 -0
- package/dist/tui/components/TaskCardMeasure.js +133 -0
- package/dist/tui/components/TaskCardMeasure.js.map +1 -0
- package/dist/tui/components/TaskDetail.d.ts +13 -0
- package/dist/tui/components/TaskDetail.d.ts.map +1 -0
- package/dist/tui/components/TaskDetail.js +64 -0
- package/dist/tui/components/TaskDetail.js.map +1 -0
- package/dist/tui/components/TaskList.d.ts +12 -0
- package/dist/tui/components/TaskList.d.ts.map +1 -0
- package/dist/tui/components/TaskList.js +75 -0
- package/dist/tui/components/TaskList.js.map +1 -0
- package/dist/tui/components/index.d.ts +31 -0
- package/dist/tui/components/index.d.ts.map +1 -0
- package/dist/tui/components/index.js +43 -0
- package/dist/tui/components/index.js.map +1 -0
- package/dist/tui/hooks/index.d.ts +3 -0
- package/dist/tui/hooks/index.d.ts.map +1 -0
- package/dist/tui/hooks/index.js +8 -0
- package/dist/tui/hooks/index.js.map +1 -0
- package/dist/tui/hooks/useBrainfileLoader.d.ts +5 -0
- package/dist/tui/hooks/useBrainfileLoader.d.ts.map +1 -0
- package/dist/tui/hooks/useBrainfileLoader.js +237 -0
- package/dist/tui/hooks/useBrainfileLoader.js.map +1 -0
- package/dist/tui/hooks/useKeyboardNavigation.d.ts +20 -0
- package/dist/tui/hooks/useKeyboardNavigation.d.ts.map +1 -0
- package/dist/tui/hooks/useKeyboardNavigation.js +806 -0
- package/dist/tui/hooks/useKeyboardNavigation.js.map +1 -0
- package/dist/tui/index.d.ts +10 -0
- package/dist/tui/index.d.ts.map +1 -0
- package/dist/tui/index.js +38 -0
- package/dist/tui/index.js.map +1 -0
- package/dist/tui/theme.d.ts +119 -0
- package/dist/tui/theme.d.ts.map +1 -0
- package/dist/tui/theme.js +138 -0
- package/dist/tui/theme.js.map +1 -0
- package/dist/tui/types.d.ts +53 -0
- package/dist/tui/types.d.ts.map +1 -0
- package/dist/tui/types.js +12 -0
- package/dist/tui/types.js.map +1 -0
- package/dist/tui/utils.d.ts +24 -0
- package/dist/tui/utils.d.ts.map +1 -0
- package/dist/tui/utils.js +161 -0
- package/dist/tui/utils.js.map +1 -0
- package/dist/utils/archive.d.ts +57 -0
- package/dist/utils/archive.d.ts.map +1 -0
- package/dist/utils/archive.js +304 -0
- package/dist/utils/archive.js.map +1 -0
- package/dist/utils/auth-store.d.ts +69 -0
- package/dist/utils/auth-store.d.ts.map +1 -0
- package/dist/utils/auth-store.js +308 -0
- package/dist/utils/auth-store.js.map +1 -0
- package/dist/utils/board-types.d.ts +13 -0
- package/dist/utils/board-types.d.ts.map +1 -0
- package/dist/utils/board-types.js +7 -0
- package/dist/utils/board-types.js.map +1 -0
- package/dist/utils/brainfile-finder.d.ts +12 -0
- package/dist/utils/brainfile-finder.d.ts.map +1 -0
- package/dist/utils/brainfile-finder.js +69 -0
- package/dist/utils/brainfile-finder.js.map +1 -0
- package/dist/utils/brainfile-path.d.ts +10 -0
- package/dist/utils/brainfile-path.d.ts.map +1 -0
- package/dist/utils/brainfile-path.js +88 -0
- package/dist/utils/brainfile-path.js.map +1 -0
- package/dist/utils/cli-error.d.ts +44 -0
- package/dist/utils/cli-error.d.ts.map +1 -0
- package/dist/utils/cli-error.js +85 -0
- package/dist/utils/cli-error.js.map +1 -0
- package/dist/utils/config.d.ts +119 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/config.js +304 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/contractSpec.d.ts +13 -0
- package/dist/utils/contractSpec.d.ts.map +1 -0
- package/dist/utils/contractSpec.js +61 -0
- package/dist/utils/contractSpec.js.map +1 -0
- package/dist/utils/dot-brainfile.d.ts +9 -0
- package/dist/utils/dot-brainfile.d.ts.map +1 -0
- package/dist/utils/dot-brainfile.js +74 -0
- package/dist/utils/dot-brainfile.js.map +1 -0
- package/dist/utils/errorHandler.d.ts +106 -0
- package/dist/utils/errorHandler.d.ts.map +1 -0
- package/dist/utils/errorHandler.js +223 -0
- package/dist/utils/errorHandler.js.map +1 -0
- package/dist/utils/git-helper.d.ts +17 -0
- package/dist/utils/git-helper.d.ts.map +1 -0
- package/dist/utils/git-helper.js +62 -0
- package/dist/utils/git-helper.js.map +1 -0
- package/dist/utils/github-auth.d.ts +56 -0
- package/dist/utils/github-auth.d.ts.map +1 -0
- package/dist/utils/github-auth.js +341 -0
- package/dist/utils/github-auth.js.map +1 -0
- package/dist/utils/hook-parser.d.ts +15 -0
- package/dist/utils/hook-parser.d.ts.map +1 -0
- package/dist/utils/hook-parser.js +69 -0
- package/dist/utils/hook-parser.js.map +1 -0
- package/dist/utils/hook-settings.d.ts +27 -0
- package/dist/utils/hook-settings.d.ts.map +1 -0
- package/dist/utils/hook-settings.js +364 -0
- package/dist/utils/hook-settings.js.map +1 -0
- package/dist/utils/linear-auth.d.ts +59 -0
- package/dist/utils/linear-auth.d.ts.map +1 -0
- package/dist/utils/linear-auth.js +295 -0
- package/dist/utils/linear-auth.js.map +1 -0
- package/dist/utils/logger.d.ts +34 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +56 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/strict-validation.d.ts +8 -0
- package/dist/utils/strict-validation.d.ts.map +1 -0
- package/dist/utils/strict-validation.js +41 -0
- package/dist/utils/strict-validation.js.map +1 -0
- package/dist/utils/v2-detect.d.ts +28 -0
- package/dist/utils/v2-detect.d.ts.map +1 -0
- package/dist/utils/v2-detect.js +112 -0
- package/dist/utils/v2-detect.js.map +1 -0
- package/dist/utils/v2-only.d.ts +9 -0
- package/dist/utils/v2-only.d.ts.map +1 -0
- package/dist/utils/v2-only.js +64 -0
- package/dist/utils/v2-only.js.map +1 -0
- package/dist/utils/workspace-format.d.ts +25 -0
- package/dist/utils/workspace-format.d.ts.map +1 -0
- package/dist/utils/workspace-format.js +106 -0
- package/dist/utils/workspace-format.js.map +1 -0
- package/dist/validation/command-lint.d.ts +11 -0
- package/dist/validation/command-lint.d.ts.map +1 -0
- package/dist/validation/command-lint.js +88 -0
- package/dist/validation/command-lint.js.map +1 -0
- package/package.json +69 -0
|
@@ -0,0 +1,715 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.CONTRACT_GRAPH_HELP = exports.CONTRACT_ACTIVATE_HELP = exports.CONTRACT_ATTACH_HELP = exports.CONTRACT_VALIDATE_HELP = exports.CONTRACT_DELIVER_HELP = exports.CONTRACT_PICKUP_HELP = exports.CONTRACT_COMMAND_HELP = void 0;
|
|
37
|
+
exports.parseContractGraphArgs = parseContractGraphArgs;
|
|
38
|
+
exports.contractGraphCommand = contractGraphCommand;
|
|
39
|
+
exports.contractPickupCommand = contractPickupCommand;
|
|
40
|
+
exports.contractDeliverCommand = contractDeliverCommand;
|
|
41
|
+
exports.contractValidateCommand = contractValidateCommand;
|
|
42
|
+
exports.contractAttachCommand = contractAttachCommand;
|
|
43
|
+
exports.contractActivateCommand = contractActivateCommand;
|
|
44
|
+
const contractRunner_1 = require("../lib/contractRunner");
|
|
45
|
+
const logger_1 = require("../utils/logger");
|
|
46
|
+
const fs = __importStar(require("fs"));
|
|
47
|
+
const path = __importStar(require("path"));
|
|
48
|
+
const core_1 = require("@brainfile/core");
|
|
49
|
+
const cli_error_1 = require("../utils/cli-error");
|
|
50
|
+
const contractSpec_1 = require("../utils/contractSpec");
|
|
51
|
+
const brainfile_path_1 = require("../utils/brainfile-path");
|
|
52
|
+
const v2_detect_1 = require("../utils/v2-detect");
|
|
53
|
+
const command_lint_1 = require("../validation/command-lint");
|
|
54
|
+
exports.CONTRACT_COMMAND_HELP = `
|
|
55
|
+
Workflow (PM → Agent → PM):
|
|
56
|
+
1) PM creates a task with a contract:
|
|
57
|
+
brainfile add -c todo -t "Feature" --assignee codex --with-contract --deliverable "file:src/feature.ts:Impl"
|
|
58
|
+
|
|
59
|
+
2) Agent picks it up (status → in_progress):
|
|
60
|
+
brainfile contract pickup -t task-123
|
|
61
|
+
|
|
62
|
+
3) Agent delivers (status → delivered):
|
|
63
|
+
brainfile contract deliver -t task-123
|
|
64
|
+
|
|
65
|
+
4) PM validates (status → done | failed):
|
|
66
|
+
brainfile contract validate -t task-123
|
|
67
|
+
|
|
68
|
+
Contract metrics are tracked on the task file under contract.metrics.
|
|
69
|
+
|
|
70
|
+
Find available contracts:
|
|
71
|
+
brainfile list --contract ready
|
|
72
|
+
`.trimEnd();
|
|
73
|
+
exports.CONTRACT_PICKUP_HELP = `
|
|
74
|
+
Examples:
|
|
75
|
+
brainfile contract pickup -t task-123
|
|
76
|
+
brainfile contract pickup -t task-123 -f .brainfile/brainfile.md
|
|
77
|
+
`.trimEnd();
|
|
78
|
+
exports.CONTRACT_DELIVER_HELP = `
|
|
79
|
+
Examples:
|
|
80
|
+
brainfile contract deliver -t task-123
|
|
81
|
+
`.trimEnd();
|
|
82
|
+
exports.CONTRACT_VALIDATE_HELP = `
|
|
83
|
+
Examples:
|
|
84
|
+
brainfile contract validate -t task-123
|
|
85
|
+
`.trimEnd();
|
|
86
|
+
exports.CONTRACT_ATTACH_HELP = `
|
|
87
|
+
Examples:
|
|
88
|
+
brainfile contract attach -t task-123 \\
|
|
89
|
+
--deliverable "file:src/feature.ts:Implementation" \\
|
|
90
|
+
--validation "npm test" \\
|
|
91
|
+
--constraint "Make minimal changes"
|
|
92
|
+
`.trimEnd();
|
|
93
|
+
exports.CONTRACT_ACTIVATE_HELP = `
|
|
94
|
+
Examples:
|
|
95
|
+
# Activate a single contract (draft → ready)
|
|
96
|
+
brainfile contract activate -t task-123
|
|
97
|
+
|
|
98
|
+
# Activate all draft contracts whose parent is epic-1
|
|
99
|
+
brainfile contract activate --parent epic-1
|
|
100
|
+
`.trimEnd();
|
|
101
|
+
exports.CONTRACT_GRAPH_HELP = `
|
|
102
|
+
Examples:
|
|
103
|
+
brainfile contract graph \\
|
|
104
|
+
--task research-1 --deliverable "file:docs/findings.md" \\
|
|
105
|
+
--task impl-1 --deliverable "file:src/bridge.ts" --depends-on research-1 \\
|
|
106
|
+
--task test-1 --deliverable "test:src/tests/bridge.test.ts" --depends-on impl-1 \\
|
|
107
|
+
--ready
|
|
108
|
+
|
|
109
|
+
brainfile contract graph --show
|
|
110
|
+
`.trimEnd();
|
|
111
|
+
class MissingDependencyError extends Error {
|
|
112
|
+
constructor(taskId, dependencyId) {
|
|
113
|
+
super(`Task ${taskId} depends on missing task ${dependencyId}`);
|
|
114
|
+
this.name = 'MissingDependencyError';
|
|
115
|
+
this.taskId = taskId;
|
|
116
|
+
this.dependencyId = dependencyId;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
class DependencyCycleError extends Error {
|
|
120
|
+
constructor(cycle) {
|
|
121
|
+
super(`Dependency cycle detected: ${cycle.join(' -> ')}`);
|
|
122
|
+
this.name = 'DependencyCycleError';
|
|
123
|
+
this.cycle = cycle;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
function isUnsafeTaskId(taskId) {
|
|
127
|
+
const trimmed = taskId.trim();
|
|
128
|
+
if (!trimmed || trimmed !== taskId) {
|
|
129
|
+
return true;
|
|
130
|
+
}
|
|
131
|
+
if (taskId === '.' || taskId === '..') {
|
|
132
|
+
return true;
|
|
133
|
+
}
|
|
134
|
+
return /[\\/]/.test(taskId);
|
|
135
|
+
}
|
|
136
|
+
function normalizeDependencyIds(dependsOn) {
|
|
137
|
+
if (!Array.isArray(dependsOn)) {
|
|
138
|
+
return undefined;
|
|
139
|
+
}
|
|
140
|
+
const normalized = [...new Set(dependsOn.map((value) => value.trim()).filter(Boolean))];
|
|
141
|
+
return normalized.length > 0 ? normalized : undefined;
|
|
142
|
+
}
|
|
143
|
+
function taskDependsOn(task) {
|
|
144
|
+
const rawDependsOn = task?.dependsOn;
|
|
145
|
+
if (!Array.isArray(rawDependsOn)) {
|
|
146
|
+
return undefined;
|
|
147
|
+
}
|
|
148
|
+
return rawDependsOn.filter((value) => typeof value === 'string');
|
|
149
|
+
}
|
|
150
|
+
function topologicalSort(nodes) {
|
|
151
|
+
const dependenciesById = new Map();
|
|
152
|
+
for (const node of nodes) {
|
|
153
|
+
if (dependenciesById.has(node.id)) {
|
|
154
|
+
throw new Error(`Duplicate graph node: ${node.id}`);
|
|
155
|
+
}
|
|
156
|
+
dependenciesById.set(node.id, normalizeDependencyIds(node.dependsOn ? [...node.dependsOn] : undefined) ?? []);
|
|
157
|
+
}
|
|
158
|
+
const state = new Map();
|
|
159
|
+
const stack = [];
|
|
160
|
+
const order = [];
|
|
161
|
+
const visit = (taskId) => {
|
|
162
|
+
const currentState = state.get(taskId);
|
|
163
|
+
if (currentState === 'done') {
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
if (currentState === 'visiting') {
|
|
167
|
+
const cycleStart = stack.indexOf(taskId);
|
|
168
|
+
const cycle = cycleStart >= 0
|
|
169
|
+
? [...stack.slice(cycleStart), taskId]
|
|
170
|
+
: [taskId, taskId];
|
|
171
|
+
throw new DependencyCycleError(cycle);
|
|
172
|
+
}
|
|
173
|
+
state.set(taskId, 'visiting');
|
|
174
|
+
stack.push(taskId);
|
|
175
|
+
for (const dependencyId of dependenciesById.get(taskId) ?? []) {
|
|
176
|
+
if (!dependenciesById.has(dependencyId)) {
|
|
177
|
+
throw new MissingDependencyError(taskId, dependencyId);
|
|
178
|
+
}
|
|
179
|
+
visit(dependencyId);
|
|
180
|
+
}
|
|
181
|
+
stack.pop();
|
|
182
|
+
state.set(taskId, 'done');
|
|
183
|
+
order.push(taskId);
|
|
184
|
+
};
|
|
185
|
+
for (const taskId of dependenciesById.keys()) {
|
|
186
|
+
visit(taskId);
|
|
187
|
+
}
|
|
188
|
+
return order;
|
|
189
|
+
}
|
|
190
|
+
function boardTasks(board) {
|
|
191
|
+
return board.columns.flatMap((column) => column.tasks);
|
|
192
|
+
}
|
|
193
|
+
function validateTaskDependencyGraph(tasks) {
|
|
194
|
+
try {
|
|
195
|
+
return topologicalSort(tasks.map((task) => ({
|
|
196
|
+
id: task.id,
|
|
197
|
+
dependsOn: taskDependsOn(task),
|
|
198
|
+
})));
|
|
199
|
+
}
|
|
200
|
+
catch (error) {
|
|
201
|
+
if (error instanceof DependencyCycleError || error instanceof MissingDependencyError) {
|
|
202
|
+
throw (0, cli_error_1.validationError)(error.message);
|
|
203
|
+
}
|
|
204
|
+
throw error;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
function renderTaskDependencyGraph(tasks) {
|
|
208
|
+
if (tasks.length === 0) {
|
|
209
|
+
return '(no active tasks)';
|
|
210
|
+
}
|
|
211
|
+
const order = validateTaskDependencyGraph(tasks);
|
|
212
|
+
const taskById = new Map(tasks.map((task) => [task.id, task]));
|
|
213
|
+
return order
|
|
214
|
+
.map((taskId) => {
|
|
215
|
+
const dependsOn = normalizeDependencyIds(taskDependsOn(taskById.get(taskId)));
|
|
216
|
+
return dependsOn && dependsOn.length > 0
|
|
217
|
+
? `${dependsOn.join(', ')} -> ${taskId}`
|
|
218
|
+
: taskId;
|
|
219
|
+
})
|
|
220
|
+
.join('\n');
|
|
221
|
+
}
|
|
222
|
+
function updateBoardTask(board, taskId, updater) {
|
|
223
|
+
let found = false;
|
|
224
|
+
const columns = board.columns.map((column) => ({
|
|
225
|
+
...column,
|
|
226
|
+
tasks: column.tasks.map((task) => {
|
|
227
|
+
if (task.id !== taskId) {
|
|
228
|
+
return task;
|
|
229
|
+
}
|
|
230
|
+
found = true;
|
|
231
|
+
return updater(task);
|
|
232
|
+
}),
|
|
233
|
+
}));
|
|
234
|
+
if (!found) {
|
|
235
|
+
throw (0, cli_error_1.operationFailed)(`Task not found: ${taskId}`);
|
|
236
|
+
}
|
|
237
|
+
return { ...board, columns };
|
|
238
|
+
}
|
|
239
|
+
function applyGraphTaskUpdate(task, spec, ready, now) {
|
|
240
|
+
let contract;
|
|
241
|
+
try {
|
|
242
|
+
contract = (0, contractSpec_1.buildContract)({
|
|
243
|
+
deliverableSpecs: spec.deliverable,
|
|
244
|
+
validationCommands: spec.validation,
|
|
245
|
+
constraints: spec.constraint,
|
|
246
|
+
status: ready ? 'ready' : 'draft',
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
catch (error) {
|
|
250
|
+
throw (0, cli_error_1.validationError)(error.message);
|
|
251
|
+
}
|
|
252
|
+
const nextTask = {
|
|
253
|
+
...task,
|
|
254
|
+
contract,
|
|
255
|
+
updatedAt: now,
|
|
256
|
+
};
|
|
257
|
+
if (spec.dependsOn !== undefined) {
|
|
258
|
+
const normalized = normalizeDependencyIds(spec.dependsOn);
|
|
259
|
+
if (normalized) {
|
|
260
|
+
nextTask.dependsOn = normalized;
|
|
261
|
+
}
|
|
262
|
+
else {
|
|
263
|
+
delete nextTask.dependsOn;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
return nextTask;
|
|
267
|
+
}
|
|
268
|
+
function ensureUniqueGraphTasks(specs) {
|
|
269
|
+
const seen = new Set();
|
|
270
|
+
for (const spec of specs) {
|
|
271
|
+
if (seen.has(spec.task)) {
|
|
272
|
+
throw (0, cli_error_1.validationError)(`Duplicate task in graph input: ${spec.task}`);
|
|
273
|
+
}
|
|
274
|
+
seen.add(spec.task);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
function loadActiveTasks(filePath) {
|
|
278
|
+
if ((0, v2_detect_1.isV2)(filePath)) {
|
|
279
|
+
const dirs = (0, v2_detect_1.getV2Dirs)(filePath);
|
|
280
|
+
return (0, core_1.readTasksDir)(dirs.boardDir).map((doc) => doc.task);
|
|
281
|
+
}
|
|
282
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
283
|
+
const parsed = core_1.Brainfile.parseWithErrors(content);
|
|
284
|
+
if (!parsed.board) {
|
|
285
|
+
throw (0, cli_error_1.operationFailed)(parsed.error || 'Failed to parse brainfile');
|
|
286
|
+
}
|
|
287
|
+
return boardTasks(parsed.board);
|
|
288
|
+
}
|
|
289
|
+
function parseContractGraphArgs(args) {
|
|
290
|
+
const options = {
|
|
291
|
+
file: 'brainfile.md',
|
|
292
|
+
tasks: [],
|
|
293
|
+
};
|
|
294
|
+
let currentTask;
|
|
295
|
+
const readValue = (index, flag) => {
|
|
296
|
+
const value = args[index + 1];
|
|
297
|
+
if (value === undefined) {
|
|
298
|
+
throw (0, cli_error_1.validationError)(`Missing value for ${flag}`);
|
|
299
|
+
}
|
|
300
|
+
return value;
|
|
301
|
+
};
|
|
302
|
+
const pushValue = (spec, key, value, flag) => {
|
|
303
|
+
if (!spec) {
|
|
304
|
+
throw (0, cli_error_1.validationError)(`${flag} must follow a --task declaration`);
|
|
305
|
+
}
|
|
306
|
+
const existing = spec[key];
|
|
307
|
+
if (!existing) {
|
|
308
|
+
spec[key] = [value];
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
spec[key] = Array.isArray(existing) ? [...existing, value] : [existing, value];
|
|
312
|
+
};
|
|
313
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
314
|
+
const token = args[index];
|
|
315
|
+
switch (token) {
|
|
316
|
+
case '-f':
|
|
317
|
+
case '--file':
|
|
318
|
+
options.file = readValue(index, token);
|
|
319
|
+
index += 1;
|
|
320
|
+
break;
|
|
321
|
+
case '--ready':
|
|
322
|
+
options.ready = true;
|
|
323
|
+
break;
|
|
324
|
+
case '--show':
|
|
325
|
+
options.show = true;
|
|
326
|
+
break;
|
|
327
|
+
case '-t':
|
|
328
|
+
case '--task': {
|
|
329
|
+
const task = readValue(index, token);
|
|
330
|
+
currentTask = { task };
|
|
331
|
+
options.tasks.push(currentTask);
|
|
332
|
+
index += 1;
|
|
333
|
+
break;
|
|
334
|
+
}
|
|
335
|
+
case '--deliverable':
|
|
336
|
+
pushValue(currentTask, 'deliverable', readValue(index, token), token);
|
|
337
|
+
index += 1;
|
|
338
|
+
break;
|
|
339
|
+
case '--validation':
|
|
340
|
+
pushValue(currentTask, 'validation', readValue(index, token), token);
|
|
341
|
+
index += 1;
|
|
342
|
+
break;
|
|
343
|
+
case '--constraint':
|
|
344
|
+
pushValue(currentTask, 'constraint', readValue(index, token), token);
|
|
345
|
+
index += 1;
|
|
346
|
+
break;
|
|
347
|
+
case '--depends-on':
|
|
348
|
+
if (!currentTask) {
|
|
349
|
+
throw (0, cli_error_1.validationError)('--depends-on must follow a --task declaration');
|
|
350
|
+
}
|
|
351
|
+
currentTask.dependsOn = [...(currentTask.dependsOn ?? []), readValue(index, token)];
|
|
352
|
+
index += 1;
|
|
353
|
+
break;
|
|
354
|
+
default:
|
|
355
|
+
throw (0, cli_error_1.validationError)(`Unknown contract graph flag: ${token}`);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
if (options.show && options.tasks.length > 0) {
|
|
359
|
+
throw (0, cli_error_1.validationError)('--show cannot be combined with task graph mutations');
|
|
360
|
+
}
|
|
361
|
+
if (!options.show && options.tasks.length === 0) {
|
|
362
|
+
throw (0, cli_error_1.missingRequired)('--task', 'brainfile contract graph --task <task-id> [graph options]');
|
|
363
|
+
}
|
|
364
|
+
return options;
|
|
365
|
+
}
|
|
366
|
+
function contractGraphCommand(options, logger = logger_1.defaultLogger) {
|
|
367
|
+
const filePath = (0, brainfile_path_1.resolveCliBrainfilePath)(options.file);
|
|
368
|
+
if (!fs.existsSync(filePath)) {
|
|
369
|
+
throw (0, cli_error_1.operationFailed)(`File not found: ${filePath}`);
|
|
370
|
+
}
|
|
371
|
+
if (options.show) {
|
|
372
|
+
const activeTasks = loadActiveTasks(filePath);
|
|
373
|
+
const graph = renderTaskDependencyGraph(activeTasks);
|
|
374
|
+
logger.log(graph);
|
|
375
|
+
return { success: true, attached: [], order: validateTaskDependencyGraph(activeTasks), graph };
|
|
376
|
+
}
|
|
377
|
+
const specs = options.tasks ?? [];
|
|
378
|
+
ensureUniqueGraphTasks(specs);
|
|
379
|
+
const now = new Date().toISOString();
|
|
380
|
+
if ((0, v2_detect_1.isV2)(filePath)) {
|
|
381
|
+
const dirs = (0, v2_detect_1.getV2Dirs)(filePath);
|
|
382
|
+
const docs = (0, core_1.readTasksDir)(dirs.boardDir);
|
|
383
|
+
const docById = new Map(docs.map((doc) => [doc.task.id, doc]));
|
|
384
|
+
for (const spec of specs) {
|
|
385
|
+
if (isUnsafeTaskId(spec.task)) {
|
|
386
|
+
throw (0, cli_error_1.operationFailed)(`Invalid task ID: ${spec.task}`);
|
|
387
|
+
}
|
|
388
|
+
if (!docById.has(spec.task)) {
|
|
389
|
+
throw (0, cli_error_1.operationFailed)(`Task not found: ${spec.task}`);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
const nextTasks = new Map(docs.map((doc) => [doc.task.id, { ...doc.task }]));
|
|
393
|
+
for (const spec of specs) {
|
|
394
|
+
nextTasks.set(spec.task, applyGraphTaskUpdate(nextTasks.get(spec.task), spec, Boolean(options.ready), now));
|
|
395
|
+
}
|
|
396
|
+
const order = validateTaskDependencyGraph([...nextTasks.values()]);
|
|
397
|
+
const graph = renderTaskDependencyGraph([...nextTasks.values()]);
|
|
398
|
+
for (const taskId of order) {
|
|
399
|
+
if (!specs.some((spec) => spec.task === taskId)) {
|
|
400
|
+
continue;
|
|
401
|
+
}
|
|
402
|
+
const doc = docById.get(taskId);
|
|
403
|
+
(0, core_1.writeTaskFile)(doc.filePath, nextTasks.get(taskId), doc.body);
|
|
404
|
+
}
|
|
405
|
+
logger.log(`Contract graph attached (${options.ready ? 'ready' : 'draft'}): ${specs.map((spec) => spec.task).join(', ')}`);
|
|
406
|
+
return {
|
|
407
|
+
success: true,
|
|
408
|
+
attached: specs.map((spec) => spec.task),
|
|
409
|
+
order,
|
|
410
|
+
graph,
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
414
|
+
const parsed = core_1.Brainfile.parseWithErrors(content);
|
|
415
|
+
if (!parsed.board) {
|
|
416
|
+
throw (0, cli_error_1.operationFailed)(parsed.error || 'Failed to parse brainfile');
|
|
417
|
+
}
|
|
418
|
+
let board = parsed.board;
|
|
419
|
+
for (const spec of specs) {
|
|
420
|
+
if (!(0, core_1.findTaskById)(board, spec.task)) {
|
|
421
|
+
throw (0, cli_error_1.operationFailed)(`Task not found: ${spec.task}`);
|
|
422
|
+
}
|
|
423
|
+
board = updateBoardTask(board, spec.task, (task) => applyGraphTaskUpdate(task, spec, Boolean(options.ready), now));
|
|
424
|
+
}
|
|
425
|
+
const order = validateTaskDependencyGraph(boardTasks(board));
|
|
426
|
+
const graph = renderTaskDependencyGraph(boardTasks(board));
|
|
427
|
+
fs.writeFileSync(filePath, core_1.Brainfile.serialize(board), 'utf-8');
|
|
428
|
+
logger.log(`Contract graph attached (${options.ready ? 'ready' : 'draft'}): ${specs.map((spec) => spec.task).join(', ')}`);
|
|
429
|
+
return {
|
|
430
|
+
success: true,
|
|
431
|
+
attached: specs.map((spec) => spec.task),
|
|
432
|
+
order,
|
|
433
|
+
graph,
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
function contractPickupCommand(options, logger = logger_1.defaultLogger) {
|
|
437
|
+
if (!options.task) {
|
|
438
|
+
throw (0, cli_error_1.missingRequired)('--task', 'brainfile contract pickup --task <task-id> [--file <path>]');
|
|
439
|
+
}
|
|
440
|
+
const filePath = (0, brainfile_path_1.resolveCliBrainfilePath)(options.file);
|
|
441
|
+
const result = (0, contractRunner_1.pickupContract)({ filePath, taskId: options.task });
|
|
442
|
+
if ('error' in result) {
|
|
443
|
+
throw (0, cli_error_1.operationFailed)(result.error);
|
|
444
|
+
}
|
|
445
|
+
logger.log(result.markdown);
|
|
446
|
+
return { success: true, markdown: result.markdown };
|
|
447
|
+
}
|
|
448
|
+
function contractDeliverCommand(options, logger = logger_1.defaultLogger) {
|
|
449
|
+
if (!options.task) {
|
|
450
|
+
throw (0, cli_error_1.missingRequired)('--task', 'brainfile contract deliver --task <task-id> [--file <path>]');
|
|
451
|
+
}
|
|
452
|
+
const filePath = (0, brainfile_path_1.resolveCliBrainfilePath)(options.file);
|
|
453
|
+
const result = (0, contractRunner_1.deliverContract)({ filePath, taskId: options.task });
|
|
454
|
+
if ('error' in result) {
|
|
455
|
+
throw (0, cli_error_1.operationFailed)(result.error);
|
|
456
|
+
}
|
|
457
|
+
logger.log(`Contract delivered: ${options.task}`);
|
|
458
|
+
return { success: true };
|
|
459
|
+
}
|
|
460
|
+
function contractValidateCommand(options, logger = logger_1.defaultLogger) {
|
|
461
|
+
if (!options.task) {
|
|
462
|
+
throw (0, cli_error_1.missingRequired)('--task', 'brainfile contract validate --task <task-id> [--file <path>]');
|
|
463
|
+
}
|
|
464
|
+
const filePath = (0, brainfile_path_1.resolveCliBrainfilePath)(options.file);
|
|
465
|
+
const result = (0, contractRunner_1.validateContract)({ filePath, taskId: options.task });
|
|
466
|
+
if ('error' in result) {
|
|
467
|
+
throw (0, cli_error_1.operationFailed)(result.error);
|
|
468
|
+
}
|
|
469
|
+
const deliverablesOk = result.deliverableChecks.every((c) => c.ok);
|
|
470
|
+
const commandsOk = result.commandResults.every((c) => c.exitCode === 0);
|
|
471
|
+
const ok = result.ok;
|
|
472
|
+
// Show warnings first (before success/fail status)
|
|
473
|
+
if (result.warnings.length > 0) {
|
|
474
|
+
logger.log('');
|
|
475
|
+
logger.log('⚠️ Warnings:');
|
|
476
|
+
for (const warning of result.warnings) {
|
|
477
|
+
logger.log(` Command: ${warning.command}`);
|
|
478
|
+
logger.log(` ${warning.message}`);
|
|
479
|
+
logger.log('');
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
logger.log(`Contract validation ${ok ? 'passed' : 'failed'}: ${options.task}`);
|
|
483
|
+
if (!deliverablesOk) {
|
|
484
|
+
logger.log('');
|
|
485
|
+
logger.log('Deliverables:');
|
|
486
|
+
for (const check of result.deliverableChecks) {
|
|
487
|
+
if (check.deliverable.type !== 'file')
|
|
488
|
+
continue;
|
|
489
|
+
const status = check.ok ? 'OK' : 'MISSING';
|
|
490
|
+
logger.log(`- [${status}] ${check.deliverable.path}${check.resolvedPath ? ` (${check.resolvedPath})` : ''}`);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
if (!commandsOk && result.commandResults.length > 0) {
|
|
494
|
+
logger.log('');
|
|
495
|
+
logger.log('Validation commands:');
|
|
496
|
+
for (const cmd of result.commandResults) {
|
|
497
|
+
logger.log(`- [${cmd.exitCode === 0 ? 'OK' : `FAIL(${cmd.exitCode})`}] ${cmd.command}`);
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
return { success: ok };
|
|
501
|
+
}
|
|
502
|
+
/**
|
|
503
|
+
* Attach a new contract to an existing task (default status=draft).
|
|
504
|
+
* This is for programmatic contract creation (no manual YAML editing).
|
|
505
|
+
* Pass ready:true via options to make the contract immediately dispatchable.
|
|
506
|
+
*/
|
|
507
|
+
function contractAttachCommand(options, logger = logger_1.defaultLogger) {
|
|
508
|
+
if (!options.task) {
|
|
509
|
+
throw (0, cli_error_1.missingRequired)('--task', 'brainfile contract attach --task <task-id> [--file <path>] [--deliverable ...]');
|
|
510
|
+
}
|
|
511
|
+
const filePath = (0, brainfile_path_1.resolveCliBrainfilePath)(options.file);
|
|
512
|
+
if (!fs.existsSync(filePath)) {
|
|
513
|
+
throw (0, cli_error_1.operationFailed)(`File not found: ${filePath}`);
|
|
514
|
+
}
|
|
515
|
+
const validationCommands = Array.isArray(options.validation)
|
|
516
|
+
? options.validation
|
|
517
|
+
: options.validation
|
|
518
|
+
? [options.validation]
|
|
519
|
+
: [];
|
|
520
|
+
for (const warning of (0, command_lint_1.lintValidationCommands)(validationCommands, filePath)) {
|
|
521
|
+
logger.warn(`Warning: ${warning.message}`);
|
|
522
|
+
}
|
|
523
|
+
let contract;
|
|
524
|
+
try {
|
|
525
|
+
contract = (0, contractSpec_1.buildContract)({
|
|
526
|
+
deliverableSpecs: options.deliverable,
|
|
527
|
+
validationCommands: options.validation,
|
|
528
|
+
constraints: options.constraint,
|
|
529
|
+
status: options.ready ? 'ready' : 'draft',
|
|
530
|
+
});
|
|
531
|
+
}
|
|
532
|
+
catch (e) {
|
|
533
|
+
throw (0, cli_error_1.validationError)(e.message);
|
|
534
|
+
}
|
|
535
|
+
if ((0, v2_detect_1.isV2)(filePath)) {
|
|
536
|
+
if (isUnsafeTaskId(options.task)) {
|
|
537
|
+
throw (0, cli_error_1.operationFailed)(`Invalid task ID: ${options.task}`);
|
|
538
|
+
}
|
|
539
|
+
const dirs = (0, v2_detect_1.getV2Dirs)(filePath);
|
|
540
|
+
const found = (0, v2_detect_1.findV2Task)(dirs, options.task, false);
|
|
541
|
+
if (!found || found.isLog) {
|
|
542
|
+
throw (0, cli_error_1.operationFailed)(`Task not found: ${options.task}`);
|
|
543
|
+
}
|
|
544
|
+
found.doc.task.contract = contract;
|
|
545
|
+
(0, core_1.writeTaskFile)(found.filePath, found.doc.task, found.doc.body);
|
|
546
|
+
logger.log(`Contract attached: ${options.task}`);
|
|
547
|
+
return { success: true };
|
|
548
|
+
}
|
|
549
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
550
|
+
const parsed = core_1.Brainfile.parseWithErrors(content);
|
|
551
|
+
if (!parsed.board) {
|
|
552
|
+
throw (0, cli_error_1.operationFailed)(parsed.error || 'Failed to parse brainfile');
|
|
553
|
+
}
|
|
554
|
+
const taskInfo = (0, core_1.findTaskById)(parsed.board, options.task);
|
|
555
|
+
if (!taskInfo) {
|
|
556
|
+
throw (0, cli_error_1.operationFailed)(`Task not found: ${options.task}`);
|
|
557
|
+
}
|
|
558
|
+
const result = (0, core_1.setTaskContract)(parsed.board, options.task, contract);
|
|
559
|
+
if (!result.success || !result.board) {
|
|
560
|
+
throw (0, cli_error_1.operationFailed)(result.error || 'Failed to attach contract');
|
|
561
|
+
}
|
|
562
|
+
fs.writeFileSync(filePath, core_1.Brainfile.serialize(result.board), 'utf-8');
|
|
563
|
+
logger.log(`Contract attached: ${options.task}`);
|
|
564
|
+
return { success: true };
|
|
565
|
+
}
|
|
566
|
+
/**
|
|
567
|
+
* Activate one or more draft contracts (draft → ready).
|
|
568
|
+
*
|
|
569
|
+
* - Single task: `--task <id>`
|
|
570
|
+
* - Bulk by parent: `--parent <epic-id>` (activates all draft children)
|
|
571
|
+
*/
|
|
572
|
+
function contractActivateCommand(options, logger = logger_1.defaultLogger) {
|
|
573
|
+
if (!options.task && !options.parent) {
|
|
574
|
+
throw (0, cli_error_1.missingRequired)('--task or --parent', 'brainfile contract activate -t <task-id> | --parent <epic-id>');
|
|
575
|
+
}
|
|
576
|
+
const filePath = (0, brainfile_path_1.resolveCliBrainfilePath)(options.file);
|
|
577
|
+
if (!fs.existsSync(filePath)) {
|
|
578
|
+
throw (0, cli_error_1.operationFailed)(`File not found: ${filePath}`);
|
|
579
|
+
}
|
|
580
|
+
const activated = [];
|
|
581
|
+
// ── V2 per-task file architecture ──────────────────────────────────────────
|
|
582
|
+
if ((0, v2_detect_1.isV2)(filePath)) {
|
|
583
|
+
const dirs = (0, v2_detect_1.getV2Dirs)(filePath);
|
|
584
|
+
if (options.task) {
|
|
585
|
+
// Single task activation
|
|
586
|
+
if (isUnsafeTaskId(options.task)) {
|
|
587
|
+
throw (0, cli_error_1.operationFailed)(`Invalid task ID: ${options.task}`);
|
|
588
|
+
}
|
|
589
|
+
const found = (0, v2_detect_1.findV2Task)(dirs, options.task, false);
|
|
590
|
+
if (!found || found.isLog) {
|
|
591
|
+
throw (0, cli_error_1.operationFailed)(`Task not found: ${options.task}`);
|
|
592
|
+
}
|
|
593
|
+
if (!found.doc.task.contract) {
|
|
594
|
+
throw (0, cli_error_1.operationFailed)(`Task ${options.task} has no contract`);
|
|
595
|
+
}
|
|
596
|
+
if (found.doc.task.contract.status !== 'draft') {
|
|
597
|
+
throw (0, cli_error_1.operationFailed)(`Contract is not in draft status (current: ${found.doc.task.contract.status})`);
|
|
598
|
+
}
|
|
599
|
+
const readyAt = new Date().toISOString();
|
|
600
|
+
found.doc.task.contract = {
|
|
601
|
+
...found.doc.task.contract,
|
|
602
|
+
status: 'ready',
|
|
603
|
+
metrics: {
|
|
604
|
+
...(found.doc.task.contract.metrics ?? {}),
|
|
605
|
+
readyAt,
|
|
606
|
+
},
|
|
607
|
+
};
|
|
608
|
+
found.doc.task.updatedAt = readyAt;
|
|
609
|
+
(0, core_1.writeTaskFile)(found.filePath, found.doc.task, found.doc.body);
|
|
610
|
+
activated.push(options.task);
|
|
611
|
+
logger.log(`Contract activated: ${options.task}`);
|
|
612
|
+
}
|
|
613
|
+
else {
|
|
614
|
+
// Bulk activation by parentId
|
|
615
|
+
const parentId = options.parent;
|
|
616
|
+
const allTasks = (0, core_1.readTasksDir)(dirs.boardDir);
|
|
617
|
+
for (const doc of allTasks) {
|
|
618
|
+
const task = doc.task;
|
|
619
|
+
if (task.parentId !== parentId)
|
|
620
|
+
continue;
|
|
621
|
+
if (!task.contract || task.contract.status !== 'draft')
|
|
622
|
+
continue;
|
|
623
|
+
const readyAt = new Date().toISOString();
|
|
624
|
+
task.contract = {
|
|
625
|
+
...task.contract,
|
|
626
|
+
status: 'ready',
|
|
627
|
+
metrics: {
|
|
628
|
+
...(task.contract.metrics ?? {}),
|
|
629
|
+
readyAt,
|
|
630
|
+
},
|
|
631
|
+
};
|
|
632
|
+
task.updatedAt = readyAt;
|
|
633
|
+
const taskPath = path.join(dirs.boardDir, (0, core_1.taskFileName)(task.id));
|
|
634
|
+
(0, core_1.writeTaskFile)(taskPath, task, doc.body);
|
|
635
|
+
activated.push(task.id);
|
|
636
|
+
logger.log(`Contract activated: ${task.id}`);
|
|
637
|
+
}
|
|
638
|
+
if (activated.length === 0) {
|
|
639
|
+
logger.log(`No draft contracts found with parent: ${parentId}`);
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
return { success: true, activated };
|
|
643
|
+
}
|
|
644
|
+
// ── V1 board ──────────────────────────────────────────────────────────────
|
|
645
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
646
|
+
const parsed = core_1.Brainfile.parseWithErrors(content);
|
|
647
|
+
if (!parsed.board) {
|
|
648
|
+
throw (0, cli_error_1.operationFailed)(parsed.error || 'Failed to parse brainfile');
|
|
649
|
+
}
|
|
650
|
+
let board = parsed.board;
|
|
651
|
+
if (options.task) {
|
|
652
|
+
// Single task
|
|
653
|
+
const taskInfo = (0, core_1.findTaskById)(board, options.task);
|
|
654
|
+
if (!taskInfo) {
|
|
655
|
+
throw (0, cli_error_1.operationFailed)(`Task not found: ${options.task}`);
|
|
656
|
+
}
|
|
657
|
+
if (!taskInfo.task.contract) {
|
|
658
|
+
throw (0, cli_error_1.operationFailed)(`Task ${options.task} has no contract`);
|
|
659
|
+
}
|
|
660
|
+
if (taskInfo.task.contract.status !== 'draft') {
|
|
661
|
+
throw (0, cli_error_1.operationFailed)(`Contract is not in draft status (current: ${taskInfo.task.contract.status})`);
|
|
662
|
+
}
|
|
663
|
+
// Mutate via setTaskContract (re-attach with status=ready)
|
|
664
|
+
const readyAt = new Date().toISOString();
|
|
665
|
+
const updatedContract = {
|
|
666
|
+
...taskInfo.task.contract,
|
|
667
|
+
status: 'ready',
|
|
668
|
+
metrics: {
|
|
669
|
+
...(taskInfo.task.contract.metrics ?? {}),
|
|
670
|
+
readyAt,
|
|
671
|
+
},
|
|
672
|
+
};
|
|
673
|
+
const result = (0, core_1.setTaskContract)(board, options.task, updatedContract);
|
|
674
|
+
if (!result.success || !result.board) {
|
|
675
|
+
throw (0, cli_error_1.operationFailed)(result.error || 'Failed to activate contract');
|
|
676
|
+
}
|
|
677
|
+
board = result.board;
|
|
678
|
+
activated.push(options.task);
|
|
679
|
+
logger.log(`Contract activated: ${options.task}`);
|
|
680
|
+
}
|
|
681
|
+
else {
|
|
682
|
+
// Bulk by parentId (V1 boards store parentId on task)
|
|
683
|
+
const parentId = options.parent;
|
|
684
|
+
for (const col of board.columns) {
|
|
685
|
+
for (const task of col.tasks) {
|
|
686
|
+
const t = task;
|
|
687
|
+
if (t.parentId !== parentId)
|
|
688
|
+
continue;
|
|
689
|
+
if (!task.contract || task.contract.status !== 'draft')
|
|
690
|
+
continue;
|
|
691
|
+
const readyAt = new Date().toISOString();
|
|
692
|
+
const updatedContract = {
|
|
693
|
+
...task.contract,
|
|
694
|
+
status: 'ready',
|
|
695
|
+
metrics: {
|
|
696
|
+
...(task.contract.metrics ?? {}),
|
|
697
|
+
readyAt,
|
|
698
|
+
},
|
|
699
|
+
};
|
|
700
|
+
const result = (0, core_1.setTaskContract)(board, task.id, updatedContract);
|
|
701
|
+
if (result.success && result.board) {
|
|
702
|
+
board = result.board;
|
|
703
|
+
activated.push(task.id);
|
|
704
|
+
logger.log(`Contract activated: ${task.id}`);
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
if (activated.length === 0) {
|
|
709
|
+
logger.log(`No draft contracts found with parent: ${parentId}`);
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
fs.writeFileSync(filePath, core_1.Brainfile.serialize(board), 'utf-8');
|
|
713
|
+
return { success: true, activated };
|
|
714
|
+
}
|
|
715
|
+
//# sourceMappingURL=contract.js.map
|