@notis_ai/cli 0.2.14 → 0.2.16

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 (299) hide show
  1. package/README.md +83 -21
  2. package/dist/agent-hooks/notis-agent-hook.mjs +20168 -0
  3. package/{skills → dist/base-skills}/notis-apps/SKILL.md +167 -72
  4. package/{skills → dist/base-skills}/notis-cli/SKILL.md +86 -28
  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 +39 -18
  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 +1021 -424
  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-process-identity.js +111 -0
  25. package/src/runtime/app-dev-roots.js +284 -0
  26. package/src/runtime/app-dev-server.js +447 -29
  27. package/src/runtime/app-dev-sessions.js +105 -145
  28. package/src/runtime/app-platform.js +1314 -166
  29. package/src/runtime/app-registry-scaffolds.js +367 -0
  30. package/src/runtime/base-skills.d.ts +20 -0
  31. package/src/runtime/base-skills.js +167 -0
  32. package/src/runtime/login-listener.js +15 -0
  33. package/src/runtime/oauth.js +1506 -141
  34. package/src/runtime/output.js +7 -0
  35. package/src/runtime/ports.js +16 -0
  36. package/src/runtime/profiles.js +19 -16
  37. package/src/runtime/skill-sync/cloud-client.ts +96 -0
  38. package/src/runtime/skill-sync/index.ts +644 -0
  39. package/src/runtime/skill-sync/local-scanner.ts +1046 -0
  40. package/src/runtime/skill-sync/symlink-manager.ts +383 -0
  41. package/src/runtime/skill-sync/sync-plan.ts +22 -0
  42. package/src/runtime/skill-sync/types.ts +103 -0
  43. package/src/runtime/skill-sync/write-cloud-skill.ts +50 -0
  44. package/src/runtime/store-screenshot.js +6 -1
  45. package/src/runtime/sync-skills.d.ts +37 -0
  46. package/src/runtime/sync-skills.js +231 -0
  47. package/template/app/layout.tsx +5 -1
  48. package/template/notis.config.ts +1 -0
  49. package/template/packages/sdk/package.json +2 -1
  50. package/template/packages/sdk/src/components/DocumentEditor.tsx +13 -3
  51. package/template/packages/sdk/src/components/MarkdownEditor.tsx +121 -0
  52. package/template/packages/sdk/src/components/MultiSelectActionBar.tsx +43 -119
  53. package/template/packages/sdk/src/components/MultiSelectCheckbox.tsx +5 -1
  54. package/template/packages/sdk/src/components/MultiSelectDragOverlay.tsx +1 -1
  55. package/template/packages/sdk/src/components/NotisSelectionBoundary.tsx +55 -0
  56. package/template/packages/sdk/src/components/ShortcutHints.tsx +56 -0
  57. package/template/packages/sdk/src/config.ts +25 -0
  58. package/template/packages/sdk/src/documents.ts +7 -1
  59. package/template/packages/sdk/src/hooks/useActiveResource.ts +19 -0
  60. package/template/packages/sdk/src/hooks/useCollectionInteractions.ts +726 -0
  61. package/template/packages/sdk/src/hooks/useHandover.ts +3 -0
  62. package/template/packages/sdk/src/hooks/useMultiSelect.ts +44 -482
  63. package/template/packages/sdk/src/hooks/useNotis.ts +3 -0
  64. package/template/packages/sdk/src/hooks/useNotisNavigation.ts +7 -4
  65. package/template/packages/sdk/src/hooks/useTool.ts +5 -4
  66. package/template/packages/sdk/src/index.ts +47 -2
  67. package/template/packages/sdk/src/interactions/actions.ts +46 -0
  68. package/template/packages/sdk/src/interactions/shortcuts.tsx +634 -0
  69. package/template/packages/sdk/src/interactions.ts +41 -0
  70. package/template/packages/sdk/src/provider.tsx +2 -1
  71. package/template/packages/sdk/src/runtime.ts +92 -4
  72. package/dist/scaffolds/notis-database/CHANGELOG.md +0 -5
  73. package/dist/scaffolds/notis-database/app/globals.css +0 -44
  74. package/dist/scaffolds/notis-database/app/layout.tsx +0 -6
  75. package/dist/scaffolds/notis-database/app/page.tsx +0 -1088
  76. package/dist/scaffolds/notis-database/components/ui/badge.tsx +0 -28
  77. package/dist/scaffolds/notis-database/components/ui/button.tsx +0 -53
  78. package/dist/scaffolds/notis-database/components/ui/card.tsx +0 -56
  79. package/dist/scaffolds/notis-database/components/ui/table.tsx +0 -120
  80. package/dist/scaffolds/notis-database/components.json +0 -20
  81. package/dist/scaffolds/notis-database/index.html +0 -12
  82. package/dist/scaffolds/notis-database/lib/types.ts +0 -132
  83. package/dist/scaffolds/notis-database/lib/utils.ts +0 -6
  84. package/dist/scaffolds/notis-database/metadata/screenshot-1.png +0 -0
  85. package/dist/scaffolds/notis-database/metadata/screenshot-2.png +0 -0
  86. package/dist/scaffolds/notis-database/metadata/screenshot-3.png +0 -0
  87. package/dist/scaffolds/notis-database/metadata/screenshot-4.png +0 -0
  88. package/dist/scaffolds/notis-database/metadata/screenshot-5.png +0 -0
  89. package/dist/scaffolds/notis-database/metadata/screenshot-fixtures.json +0 -1839
  90. package/dist/scaffolds/notis-database/notis.config.ts +0 -73
  91. package/dist/scaffolds/notis-database/package-lock.json +0 -3935
  92. package/dist/scaffolds/notis-database/package.json +0 -32
  93. package/dist/scaffolds/notis-database/packages/sdk/package.json +0 -36
  94. package/dist/scaffolds/notis-database/packages/sdk/src/components/DocumentEditor.tsx +0 -93
  95. package/dist/scaffolds/notis-database/packages/sdk/src/components/Markdown.tsx +0 -60
  96. package/dist/scaffolds/notis-database/packages/sdk/src/components/MultiSelectActionBar.tsx +0 -278
  97. package/dist/scaffolds/notis-database/packages/sdk/src/components/MultiSelectCheckbox.tsx +0 -91
  98. package/dist/scaffolds/notis-database/packages/sdk/src/components/MultiSelectDragOverlay.tsx +0 -39
  99. package/dist/scaffolds/notis-database/packages/sdk/src/config.ts +0 -234
  100. package/dist/scaffolds/notis-database/packages/sdk/src/documents.ts +0 -250
  101. package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useBackend.ts +0 -41
  102. package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useCloudComputer.ts +0 -97
  103. package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useDatabaseSchema.ts +0 -85
  104. package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useDatabaseSubscription.ts +0 -76
  105. package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useDocument.ts +0 -78
  106. package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useDocuments.ts +0 -121
  107. package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useHandover.ts +0 -75
  108. package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useMultiSelect.ts +0 -539
  109. package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useNotis.ts +0 -34
  110. package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useNotisNavigation.ts +0 -49
  111. package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useTool.ts +0 -64
  112. package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useTools.ts +0 -56
  113. package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useTopBarSearch.ts +0 -73
  114. package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useUpsertDocument.ts +0 -95
  115. package/dist/scaffolds/notis-database/packages/sdk/src/index.ts +0 -100
  116. package/dist/scaffolds/notis-database/packages/sdk/src/provider.tsx +0 -43
  117. package/dist/scaffolds/notis-database/packages/sdk/src/runtime.ts +0 -351
  118. package/dist/scaffolds/notis-database/packages/sdk/src/styles.css +0 -186
  119. package/dist/scaffolds/notis-database/packages/sdk/src/ui.ts +0 -15
  120. package/dist/scaffolds/notis-database/packages/sdk/src/vite.ts +0 -56
  121. package/dist/scaffolds/notis-database/packages/sdk/tsconfig.json +0 -15
  122. package/dist/scaffolds/notis-database/postcss.config.mjs +0 -8
  123. package/dist/scaffolds/notis-database/src/dev-main.tsx +0 -23
  124. package/dist/scaffolds/notis-database/src/mock-runtime.ts +0 -561
  125. package/dist/scaffolds/notis-database/tailwind.config.ts +0 -59
  126. package/dist/scaffolds/notis-database/tsconfig.json +0 -23
  127. package/dist/scaffolds/notis-database/vite.config.ts +0 -22
  128. package/dist/scaffolds/notis-journal/CHANGELOG.md +0 -29
  129. package/dist/scaffolds/notis-journal/app/globals.css +0 -37
  130. package/dist/scaffolds/notis-journal/app/insights/page.tsx +0 -513
  131. package/dist/scaffolds/notis-journal/app/journal-core.tsx +0 -362
  132. package/dist/scaffolds/notis-journal/app/journal-ui.tsx +0 -337
  133. package/dist/scaffolds/notis-journal/app/layout.tsx +0 -6
  134. package/dist/scaffolds/notis-journal/app/page.tsx +0 -486
  135. package/dist/scaffolds/notis-journal/components/ui/badge.tsx +0 -28
  136. package/dist/scaffolds/notis-journal/components/ui/button.tsx +0 -53
  137. package/dist/scaffolds/notis-journal/components/ui/card.tsx +0 -56
  138. package/dist/scaffolds/notis-journal/components.json +0 -20
  139. package/dist/scaffolds/notis-journal/index.html +0 -12
  140. package/dist/scaffolds/notis-journal/lib/utils.ts +0 -6
  141. package/dist/scaffolds/notis-journal/metadata/screenshot-1.png +0 -0
  142. package/dist/scaffolds/notis-journal/metadata/screenshot-2.png +0 -0
  143. package/dist/scaffolds/notis-journal/metadata/screenshot-3.png +0 -0
  144. package/dist/scaffolds/notis-journal/metadata/screenshot-4.png +0 -0
  145. package/dist/scaffolds/notis-journal/metadata/screenshot-5.png +0 -0
  146. package/dist/scaffolds/notis-journal/metadata/screenshot-6.png +0 -0
  147. package/dist/scaffolds/notis-journal/metadata/screenshot-fixtures.json +0 -132
  148. package/dist/scaffolds/notis-journal/notis.config.ts +0 -93
  149. package/dist/scaffolds/notis-journal/package-lock.json +0 -4615
  150. package/dist/scaffolds/notis-journal/package.json +0 -34
  151. package/dist/scaffolds/notis-journal/packages/sdk/package.json +0 -36
  152. package/dist/scaffolds/notis-journal/packages/sdk/src/components/DocumentEditor.tsx +0 -93
  153. package/dist/scaffolds/notis-journal/packages/sdk/src/components/Markdown.tsx +0 -60
  154. package/dist/scaffolds/notis-journal/packages/sdk/src/components/MultiSelectActionBar.tsx +0 -278
  155. package/dist/scaffolds/notis-journal/packages/sdk/src/components/MultiSelectCheckbox.tsx +0 -91
  156. package/dist/scaffolds/notis-journal/packages/sdk/src/components/MultiSelectDragOverlay.tsx +0 -39
  157. package/dist/scaffolds/notis-journal/packages/sdk/src/config.ts +0 -234
  158. package/dist/scaffolds/notis-journal/packages/sdk/src/documents.ts +0 -250
  159. package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useBackend.ts +0 -41
  160. package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useCloudComputer.ts +0 -97
  161. package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useDatabaseSchema.ts +0 -85
  162. package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useDatabaseSubscription.ts +0 -76
  163. package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useDocument.ts +0 -78
  164. package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useDocuments.ts +0 -121
  165. package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useHandover.ts +0 -75
  166. package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useMultiSelect.ts +0 -539
  167. package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useNotis.ts +0 -34
  168. package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useNotisNavigation.ts +0 -49
  169. package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useTool.ts +0 -64
  170. package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useTools.ts +0 -56
  171. package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useTopBarSearch.ts +0 -73
  172. package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useUpsertDocument.ts +0 -95
  173. package/dist/scaffolds/notis-journal/packages/sdk/src/index.ts +0 -100
  174. package/dist/scaffolds/notis-journal/packages/sdk/src/provider.tsx +0 -43
  175. package/dist/scaffolds/notis-journal/packages/sdk/src/runtime.ts +0 -351
  176. package/dist/scaffolds/notis-journal/packages/sdk/src/styles.css +0 -186
  177. package/dist/scaffolds/notis-journal/packages/sdk/src/ui.ts +0 -15
  178. package/dist/scaffolds/notis-journal/packages/sdk/src/vite.ts +0 -56
  179. package/dist/scaffolds/notis-journal/packages/sdk/tsconfig.json +0 -15
  180. package/dist/scaffolds/notis-journal/postcss.config.mjs +0 -8
  181. package/dist/scaffolds/notis-journal/skills/journal-onboarding/SKILL.md +0 -120
  182. package/dist/scaffolds/notis-journal/src/dev-main.tsx +0 -58
  183. package/dist/scaffolds/notis-journal/src/mock-runtime.ts +0 -199
  184. package/dist/scaffolds/notis-journal/tailwind.config.ts +0 -58
  185. package/dist/scaffolds/notis-journal/tsconfig.json +0 -23
  186. package/dist/scaffolds/notis-journal/vite.config.ts +0 -10
  187. package/dist/scaffolds/notis-notes/CHANGELOG.md +0 -5
  188. package/dist/scaffolds/notis-notes/app/globals.css +0 -3
  189. package/dist/scaffolds/notis-notes/app/layout.tsx +0 -6
  190. package/dist/scaffolds/notis-notes/app/page.tsx +0 -1943
  191. package/dist/scaffolds/notis-notes/app/phosphor-icons.ts +0 -596
  192. package/dist/scaffolds/notis-notes/components/ui/badge.tsx +0 -28
  193. package/dist/scaffolds/notis-notes/components/ui/button.tsx +0 -53
  194. package/dist/scaffolds/notis-notes/components/ui/card.tsx +0 -56
  195. package/dist/scaffolds/notis-notes/components.json +0 -20
  196. package/dist/scaffolds/notis-notes/lib/utils.ts +0 -6
  197. package/dist/scaffolds/notis-notes/lib/visible-properties.ts +0 -144
  198. package/dist/scaffolds/notis-notes/metadata/screenshot-1.png +0 -0
  199. package/dist/scaffolds/notis-notes/metadata/screenshot-2.png +0 -0
  200. package/dist/scaffolds/notis-notes/metadata/screenshot-3.png +0 -0
  201. package/dist/scaffolds/notis-notes/metadata/screenshot-4.png +0 -0
  202. package/dist/scaffolds/notis-notes/metadata/screenshot-5.png +0 -0
  203. package/dist/scaffolds/notis-notes/metadata/screenshot-fixtures.json +0 -752
  204. package/dist/scaffolds/notis-notes/notis.config.ts +0 -80
  205. package/dist/scaffolds/notis-notes/package-lock.json +0 -4636
  206. package/dist/scaffolds/notis-notes/package.json +0 -35
  207. package/dist/scaffolds/notis-notes/packages/sdk/package.json +0 -36
  208. package/dist/scaffolds/notis-notes/packages/sdk/src/components/DocumentEditor.tsx +0 -93
  209. package/dist/scaffolds/notis-notes/packages/sdk/src/components/Markdown.tsx +0 -60
  210. package/dist/scaffolds/notis-notes/packages/sdk/src/components/MultiSelectActionBar.tsx +0 -278
  211. package/dist/scaffolds/notis-notes/packages/sdk/src/components/MultiSelectCheckbox.tsx +0 -91
  212. package/dist/scaffolds/notis-notes/packages/sdk/src/components/MultiSelectDragOverlay.tsx +0 -39
  213. package/dist/scaffolds/notis-notes/packages/sdk/src/config.ts +0 -234
  214. package/dist/scaffolds/notis-notes/packages/sdk/src/documents.ts +0 -250
  215. package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useBackend.ts +0 -41
  216. package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useCloudComputer.ts +0 -97
  217. package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useDatabaseSchema.ts +0 -85
  218. package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useDatabaseSubscription.ts +0 -76
  219. package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useDocument.ts +0 -78
  220. package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useDocuments.ts +0 -121
  221. package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useHandover.ts +0 -75
  222. package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useMultiSelect.ts +0 -539
  223. package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useNotis.ts +0 -34
  224. package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useNotisNavigation.ts +0 -49
  225. package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useTool.ts +0 -64
  226. package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useTools.ts +0 -56
  227. package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useTopBarSearch.ts +0 -73
  228. package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useUpsertDocument.ts +0 -95
  229. package/dist/scaffolds/notis-notes/packages/sdk/src/index.ts +0 -100
  230. package/dist/scaffolds/notis-notes/packages/sdk/src/provider.tsx +0 -43
  231. package/dist/scaffolds/notis-notes/packages/sdk/src/runtime.ts +0 -351
  232. package/dist/scaffolds/notis-notes/packages/sdk/src/styles.css +0 -186
  233. package/dist/scaffolds/notis-notes/packages/sdk/src/ui.ts +0 -15
  234. package/dist/scaffolds/notis-notes/packages/sdk/src/vite.ts +0 -56
  235. package/dist/scaffolds/notis-notes/packages/sdk/tsconfig.json +0 -15
  236. package/dist/scaffolds/notis-notes/postcss.config.mjs +0 -8
  237. package/dist/scaffolds/notis-notes/tailwind.config.ts +0 -58
  238. package/dist/scaffolds/notis-notes/tsconfig.json +0 -23
  239. package/dist/scaffolds/notis-notes/vite.config.ts +0 -10
  240. package/dist/scaffolds/notis-random/CHANGELOG.md +0 -15
  241. package/dist/scaffolds/notis-random/README.md +0 -33
  242. package/dist/scaffolds/notis-random/app/globals.css +0 -11
  243. package/dist/scaffolds/notis-random/app/history/page.tsx +0 -67
  244. package/dist/scaffolds/notis-random/app/layout.tsx +0 -7
  245. package/dist/scaffolds/notis-random/app/page.tsx +0 -289
  246. package/dist/scaffolds/notis-random/components/ui/button.tsx +0 -50
  247. package/dist/scaffolds/notis-random/components/ui/card.tsx +0 -16
  248. package/dist/scaffolds/notis-random/components/ui/input.tsx +0 -23
  249. package/dist/scaffolds/notis-random/components.json +0 -20
  250. package/dist/scaffolds/notis-random/index.html +0 -12
  251. package/dist/scaffolds/notis-random/lib/notis-tools.ts +0 -128
  252. package/dist/scaffolds/notis-random/lib/rng.ts +0 -202
  253. package/dist/scaffolds/notis-random/lib/roll-record.ts +0 -189
  254. package/dist/scaffolds/notis-random/lib/utils.ts +0 -25
  255. package/dist/scaffolds/notis-random/metadata/screenshot-1.png +0 -0
  256. package/dist/scaffolds/notis-random/metadata/screenshot-2.png +0 -0
  257. package/dist/scaffolds/notis-random/metadata/screenshot-3.png +0 -0
  258. package/dist/scaffolds/notis-random/metadata/screenshot-4.png +0 -0
  259. package/dist/scaffolds/notis-random/metadata/screenshot-5.png +0 -0
  260. package/dist/scaffolds/notis-random/metadata/screenshot-fixtures.json +0 -753
  261. package/dist/scaffolds/notis-random/notis.config.ts +0 -86
  262. package/dist/scaffolds/notis-random/package-lock.json +0 -4513
  263. package/dist/scaffolds/notis-random/package.json +0 -36
  264. package/dist/scaffolds/notis-random/packages/sdk/package.json +0 -36
  265. package/dist/scaffolds/notis-random/packages/sdk/src/components/DocumentEditor.tsx +0 -93
  266. package/dist/scaffolds/notis-random/packages/sdk/src/components/Markdown.tsx +0 -60
  267. package/dist/scaffolds/notis-random/packages/sdk/src/components/MultiSelectActionBar.tsx +0 -278
  268. package/dist/scaffolds/notis-random/packages/sdk/src/components/MultiSelectCheckbox.tsx +0 -91
  269. package/dist/scaffolds/notis-random/packages/sdk/src/components/MultiSelectDragOverlay.tsx +0 -39
  270. package/dist/scaffolds/notis-random/packages/sdk/src/config.ts +0 -234
  271. package/dist/scaffolds/notis-random/packages/sdk/src/documents.ts +0 -250
  272. package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useBackend.ts +0 -41
  273. package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useCloudComputer.ts +0 -97
  274. package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useDatabaseSchema.ts +0 -85
  275. package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useDatabaseSubscription.ts +0 -76
  276. package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useDocument.ts +0 -78
  277. package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useDocuments.ts +0 -121
  278. package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useHandover.ts +0 -75
  279. package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useMultiSelect.ts +0 -539
  280. package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useNotis.ts +0 -34
  281. package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useNotisNavigation.ts +0 -49
  282. package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useTool.ts +0 -64
  283. package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useTools.ts +0 -56
  284. package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useTopBarSearch.ts +0 -73
  285. package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useUpsertDocument.ts +0 -95
  286. package/dist/scaffolds/notis-random/packages/sdk/src/index.ts +0 -100
  287. package/dist/scaffolds/notis-random/packages/sdk/src/provider.tsx +0 -43
  288. package/dist/scaffolds/notis-random/packages/sdk/src/runtime.ts +0 -351
  289. package/dist/scaffolds/notis-random/packages/sdk/src/styles.css +0 -186
  290. package/dist/scaffolds/notis-random/packages/sdk/src/ui.ts +0 -15
  291. package/dist/scaffolds/notis-random/packages/sdk/src/vite.ts +0 -56
  292. package/dist/scaffolds/notis-random/packages/sdk/tsconfig.json +0 -15
  293. package/dist/scaffolds/notis-random/postcss.config.mjs +0 -6
  294. package/dist/scaffolds/notis-random/src/dev-main.tsx +0 -70
  295. package/dist/scaffolds/notis-random/src/mock-runtime.ts +0 -129
  296. package/dist/scaffolds/notis-random/tailwind.config.ts +0 -50
  297. package/dist/scaffolds/notis-random/tsconfig.json +0 -23
  298. package/dist/scaffolds/notis-random/vite.config.ts +0 -11
  299. 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
+ }