@compilr-dev/cli 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (315) hide show
  1. package/README.md +30 -12
  2. package/dist/agent.d.ts +74 -1
  3. package/dist/agent.js +259 -76
  4. package/dist/anchors/index.d.ts +9 -0
  5. package/dist/anchors/index.js +9 -0
  6. package/dist/anchors/project-anchors.d.ts +79 -0
  7. package/dist/anchors/project-anchors.js +202 -0
  8. package/dist/commands/handler-types.d.ts +68 -0
  9. package/dist/commands/handler-types.js +8 -0
  10. package/dist/commands/handlers/agent-commands.d.ts +13 -0
  11. package/dist/commands/handlers/agent-commands.js +305 -0
  12. package/dist/commands/handlers/design-commands.d.ts +15 -0
  13. package/dist/commands/handlers/design-commands.js +334 -0
  14. package/dist/commands/handlers/index.d.ts +20 -0
  15. package/dist/commands/handlers/index.js +43 -0
  16. package/dist/commands/handlers/overlay-commands.d.ts +21 -0
  17. package/dist/commands/handlers/overlay-commands.js +287 -0
  18. package/dist/commands/handlers/project-commands.d.ts +11 -0
  19. package/dist/commands/handlers/project-commands.js +167 -0
  20. package/dist/commands/handlers/simple-commands.d.ts +19 -0
  21. package/dist/commands/handlers/simple-commands.js +144 -0
  22. package/dist/commands/index.d.ts +2 -1
  23. package/dist/commands/registry.d.ts +50 -0
  24. package/dist/commands/registry.js +75 -0
  25. package/dist/commands-v2/handlers/context.d.ts +13 -0
  26. package/dist/commands-v2/handlers/context.js +348 -0
  27. package/dist/commands-v2/handlers/core.d.ts +13 -0
  28. package/dist/commands-v2/handlers/core.js +165 -0
  29. package/dist/commands-v2/handlers/debug.d.ts +11 -0
  30. package/dist/commands-v2/handlers/debug.js +159 -0
  31. package/dist/commands-v2/handlers/index.d.ts +12 -0
  32. package/dist/commands-v2/handlers/index.js +24 -0
  33. package/dist/commands-v2/handlers/project.d.ts +22 -0
  34. package/dist/commands-v2/handlers/project.js +814 -0
  35. package/dist/commands-v2/handlers/settings.d.ts +15 -0
  36. package/dist/commands-v2/handlers/settings.js +235 -0
  37. package/dist/commands-v2/index.d.ts +13 -0
  38. package/dist/commands-v2/index.js +15 -0
  39. package/dist/commands-v2/registry.d.ts +37 -0
  40. package/dist/commands-v2/registry.js +80 -0
  41. package/dist/commands-v2/types.d.ts +75 -0
  42. package/dist/commands-v2/types.js +7 -0
  43. package/dist/commands.js +110 -7
  44. package/dist/index.js +288 -29
  45. package/dist/input-handlers/index.d.ts +7 -0
  46. package/dist/input-handlers/index.js +7 -0
  47. package/dist/input-handlers/memory-handler.d.ts +26 -0
  48. package/dist/input-handlers/memory-handler.js +68 -0
  49. package/dist/repl-helpers.d.ts +63 -0
  50. package/dist/repl-helpers.js +318 -0
  51. package/dist/repl-v2.d.ts +155 -0
  52. package/dist/repl-v2.js +774 -0
  53. package/dist/repl.d.ts +32 -4
  54. package/dist/repl.js +250 -977
  55. package/dist/settings/index.d.ts +23 -0
  56. package/dist/settings/index.js +48 -0
  57. package/dist/settings/paths.d.ts +110 -0
  58. package/dist/settings/paths.js +264 -0
  59. package/dist/templates/compilr-md.js +7 -4
  60. package/dist/templates/index.js +3 -4
  61. package/dist/themes/colors.js +3 -1
  62. package/dist/themes/registry.d.ts +5 -36
  63. package/dist/themes/registry.js +11 -95
  64. package/dist/themes/types.d.ts +3 -38
  65. package/dist/themes/types.js +2 -2
  66. package/dist/tools/anchor-tools.d.ts +31 -0
  67. package/dist/tools/anchor-tools.js +255 -0
  68. package/dist/tools/backlog-wrappers.d.ts +54 -0
  69. package/dist/tools/backlog-wrappers.js +338 -0
  70. package/dist/tools/backlog.js +1 -1
  71. package/dist/tools/db-tools.d.ts +65 -0
  72. package/dist/tools/db-tools.js +19 -0
  73. package/dist/tools/document-db.d.ts +43 -0
  74. package/dist/tools/document-db.js +220 -0
  75. package/dist/tools/project-db.d.ts +102 -0
  76. package/dist/tools/project-db.js +370 -0
  77. package/dist/tools/workitem-db.d.ts +103 -0
  78. package/dist/tools/workitem-db.js +549 -0
  79. package/dist/tools.js +13 -3
  80. package/dist/ui/agents-overlay-v2.d.ts +43 -0
  81. package/dist/ui/agents-overlay-v2.js +809 -0
  82. package/dist/ui/agents-overlay.d.ts +5 -5
  83. package/dist/ui/agents-overlay.js +782 -420
  84. package/dist/ui/anchors-overlay.d.ts +12 -0
  85. package/dist/ui/anchors-overlay.js +775 -0
  86. package/dist/ui/arch-type-overlay.d.ts +1 -6
  87. package/dist/ui/arch-type-overlay.js +175 -203
  88. package/dist/ui/ask-user-overlay-v2.d.ts +26 -0
  89. package/dist/ui/ask-user-overlay-v2.js +555 -0
  90. package/dist/ui/ask-user-overlay.d.ts +2 -2
  91. package/dist/ui/ask-user-overlay.js +443 -535
  92. package/dist/ui/ask-user-simple-overlay-v2.d.ts +25 -0
  93. package/dist/ui/ask-user-simple-overlay-v2.js +215 -0
  94. package/dist/ui/ask-user-simple-overlay.d.ts +2 -2
  95. package/dist/ui/ask-user-simple-overlay.js +182 -209
  96. package/dist/ui/backlog-overlay.d.ts +16 -1
  97. package/dist/ui/backlog-overlay.js +525 -659
  98. package/dist/ui/base/index.d.ts +26 -0
  99. package/dist/ui/base/index.js +33 -0
  100. package/dist/ui/base/inline-overlay-utils.d.ts +217 -0
  101. package/dist/ui/base/inline-overlay-utils.js +320 -0
  102. package/dist/ui/base/inline-overlay.d.ts +159 -0
  103. package/dist/ui/base/inline-overlay.js +257 -0
  104. package/dist/ui/base/key-utils.d.ts +15 -0
  105. package/dist/ui/base/key-utils.js +30 -0
  106. package/dist/ui/base/overlay-base-v2.d.ts +193 -0
  107. package/dist/ui/base/overlay-base-v2.js +246 -0
  108. package/dist/ui/base/overlay-base.d.ts +156 -0
  109. package/dist/ui/base/overlay-base.js +238 -0
  110. package/dist/ui/base/overlay-lifecycle.d.ts +65 -0
  111. package/dist/ui/base/overlay-lifecycle.js +159 -0
  112. package/dist/ui/base/overlay-types.d.ts +185 -0
  113. package/dist/ui/base/overlay-types.js +7 -0
  114. package/dist/ui/base/render-utils.d.ts +8 -0
  115. package/dist/ui/base/render-utils.js +11 -0
  116. package/dist/ui/base/screen-stack.d.ts +148 -0
  117. package/dist/ui/base/screen-stack.js +184 -0
  118. package/dist/ui/base/tabbed-list-overlay-v2.d.ts +103 -0
  119. package/dist/ui/base/tabbed-list-overlay-v2.js +317 -0
  120. package/dist/ui/base/tabbed-list-overlay.d.ts +153 -0
  121. package/dist/ui/base/tabbed-list-overlay.js +369 -0
  122. package/dist/ui/commands-overlay-v2.d.ts +33 -0
  123. package/dist/ui/commands-overlay-v2.js +441 -0
  124. package/dist/ui/commands-overlay.d.ts +7 -2
  125. package/dist/ui/commands-overlay.js +384 -355
  126. package/dist/ui/config-overlay.d.ts +5 -4
  127. package/dist/ui/config-overlay.js +243 -513
  128. package/dist/ui/conversation.d.ts +75 -4
  129. package/dist/ui/conversation.js +374 -161
  130. package/dist/ui/docs-overlay.d.ts +17 -0
  131. package/dist/ui/docs-overlay.js +303 -0
  132. package/dist/ui/ephemeral.d.ts +1 -1
  133. package/dist/ui/ephemeral.js +1 -1
  134. package/dist/ui/features/index.d.ts +34 -0
  135. package/dist/ui/features/index.js +34 -0
  136. package/dist/ui/features/input-feature.d.ts +85 -0
  137. package/dist/ui/features/input-feature.js +238 -0
  138. package/dist/ui/features/list-feature.d.ts +155 -0
  139. package/dist/ui/features/list-feature.js +244 -0
  140. package/dist/ui/features/pagination-feature.d.ts +154 -0
  141. package/dist/ui/features/pagination-feature.js +238 -0
  142. package/dist/ui/features/search-feature.d.ts +148 -0
  143. package/dist/ui/features/search-feature.js +185 -0
  144. package/dist/ui/features/tab-feature.d.ts +194 -0
  145. package/dist/ui/features/tab-feature.js +307 -0
  146. package/dist/ui/footer-v2.d.ts +222 -0
  147. package/dist/ui/footer-v2.js +1349 -0
  148. package/dist/ui/footer.d.ts +107 -0
  149. package/dist/ui/footer.js +359 -67
  150. package/dist/ui/guardrail-overlay.d.ts +29 -0
  151. package/dist/ui/guardrail-overlay.js +145 -0
  152. package/dist/ui/help-overlay-v2.d.ts +34 -0
  153. package/dist/ui/help-overlay-v2.js +309 -0
  154. package/dist/ui/help-overlay.d.ts +16 -0
  155. package/dist/ui/help-overlay.js +316 -0
  156. package/dist/ui/index.d.ts +1 -1
  157. package/dist/ui/index.js +1 -3
  158. package/dist/ui/init-overlay-v2.d.ts +34 -0
  159. package/dist/ui/init-overlay-v2.js +600 -0
  160. package/dist/ui/init-overlay.d.ts +12 -2
  161. package/dist/ui/init-overlay.js +349 -270
  162. package/dist/ui/input-prompt-v2.d.ts +1 -0
  163. package/dist/ui/input-prompt-v2.js +14 -6
  164. package/dist/ui/input-prompt.d.ts +116 -33
  165. package/dist/ui/input-prompt.js +536 -337
  166. package/dist/ui/iteration-limit-overlay-v2.d.ts +21 -0
  167. package/dist/ui/iteration-limit-overlay-v2.js +114 -0
  168. package/dist/ui/iteration-limit-overlay.d.ts +2 -2
  169. package/dist/ui/iteration-limit-overlay.js +92 -128
  170. package/dist/ui/keys-overlay-v2.d.ts +41 -0
  171. package/dist/ui/keys-overlay-v2.js +248 -0
  172. package/dist/ui/keys-overlay.d.ts +1 -0
  173. package/dist/ui/keys-overlay.js +203 -141
  174. package/dist/ui/line-utils.d.ts +88 -0
  175. package/dist/ui/line-utils.js +150 -0
  176. package/dist/ui/live-region.d.ts +161 -0
  177. package/dist/ui/live-region.js +387 -0
  178. package/dist/ui/mascot/expressions.d.ts +32 -0
  179. package/dist/ui/mascot/expressions.js +213 -0
  180. package/dist/ui/mascot/index.d.ts +8 -0
  181. package/dist/ui/mascot/index.js +8 -0
  182. package/dist/ui/mascot/renderer.d.ts +19 -0
  183. package/dist/ui/mascot/renderer.js +97 -0
  184. package/dist/ui/mascot-overlay-v2.d.ts +41 -0
  185. package/dist/ui/mascot-overlay-v2.js +138 -0
  186. package/dist/ui/mascot-overlay.d.ts +21 -0
  187. package/dist/ui/mascot-overlay.js +146 -0
  188. package/dist/ui/model-overlay-v2.d.ts +49 -0
  189. package/dist/ui/model-overlay-v2.js +118 -0
  190. package/dist/ui/model-overlay.d.ts +27 -0
  191. package/dist/ui/model-overlay.js +221 -0
  192. package/dist/ui/model-warning-overlay.js +3 -5
  193. package/dist/ui/new-overlay.d.ts +34 -0
  194. package/dist/ui/new-overlay.js +604 -0
  195. package/dist/ui/overlay/impl/agents-overlay-v2.d.ts +45 -0
  196. package/dist/ui/overlay/impl/agents-overlay-v2.js +825 -0
  197. package/dist/ui/overlay/impl/anchors-overlay-v2.d.ts +47 -0
  198. package/dist/ui/overlay/impl/anchors-overlay-v2.js +783 -0
  199. package/dist/ui/overlay/impl/arch-type-overlay-v2.d.ts +37 -0
  200. package/dist/ui/overlay/impl/arch-type-overlay-v2.js +240 -0
  201. package/dist/ui/overlay/impl/ask-user-overlay-v2.d.ts +72 -0
  202. package/dist/ui/overlay/impl/ask-user-overlay-v2.js +584 -0
  203. package/dist/ui/overlay/impl/ask-user-simple-overlay-v2.d.ts +46 -0
  204. package/dist/ui/overlay/impl/ask-user-simple-overlay-v2.js +204 -0
  205. package/dist/ui/overlay/impl/backlog-overlay-v2.d.ts +49 -0
  206. package/dist/ui/overlay/impl/backlog-overlay-v2.js +642 -0
  207. package/dist/ui/overlay/impl/commands-overlay-v2.d.ts +33 -0
  208. package/dist/ui/overlay/impl/commands-overlay-v2.js +441 -0
  209. package/dist/ui/overlay/impl/config-overlay-v2.d.ts +100 -0
  210. package/dist/ui/overlay/impl/config-overlay-v2.js +654 -0
  211. package/dist/ui/overlay/impl/dashboard-overlay-v2.d.ts +55 -0
  212. package/dist/ui/overlay/impl/dashboard-overlay-v2.js +359 -0
  213. package/dist/ui/overlay/impl/docs-overlay-v2.d.ts +45 -0
  214. package/dist/ui/overlay/impl/docs-overlay-v2.js +114 -0
  215. package/dist/ui/overlay/impl/document-detail-overlay-v2.d.ts +77 -0
  216. package/dist/ui/overlay/impl/document-detail-overlay-v2.js +1071 -0
  217. package/dist/ui/overlay/impl/guardrail-overlay-v2.d.ts +43 -0
  218. package/dist/ui/overlay/impl/guardrail-overlay-v2.js +114 -0
  219. package/dist/ui/overlay/impl/help-overlay-v2.d.ts +34 -0
  220. package/dist/ui/overlay/impl/help-overlay-v2.js +309 -0
  221. package/dist/ui/overlay/impl/init-overlay-v2.d.ts +77 -0
  222. package/dist/ui/overlay/impl/init-overlay-v2.js +593 -0
  223. package/dist/ui/overlay/impl/init-setup-overlay-v2.d.ts +25 -0
  224. package/dist/ui/overlay/impl/init-setup-overlay-v2.js +97 -0
  225. package/dist/ui/overlay/impl/iteration-limit-overlay-v2.d.ts +35 -0
  226. package/dist/ui/overlay/impl/iteration-limit-overlay-v2.js +105 -0
  227. package/dist/ui/overlay/impl/keys-overlay-v2.d.ts +41 -0
  228. package/dist/ui/overlay/impl/keys-overlay-v2.js +248 -0
  229. package/dist/ui/overlay/impl/mascot-overlay-v2.d.ts +41 -0
  230. package/dist/ui/overlay/impl/mascot-overlay-v2.js +138 -0
  231. package/dist/ui/overlay/impl/model-overlay-v2.d.ts +49 -0
  232. package/dist/ui/overlay/impl/model-overlay-v2.js +118 -0
  233. package/dist/ui/overlay/impl/model-warning-overlay-v2.d.ts +46 -0
  234. package/dist/ui/overlay/impl/model-warning-overlay-v2.js +132 -0
  235. package/dist/ui/overlay/impl/new-overlay-v2.d.ts +77 -0
  236. package/dist/ui/overlay/impl/new-overlay-v2.js +593 -0
  237. package/dist/ui/overlay/impl/permission-overlay-v2.d.ts +36 -0
  238. package/dist/ui/overlay/impl/permission-overlay-v2.js +380 -0
  239. package/dist/ui/overlay/impl/projects-overlay-v2.d.ts +36 -0
  240. package/dist/ui/overlay/impl/projects-overlay-v2.js +499 -0
  241. package/dist/ui/overlay/impl/theme-overlay-v2.d.ts +42 -0
  242. package/dist/ui/overlay/impl/theme-overlay-v2.js +135 -0
  243. package/dist/ui/overlay/impl/tools-overlay-v2.d.ts +47 -0
  244. package/dist/ui/overlay/impl/tools-overlay-v2.js +218 -0
  245. package/dist/ui/overlay/impl/tutorial-overlay-v2.d.ts +31 -0
  246. package/dist/ui/overlay/impl/tutorial-overlay-v2.js +1035 -0
  247. package/dist/ui/overlay/impl/workflow-overlay-v2.d.ts +80 -0
  248. package/dist/ui/overlay/impl/workflow-overlay-v2.js +637 -0
  249. package/dist/ui/overlay/index.d.ts +33 -0
  250. package/dist/ui/overlay/index.js +35 -0
  251. package/dist/ui/overlay/key-utils.d.ts +6 -0
  252. package/dist/ui/overlay/key-utils.js +6 -0
  253. package/dist/ui/overlay/overlay-types.d.ts +128 -0
  254. package/dist/ui/overlay/overlay-types.js +22 -0
  255. package/dist/ui/overlay/types.d.ts +135 -0
  256. package/dist/ui/overlay/types.js +22 -0
  257. package/dist/ui/overlays/help-overlay-v2.d.ts +28 -0
  258. package/dist/ui/overlays/help-overlay-v2.js +198 -0
  259. package/dist/ui/overlays/index.d.ts +11 -0
  260. package/dist/ui/overlays/index.js +11 -0
  261. package/dist/ui/overlays.d.ts +0 -4
  262. package/dist/ui/overlays.js +0 -444
  263. package/dist/ui/permission-overlay-v2.d.ts +36 -0
  264. package/dist/ui/permission-overlay-v2.js +380 -0
  265. package/dist/ui/permission-overlay.d.ts +1 -1
  266. package/dist/ui/permission-overlay.js +186 -298
  267. package/dist/ui/projects-overlay.d.ts +19 -0
  268. package/dist/ui/projects-overlay.js +484 -0
  269. package/dist/ui/providers/types.d.ts +178 -0
  270. package/dist/ui/providers/types.js +9 -0
  271. package/dist/ui/render-modes.d.ts +36 -0
  272. package/dist/ui/render-modes.js +44 -0
  273. package/dist/ui/startup-menu.d.ts +36 -0
  274. package/dist/ui/startup-menu.js +236 -0
  275. package/dist/ui/subagent-renderer.d.ts +117 -0
  276. package/dist/ui/subagent-renderer.js +334 -0
  277. package/dist/ui/terminal-codes.d.ts +94 -0
  278. package/dist/ui/terminal-codes.js +124 -0
  279. package/dist/ui/terminal-renderer.d.ts +221 -0
  280. package/dist/ui/terminal-renderer.js +751 -0
  281. package/dist/ui/terminal-ui.d.ts +463 -0
  282. package/dist/ui/terminal-ui.js +2296 -0
  283. package/dist/ui/terminal.d.ts +20 -0
  284. package/dist/ui/terminal.js +72 -0
  285. package/dist/ui/theme-overlay-v2.d.ts +42 -0
  286. package/dist/ui/theme-overlay-v2.js +135 -0
  287. package/dist/ui/theme-overlay.d.ts +24 -0
  288. package/dist/ui/theme-overlay.js +127 -0
  289. package/dist/ui/todo-zone.js +53 -25
  290. package/dist/ui/tool-formatters.d.ts +16 -0
  291. package/dist/ui/tool-formatters.js +516 -0
  292. package/dist/ui/tools-overlay-v2.d.ts +47 -0
  293. package/dist/ui/tools-overlay-v2.js +218 -0
  294. package/dist/ui/tools-overlay.d.ts +10 -2
  295. package/dist/ui/tools-overlay.js +172 -220
  296. package/dist/ui/tutorial-overlay-v2.d.ts +31 -0
  297. package/dist/ui/tutorial-overlay-v2.js +1035 -0
  298. package/dist/ui/tutorial-overlay.d.ts +1 -0
  299. package/dist/ui/tutorial-overlay.js +400 -302
  300. package/dist/ui/workflow-overlay.d.ts +22 -0
  301. package/dist/ui/workflow-overlay.js +636 -0
  302. package/dist/utils/debug-log.d.ts +28 -0
  303. package/dist/utils/debug-log.js +57 -0
  304. package/dist/utils/model-tiers.js +1 -1
  305. package/dist/utils/path-safety.d.ts +56 -0
  306. package/dist/utils/path-safety.js +239 -0
  307. package/dist/workflow/guided-mode-injector.d.ts +42 -0
  308. package/dist/workflow/guided-mode-injector.js +191 -0
  309. package/dist/workflow/index.d.ts +8 -0
  310. package/dist/workflow/index.js +8 -0
  311. package/dist/workflow/step-criteria.d.ts +62 -0
  312. package/dist/workflow/step-criteria.js +150 -0
  313. package/dist/workflow/step-tracker.d.ts +92 -0
  314. package/dist/workflow/step-tracker.js +141 -0
  315. package/package.json +12 -5
@@ -0,0 +1,783 @@
1
+ /**
2
+ * Anchors Overlay V2
3
+ *
4
+ * Provides a UI for viewing and managing anchors:
5
+ * - View all anchors (global + project-specific)
6
+ * - Add new anchors
7
+ * - Edit existing anchors
8
+ * - Delete anchors
9
+ *
10
+ * Uses TabbedListOverlayV2 for consistent tabbed list behavior.
11
+ */
12
+ import { TabbedListOverlayV2 } from '../../base/tabbed-list-overlay-v2.js';
13
+ import { BaseScreen, stay, popScreen, closeOverlay, renderBorder, } from '../../base/index.js';
14
+ import { getGlobalAnchorManager, getAnchorManager, } from '../../../anchors/index.js';
15
+ import { getActiveProject } from '../../../tools/project-db.js';
16
+ import * as terminal from '../../terminal.js';
17
+ // =============================================================================
18
+ // Constants
19
+ // =============================================================================
20
+ const PAGE_SIZE = 10;
21
+ const MAX_ANCHOR_LENGTH = 500;
22
+ const PRIORITIES = ['critical', 'safety', 'info'];
23
+ const TABS = [
24
+ { id: 'all', label: 'All' },
25
+ { id: 'critical', label: 'Critical' },
26
+ { id: 'safety', label: 'Safety' },
27
+ { id: 'info', label: 'Info' },
28
+ { id: 'global', label: 'Global' },
29
+ { id: 'project', label: 'Project' },
30
+ ];
31
+ // =============================================================================
32
+ // Helper Functions
33
+ // =============================================================================
34
+ function getPriorityBadge(priority, s) {
35
+ switch (priority) {
36
+ case 'critical':
37
+ return s.error('[!]');
38
+ case 'safety':
39
+ return s.warning('[S]');
40
+ case 'info':
41
+ return s.muted('[i]');
42
+ }
43
+ }
44
+ function loadAnchors(activeProjectId) {
45
+ const items = [];
46
+ // Global anchors
47
+ const globalManager = getGlobalAnchorManager();
48
+ for (const anchor of globalManager.getAll()) {
49
+ items.push({ anchor, scope: 'global', projectId: null });
50
+ }
51
+ // Project anchors (if active project)
52
+ if (activeProjectId !== null) {
53
+ const projectManager = getAnchorManager(String(activeProjectId));
54
+ for (const anchor of projectManager.getAll()) {
55
+ items.push({ anchor, scope: 'project', projectId: String(activeProjectId) });
56
+ }
57
+ }
58
+ // Sort by priority (critical first), then by content
59
+ const priorityOrder = { critical: 0, safety: 1, info: 2 };
60
+ items.sort((a, b) => {
61
+ const pa = priorityOrder[a.anchor.priority] ?? 2;
62
+ const pb = priorityOrder[b.anchor.priority] ?? 2;
63
+ if (pa !== pb)
64
+ return pa - pb;
65
+ return a.anchor.content.localeCompare(b.anchor.content);
66
+ });
67
+ return items;
68
+ }
69
+ // =============================================================================
70
+ // Key Detection Helpers
71
+ // =============================================================================
72
+ function isCtrlC(data) {
73
+ return data.length === 1 && data[0] === 0x03;
74
+ }
75
+ function isEscape(data) {
76
+ return data.length === 1 && data[0] === 0x1b;
77
+ }
78
+ function isEnter(data) {
79
+ return data.length === 1 && (data[0] === 0x0d || data[0] === 0x0a);
80
+ }
81
+ function isBackspace(data) {
82
+ return data.length === 1 && (data[0] === 0x7f || data[0] === 0x08);
83
+ }
84
+ function isTab(data) {
85
+ return data.length === 1 && data[0] === 0x09;
86
+ }
87
+ function isShiftTab(data) {
88
+ return data.toString() === '\x1b[Z';
89
+ }
90
+ function isNavigateUp(data) {
91
+ const str = data.toString();
92
+ return str === '\x1b[A' || str === 'k';
93
+ }
94
+ function isNavigateDown(data) {
95
+ const str = data.toString();
96
+ return str === '\x1b[B' || str === 'j';
97
+ }
98
+ function isLeftArrow(data) {
99
+ return data.toString() === '\x1b[D';
100
+ }
101
+ function isRightArrow(data) {
102
+ return data.toString() === '\x1b[C';
103
+ }
104
+ function extractPrintable(data) {
105
+ if (data.length === 1 && data[0] >= 0x20 && data[0] < 0x7f) {
106
+ return String.fromCharCode(data[0]);
107
+ }
108
+ return null;
109
+ }
110
+ // =============================================================================
111
+ // Add Anchor Screen
112
+ // =============================================================================
113
+ class AddAnchorScreen extends BaseScreen {
114
+ editState;
115
+ styles;
116
+ hasProject;
117
+ activeProjectId;
118
+ onComplete;
119
+ constructor(editState, styles, hasProject, activeProjectId, onComplete) {
120
+ super();
121
+ this.editState = editState;
122
+ this.styles = styles;
123
+ this.hasProject = hasProject;
124
+ this.activeProjectId = activeProjectId;
125
+ this.onComplete = onComplete;
126
+ }
127
+ render() {
128
+ const s = this.styles;
129
+ const cols = terminal.getTerminalWidth();
130
+ const border = renderBorder(cols, s);
131
+ const lines = [];
132
+ lines.push(border);
133
+ lines.push(` ${s.primaryBold('ADD NEW ANCHOR')}`);
134
+ lines.push('');
135
+ // Content field
136
+ const contentLabel = this.editState.addField === 'content' ? s.primary('Content:') : s.muted('Content:');
137
+ lines.push(` ${contentLabel}`);
138
+ const maxDisplayLen = cols - 6;
139
+ let displayContent = this.editState.addContent;
140
+ if (displayContent.length > maxDisplayLen) {
141
+ displayContent = '…' + displayContent.slice(-(maxDisplayLen - 1));
142
+ }
143
+ lines.push(` ${displayContent}${this.editState.addField === 'content' ? '█' : ''}`);
144
+ lines.push('');
145
+ // Priority field
146
+ const priorityLabel = this.editState.addField === 'priority' ? s.primary('Priority:') : s.muted('Priority:');
147
+ const priorityOptions = PRIORITIES.map(p => p === this.editState.addPriority ? s.inverse(` ${p} `) : s.muted(` ${p} `)).join(' ');
148
+ lines.push(` ${priorityLabel} ${priorityOptions}`);
149
+ // Scope field (only if project is active)
150
+ if (this.hasProject) {
151
+ const scopeLabel = this.editState.addField === 'scope' ? s.primary('Scope:') : s.muted('Scope:');
152
+ const scopeOptions = ['global', 'project'].map(sc => sc === this.editState.addScope ? s.inverse(` ${sc} `) : s.muted(` ${sc} `)).join(' ');
153
+ lines.push(` ${scopeLabel} ${scopeOptions}`);
154
+ }
155
+ lines.push('');
156
+ const remaining = MAX_ANCHOR_LENGTH - this.editState.addContent.length;
157
+ lines.push(s.muted(` ${String(remaining)} chars remaining`));
158
+ // Message
159
+ if (this.editState.message) {
160
+ lines.push('');
161
+ switch (this.editState.messageType) {
162
+ case 'success':
163
+ lines.push(s.success(` ${this.editState.message}`));
164
+ break;
165
+ case 'error':
166
+ lines.push(s.error(` ${this.editState.message}`));
167
+ break;
168
+ default:
169
+ lines.push(s.muted(` ${this.editState.message}`));
170
+ }
171
+ }
172
+ lines.push('');
173
+ lines.push(border);
174
+ lines.push(s.muted(' Tab Switch field · ↑↓/←→ Change value · Enter Save · Esc Cancel'));
175
+ lines.push(border);
176
+ return lines;
177
+ }
178
+ handleKey(data) {
179
+ const char = extractPrintable(data);
180
+ if (isCtrlC(data)) {
181
+ return closeOverlay({ modified: this.editState.modified });
182
+ }
183
+ if (isEscape(data)) {
184
+ this.editState.message = null;
185
+ return popScreen();
186
+ }
187
+ // Tab - switch fields
188
+ if (isTab(data)) {
189
+ if (this.editState.addField === 'content') {
190
+ this.editState.addField = 'priority';
191
+ }
192
+ else if (this.editState.addField === 'priority') {
193
+ this.editState.addField = this.hasProject ? 'scope' : 'content';
194
+ }
195
+ else {
196
+ this.editState.addField = 'content';
197
+ }
198
+ return stay();
199
+ }
200
+ // Shift+Tab - switch fields backward
201
+ if (isShiftTab(data)) {
202
+ if (this.editState.addField === 'content') {
203
+ this.editState.addField = this.hasProject ? 'scope' : 'priority';
204
+ }
205
+ else if (this.editState.addField === 'priority') {
206
+ this.editState.addField = 'content';
207
+ }
208
+ else {
209
+ this.editState.addField = 'priority';
210
+ }
211
+ return stay();
212
+ }
213
+ // Enter - save
214
+ if (isEnter(data)) {
215
+ if (this.editState.addContent.trim()) {
216
+ try {
217
+ const manager = this.editState.addScope === 'global'
218
+ ? getGlobalAnchorManager()
219
+ : getAnchorManager(String(this.activeProjectId));
220
+ manager.add({
221
+ content: this.editState.addContent.trim(),
222
+ priority: this.editState.addPriority,
223
+ scope: 'persistent',
224
+ });
225
+ this.editState.modified = true;
226
+ this.onComplete();
227
+ this.editState.message = null;
228
+ return popScreen();
229
+ }
230
+ catch (error) {
231
+ this.editState.message = `Error: ${error.message}`;
232
+ this.editState.messageType = 'error';
233
+ return stay();
234
+ }
235
+ }
236
+ else {
237
+ this.editState.message = 'Content cannot be empty';
238
+ this.editState.messageType = 'error';
239
+ return stay();
240
+ }
241
+ }
242
+ // Field-specific input
243
+ if (this.editState.addField === 'content') {
244
+ if (isBackspace(data)) {
245
+ this.editState.addContent = this.editState.addContent.slice(0, -1);
246
+ return stay();
247
+ }
248
+ if (char && this.editState.addContent.length < MAX_ANCHOR_LENGTH) {
249
+ this.editState.addContent += char;
250
+ return stay();
251
+ }
252
+ }
253
+ else if (this.editState.addField === 'priority') {
254
+ if (isNavigateUp(data) || isLeftArrow(data)) {
255
+ const idx = PRIORITIES.indexOf(this.editState.addPriority);
256
+ this.editState.addPriority = PRIORITIES[(idx - 1 + PRIORITIES.length) % PRIORITIES.length];
257
+ return stay();
258
+ }
259
+ if (isNavigateDown(data) || isRightArrow(data)) {
260
+ const idx = PRIORITIES.indexOf(this.editState.addPriority);
261
+ this.editState.addPriority = PRIORITIES[(idx + 1) % PRIORITIES.length];
262
+ return stay();
263
+ }
264
+ }
265
+ else {
266
+ // Scope field
267
+ if (isNavigateUp(data) || isNavigateDown(data) || isLeftArrow(data) || isRightArrow(data)) {
268
+ this.editState.addScope = this.editState.addScope === 'global' ? 'project' : 'global';
269
+ return stay();
270
+ }
271
+ }
272
+ return stay(false);
273
+ }
274
+ getMinHeight() {
275
+ return 16;
276
+ }
277
+ }
278
+ // =============================================================================
279
+ // Edit Anchor Screen
280
+ // =============================================================================
281
+ class EditAnchorScreen extends BaseScreen {
282
+ editState;
283
+ styles;
284
+ onComplete;
285
+ maxVisibleLines = 6;
286
+ constructor(editState, styles, onComplete) {
287
+ super();
288
+ this.editState = editState;
289
+ this.styles = styles;
290
+ this.onComplete = onComplete;
291
+ }
292
+ wrapText(text, maxWidth) {
293
+ if (text.length <= maxWidth)
294
+ return [text];
295
+ const wrapped = [];
296
+ let remaining = text;
297
+ while (remaining.length > 0) {
298
+ if (remaining.length <= maxWidth) {
299
+ wrapped.push(remaining);
300
+ break;
301
+ }
302
+ let breakPoint = maxWidth;
303
+ const searchStart = Math.max(0, maxWidth - 20);
304
+ for (let i = maxWidth - 1; i >= searchStart; i--) {
305
+ if (remaining[i] === ' ') {
306
+ breakPoint = i + 1;
307
+ break;
308
+ }
309
+ }
310
+ wrapped.push(remaining.slice(0, breakPoint));
311
+ remaining = remaining.slice(breakPoint);
312
+ }
313
+ return wrapped;
314
+ }
315
+ getWrappedCursorPosition(wrappedLines, linearCursor) {
316
+ let pos = 0;
317
+ for (let row = 0; row < wrappedLines.length; row++) {
318
+ const lineLen = wrappedLines[row].length;
319
+ if (pos + lineLen >= linearCursor) {
320
+ return { row, col: linearCursor - pos };
321
+ }
322
+ pos += lineLen;
323
+ }
324
+ const lastRow = wrappedLines.length - 1;
325
+ return { row: lastRow, col: wrappedLines[lastRow]?.length ?? 0 };
326
+ }
327
+ render() {
328
+ const s = this.styles;
329
+ const cols = terminal.getTerminalWidth();
330
+ const border = renderBorder(cols, s);
331
+ const lines = [];
332
+ const item = this.editState.targetItem;
333
+ if (!item) {
334
+ lines.push(border);
335
+ lines.push(s.error(' No anchor selected'));
336
+ lines.push(border);
337
+ return lines;
338
+ }
339
+ lines.push(border);
340
+ lines.push(` ${s.primaryBold('EDIT ANCHOR')}`);
341
+ lines.push('');
342
+ lines.push(s.muted(` ID: ${item.anchor.id}`));
343
+ lines.push(s.muted(` Priority: ${item.anchor.priority}`));
344
+ lines.push(s.muted(` Scope: ${item.scope}`));
345
+ lines.push('');
346
+ lines.push(` ${s.primary('Content:')}`);
347
+ const maxLineWidth = cols - 4;
348
+ const content = this.editState.editContent;
349
+ const cursor = this.editState.editCursor;
350
+ const wrappedLines = this.wrapText(content, maxLineWidth);
351
+ const { row: cursorRow, col: cursorCol } = this.getWrappedCursorPosition(wrappedLines, cursor);
352
+ let startLine = 0;
353
+ if (cursorRow >= this.maxVisibleLines) {
354
+ startLine = cursorRow - this.maxVisibleLines + 1;
355
+ }
356
+ const endLine = Math.min(startLine + this.maxVisibleLines, wrappedLines.length);
357
+ for (let i = startLine; i < endLine; i++) {
358
+ const line = wrappedLines[i];
359
+ const isCursorLine = i === cursorRow;
360
+ if (isCursorLine) {
361
+ if (cursorCol < line.length) {
362
+ const beforeCursor = line.slice(0, cursorCol);
363
+ const cursorChar = line[cursorCol];
364
+ const afterCursor = line.slice(cursorCol + 1);
365
+ lines.push(` ${beforeCursor}${s.inverse(cursorChar)}${afterCursor}`);
366
+ }
367
+ else {
368
+ lines.push(` ${line}${s.inverse(' ')}`);
369
+ }
370
+ }
371
+ else {
372
+ lines.push(` ${line}`);
373
+ }
374
+ }
375
+ for (let i = endLine - startLine; i < this.maxVisibleLines; i++) {
376
+ lines.push('');
377
+ }
378
+ if (wrappedLines.length > this.maxVisibleLines) {
379
+ const scrollInfo = `Lines ${String(startLine + 1)}-${String(endLine)} of ${String(wrappedLines.length)}`;
380
+ lines.push(s.muted(` ${scrollInfo}`));
381
+ }
382
+ else {
383
+ lines.push('');
384
+ }
385
+ const remaining = MAX_ANCHOR_LENGTH - this.editState.editContent.length;
386
+ lines.push(s.muted(` ${String(remaining)} chars remaining`));
387
+ if (this.editState.message) {
388
+ lines.push('');
389
+ switch (this.editState.messageType) {
390
+ case 'success':
391
+ lines.push(s.success(` ${this.editState.message}`));
392
+ break;
393
+ case 'error':
394
+ lines.push(s.error(` ${this.editState.message}`));
395
+ break;
396
+ default:
397
+ lines.push(s.muted(` ${this.editState.message}`));
398
+ }
399
+ }
400
+ lines.push('');
401
+ lines.push(border);
402
+ lines.push(s.muted(' ←→ Navigate · Enter Save · Esc Cancel'));
403
+ lines.push(border);
404
+ return lines;
405
+ }
406
+ handleKey(data) {
407
+ const char = extractPrintable(data);
408
+ const keyStr = data.toString();
409
+ const item = this.editState.targetItem;
410
+ if (!item) {
411
+ return popScreen();
412
+ }
413
+ if (isCtrlC(data)) {
414
+ return closeOverlay({ modified: this.editState.modified });
415
+ }
416
+ if (isEscape(data)) {
417
+ this.editState.message = null;
418
+ this.editState.targetItem = null;
419
+ return popScreen();
420
+ }
421
+ if (isEnter(data)) {
422
+ return this.saveAndClose(item);
423
+ }
424
+ // Left arrow
425
+ if (isLeftArrow(data)) {
426
+ if (this.editState.editCursor > 0) {
427
+ this.editState.editCursor--;
428
+ return stay();
429
+ }
430
+ return stay(false);
431
+ }
432
+ // Right arrow
433
+ if (isRightArrow(data)) {
434
+ if (this.editState.editCursor < this.editState.editContent.length) {
435
+ this.editState.editCursor++;
436
+ return stay();
437
+ }
438
+ return stay(false);
439
+ }
440
+ // Home/Ctrl+A
441
+ if (keyStr === '\x1b[H' || keyStr === '\x01') {
442
+ this.editState.editCursor = 0;
443
+ return stay();
444
+ }
445
+ // End/Ctrl+E
446
+ if (keyStr === '\x1b[F' || keyStr === '\x05') {
447
+ this.editState.editCursor = this.editState.editContent.length;
448
+ return stay();
449
+ }
450
+ // Backspace
451
+ if (isBackspace(data) && this.editState.editCursor > 0) {
452
+ this.editState.editContent =
453
+ this.editState.editContent.slice(0, this.editState.editCursor - 1) +
454
+ this.editState.editContent.slice(this.editState.editCursor);
455
+ this.editState.editCursor--;
456
+ return stay();
457
+ }
458
+ // Delete key
459
+ if (keyStr === '\x1b[3~' && this.editState.editCursor < this.editState.editContent.length) {
460
+ this.editState.editContent =
461
+ this.editState.editContent.slice(0, this.editState.editCursor) +
462
+ this.editState.editContent.slice(this.editState.editCursor + 1);
463
+ return stay();
464
+ }
465
+ // Printable character
466
+ if (char && this.editState.editContent.length < MAX_ANCHOR_LENGTH) {
467
+ this.editState.editContent =
468
+ this.editState.editContent.slice(0, this.editState.editCursor) +
469
+ char +
470
+ this.editState.editContent.slice(this.editState.editCursor);
471
+ this.editState.editCursor++;
472
+ return stay();
473
+ }
474
+ return stay(false);
475
+ }
476
+ saveAndClose(item) {
477
+ if (this.editState.editContent.trim()) {
478
+ try {
479
+ const manager = item.scope === 'global'
480
+ ? getGlobalAnchorManager()
481
+ : getAnchorManager(item.projectId ?? '');
482
+ const oldAnchor = item.anchor;
483
+ manager.remove(oldAnchor.id);
484
+ manager.add({
485
+ id: oldAnchor.id,
486
+ content: this.editState.editContent.trim(),
487
+ priority: oldAnchor.priority,
488
+ scope: oldAnchor.scope,
489
+ tags: oldAnchor.tags,
490
+ metadata: oldAnchor.metadata,
491
+ projectId: oldAnchor.projectId,
492
+ });
493
+ this.editState.modified = true;
494
+ this.onComplete();
495
+ this.editState.message = null;
496
+ this.editState.targetItem = null;
497
+ return popScreen();
498
+ }
499
+ catch (error) {
500
+ this.editState.message = `Error: ${error.message}`;
501
+ this.editState.messageType = 'error';
502
+ return stay();
503
+ }
504
+ }
505
+ else {
506
+ this.editState.message = 'Content cannot be empty';
507
+ this.editState.messageType = 'error';
508
+ return stay();
509
+ }
510
+ }
511
+ getMinHeight() {
512
+ return 20;
513
+ }
514
+ }
515
+ // =============================================================================
516
+ // Delete Confirm Screen
517
+ // =============================================================================
518
+ class DeleteConfirmScreen extends BaseScreen {
519
+ editState;
520
+ styles;
521
+ onComplete;
522
+ constructor(editState, styles, onComplete) {
523
+ super();
524
+ this.editState = editState;
525
+ this.styles = styles;
526
+ this.onComplete = onComplete;
527
+ }
528
+ render() {
529
+ const s = this.styles;
530
+ const cols = terminal.getTerminalWidth();
531
+ const border = renderBorder(cols, s);
532
+ const lines = [];
533
+ const item = this.editState.targetItem;
534
+ if (!item) {
535
+ lines.push(border);
536
+ lines.push(s.error(' No anchor selected'));
537
+ lines.push(border);
538
+ return lines;
539
+ }
540
+ lines.push(border);
541
+ lines.push(` ${s.warning('DELETE ANCHOR')}`);
542
+ lines.push('');
543
+ lines.push(s.muted(` ID: ${item.anchor.id}`));
544
+ lines.push(s.muted(` Priority: ${item.anchor.priority}`));
545
+ lines.push(s.muted(` Scope: ${item.scope}`));
546
+ lines.push('');
547
+ const maxContentLen = cols - 14;
548
+ let content = item.anchor.content;
549
+ if (content.length > maxContentLen) {
550
+ content = content.slice(0, maxContentLen - 3) + '...';
551
+ }
552
+ lines.push(` Content: ${content}`);
553
+ lines.push('');
554
+ lines.push(s.warning(' Are you sure you want to delete this anchor?'));
555
+ lines.push('');
556
+ lines.push(border);
557
+ lines.push(s.muted(' y Yes · n/Esc No'));
558
+ lines.push(border);
559
+ return lines;
560
+ }
561
+ handleKey(data) {
562
+ const char = extractPrintable(data);
563
+ const item = this.editState.targetItem;
564
+ if (!item) {
565
+ return popScreen();
566
+ }
567
+ if (isCtrlC(data)) {
568
+ return closeOverlay({ modified: this.editState.modified });
569
+ }
570
+ if (char === 'y' || char === 'Y') {
571
+ try {
572
+ const manager = item.scope === 'global'
573
+ ? getGlobalAnchorManager()
574
+ : getAnchorManager(item.projectId ?? '');
575
+ manager.remove(item.anchor.id);
576
+ this.editState.modified = true;
577
+ this.onComplete();
578
+ this.editState.targetItem = null;
579
+ return popScreen();
580
+ }
581
+ catch (error) {
582
+ this.editState.message = `Error: ${error.message}`;
583
+ this.editState.messageType = 'error';
584
+ return popScreen();
585
+ }
586
+ }
587
+ if (char === 'n' || char === 'N' || isEscape(data)) {
588
+ this.editState.targetItem = null;
589
+ return popScreen();
590
+ }
591
+ return stay(false);
592
+ }
593
+ getMinHeight() {
594
+ return 14;
595
+ }
596
+ }
597
+ // =============================================================================
598
+ // Anchors Overlay V2
599
+ // =============================================================================
600
+ export class AnchorsOverlayV2 extends TabbedListOverlayV2 {
601
+ type = 'inline';
602
+ id = 'anchors-overlay-v2';
603
+ editState;
604
+ activeProject;
605
+ /**
606
+ * @param projectId - Optional project ID to view anchors for.
607
+ * If not provided, uses the active project.
608
+ * Pass this when opening from workflow overlay for a specific project.
609
+ */
610
+ constructor(projectId) {
611
+ // Use provided projectId, or fall back to active project
612
+ const activeProject = getActiveProject();
613
+ const resolvedProjectId = projectId ?? activeProject?.id ?? null;
614
+ const anchors = loadAnchors(resolvedProjectId);
615
+ super({
616
+ title: 'Anchors',
617
+ tabs: TABS,
618
+ items: anchors,
619
+ pageSize: PAGE_SIZE,
620
+ filterByTab: (item, tabId) => {
621
+ if (tabId === 'all')
622
+ return true;
623
+ if (tabId === 'critical' || tabId === 'safety' || tabId === 'info') {
624
+ return item.anchor.priority === tabId;
625
+ }
626
+ if (tabId === 'global')
627
+ return item.scope === 'global';
628
+ if (tabId === 'project')
629
+ return item.scope === 'project';
630
+ return true;
631
+ },
632
+ getSearchText: (item) => `${item.anchor.content} ${item.anchor.priority} ${item.scope}`,
633
+ renderItem: (item, isSelected, s) => {
634
+ const cols = terminal.getTerminalWidth();
635
+ const priorityBadge = getPriorityBadge(item.anchor.priority, s);
636
+ const scopeBadge = item.scope === 'global' ? s.muted('[G]') : s.info('[P]');
637
+ const prefixLen = 15;
638
+ const maxContentLen = Math.max(20, cols - prefixLen - 8);
639
+ let content = item.anchor.content;
640
+ if (content.length > maxContentLen) {
641
+ content = content.slice(0, maxContentLen - 3) + '...';
642
+ }
643
+ const cursor = isSelected ? s.primary('❯ ') : ' ';
644
+ const line = `${priorityBadge} ${scopeBadge} ${content}`;
645
+ if (isSelected) {
646
+ return `${cursor}${s.primary(line)}`;
647
+ }
648
+ return `${cursor}${line}`;
649
+ },
650
+ showCount: true,
651
+ emptyMessage: 'No anchors defined. Press [a] to add one.',
652
+ noResultsMessage: 'No anchors match the filter.',
653
+ footerHints: (searchMode) => {
654
+ if (searchMode) {
655
+ return 'Type to filter · ↑↓/jk Navigate · Enter Edit · Esc Exit search';
656
+ }
657
+ return '↑↓/jk Navigate · ←→/hl Tabs · / Search · a Add · e Edit · d Delete · q/Esc Close';
658
+ },
659
+ });
660
+ // Store resolved project info - uses provided projectId or falls back to active project
661
+ this.activeProject = resolvedProjectId !== null
662
+ ? { id: resolvedProjectId, displayName: activeProject?.displayName ?? 'Project' }
663
+ : null;
664
+ this.editState = {
665
+ addContent: '',
666
+ addPriority: 'info',
667
+ addScope: resolvedProjectId !== null ? 'project' : 'global',
668
+ addField: 'content',
669
+ editContent: '',
670
+ editCursor: 0,
671
+ targetItem: null,
672
+ message: null,
673
+ messageType: 'info',
674
+ modified: false,
675
+ };
676
+ this.minHeight = 20;
677
+ }
678
+ /**
679
+ * Refresh anchors from storage
680
+ */
681
+ refreshAnchors() {
682
+ const anchors = loadAnchors(this.activeProject?.id ?? null);
683
+ this.state.items = anchors;
684
+ // Re-apply tab filter
685
+ const tabId = TABS[this.state.currentTab]?.id ?? 'all';
686
+ let filtered = anchors.filter((item) => {
687
+ if (tabId === 'all')
688
+ return true;
689
+ if (tabId === 'critical' || tabId === 'safety' || tabId === 'info') {
690
+ return item.anchor.priority === tabId;
691
+ }
692
+ if (tabId === 'global')
693
+ return item.scope === 'global';
694
+ if (tabId === 'project')
695
+ return item.scope === 'project';
696
+ return true;
697
+ });
698
+ // Re-apply search filter
699
+ if (this.state.searchQuery) {
700
+ const query = this.state.searchQuery.toLowerCase();
701
+ filtered = filtered.filter((item) => item.anchor.content.toLowerCase().includes(query) ||
702
+ item.anchor.priority.toLowerCase().includes(query) ||
703
+ item.scope.toLowerCase().includes(query));
704
+ }
705
+ this.state.filteredItems = filtered;
706
+ this.state.selectedIndex = Math.min(this.state.selectedIndex, Math.max(0, filtered.length - 1));
707
+ }
708
+ /**
709
+ * Override handleKey to add custom behavior (a add, e edit, d delete)
710
+ */
711
+ handleKey(key) {
712
+ // Ctrl+C always closes
713
+ if (this.isInterruptKey(key)) {
714
+ return this.close({ modified: this.editState.modified });
715
+ }
716
+ // q or Esc closes (only if not in search mode AND on main list)
717
+ // If on a detail screen (screenStack.size() > 1), let super.handleKey() pop the screen
718
+ if (this.isCloseKey(key) && !this.state.searchMode && this.screenStack.size() === 1) {
719
+ return this.close({ modified: this.editState.modified });
720
+ }
721
+ // Only handle custom keys when on main list (screen stack size = 1)
722
+ if (this.screenStack.size() === 1 && !this.state.searchMode) {
723
+ // Enter - edit selected anchor (handle here instead of onItemSelected for proper rerender)
724
+ if (this.isEnterKey(key) && this.state.filteredItems.length > 0) {
725
+ const item = this.state.filteredItems[this.state.selectedIndex];
726
+ this.editState.targetItem = item;
727
+ this.editState.editContent = item.anchor.content;
728
+ this.editState.editCursor = item.anchor.content.length;
729
+ this.editState.message = null;
730
+ this.screenStack.push(new EditAnchorScreen(this.editState, this.getStyles(), () => { this.refreshAnchors(); }));
731
+ return this.rerender();
732
+ }
733
+ // 'a' - add mode
734
+ if (key.char === 'a') {
735
+ this.editState.addContent = '';
736
+ this.editState.addPriority = 'info';
737
+ this.editState.addScope = this.activeProject ? 'project' : 'global';
738
+ this.editState.addField = 'content';
739
+ this.editState.message = null;
740
+ this.screenStack.push(new AddAnchorScreen(this.editState, this.getStyles(), this.activeProject !== null, this.activeProject?.id ?? null, () => { this.refreshAnchors(); }));
741
+ return this.rerender();
742
+ }
743
+ // 'e' - edit mode (when items exist)
744
+ if (key.char === 'e' && this.state.filteredItems.length > 0) {
745
+ const item = this.state.filteredItems[this.state.selectedIndex];
746
+ this.editState.targetItem = item;
747
+ this.editState.editContent = item.anchor.content;
748
+ this.editState.editCursor = item.anchor.content.length;
749
+ this.editState.message = null;
750
+ this.screenStack.push(new EditAnchorScreen(this.editState, this.getStyles(), () => { this.refreshAnchors(); }));
751
+ return this.rerender();
752
+ }
753
+ // 'd' - delete confirm (when items exist)
754
+ if (key.char === 'd' && this.state.filteredItems.length > 0) {
755
+ const item = this.state.filteredItems[this.state.selectedIndex];
756
+ this.editState.targetItem = item;
757
+ this.editState.message = null;
758
+ this.screenStack.push(new DeleteConfirmScreen(this.editState, this.getStyles(), () => { this.refreshAnchors(); }));
759
+ return this.rerender();
760
+ }
761
+ }
762
+ // Let parent handle everything else
763
+ const result = super.handleKey(key);
764
+ // Convert close result to our result type with modified flag
765
+ if (result.type === 'close') {
766
+ return this.close({ modified: this.editState.modified });
767
+ }
768
+ // Safe to cast other actions (render, none, etc.)
769
+ return result;
770
+ }
771
+ /**
772
+ * Override to open edit screen on Enter
773
+ */
774
+ onItemSelected(item) {
775
+ this.editState.targetItem = item;
776
+ this.editState.editContent = item.anchor.content;
777
+ this.editState.editCursor = item.anchor.content.length;
778
+ this.editState.message = null;
779
+ this.screenStack.push(new EditAnchorScreen(this.editState, this.getStyles(), () => { this.refreshAnchors(); }));
780
+ // Return undefined to not close (we pushed a screen instead)
781
+ return undefined;
782
+ }
783
+ }