brainfile 0.21.2 → 0.22.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (233) hide show
  1. package/README.md +1 -1
  2. package/dist/__tests__/helpers/mcp-wire.d.ts +2 -0
  3. package/dist/__tests__/helpers/mcp-wire.d.ts.map +1 -1
  4. package/dist/__tests__/helpers/mcp-wire.js +4 -2
  5. package/dist/__tests__/helpers/mcp-wire.js.map +1 -1
  6. package/dist/chunk-CWUTU5VV.mjs +5585 -0
  7. package/dist/chunk-L4WSDMK3.mjs +1973 -0
  8. package/dist/{chunk-7KU2R7QC.mjs → chunk-XFDTTLSW.mjs} +3230 -4322
  9. package/dist/cli.js +7 -1
  10. package/dist/cli.mjs +8078 -5357
  11. package/dist/commands/add.d.ts +4 -0
  12. package/dist/commands/add.d.ts.map +1 -1
  13. package/dist/commands/add.js +68 -72
  14. package/dist/commands/add.js.map +1 -1
  15. package/dist/commands/boards.d.ts +18 -0
  16. package/dist/commands/boards.d.ts.map +1 -0
  17. package/dist/commands/boards.js +166 -0
  18. package/dist/commands/boards.js.map +1 -0
  19. package/dist/commands/brief.d.ts +17 -11
  20. package/dist/commands/brief.d.ts.map +1 -1
  21. package/dist/commands/brief.js +38 -35
  22. package/dist/commands/brief.js.map +1 -1
  23. package/dist/commands/check.d.ts +47 -0
  24. package/dist/commands/check.d.ts.map +1 -0
  25. package/dist/commands/check.js +134 -0
  26. package/dist/commands/check.js.map +1 -0
  27. package/dist/commands/columns.d.ts +48 -0
  28. package/dist/commands/columns.d.ts.map +1 -0
  29. package/dist/commands/columns.js +132 -0
  30. package/dist/commands/columns.js.map +1 -0
  31. package/dist/commands/complete.js +9 -4
  32. package/dist/commands/complete.js.map +1 -1
  33. package/dist/commands/decide.d.ts +72 -0
  34. package/dist/commands/decide.d.ts.map +1 -0
  35. package/dist/commands/decide.js +264 -0
  36. package/dist/commands/decide.js.map +1 -0
  37. package/dist/commands/hooks.d.ts +5 -1
  38. package/dist/commands/hooks.d.ts.map +1 -1
  39. package/dist/commands/hooks.js +10 -11
  40. package/dist/commands/hooks.js.map +1 -1
  41. package/dist/commands/init.d.ts.map +1 -1
  42. package/dist/commands/init.js +8 -0
  43. package/dist/commands/init.js.map +1 -1
  44. package/dist/commands/lint.d.ts +27 -0
  45. package/dist/commands/lint.d.ts.map +1 -1
  46. package/dist/commands/lint.js +127 -20
  47. package/dist/commands/lint.js.map +1 -1
  48. package/dist/commands/list.d.ts +6 -0
  49. package/dist/commands/list.d.ts.map +1 -1
  50. package/dist/commands/list.js +216 -7
  51. package/dist/commands/list.js.map +1 -1
  52. package/dist/commands/log.d.ts +51 -0
  53. package/dist/commands/log.d.ts.map +1 -1
  54. package/dist/commands/log.js +113 -84
  55. package/dist/commands/log.js.map +1 -1
  56. package/dist/commands/mcp.d.ts +5 -2
  57. package/dist/commands/mcp.d.ts.map +1 -1
  58. package/dist/commands/mcp.js +44 -9
  59. package/dist/commands/mcp.js.map +1 -1
  60. package/dist/commands/memory-index.d.ts +34 -0
  61. package/dist/commands/memory-index.d.ts.map +1 -0
  62. package/dist/commands/memory-index.js +135 -0
  63. package/dist/commands/memory-index.js.map +1 -0
  64. package/dist/commands/merge-driver.d.ts +1 -15
  65. package/dist/commands/merge-driver.d.ts.map +1 -1
  66. package/dist/commands/merge-driver.js +8 -134
  67. package/dist/commands/merge-driver.js.map +1 -1
  68. package/dist/commands/migrate-tracked.d.ts +1 -1
  69. package/dist/commands/migrate-tracked.d.ts.map +1 -1
  70. package/dist/commands/migrate-tracked.js +21 -10
  71. package/dist/commands/migrate-tracked.js.map +1 -1
  72. package/dist/commands/patch.d.ts +23 -4
  73. package/dist/commands/patch.d.ts.map +1 -1
  74. package/dist/commands/patch.js +149 -91
  75. package/dist/commands/patch.js.map +1 -1
  76. package/dist/commands/plan.d.ts.map +1 -1
  77. package/dist/commands/plan.js +11 -5
  78. package/dist/commands/plan.js.map +1 -1
  79. package/dist/commands/schema.d.ts +12 -0
  80. package/dist/commands/schema.d.ts.map +1 -1
  81. package/dist/commands/schema.js +28 -10
  82. package/dist/commands/schema.js.map +1 -1
  83. package/dist/commands/search.d.ts +46 -12
  84. package/dist/commands/search.d.ts.map +1 -1
  85. package/dist/commands/search.js +89 -44
  86. package/dist/commands/search.js.map +1 -1
  87. package/dist/commands/show.d.ts.map +1 -1
  88. package/dist/commands/show.js +175 -10
  89. package/dist/commands/show.js.map +1 -1
  90. package/dist/commands/template.d.ts +2 -0
  91. package/dist/commands/template.d.ts.map +1 -1
  92. package/dist/commands/template.js +2 -1
  93. package/dist/commands/template.js.map +1 -1
  94. package/dist/commands/types.d.ts +31 -1
  95. package/dist/commands/types.d.ts.map +1 -1
  96. package/dist/commands/types.js +122 -22
  97. package/dist/commands/types.js.map +1 -1
  98. package/dist/commands/where.d.ts +20 -1
  99. package/dist/commands/where.d.ts.map +1 -1
  100. package/dist/commands/where.js +56 -6
  101. package/dist/commands/where.js.map +1 -1
  102. package/dist/hook.mjs +15 -0
  103. package/dist/hooks/session-start.d.ts +39 -0
  104. package/dist/hooks/session-start.d.ts.map +1 -0
  105. package/dist/hooks/session-start.js +181 -0
  106. package/dist/hooks/session-start.js.map +1 -0
  107. package/dist/lib/contractRunner.d.ts.map +1 -1
  108. package/dist/lib/contractRunner.js +8 -0
  109. package/dist/lib/contractRunner.js.map +1 -1
  110. package/dist/mcp/helpers.d.ts +29 -1
  111. package/dist/mcp/helpers.d.ts.map +1 -1
  112. package/dist/mcp/helpers.js +57 -2
  113. package/dist/mcp/helpers.js.map +1 -1
  114. package/dist/mcp/instructions.d.ts +5 -0
  115. package/dist/mcp/instructions.d.ts.map +1 -0
  116. package/dist/mcp/instructions.js +57 -0
  117. package/dist/mcp/instructions.js.map +1 -0
  118. package/dist/mcp/resources.d.ts +9 -0
  119. package/dist/mcp/resources.d.ts.map +1 -0
  120. package/dist/mcp/resources.js +29 -0
  121. package/dist/mcp/resources.js.map +1 -0
  122. package/dist/mcp/schemas.d.ts +600 -9
  123. package/dist/mcp/schemas.d.ts.map +1 -1
  124. package/dist/mcp/schemas.js +374 -12
  125. package/dist/mcp/schemas.js.map +1 -1
  126. package/dist/mcp/tools/adr_promote_tool.d.ts +7 -0
  127. package/dist/mcp/tools/adr_promote_tool.d.ts.map +1 -0
  128. package/dist/mcp/tools/adr_promote_tool.js +37 -0
  129. package/dist/mcp/tools/adr_promote_tool.js.map +1 -0
  130. package/dist/mcp/tools/board_tool.d.ts +7 -0
  131. package/dist/mcp/tools/board_tool.d.ts.map +1 -0
  132. package/dist/mcp/tools/board_tool.js +61 -0
  133. package/dist/mcp/tools/board_tool.js.map +1 -0
  134. package/dist/mcp/tools/brief_tool.d.ts.map +1 -1
  135. package/dist/mcp/tools/brief_tool.js +13 -5
  136. package/dist/mcp/tools/brief_tool.js.map +1 -1
  137. package/dist/mcp/tools/check_tool.d.ts +6 -0
  138. package/dist/mcp/tools/check_tool.d.ts.map +1 -0
  139. package/dist/mcp/tools/check_tool.js +54 -0
  140. package/dist/mcp/tools/check_tool.js.map +1 -0
  141. package/dist/mcp/tools/columns_tool.d.ts +7 -0
  142. package/dist/mcp/tools/columns_tool.d.ts.map +1 -0
  143. package/dist/mcp/tools/columns_tool.js +94 -0
  144. package/dist/mcp/tools/columns_tool.js.map +1 -0
  145. package/dist/mcp/tools/decide_tool.d.ts +91 -0
  146. package/dist/mcp/tools/decide_tool.d.ts.map +1 -0
  147. package/dist/mcp/tools/decide_tool.js +141 -0
  148. package/dist/mcp/tools/decide_tool.js.map +1 -0
  149. package/dist/mcp/tools/get_task_tool.d.ts.map +1 -1
  150. package/dist/mcp/tools/get_task_tool.js +62 -4
  151. package/dist/mcp/tools/get_task_tool.js.map +1 -1
  152. package/dist/mcp/tools/lint_tool.d.ts +7 -0
  153. package/dist/mcp/tools/lint_tool.d.ts.map +1 -0
  154. package/dist/mcp/tools/lint_tool.js +48 -0
  155. package/dist/mcp/tools/lint_tool.js.map +1 -0
  156. package/dist/mcp/tools/list_tasks_tool.d.ts.map +1 -1
  157. package/dist/mcp/tools/list_tasks_tool.js +132 -15
  158. package/dist/mcp/tools/list_tasks_tool.js.map +1 -1
  159. package/dist/mcp/tools/log_tool.d.ts +4 -0
  160. package/dist/mcp/tools/log_tool.d.ts.map +1 -0
  161. package/dist/mcp/tools/log_tool.js +52 -0
  162. package/dist/mcp/tools/log_tool.js.map +1 -0
  163. package/dist/mcp/tools/note_tool.d.ts +7 -0
  164. package/dist/mcp/tools/note_tool.d.ts.map +1 -0
  165. package/dist/mcp/tools/note_tool.js +39 -0
  166. package/dist/mcp/tools/note_tool.js.map +1 -0
  167. package/dist/mcp/tools/retire_tool.d.ts +4 -0
  168. package/dist/mcp/tools/retire_tool.d.ts.map +1 -0
  169. package/dist/mcp/tools/retire_tool.js +81 -0
  170. package/dist/mcp/tools/retire_tool.js.map +1 -0
  171. package/dist/mcp/tools/search_tool.d.ts.map +1 -1
  172. package/dist/mcp/tools/search_tool.js +36 -19
  173. package/dist/mcp/tools/search_tool.js.map +1 -1
  174. package/dist/mcp/tools/supersede_tool.d.ts +4 -0
  175. package/dist/mcp/tools/supersede_tool.d.ts.map +1 -0
  176. package/dist/mcp/tools/supersede_tool.js +72 -0
  177. package/dist/mcp/tools/supersede_tool.js.map +1 -0
  178. package/dist/mcp/tools/sync_tool.d.ts +9 -0
  179. package/dist/mcp/tools/sync_tool.d.ts.map +1 -0
  180. package/dist/mcp/tools/sync_tool.js +87 -0
  181. package/dist/mcp/tools/sync_tool.js.map +1 -0
  182. package/dist/mcp/tools/task_add_tool.d.ts.map +1 -1
  183. package/dist/mcp/tools/task_add_tool.js +39 -17
  184. package/dist/mcp/tools/task_add_tool.js.map +1 -1
  185. package/dist/mcp/tools/task_complete_tool.d.ts.map +1 -1
  186. package/dist/mcp/tools/task_complete_tool.js +3 -2
  187. package/dist/mcp/tools/task_complete_tool.js.map +1 -1
  188. package/dist/mcp/tools/task_patch_tool.d.ts.map +1 -1
  189. package/dist/mcp/tools/task_patch_tool.js +30 -5
  190. package/dist/mcp/tools/task_patch_tool.js.map +1 -1
  191. package/dist/mcp/tools/template_tool.d.ts +4 -0
  192. package/dist/mcp/tools/template_tool.d.ts.map +1 -0
  193. package/dist/mcp/tools/template_tool.js +70 -0
  194. package/dist/mcp/tools/template_tool.js.map +1 -0
  195. package/dist/mcp/tools/types_tool.d.ts +4 -0
  196. package/dist/mcp/tools/types_tool.d.ts.map +1 -0
  197. package/dist/mcp/tools/types_tool.js +61 -0
  198. package/dist/mcp/tools/types_tool.js.map +1 -0
  199. package/dist/mcp/tools/where_tool.d.ts +4 -0
  200. package/dist/mcp/tools/where_tool.d.ts.map +1 -0
  201. package/dist/mcp/tools/where_tool.js +73 -0
  202. package/dist/mcp/tools/where_tool.js.map +1 -0
  203. package/dist/schemas/board.json +29 -8
  204. package/dist/{tui-3Z7ZJV7M.mjs → tui-D6RMFL6X.mjs} +994 -315
  205. package/dist/utils/board-autosync.d.ts +1 -10
  206. package/dist/utils/board-autosync.d.ts.map +1 -1
  207. package/dist/utils/board-autosync.js +11 -29
  208. package/dist/utils/board-autosync.js.map +1 -1
  209. package/dist/utils/board-commit-hooks.d.ts +8 -0
  210. package/dist/utils/board-commit-hooks.d.ts.map +1 -1
  211. package/dist/utils/board-commit-hooks.js +45 -1
  212. package/dist/utils/board-commit-hooks.js.map +1 -1
  213. package/dist/utils/board-repo.d.ts +1 -119
  214. package/dist/utils/board-repo.d.ts.map +1 -1
  215. package/dist/utils/board-repo.js +43 -571
  216. package/dist/utils/board-repo.js.map +1 -1
  217. package/dist/utils/board-sync.d.ts +3 -123
  218. package/dist/utils/board-sync.d.ts.map +1 -1
  219. package/dist/utils/board-sync.js +34 -551
  220. package/dist/utils/board-sync.js.map +1 -1
  221. package/dist/utils/graph.d.ts +20 -0
  222. package/dist/utils/graph.d.ts.map +1 -0
  223. package/dist/utils/graph.js +61 -0
  224. package/dist/utils/graph.js.map +1 -0
  225. package/dist/utils/related.d.ts +21 -0
  226. package/dist/utils/related.d.ts.map +1 -0
  227. package/dist/utils/related.js +36 -0
  228. package/dist/utils/related.js.map +1 -0
  229. package/package.json +2 -2
  230. package/dist/tui/theme.d.ts +0 -98
  231. package/dist/tui/theme.d.ts.map +0 -1
  232. package/dist/tui/theme.js +0 -141
  233. package/dist/tui/theme.js.map +0 -1
@@ -5,30 +5,26 @@ const require = __brainfileCreateRequire(import.meta.url);
5
5
  const __filename = __brainfileFileURLToPath(import.meta.url);
6
6
  const __dirname = __brainfileDirname(__filename);
7
7
  import {
8
- GLYPHS,
9
- PALETTE,
10
- RULE,
11
8
  createGitHubIssue,
12
9
  createLinearIssue,
13
- fetchBeforeRead,
14
10
  getArchiveConfig,
15
- getContractStateColor,
16
11
  getEffectiveDestination,
17
12
  getLinearTeams,
18
- getTypeGlyph,
19
13
  import_core,
14
+ import_repo,
15
+ import_repo2,
16
+ import_repo3,
20
17
  installAutosync,
21
18
  isGitHubAuthenticated,
22
19
  isLinearAuthenticated,
23
20
  markV2MigrationHintShown,
24
- require_dist,
25
21
  require_source,
26
22
  resolveCliBrainfilePath,
27
- scheduleBoardCommit,
28
- shouldSuggestV2Migration,
29
- syncMessage,
30
- syncStatusLabel
31
- } from "./chunk-7KU2R7QC.mjs";
23
+ shouldSuggestV2Migration
24
+ } from "./chunk-CWUTU5VV.mjs";
25
+ import {
26
+ require_dist
27
+ } from "./chunk-XFDTTLSW.mjs";
32
28
  import {
33
29
  __commonJS,
34
30
  __export,
@@ -8388,14 +8384,14 @@ var require_react_reconciler_production = __commonJS({
8388
8384
  }
8389
8385
  var exports2 = {};
8390
8386
  "use strict";
8391
- var React20 = require_react(), Scheduler2 = require_scheduler(), assign = Object.assign, REACT_LEGACY_ELEMENT_TYPE = /* @__PURE__ */ Symbol.for("react.element"), REACT_ELEMENT_TYPE = /* @__PURE__ */ Symbol.for("react.transitional.element"), REACT_PORTAL_TYPE = /* @__PURE__ */ Symbol.for("react.portal"), REACT_FRAGMENT_TYPE = /* @__PURE__ */ Symbol.for("react.fragment"), REACT_STRICT_MODE_TYPE = /* @__PURE__ */ Symbol.for("react.strict_mode"), REACT_PROFILER_TYPE = /* @__PURE__ */ Symbol.for("react.profiler"), REACT_CONSUMER_TYPE = /* @__PURE__ */ Symbol.for("react.consumer"), REACT_CONTEXT_TYPE = /* @__PURE__ */ Symbol.for("react.context"), REACT_FORWARD_REF_TYPE = /* @__PURE__ */ Symbol.for("react.forward_ref"), REACT_SUSPENSE_TYPE = /* @__PURE__ */ Symbol.for("react.suspense"), REACT_SUSPENSE_LIST_TYPE = /* @__PURE__ */ Symbol.for("react.suspense_list"), REACT_MEMO_TYPE = /* @__PURE__ */ Symbol.for("react.memo"), REACT_LAZY_TYPE = /* @__PURE__ */ Symbol.for("react.lazy");
8387
+ var React21 = require_react(), Scheduler2 = require_scheduler(), assign = Object.assign, REACT_LEGACY_ELEMENT_TYPE = /* @__PURE__ */ Symbol.for("react.element"), REACT_ELEMENT_TYPE = /* @__PURE__ */ Symbol.for("react.transitional.element"), REACT_PORTAL_TYPE = /* @__PURE__ */ Symbol.for("react.portal"), REACT_FRAGMENT_TYPE = /* @__PURE__ */ Symbol.for("react.fragment"), REACT_STRICT_MODE_TYPE = /* @__PURE__ */ Symbol.for("react.strict_mode"), REACT_PROFILER_TYPE = /* @__PURE__ */ Symbol.for("react.profiler"), REACT_CONSUMER_TYPE = /* @__PURE__ */ Symbol.for("react.consumer"), REACT_CONTEXT_TYPE = /* @__PURE__ */ Symbol.for("react.context"), REACT_FORWARD_REF_TYPE = /* @__PURE__ */ Symbol.for("react.forward_ref"), REACT_SUSPENSE_TYPE = /* @__PURE__ */ Symbol.for("react.suspense"), REACT_SUSPENSE_LIST_TYPE = /* @__PURE__ */ Symbol.for("react.suspense_list"), REACT_MEMO_TYPE = /* @__PURE__ */ Symbol.for("react.memo"), REACT_LAZY_TYPE = /* @__PURE__ */ Symbol.for("react.lazy");
8392
8388
  /* @__PURE__ */ Symbol.for("react.scope");
8393
8389
  var REACT_ACTIVITY_TYPE = /* @__PURE__ */ Symbol.for("react.activity");
8394
8390
  /* @__PURE__ */ Symbol.for("react.legacy_hidden");
8395
8391
  /* @__PURE__ */ Symbol.for("react.tracing_marker");
8396
8392
  var REACT_MEMO_CACHE_SENTINEL = /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel");
8397
8393
  /* @__PURE__ */ Symbol.for("react.view_transition");
8398
- var MAYBE_ITERATOR_SYMBOL = Symbol.iterator, REACT_CLIENT_REFERENCE = /* @__PURE__ */ Symbol.for("react.client.reference"), isArrayImpl = Array.isArray, ReactSharedInternals = React20.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, rendererVersion = $$$config.rendererVersion, rendererPackageName = $$$config.rendererPackageName, extraDevToolsConfig = $$$config.extraDevToolsConfig, getPublicInstance = $$$config.getPublicInstance, getRootHostContext = $$$config.getRootHostContext, getChildHostContext = $$$config.getChildHostContext, prepareForCommit = $$$config.prepareForCommit, resetAfterCommit = $$$config.resetAfterCommit, createInstance = $$$config.createInstance;
8394
+ var MAYBE_ITERATOR_SYMBOL = Symbol.iterator, REACT_CLIENT_REFERENCE = /* @__PURE__ */ Symbol.for("react.client.reference"), isArrayImpl = Array.isArray, ReactSharedInternals = React21.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, rendererVersion = $$$config.rendererVersion, rendererPackageName = $$$config.rendererPackageName, extraDevToolsConfig = $$$config.extraDevToolsConfig, getPublicInstance = $$$config.getPublicInstance, getRootHostContext = $$$config.getRootHostContext, getChildHostContext = $$$config.getChildHostContext, prepareForCommit = $$$config.prepareForCommit, resetAfterCommit = $$$config.resetAfterCommit, createInstance = $$$config.createInstance;
8399
8395
  $$$config.cloneMutableInstance;
8400
8396
  var appendInitialChild = $$$config.appendInitialChild, finalizeInitialChildren = $$$config.finalizeInitialChildren, shouldSetTextContent = $$$config.shouldSetTextContent, createTextInstance = $$$config.createTextInstance;
8401
8397
  $$$config.cloneMutableTextInstance;
@@ -9508,7 +9504,7 @@ var require_stack_utils = __commonJS({
9508
9504
  });
9509
9505
 
9510
9506
  // src/commands/tui.tsx
9511
- var import_react45 = __toESM(require_react());
9507
+ var import_react46 = __toESM(require_react());
9512
9508
 
9513
9509
  // node_modules/ink/build/render.js
9514
9510
  import { Stream } from "node:stream";
@@ -19197,8 +19193,99 @@ var import_chalk4 = __toESM(require_source());
19197
19193
  import * as fs6 from "fs";
19198
19194
 
19199
19195
  // src/tui/BrainfileTUI.tsx
19200
- var import_react44 = __toESM(require_react());
19201
- var import_core6 = __toESM(require_dist());
19196
+ var import_react45 = __toESM(require_react());
19197
+ var import_core10 = __toESM(require_dist());
19198
+
19199
+ // src/tui/theme.ts
19200
+ var import_core2 = __toESM(require_dist());
19201
+ var RAW_PALETTE = {
19202
+ // Text hierarchy
19203
+ text: "#ffffff",
19204
+ textSecondary: "#a0a0a0",
19205
+ textMuted: "#606060",
19206
+ textDim: "#404040",
19207
+ // Priority signal
19208
+ critical: "#ef4444",
19209
+ high: "#f59e0b",
19210
+ medium: "#3b82f6",
19211
+ low: "#6b7280",
19212
+ // Contract-state signal (design §3: draft dim · ready cyan · in_progress
19213
+ // yellow · delivered magenta · done green · failed/blocked red)
19214
+ contractDraft: "#606060",
19215
+ contractReady: "#06b6d4",
19216
+ contractInProgress: "#eab308",
19217
+ contractDelivered: "#c026d3",
19218
+ contractDone: "#22c55e",
19219
+ contractFailed: "#ef4444",
19220
+ // Feedback
19221
+ success: "#22c55e",
19222
+ warning: "#eab308",
19223
+ error: "#ef4444",
19224
+ info: "#3b82f6",
19225
+ accent: "#7c3aed"
19226
+ };
19227
+ function isNoColor(env3 = process.env) {
19228
+ return Boolean(env3.NO_COLOR);
19229
+ }
19230
+ function makePalette(noColor) {
19231
+ if (!noColor) return RAW_PALETTE;
19232
+ return new Proxy(RAW_PALETTE, { get: () => void 0 });
19233
+ }
19234
+ var NO_COLOR = isNoColor();
19235
+ var PALETTE = makePalette(NO_COLOR);
19236
+ var RULE = "\u2500";
19237
+ var TYPE_GLYPHS = {
19238
+ ...import_core2.TYPE_GLYPHS,
19239
+ adr: import_core2.TYPE_GLYPHS.decision
19240
+ };
19241
+ function getTypeGlyph(type) {
19242
+ if (!type) return "";
19243
+ return TYPE_GLYPHS[(0, import_core2.canonicalTypeName)(type)] ?? "";
19244
+ }
19245
+ var GLYPHS = {
19246
+ pointer: "\u25B8",
19247
+ cursor: "\u258C",
19248
+ collapsed: "\u25B6",
19249
+ success: "\u2713",
19250
+ error: "\u2717",
19251
+ warning: "\u26A0",
19252
+ live: "\u25CF",
19253
+ subtaskOpen: "\u25FB",
19254
+ subtaskDone: "\u2611",
19255
+ orphanParent: "\u2190",
19256
+ /** Board-list row glyph for a parent-with-children that is expanded (§A1). */
19257
+ parentExpanded: "\u25BE",
19258
+ /** Board-list row glyph for a parent-with-children that is collapsed (§A1). */
19259
+ parentCollapsed: "\u25B8",
19260
+ /** Detail body scroll indicator (§B2), e.g. `↕ 2/9`. */
19261
+ scroll: "\u2195",
19262
+ /**
19263
+ * A decision that no longer stands (superseded, rejected, stale): the
19264
+ * hollow of the decision glyph `●`, as the desktop draws it (task-19).
19265
+ */
19266
+ retiredDecision: "\u25CB",
19267
+ /** Fold glyphs for the decisions view's history heading. */
19268
+ foldOpen: "\u25BE",
19269
+ foldClosed: "\u25B8"
19270
+ };
19271
+ function getContractStateColor(state) {
19272
+ switch (state?.toLowerCase()) {
19273
+ case "ready":
19274
+ return PALETTE.contractReady;
19275
+ case "in_progress":
19276
+ return PALETTE.contractInProgress;
19277
+ case "delivered":
19278
+ return PALETTE.contractDelivered;
19279
+ case "done":
19280
+ return PALETTE.contractDone;
19281
+ case "failed":
19282
+ case "blocked":
19283
+ return PALETTE.contractFailed;
19284
+ case "draft":
19285
+ default:
19286
+ return PALETTE.contractDraft;
19287
+ }
19288
+ }
19202
19289
 
19203
19290
  // src/tui/copy.ts
19204
19291
  var STATUS = {
@@ -19231,14 +19318,19 @@ var STATUS = {
19231
19318
  priorityUpdated: (priority) => `Priority: ${priority}`,
19232
19319
  archived: (id) => `Archived ${id}`,
19233
19320
  archiving: "Archiving\u2026",
19234
- archiveFailed: "Could not archive"
19321
+ archiveFailed: "Could not archive",
19322
+ decisionsReadOnly: "Decisions change with brainfile decide, supersede or retire",
19323
+ decisionNoColumn: "Decisions have no column",
19324
+ cannotOpen: (id, why) => `${id} is ${why}`,
19325
+ noDecisionHistory: "No retired decisions"
19235
19326
  };
19236
19327
  var EMPTY = {
19237
19328
  noMatches: "No matches \xB7 esc to clear",
19238
19329
  nothingCompleted: "Nothing completed yet \xB7 L board",
19239
19330
  emptyColumn: "No documents in this column \xB7 a add",
19240
19331
  noType: (type) => `No ${type} documents`,
19241
- noDocuments: "No documents"
19332
+ noDocuments: "No documents",
19333
+ noDecisions: "No decisions yet \xB7 brainfile decide records one"
19242
19334
  };
19243
19335
  var CHROME = {
19244
19336
  filterAffordance: "/ filter ? help",
@@ -19274,49 +19366,80 @@ var HEADER_ROWS = 2;
19274
19366
  var FOOTER_ROWS = 3;
19275
19367
 
19276
19368
  // src/tui/rows.ts
19277
- function buildRows(tasks, collapsedIds) {
19278
- const visibleIds = new Set(tasks.map((t) => t.id));
19279
- const emitted = /* @__PURE__ */ new Set();
19280
- const rows = [];
19369
+ function markDecisionChanges(rows, changed) {
19370
+ if (changed.size === 0) return rows;
19371
+ return rows.map((row) => changed.has(row.task.id) ? { ...row, decisionChanged: true } : row);
19372
+ }
19373
+ function buildOutline({ columns, logs = [] }) {
19374
+ const columnOf = /* @__PURE__ */ new Map();
19375
+ for (const column of columns) for (const task of column.tasks ?? []) if (!columnOf.has(task.id)) columnOf.set(task.id, column.id);
19376
+ const done = logs.filter((task) => !columnOf.has(task.id));
19377
+ const shown = /* @__PURE__ */ new Set([...columnOf.keys(), ...done.map((task) => task.id)]);
19281
19378
  const childrenOf = /* @__PURE__ */ new Map();
19282
- for (const task of tasks) {
19379
+ const hang = (task, archived, under) => {
19283
19380
  const parentId = task.parentId;
19284
- if (!parentId || parentId === task.id || !visibleIds.has(parentId)) continue;
19381
+ if (!parentId || parentId === task.id || !under(parentId)) return;
19285
19382
  const bucket = childrenOf.get(parentId);
19286
- if (bucket) bucket.push(task);
19287
- else childrenOf.set(parentId, [task]);
19383
+ if (bucket) bucket.push({ task, archived });
19384
+ else childrenOf.set(parentId, [{ task, archived }]);
19385
+ };
19386
+ for (const column of columns) {
19387
+ for (const task of column.tasks ?? []) if (columnOf.get(task.id) === column.id) hang(task, false, (id) => columnOf.has(id));
19288
19388
  }
19289
- for (const task of tasks) {
19290
- if (emitted.has(task.id)) continue;
19291
- const parentId = task.parentId;
19292
- const parentVisible = Boolean(parentId && parentId !== task.id && visibleIds.has(parentId));
19293
- if (parentVisible) continue;
19389
+ for (const task of done) hang(task, true, (id) => shown.has(id));
19390
+ const emitted = /* @__PURE__ */ new Set();
19391
+ const lists = new Map(columns.map((column) => [column.id, []]));
19392
+ const emit = (task, archived, depth, listColumn, rows) => {
19294
19393
  emitted.add(task.id);
19295
- const children = childrenOf.get(task.id) ?? [];
19296
- const isCollapsed = children.length > 0 && Boolean(collapsedIds?.has(task.id));
19394
+ const children = (childrenOf.get(task.id) ?? []).filter((child) => !emitted.has(child.task.id));
19395
+ const own = columnOf.get(task.id);
19396
+ const parentId = task.parentId && task.parentId !== task.id ? task.parentId : void 0;
19297
19397
  rows.push({
19298
19398
  task,
19299
- depth: 0,
19300
- orphanParentId: parentId && !parentVisible ? parentId : void 0,
19399
+ depth,
19400
+ orphanParentId: depth === 0 && parentId && !columnOf.has(parentId) ? parentId : void 0,
19301
19401
  childCount: children.length > 0 ? children.length : void 0,
19302
- collapsed: children.length > 0 ? isCollapsed : void 0
19402
+ ...archived ? { archived: true } : own && own !== listColumn ? { column: own } : {}
19303
19403
  });
19304
- if (isCollapsed) {
19305
- for (const child of children) emitted.add(child.id);
19306
- continue;
19307
- }
19308
19404
  for (const child of children) {
19309
- if (emitted.has(child.id)) continue;
19310
- emitted.add(child.id);
19311
- rows.push({ task: child, depth: 1 });
19405
+ if (!emitted.has(child.task.id)) emit(child.task, child.archived, depth + 1, listColumn, rows);
19406
+ }
19407
+ };
19408
+ for (const column of columns) {
19409
+ const rows = lists.get(column.id);
19410
+ for (const task of column.tasks ?? []) {
19411
+ if (emitted.has(task.id) || columnOf.get(task.id) !== column.id) continue;
19412
+ const parentId = task.parentId;
19413
+ if (parentId && parentId !== task.id && columnOf.has(parentId)) continue;
19414
+ emit(task, false, 0, column.id, rows);
19312
19415
  }
19313
19416
  }
19314
- for (const task of tasks) {
19315
- if (emitted.has(task.id)) continue;
19316
- emitted.add(task.id);
19317
- rows.push({ task, depth: 0, orphanParentId: task.parentId });
19417
+ for (const column of columns) {
19418
+ const rows = lists.get(column.id);
19419
+ for (const task of column.tasks ?? []) {
19420
+ if (emitted.has(task.id) || columnOf.get(task.id) !== column.id) continue;
19421
+ const before = rows.length;
19422
+ emit(task, false, 0, column.id, rows);
19423
+ rows[before] = { ...rows[before], orphanParentId: task.parentId };
19424
+ }
19318
19425
  }
19319
- return rows;
19426
+ return lists;
19427
+ }
19428
+ function collapseRows(rows, collapsedIds) {
19429
+ const out = [];
19430
+ let hiddenBelow = Infinity;
19431
+ for (const row of rows) {
19432
+ if (row.depth > hiddenBelow) continue;
19433
+ hiddenBelow = Infinity;
19434
+ if (!row.childCount) {
19435
+ out.push(row);
19436
+ continue;
19437
+ }
19438
+ const collapsed = Boolean(collapsedIds?.has(row.task.id));
19439
+ if (collapsed) hiddenBelow = row.depth;
19440
+ out.push({ ...row, collapsed });
19441
+ }
19442
+ return out;
19320
19443
  }
19321
19444
  function buildFlatRows(tasks) {
19322
19445
  return tasks.map((task) => ({
@@ -19333,83 +19456,136 @@ function windowRows(rows, selectedIndex, viewportHeight) {
19333
19456
  return { visible: rows.slice(start, start + viewportHeight), start };
19334
19457
  }
19335
19458
 
19336
- // src/tui/utils.ts
19337
- var import_core2 = __toESM(require_dist());
19338
-
19339
- // src/tui/text.ts
19340
- function truncate(text, maxWidth) {
19341
- if (!text) return " ";
19342
- if (maxWidth <= 0) return " ";
19343
- if (maxWidth <= 3) return text.slice(0, maxWidth) || " ";
19344
- if (text.length <= maxWidth) return text;
19345
- return `${text.slice(0, maxWidth - 1)}\u2026`;
19459
+ // src/tui/decisions.ts
19460
+ var import_core3 = __toESM(require_dist());
19461
+ function boardTypes(board) {
19462
+ return board?.types;
19346
19463
  }
19347
- function truncateStart(text, maxWidth) {
19348
- if (!text) return " ";
19349
- if (maxWidth <= 0) return " ";
19350
- if (maxWidth <= 1) return text.slice(-1) || " ";
19351
- if (text.length <= maxWidth) return text;
19352
- return `\u2026${text.slice(text.length - (maxWidth - 1))}`;
19464
+ function isDecisionTask(task, types) {
19465
+ return Boolean(task) && (0, import_core3.resolveDocType)(task.type, types).role === "decision";
19353
19466
  }
19354
- function wrapText2(text, maxWidth) {
19355
- if (!text || maxWidth <= 0) return [];
19356
- const words = text.split(/\s+/);
19357
- const lines = [];
19358
- let currentLine = "";
19359
- for (const word of words) {
19360
- if (word.length > maxWidth) {
19361
- if (currentLine) {
19362
- lines.push(currentLine);
19363
- currentLine = "";
19364
- }
19365
- for (let i = 0; i < word.length; i += maxWidth) {
19366
- lines.push(word.slice(i, i + maxWidth));
19367
- }
19368
- } else if (currentLine.length + 1 + word.length <= maxWidth) {
19369
- currentLine = currentLine ? `${currentLine} ${word}` : word;
19370
- } else {
19371
- if (currentLine) lines.push(currentLine);
19372
- currentLine = word;
19467
+ function isDecisionStop(type, types) {
19468
+ if (!type || type === "all") return false;
19469
+ return (0, import_core3.resolveDocType)(type, types).role === "decision";
19470
+ }
19471
+ function splitDecisions(board, logs) {
19472
+ const types = boardTypes(board);
19473
+ const decisions = [];
19474
+ const columns = board.columns.map((column) => {
19475
+ const tasks = column.tasks ?? [];
19476
+ const work2 = tasks.filter((task) => {
19477
+ if (!isDecisionTask(task, types)) return true;
19478
+ decisions.push(task);
19479
+ return false;
19480
+ });
19481
+ return work2.length === tasks.length ? column : { ...column, tasks: work2 };
19482
+ });
19483
+ const seen = new Set(decisions.map((task) => task.id));
19484
+ const work = logs.filter((task) => {
19485
+ if (!isDecisionTask(task, types)) return true;
19486
+ if (!seen.has(task.id)) {
19487
+ seen.add(task.id);
19488
+ decisions.push(task);
19373
19489
  }
19374
- }
19375
- if (currentLine) lines.push(currentLine);
19376
- return lines.length > 0 ? lines : [""];
19490
+ return false;
19491
+ });
19492
+ return { board: { ...board, columns }, logs: work, decisions };
19377
19493
  }
19378
- function pad(n) {
19379
- return " ".repeat(Math.max(0, n));
19494
+ function filterDecisions(decisions, query) {
19495
+ const q = query.trim();
19496
+ if (!q) return decisions;
19497
+ const docs = decisions.map((task) => ({ task, body: task.description ?? "" }));
19498
+ return (0, import_core3.searchTasksRanked)(docs, q).map((match) => match.doc.task);
19380
19499
  }
19381
-
19382
- // src/tui/utils.ts
19383
- function getPriorityColor(priority) {
19384
- switch (priority?.toLowerCase()) {
19385
- case "critical":
19386
- return PALETTE.critical;
19387
- case "high":
19388
- return PALETTE.high;
19389
- case "medium":
19390
- return PALETTE.medium;
19391
- case "low":
19392
- return PALETTE.low;
19393
- default:
19394
- return PALETTE.textDim;
19395
- }
19500
+ function today() {
19501
+ return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
19396
19502
  }
19397
- function getDocType(task) {
19398
- return (task.type || "task").toLowerCase();
19503
+ function isPastValidUntil(summary, on = today()) {
19504
+ return Boolean(summary.validUntil) && summary.validUntil.slice(0, 10) < on;
19399
19505
  }
19400
- function isCompletable(task) {
19401
- if (!task) return false;
19402
- return getDocType(task) !== "adr";
19506
+ function claimKeyTail(claimKey) {
19507
+ if (!claimKey) return "";
19508
+ const parts = claimKey.split(".");
19509
+ return parts.length > 1 ? parts.slice(1).join(".") : claimKey;
19403
19510
  }
19404
- function getSubtaskProgress(task) {
19405
- const subtasks = task.subtasks ?? [];
19406
- if (subtasks.length === 0) return void 0;
19407
- const done = subtasks.filter((s) => s.completed).length;
19408
- return `${done}/${subtasks.length}`;
19511
+ function retiredLabel(summary) {
19512
+ return summary.supersededBy ? `${summary.status} \u2192 ${summary.supersededBy}` : summary.status;
19409
19513
  }
19410
- function getContractState(task) {
19411
- const contract = task.contract;
19412
- return contract?.status;
19514
+ var UNFILED = "unfiled";
19515
+ function buildDecisionView(decisions, { historyOpen, types }) {
19516
+ const lines = [];
19517
+ const selectable = [];
19518
+ const history = [];
19519
+ const pushRow = (task, retired) => {
19520
+ lines.push({ kind: "row", task, summary: (0, import_core3.summarizeDecision)(task), retired, index: selectable.length });
19521
+ selectable.push(task);
19522
+ };
19523
+ let standingCount = 0;
19524
+ for (const group of (0, import_core3.groupDecisionsByPrefix)(decisions, types)) {
19525
+ const standing = group.families.flatMap((family) => [...family.current, ...family.other]);
19526
+ history.push(...group.families.flatMap((family) => family.history));
19527
+ if (standing.length === 0) continue;
19528
+ if (lines.length > 0) lines.push({ kind: "gap" });
19529
+ lines.push({ kind: "heading", label: group.prefix || UNFILED, count: standing.length });
19530
+ for (const task of standing) pushRow(task, false);
19531
+ standingCount += standing.length;
19532
+ }
19533
+ if (history.length > 0) {
19534
+ if (standingCount === 0) lines.push({ kind: "note", text: "No standing decisions" });
19535
+ lines.push({ kind: "gap" });
19536
+ lines.push({ kind: "history", count: history.length, open: historyOpen });
19537
+ if (historyOpen) {
19538
+ for (const task of [...history].sort(import_core3.compareDecisionsNewestFirst)) pushRow(task, true);
19539
+ }
19540
+ }
19541
+ return { lines, selectable, standingCount, historyCount: history.length };
19542
+ }
19543
+ function selectedLineIndex(lines, selectedIndex) {
19544
+ const at = lines.findIndex((line) => line.kind === "row" && line.index === selectedIndex);
19545
+ return at < 0 ? 0 : at;
19546
+ }
19547
+ function windowLines(lines, selectedIndex, viewportHeight) {
19548
+ if (lines.length <= viewportHeight) return { visible: lines, start: 0 };
19549
+ const selected = selectedLineIndex(lines, selectedIndex);
19550
+ const half = Math.floor(viewportHeight / 2);
19551
+ const start = Math.max(0, Math.min(selected - half, lines.length - viewportHeight));
19552
+ return { visible: lines.slice(start, start + viewportHeight), start };
19553
+ }
19554
+ var NO_RELATIONS = { refs: [], resting: [] };
19555
+ function refFor(relation, id, graph) {
19556
+ if (id.includes(":")) return { relation, id, missing: "on a related board" };
19557
+ const task = graph.get(id);
19558
+ if (!task) return { relation, id, missing: "not on this board" };
19559
+ if (graph.typeOf(task).role !== "decision") return { relation, id, task, missing: "not a decision" };
19560
+ return { relation, id, task, summary: (0, import_core3.summarizeDecision)(task) };
19561
+ }
19562
+ function relationsFor(task, graph) {
19563
+ if (!task) return NO_RELATIONS;
19564
+ if (graph.typeOf(task).role === "decision") {
19565
+ const decision = (0, import_core3.summarizeDecision)(task);
19566
+ const refs2 = [
19567
+ ...decision.supersedes.map((id) => refFor("replaces", id, graph)),
19568
+ ...decision.supersededBy ? [refFor("replaced-by", decision.supersededBy, graph)] : []
19569
+ ];
19570
+ const resting = graph.restingOn(task.id).filter((t) => graph.isOpenWork(t));
19571
+ return { decision, refs: refs2, resting };
19572
+ }
19573
+ const refs = graph.decisionsOf(task.id).map((link2) => refFor("rests-on", link2.id, graph));
19574
+ return { refs, resting: [] };
19575
+ }
19576
+ function isRetiredRef(ref) {
19577
+ return Boolean(ref.summary && (0, import_core3.isHistoricalStatus)(ref.summary.status));
19578
+ }
19579
+ function workOnRetiredDecisions(graph) {
19580
+ const out = /* @__PURE__ */ new Set();
19581
+ for (const task of graph.tasks) {
19582
+ if (!graph.isOpenWork(task)) continue;
19583
+ const links = graph.decisionsOf(task.id);
19584
+ if (links.some((link2) => link2.isDecision && link2.status && (0, import_core3.isHistoricalStatus)(link2.status))) {
19585
+ out.add(task.id);
19586
+ }
19587
+ }
19588
+ return out;
19413
19589
  }
19414
19590
 
19415
19591
  // src/tui/hooks/useBrainfileLoader.ts
@@ -21108,17 +21284,17 @@ function watch(paths, options = {}) {
21108
21284
  }
21109
21285
 
21110
21286
  // src/tui/hooks/useBrainfileLoader.ts
21111
- var import_core4 = __toESM(require_dist());
21287
+ var import_core5 = __toESM(require_dist());
21112
21288
 
21113
21289
  // src/tui/actions.ts
21114
21290
  import * as fs3 from "fs";
21115
21291
  import * as path from "path";
21116
21292
  import { spawnSync } from "child_process";
21117
- var import_core3 = __toESM(require_dist());
21293
+ var import_core4 = __toESM(require_dist());
21118
21294
  function readBrainfile(filePath) {
21119
21295
  try {
21120
21296
  const content = fs3.readFileSync(filePath, "utf-8");
21121
- const result = import_core3.Brainfile.parseWithErrors(content);
21297
+ const result = import_core4.Brainfile.parseWithErrors(content);
21122
21298
  if (!result.board) {
21123
21299
  return { board: null, content, error: result.error || "Failed to parse brainfile" };
21124
21300
  }
@@ -21129,7 +21305,7 @@ function readBrainfile(filePath) {
21129
21305
  }
21130
21306
  function writeBrainfile(filePath, board) {
21131
21307
  try {
21132
- const content = import_core3.Brainfile.serialize(board);
21308
+ const content = import_core4.Brainfile.serialize(board);
21133
21309
  fs3.writeFileSync(filePath, content, "utf-8");
21134
21310
  return { success: true };
21135
21311
  } catch (err) {
@@ -21150,61 +21326,61 @@ function readBoardState(filePath) {
21150
21326
  }
21151
21327
  function resolveV2TaskPath(filePath, taskId) {
21152
21328
  const dirs = (0, import_core.getV2Dirs)(filePath);
21153
- const doc = (0, import_core3.findTask)(dirs.boardDir, taskId);
21329
+ const doc = (0, import_core4.findTask)(dirs.boardDir, taskId);
21154
21330
  if (!doc || !doc.filePath) return null;
21155
21331
  return { taskPath: doc.filePath, task: doc.task };
21156
21332
  }
21157
21333
  function moveTaskAction(filePath, taskId, targetColumnId) {
21158
21334
  const { board, error } = readBoardState(filePath);
21159
21335
  if (!board) return { success: false, error };
21160
- const taskInfo = (0, import_core3.findTaskById)(board, taskId);
21336
+ const taskInfo = (0, import_core4.findTaskById)(board, taskId);
21161
21337
  if (!taskInfo) return { success: false, error: `Task ${taskId} not found` };
21162
- const targetColumn = (0, import_core3.findColumnById)(board, targetColumnId) || (0, import_core3.findColumnByName)(board, targetColumnId);
21338
+ const targetColumn = (0, import_core4.findColumnById)(board, targetColumnId) || (0, import_core4.findColumnByName)(board, targetColumnId);
21163
21339
  if (!targetColumn) return { success: false, error: `Column ${targetColumnId} not found` };
21164
21340
  if (taskInfo.column.id === targetColumn.id) {
21165
21341
  return { success: true, message: "Task already in this column" };
21166
21342
  }
21167
21343
  if ((0, import_core.isV2)(filePath)) {
21168
- const boardFile = (0, import_core3.readBoardConfig)(filePath);
21344
+ const boardFile = (0, import_core4.readBoardConfig)(filePath);
21169
21345
  if (!boardFile) return { success: false, error: `Failed to parse brainfile: ${filePath}` };
21170
- const moveResult = (0, import_core3.moveTaskFileToColumn)(
21346
+ const moveResult = (0, import_core4.moveTaskFileToColumn)(
21171
21347
  (0, import_core.getV2Dirs)(filePath),
21172
21348
  boardFile.config,
21173
21349
  taskId,
21174
21350
  targetColumn.id
21175
21351
  );
21176
21352
  if (!moveResult.success) return { success: false, error: moveResult.error };
21177
- scheduleBoardCommit(filePath, `move ${taskId}: ${targetColumn.id}`);
21353
+ (0, import_repo.scheduleBoardCommit)(filePath, `move ${taskId}: ${targetColumn.id}`);
21178
21354
  return {
21179
21355
  success: true,
21180
21356
  message: moveResult.autoCompleted ? `Completed via ${targetColumn.title}` : `Moved to ${targetColumn.title}`
21181
21357
  };
21182
21358
  }
21183
- const result = (0, import_core3.moveTask)(board, taskId, taskInfo.column.id, targetColumn.id, targetColumn.tasks.length);
21359
+ const result = (0, import_core4.moveTask)(board, taskId, taskInfo.column.id, targetColumn.id, targetColumn.tasks.length);
21184
21360
  if (!result.success) return { success: false, error: result.error };
21185
21361
  const writeResult = writeBrainfile(filePath, result.board);
21186
21362
  if (!writeResult.success) return writeResult;
21187
- scheduleBoardCommit(filePath, `move ${taskId}: ${targetColumn.id}`);
21363
+ (0, import_repo.scheduleBoardCommit)(filePath, `move ${taskId}: ${targetColumn.id}`);
21188
21364
  return { success: true, message: `Moved to ${targetColumn.title}` };
21189
21365
  }
21190
21366
  function deleteTaskAction(filePath, taskId) {
21191
21367
  const { board, error } = readBoardState(filePath);
21192
21368
  if (!board) return { success: false, error };
21193
- const taskInfo = (0, import_core3.findTaskById)(board, taskId);
21369
+ const taskInfo = (0, import_core4.findTaskById)(board, taskId);
21194
21370
  if (!taskInfo) return { success: false, error: `Task ${taskId} not found` };
21195
21371
  if ((0, import_core.isV2)(filePath)) {
21196
21372
  const located = resolveV2TaskPath(filePath, taskId);
21197
21373
  if (!located) return { success: false, error: `Task ${taskId} not found` };
21198
- const result2 = (0, import_core3.deleteTaskFile)(located.taskPath);
21374
+ const result2 = (0, import_core4.deleteTaskFile)(located.taskPath);
21199
21375
  if (!result2.success) return { success: false, error: result2.error };
21200
- scheduleBoardCommit(filePath, `delete ${taskId}`);
21376
+ (0, import_repo.scheduleBoardCommit)(filePath, `delete ${taskId}`);
21201
21377
  return { success: true, message: `Deleted ${taskId}` };
21202
21378
  }
21203
- const result = (0, import_core3.deleteTask)(board, taskInfo.column.id, taskId);
21379
+ const result = (0, import_core4.deleteTask)(board, taskInfo.column.id, taskId);
21204
21380
  if (!result.success) return { success: false, error: result.error };
21205
21381
  const writeResult = writeBrainfile(filePath, result.board);
21206
21382
  if (!writeResult.success) return writeResult;
21207
- scheduleBoardCommit(filePath, `delete ${taskId}`);
21383
+ (0, import_repo.scheduleBoardCommit)(filePath, `delete ${taskId}`);
21208
21384
  return { success: true, message: `Deleted ${taskId}` };
21209
21385
  }
21210
21386
  function getArchivePath(filePath) {
@@ -21225,7 +21401,7 @@ function archiveTaskAction(filePath, taskId, options) {
21225
21401
  const dirs = (0, import_core.getV2Dirs)(filePath);
21226
21402
  const located = resolveV2TaskPath(filePath, taskId);
21227
21403
  if (!located) return { success: false, error: `Task ${taskId} not found` };
21228
- const result2 = (0, import_core3.completeTaskFile)(located.taskPath, dirs.logsDir, { force: options?.force });
21404
+ const result2 = (0, import_core4.completeTaskFile)(located.taskPath, dirs.logsDir, { force: options?.force });
21229
21405
  if (!result2.success) {
21230
21406
  return {
21231
21407
  success: false,
@@ -21233,16 +21409,16 @@ function archiveTaskAction(filePath, taskId, options) {
21233
21409
  incompleteChildren: result2.incompleteChildren
21234
21410
  };
21235
21411
  }
21236
- scheduleBoardCommit(filePath, `complete ${taskId}`);
21412
+ (0, import_repo.scheduleBoardCommit)(filePath, `complete ${taskId}`);
21237
21413
  return { success: true, message: `Moved ${taskId} to logs` };
21238
21414
  }
21239
21415
  const { board, error } = readBrainfile(filePath);
21240
21416
  if (!board) return { success: false, error };
21241
- const taskInfo = (0, import_core3.findTaskById)(board, taskId);
21417
+ const taskInfo = (0, import_core4.findTaskById)(board, taskId);
21242
21418
  if (!taskInfo) return { success: false, error: `Task ${taskId} not found` };
21243
21419
  const task = taskInfo.task;
21244
21420
  const columnId = taskInfo.column.id;
21245
- const result = (0, import_core3.deleteTask)(board, columnId, taskId);
21421
+ const result = (0, import_core4.deleteTask)(board, columnId, taskId);
21246
21422
  if (!result.success) return { success: false, error: result.error };
21247
21423
  const writeResult = writeBrainfile(filePath, result.board);
21248
21424
  if (!writeResult.success) return writeResult;
@@ -21251,7 +21427,7 @@ function archiveTaskAction(filePath, taskId, options) {
21251
21427
  if (fs3.existsSync(archivePath)) {
21252
21428
  try {
21253
21429
  const archiveContent = fs3.readFileSync(archivePath, "utf-8");
21254
- const parseResult = import_core3.Brainfile.parseWithErrors(archiveContent);
21430
+ const parseResult = import_core4.Brainfile.parseWithErrors(archiveContent);
21255
21431
  if (parseResult.board) {
21256
21432
  archiveBoard = parseResult.board;
21257
21433
  } else {
@@ -21268,7 +21444,7 @@ function archiveTaskAction(filePath, taskId, options) {
21268
21444
  }
21269
21445
  archiveBoard.archive.unshift(task);
21270
21446
  try {
21271
- const archiveContent = import_core3.Brainfile.serialize(archiveBoard);
21447
+ const archiveContent = import_core4.Brainfile.serialize(archiveBoard);
21272
21448
  fs3.writeFileSync(archivePath, archiveContent, "utf-8");
21273
21449
  } catch (err) {
21274
21450
  return {
@@ -21276,13 +21452,13 @@ function archiveTaskAction(filePath, taskId, options) {
21276
21452
  error: `Task removed from board but failed to write logs: ${err instanceof Error ? err.message : String(err)}`
21277
21453
  };
21278
21454
  }
21279
- scheduleBoardCommit(filePath, `complete ${taskId}`);
21455
+ (0, import_repo.scheduleBoardCommit)(filePath, `complete ${taskId}`);
21280
21456
  return { success: true, message: `Moved ${taskId} to logs` };
21281
21457
  }
21282
21458
  async function archiveTaskActionAsync(filePath, taskId, options) {
21283
21459
  const { board, error } = readBoardState(filePath);
21284
21460
  if (!board) return { success: false, error };
21285
- const taskInfo = (0, import_core3.findTaskById)(board, taskId);
21461
+ const taskInfo = (0, import_core4.findTaskById)(board, taskId);
21286
21462
  if (!taskInfo) return { success: false, error: `Task ${taskId} not found` };
21287
21463
  const task = taskInfo.task;
21288
21464
  const columnId = taskInfo.column.id;
@@ -21303,7 +21479,7 @@ async function archiveTaskActionAsync(filePath, taskId, options) {
21303
21479
  if (!owner || !repo) {
21304
21480
  return { success: false, error: "GitHub owner/repo not configured. Set archive.destination: github:owner/repo" };
21305
21481
  }
21306
- const payload = (0, import_core3.formatTaskForGitHub)(task, {
21482
+ const payload = (0, import_core4.formatTaskForGitHub)(task, {
21307
21483
  includeMeta: true,
21308
21484
  includeSubtasks: true,
21309
21485
  includeRelatedFiles: true,
@@ -21325,10 +21501,10 @@ async function archiveTaskActionAsync(filePath, taskId, options) {
21325
21501
  if ((0, import_core.isV2)(filePath)) {
21326
21502
  const located = resolveV2TaskPath(filePath, taskId);
21327
21503
  if (located) {
21328
- (0, import_core3.deleteTaskFile)(located.taskPath);
21504
+ (0, import_core4.deleteTaskFile)(located.taskPath);
21329
21505
  }
21330
21506
  } else {
21331
- const deleteResult = (0, import_core3.deleteTask)(board, columnId, taskId);
21507
+ const deleteResult = (0, import_core4.deleteTask)(board, columnId, taskId);
21332
21508
  if (deleteResult.success) {
21333
21509
  writeBrainfile(filePath, deleteResult.board);
21334
21510
  }
@@ -21362,7 +21538,7 @@ async function archiveTaskActionAsync(filePath, taskId, options) {
21362
21538
  return { success: false, error: "No Linear teams found" };
21363
21539
  }
21364
21540
  }
21365
- const payload = (0, import_core3.formatTaskForLinear)(task, {
21541
+ const payload = (0, import_core4.formatTaskForLinear)(task, {
21366
21542
  includeMeta: true,
21367
21543
  includeSubtasks: true,
21368
21544
  includeRelatedFiles: true,
@@ -21384,10 +21560,10 @@ async function archiveTaskActionAsync(filePath, taskId, options) {
21384
21560
  if ((0, import_core.isV2)(filePath)) {
21385
21561
  const located = resolveV2TaskPath(filePath, taskId);
21386
21562
  if (located) {
21387
- (0, import_core3.deleteTaskFile)(located.taskPath);
21563
+ (0, import_core4.deleteTaskFile)(located.taskPath);
21388
21564
  }
21389
21565
  } else {
21390
- const deleteResult = (0, import_core3.deleteTask)(board, columnId, taskId);
21566
+ const deleteResult = (0, import_core4.deleteTask)(board, columnId, taskId);
21391
21567
  if (deleteResult.success) {
21392
21568
  writeBrainfile(filePath, deleteResult.board);
21393
21569
  }
@@ -21401,62 +21577,62 @@ function cyclePriorityAction(filePath, taskId) {
21401
21577
  if ((0, import_core.isV2)(filePath)) {
21402
21578
  const located = resolveV2TaskPath(filePath, taskId);
21403
21579
  if (!located) return { success: false, error: `Task ${taskId} not found` };
21404
- const doc = (0, import_core3.readTaskFile)(located.taskPath);
21580
+ const doc = (0, import_core4.readTaskFile)(located.taskPath);
21405
21581
  if (!doc) return { success: false, error: `Failed to read task ${taskId}` };
21406
21582
  const currentIndex2 = priorities.indexOf(doc.task.priority);
21407
21583
  const nextIndex2 = (currentIndex2 + 1) % priorities.length;
21408
21584
  const nextPriority2 = priorities[nextIndex2];
21409
- const result2 = (0, import_core3.patchTaskFile)(located.taskPath, { priority: nextPriority2 ?? null });
21585
+ const result2 = (0, import_core4.patchTaskFile)(located.taskPath, { priority: nextPriority2 ?? null });
21410
21586
  if (!result2.success) return { success: false, error: result2.error };
21411
- scheduleBoardCommit(filePath, `patch ${taskId}: priority ${nextPriority2 || "none"}`);
21587
+ (0, import_repo.scheduleBoardCommit)(filePath, `patch ${taskId}: priority ${nextPriority2 || "none"}`);
21412
21588
  return { success: true, message: `Priority: ${nextPriority2 || "none"}` };
21413
21589
  }
21414
21590
  const { board, error } = readBrainfile(filePath);
21415
21591
  if (!board) return { success: false, error };
21416
- const taskInfo = (0, import_core3.findTaskById)(board, taskId);
21592
+ const taskInfo = (0, import_core4.findTaskById)(board, taskId);
21417
21593
  if (!taskInfo) return { success: false, error: `Task ${taskId} not found` };
21418
21594
  const currentIndex = priorities.indexOf(taskInfo.task.priority);
21419
21595
  const nextIndex = (currentIndex + 1) % priorities.length;
21420
21596
  const nextPriority = priorities[nextIndex];
21421
21597
  const patch = { priority: nextPriority || null };
21422
- const result = (0, import_core3.patchTask)(board, taskId, patch);
21598
+ const result = (0, import_core4.patchTask)(board, taskId, patch);
21423
21599
  if (!result.success) return { success: false, error: result.error };
21424
21600
  const writeResult = writeBrainfile(filePath, result.board);
21425
21601
  if (!writeResult.success) return writeResult;
21426
- scheduleBoardCommit(filePath, `patch ${taskId}: priority ${nextPriority || "none"}`);
21602
+ (0, import_repo.scheduleBoardCommit)(filePath, `patch ${taskId}: priority ${nextPriority || "none"}`);
21427
21603
  return { success: true, message: `Priority: ${nextPriority || "none"}` };
21428
21604
  }
21429
21605
  function toggleSubtaskAction(filePath, taskId, subtaskId) {
21430
21606
  if ((0, import_core.isV2)(filePath)) {
21431
21607
  const located = resolveV2TaskPath(filePath, taskId);
21432
21608
  if (!located) return { success: false, error: `Task ${taskId} not found` };
21433
- const result2 = (0, import_core3.toggleSubtasksInFile)(located.taskPath, [subtaskId]);
21609
+ const result2 = (0, import_core4.toggleSubtasksInFile)(located.taskPath, [subtaskId]);
21434
21610
  if (!result2.success) {
21435
21611
  return {
21436
21612
  success: false,
21437
21613
  error: result2.missing?.length ? `Subtask ${subtaskId} not found` : result2.error
21438
21614
  };
21439
21615
  }
21440
- scheduleBoardCommit(filePath, `subtask toggle ${subtaskId}`);
21616
+ (0, import_repo.scheduleBoardCommit)(filePath, `subtask toggle ${subtaskId}`);
21441
21617
  return { success: true, message: `Toggled ${subtaskId}` };
21442
21618
  }
21443
21619
  const { board, error } = readBrainfile(filePath);
21444
21620
  if (!board) return { success: false, error };
21445
- const result = (0, import_core3.toggleSubtask)(board, taskId, subtaskId);
21621
+ const result = (0, import_core4.toggleSubtask)(board, taskId, subtaskId);
21446
21622
  if (!result.success) return { success: false, error: result.error };
21447
21623
  const writeResult = writeBrainfile(filePath, result.board);
21448
21624
  if (!writeResult.success) return writeResult;
21449
- scheduleBoardCommit(filePath, `subtask toggle ${subtaskId}`);
21625
+ (0, import_repo.scheduleBoardCommit)(filePath, `subtask toggle ${subtaskId}`);
21450
21626
  return { success: true, message: `Toggled ${subtaskId}` };
21451
21627
  }
21452
21628
  function addTaskAction(filePath, columnId, taskInput) {
21453
21629
  const { board, error } = readBoardState(filePath);
21454
21630
  if (!board) return { success: false, error };
21455
- const column = (0, import_core3.findColumnById)(board, columnId) || (0, import_core3.findColumnByName)(board, columnId);
21631
+ const column = (0, import_core4.findColumnById)(board, columnId) || (0, import_core4.findColumnByName)(board, columnId);
21456
21632
  if (!column) return { success: false, error: `Column ${columnId} not found` };
21457
21633
  if ((0, import_core.isV2)(filePath)) {
21458
21634
  const dirs = (0, import_core.getV2Dirs)(filePath);
21459
- const result2 = (0, import_core3.addTaskFile)(
21635
+ const result2 = (0, import_core4.addTaskFile)(
21460
21636
  dirs.boardDir,
21461
21637
  {
21462
21638
  title: taskInput.title,
@@ -21474,16 +21650,16 @@ ${taskInput.description.trim()}
21474
21650
  dirs.logsDir
21475
21651
  );
21476
21652
  if (!result2.success) return { success: false, error: result2.error };
21477
- scheduleBoardCommit(filePath, `add ${result2.task?.id || "task"}`);
21653
+ (0, import_repo.scheduleBoardCommit)(filePath, `add ${result2.task?.id || "task"}`);
21478
21654
  return { success: true, message: `Added ${result2.task?.id || "task"}` };
21479
21655
  }
21480
- const result = (0, import_core3.addTask)(board, column.id, taskInput);
21656
+ const result = (0, import_core4.addTask)(board, column.id, taskInput);
21481
21657
  if (!result.success) return { success: false, error: result.error };
21482
21658
  const writeResult = writeBrainfile(filePath, result.board);
21483
21659
  if (!writeResult.success) return writeResult;
21484
21660
  const newColumn = result.board.columns.find((c) => c.id === column.id);
21485
21661
  const newTask = newColumn?.tasks[newColumn.tasks.length - 1];
21486
- scheduleBoardCommit(filePath, `add ${newTask?.id || "task"}`);
21662
+ (0, import_repo.scheduleBoardCommit)(filePath, `add ${newTask?.id || "task"}`);
21487
21663
  return { success: true, message: `Added ${newTask?.id || "task"}` };
21488
21664
  }
21489
21665
  function resolveTaskFilePath(filePath, taskId) {
@@ -21534,10 +21710,10 @@ function getTaskActivity(filePath, taskId, limit = 5) {
21534
21710
  if (!(0, import_core.isV2)(filePath)) return [];
21535
21711
  try {
21536
21712
  const dirs = (0, import_core.getV2Dirs)(filePath);
21537
- const doc = (0, import_core3.findTask)(dirs.boardDir, taskId) ?? (0, import_core3.findTask)(dirs.logsDir, taskId);
21713
+ const doc = (0, import_core4.findTask)(dirs.boardDir, taskId) ?? (0, import_core4.findTask)(dirs.logsDir, taskId);
21538
21714
  const entries = [];
21539
21715
  if (doc) {
21540
- for (const note of (0, import_core3.parseNotes)(doc.body)) {
21716
+ for (const note of (0, import_core4.parseNotes)(doc.body)) {
21541
21717
  entries.push({
21542
21718
  at: note.at,
21543
21719
  text: note.agent ? `[${note.agent}] ${note.text}` : note.text
@@ -21545,7 +21721,7 @@ function getTaskActivity(filePath, taskId, limit = 5) {
21545
21721
  }
21546
21722
  }
21547
21723
  try {
21548
- for (const record of (0, import_core3.readLedger)(dirs.logsDir)) {
21724
+ for (const record of (0, import_core4.readLedger)(dirs.logsDir)) {
21549
21725
  if (record.id !== taskId) continue;
21550
21726
  entries.push({ at: record.completedAt, text: `completed \xB7 ${record.summary}` });
21551
21727
  }
@@ -21561,8 +21737,15 @@ function loadLogs(filePath) {
21561
21737
  if ((0, import_core.isV2)(filePath)) {
21562
21738
  try {
21563
21739
  const dirs = (0, import_core.getV2Dirs)(filePath);
21564
- const docs = (0, import_core3.readTasksDir)(dirs.logsDir);
21565
- const logs = docs.map((doc) => ({ ...doc.task })).sort((a, b) => {
21740
+ const docs = (0, import_core4.readTasksDir)(dirs.logsDir);
21741
+ const logs = docs.map((doc) => {
21742
+ const task = { ...doc.task };
21743
+ if (!task.description) {
21744
+ const description = (0, import_core4.extractDescription)(doc.body);
21745
+ if (description) task.description = description;
21746
+ }
21747
+ return task;
21748
+ }).sort((a, b) => {
21566
21749
  const aTime = a.completedAt ? Date.parse(a.completedAt) : 0;
21567
21750
  const bTime = b.completedAt ? Date.parse(b.completedAt) : 0;
21568
21751
  return bTime - aTime;
@@ -21578,7 +21761,7 @@ function loadLogs(filePath) {
21578
21761
  }
21579
21762
  try {
21580
21763
  const archiveContent = fs3.readFileSync(archivePath, "utf-8");
21581
- const parseResult = import_core3.Brainfile.parseWithErrors(archiveContent);
21764
+ const parseResult = import_core4.Brainfile.parseWithErrors(archiveContent);
21582
21765
  if (parseResult.board && parseResult.board.archive) {
21583
21766
  return { logs: parseResult.board.archive };
21584
21767
  }
@@ -21624,30 +21807,53 @@ function useBrainfileLoader(filePath, state, setState) {
21624
21807
  try {
21625
21808
  const content = fs4.readFileSync(filePath, "utf-8");
21626
21809
  const v2 = (0, import_core.isV2)(filePath);
21627
- let board = null;
21810
+ let parsed = null;
21628
21811
  let parseError;
21629
21812
  if (v2) {
21630
21813
  try {
21631
- board = (0, import_core.buildBoardFromV2)(filePath);
21814
+ parsed = (0, import_core.buildBoardFromV2)(filePath);
21632
21815
  } catch (err) {
21633
21816
  parseError = err instanceof Error ? err.message : String(err);
21634
21817
  }
21635
21818
  } else {
21636
- const result = import_core4.Brainfile.parseWithErrors(content);
21819
+ const result = import_core5.Brainfile.parseWithErrors(content);
21637
21820
  if (result.board) {
21638
- board = result.board;
21821
+ parsed = result.board;
21639
21822
  } else {
21640
21823
  parseError = result.error || "Not a valid brainfile";
21641
21824
  }
21642
21825
  }
21643
- const contentHash = v2 ? (0, import_core4.hashBoardContent)(`${content}
21644
- ${getV2ChangeSignature(filePath)}`) : (0, import_core4.hashBoardContent)(content);
21645
- const logs = loadLogs(filePath).logs;
21826
+ const contentHash = v2 ? (0, import_core5.hashBoardContent)(`${content}
21827
+ ${getV2ChangeSignature(filePath)}`) : (0, import_core5.hashBoardContent)(content);
21828
+ const split = parsed ? splitDecisions(parsed, loadLogs(filePath).logs) : null;
21829
+ const board = split?.board ?? null;
21830
+ const logs = split?.logs ?? [];
21831
+ const decisions = split?.decisions ?? [];
21646
21832
  setState((prev) => {
21647
21833
  if (!forceRefresh && prev.lastContentHash === contentHash) {
21648
21834
  return prev;
21649
21835
  }
21650
21836
  if (board) {
21837
+ if (isDecisionStop(prev.activeTypeFilter, boardTypes(board))) {
21838
+ const order = (list) => buildDecisionView(filterDecisions(list, prev.filterQuery), {
21839
+ historyOpen: prev.decisionHistory,
21840
+ types: boardTypes(board)
21841
+ }).selectable;
21842
+ const prevId = order(prev.decisions)[prev.selectedTaskIndex]?.id;
21843
+ const next = order(decisions);
21844
+ const foundIdx = prevId ? next.findIndex((t) => t.id === prevId) : -1;
21845
+ return {
21846
+ ...prev,
21847
+ board,
21848
+ logs,
21849
+ decisions,
21850
+ error: null,
21851
+ lastUpdated: /* @__PURE__ */ new Date(),
21852
+ selectedTaskIndex: foundIdx >= 0 ? foundIdx : Math.max(0, Math.min(prev.selectedTaskIndex, next.length - 1)),
21853
+ reloadFlash: true,
21854
+ lastContentHash: contentHash
21855
+ };
21856
+ }
21651
21857
  if (prev.doneView) {
21652
21858
  const prevLogId = prev.logs[prev.selectedTaskIndex]?.id;
21653
21859
  const foundIdx = prevLogId ? logs.findIndex((t) => t.id === prevLogId) : -1;
@@ -21656,6 +21862,7 @@ ${getV2ChangeSignature(filePath)}`) : (0, import_core4.hashBoardContent)(content
21656
21862
  ...prev,
21657
21863
  board,
21658
21864
  logs,
21865
+ decisions,
21659
21866
  error: null,
21660
21867
  lastUpdated: /* @__PURE__ */ new Date(),
21661
21868
  selectedTaskIndex: nextIndex,
@@ -21684,6 +21891,7 @@ ${getV2ChangeSignature(filePath)}`) : (0, import_core4.hashBoardContent)(content
21684
21891
  ...prev,
21685
21892
  board,
21686
21893
  logs,
21894
+ decisions,
21687
21895
  error: null,
21688
21896
  lastUpdated: /* @__PURE__ */ new Date(),
21689
21897
  activeColumnIndex: newColumnIndex,
@@ -21770,9 +21978,9 @@ import * as path3 from "path";
21770
21978
  var POLL_MS = 5e3;
21771
21979
  function useSyncStatus(filePath, lastUpdated) {
21772
21980
  const dotDir = path3.dirname(path3.resolve(filePath));
21773
- const [label, setLabel] = (0, import_react35.useState)(() => syncStatusLabel(dotDir) ?? void 0);
21981
+ const [label, setLabel] = (0, import_react35.useState)(() => (0, import_repo2.syncStatusLabel)(dotDir) ?? void 0);
21774
21982
  (0, import_react35.useEffect)(() => {
21775
- const refresh = () => setLabel(syncStatusLabel(dotDir) ?? void 0);
21983
+ const refresh = () => setLabel((0, import_repo2.syncStatusLabel)(dotDir) ?? void 0);
21776
21984
  refresh();
21777
21985
  const timer = setInterval(refresh, POLL_MS);
21778
21986
  timer.unref?.();
@@ -21785,9 +21993,93 @@ function useSyncStatus(filePath, lastUpdated) {
21785
21993
  var import_react37 = __toESM(require_react());
21786
21994
  import { spawnSync as spawnSync2 } from "child_process";
21787
21995
 
21996
+ // src/tui/utils.ts
21997
+ var import_core6 = __toESM(require_dist());
21998
+ var import_core7 = __toESM(require_dist());
21999
+
22000
+ // src/tui/text.ts
22001
+ function truncate(text, maxWidth) {
22002
+ if (!text) return " ";
22003
+ if (maxWidth <= 0) return " ";
22004
+ if (maxWidth <= 3) return text.slice(0, maxWidth) || " ";
22005
+ if (text.length <= maxWidth) return text;
22006
+ return `${text.slice(0, maxWidth - 1)}\u2026`;
22007
+ }
22008
+ function truncateStart(text, maxWidth) {
22009
+ if (!text) return " ";
22010
+ if (maxWidth <= 0) return " ";
22011
+ if (maxWidth <= 1) return text.slice(-1) || " ";
22012
+ if (text.length <= maxWidth) return text;
22013
+ return `\u2026${text.slice(text.length - (maxWidth - 1))}`;
22014
+ }
22015
+ function wrapText2(text, maxWidth) {
22016
+ if (!text || maxWidth <= 0) return [];
22017
+ const words = text.split(/\s+/);
22018
+ const lines = [];
22019
+ let currentLine = "";
22020
+ for (const word of words) {
22021
+ if (word.length > maxWidth) {
22022
+ if (currentLine) {
22023
+ lines.push(currentLine);
22024
+ currentLine = "";
22025
+ }
22026
+ for (let i = 0; i < word.length; i += maxWidth) {
22027
+ lines.push(word.slice(i, i + maxWidth));
22028
+ }
22029
+ } else if (currentLine.length + 1 + word.length <= maxWidth) {
22030
+ currentLine = currentLine ? `${currentLine} ${word}` : word;
22031
+ } else {
22032
+ if (currentLine) lines.push(currentLine);
22033
+ currentLine = word;
22034
+ }
22035
+ }
22036
+ if (currentLine) lines.push(currentLine);
22037
+ return lines.length > 0 ? lines : [""];
22038
+ }
22039
+ function pad(n) {
22040
+ return " ".repeat(Math.max(0, n));
22041
+ }
22042
+
22043
+ // src/tui/utils.ts
22044
+ function getPriorityColor(priority) {
22045
+ switch (priority?.toLowerCase()) {
22046
+ case "critical":
22047
+ return PALETTE.critical;
22048
+ case "high":
22049
+ return PALETTE.high;
22050
+ case "medium":
22051
+ return PALETTE.medium;
22052
+ case "low":
22053
+ return PALETTE.low;
22054
+ default:
22055
+ return PALETTE.textDim;
22056
+ }
22057
+ }
22058
+ function getDocType(task) {
22059
+ return (task.type || "task").toLowerCase();
22060
+ }
22061
+ function isCompletable(task) {
22062
+ if (!task) return false;
22063
+ return (0, import_core6.docRole)(task.type) !== "decision";
22064
+ }
22065
+ function getSubtaskProgress(task) {
22066
+ const subtasks = task.subtasks ?? [];
22067
+ if (subtasks.length === 0) return void 0;
22068
+ const done = subtasks.filter((s) => s.completed).length;
22069
+ return `${done}/${subtasks.length}`;
22070
+ }
22071
+ function getContractState(task) {
22072
+ const contract = task.contract;
22073
+ return contract?.status;
22074
+ }
22075
+
21788
22076
  // src/tui/detailStops.ts
21789
- function buildDetailStops(task, children) {
21790
- const stops = children.map((child) => ({ kind: "child", task: child }));
22077
+ function buildDetailStops(task, children, relations) {
22078
+ const stops = [
22079
+ ...(relations?.refs ?? []).map((ref) => ({ kind: "ref", ref })),
22080
+ ...(relations?.resting ?? []).map((resting) => ({ kind: "resting", task: resting })),
22081
+ ...children.map((child) => ({ kind: "child", task: child }))
22082
+ ];
21791
22083
  (task.subtasks ?? []).forEach((subtask, index) => {
21792
22084
  stops.push({ kind: "subtask", subtask, index });
21793
22085
  });
@@ -21802,14 +22094,17 @@ function clampDetailCursor(cursor, stopCount) {
21802
22094
  var import_react36 = __toESM(require_react());
21803
22095
  var MAX_DELIVERABLES = 3;
21804
22096
  var MAX_VALIDATION_COMMANDS = 2;
21805
- function computeDetailLayout(task, width, height, children, activity) {
22097
+ function computeDetailLayout(task, width, height, children, activity, relations = NO_RELATIONS) {
21806
22098
  const contentWidth = Math.max(20, width - 2);
21807
22099
  const stacked = contentWidth < 60;
21808
22100
  const subtasks = task.subtasks ?? [];
21809
22101
  const files = task.relatedFiles ?? [];
21810
22102
  const contract = task.contract;
22103
+ const { decision, refs, resting } = relations;
21811
22104
  const bodyLines = buildBodyLines(task.description, contentWidth);
21812
- const metaLines = stacked ? 4 : 1;
22105
+ const metaLines = decision ? decisionFieldLines(decision, refs, contentWidth).length : stacked ? 4 : 1;
22106
+ const restsOnLines = !decision && refs.length > 0 ? 1 + refs.length : 0;
22107
+ const restingLines = resting.length > 0 ? 1 + resting.length : 0;
21813
22108
  const childrenLines = children.length > 0 ? 1 + children.length : 0;
21814
22109
  const subtasksLines = subtasks.length > 0 ? 1 + subtasks.length : 0;
21815
22110
  const contractLines = contract ? 1 + Math.min(contract.deliverables?.length ?? 0, MAX_DELIVERABLES) + Math.min(contract.validation?.commands?.length ?? 0, MAX_VALIDATION_COMMANDS) + (contract.feedback ? 1 : 0) : 0;
@@ -21817,6 +22112,8 @@ function computeDetailLayout(task, width, height, children, activity) {
21817
22112
  const filesLines = files.length > 0 ? 1 : 0;
21818
22113
  const sectionsWithMargin = [
21819
22114
  bodyLines.length > 0,
22115
+ restsOnLines > 0,
22116
+ restingLines > 0,
21820
22117
  childrenLines > 0,
21821
22118
  subtasksLines > 0,
21822
22119
  contractLines > 0,
@@ -21825,7 +22122,7 @@ function computeDetailLayout(task, width, height, children, activity) {
21825
22122
  ].filter(Boolean).length;
21826
22123
  const bodyBudget = Math.max(
21827
22124
  2,
21828
- height - 2 - metaLines - 1 - childrenLines - subtasksLines - contractLines - activityLines - filesLines - sectionsWithMargin
22125
+ height - 2 - metaLines - 1 - restsOnLines - restingLines - childrenLines - subtasksLines - contractLines - activityLines - filesLines - sectionsWithMargin
21829
22126
  );
21830
22127
  return { bodyLines, bodyBudget, bodyOverflows: bodyLines.length > bodyBudget };
21831
22128
  }
@@ -21839,20 +22136,26 @@ function DetailView({
21839
22136
  children,
21840
22137
  activity,
21841
22138
  cursor,
21842
- scrollOffset
22139
+ scrollOffset,
22140
+ relations = NO_RELATIONS
21843
22141
  }) {
21844
22142
  const contentWidth = Math.max(20, width - 2);
21845
22143
  const stacked = contentWidth < 60;
21846
- const priority = task.priority;
22144
+ const { decision, refs, resting } = relations;
22145
+ const priority = decision ? void 0 : task.priority;
21847
22146
  const subtasks = task.subtasks ?? [];
21848
22147
  const files = task.relatedFiles ?? [];
21849
22148
  const contract = task.contract;
22149
+ const restingBase = refs.length;
22150
+ const childBase = restingBase + resting.length;
22151
+ const subtaskBase = childBase + children.length;
21850
22152
  const { bodyLines, bodyBudget, bodyOverflows } = computeDetailLayout(
21851
22153
  task,
21852
22154
  width,
21853
22155
  height,
21854
22156
  children,
21855
- activity
22157
+ activity,
22158
+ relations
21856
22159
  );
21857
22160
  const clampedScroll = Math.max(0, Math.min(scrollOffset, Math.max(0, bodyLines.length - bodyBudget)));
21858
22161
  const visibleBody = bodyLines.slice(clampedScroll, clampedScroll + bodyBudget);
@@ -21861,7 +22164,16 @@ function DetailView({
21861
22164
  const breadcrumbStr = truncateStart(breadcrumb.join(" \u25B8 ") || task.id, breadcrumbBudget);
21862
22165
  const headWidth = Math.max(1, contentWidth - stateText.length - 2);
21863
22166
  const head = truncate(`${breadcrumbStr} ${task.title}`, headWidth);
21864
- return /* @__PURE__ */ import_react36.default.createElement(Box_default, { flexDirection: "column", width, flexShrink: 0 }, /* @__PURE__ */ import_react36.default.createElement(Box_default, { paddingLeft: 1, width }, /* @__PURE__ */ import_react36.default.createElement(Text, { wrap: "truncate" }, /* @__PURE__ */ import_react36.default.createElement(Text, { color: PALETTE.text, bold: true }, head), /* @__PURE__ */ import_react36.default.createElement(Text, null, pad(Math.max(1, contentWidth - head.length - stateText.length))), /* @__PURE__ */ import_react36.default.createElement(Text, { color: PALETTE.textSecondary }, columnLabel), priority ? /* @__PURE__ */ import_react36.default.createElement(Text, null, /* @__PURE__ */ import_react36.default.createElement(Text, { color: PALETTE.textDim }, " \xB7 "), /* @__PURE__ */ import_react36.default.createElement(Text, { color: getPriorityColor(priority) }, priority)) : null)), /* @__PURE__ */ import_react36.default.createElement(Box_default, { paddingLeft: 1, width }, /* @__PURE__ */ import_react36.default.createElement(Text, { color: PALETTE.textDim }, RULE.repeat(contentWidth))), /* @__PURE__ */ import_react36.default.createElement(MetadataBlock, { task, parent, stacked, width }), bodyLines.length > 0 ? /* @__PURE__ */ import_react36.default.createElement(Box_default, { flexDirection: "column", paddingLeft: 1, marginTop: 1 }, /* @__PURE__ */ import_react36.default.createElement(
22167
+ return /* @__PURE__ */ import_react36.default.createElement(Box_default, { flexDirection: "column", width, flexShrink: 0 }, /* @__PURE__ */ import_react36.default.createElement(Box_default, { paddingLeft: 1, width }, /* @__PURE__ */ import_react36.default.createElement(Text, { wrap: "truncate" }, /* @__PURE__ */ import_react36.default.createElement(Text, { color: PALETTE.text, bold: true }, head), /* @__PURE__ */ import_react36.default.createElement(Text, null, pad(Math.max(1, contentWidth - head.length - stateText.length))), /* @__PURE__ */ import_react36.default.createElement(Text, { color: PALETTE.textSecondary }, columnLabel), priority ? /* @__PURE__ */ import_react36.default.createElement(Text, null, /* @__PURE__ */ import_react36.default.createElement(Text, { color: PALETTE.textDim }, " \xB7 "), /* @__PURE__ */ import_react36.default.createElement(Text, { color: getPriorityColor(priority) }, priority)) : null)), /* @__PURE__ */ import_react36.default.createElement(Box_default, { paddingLeft: 1, width }, /* @__PURE__ */ import_react36.default.createElement(Text, { color: PALETTE.textDim }, RULE.repeat(contentWidth))), decision ? /* @__PURE__ */ import_react36.default.createElement(
22168
+ DecisionFields,
22169
+ {
22170
+ decision,
22171
+ refs,
22172
+ cursor,
22173
+ contentWidth,
22174
+ width
22175
+ }
22176
+ ) : /* @__PURE__ */ import_react36.default.createElement(MetadataBlock, { task, parent, stacked, width }), bodyLines.length > 0 ? /* @__PURE__ */ import_react36.default.createElement(Box_default, { flexDirection: "column", paddingLeft: 1, marginTop: 1 }, /* @__PURE__ */ import_react36.default.createElement(
21865
22177
  DescriptionHeading,
21866
22178
  {
21867
22179
  bodyOverflows,
@@ -21878,18 +22190,42 @@ function DetailView({
21878
22190
  color: line.kind === "code" ? PALETTE.textMuted : line.kind === "heading" ? PALETTE.text : PALETTE.textSecondary
21879
22191
  },
21880
22192
  line.text || " "
22193
+ ))) : null, !decision && refs.length > 0 ? /* @__PURE__ */ import_react36.default.createElement(Box_default, { flexDirection: "column", paddingLeft: 1, marginTop: 1 }, /* @__PURE__ */ import_react36.default.createElement(Text, { color: PALETTE.textMuted }, `rests on (${refs.length})`), refs.map((ref, index) => {
22194
+ const retired = isRetiredRef(ref);
22195
+ return /* @__PURE__ */ import_react36.default.createElement(
22196
+ StopRow,
22197
+ {
22198
+ key: `ref-${ref.id}`,
22199
+ selected: index === cursor,
22200
+ left: ` ${refGlyph(ref)} ${ref.id} ${ref.task?.title ?? ref.missing ?? ""}`,
22201
+ leftColor: ref.task && !ref.missing ? PALETTE.textSecondary : PALETTE.textMuted,
22202
+ right: retired && ref.summary ? retiredLabel(ref.summary) : ref.missing === "not a decision" ? ref.missing : void 0,
22203
+ rightColor: retired ? PALETTE.warning : PALETTE.textMuted,
22204
+ contentWidth
22205
+ }
22206
+ );
22207
+ })) : null, resting.length > 0 ? /* @__PURE__ */ import_react36.default.createElement(Box_default, { flexDirection: "column", paddingLeft: 1, marginTop: 1 }, /* @__PURE__ */ import_react36.default.createElement(Text, { color: PALETTE.textMuted }, `work resting on it (${resting.length})`), resting.map((work, index) => /* @__PURE__ */ import_react36.default.createElement(
22208
+ StopRow,
22209
+ {
22210
+ key: `resting-${work.id}`,
22211
+ selected: restingBase + index === cursor,
22212
+ left: ` ${work.id} ${work.title}`,
22213
+ right: work.column ?? "",
22214
+ rightColor: PALETTE.textSecondary,
22215
+ contentWidth
22216
+ }
21881
22217
  ))) : null, children.length > 0 ? /* @__PURE__ */ import_react36.default.createElement(Box_default, { flexDirection: "column", paddingLeft: 1, marginTop: 1 }, /* @__PURE__ */ import_react36.default.createElement(Text, { color: PALETTE.textMuted }, `children (${children.length})`), children.map((child, index) => /* @__PURE__ */ import_react36.default.createElement(
21882
22218
  StopRow,
21883
22219
  {
21884
22220
  key: child.id,
21885
- selected: index === cursor,
22221
+ selected: childBase + index === cursor,
21886
22222
  left: ` ${child.id} ${child.title}`,
21887
22223
  right: child.column ?? "",
21888
22224
  rightColor: PALETTE.textSecondary,
21889
22225
  contentWidth
21890
22226
  }
21891
22227
  ))) : null, subtasks.length > 0 ? /* @__PURE__ */ import_react36.default.createElement(Box_default, { flexDirection: "column", paddingLeft: 1, marginTop: 1 }, /* @__PURE__ */ import_react36.default.createElement(Text, { color: PALETTE.textMuted }, `subtasks (${subtasks.filter((s) => s.completed).length}/${subtasks.length})`), subtasks.map((subtask, index) => {
21892
- const stopIndex = children.length + index;
22228
+ const stopIndex = subtaskBase + index;
21893
22229
  const glyph = subtask.completed ? GLYPHS.subtaskDone : GLYPHS.subtaskOpen;
21894
22230
  return /* @__PURE__ */ import_react36.default.createElement(
21895
22231
  StopRow,
@@ -21942,7 +22278,8 @@ function StopRow({
21942
22278
  contentWidth
21943
22279
  }) {
21944
22280
  const rightText = right ?? "";
21945
- const leftTruncated = truncate(left, Math.max(1, contentWidth - rightText.length - 2));
22281
+ const marked = selected && left.startsWith(" ") ? ` ${GLYPHS.cursor}${left.slice(2)}` : left;
22282
+ const leftTruncated = truncate(marked, Math.max(1, contentWidth - rightText.length - 2));
21946
22283
  const gap = Math.max(1, contentWidth - leftTruncated.length - rightText.length);
21947
22284
  return /* @__PURE__ */ import_react36.default.createElement(Text, { wrap: "truncate" }, /* @__PURE__ */ import_react36.default.createElement(Text, { color: selected ? void 0 : leftColor, inverse: selected }, leftTruncated), /* @__PURE__ */ import_react36.default.createElement(Text, null, pad(gap)), rightText ? /* @__PURE__ */ import_react36.default.createElement(Text, { color: rightColor }, rightText) : null);
21948
22285
  }
@@ -21955,6 +22292,74 @@ function LabeledList({
21955
22292
  const labelWidth = 16;
21956
22293
  return /* @__PURE__ */ import_react36.default.createElement(import_react36.default.Fragment, null, items.map((item, index) => /* @__PURE__ */ import_react36.default.createElement(Text, { key: `${label}-${index}`, wrap: "truncate" }, /* @__PURE__ */ import_react36.default.createElement(Text, { color: PALETTE.textMuted }, ` ${index === 0 ? label.padEnd(labelWidth - 2) : " ".repeat(labelWidth - 2)}`), /* @__PURE__ */ import_react36.default.createElement(Text, { color }, truncate(item, Math.max(1, contentWidth - labelWidth))))));
21957
22294
  }
22295
+ var FIELD_LABEL_WIDTH = 13;
22296
+ var MAX_FIELD_LINES = 3;
22297
+ function decisionFieldLines(decision, refs, contentWidth) {
22298
+ const valueWidth = Math.max(8, contentWidth - FIELD_LABEL_WIDTH);
22299
+ const lines = [];
22300
+ const add = (label, value, wrap = false) => {
22301
+ if (!value) return;
22302
+ let parts = wrap ? wrapText2(value, valueWidth) : [value];
22303
+ if (parts.length > MAX_FIELD_LINES) {
22304
+ const rest = parts.slice(MAX_FIELD_LINES - 1).join(" ");
22305
+ parts = [...parts.slice(0, MAX_FIELD_LINES - 1), truncate(rest, valueWidth)];
22306
+ }
22307
+ parts.forEach((text, index) => lines.push({ label: index === 0 ? label : "", value: text }));
22308
+ };
22309
+ add("claim key", decision.claimKey);
22310
+ add("kind", decision.kind);
22311
+ if (decision.stance) {
22312
+ add("stance", decision.subject ? `${decision.stance} ${decision.subject}` : decision.stance, true);
22313
+ } else {
22314
+ add("subject", decision.subject, true);
22315
+ }
22316
+ add("holds while", decision.holdsWhile, true);
22317
+ add("recheck", decision.recheck, true);
22318
+ if (decision.validUntil) {
22319
+ lines.push({
22320
+ label: "valid until",
22321
+ value: decision.validUntil.slice(0, 10),
22322
+ ...isPastValidUntil(decision) ? { signal: "review" } : {}
22323
+ });
22324
+ }
22325
+ refs.forEach((ref, index) => {
22326
+ lines.push({
22327
+ label: ref.relation === "replaced-by" ? "replaced by" : "replaces",
22328
+ value: `${ref.id} ${ref.task?.title ?? ref.missing ?? ""}`,
22329
+ ...ref.missing ? { muted: true } : {},
22330
+ stop: index
22331
+ });
22332
+ });
22333
+ add("created", decision.createdAt?.slice(0, 10));
22334
+ return lines;
22335
+ }
22336
+ function DecisionFields({
22337
+ decision,
22338
+ refs,
22339
+ cursor,
22340
+ contentWidth,
22341
+ width
22342
+ }) {
22343
+ const lines = decisionFieldLines(decision, refs, contentWidth);
22344
+ if (lines.length === 0) return null;
22345
+ return /* @__PURE__ */ import_react36.default.createElement(Box_default, { flexDirection: "column", paddingLeft: 1, width }, lines.map((line, index) => {
22346
+ const selected = line.stop !== void 0 && line.stop === cursor;
22347
+ const signal = line.signal ?? "";
22348
+ const valueWidth = Math.max(1, contentWidth - FIELD_LABEL_WIDTH - (signal ? signal.length + 2 : 0));
22349
+ return /* @__PURE__ */ import_react36.default.createElement(Text, { key: `field-${index}`, wrap: "truncate" }, /* @__PURE__ */ import_react36.default.createElement(Text, { color: PALETTE.textMuted }, line.label.padEnd(FIELD_LABEL_WIDTH - 1)), /* @__PURE__ */ import_react36.default.createElement(Text, null, selected ? GLYPHS.cursor : " "), /* @__PURE__ */ import_react36.default.createElement(
22350
+ Text,
22351
+ {
22352
+ color: selected ? void 0 : line.muted ? PALETTE.textMuted : PALETTE.textSecondary,
22353
+ inverse: selected
22354
+ },
22355
+ truncate(line.value, valueWidth)
22356
+ ), signal ? /* @__PURE__ */ import_react36.default.createElement(Text, null, pad(2)) : null, signal ? /* @__PURE__ */ import_react36.default.createElement(Text, { color: PALETTE.warning }, signal) : null);
22357
+ }));
22358
+ }
22359
+ function refGlyph(ref) {
22360
+ if (!ref.summary) return " ";
22361
+ return isRetiredRef(ref) ? GLYPHS.retiredDecision : getTypeGlyph("decision");
22362
+ }
21958
22363
  function MetadataBlock({
21959
22364
  task,
21960
22365
  parent,
@@ -22019,11 +22424,11 @@ function buildBodyLines(description, width) {
22019
22424
  }
22020
22425
 
22021
22426
  // src/tui/tuiState.ts
22022
- var import_core5 = __toESM(require_dist());
22427
+ var import_core8 = __toESM(require_dist());
22023
22428
  import * as fs5 from "fs";
22024
22429
  import * as path4 from "path";
22025
22430
  function getTuiStatePath(brainfilePath) {
22026
- return path4.join((0, import_core5.getBrainfileStateDir)(brainfilePath), import_core5.DOT_BRAINFILE_STATE_DIRNAME, "tui.json");
22431
+ return path4.join((0, import_core8.getBrainfileStateDir)(brainfilePath), import_core8.DOT_BRAINFILE_STATE_DIRNAME, "tui.json");
22027
22432
  }
22028
22433
  function readTuiState(brainfilePath) {
22029
22434
  const statePath = getTuiStatePath(brainfilePath);
@@ -22045,8 +22450,8 @@ function patchTuiState(brainfilePath, patch) {
22045
22450
  writeTuiState(brainfilePath, { ...current, ...patch });
22046
22451
  }
22047
22452
  function writeTuiState(brainfilePath, state) {
22048
- (0, import_core5.ensureDotBrainfileGitignore)(brainfilePath);
22049
- (0, import_core5.atomicWriteFileSync)(getTuiStatePath(brainfilePath), `${JSON.stringify(state, null, 2)}
22453
+ (0, import_core8.ensureDotBrainfileGitignore)(brainfilePath);
22454
+ (0, import_core8.atomicWriteFileSync)(getTuiStatePath(brainfilePath), `${JSON.stringify(state, null, 2)}
22050
22455
  `);
22051
22456
  }
22052
22457
 
@@ -22069,7 +22474,12 @@ function useKeyboardNavigation({
22069
22474
  detailWidth,
22070
22475
  detailHeight,
22071
22476
  doneView,
22072
- detailArchived
22477
+ detailArchived,
22478
+ decisionsView = false,
22479
+ decisionStop = "decision",
22480
+ decisionHistoryCount = 0,
22481
+ detailRelations = NO_RELATIONS,
22482
+ detailIsDecision = false
22073
22483
  }) {
22074
22484
  const { exit, suspendTerminal } = use_app_default();
22075
22485
  const { isRawModeSupported } = use_stdin_default();
@@ -22129,6 +22539,11 @@ function useKeyboardNavigation({
22129
22539
  );
22130
22540
  const currentTask = rows[state.selectedTaskIndex]?.task;
22131
22541
  const maxRowIndex = Math.max(0, rows.length - 1);
22542
+ const rowArchived = doneView || Boolean(rows[state.selectedTaskIndex]?.archived);
22543
+ const ownColumnIndex = (task, fallback) => {
22544
+ const at = task ? allColumns.findIndex((column) => column.tasks?.some((t) => t.id === task.id)) : -1;
22545
+ return at >= 0 ? at : fallback;
22546
+ };
22132
22547
  const overlayTarget = state.detailPath.length > 0 ? detailTask : currentTask;
22133
22548
  const setMode = (mode, extra = {}) => setState((prev) => ({ ...prev, mode, ...extra }));
22134
22549
  const closeOverlay = (opts = {}) => {
@@ -22331,10 +22746,17 @@ function useKeyboardNavigation({
22331
22746
  return;
22332
22747
  }
22333
22748
  if (state.mode === "detail") {
22334
- const stops = detailTask ? buildDetailStops(detailTask, detailChildren) : [];
22749
+ const stops = detailTask ? buildDetailStops(detailTask, detailChildren, detailRelations) : [];
22335
22750
  const cursor = clampDetailCursor(state.detailCursor, stops.length);
22336
22751
  const stop = stops[cursor];
22337
- const layout = detailTask ? computeDetailLayout(detailTask, detailWidth, detailHeight, detailChildren, detailActivity) : null;
22752
+ const layout = detailTask ? computeDetailLayout(
22753
+ detailTask,
22754
+ detailWidth,
22755
+ detailHeight,
22756
+ detailChildren,
22757
+ detailActivity,
22758
+ detailRelations
22759
+ ) : null;
22338
22760
  if (input === "q") {
22339
22761
  exit();
22340
22762
  return;
@@ -22389,17 +22811,24 @@ function useKeyboardNavigation({
22389
22811
  return;
22390
22812
  }
22391
22813
  if (key.return) {
22392
- if (stop?.kind === "child") {
22814
+ const target = stop?.kind === "child" || stop?.kind === "resting" ? stop.task.id : stop?.kind === "ref" && stop.ref.task ? stop.ref.id : void 0;
22815
+ if (target) {
22393
22816
  setState((prev) => ({
22394
22817
  ...prev,
22395
- detailPath: [...prev.detailPath, stop.task.id],
22818
+ detailPath: [...prev.detailPath, target],
22396
22819
  detailCursor: 0,
22397
22820
  detailScroll: 0
22398
22821
  }));
22822
+ } else if (stop?.kind === "ref" && stop.ref.missing) {
22823
+ showStatus(STATUS.cannotOpen(stop.ref.id, stop.ref.missing), "info");
22399
22824
  }
22400
22825
  return;
22401
22826
  }
22402
22827
  if (input === " ") {
22828
+ if (detailIsDecision) {
22829
+ showStatus(STATUS.decisionsReadOnly, "info");
22830
+ return;
22831
+ }
22403
22832
  if (detailArchived) {
22404
22833
  showStatus(STATUS.readOnly, "info");
22405
22834
  return;
@@ -22430,8 +22859,12 @@ function useKeyboardNavigation({
22430
22859
  });
22431
22860
  return;
22432
22861
  }
22862
+ if (detailIsDecision && detailTask && (input === "m" || input === "c")) {
22863
+ showStatus(input === "m" ? STATUS.decisionNoColumn : STATUS.cannotComplete(detailTask.id), "info");
22864
+ return;
22865
+ }
22433
22866
  if (input === "m" && detailTask && !detailArchived) {
22434
- setState((prev) => ({ ...prev, mode: "move", moveTargetIndex: prev.activeColumnIndex }));
22867
+ setState((prev) => ({ ...prev, mode: "move", moveTargetIndex: ownColumnIndex(detailTask, prev.activeColumnIndex) }));
22435
22868
  return;
22436
22869
  }
22437
22870
  if (input === "c" && detailTask && !detailArchived && isCompletable(detailTask)) {
@@ -22456,21 +22889,40 @@ function useKeyboardNavigation({
22456
22889
  setMode("filter", { filterQuery: "", selectedTaskIndex: 0 });
22457
22890
  return;
22458
22891
  }
22892
+ if (input === "D") {
22893
+ const next = decisionsView ? state.decisionsReturn ?? "all" : decisionStop;
22894
+ patchTuiState(filePath, { lastTypeFilter: next });
22895
+ setState((prev) => ({
22896
+ ...prev,
22897
+ activeTypeFilter: next,
22898
+ decisionsReturn: decisionsView ? null : prev.activeTypeFilter,
22899
+ selectedTaskIndex: 0
22900
+ }));
22901
+ return;
22902
+ }
22459
22903
  if (input === "L") {
22904
+ if (decisionsView) {
22905
+ if (decisionHistoryCount === 0) {
22906
+ showStatus(STATUS.noDecisionHistory, "info");
22907
+ return;
22908
+ }
22909
+ setState((prev) => ({ ...prev, decisionHistory: !prev.decisionHistory }));
22910
+ return;
22911
+ }
22460
22912
  setState((prev) => ({ ...prev, doneView: !prev.doneView, selectedTaskIndex: 0 }));
22461
22913
  return;
22462
22914
  }
22463
22915
  if (input === "t") {
22464
22916
  if (typeCycleOptions.length > 1) {
22465
- const idx = typeCycleOptions.indexOf(state.activeTypeFilter);
22917
+ const idx = typeCycleOptions.indexOf(decisionsView ? decisionStop : state.activeTypeFilter);
22466
22918
  const next = typeCycleOptions[(idx < 0 ? 0 : idx + 1) % typeCycleOptions.length];
22467
22919
  patchTuiState(filePath, { lastTypeFilter: next });
22468
- setState((prev) => ({ ...prev, activeTypeFilter: next, selectedTaskIndex: 0 }));
22920
+ setState((prev) => ({ ...prev, activeTypeFilter: next, decisionsReturn: null, selectedTaskIndex: 0 }));
22469
22921
  }
22470
22922
  return;
22471
22923
  }
22472
22924
  if (input === " ") {
22473
- if (doneView) return;
22925
+ if (doneView || decisionsView) return;
22474
22926
  const row = rows[state.selectedTaskIndex];
22475
22927
  if (row && (row.childCount ?? 0) > 0) {
22476
22928
  const id = row.task.id;
@@ -22497,6 +22949,12 @@ function useKeyboardNavigation({
22497
22949
  }
22498
22950
  return;
22499
22951
  }
22952
+ if (decisionsView && ["a", "n", "N", "m", "c", "d", "p", "A"].includes(input) && !key.ctrl) {
22953
+ if (input === "c" && currentTask) showStatus(STATUS.cannotComplete(currentTask.id), "info");
22954
+ else if (input === "m") showStatus(STATUS.decisionNoColumn, "info");
22955
+ else showStatus(STATUS.decisionsReadOnly, "info");
22956
+ return;
22957
+ }
22500
22958
  if (input === "a" || input === "n") {
22501
22959
  if (doneView) {
22502
22960
  showStatus(STATUS.readOnly, "info");
@@ -22506,7 +22964,7 @@ function useKeyboardNavigation({
22506
22964
  return;
22507
22965
  }
22508
22966
  if (input === "m") {
22509
- if (doneView) {
22967
+ if (rowArchived) {
22510
22968
  showStatus(STATUS.readOnly, "info");
22511
22969
  return;
22512
22970
  }
@@ -22514,11 +22972,11 @@ function useKeyboardNavigation({
22514
22972
  showStatus(STATUS.noDocument, "error");
22515
22973
  return;
22516
22974
  }
22517
- setState((prev) => ({ ...prev, mode: "move", moveTargetIndex: prev.activeColumnIndex }));
22975
+ setState((prev) => ({ ...prev, mode: "move", moveTargetIndex: ownColumnIndex(currentTask, prev.activeColumnIndex) }));
22518
22976
  return;
22519
22977
  }
22520
22978
  if (input === "c") {
22521
- if (doneView) {
22979
+ if (rowArchived) {
22522
22980
  showStatus(STATUS.alreadyCompleted, "info");
22523
22981
  return;
22524
22982
  }
@@ -22534,7 +22992,7 @@ function useKeyboardNavigation({
22534
22992
  return;
22535
22993
  }
22536
22994
  if (input === "d") {
22537
- if (doneView) {
22995
+ if (rowArchived) {
22538
22996
  showStatus(STATUS.readOnly, "info");
22539
22997
  return;
22540
22998
  }
@@ -22555,7 +23013,7 @@ function useKeyboardNavigation({
22555
23013
  }
22556
23014
  if (input === "p") {
22557
23015
  if (!currentTask) return;
22558
- if (doneView) {
23016
+ if (rowArchived) {
22559
23017
  showStatus(STATUS.readOnly, "info");
22560
23018
  return;
22561
23019
  }
@@ -22587,7 +23045,7 @@ function useKeyboardNavigation({
22587
23045
  }
22588
23046
  if (input === "A") {
22589
23047
  if (!currentTask) return;
22590
- if (doneView) {
23048
+ if (rowArchived) {
22591
23049
  showStatus(STATUS.readOnly, "info");
22592
23050
  return;
22593
23051
  }
@@ -22641,7 +23099,7 @@ function useKeyboardNavigation({
22641
23099
  return;
22642
23100
  }
22643
23101
  if (key.tab || key.rightArrow || input === "l") {
22644
- if (!doneView && filteredColumnsLength > 0) {
23102
+ if (!doneView && !decisionsView && filteredColumnsLength > 0) {
22645
23103
  setState((prev) => {
22646
23104
  const nextIndex = (prev.activeColumnIndex + 1) % filteredColumnsLength;
22647
23105
  rememberColumn(nextIndex);
@@ -22651,7 +23109,7 @@ function useKeyboardNavigation({
22651
23109
  return;
22652
23110
  }
22653
23111
  if (key.shift && key.tab || key.leftArrow || input === "h") {
22654
- if (!doneView && filteredColumnsLength > 0) {
23112
+ if (!doneView && !decisionsView && filteredColumnsLength > 0) {
22655
23113
  setState((prev) => {
22656
23114
  const nextIndex = prev.activeColumnIndex === 0 ? filteredColumnsLength - 1 : prev.activeColumnIndex - 1;
22657
23115
  rememberColumn(nextIndex);
@@ -22668,14 +23126,25 @@ function useKeyboardNavigation({
22668
23126
  }
22669
23127
 
22670
23128
  // src/tui/typeCycle.ts
22671
- var FIXED_TYPE_ORDER = ["task", "epic", "spec", "plan", "adr"];
22672
- function getTypeCycleOptions(board) {
23129
+ var import_core9 = __toESM(require_dist());
23130
+ var FIXED_TYPE_ORDER = ["task", "epic", "spec", "plan", "adr", "decision"];
23131
+ function getTypeCycleOptions(board, decisions = []) {
22673
23132
  const configured = Object.keys(
22674
23133
  board?.types ?? {}
22675
23134
  );
22676
23135
  const configuredSet = new Set(configured);
22677
- const known = FIXED_TYPE_ORDER.filter((type) => type === "task" || configuredSet.has(type));
22678
- const extra = configured.filter((type) => !FIXED_TYPE_ORDER.includes(type)).sort();
23136
+ const present = new Set(
23137
+ (board?.columns ?? []).flatMap((column) => (column.tasks ?? []).map((task) => (task.type || "task").toLowerCase()))
23138
+ );
23139
+ const types = boardTypes(board);
23140
+ const isDecisionType = (type) => (0, import_core9.resolveDocType)(type, types).role === "decision";
23141
+ const custom = configured.filter((type) => !FIXED_TYPE_ORDER.includes(type) && !(0, import_core9.isTypeAlias)(type));
23142
+ const holdsDecisions = decisions.length > 0 || [...present].some(isDecisionType) || custom.some(isDecisionType);
23143
+ let known = FIXED_TYPE_ORDER.filter(
23144
+ (type) => type === "task" || configuredSet.has(type) || present.has(type) || type === "decision" && holdsDecisions
23145
+ );
23146
+ if (known.includes("adr") && known.includes("decision")) known = known.filter((type) => type !== "adr");
23147
+ const extra = custom.filter((type) => !isDecisionType(type)).sort();
22679
23148
  return ["all", ...known, ...extra];
22680
23149
  }
22681
23150
 
@@ -22684,6 +23153,7 @@ var import_react38 = __toESM(require_react());
22684
23153
  function HeaderBar({
22685
23154
  title,
22686
23155
  columns,
23156
+ counts,
22687
23157
  activeColumnIndex,
22688
23158
  width,
22689
23159
  filterQuery = "",
@@ -22698,9 +23168,10 @@ function HeaderBar({
22698
23168
  const boardTitle = truncate(title, 24);
22699
23169
  const separator = ` ${GLYPHS.pointer} `;
22700
23170
  const typeLabel = activeType && activeType !== "all" ? ` \xB7 ${activeType}` : "";
23171
+ const countOf = (column, index) => counts?.[index] ?? column.tasks?.length ?? 0;
22701
23172
  const tabsWidth = (panelLabel ? panelLabel.length : columns.reduce((sum, column, index) => {
22702
23173
  const active = index === activeColumnIndex;
22703
- return sum + (index > 0 ? 3 : 0) + `${column.title} ${column.tasks?.length ?? 0}`.length + (active ? 1 : 0);
23174
+ return sum + (index > 0 ? 3 : 0) + `${column.title} ${countOf(column, index)}`.length + (active ? 1 : 0);
22704
23175
  }, 0)) + typeLabel.length;
22705
23176
  const status = statusLabel ? `${statusLabel} ` : "";
22706
23177
  const used = boardTitle.length + separator.length + tabsWidth;
@@ -22712,7 +23183,7 @@ function HeaderBar({
22712
23183
  // header silently loses rows rather than the frame simply growing.
22713
23184
  /* @__PURE__ */ import_react38.default.createElement(Box_default, { flexDirection: "column", flexShrink: 0 }, /* @__PURE__ */ import_react38.default.createElement(Box_default, { paddingLeft: 1, width }, /* @__PURE__ */ import_react38.default.createElement(Text, { wrap: "truncate" }, /* @__PURE__ */ import_react38.default.createElement(Text, { color: PALETTE.text, bold: true }, boardTitle), /* @__PURE__ */ import_react38.default.createElement(Text, { color: PALETTE.textDim }, separator), panelLabel ? /* @__PURE__ */ import_react38.default.createElement(Text, { color: PALETTE.text }, panelLabel) : columns.map((column, index) => {
22714
23185
  const active = index === activeColumnIndex;
22715
- const count = column.tasks?.length ?? 0;
23186
+ const count = countOf(column, index);
22716
23187
  return /* @__PURE__ */ import_react38.default.createElement(Text, { key: column.id }, index > 0 ? /* @__PURE__ */ import_react38.default.createElement(Text, { color: PALETTE.textDim }, " \u2502 ") : null, /* @__PURE__ */ import_react38.default.createElement(Text, { color: active ? PALETTE.text : PALETTE.textMuted, bold: active }, `${column.title} ${count}`), active ? /* @__PURE__ */ import_react38.default.createElement(Text, { color: PALETTE.text, bold: true }, "*") : null);
22717
23188
  }), typeLabel ? /* @__PURE__ */ import_react38.default.createElement(Text, { color: PALETTE.textDim }, typeLabel) : null, /* @__PURE__ */ import_react38.default.createElement(Text, null, pad(gap)), status ? /* @__PURE__ */ import_react38.default.createElement(Text, { color: statusLabel?.startsWith("not synced \xB7") ? PALETTE.warning : PALETTE.textDim }, status) : null, /* @__PURE__ */ import_react38.default.createElement(Text, { color: PALETTE.textMuted }, affordances))), filterActive ? /* @__PURE__ */ import_react38.default.createElement(
22718
23189
  FilterLine,
@@ -22739,21 +23210,37 @@ function FilterLine({
22739
23210
 
22740
23211
  // src/tui/components/FooterBar.tsx
22741
23212
  var import_react39 = __toESM(require_react());
22742
- function browseActions(selected, archived = false) {
23213
+ function browseActions(selected, archived = false, hasDecisions = false, selectedDone = false) {
22743
23214
  if (archived) return ["\u21B5 detail", "e edit", "t type", "L board", "q quit"];
22744
- const actions = ["\u21B5 detail", "m move"];
22745
- if (isCompletable(selected)) actions.push("c complete");
22746
- actions.push("a add", "t type", "L done", "tab column", "q quit");
23215
+ const actions = ["\u21B5 detail"];
23216
+ if (selectedDone) actions.push("e edit");
23217
+ else {
23218
+ actions.push("m move");
23219
+ if (isCompletable(selected)) actions.push("c complete");
23220
+ }
23221
+ actions.push("a add", "t type", "L done");
23222
+ if (hasDecisions) actions.push("D decisions");
23223
+ actions.push("tab column", "q quit");
23224
+ return actions;
23225
+ }
23226
+ function decisionsActions({
23227
+ historyCount,
23228
+ historyOpen
23229
+ }) {
23230
+ const actions = ["\u21B5 detail", "e edit"];
23231
+ if (historyCount > 0) actions.push(historyOpen ? "L hide history" : "L history");
23232
+ actions.push("t type", "D board", "q quit");
22747
23233
  return actions;
22748
23234
  }
22749
23235
  function detailActions(ctx) {
22750
23236
  const actions = [];
22751
- if (ctx.hasChildren) actions.push("\u21B5 open child");
22752
- if (ctx.hasSubtasks) actions.push("space toggle");
23237
+ if (ctx.hasLinks) actions.push("\u21B5 open");
23238
+ else if (ctx.hasChildren) actions.push("\u21B5 open child");
23239
+ if (ctx.hasSubtasks && !ctx.decision) actions.push("space toggle");
22753
23240
  if (ctx.bodyOverflows) actions.push("u/d scroll");
22754
23241
  if (ctx.hasParent) actions.push("p parent");
22755
23242
  actions.push("e edit");
22756
- if (!ctx.archived) {
23243
+ if (!ctx.archived && !ctx.decision) {
22757
23244
  actions.push("m move");
22758
23245
  if (isCompletable(ctx.task)) actions.push("c complete");
22759
23246
  }
@@ -22785,12 +23272,16 @@ function FooterBar({
22785
23272
  // src/tui/components/DocumentRow.tsx
22786
23273
  var import_react40 = __toESM(require_react());
22787
23274
  var MAX_CHIPS = 3;
23275
+ var DECISION_CHANGED = "decision changed";
22788
23276
  function buildChips(row) {
22789
23277
  const chips = [];
22790
23278
  const { task } = row;
22791
23279
  if (row.collapsed && row.childCount) {
22792
23280
  chips.push({ text: `${row.childCount} hidden`, color: PALETTE.textMuted });
22793
23281
  }
23282
+ if (row.decisionChanged) {
23283
+ chips.push({ text: DECISION_CHANGED, color: PALETTE.warning });
23284
+ }
22794
23285
  const contractState = getContractState(task);
22795
23286
  if (contractState) {
22796
23287
  chips.push({ text: contractState, color: getContractStateColor(contractState) });
@@ -22805,6 +23296,11 @@ function buildChips(row) {
22805
23296
  }
22806
23297
  return chips.slice(0, MAX_CHIPS);
22807
23298
  }
23299
+ function buildStatus(row) {
23300
+ if (row.archived) return { text: `${GLYPHS.success} done`, color: PALETTE.success };
23301
+ if (row.column) return { text: row.column, color: PALETTE.textSecondary };
23302
+ return void 0;
23303
+ }
22808
23304
  function buildPriorityMarker(row) {
22809
23305
  const priority = row.task.priority;
22810
23306
  if (priority !== "high" && priority !== "critical") return void 0;
@@ -22821,16 +23317,19 @@ function DocumentRow({ row, selected, width, idWidth, archived = false }) {
22821
23317
  const marker = buildPriorityMarker(row);
22822
23318
  const chips = buildChips(row);
22823
23319
  const orphan = orphanParentId ? `${GLYPHS.orphanParent} ${orphanParentId}` : void 0;
23320
+ const status = buildStatus(row);
23321
+ const muted = archived || Boolean(row.archived);
22824
23322
  const rightSegments = [];
22825
23323
  if (orphan) rightSegments.push({ text: orphan, color: PALETTE.textDim });
22826
23324
  if (marker) rightSegments.push(marker);
22827
23325
  rightSegments.push(...chips);
23326
+ if (status) rightSegments.push(status);
22828
23327
  const rightWidth = rightSegments.reduce((sum, seg) => sum + seg.text.length, 0) + Math.max(0, rightSegments.length - 1) * 2;
22829
23328
  const titleWidth = Math.max(1, width - leftWidth - rightWidth - 2);
22830
23329
  const title = truncate(task.title, titleWidth);
22831
23330
  const gap = Math.max(1, width - leftWidth - title.length - rightWidth - 1);
22832
23331
  const dim = selected ? void 0 : PALETTE.textMuted;
22833
- return /* @__PURE__ */ import_react40.default.createElement(Text, { wrap: "truncate", inverse: selected }, /* @__PURE__ */ import_react40.default.createElement(Text, null, indent), /* @__PURE__ */ import_react40.default.createElement(Text, { color: selected ? void 0 : PALETTE.textSecondary }, glyphCell), /* @__PURE__ */ import_react40.default.createElement(Text, { color: dim }, idCell), /* @__PURE__ */ import_react40.default.createElement(Text, { color: selected ? void 0 : archived ? PALETTE.textMuted : PALETTE.text }, title), /* @__PURE__ */ import_react40.default.createElement(Text, null, pad(gap)), rightSegments.map((seg, index) => /* @__PURE__ */ import_react40.default.createElement(Text, { key: `${seg.text}-${index}` }, index > 0 ? /* @__PURE__ */ import_react40.default.createElement(Text, null, pad(2)) : null, /* @__PURE__ */ import_react40.default.createElement(Text, { color: selected ? void 0 : seg.color }, seg.text))), /* @__PURE__ */ import_react40.default.createElement(Text, null, pad(1)));
23332
+ return /* @__PURE__ */ import_react40.default.createElement(Text, { wrap: "truncate", inverse: selected }, /* @__PURE__ */ import_react40.default.createElement(Text, null, indent), /* @__PURE__ */ import_react40.default.createElement(Text, { color: selected ? void 0 : PALETTE.textSecondary }, glyphCell), /* @__PURE__ */ import_react40.default.createElement(Text, { color: dim }, idCell), /* @__PURE__ */ import_react40.default.createElement(Text, { color: selected ? void 0 : muted ? PALETTE.textMuted : PALETTE.text }, title), /* @__PURE__ */ import_react40.default.createElement(Text, null, pad(gap)), rightSegments.map((seg, index) => /* @__PURE__ */ import_react40.default.createElement(Text, { key: `${seg.text}-${index}` }, index > 0 ? /* @__PURE__ */ import_react40.default.createElement(Text, null, pad(2)) : null, /* @__PURE__ */ import_react40.default.createElement(Text, { color: selected ? void 0 : seg.color }, seg.text))), /* @__PURE__ */ import_react40.default.createElement(Text, null, pad(1)));
22834
23333
  }
22835
23334
 
22836
23335
  // src/tui/components/DocumentList.tsx
@@ -22861,8 +23360,91 @@ function DocumentList({
22861
23360
  )));
22862
23361
  }
22863
23362
 
22864
- // src/tui/components/HelpOverlay.tsx
23363
+ // src/tui/components/DecisionList.tsx
22865
23364
  var import_react42 = __toESM(require_react());
23365
+ function decisionSignals(line) {
23366
+ const { summary, retired } = line;
23367
+ const out = [];
23368
+ if (retired) {
23369
+ out.push({ text: retiredLabel(summary), color: PALETTE.textMuted });
23370
+ return out;
23371
+ }
23372
+ if (summary.status === "proposed") out.push({ text: "proposed", color: PALETTE.textMuted });
23373
+ if (summary.kind && summary.kind !== "decision") out.push({ text: summary.kind, color: PALETTE.textMuted });
23374
+ if (isPastValidUntil(summary)) out.push({ text: "review", color: PALETTE.warning });
23375
+ return out;
23376
+ }
23377
+ function revealedClaimKey(line) {
23378
+ const key = line.summary.claimKey;
23379
+ if (!key) return "";
23380
+ return line.retired ? key : claimKeyTail(key);
23381
+ }
23382
+ var widthOf = (signals) => signals.reduce((sum, signal) => sum + signal.text.length, 0) + Math.max(0, signals.length - 1) * 2;
23383
+ function DecisionRow({ line, selected, width, idWidth }) {
23384
+ const { task, retired } = line;
23385
+ const indent = selected ? GLYPHS.cursor : " ";
23386
+ const glyphCell = `${retired ? GLYPHS.retiredDecision : getTypeGlyph("decision")} `;
23387
+ const idCell = `${task.id.padEnd(idWidth)} `;
23388
+ const leftWidth = indent.length + glyphCell.length + idCell.length;
23389
+ const atRest = decisionSignals(line);
23390
+ const key = selected ? revealedClaimKey(line) : "";
23391
+ const withKey = key ? [{ text: key, color: PALETTE.textMuted }, ...atRest] : atRest;
23392
+ const fits = width - leftWidth - widthOf(withKey) - 2 >= task.title.length;
23393
+ const signals = key && fits ? withKey : atRest;
23394
+ const rightWidth = widthOf(signals);
23395
+ const titleWidth = Math.max(1, width - leftWidth - rightWidth - 2);
23396
+ const title = truncate(task.title, titleWidth);
23397
+ const gap = Math.max(1, width - leftWidth - title.length - rightWidth - 1);
23398
+ return /* @__PURE__ */ import_react42.default.createElement(Text, { wrap: "truncate", inverse: selected }, /* @__PURE__ */ import_react42.default.createElement(Text, null, indent), /* @__PURE__ */ import_react42.default.createElement(Text, { color: selected ? void 0 : retired ? PALETTE.textMuted : PALETTE.textSecondary }, glyphCell), /* @__PURE__ */ import_react42.default.createElement(Text, { color: selected ? void 0 : PALETTE.textMuted }, idCell), /* @__PURE__ */ import_react42.default.createElement(Text, { color: selected ? void 0 : retired ? PALETTE.textMuted : PALETTE.text }, title), /* @__PURE__ */ import_react42.default.createElement(Text, null, pad(gap)), signals.map((signal, index) => /* @__PURE__ */ import_react42.default.createElement(Text, { key: `${signal.text}-${index}` }, index > 0 ? /* @__PURE__ */ import_react42.default.createElement(Text, null, pad(2)) : null, /* @__PURE__ */ import_react42.default.createElement(Text, { color: selected ? void 0 : signal.color }, signal.text))), /* @__PURE__ */ import_react42.default.createElement(Text, null, pad(1)));
23399
+ }
23400
+ function DecisionList({
23401
+ lines,
23402
+ selectedIndex,
23403
+ viewportHeight,
23404
+ width,
23405
+ emptyMessage = EMPTY.noDecisions
23406
+ }) {
23407
+ if (lines.length === 0) {
23408
+ return /* @__PURE__ */ import_react42.default.createElement(Box_default, { flexDirection: "column", flexGrow: 1, paddingLeft: 1 }, /* @__PURE__ */ import_react42.default.createElement(Text, { color: PALETTE.textMuted }, emptyMessage));
23409
+ }
23410
+ const { visible, start } = windowLines(lines, selectedIndex, viewportHeight);
23411
+ const idWidth = Math.max(
23412
+ 6,
23413
+ ...visible.flatMap((line) => line.kind === "row" ? [line.task.id.length] : [])
23414
+ );
23415
+ return /* @__PURE__ */ import_react42.default.createElement(Box_default, { flexDirection: "column", flexGrow: 1 }, visible.map((line, offset) => {
23416
+ const key = `${line.kind}-${start + offset}`;
23417
+ switch (line.kind) {
23418
+ case "row":
23419
+ return /* @__PURE__ */ import_react42.default.createElement(
23420
+ DecisionRow,
23421
+ {
23422
+ key: line.task.id,
23423
+ line,
23424
+ selected: line.index === selectedIndex,
23425
+ width,
23426
+ idWidth
23427
+ }
23428
+ );
23429
+ case "heading":
23430
+ return (
23431
+ // Headings sit in the glyph column, so a family's name lines up
23432
+ // with the marks of the decisions under it.
23433
+ /* @__PURE__ */ import_react42.default.createElement(Text, { key, wrap: "truncate" }, /* @__PURE__ */ import_react42.default.createElement(Text, null, " "), /* @__PURE__ */ import_react42.default.createElement(Text, { color: PALETTE.textSecondary }, truncate(line.label, Math.max(1, width - 10))), /* @__PURE__ */ import_react42.default.createElement(Text, { color: PALETTE.textMuted }, ` (${line.count})`))
23434
+ );
23435
+ case "history":
23436
+ return /* @__PURE__ */ import_react42.default.createElement(Text, { key, wrap: "truncate" }, /* @__PURE__ */ import_react42.default.createElement(Text, null, " "), /* @__PURE__ */ import_react42.default.createElement(Text, { color: PALETTE.textMuted }, `${line.open ? GLYPHS.foldOpen : GLYPHS.foldClosed} `), /* @__PURE__ */ import_react42.default.createElement(Text, { color: PALETTE.textSecondary }, "history"), /* @__PURE__ */ import_react42.default.createElement(Text, { color: PALETTE.textMuted }, ` (${line.count})`));
23437
+ case "note":
23438
+ return /* @__PURE__ */ import_react42.default.createElement(Text, { key, wrap: "truncate", color: PALETTE.textMuted }, ` ${line.text}`);
23439
+ case "gap":
23440
+ default:
23441
+ return /* @__PURE__ */ import_react42.default.createElement(Text, { key }, " ");
23442
+ }
23443
+ }));
23444
+ }
23445
+
23446
+ // src/tui/components/HelpOverlay.tsx
23447
+ var import_react43 = __toESM(require_react());
22866
23448
  var LIST = {
22867
23449
  heading: "list",
22868
23450
  rows: [
@@ -22872,14 +23454,15 @@ var LIST = {
22872
23454
  ["ctrl-d/u", "half page"],
22873
23455
  ["\u21B5", "open"],
22874
23456
  ["t", "type"],
22875
- ["L", "done"],
23457
+ ["L", "done \xB7 history"],
23458
+ ["D", "decisions"],
22876
23459
  ["/", "filter"]
22877
23460
  ]
22878
23461
  };
22879
23462
  var DETAIL = {
22880
23463
  heading: "detail",
22881
23464
  rows: [
22882
- ["\u21B5", "open child"],
23465
+ ["\u21B5", "open child or link"],
22883
23466
  ["space", "toggle subtask"],
22884
23467
  ["u d", "scroll body"],
22885
23468
  ["p", "parent"],
@@ -22917,29 +23500,34 @@ var COLUMNS = [
22917
23500
  [LIST, DETAIL],
22918
23501
  [ACTIONS, FILTER]
22919
23502
  ];
22920
- var KEYS_WIDTH = 14;
23503
+ var KEYS_WIDTHS = COLUMNS.map(
23504
+ (column) => Math.max(...column.flatMap((group) => group.rows.map(([keys]) => keys.length))) + 2
23505
+ );
22921
23506
  function HelpOverlay({ termWidth, termHeight }) {
22922
23507
  const columnWidth = Math.max(24, Math.floor((termWidth - 4) / COLUMNS.length));
22923
- const descriptionWidth = Math.max(6, columnWidth - KEYS_WIDTH - 2);
22924
- return /* @__PURE__ */ import_react42.default.createElement(Box_default, { flexDirection: "column", width: termWidth, height: termHeight, paddingLeft: 1 }, /* @__PURE__ */ import_react42.default.createElement(Text, { color: PALETTE.text, bold: true }, CHROME.helpTitle), /* @__PURE__ */ import_react42.default.createElement(Text, { color: PALETTE.textDim }, RULE.repeat(Math.max(1, termWidth - 2))), /* @__PURE__ */ import_react42.default.createElement(Box_default, { marginTop: 1 }, COLUMNS.map((column, columnIndex) => /* @__PURE__ */ import_react42.default.createElement(Box_default, { key: columnIndex, flexDirection: "column", width: columnWidth }, column.map((group, groupIndex) => /* @__PURE__ */ import_react42.default.createElement(
22925
- Box_default,
22926
- {
22927
- key: group.heading,
22928
- flexDirection: "column",
22929
- marginTop: groupIndex === 0 ? 0 : 1
22930
- },
22931
- /* @__PURE__ */ import_react42.default.createElement(Text, { color: PALETTE.textSecondary, bold: true }, group.heading),
22932
- group.rows.map(([keys, description]) => /* @__PURE__ */ import_react42.default.createElement(Text, { key: keys, wrap: "truncate" }, /* @__PURE__ */ import_react42.default.createElement(Text, { color: PALETTE.text }, keys.padEnd(KEYS_WIDTH)), /* @__PURE__ */ import_react42.default.createElement(Text, { color: PALETTE.textMuted }, truncate(description, descriptionWidth))))
22933
- ))))), /* @__PURE__ */ import_react42.default.createElement(Box_default, { marginTop: 1 }, /* @__PURE__ */ import_react42.default.createElement(Text, { color: PALETTE.textDim }, CHROME.helpDismiss)));
23508
+ return /* @__PURE__ */ import_react43.default.createElement(Box_default, { flexDirection: "column", width: termWidth, height: termHeight, paddingLeft: 1 }, /* @__PURE__ */ import_react43.default.createElement(Text, { color: PALETTE.text, bold: true }, CHROME.helpTitle), /* @__PURE__ */ import_react43.default.createElement(Text, { color: PALETTE.textDim }, RULE.repeat(Math.max(1, termWidth - 2))), /* @__PURE__ */ import_react43.default.createElement(Box_default, { marginTop: 1 }, COLUMNS.map((column, columnIndex) => {
23509
+ const keysWidth = KEYS_WIDTHS[columnIndex];
23510
+ const descriptionWidth = Math.max(6, columnWidth - keysWidth - 2);
23511
+ return /* @__PURE__ */ import_react43.default.createElement(Box_default, { key: columnIndex, flexDirection: "column", width: columnWidth }, column.map((group, groupIndex) => /* @__PURE__ */ import_react43.default.createElement(
23512
+ Box_default,
23513
+ {
23514
+ key: group.heading,
23515
+ flexDirection: "column",
23516
+ marginTop: groupIndex === 0 ? 0 : 1
23517
+ },
23518
+ /* @__PURE__ */ import_react43.default.createElement(Text, { color: PALETTE.textSecondary, bold: true }, group.heading),
23519
+ group.rows.map(([keys, description]) => /* @__PURE__ */ import_react43.default.createElement(Text, { key: keys, wrap: "truncate" }, /* @__PURE__ */ import_react43.default.createElement(Text, { color: PALETTE.text }, keys.padEnd(keysWidth)), /* @__PURE__ */ import_react43.default.createElement(Text, { color: PALETTE.textMuted }, truncate(description, descriptionWidth))))
23520
+ )));
23521
+ })), /* @__PURE__ */ import_react43.default.createElement(Box_default, { marginTop: 1 }, /* @__PURE__ */ import_react43.default.createElement(Text, { color: PALETTE.textDim }, CHROME.helpDismiss)));
22934
23522
  }
22935
23523
 
22936
23524
  // src/tui/components/Overlays.tsx
22937
- var import_react43 = __toESM(require_react());
23525
+ var import_react44 = __toESM(require_react());
22938
23526
  function StatusMessageDisplay({ message }) {
22939
23527
  if (!message) return null;
22940
23528
  const color = message.type === "success" ? PALETTE.success : message.type === "error" ? PALETTE.error : PALETTE.textSecondary;
22941
23529
  const glyph = message.type === "success" ? GLYPHS.success : message.type === "error" ? GLYPHS.error : GLYPHS.live;
22942
- return /* @__PURE__ */ import_react43.default.createElement(Box_default, { paddingLeft: 1 }, /* @__PURE__ */ import_react43.default.createElement(Text, { color, wrap: "truncate" }, `${glyph} ${message.text}`));
23530
+ return /* @__PURE__ */ import_react44.default.createElement(Box_default, { paddingLeft: 1 }, /* @__PURE__ */ import_react44.default.createElement(Text, { color, wrap: "truncate" }, `${glyph} ${message.text}`));
22943
23531
  }
22944
23532
  function MoveOverlay({
22945
23533
  columns,
@@ -22948,16 +23536,16 @@ function MoveOverlay({
22948
23536
  taskTitle,
22949
23537
  width
22950
23538
  }) {
22951
- return /* @__PURE__ */ import_react43.default.createElement(Box_default, { flexDirection: "column", flexGrow: 1, paddingLeft: 1 }, /* @__PURE__ */ import_react43.default.createElement(Text, { color: PALETTE.textMuted }, "move"), /* @__PURE__ */ import_react43.default.createElement(Text, { wrap: "truncate" }, /* @__PURE__ */ import_react43.default.createElement(Text, { color: PALETTE.textMuted }, ` ${taskId} `), /* @__PURE__ */ import_react43.default.createElement(Text, { color: PALETTE.text }, truncate(taskTitle, Math.max(1, width - taskId.length - 6)))), /* @__PURE__ */ import_react43.default.createElement(Box_default, { flexDirection: "column", marginTop: 1, paddingLeft: 2 }, columns.map((column, index) => /* @__PURE__ */ import_react43.default.createElement(Text, { key: column.id, inverse: index === selectedIndex, wrap: "truncate" }, `${index + 1} ${column.title}`))), /* @__PURE__ */ import_react43.default.createElement(Box_default, { marginTop: 1, paddingLeft: 2 }, /* @__PURE__ */ import_react43.default.createElement(Text, { color: PALETTE.textMuted }, "\u2191\u2193 select \xB7 1-", columns.length, " jump \xB7 \u21B5 confirm \xB7 esc cancel")));
23539
+ return /* @__PURE__ */ import_react44.default.createElement(Box_default, { flexDirection: "column", flexGrow: 1, paddingLeft: 1 }, /* @__PURE__ */ import_react44.default.createElement(Text, { color: PALETTE.textMuted }, "move"), /* @__PURE__ */ import_react44.default.createElement(Text, { wrap: "truncate" }, /* @__PURE__ */ import_react44.default.createElement(Text, { color: PALETTE.textMuted }, ` ${taskId} `), /* @__PURE__ */ import_react44.default.createElement(Text, { color: PALETTE.text }, truncate(taskTitle, Math.max(1, width - taskId.length - 6)))), /* @__PURE__ */ import_react44.default.createElement(Box_default, { flexDirection: "column", marginTop: 1, paddingLeft: 2 }, columns.map((column, index) => /* @__PURE__ */ import_react44.default.createElement(Text, { key: column.id, inverse: index === selectedIndex, wrap: "truncate" }, `${index + 1} ${column.title}`))), /* @__PURE__ */ import_react44.default.createElement(Box_default, { marginTop: 1, paddingLeft: 2 }, /* @__PURE__ */ import_react44.default.createElement(Text, { color: PALETTE.textMuted }, "\u2191\u2193 select \xB7 1-", columns.length, " jump \xB7 \u21B5 confirm \xB7 esc cancel")));
22952
23540
  }
22953
23541
  function DeleteConfirmOverlay({ taskId, taskTitle, width }) {
22954
- return /* @__PURE__ */ import_react43.default.createElement(Box_default, { flexDirection: "column", flexGrow: 1, paddingLeft: 1 }, /* @__PURE__ */ import_react43.default.createElement(Text, { color: PALETTE.error }, "delete"), /* @__PURE__ */ import_react43.default.createElement(Text, { wrap: "truncate" }, /* @__PURE__ */ import_react43.default.createElement(Text, { color: PALETTE.textMuted }, ` ${taskId} `), /* @__PURE__ */ import_react43.default.createElement(Text, { color: PALETTE.text }, truncate(taskTitle, Math.max(1, width - taskId.length - 6)))), /* @__PURE__ */ import_react43.default.createElement(Box_default, { marginTop: 1, paddingLeft: 2 }, /* @__PURE__ */ import_react43.default.createElement(Text, { color: PALETTE.textMuted }, "Deletes the file. This cannot be undone.")), /* @__PURE__ */ import_react43.default.createElement(Box_default, { marginTop: 1, paddingLeft: 2 }, /* @__PURE__ */ import_react43.default.createElement(Text, { color: PALETTE.textMuted }, "y delete \xB7 n cancel")));
23542
+ return /* @__PURE__ */ import_react44.default.createElement(Box_default, { flexDirection: "column", flexGrow: 1, paddingLeft: 1 }, /* @__PURE__ */ import_react44.default.createElement(Text, { color: PALETTE.error }, "delete"), /* @__PURE__ */ import_react44.default.createElement(Text, { wrap: "truncate" }, /* @__PURE__ */ import_react44.default.createElement(Text, { color: PALETTE.textMuted }, ` ${taskId} `), /* @__PURE__ */ import_react44.default.createElement(Text, { color: PALETTE.text }, truncate(taskTitle, Math.max(1, width - taskId.length - 6)))), /* @__PURE__ */ import_react44.default.createElement(Box_default, { marginTop: 1, paddingLeft: 2 }, /* @__PURE__ */ import_react44.default.createElement(Text, { color: PALETTE.textMuted }, "Deletes the file. This cannot be undone.")), /* @__PURE__ */ import_react44.default.createElement(Box_default, { marginTop: 1, paddingLeft: 2 }, /* @__PURE__ */ import_react44.default.createElement(Text, { color: PALETTE.textMuted }, "y delete \xB7 n cancel")));
22955
23543
  }
22956
23544
  function CompleteConfirmOverlay({ target, width }) {
22957
- return /* @__PURE__ */ import_react43.default.createElement(Box_default, { flexDirection: "column", flexGrow: 1, paddingLeft: 1 }, /* @__PURE__ */ import_react43.default.createElement(Text, { color: PALETTE.warning }, "complete"), /* @__PURE__ */ import_react43.default.createElement(Text, { wrap: "truncate" }, /* @__PURE__ */ import_react43.default.createElement(Text, { color: PALETTE.textMuted }, ` ${target.id} `), /* @__PURE__ */ import_react43.default.createElement(Text, { color: PALETTE.text }, truncate(target.title, Math.max(1, width - target.id.length - 6)))), /* @__PURE__ */ import_react43.default.createElement(Box_default, { marginTop: 1, paddingLeft: 2 }, /* @__PURE__ */ import_react43.default.createElement(Text, { color: PALETTE.textMuted }, incompleteChildrenLabel(target.incompleteChildren.length))), /* @__PURE__ */ import_react43.default.createElement(Box_default, { flexDirection: "column", paddingLeft: 4 }, target.incompleteChildren.map((child) => /* @__PURE__ */ import_react43.default.createElement(Text, { key: child.id, wrap: "truncate" }, /* @__PURE__ */ import_react43.default.createElement(Text, { color: PALETTE.textMuted }, `${child.id} `), /* @__PURE__ */ import_react43.default.createElement(Text, { color: PALETTE.textSecondary }, truncate(child.title, Math.max(1, width - child.id.length - 8)))))), /* @__PURE__ */ import_react43.default.createElement(Box_default, { marginTop: 1, paddingLeft: 2 }, /* @__PURE__ */ import_react43.default.createElement(Text, { color: PALETTE.textMuted }, "y complete anyway \xB7 n cancel")));
23545
+ return /* @__PURE__ */ import_react44.default.createElement(Box_default, { flexDirection: "column", flexGrow: 1, paddingLeft: 1 }, /* @__PURE__ */ import_react44.default.createElement(Text, { color: PALETTE.warning }, "complete"), /* @__PURE__ */ import_react44.default.createElement(Text, { wrap: "truncate" }, /* @__PURE__ */ import_react44.default.createElement(Text, { color: PALETTE.textMuted }, ` ${target.id} `), /* @__PURE__ */ import_react44.default.createElement(Text, { color: PALETTE.text }, truncate(target.title, Math.max(1, width - target.id.length - 6)))), /* @__PURE__ */ import_react44.default.createElement(Box_default, { marginTop: 1, paddingLeft: 2 }, /* @__PURE__ */ import_react44.default.createElement(Text, { color: PALETTE.textMuted }, incompleteChildrenLabel(target.incompleteChildren.length))), /* @__PURE__ */ import_react44.default.createElement(Box_default, { flexDirection: "column", paddingLeft: 4 }, target.incompleteChildren.map((child) => /* @__PURE__ */ import_react44.default.createElement(Text, { key: child.id, wrap: "truncate" }, /* @__PURE__ */ import_react44.default.createElement(Text, { color: PALETTE.textMuted }, `${child.id} `), /* @__PURE__ */ import_react44.default.createElement(Text, { color: PALETTE.textSecondary }, truncate(child.title, Math.max(1, width - child.id.length - 8)))))), /* @__PURE__ */ import_react44.default.createElement(Box_default, { marginTop: 1, paddingLeft: 2 }, /* @__PURE__ */ import_react44.default.createElement(Text, { color: PALETTE.textMuted }, "y complete anyway \xB7 n cancel")));
22958
23546
  }
22959
23547
  function AddOverlay({ title, columnName, thenEdit = false }) {
22960
- return /* @__PURE__ */ import_react43.default.createElement(Box_default, { flexDirection: "column", flexGrow: 1, paddingLeft: 1 }, /* @__PURE__ */ import_react43.default.createElement(Text, { color: PALETTE.textMuted }, thenEdit ? `add to ${columnName}, then edit` : `add to ${columnName}`), /* @__PURE__ */ import_react43.default.createElement(Box_default, { marginTop: 1, paddingLeft: 2 }, /* @__PURE__ */ import_react43.default.createElement(Text, { wrap: "truncate" }, /* @__PURE__ */ import_react43.default.createElement(Text, { color: title ? PALETTE.text : PALETTE.textMuted }, title || "title\u2026"), /* @__PURE__ */ import_react43.default.createElement(Text, { color: PALETTE.accent }, GLYPHS.cursor))), /* @__PURE__ */ import_react43.default.createElement(Box_default, { marginTop: 1, paddingLeft: 2 }, /* @__PURE__ */ import_react43.default.createElement(Text, { color: PALETTE.textMuted }, thenEdit ? "\u21B5 create and edit \xB7 esc cancel" : "\u21B5 create \xB7 esc cancel")));
23548
+ return /* @__PURE__ */ import_react44.default.createElement(Box_default, { flexDirection: "column", flexGrow: 1, paddingLeft: 1 }, /* @__PURE__ */ import_react44.default.createElement(Text, { color: PALETTE.textMuted }, thenEdit ? `add to ${columnName}, then edit` : `add to ${columnName}`), /* @__PURE__ */ import_react44.default.createElement(Box_default, { marginTop: 1, paddingLeft: 2 }, /* @__PURE__ */ import_react44.default.createElement(Text, { wrap: "truncate" }, /* @__PURE__ */ import_react44.default.createElement(Text, { color: title ? PALETTE.text : PALETTE.textMuted }, title || "title\u2026"), /* @__PURE__ */ import_react44.default.createElement(Text, { color: PALETTE.accent }, GLYPHS.cursor))), /* @__PURE__ */ import_react44.default.createElement(Box_default, { marginTop: 1, paddingLeft: 2 }, /* @__PURE__ */ import_react44.default.createElement(Text, { color: PALETTE.textMuted }, thenEdit ? "\u21B5 create and edit \xB7 esc cancel" : "\u21B5 create \xB7 esc cancel")));
22961
23549
  }
22962
23550
 
22963
23551
  // src/tui/BrainfileTUI.tsx
@@ -22982,15 +23570,18 @@ var baseInitialState = {
22982
23570
  statusMessage: null,
22983
23571
  lastContentHash: null,
22984
23572
  reloadFlash: false,
22985
- logs: []
23573
+ logs: [],
23574
+ decisions: [],
23575
+ decisionHistory: false,
23576
+ decisionsReturn: null
22986
23577
  };
22987
23578
  function BrainfileTUI({ filePath, width, height }) {
22988
23579
  const { stdout } = use_stdout_default();
22989
- const [dimensions, setDimensions] = (0, import_react44.useState)({
23580
+ const [dimensions, setDimensions] = (0, import_react45.useState)({
22990
23581
  width: width ?? stdout?.columns ?? process.stdout.columns ?? 80,
22991
23582
  height: height ?? stdout?.rows ?? process.stdout.rows ?? 24
22992
23583
  });
22993
- (0, import_react44.useEffect)(() => {
23584
+ (0, import_react45.useEffect)(() => {
22994
23585
  if (width !== void 0 && height !== void 0) return void 0;
22995
23586
  const handleResize = () => {
22996
23587
  setDimensions({
@@ -23007,8 +23598,8 @@ function BrainfileTUI({ filePath, width, height }) {
23007
23598
  const termHeight = height ?? dimensions.height;
23008
23599
  const isTooSmall = termWidth < LAYOUT.MIN_WIDTH || termHeight < LAYOUT.MIN_HEIGHT;
23009
23600
  const layoutMode = termWidth >= LAYOUT.DETAIL_PANE_MIN_WIDTH ? "wide" : "narrow";
23010
- const resumeColumn = (0, import_react44.useRef)(void 0);
23011
- const [state, setState] = (0, import_react44.useState)(() => {
23601
+ const resumeColumn = (0, import_react45.useRef)(void 0);
23602
+ const [state, setState] = (0, import_react45.useState)(() => {
23012
23603
  const persisted = readTuiState(filePath);
23013
23604
  resumeColumn.current = persisted.lastColumn;
23014
23605
  return {
@@ -23021,7 +23612,7 @@ function BrainfileTUI({ filePath, width, height }) {
23021
23612
  const viewportHeight = Math.max(termHeight - HEADER_ROWS - FOOTER_ROWS, 3);
23022
23613
  const { loadBrainfile } = useBrainfileLoader(filePath, state, setState);
23023
23614
  const syncStatus = useSyncStatus(filePath, state.lastUpdated);
23024
- const orderedColumns = (0, import_react44.useMemo)(() => {
23615
+ const orderedColumns = (0, import_react45.useMemo)(() => {
23025
23616
  if (!state.board) return [];
23026
23617
  return [...state.board.columns].sort((a, b) => {
23027
23618
  const orderA = a.order ?? Number.MAX_SAFE_INTEGER;
@@ -23029,79 +23620,137 @@ function BrainfileTUI({ filePath, width, height }) {
23029
23620
  return orderA - orderB;
23030
23621
  });
23031
23622
  }, [state.board]);
23032
- const typeCycleOptions = (0, import_react44.useMemo)(() => getTypeCycleOptions(state.board), [state.board]);
23033
- const activeTypeFilter = typeCycleOptions.includes(state.activeTypeFilter) ? state.activeTypeFilter : "all";
23034
- const typeFilterActive = activeTypeFilter !== "all";
23035
- const doneView = state.doneView;
23036
- const typeFilteredColumns = (0, import_react44.useMemo)(() => {
23623
+ const types = boardTypes(state.board);
23624
+ const typeCycleOptions = (0, import_react45.useMemo)(
23625
+ () => getTypeCycleOptions(state.board, state.decisions),
23626
+ [state.board, state.decisions]
23627
+ );
23628
+ const activeTypeFilter = typeCycleOptions.includes(state.activeTypeFilter) || isDecisionStop(state.activeTypeFilter, types) ? state.activeTypeFilter : "all";
23629
+ const decisionsView = isDecisionStop(activeTypeFilter, types);
23630
+ const decisionStop = typeCycleOptions.find((type) => isDecisionStop(type, types)) ?? "decision";
23631
+ const typeFilterActive = activeTypeFilter !== "all" && !decisionsView;
23632
+ const doneView = state.doneView && !decisionsView;
23633
+ const typeFilteredColumns = (0, import_react45.useMemo)(() => {
23037
23634
  if (!typeFilterActive) return orderedColumns;
23038
23635
  return orderedColumns.map((column) => ({
23039
23636
  ...column,
23040
- tasks: (column.tasks ?? []).filter((task) => getDocType(task) === activeTypeFilter)
23637
+ tasks: (column.tasks ?? []).filter((task) => (0, import_core10.typesMatch)(task.type, activeTypeFilter))
23041
23638
  }));
23042
23639
  }, [orderedColumns, typeFilterActive, doneView, activeTypeFilter]);
23043
- const totalCount = (0, import_react44.useMemo)(
23044
- () => doneView ? typeFilterActive ? state.logs.filter((task) => getDocType(task) === activeTypeFilter).length : state.logs.length : typeFilteredColumns.reduce((sum, column) => sum + (column.tasks?.length ?? 0), 0),
23045
- [typeFilteredColumns, doneView, state.logs]
23046
- );
23640
+ const totalCount = (0, import_react45.useMemo)(() => {
23641
+ if (decisionsView) return state.decisions.length;
23642
+ if (doneView) {
23643
+ return typeFilterActive ? state.logs.filter((task) => (0, import_core10.typesMatch)(task.type, activeTypeFilter)).length : state.logs.length;
23644
+ }
23645
+ return typeFilteredColumns.reduce((sum, column) => sum + (column.tasks?.length ?? 0), 0);
23646
+ }, [typeFilteredColumns, doneView, decisionsView, state.logs, state.decisions]);
23047
23647
  const filterQuery = state.filterQuery.trim();
23048
23648
  const hasFilter = filterQuery.length > 0;
23049
- const filteredColumns = (0, import_react44.useMemo)(() => {
23649
+ const filteredColumns = (0, import_react45.useMemo)(() => {
23050
23650
  if (!hasFilter) return typeFilteredColumns;
23051
23651
  return typeFilteredColumns.map((column) => {
23052
23652
  const docs = (column.tasks ?? []).map((task) => ({ task, body: task.description ?? "" }));
23053
- const matches = (0, import_core6.searchTasksRanked)(docs, filterQuery);
23653
+ const matches = (0, import_core10.searchTasksRanked)(docs, filterQuery);
23054
23654
  return { ...column, tasks: matches.map((match) => match.doc.task) };
23055
23655
  });
23056
23656
  }, [typeFilteredColumns, hasFilter, filterQuery]);
23057
- const filteredLogs = (0, import_react44.useMemo)(() => {
23657
+ const filteredLogs = (0, import_react45.useMemo)(() => {
23058
23658
  if (!doneView) return [];
23059
- const typed = typeFilterActive ? state.logs.filter((task) => getDocType(task) === activeTypeFilter) : state.logs;
23659
+ const typed = typeFilterActive ? state.logs.filter((task) => (0, import_core10.typesMatch)(task.type, activeTypeFilter)) : state.logs;
23060
23660
  if (!hasFilter) return typed;
23061
23661
  const docs = typed.map((task) => ({ task, body: task.description ?? "" }));
23062
- return (0, import_core6.searchTasksRanked)(docs, filterQuery).map((match) => match.doc.task);
23662
+ return (0, import_core10.searchTasksRanked)(docs, filterQuery).map((match) => match.doc.task);
23063
23663
  }, [doneView, state.logs, typeFilterActive, activeTypeFilter, hasFilter, filterQuery]);
23064
- const matchCount = (0, import_react44.useMemo)(
23065
- () => doneView ? filteredLogs.length : filteredColumns.reduce((sum, column) => sum + (column.tasks?.length ?? 0), 0),
23066
- [filteredColumns, doneView, filteredLogs]
23664
+ const filteredDecisions = (0, import_react45.useMemo)(
23665
+ () => decisionsView ? filterDecisions(state.decisions, filterQuery) : [],
23666
+ [decisionsView, state.decisions, filterQuery]
23667
+ );
23668
+ const decisionModel = (0, import_react45.useMemo)(
23669
+ () => buildDecisionView(filteredDecisions, { historyOpen: state.decisionHistory, types }),
23670
+ [filteredDecisions, state.decisionHistory, types]
23671
+ );
23672
+ const matchCount = (0, import_react45.useMemo)(() => {
23673
+ if (decisionsView) return filteredDecisions.length;
23674
+ if (doneView) return filteredLogs.length;
23675
+ return filteredColumns.reduce((sum, column) => sum + (column.tasks?.length ?? 0), 0);
23676
+ }, [filteredColumns, doneView, filteredLogs, decisionsView, filteredDecisions]);
23677
+ const allBoardTasks = (0, import_react45.useMemo)(
23678
+ () => orderedColumns.flatMap((column) => column.tasks ?? []),
23679
+ [orderedColumns]
23680
+ );
23681
+ const graph = (0, import_react45.useMemo)(
23682
+ () => (0, import_core10.buildDocGraph)([...allBoardTasks, ...state.decisions, ...state.logs], { types }),
23683
+ [allBoardTasks, state.decisions, state.logs, types]
23067
23684
  );
23685
+ const decisionChanged = (0, import_react45.useMemo)(() => workOnRetiredDecisions(graph), [graph]);
23068
23686
  const activeColumnIndex = Math.min(
23069
23687
  state.activeColumnIndex,
23070
23688
  Math.max(0, filteredColumns.length - 1)
23071
23689
  );
23072
23690
  const currentColumn = filteredColumns[activeColumnIndex];
23073
- const rows = (0, import_react44.useMemo)(
23074
- () => doneView ? buildFlatRows(filteredLogs) : typeFilterActive ? buildFlatRows(currentColumn?.tasks ?? []) : buildRows(currentColumn?.tasks ?? [], state.collapsedIds),
23075
- [currentColumn, doneView, filteredLogs, typeFilterActive, state.collapsedIds]
23691
+ const outline = (0, import_react45.useMemo)(() => {
23692
+ if (typeFilterActive || doneView || decisionsView) return null;
23693
+ const logs = hasFilter ? (0, import_core10.searchTasksRanked)(state.logs.map((task) => ({ task, body: task.description ?? "" })), filterQuery).map(
23694
+ (match) => match.doc.task
23695
+ ) : state.logs;
23696
+ return buildOutline({ columns: filteredColumns, logs });
23697
+ }, [typeFilterActive, doneView, decisionsView, hasFilter, filterQuery, state.logs, filteredColumns]);
23698
+ const tabCounts = (0, import_react45.useMemo)(
23699
+ () => outline ? filteredColumns.map((column) => outline.get(column.id)?.length ?? 0) : void 0,
23700
+ [outline, filteredColumns]
23076
23701
  );
23702
+ const rows = (0, import_react45.useMemo)(() => {
23703
+ if (decisionsView) return decisionModel.selectable.map((task) => ({ task, depth: 0 }));
23704
+ if (doneView) return buildFlatRows(filteredLogs);
23705
+ const tasks = currentColumn?.tasks ?? [];
23706
+ return markDecisionChanges(
23707
+ outline && currentColumn ? collapseRows(outline.get(currentColumn.id) ?? [], state.collapsedIds) : buildFlatRows(tasks),
23708
+ decisionChanged
23709
+ );
23710
+ }, [currentColumn, doneView, filteredLogs, outline, state.collapsedIds, decisionsView, decisionModel, decisionChanged]);
23077
23711
  const maxRowIndex = Math.max(0, rows.length - 1);
23078
23712
  const selectedIndex = Math.min(state.selectedTaskIndex, maxRowIndex);
23079
23713
  const selectedTask = rows[selectedIndex]?.task;
23080
- const allBoardTasks = (0, import_react44.useMemo)(
23081
- () => orderedColumns.flatMap((column) => column.tasks ?? []),
23082
- [orderedColumns]
23083
- );
23084
- const allDocs = (0, import_react44.useMemo)(
23085
- () => [...allBoardTasks, ...state.logs],
23086
- [allBoardTasks, state.logs]
23714
+ const lastSelection = (0, import_react45.useRef)({
23715
+ board: null,
23716
+ logs: []
23717
+ });
23718
+ (0, import_react45.useEffect)(() => {
23719
+ const was = lastSelection.current;
23720
+ const reloaded = was.board !== null && (was.board !== state.board || was.logs !== state.logs);
23721
+ if (reloaded && was.id && rows[selectedIndex]?.task.id !== was.id) {
23722
+ const found = rows.findIndex((row) => row.task.id === was.id);
23723
+ if (found >= 0) setState((prev) => ({ ...prev, selectedTaskIndex: found }));
23724
+ }
23725
+ lastSelection.current = { board: state.board, logs: state.logs, id: rows[selectedIndex]?.task.id };
23726
+ });
23727
+ const allDocs = (0, import_react45.useMemo)(
23728
+ () => [...allBoardTasks, ...state.decisions, ...state.logs],
23729
+ [allBoardTasks, state.decisions, state.logs]
23087
23730
  );
23088
- const archivedIds = (0, import_react44.useMemo)(() => new Set(state.logs.map((t) => t.id)), [state.logs]);
23731
+ const archivedIds = (0, import_react45.useMemo)(() => new Set(state.logs.map((t) => t.id)), [state.logs]);
23089
23732
  const detailTaskId = state.detailPath[state.detailPath.length - 1];
23090
23733
  const detailTask = state.detailPath.length > 0 ? allDocs.find((t) => t.id === detailTaskId) : void 0;
23091
- const detailChildren = (0, import_react44.useMemo)(
23734
+ const detailChildren = (0, import_react45.useMemo)(
23092
23735
  () => detailTask ? allDocs.filter((t) => t.parentId === detailTask.id) : [],
23093
23736
  [allDocs, detailTask]
23094
23737
  );
23095
23738
  const detailParent = detailTask?.parentId ? allDocs.find((t) => t.id === detailTask.parentId) : void 0;
23096
- const detailColumnLabel = (0, import_react44.useMemo)(() => {
23739
+ const detailRelations = (0, import_react45.useMemo)(
23740
+ () => detailTask ? relationsFor(detailTask, graph) : NO_RELATIONS,
23741
+ [detailTask, graph]
23742
+ );
23743
+ const detailIsDecision = Boolean(detailRelations.decision);
23744
+ const detailColumnLabel = (0, import_react45.useMemo)(() => {
23097
23745
  if (!detailTask) return "";
23746
+ if (detailRelations.decision) return detailRelations.decision.status;
23098
23747
  if (archivedIds.has(detailTask.id)) {
23099
23748
  const at = detailTask.completedAt;
23100
23749
  return at ? `completed ${at.slice(5, 10)}` : "completed";
23101
23750
  }
23102
23751
  return orderedColumns.find((c) => c.tasks?.some((t) => t.id === detailTask.id))?.id ?? "";
23103
- }, [orderedColumns, detailTask, archivedIds]);
23104
- const detailActivity = (0, import_react44.useMemo)(
23752
+ }, [orderedColumns, detailTask, archivedIds, detailRelations]);
23753
+ const detailActivity = (0, import_react45.useMemo)(
23105
23754
  () => detailTask ? getTaskActivity(filePath, detailTask.id) : [],
23106
23755
  [detailTask, filePath, state.lastUpdated]
23107
23756
  );
@@ -23110,15 +23759,22 @@ function BrainfileTUI({ filePath, width, height }) {
23110
23759
  const detailPaneWidth = Math.floor(termWidth * LAYOUT.DETAIL_PANE_FRACTION);
23111
23760
  const detailViewWidth = fullscreenDetailMode ? termWidth : detailPaneWidth;
23112
23761
  const detailViewHeight = fullscreenDetailMode ? termHeight - FOOTER_ROWS : viewportHeight;
23113
- const detailLayout = detailTask ? computeDetailLayout(detailTask, detailViewWidth, detailViewHeight, detailChildren, detailActivity) : null;
23114
- (0, import_react44.useEffect)(() => {
23762
+ const detailLayout = detailTask ? computeDetailLayout(
23763
+ detailTask,
23764
+ detailViewWidth,
23765
+ detailViewHeight,
23766
+ detailChildren,
23767
+ detailActivity,
23768
+ detailRelations
23769
+ ) : null;
23770
+ (0, import_react45.useEffect)(() => {
23115
23771
  const wanted = resumeColumn.current;
23116
23772
  if (!wanted || orderedColumns.length === 0) return;
23117
23773
  resumeColumn.current = void 0;
23118
23774
  const idx = orderedColumns.findIndex((c) => c.id === wanted);
23119
23775
  if (idx > 0) setState((prev) => ({ ...prev, activeColumnIndex: idx }));
23120
23776
  }, [orderedColumns]);
23121
- (0, import_react44.useEffect)(() => {
23777
+ (0, import_react45.useEffect)(() => {
23122
23778
  setState((prev) => ({
23123
23779
  ...prev,
23124
23780
  activeColumnIndex: Math.min(prev.activeColumnIndex, Math.max(0, filteredColumns.length - 1)),
@@ -23142,22 +23798,27 @@ function BrainfileTUI({ filePath, width, height }) {
23142
23798
  detailWidth: detailViewWidth,
23143
23799
  detailHeight: detailViewHeight,
23144
23800
  doneView,
23145
- detailArchived: Boolean(detailTask && archivedIds.has(detailTask.id))
23801
+ detailArchived: Boolean(detailTask && archivedIds.has(detailTask.id)),
23802
+ decisionsView,
23803
+ decisionStop,
23804
+ decisionHistoryCount: decisionModel.historyCount,
23805
+ detailRelations,
23806
+ detailIsDecision
23146
23807
  });
23147
23808
  if (isTooSmall) {
23148
- return /* @__PURE__ */ import_react44.default.createElement(Box_default, { flexDirection: "column", width: termWidth, height: termHeight, paddingLeft: 1 }, /* @__PURE__ */ import_react44.default.createElement(Text, { color: PALETTE.error }, CHROME.tooSmall), /* @__PURE__ */ import_react44.default.createElement(Text, { color: PALETTE.textMuted }, "Minimum ", LAYOUT.MIN_WIDTH, "\xD7", LAYOUT.MIN_HEIGHT, " \xB7 current ", termWidth, "\xD7", termHeight));
23809
+ return /* @__PURE__ */ import_react45.default.createElement(Box_default, { flexDirection: "column", width: termWidth, height: termHeight, paddingLeft: 1 }, /* @__PURE__ */ import_react45.default.createElement(Text, { color: PALETTE.error }, CHROME.tooSmall), /* @__PURE__ */ import_react45.default.createElement(Text, { color: PALETTE.textMuted }, "Minimum ", LAYOUT.MIN_WIDTH, "\xD7", LAYOUT.MIN_HEIGHT, " \xB7 current ", termWidth, "\xD7", termHeight));
23149
23810
  }
23150
23811
  if (state.error) {
23151
- return /* @__PURE__ */ import_react44.default.createElement(Box_default, { flexDirection: "column", paddingLeft: 1 }, /* @__PURE__ */ import_react44.default.createElement(Text, { color: PALETTE.error }, CHROME.errorTitle), /* @__PURE__ */ import_react44.default.createElement(Text, { color: PALETTE.textSecondary }, state.error), /* @__PURE__ */ import_react44.default.createElement(Text, { color: PALETTE.textMuted }, CHROME.errorHint));
23812
+ return /* @__PURE__ */ import_react45.default.createElement(Box_default, { flexDirection: "column", paddingLeft: 1 }, /* @__PURE__ */ import_react45.default.createElement(Text, { color: PALETTE.error }, CHROME.errorTitle), /* @__PURE__ */ import_react45.default.createElement(Text, { color: PALETTE.textSecondary }, state.error), /* @__PURE__ */ import_react45.default.createElement(Text, { color: PALETTE.textMuted }, CHROME.errorHint));
23152
23813
  }
23153
23814
  if (!state.board) {
23154
- return /* @__PURE__ */ import_react44.default.createElement(Box_default, { paddingLeft: 1 }, /* @__PURE__ */ import_react44.default.createElement(Text, { color: PALETTE.textMuted }, CHROME.loading));
23815
+ return /* @__PURE__ */ import_react45.default.createElement(Box_default, { paddingLeft: 1 }, /* @__PURE__ */ import_react45.default.createElement(Text, { color: PALETTE.textMuted }, CHROME.loading));
23155
23816
  }
23156
23817
  if (state.mode === "help") {
23157
- return /* @__PURE__ */ import_react44.default.createElement(HelpOverlay, { termWidth, termHeight });
23818
+ return /* @__PURE__ */ import_react45.default.createElement(HelpOverlay, { termWidth, termHeight });
23158
23819
  }
23159
23820
  const boardTitle = state.board.title || "brainfile";
23160
- const columnLabel = doneView ? "done" : currentColumn?.id ?? "";
23821
+ const columnLabel = decisionsView ? "decisions" : doneView ? "done" : currentColumn?.id ?? "";
23161
23822
  const columnName = currentColumn?.title ?? "";
23162
23823
  const fullscreenDetail = detailOpen && layoutMode === "narrow";
23163
23824
  const detailArchived = Boolean(detailTask && archivedIds.has(detailTask.id));
@@ -23167,10 +23828,12 @@ function BrainfileTUI({ filePath, width, height }) {
23167
23828
  hasChildren: detailChildren.length > 0,
23168
23829
  hasSubtasks: (detailTask?.subtasks?.length ?? 0) > 0,
23169
23830
  bodyOverflows: detailLayout?.bodyOverflows ?? false,
23170
- archived: detailArchived
23831
+ archived: detailArchived,
23832
+ decision: detailIsDecision,
23833
+ hasLinks: detailRelations.refs.length + detailRelations.resting.length > 0
23171
23834
  };
23172
23835
  if (fullscreenDetail && detailTask) {
23173
- return /* @__PURE__ */ import_react44.default.createElement(Box_default, { flexDirection: "column", width: termWidth, height: termHeight }, /* @__PURE__ */ import_react44.default.createElement(
23836
+ return /* @__PURE__ */ import_react45.default.createElement(Box_default, { flexDirection: "column", width: termWidth, height: termHeight }, /* @__PURE__ */ import_react45.default.createElement(
23174
23837
  DetailView,
23175
23838
  {
23176
23839
  task: detailTask,
@@ -23182,9 +23845,10 @@ function BrainfileTUI({ filePath, width, height }) {
23182
23845
  children: detailChildren,
23183
23846
  activity: detailActivity,
23184
23847
  cursor: state.detailCursor,
23185
- scrollOffset: state.detailScroll
23848
+ scrollOffset: state.detailScroll,
23849
+ relations: detailRelations
23186
23850
  }
23187
- ), /* @__PURE__ */ import_react44.default.createElement(Box_default, { flexGrow: 1 }), /* @__PURE__ */ import_react44.default.createElement(Box_default, { height: 1, flexShrink: 0 }, /* @__PURE__ */ import_react44.default.createElement(StatusMessageDisplay, { message: state.statusMessage })), /* @__PURE__ */ import_react44.default.createElement(
23851
+ ), /* @__PURE__ */ import_react45.default.createElement(Box_default, { flexGrow: 1 }), /* @__PURE__ */ import_react45.default.createElement(Box_default, { height: 1, flexShrink: 0 }, /* @__PURE__ */ import_react45.default.createElement(StatusMessageDisplay, { message: state.statusMessage })), /* @__PURE__ */ import_react45.default.createElement(
23188
23852
  FooterBar,
23189
23853
  {
23190
23854
  width: termWidth,
@@ -23194,23 +23858,24 @@ function BrainfileTUI({ filePath, width, height }) {
23194
23858
  ));
23195
23859
  }
23196
23860
  const listWidth = detailOpen ? termWidth - detailPaneWidth : termWidth;
23197
- const footerActions = detailOpen ? detailActions(detailFooterCtx) : browseActions(selectedTask, doneView);
23198
- return /* @__PURE__ */ import_react44.default.createElement(Box_default, { flexDirection: "column", width: termWidth, height: termHeight }, /* @__PURE__ */ import_react44.default.createElement(
23861
+ const footerActions = detailOpen ? detailActions(detailFooterCtx) : decisionsView ? decisionsActions({ historyCount: decisionModel.historyCount, historyOpen: state.decisionHistory }) : browseActions(selectedTask, doneView, state.decisions.length > 0, Boolean(rows[selectedIndex]?.archived));
23862
+ return /* @__PURE__ */ import_react45.default.createElement(Box_default, { flexDirection: "column", width: termWidth, height: termHeight }, /* @__PURE__ */ import_react45.default.createElement(
23199
23863
  HeaderBar,
23200
23864
  {
23201
23865
  title: boardTitle,
23202
23866
  columns: filteredColumns,
23867
+ counts: tabCounts,
23203
23868
  activeColumnIndex,
23204
23869
  width: termWidth,
23205
23870
  filterQuery: state.filterQuery,
23206
23871
  filterActive: state.mode === "filter" || hasFilter,
23207
23872
  matchCount,
23208
23873
  totalCount,
23209
- panelLabel: doneView ? "done" : void 0,
23874
+ panelLabel: decisionsView ? "decisions" : doneView ? "done" : void 0,
23210
23875
  activeType: typeFilterActive ? activeTypeFilter : "all",
23211
23876
  statusLabel: syncStatus
23212
23877
  }
23213
- ), /* @__PURE__ */ import_react44.default.createElement(Box_default, { flexGrow: 1, flexShrink: 0, flexDirection: "column" }, /* @__PURE__ */ import_react44.default.createElement(
23878
+ ), /* @__PURE__ */ import_react45.default.createElement(Box_default, { flexGrow: 1, flexShrink: 0, flexDirection: "column" }, /* @__PURE__ */ import_react45.default.createElement(
23214
23879
  BoardContent,
23215
23880
  {
23216
23881
  state,
@@ -23232,9 +23897,11 @@ function BrainfileTUI({ filePath, width, height }) {
23232
23897
  hasFilter,
23233
23898
  archived: doneView,
23234
23899
  typeFilter: typeFilterActive ? activeTypeFilter : void 0,
23235
- thenEdit: state.addThenEdit
23900
+ thenEdit: state.addThenEdit,
23901
+ decisionLines: decisionsView ? decisionModel.lines : void 0,
23902
+ detailRelations
23236
23903
  }
23237
- )), /* @__PURE__ */ import_react44.default.createElement(Box_default, { height: 1, flexShrink: 0 }, /* @__PURE__ */ import_react44.default.createElement(StatusMessageDisplay, { message: state.statusMessage })), /* @__PURE__ */ import_react44.default.createElement(
23904
+ )), /* @__PURE__ */ import_react45.default.createElement(Box_default, { height: 1, flexShrink: 0 }, /* @__PURE__ */ import_react45.default.createElement(StatusMessageDisplay, { message: state.statusMessage })), /* @__PURE__ */ import_react45.default.createElement(
23238
23905
  FooterBar,
23239
23906
  {
23240
23907
  width: termWidth,
@@ -23264,11 +23931,13 @@ function BoardContent({
23264
23931
  hasFilter,
23265
23932
  archived,
23266
23933
  typeFilter,
23267
- thenEdit
23934
+ thenEdit,
23935
+ decisionLines,
23936
+ detailRelations
23268
23937
  }) {
23269
23938
  const overlayTarget = state.detailPath.length > 0 ? detailTask : selectedTask;
23270
23939
  if (state.mode === "move" && overlayTarget) {
23271
- return /* @__PURE__ */ import_react44.default.createElement(
23940
+ return /* @__PURE__ */ import_react45.default.createElement(
23272
23941
  MoveOverlay,
23273
23942
  {
23274
23943
  columns,
@@ -23280,7 +23949,7 @@ function BoardContent({
23280
23949
  );
23281
23950
  }
23282
23951
  if (state.mode === "delete-confirm" && overlayTarget) {
23283
- return /* @__PURE__ */ import_react44.default.createElement(
23952
+ return /* @__PURE__ */ import_react45.default.createElement(
23284
23953
  DeleteConfirmOverlay,
23285
23954
  {
23286
23955
  taskId: overlayTarget.id,
@@ -23290,12 +23959,21 @@ function BoardContent({
23290
23959
  );
23291
23960
  }
23292
23961
  if (state.mode === "complete-confirm" && state.completeConfirm) {
23293
- return /* @__PURE__ */ import_react44.default.createElement(CompleteConfirmOverlay, { target: state.completeConfirm, width: termWidth });
23962
+ return /* @__PURE__ */ import_react45.default.createElement(CompleteConfirmOverlay, { target: state.completeConfirm, width: termWidth });
23294
23963
  }
23295
23964
  if (state.mode === "add") {
23296
- return /* @__PURE__ */ import_react44.default.createElement(AddOverlay, { title: state.newTaskTitle, columnName, thenEdit });
23965
+ return /* @__PURE__ */ import_react45.default.createElement(AddOverlay, { title: state.newTaskTitle, columnName, thenEdit });
23297
23966
  }
23298
- const list = /* @__PURE__ */ import_react44.default.createElement(
23967
+ const list = decisionLines ? /* @__PURE__ */ import_react45.default.createElement(
23968
+ DecisionList,
23969
+ {
23970
+ lines: decisionLines,
23971
+ selectedIndex,
23972
+ viewportHeight,
23973
+ width: listWidth,
23974
+ emptyMessage: hasFilter ? EMPTY.noMatches : EMPTY.noDecisions
23975
+ }
23976
+ ) : /* @__PURE__ */ import_react45.default.createElement(
23299
23977
  DocumentList,
23300
23978
  {
23301
23979
  rows,
@@ -23314,7 +23992,7 @@ function BoardContent({
23314
23992
  // shrink to fit corrupts the layout — rows silently lose height and their
23315
23993
  // text vanishes — rather than the frame simply growing taller, which ink
23316
23994
  // and real terminals handle fine.
23317
- /* @__PURE__ */ import_react44.default.createElement(Box_default, { flexDirection: "row", flexGrow: 1, flexShrink: 0 }, /* @__PURE__ */ import_react44.default.createElement(Box_default, { width: listWidth, flexDirection: "column", flexShrink: 0 }, list), /* @__PURE__ */ import_react44.default.createElement(Box_default, { width: detailPaneWidth, flexDirection: "column", flexShrink: 0 }, /* @__PURE__ */ import_react44.default.createElement(
23995
+ /* @__PURE__ */ import_react45.default.createElement(Box_default, { flexDirection: "row", flexGrow: 1, flexShrink: 0 }, /* @__PURE__ */ import_react45.default.createElement(Box_default, { width: listWidth, flexDirection: "column", flexShrink: 0 }, list), /* @__PURE__ */ import_react45.default.createElement(Box_default, { width: detailPaneWidth, flexDirection: "column", flexShrink: 0 }, /* @__PURE__ */ import_react45.default.createElement(
23318
23996
  DetailView,
23319
23997
  {
23320
23998
  task: detailTask,
@@ -23326,7 +24004,8 @@ function BoardContent({
23326
24004
  children: detailChildren,
23327
24005
  activity: detailActivity,
23328
24006
  cursor: state.detailCursor,
23329
- scrollOffset: state.detailScroll
24007
+ scrollOffset: state.detailScroll,
24008
+ relations: detailRelations
23330
24009
  }
23331
24010
  )))
23332
24011
  );
@@ -23354,9 +24033,9 @@ async function tuiCommand(options) {
23354
24033
  markV2MigrationHintShown(filePath);
23355
24034
  }
23356
24035
  installAutosync();
23357
- const fetched = fetchBeforeRead(path5.dirname(filePath));
23358
- if (fetched && !fetched.ok) console.error(import_chalk4.default.yellow(syncMessage(fetched).text));
23359
- const { waitUntilExit } = render_default(/* @__PURE__ */ import_react45.default.createElement(BrainfileTUI, { filePath }));
24036
+ const fetched = (0, import_repo3.fetchBeforeRead)(path5.dirname(filePath));
24037
+ if (fetched && !fetched.ok) console.error(import_chalk4.default.yellow((0, import_repo2.syncMessage)(fetched).text));
24038
+ const { waitUntilExit } = render_default(/* @__PURE__ */ import_react46.default.createElement(BrainfileTUI, { filePath }));
23360
24039
  await waitUntilExit();
23361
24040
  process.stdout.write("\x1B[2J");
23362
24041
  process.stdout.write("\x1B[H");