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