@notis_ai/cli 0.2.14 → 0.2.15

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 (296) hide show
  1. package/README.md +82 -20
  2. package/dist/agent-hooks/notis-agent-hook.mjs +19750 -0
  3. package/{skills → dist/base-skills}/notis-apps/SKILL.md +84 -53
  4. package/{skills → dist/base-skills}/notis-cli/SKILL.md +9 -9
  5. package/dist/base-skills/notis-query/SKILL.md +705 -0
  6. package/dist/skill-sync/index.js +1528 -0
  7. package/dist/skill-sync/index.js.map +7 -0
  8. package/package.json +5 -2
  9. package/skills/notis-apps/cli.md +38 -17
  10. package/skills/notis-cli/AGENT_INSTRUCTIONS.md +39 -0
  11. package/skills/notis-onboarding/BRIEF.md +16 -0
  12. package/src/agent-hook-entry.js +5 -0
  13. package/src/cli.js +23 -14
  14. package/src/command-specs/agents.js +392 -0
  15. package/src/command-specs/apps.js +871 -387
  16. package/src/command-specs/auth.js +63 -10
  17. package/src/command-specs/index.js +6 -0
  18. package/src/command-specs/onboarding.js +69 -5
  19. package/src/command-specs/skills.js +56 -0
  20. package/src/runtime/agent-memory-state.js +126 -0
  21. package/src/runtime/agent-setup.js +383 -0
  22. package/src/runtime/app-dev-consumers.js +154 -0
  23. package/src/runtime/app-dev-host-lock.js +80 -0
  24. package/src/runtime/app-dev-roots.js +284 -0
  25. package/src/runtime/app-dev-server.js +212 -22
  26. package/src/runtime/app-dev-sessions.js +105 -145
  27. package/src/runtime/app-platform.js +1291 -165
  28. package/src/runtime/app-registry-scaffolds.js +367 -0
  29. package/src/runtime/base-skills.d.ts +20 -0
  30. package/src/runtime/base-skills.js +167 -0
  31. package/src/runtime/login-listener.js +15 -0
  32. package/src/runtime/oauth.js +1506 -141
  33. package/src/runtime/output.js +7 -0
  34. package/src/runtime/ports.js +16 -0
  35. package/src/runtime/profiles.js +0 -5
  36. package/src/runtime/skill-sync/cloud-client.ts +96 -0
  37. package/src/runtime/skill-sync/index.ts +644 -0
  38. package/src/runtime/skill-sync/local-scanner.ts +1046 -0
  39. package/src/runtime/skill-sync/symlink-manager.ts +383 -0
  40. package/src/runtime/skill-sync/sync-plan.ts +22 -0
  41. package/src/runtime/skill-sync/types.ts +103 -0
  42. package/src/runtime/skill-sync/write-cloud-skill.ts +50 -0
  43. package/src/runtime/store-screenshot.js +6 -1
  44. package/src/runtime/sync-skills.d.ts +37 -0
  45. package/src/runtime/sync-skills.js +215 -0
  46. package/template/app/layout.tsx +5 -1
  47. package/template/notis.config.ts +1 -0
  48. package/template/packages/sdk/package.json +2 -1
  49. package/template/packages/sdk/src/components/DocumentEditor.tsx +13 -3
  50. package/template/packages/sdk/src/components/MarkdownEditor.tsx +121 -0
  51. package/template/packages/sdk/src/components/MultiSelectActionBar.tsx +37 -119
  52. package/template/packages/sdk/src/components/MultiSelectCheckbox.tsx +5 -1
  53. package/template/packages/sdk/src/components/MultiSelectDragOverlay.tsx +1 -1
  54. package/template/packages/sdk/src/components/NotisSelectionBoundary.tsx +55 -0
  55. package/template/packages/sdk/src/components/ShortcutHints.tsx +56 -0
  56. package/template/packages/sdk/src/config.ts +19 -0
  57. package/template/packages/sdk/src/documents.ts +7 -1
  58. package/template/packages/sdk/src/hooks/useActiveResource.ts +19 -0
  59. package/template/packages/sdk/src/hooks/useCollectionInteractions.ts +709 -0
  60. package/template/packages/sdk/src/hooks/useHandover.ts +3 -0
  61. package/template/packages/sdk/src/hooks/useMultiSelect.ts +44 -482
  62. package/template/packages/sdk/src/hooks/useTool.ts +5 -4
  63. package/template/packages/sdk/src/index.ts +47 -2
  64. package/template/packages/sdk/src/interactions/actions.ts +46 -0
  65. package/template/packages/sdk/src/interactions/shortcuts.tsx +628 -0
  66. package/template/packages/sdk/src/interactions.ts +41 -0
  67. package/template/packages/sdk/src/provider.tsx +2 -1
  68. package/template/packages/sdk/src/runtime.ts +89 -4
  69. package/dist/scaffolds/notis-database/CHANGELOG.md +0 -5
  70. package/dist/scaffolds/notis-database/app/globals.css +0 -44
  71. package/dist/scaffolds/notis-database/app/layout.tsx +0 -6
  72. package/dist/scaffolds/notis-database/app/page.tsx +0 -1088
  73. package/dist/scaffolds/notis-database/components/ui/badge.tsx +0 -28
  74. package/dist/scaffolds/notis-database/components/ui/button.tsx +0 -53
  75. package/dist/scaffolds/notis-database/components/ui/card.tsx +0 -56
  76. package/dist/scaffolds/notis-database/components/ui/table.tsx +0 -120
  77. package/dist/scaffolds/notis-database/components.json +0 -20
  78. package/dist/scaffolds/notis-database/index.html +0 -12
  79. package/dist/scaffolds/notis-database/lib/types.ts +0 -132
  80. package/dist/scaffolds/notis-database/lib/utils.ts +0 -6
  81. package/dist/scaffolds/notis-database/metadata/screenshot-1.png +0 -0
  82. package/dist/scaffolds/notis-database/metadata/screenshot-2.png +0 -0
  83. package/dist/scaffolds/notis-database/metadata/screenshot-3.png +0 -0
  84. package/dist/scaffolds/notis-database/metadata/screenshot-4.png +0 -0
  85. package/dist/scaffolds/notis-database/metadata/screenshot-5.png +0 -0
  86. package/dist/scaffolds/notis-database/metadata/screenshot-fixtures.json +0 -1839
  87. package/dist/scaffolds/notis-database/notis.config.ts +0 -73
  88. package/dist/scaffolds/notis-database/package-lock.json +0 -3935
  89. package/dist/scaffolds/notis-database/package.json +0 -32
  90. package/dist/scaffolds/notis-database/packages/sdk/package.json +0 -36
  91. package/dist/scaffolds/notis-database/packages/sdk/src/components/DocumentEditor.tsx +0 -93
  92. package/dist/scaffolds/notis-database/packages/sdk/src/components/Markdown.tsx +0 -60
  93. package/dist/scaffolds/notis-database/packages/sdk/src/components/MultiSelectActionBar.tsx +0 -278
  94. package/dist/scaffolds/notis-database/packages/sdk/src/components/MultiSelectCheckbox.tsx +0 -91
  95. package/dist/scaffolds/notis-database/packages/sdk/src/components/MultiSelectDragOverlay.tsx +0 -39
  96. package/dist/scaffolds/notis-database/packages/sdk/src/config.ts +0 -234
  97. package/dist/scaffolds/notis-database/packages/sdk/src/documents.ts +0 -250
  98. package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useBackend.ts +0 -41
  99. package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useCloudComputer.ts +0 -97
  100. package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useDatabaseSchema.ts +0 -85
  101. package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useDatabaseSubscription.ts +0 -76
  102. package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useDocument.ts +0 -78
  103. package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useDocuments.ts +0 -121
  104. package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useHandover.ts +0 -75
  105. package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useMultiSelect.ts +0 -539
  106. package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useNotis.ts +0 -34
  107. package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useNotisNavigation.ts +0 -49
  108. package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useTool.ts +0 -64
  109. package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useTools.ts +0 -56
  110. package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useTopBarSearch.ts +0 -73
  111. package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useUpsertDocument.ts +0 -95
  112. package/dist/scaffolds/notis-database/packages/sdk/src/index.ts +0 -100
  113. package/dist/scaffolds/notis-database/packages/sdk/src/provider.tsx +0 -43
  114. package/dist/scaffolds/notis-database/packages/sdk/src/runtime.ts +0 -351
  115. package/dist/scaffolds/notis-database/packages/sdk/src/styles.css +0 -186
  116. package/dist/scaffolds/notis-database/packages/sdk/src/ui.ts +0 -15
  117. package/dist/scaffolds/notis-database/packages/sdk/src/vite.ts +0 -56
  118. package/dist/scaffolds/notis-database/packages/sdk/tsconfig.json +0 -15
  119. package/dist/scaffolds/notis-database/postcss.config.mjs +0 -8
  120. package/dist/scaffolds/notis-database/src/dev-main.tsx +0 -23
  121. package/dist/scaffolds/notis-database/src/mock-runtime.ts +0 -561
  122. package/dist/scaffolds/notis-database/tailwind.config.ts +0 -59
  123. package/dist/scaffolds/notis-database/tsconfig.json +0 -23
  124. package/dist/scaffolds/notis-database/vite.config.ts +0 -22
  125. package/dist/scaffolds/notis-journal/CHANGELOG.md +0 -29
  126. package/dist/scaffolds/notis-journal/app/globals.css +0 -37
  127. package/dist/scaffolds/notis-journal/app/insights/page.tsx +0 -513
  128. package/dist/scaffolds/notis-journal/app/journal-core.tsx +0 -362
  129. package/dist/scaffolds/notis-journal/app/journal-ui.tsx +0 -337
  130. package/dist/scaffolds/notis-journal/app/layout.tsx +0 -6
  131. package/dist/scaffolds/notis-journal/app/page.tsx +0 -486
  132. package/dist/scaffolds/notis-journal/components/ui/badge.tsx +0 -28
  133. package/dist/scaffolds/notis-journal/components/ui/button.tsx +0 -53
  134. package/dist/scaffolds/notis-journal/components/ui/card.tsx +0 -56
  135. package/dist/scaffolds/notis-journal/components.json +0 -20
  136. package/dist/scaffolds/notis-journal/index.html +0 -12
  137. package/dist/scaffolds/notis-journal/lib/utils.ts +0 -6
  138. package/dist/scaffolds/notis-journal/metadata/screenshot-1.png +0 -0
  139. package/dist/scaffolds/notis-journal/metadata/screenshot-2.png +0 -0
  140. package/dist/scaffolds/notis-journal/metadata/screenshot-3.png +0 -0
  141. package/dist/scaffolds/notis-journal/metadata/screenshot-4.png +0 -0
  142. package/dist/scaffolds/notis-journal/metadata/screenshot-5.png +0 -0
  143. package/dist/scaffolds/notis-journal/metadata/screenshot-6.png +0 -0
  144. package/dist/scaffolds/notis-journal/metadata/screenshot-fixtures.json +0 -132
  145. package/dist/scaffolds/notis-journal/notis.config.ts +0 -93
  146. package/dist/scaffolds/notis-journal/package-lock.json +0 -4615
  147. package/dist/scaffolds/notis-journal/package.json +0 -34
  148. package/dist/scaffolds/notis-journal/packages/sdk/package.json +0 -36
  149. package/dist/scaffolds/notis-journal/packages/sdk/src/components/DocumentEditor.tsx +0 -93
  150. package/dist/scaffolds/notis-journal/packages/sdk/src/components/Markdown.tsx +0 -60
  151. package/dist/scaffolds/notis-journal/packages/sdk/src/components/MultiSelectActionBar.tsx +0 -278
  152. package/dist/scaffolds/notis-journal/packages/sdk/src/components/MultiSelectCheckbox.tsx +0 -91
  153. package/dist/scaffolds/notis-journal/packages/sdk/src/components/MultiSelectDragOverlay.tsx +0 -39
  154. package/dist/scaffolds/notis-journal/packages/sdk/src/config.ts +0 -234
  155. package/dist/scaffolds/notis-journal/packages/sdk/src/documents.ts +0 -250
  156. package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useBackend.ts +0 -41
  157. package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useCloudComputer.ts +0 -97
  158. package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useDatabaseSchema.ts +0 -85
  159. package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useDatabaseSubscription.ts +0 -76
  160. package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useDocument.ts +0 -78
  161. package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useDocuments.ts +0 -121
  162. package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useHandover.ts +0 -75
  163. package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useMultiSelect.ts +0 -539
  164. package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useNotis.ts +0 -34
  165. package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useNotisNavigation.ts +0 -49
  166. package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useTool.ts +0 -64
  167. package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useTools.ts +0 -56
  168. package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useTopBarSearch.ts +0 -73
  169. package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useUpsertDocument.ts +0 -95
  170. package/dist/scaffolds/notis-journal/packages/sdk/src/index.ts +0 -100
  171. package/dist/scaffolds/notis-journal/packages/sdk/src/provider.tsx +0 -43
  172. package/dist/scaffolds/notis-journal/packages/sdk/src/runtime.ts +0 -351
  173. package/dist/scaffolds/notis-journal/packages/sdk/src/styles.css +0 -186
  174. package/dist/scaffolds/notis-journal/packages/sdk/src/ui.ts +0 -15
  175. package/dist/scaffolds/notis-journal/packages/sdk/src/vite.ts +0 -56
  176. package/dist/scaffolds/notis-journal/packages/sdk/tsconfig.json +0 -15
  177. package/dist/scaffolds/notis-journal/postcss.config.mjs +0 -8
  178. package/dist/scaffolds/notis-journal/skills/journal-onboarding/SKILL.md +0 -120
  179. package/dist/scaffolds/notis-journal/src/dev-main.tsx +0 -58
  180. package/dist/scaffolds/notis-journal/src/mock-runtime.ts +0 -199
  181. package/dist/scaffolds/notis-journal/tailwind.config.ts +0 -58
  182. package/dist/scaffolds/notis-journal/tsconfig.json +0 -23
  183. package/dist/scaffolds/notis-journal/vite.config.ts +0 -10
  184. package/dist/scaffolds/notis-notes/CHANGELOG.md +0 -5
  185. package/dist/scaffolds/notis-notes/app/globals.css +0 -3
  186. package/dist/scaffolds/notis-notes/app/layout.tsx +0 -6
  187. package/dist/scaffolds/notis-notes/app/page.tsx +0 -1943
  188. package/dist/scaffolds/notis-notes/app/phosphor-icons.ts +0 -596
  189. package/dist/scaffolds/notis-notes/components/ui/badge.tsx +0 -28
  190. package/dist/scaffolds/notis-notes/components/ui/button.tsx +0 -53
  191. package/dist/scaffolds/notis-notes/components/ui/card.tsx +0 -56
  192. package/dist/scaffolds/notis-notes/components.json +0 -20
  193. package/dist/scaffolds/notis-notes/lib/utils.ts +0 -6
  194. package/dist/scaffolds/notis-notes/lib/visible-properties.ts +0 -144
  195. package/dist/scaffolds/notis-notes/metadata/screenshot-1.png +0 -0
  196. package/dist/scaffolds/notis-notes/metadata/screenshot-2.png +0 -0
  197. package/dist/scaffolds/notis-notes/metadata/screenshot-3.png +0 -0
  198. package/dist/scaffolds/notis-notes/metadata/screenshot-4.png +0 -0
  199. package/dist/scaffolds/notis-notes/metadata/screenshot-5.png +0 -0
  200. package/dist/scaffolds/notis-notes/metadata/screenshot-fixtures.json +0 -752
  201. package/dist/scaffolds/notis-notes/notis.config.ts +0 -80
  202. package/dist/scaffolds/notis-notes/package-lock.json +0 -4636
  203. package/dist/scaffolds/notis-notes/package.json +0 -35
  204. package/dist/scaffolds/notis-notes/packages/sdk/package.json +0 -36
  205. package/dist/scaffolds/notis-notes/packages/sdk/src/components/DocumentEditor.tsx +0 -93
  206. package/dist/scaffolds/notis-notes/packages/sdk/src/components/Markdown.tsx +0 -60
  207. package/dist/scaffolds/notis-notes/packages/sdk/src/components/MultiSelectActionBar.tsx +0 -278
  208. package/dist/scaffolds/notis-notes/packages/sdk/src/components/MultiSelectCheckbox.tsx +0 -91
  209. package/dist/scaffolds/notis-notes/packages/sdk/src/components/MultiSelectDragOverlay.tsx +0 -39
  210. package/dist/scaffolds/notis-notes/packages/sdk/src/config.ts +0 -234
  211. package/dist/scaffolds/notis-notes/packages/sdk/src/documents.ts +0 -250
  212. package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useBackend.ts +0 -41
  213. package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useCloudComputer.ts +0 -97
  214. package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useDatabaseSchema.ts +0 -85
  215. package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useDatabaseSubscription.ts +0 -76
  216. package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useDocument.ts +0 -78
  217. package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useDocuments.ts +0 -121
  218. package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useHandover.ts +0 -75
  219. package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useMultiSelect.ts +0 -539
  220. package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useNotis.ts +0 -34
  221. package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useNotisNavigation.ts +0 -49
  222. package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useTool.ts +0 -64
  223. package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useTools.ts +0 -56
  224. package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useTopBarSearch.ts +0 -73
  225. package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useUpsertDocument.ts +0 -95
  226. package/dist/scaffolds/notis-notes/packages/sdk/src/index.ts +0 -100
  227. package/dist/scaffolds/notis-notes/packages/sdk/src/provider.tsx +0 -43
  228. package/dist/scaffolds/notis-notes/packages/sdk/src/runtime.ts +0 -351
  229. package/dist/scaffolds/notis-notes/packages/sdk/src/styles.css +0 -186
  230. package/dist/scaffolds/notis-notes/packages/sdk/src/ui.ts +0 -15
  231. package/dist/scaffolds/notis-notes/packages/sdk/src/vite.ts +0 -56
  232. package/dist/scaffolds/notis-notes/packages/sdk/tsconfig.json +0 -15
  233. package/dist/scaffolds/notis-notes/postcss.config.mjs +0 -8
  234. package/dist/scaffolds/notis-notes/tailwind.config.ts +0 -58
  235. package/dist/scaffolds/notis-notes/tsconfig.json +0 -23
  236. package/dist/scaffolds/notis-notes/vite.config.ts +0 -10
  237. package/dist/scaffolds/notis-random/CHANGELOG.md +0 -15
  238. package/dist/scaffolds/notis-random/README.md +0 -33
  239. package/dist/scaffolds/notis-random/app/globals.css +0 -11
  240. package/dist/scaffolds/notis-random/app/history/page.tsx +0 -67
  241. package/dist/scaffolds/notis-random/app/layout.tsx +0 -7
  242. package/dist/scaffolds/notis-random/app/page.tsx +0 -289
  243. package/dist/scaffolds/notis-random/components/ui/button.tsx +0 -50
  244. package/dist/scaffolds/notis-random/components/ui/card.tsx +0 -16
  245. package/dist/scaffolds/notis-random/components/ui/input.tsx +0 -23
  246. package/dist/scaffolds/notis-random/components.json +0 -20
  247. package/dist/scaffolds/notis-random/index.html +0 -12
  248. package/dist/scaffolds/notis-random/lib/notis-tools.ts +0 -128
  249. package/dist/scaffolds/notis-random/lib/rng.ts +0 -202
  250. package/dist/scaffolds/notis-random/lib/roll-record.ts +0 -189
  251. package/dist/scaffolds/notis-random/lib/utils.ts +0 -25
  252. package/dist/scaffolds/notis-random/metadata/screenshot-1.png +0 -0
  253. package/dist/scaffolds/notis-random/metadata/screenshot-2.png +0 -0
  254. package/dist/scaffolds/notis-random/metadata/screenshot-3.png +0 -0
  255. package/dist/scaffolds/notis-random/metadata/screenshot-4.png +0 -0
  256. package/dist/scaffolds/notis-random/metadata/screenshot-5.png +0 -0
  257. package/dist/scaffolds/notis-random/metadata/screenshot-fixtures.json +0 -753
  258. package/dist/scaffolds/notis-random/notis.config.ts +0 -86
  259. package/dist/scaffolds/notis-random/package-lock.json +0 -4513
  260. package/dist/scaffolds/notis-random/package.json +0 -36
  261. package/dist/scaffolds/notis-random/packages/sdk/package.json +0 -36
  262. package/dist/scaffolds/notis-random/packages/sdk/src/components/DocumentEditor.tsx +0 -93
  263. package/dist/scaffolds/notis-random/packages/sdk/src/components/Markdown.tsx +0 -60
  264. package/dist/scaffolds/notis-random/packages/sdk/src/components/MultiSelectActionBar.tsx +0 -278
  265. package/dist/scaffolds/notis-random/packages/sdk/src/components/MultiSelectCheckbox.tsx +0 -91
  266. package/dist/scaffolds/notis-random/packages/sdk/src/components/MultiSelectDragOverlay.tsx +0 -39
  267. package/dist/scaffolds/notis-random/packages/sdk/src/config.ts +0 -234
  268. package/dist/scaffolds/notis-random/packages/sdk/src/documents.ts +0 -250
  269. package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useBackend.ts +0 -41
  270. package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useCloudComputer.ts +0 -97
  271. package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useDatabaseSchema.ts +0 -85
  272. package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useDatabaseSubscription.ts +0 -76
  273. package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useDocument.ts +0 -78
  274. package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useDocuments.ts +0 -121
  275. package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useHandover.ts +0 -75
  276. package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useMultiSelect.ts +0 -539
  277. package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useNotis.ts +0 -34
  278. package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useNotisNavigation.ts +0 -49
  279. package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useTool.ts +0 -64
  280. package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useTools.ts +0 -56
  281. package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useTopBarSearch.ts +0 -73
  282. package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useUpsertDocument.ts +0 -95
  283. package/dist/scaffolds/notis-random/packages/sdk/src/index.ts +0 -100
  284. package/dist/scaffolds/notis-random/packages/sdk/src/provider.tsx +0 -43
  285. package/dist/scaffolds/notis-random/packages/sdk/src/runtime.ts +0 -351
  286. package/dist/scaffolds/notis-random/packages/sdk/src/styles.css +0 -186
  287. package/dist/scaffolds/notis-random/packages/sdk/src/ui.ts +0 -15
  288. package/dist/scaffolds/notis-random/packages/sdk/src/vite.ts +0 -56
  289. package/dist/scaffolds/notis-random/packages/sdk/tsconfig.json +0 -15
  290. package/dist/scaffolds/notis-random/postcss.config.mjs +0 -6
  291. package/dist/scaffolds/notis-random/src/dev-main.tsx +0 -70
  292. package/dist/scaffolds/notis-random/src/mock-runtime.ts +0 -129
  293. package/dist/scaffolds/notis-random/tailwind.config.ts +0 -50
  294. package/dist/scaffolds/notis-random/tsconfig.json +0 -23
  295. package/dist/scaffolds/notis-random/vite.config.ts +0 -11
  296. package/dist/scaffolds.json +0 -49
@@ -1,14 +1,28 @@
1
1
  import { loginWithOAuth, logoutOAuth } from '../runtime/oauth.js';
2
+ import { installLocalAgentContext } from './agents.js';
2
3
 
3
4
  async function loginHandler(ctx) {
4
5
  const result = await loginWithOAuth(ctx.runtime, ctx.options, ctx.output);
5
6
  if (result.agentAuthorization) {
7
+ const presentation = loginAgentAuthorizationPresentation(result.agentAuthorization);
6
8
  return ctx.output.emitSuccess({
7
9
  command: 'login',
8
10
  data: result.agentAuthorization,
9
- humanSummary: 'Open the authorization URL in a browser to continue.',
11
+ ...presentation,
10
12
  });
11
13
  }
14
+ let agentSetup = [];
15
+ const setupWarnings = [];
16
+ try {
17
+ // Login may add static CLI guidance, but automatic capture is a separate
18
+ // explicit choice made through `notis agents install`.
19
+ agentSetup = installLocalAgentContext(ctx, {
20
+ onlyExisting: true,
21
+ memoryHooks: null,
22
+ });
23
+ } catch (error) {
24
+ setupWarnings.push(`Notis CLI login succeeded, but local agent context setup failed: ${error.message}`);
25
+ }
12
26
  return ctx.output.emitSuccess({
13
27
  command: 'login',
14
28
  data: {
@@ -20,14 +34,38 @@ async function loginHandler(ctx) {
20
34
  scopes: result.profile.oauth_scopes,
21
35
  access_expires_at: result.profile.oauth_access_expires_at,
22
36
  refresh_expires_at: result.profile.oauth_refresh_expires_at,
37
+ agent_setup: agentSetup,
23
38
  },
24
39
  humanSummary: `Notis CLI is authorized for profile "${ctx.runtime.profileName}".`,
40
+ warnings: setupWarnings,
25
41
  hints: [
26
42
  { command: 'notis profile list', reason: 'See every account this machine can switch between' },
43
+ { command: 'notis agents install', reason: 'Install or refresh Notis context for Codex and Claude Code' },
27
44
  ],
28
45
  });
29
46
  }
30
47
 
48
+ export function loginAgentAuthorizationPresentation(authorization) {
49
+ const browserHandOff = authorization?.hand_off === 'browser_callback';
50
+ const nextCommand = browserHandOff
51
+ ? authorization?.confirm_command
52
+ : authorization?.redeem_command;
53
+ return {
54
+ humanSummary: browserHandOff
55
+ ? 'Give the user the authorization URL. Signing in there completes it — there is no code to ask them for.'
56
+ : 'Give the user the authorization URL, then ask them for the code it shows.',
57
+ hints: nextCommand
58
+ ? [{
59
+ command: nextCommand,
60
+ reason: browserHandOff
61
+ ? 'Confirm the account once they have finished in the browser'
62
+ : 'Redeem the code shown in the browser',
63
+ }]
64
+ : [],
65
+ renderHuman: () => `Authorize Notis CLI: ${authorization.authorize_url}`,
66
+ };
67
+ }
68
+
31
69
  async function logoutHandler(ctx) {
32
70
  const result = await logoutOAuth(ctx.runtime, {
33
71
  allProfiles: Boolean(ctx.options.allProfiles),
@@ -38,12 +76,24 @@ async function logoutHandler(ctx) {
38
76
  oauth_connected: false,
39
77
  cleared_profiles: result.profiles,
40
78
  },
41
- humanSummary: ctx.options.allProfiles
42
- ? 'OAuth credentials were removed from all CLI profiles.'
43
- : `OAuth credentials were removed from profile "${ctx.runtime.profileName}".`,
79
+ humanSummary: logoutHumanSummary(result, {
80
+ allProfiles: Boolean(ctx.options.allProfiles),
81
+ profileName: ctx.runtime.profileName,
82
+ }),
44
83
  });
45
84
  }
46
85
 
86
+ export function logoutHumanSummary(result, { allProfiles, profileName }) {
87
+ if (allProfiles) {
88
+ return result.profiles.length > 0
89
+ ? 'OAuth state was cleared from all connected or pending CLI profiles.'
90
+ : 'No OAuth credentials were connected in any CLI profile.';
91
+ }
92
+ return result.profiles.includes(profileName)
93
+ ? `OAuth state was cleared for profile "${profileName}", including any pending authorization.`
94
+ : `No OAuth credential was connected for profile "${profileName}".`;
95
+ }
96
+
47
97
  export const authCommandSpecs = [
48
98
  {
49
99
  command_path: ['login'],
@@ -55,8 +105,9 @@ export const authCommandSpecs = [
55
105
  options: [
56
106
  { flags: '--no-browser', description: 'Print the authorization URL without opening a browser.' },
57
107
  { flags: '--print-url', description: 'Print the authorization URL even when opening a browser.' },
58
- { flags: '--paste-code', description: 'Use the copy-paste callback for SSH and headless machines.' },
59
- { flags: '--timeout-seconds <n>', description: 'How long to wait for authorization (default 300).' },
108
+ { flags: '--mode <mode>', description: 'auto (default) hands the browser callback to a background listener when this command cannot wait; browser waits in-process; code shows a one-time code to copy.' },
109
+ { flags: '--paste-code', description: 'Alias for --mode code.' },
110
+ { flags: '--timeout-seconds <n>', description: 'Authorization lifetime in seconds (default 300 while waiting in a terminal; 1800 for detached or code hand-offs).' },
60
111
  { flags: '--scope <scope>', description: 'OAuth permission to request (repeatable).', collect: true },
61
112
  { flags: '--code <code>', description: 'Redeem the code shown in the browser after a non-interactive login.' },
62
113
  ],
@@ -66,11 +117,12 @@ export const authCommandSpecs = [
66
117
  'notis login --profile work',
67
118
  'notis login --profile beta --api-base https://api-beta.notis.ai',
68
119
  'notis login --no-browser --print-url',
69
- 'notis login --paste-code',
120
+ 'notis login --mode browser',
121
+ 'notis login --mode code',
70
122
  'notis login --code 4f3c2b1a',
71
123
  ],
72
124
  output_schema:
73
- 'Returns credential_source, profile, api_base, user_id, scopes, and credential expiries; agent mode returns authorize_url and expires_in.',
125
+ 'Returns credential_source, profile, api_base, user_id, scopes, and credential expiries; non-blocking agent, machine-output, or non-interactive modes return authorize_url, expires_in, and hand_off ("browser_callback" needs no code, "code" must be redeemed with redeem_command).',
74
126
  mutates: true,
75
127
  idempotent: true,
76
128
  require_auth: false,
@@ -87,14 +139,15 @@ export const authCommandSpecs = [
87
139
  args_schema: {
88
140
  arguments: [],
89
141
  options: [
90
- { flags: '--all-profiles', description: 'Remove OAuth credentials from every CLI profile.' },
142
+ { flags: '--all-profiles', description: 'Clear OAuth credentials and pending authorizations from every CLI profile.' },
91
143
  ],
92
144
  },
93
145
  examples: ['notis logout', 'notis logout --profile work', 'notis logout --all-profiles'],
94
- output_schema: 'Returns oauth_connected=false and the profiles whose OAuth credentials were removed.',
146
+ output_schema: 'Returns oauth_connected=false and the profiles whose OAuth credentials or pending authorizations were cleared.',
95
147
  mutates: true,
96
148
  idempotent: true,
97
149
  require_auth: false,
150
+ allow_unknown_profile: true,
98
151
  related_commands: ['notis login', 'notis profile list'],
99
152
  backend_call: { type: 'oauth', name: 'revocation' },
100
153
  handler: logoutHandler,
@@ -7,11 +7,15 @@ import { smokeCommandSpecs } from './smoke.js';
7
7
  import { authCommandSpecs } from './auth.js';
8
8
  import { profileCommandSpecs } from './profile.js';
9
9
  import { handoverCommandSpecs } from './handover.js';
10
+ import { agentsCommandSpecs } from './agents.js';
11
+ import { skillsCommandSpecs } from './skills.js';
10
12
 
11
13
  export const GROUP_SUMMARIES = {
12
14
  apps: 'Develop, deploy, and submit Notis Apps.',
15
+ agents: 'Install Notis context into local coding agents.',
13
16
  handover: 'Hand the branch you are on to a Notis agent, hosted or your own Codex/Claude.',
14
17
  tools: 'Discover and execute generic tools exposed through Notis.',
18
+ skills: 'Keep Notis and local-agent skills synchronized.',
15
19
  profile: 'Switch between signed-in accounts and their API endpoints.',
16
20
  debug: 'Inspect effective runtime context, worker identity, and trace costs.',
17
21
  smoke: 'Run deterministic connected-service smoke tests with guaranteed cleanup.',
@@ -21,6 +25,8 @@ export const COMMAND_SPECS = [
21
25
  ...authCommandSpecs,
22
26
  ...profileCommandSpecs,
23
27
  ...onboardingCommandSpecs,
28
+ ...agentsCommandSpecs,
29
+ ...skillsCommandSpecs,
24
30
  ...appsCommandSpecs,
25
31
  ...handoverCommandSpecs,
26
32
  ...toolsCommandSpecs,
@@ -11,6 +11,8 @@ import {
11
11
  } from '../runtime/profiles.js';
12
12
  import { ensureFreshOAuthCredential, loginWithOAuth } from '../runtime/oauth.js';
13
13
  import { runToolCommand } from './helpers.js';
14
+ import { loginAgentAuthorizationPresentation } from './auth.js';
15
+ import { installLocalAgentContext } from './agents.js';
14
16
 
15
17
  const HERE = dirname(fileURLToPath(import.meta.url));
16
18
  const BUNDLED_BRIEF_PATH = join(HERE, '..', '..', 'skills', 'notis-onboarding', 'BRIEF.md');
@@ -86,6 +88,44 @@ function isAuthenticated(runtime) {
86
88
  && !credentialIsExpired(runtime, getProfile(loadConfig(), runtime.profileName));
87
89
  }
88
90
 
91
+ function renderAgentSetup(results) {
92
+ const configured = (results || []).filter((result) => result?.agent && result?.instructions);
93
+ if (!configured.length) return '';
94
+ const lines = configured.map((result) => {
95
+ const label = result.agent === 'claude-code' ? 'Claude Code' : 'Codex';
96
+ const instructions = result.instructions?.status || 'skipped';
97
+ const memoryStatus = result.memory_hook?.status || 'skipped';
98
+ const memory = memoryStatus === 'preserved'
99
+ ? 'memory hooks not changed'
100
+ : `memory recall and capture ${memoryStatus}`;
101
+ return `- ${label}: instructions ${instructions}; ${memory}`;
102
+ });
103
+ if (configured.some((result) => (
104
+ result.agent === 'codex'
105
+ && ['installed', 'updated', 'unchanged'].includes(result.memory_hook?.status)
106
+ ))) {
107
+ lines.push('- Codex: open /hooks once and trust the Notis hooks before they can run.');
108
+ }
109
+ return ['## Coding-agent setup', ...lines].join('\n');
110
+ }
111
+
112
+ function agentSetupHints(results) {
113
+ const hints = [];
114
+ if ((results || []).some((result) => (
115
+ result.agent === 'codex'
116
+ && ['installed', 'updated', 'unchanged'].includes(result.memory_hook?.status)
117
+ ))) {
118
+ hints.push({ message: 'In Codex, open /hooks once and trust the Notis hooks.' });
119
+ }
120
+ if ((results || []).some((result) => result.status === 'not_detected')) {
121
+ hints.push({ command: 'notis agents install', reason: 'Configure Codex and Claude Code later if they were not detected now' });
122
+ }
123
+ if ((results || []).some((result) => result.memory_hook?.status === 'preserved')) {
124
+ hints.push({ command: 'notis agents install', reason: 'Explicitly enable Notis memory recall and completed-turn capture' });
125
+ }
126
+ return hints;
127
+ }
128
+
89
129
  /**
90
130
  * What an authenticated `start` reports.
91
131
  *
@@ -105,9 +145,20 @@ async function authenticatedResult(ctx) {
105
145
  onboarding_complete: onboardingComplete,
106
146
  ...(state ? { known_settings: state.settings, missing_settings: state.missingSettings } : {}),
107
147
  };
148
+ try {
149
+ base.agent_setup = installLocalAgentContext(ctx, {
150
+ onlyExisting: true,
151
+ memoryHooks: null,
152
+ });
153
+ } catch {
154
+ // Authentication and onboarding remain usable if a local vendor config is
155
+ // malformed or read-only. `notis agents install` reports the exact path.
156
+ base.agent_setup = [];
157
+ }
108
158
 
109
159
  if (onboardingComplete) {
110
160
  const name = state?.settings?.full_name;
161
+ const setupSummary = renderAgentSetup(base.agent_setup);
111
162
  return ctx.output.emitSuccess({
112
163
  command: 'start',
113
164
  data: { ...base, brief: null, brief_source: null },
@@ -120,20 +171,27 @@ async function authenticatedResult(ctx) {
120
171
  '',
121
172
  'This account has already completed onboarding. Do not run an onboarding',
122
173
  'flow and do not call LOCAL_NOTIS_COMPLETE_TUTORIAL.',
174
+ ...(setupSummary ? ['', setupSummary] : []),
123
175
  ].join('\n'),
124
176
  hints: [
125
177
  { command: 'notis whoami', reason: 'Show the account and its connected toolkits' },
126
178
  { command: 'notis tools search "<what you need>"', reason: 'Find a tool and get on with the task' },
179
+ ...agentSetupHints(base.agent_setup),
127
180
  ],
128
181
  });
129
182
  }
130
183
 
131
184
  const brief = await fetchBrief(ctx.runtime.apiBase, ctx.runtime.timeoutMs);
185
+ const setupSummary = renderAgentSetup(base.agent_setup);
132
186
  return ctx.output.emitSuccess({
133
187
  command: 'start',
134
188
  data: { ...base, brief: brief.markdown, brief_source: brief.source },
135
189
  humanSummary: `Notis CLI is authenticated for profile "${ctx.runtime.profileName}". Onboarding is not complete.`,
136
- renderHuman: () => brief.markdown || 'Notis CLI is authenticated.',
190
+ renderHuman: () => [
191
+ brief.markdown || 'Notis CLI is authenticated.',
192
+ setupSummary,
193
+ ].filter(Boolean).join('\n\n'),
194
+ hints: agentSetupHints(base.agent_setup),
137
195
  });
138
196
  }
139
197
 
@@ -166,8 +224,13 @@ async function startHandler(ctx) {
166
224
  // Browser authorization is the whole signup path: it creates the account when
167
225
  // the address is new and authorizes this machine either way. There is nothing
168
226
  // for the CLI to collect up front, and nothing to wait on afterwards.
169
- const authorization = await loginWithOAuth(runtime, { browser: true }, output);
227
+ const authorization = await loginWithOAuth(
228
+ runtime,
229
+ { browser: true, mode: options.mode, reusePersistedCredential: true },
230
+ output,
231
+ );
170
232
  if (authorization?.agentAuthorization) {
233
+ const presentation = loginAgentAuthorizationPresentation(authorization.agentAuthorization);
171
234
  return output.emitSuccess({
172
235
  command: 'start',
173
236
  data: {
@@ -175,8 +238,7 @@ async function startHandler(ctx) {
175
238
  profile: runtime.profileName,
176
239
  ...authorization.agentAuthorization,
177
240
  },
178
- humanSummary: 'Open the authorization URL to create or access the Notis account.',
179
- renderHuman: () => `Authorize Notis CLI: ${authorization.agentAuthorization.authorize_url}`,
241
+ ...presentation,
180
242
  });
181
243
  }
182
244
 
@@ -205,16 +267,18 @@ export const onboardingCommandSpecs = [
205
267
  arguments: [],
206
268
  options: [
207
269
  { flags: '--brief-only', description: 'Print the onboarding brief for an already-signed-in profile.' },
270
+ { flags: '--mode <mode>', description: 'auto (default) hands the browser callback to a background listener when this command cannot wait; browser waits in-process; code shows a one-time code to copy.' },
208
271
  ],
209
272
  },
210
273
  examples: [
211
274
  'notis start',
212
275
  'notis start --profile work',
276
+ 'notis start --mode browser',
213
277
  'notis start --brief-only',
214
278
  'notis start --json',
215
279
  ],
216
280
  output_schema:
217
- 'Returns {authenticated, profile, api_base, onboarding_complete, known_settings, missing_settings, brief, brief_source} once signed in — brief is null when onboarding_complete is true — or {authorize_url, expires_in, redeem_command} while waiting for browser authorization.',
281
+ 'Returns {authenticated, profile, api_base, onboarding_complete, known_settings, missing_settings, agent_setup, brief, brief_source} once signed in — brief is null when onboarding_complete is true — or {authorize_url, expires_in, hand_off, ...} while waiting for browser authorization. hand_off is "browser_callback" when signing in finishes the login by itself (re-run confirm_command to see it land) or "code" when the user must be asked for the code the page shows and it redeemed with redeem_command.',
218
282
  mutates: true,
219
283
  idempotent: true,
220
284
  require_auth: false,
@@ -0,0 +1,56 @@
1
+ import { getJwtSubject } from '../runtime/profiles.js';
2
+ import { reconcileAllSkills } from '../runtime/sync-skills.js';
3
+
4
+ async function loadSkillSyncEngine() {
5
+ return import('../../dist/skill-sync/index.js');
6
+ }
7
+
8
+ async function syncSkillsHandler(ctx) {
9
+ const userId = ctx.runtime.oauthUserId || getJwtSubject(ctx.runtime.jwt);
10
+ const { runSkillSync } = await loadSkillSyncEngine();
11
+ const result = await reconcileAllSkills({
12
+ serverUrl: ctx.runtime.apiBase,
13
+ jwt: ctx.runtime.jwt,
14
+ userId,
15
+ honorSyncEnabled: Boolean(ctx.options.electronRepeat),
16
+ runAccountSync: runSkillSync,
17
+ });
18
+
19
+ return ctx.output.emitSuccess({
20
+ command: 'skills sync',
21
+ data: result,
22
+ humanSummary: result.syncEnabled
23
+ ? `Synced account skills and kept ${result.baseSkills.length} base skills current.`
24
+ : `Automatic Desktop sync is off; kept ${result.baseSkills.length} base skills current.`,
25
+ renderHuman: () => result.syncEnabled
26
+ ? `Skills synced. Base skills current: ${result.baseSkills.join(', ')}.`
27
+ : `Automatic Desktop sync is off. Base skills remain current: ${result.baseSkills.join(', ')}.`,
28
+ });
29
+ }
30
+
31
+ export const skillsCommandSpecs = [
32
+ {
33
+ command_path: ['skills', 'sync'],
34
+ summary: 'Synchronize account skills and keep the three Notis base skills current.',
35
+ when_to_use:
36
+ 'Run manually whenever local agent skills should be reconciled. Manual runs ignore the Desktop automatic-sync preference.',
37
+ args_schema: {
38
+ arguments: [],
39
+ options: [
40
+ {
41
+ flags: '--electron-repeat',
42
+ description: 'Honor the automatic Desktop sync preference (used by Notis Desktop).',
43
+ },
44
+ ],
45
+ },
46
+ examples: ['notis skills sync', 'notis skills sync --json'],
47
+ output_schema:
48
+ 'Returns account sync counts plus baseSkills, baseInstalled, baseLinked, and baseBackups.',
49
+ mutates: true,
50
+ idempotent: true,
51
+ require_auth: true,
52
+ related_commands: ['notis login', 'notis start', 'notis doctor'],
53
+ backend_call: { type: 'local', name: 'skill_sync' },
54
+ handler: syncSkillsHandler,
55
+ },
56
+ ];
@@ -0,0 +1,126 @@
1
+ import { createHash } from 'node:crypto';
2
+ import {
3
+ existsSync,
4
+ mkdirSync,
5
+ readFileSync,
6
+ renameSync,
7
+ writeFileSync,
8
+ } from 'node:fs';
9
+ import { homedir } from 'node:os';
10
+ import { dirname, join } from 'node:path';
11
+
12
+ const MAX_SEEN_HASHES = 500;
13
+ const MAX_PROMPT_CHARS = 12_000;
14
+
15
+ function hash(value) {
16
+ return createHash('sha256').update(String(value || '')).digest('hex').slice(0, 24);
17
+ }
18
+
19
+ function statePath(sessionId, home = homedir()) {
20
+ if (!sessionId) return null;
21
+ return join(home, '.notis', 'agent-memory-hooks', `${hash(sessionId)}.json`);
22
+ }
23
+
24
+ function readState(sessionId, home) {
25
+ const filePath = statePath(sessionId, home);
26
+ if (!filePath || !existsSync(filePath)) return { seen: [], pending: null };
27
+ try {
28
+ const parsed = JSON.parse(readFileSync(filePath, 'utf-8'));
29
+ return parsed && typeof parsed === 'object'
30
+ ? {
31
+ seen: Array.isArray(parsed.seen) ? parsed.seen.filter((item) => typeof item === 'string') : [],
32
+ pending: parsed.pending && typeof parsed.pending === 'object' ? parsed.pending : null,
33
+ identity: parsed.identity && typeof parsed.identity === 'object' ? parsed.identity : null,
34
+ }
35
+ : { seen: [], pending: null };
36
+ } catch {
37
+ return { seen: [], pending: null };
38
+ }
39
+ }
40
+
41
+ function inputIdentity(input) {
42
+ const profile = typeof input?.profile_name === 'string' ? input.profile_name : '';
43
+ const account = typeof input?.account_id === 'string' ? input.account_id : '';
44
+ const apiBase = typeof input?.api_base === 'string' ? input.api_base : '';
45
+ return profile && account && apiBase ? { profile, account, apiBase } : null;
46
+ }
47
+
48
+ function identityMatches(left, right) {
49
+ return (!left && !right) || Boolean(
50
+ left && right
51
+ && left.profile === right.profile
52
+ && left.account === right.account
53
+ && left.apiBase === right.apiBase,
54
+ );
55
+ }
56
+
57
+ function stateForInput(sessionId, input, home) {
58
+ const state = readState(sessionId, home);
59
+ const identity = inputIdentity(input);
60
+ if (identityMatches(state.identity, identity)) return state;
61
+ return { seen: [], pending: null, identity };
62
+ }
63
+
64
+ function writeState(sessionId, state, home) {
65
+ const filePath = statePath(sessionId, home);
66
+ if (!filePath) return;
67
+ mkdirSync(dirname(filePath), { recursive: true, mode: 0o700 });
68
+ const temporaryPath = `${filePath}.${process.pid}.${Date.now()}.tmp`;
69
+ writeFileSync(temporaryPath, `${JSON.stringify(state)}\n`, { mode: 0o600 });
70
+ renameSync(temporaryPath, filePath);
71
+ }
72
+
73
+ export function rememberPendingTurn(input, home = homedir()) {
74
+ const sessionId = input?.session_id;
75
+ const prompt = typeof input?.prompt === 'string' ? input.prompt.trim() : '';
76
+ if (!sessionId || !prompt || ['/', '!', '#'].includes(prompt[0])) return;
77
+ const state = stateForInput(sessionId, input, home);
78
+ state.pending = {
79
+ prompt: prompt.slice(0, MAX_PROMPT_CHARS),
80
+ turn_id: typeof input?.turn_id === 'string' ? input.turn_id : null,
81
+ cwd: typeof input?.cwd === 'string' ? input.cwd : null,
82
+ recorded_at: new Date().toISOString(),
83
+ };
84
+ writeState(sessionId, state, home);
85
+ }
86
+
87
+ export function pendingTurn(input, home = homedir()) {
88
+ const sessionId = input?.session_id;
89
+ const state = readState(sessionId, home);
90
+ const identity = inputIdentity(input);
91
+ if (!identityMatches(state.identity, identity)) {
92
+ if (sessionId) writeState(sessionId, { seen: [], pending: null, identity }, home);
93
+ return null;
94
+ }
95
+ if (!state.pending) return null;
96
+ if (state.pending.turn_id && input?.turn_id && state.pending.turn_id !== input.turn_id) return null;
97
+ return state.pending;
98
+ }
99
+
100
+ export function completePendingTurn(input, home = homedir()) {
101
+ const sessionId = input?.session_id;
102
+ if (!sessionId) return;
103
+ const filePath = statePath(sessionId, home);
104
+ if (!filePath || !existsSync(filePath)) return;
105
+ const state = readState(sessionId, home);
106
+ if (!state.pending) return;
107
+ state.pending = null;
108
+ writeState(sessionId, state, home);
109
+ }
110
+
111
+ export function freshRecallItems(input, items, home = homedir()) {
112
+ const sessionId = input?.session_id;
113
+ if (!sessionId) return items;
114
+ const state = stateForInput(sessionId, input, home);
115
+ const seen = new Set(state.seen);
116
+ const fresh = [];
117
+ for (const item of items) {
118
+ const fingerprint = hash(item);
119
+ if (seen.has(fingerprint)) continue;
120
+ seen.add(fingerprint);
121
+ fresh.push(item);
122
+ }
123
+ state.seen = [...seen].slice(-MAX_SEEN_HASHES);
124
+ writeState(sessionId, state, home);
125
+ return fresh;
126
+ }