@notis_ai/cli 0.2.0-beta.137.1 → 0.2.0-beta.144.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (294) 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/MarkdownEditor.tsx +121 -0
  50. package/template/packages/sdk/src/components/MultiSelectActionBar.tsx +37 -119
  51. package/template/packages/sdk/src/components/MultiSelectCheckbox.tsx +5 -1
  52. package/template/packages/sdk/src/components/MultiSelectDragOverlay.tsx +1 -1
  53. package/template/packages/sdk/src/components/NotisSelectionBoundary.tsx +55 -0
  54. package/template/packages/sdk/src/components/ShortcutHints.tsx +56 -0
  55. package/template/packages/sdk/src/config.ts +19 -0
  56. package/template/packages/sdk/src/hooks/useActiveResource.ts +19 -0
  57. package/template/packages/sdk/src/hooks/useCollectionInteractions.ts +709 -0
  58. package/template/packages/sdk/src/hooks/useHandover.ts +3 -0
  59. package/template/packages/sdk/src/hooks/useMultiSelect.ts +44 -482
  60. package/template/packages/sdk/src/hooks/useTool.ts +5 -4
  61. package/template/packages/sdk/src/index.ts +47 -2
  62. package/template/packages/sdk/src/interactions/actions.ts +46 -0
  63. package/template/packages/sdk/src/interactions/shortcuts.tsx +628 -0
  64. package/template/packages/sdk/src/interactions.ts +41 -0
  65. package/template/packages/sdk/src/provider.tsx +2 -1
  66. package/template/packages/sdk/src/runtime.ts +85 -3
  67. package/dist/scaffolds/notis-database/CHANGELOG.md +0 -5
  68. package/dist/scaffolds/notis-database/app/globals.css +0 -44
  69. package/dist/scaffolds/notis-database/app/layout.tsx +0 -6
  70. package/dist/scaffolds/notis-database/app/page.tsx +0 -1088
  71. package/dist/scaffolds/notis-database/components/ui/badge.tsx +0 -28
  72. package/dist/scaffolds/notis-database/components/ui/button.tsx +0 -53
  73. package/dist/scaffolds/notis-database/components/ui/card.tsx +0 -56
  74. package/dist/scaffolds/notis-database/components/ui/table.tsx +0 -120
  75. package/dist/scaffolds/notis-database/components.json +0 -20
  76. package/dist/scaffolds/notis-database/index.html +0 -12
  77. package/dist/scaffolds/notis-database/lib/types.ts +0 -132
  78. package/dist/scaffolds/notis-database/lib/utils.ts +0 -6
  79. package/dist/scaffolds/notis-database/metadata/screenshot-1.png +0 -0
  80. package/dist/scaffolds/notis-database/metadata/screenshot-2.png +0 -0
  81. package/dist/scaffolds/notis-database/metadata/screenshot-3.png +0 -0
  82. package/dist/scaffolds/notis-database/metadata/screenshot-4.png +0 -0
  83. package/dist/scaffolds/notis-database/metadata/screenshot-5.png +0 -0
  84. package/dist/scaffolds/notis-database/metadata/screenshot-fixtures.json +0 -1839
  85. package/dist/scaffolds/notis-database/notis.config.ts +0 -73
  86. package/dist/scaffolds/notis-database/package-lock.json +0 -3935
  87. package/dist/scaffolds/notis-database/package.json +0 -32
  88. package/dist/scaffolds/notis-database/packages/sdk/package.json +0 -36
  89. package/dist/scaffolds/notis-database/packages/sdk/src/components/DocumentEditor.tsx +0 -93
  90. package/dist/scaffolds/notis-database/packages/sdk/src/components/Markdown.tsx +0 -60
  91. package/dist/scaffolds/notis-database/packages/sdk/src/components/MultiSelectActionBar.tsx +0 -278
  92. package/dist/scaffolds/notis-database/packages/sdk/src/components/MultiSelectCheckbox.tsx +0 -91
  93. package/dist/scaffolds/notis-database/packages/sdk/src/components/MultiSelectDragOverlay.tsx +0 -39
  94. package/dist/scaffolds/notis-database/packages/sdk/src/config.ts +0 -234
  95. package/dist/scaffolds/notis-database/packages/sdk/src/documents.ts +0 -250
  96. package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useBackend.ts +0 -41
  97. package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useCloudComputer.ts +0 -97
  98. package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useDatabaseSchema.ts +0 -85
  99. package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useDatabaseSubscription.ts +0 -76
  100. package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useDocument.ts +0 -78
  101. package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useDocuments.ts +0 -121
  102. package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useHandover.ts +0 -75
  103. package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useMultiSelect.ts +0 -539
  104. package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useNotis.ts +0 -34
  105. package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useNotisNavigation.ts +0 -49
  106. package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useTool.ts +0 -64
  107. package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useTools.ts +0 -56
  108. package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useTopBarSearch.ts +0 -73
  109. package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useUpsertDocument.ts +0 -95
  110. package/dist/scaffolds/notis-database/packages/sdk/src/index.ts +0 -100
  111. package/dist/scaffolds/notis-database/packages/sdk/src/provider.tsx +0 -43
  112. package/dist/scaffolds/notis-database/packages/sdk/src/runtime.ts +0 -351
  113. package/dist/scaffolds/notis-database/packages/sdk/src/styles.css +0 -186
  114. package/dist/scaffolds/notis-database/packages/sdk/src/ui.ts +0 -15
  115. package/dist/scaffolds/notis-database/packages/sdk/src/vite.ts +0 -56
  116. package/dist/scaffolds/notis-database/packages/sdk/tsconfig.json +0 -15
  117. package/dist/scaffolds/notis-database/postcss.config.mjs +0 -8
  118. package/dist/scaffolds/notis-database/src/dev-main.tsx +0 -23
  119. package/dist/scaffolds/notis-database/src/mock-runtime.ts +0 -561
  120. package/dist/scaffolds/notis-database/tailwind.config.ts +0 -59
  121. package/dist/scaffolds/notis-database/tsconfig.json +0 -23
  122. package/dist/scaffolds/notis-database/vite.config.ts +0 -22
  123. package/dist/scaffolds/notis-journal/CHANGELOG.md +0 -29
  124. package/dist/scaffolds/notis-journal/app/globals.css +0 -37
  125. package/dist/scaffolds/notis-journal/app/insights/page.tsx +0 -513
  126. package/dist/scaffolds/notis-journal/app/journal-core.tsx +0 -362
  127. package/dist/scaffolds/notis-journal/app/journal-ui.tsx +0 -337
  128. package/dist/scaffolds/notis-journal/app/layout.tsx +0 -6
  129. package/dist/scaffolds/notis-journal/app/page.tsx +0 -486
  130. package/dist/scaffolds/notis-journal/components/ui/badge.tsx +0 -28
  131. package/dist/scaffolds/notis-journal/components/ui/button.tsx +0 -53
  132. package/dist/scaffolds/notis-journal/components/ui/card.tsx +0 -56
  133. package/dist/scaffolds/notis-journal/components.json +0 -20
  134. package/dist/scaffolds/notis-journal/index.html +0 -12
  135. package/dist/scaffolds/notis-journal/lib/utils.ts +0 -6
  136. package/dist/scaffolds/notis-journal/metadata/screenshot-1.png +0 -0
  137. package/dist/scaffolds/notis-journal/metadata/screenshot-2.png +0 -0
  138. package/dist/scaffolds/notis-journal/metadata/screenshot-3.png +0 -0
  139. package/dist/scaffolds/notis-journal/metadata/screenshot-4.png +0 -0
  140. package/dist/scaffolds/notis-journal/metadata/screenshot-5.png +0 -0
  141. package/dist/scaffolds/notis-journal/metadata/screenshot-6.png +0 -0
  142. package/dist/scaffolds/notis-journal/metadata/screenshot-fixtures.json +0 -132
  143. package/dist/scaffolds/notis-journal/notis.config.ts +0 -93
  144. package/dist/scaffolds/notis-journal/package-lock.json +0 -4615
  145. package/dist/scaffolds/notis-journal/package.json +0 -34
  146. package/dist/scaffolds/notis-journal/packages/sdk/package.json +0 -36
  147. package/dist/scaffolds/notis-journal/packages/sdk/src/components/DocumentEditor.tsx +0 -93
  148. package/dist/scaffolds/notis-journal/packages/sdk/src/components/Markdown.tsx +0 -60
  149. package/dist/scaffolds/notis-journal/packages/sdk/src/components/MultiSelectActionBar.tsx +0 -278
  150. package/dist/scaffolds/notis-journal/packages/sdk/src/components/MultiSelectCheckbox.tsx +0 -91
  151. package/dist/scaffolds/notis-journal/packages/sdk/src/components/MultiSelectDragOverlay.tsx +0 -39
  152. package/dist/scaffolds/notis-journal/packages/sdk/src/config.ts +0 -234
  153. package/dist/scaffolds/notis-journal/packages/sdk/src/documents.ts +0 -250
  154. package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useBackend.ts +0 -41
  155. package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useCloudComputer.ts +0 -97
  156. package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useDatabaseSchema.ts +0 -85
  157. package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useDatabaseSubscription.ts +0 -76
  158. package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useDocument.ts +0 -78
  159. package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useDocuments.ts +0 -121
  160. package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useHandover.ts +0 -75
  161. package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useMultiSelect.ts +0 -539
  162. package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useNotis.ts +0 -34
  163. package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useNotisNavigation.ts +0 -49
  164. package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useTool.ts +0 -64
  165. package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useTools.ts +0 -56
  166. package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useTopBarSearch.ts +0 -73
  167. package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useUpsertDocument.ts +0 -95
  168. package/dist/scaffolds/notis-journal/packages/sdk/src/index.ts +0 -100
  169. package/dist/scaffolds/notis-journal/packages/sdk/src/provider.tsx +0 -43
  170. package/dist/scaffolds/notis-journal/packages/sdk/src/runtime.ts +0 -351
  171. package/dist/scaffolds/notis-journal/packages/sdk/src/styles.css +0 -186
  172. package/dist/scaffolds/notis-journal/packages/sdk/src/ui.ts +0 -15
  173. package/dist/scaffolds/notis-journal/packages/sdk/src/vite.ts +0 -56
  174. package/dist/scaffolds/notis-journal/packages/sdk/tsconfig.json +0 -15
  175. package/dist/scaffolds/notis-journal/postcss.config.mjs +0 -8
  176. package/dist/scaffolds/notis-journal/skills/journal-onboarding/SKILL.md +0 -120
  177. package/dist/scaffolds/notis-journal/src/dev-main.tsx +0 -58
  178. package/dist/scaffolds/notis-journal/src/mock-runtime.ts +0 -199
  179. package/dist/scaffolds/notis-journal/tailwind.config.ts +0 -58
  180. package/dist/scaffolds/notis-journal/tsconfig.json +0 -23
  181. package/dist/scaffolds/notis-journal/vite.config.ts +0 -10
  182. package/dist/scaffolds/notis-notes/CHANGELOG.md +0 -5
  183. package/dist/scaffolds/notis-notes/app/globals.css +0 -3
  184. package/dist/scaffolds/notis-notes/app/layout.tsx +0 -6
  185. package/dist/scaffolds/notis-notes/app/page.tsx +0 -1943
  186. package/dist/scaffolds/notis-notes/app/phosphor-icons.ts +0 -596
  187. package/dist/scaffolds/notis-notes/components/ui/badge.tsx +0 -28
  188. package/dist/scaffolds/notis-notes/components/ui/button.tsx +0 -53
  189. package/dist/scaffolds/notis-notes/components/ui/card.tsx +0 -56
  190. package/dist/scaffolds/notis-notes/components.json +0 -20
  191. package/dist/scaffolds/notis-notes/lib/utils.ts +0 -6
  192. package/dist/scaffolds/notis-notes/lib/visible-properties.ts +0 -144
  193. package/dist/scaffolds/notis-notes/metadata/screenshot-1.png +0 -0
  194. package/dist/scaffolds/notis-notes/metadata/screenshot-2.png +0 -0
  195. package/dist/scaffolds/notis-notes/metadata/screenshot-3.png +0 -0
  196. package/dist/scaffolds/notis-notes/metadata/screenshot-4.png +0 -0
  197. package/dist/scaffolds/notis-notes/metadata/screenshot-5.png +0 -0
  198. package/dist/scaffolds/notis-notes/metadata/screenshot-fixtures.json +0 -752
  199. package/dist/scaffolds/notis-notes/notis.config.ts +0 -80
  200. package/dist/scaffolds/notis-notes/package-lock.json +0 -4636
  201. package/dist/scaffolds/notis-notes/package.json +0 -35
  202. package/dist/scaffolds/notis-notes/packages/sdk/package.json +0 -36
  203. package/dist/scaffolds/notis-notes/packages/sdk/src/components/DocumentEditor.tsx +0 -93
  204. package/dist/scaffolds/notis-notes/packages/sdk/src/components/Markdown.tsx +0 -60
  205. package/dist/scaffolds/notis-notes/packages/sdk/src/components/MultiSelectActionBar.tsx +0 -278
  206. package/dist/scaffolds/notis-notes/packages/sdk/src/components/MultiSelectCheckbox.tsx +0 -91
  207. package/dist/scaffolds/notis-notes/packages/sdk/src/components/MultiSelectDragOverlay.tsx +0 -39
  208. package/dist/scaffolds/notis-notes/packages/sdk/src/config.ts +0 -234
  209. package/dist/scaffolds/notis-notes/packages/sdk/src/documents.ts +0 -250
  210. package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useBackend.ts +0 -41
  211. package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useCloudComputer.ts +0 -97
  212. package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useDatabaseSchema.ts +0 -85
  213. package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useDatabaseSubscription.ts +0 -76
  214. package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useDocument.ts +0 -78
  215. package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useDocuments.ts +0 -121
  216. package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useHandover.ts +0 -75
  217. package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useMultiSelect.ts +0 -539
  218. package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useNotis.ts +0 -34
  219. package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useNotisNavigation.ts +0 -49
  220. package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useTool.ts +0 -64
  221. package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useTools.ts +0 -56
  222. package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useTopBarSearch.ts +0 -73
  223. package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useUpsertDocument.ts +0 -95
  224. package/dist/scaffolds/notis-notes/packages/sdk/src/index.ts +0 -100
  225. package/dist/scaffolds/notis-notes/packages/sdk/src/provider.tsx +0 -43
  226. package/dist/scaffolds/notis-notes/packages/sdk/src/runtime.ts +0 -351
  227. package/dist/scaffolds/notis-notes/packages/sdk/src/styles.css +0 -186
  228. package/dist/scaffolds/notis-notes/packages/sdk/src/ui.ts +0 -15
  229. package/dist/scaffolds/notis-notes/packages/sdk/src/vite.ts +0 -56
  230. package/dist/scaffolds/notis-notes/packages/sdk/tsconfig.json +0 -15
  231. package/dist/scaffolds/notis-notes/postcss.config.mjs +0 -8
  232. package/dist/scaffolds/notis-notes/tailwind.config.ts +0 -58
  233. package/dist/scaffolds/notis-notes/tsconfig.json +0 -23
  234. package/dist/scaffolds/notis-notes/vite.config.ts +0 -10
  235. package/dist/scaffolds/notis-random/CHANGELOG.md +0 -15
  236. package/dist/scaffolds/notis-random/README.md +0 -33
  237. package/dist/scaffolds/notis-random/app/globals.css +0 -11
  238. package/dist/scaffolds/notis-random/app/history/page.tsx +0 -67
  239. package/dist/scaffolds/notis-random/app/layout.tsx +0 -7
  240. package/dist/scaffolds/notis-random/app/page.tsx +0 -289
  241. package/dist/scaffolds/notis-random/components/ui/button.tsx +0 -50
  242. package/dist/scaffolds/notis-random/components/ui/card.tsx +0 -16
  243. package/dist/scaffolds/notis-random/components/ui/input.tsx +0 -23
  244. package/dist/scaffolds/notis-random/components.json +0 -20
  245. package/dist/scaffolds/notis-random/index.html +0 -12
  246. package/dist/scaffolds/notis-random/lib/notis-tools.ts +0 -128
  247. package/dist/scaffolds/notis-random/lib/rng.ts +0 -202
  248. package/dist/scaffolds/notis-random/lib/roll-record.ts +0 -189
  249. package/dist/scaffolds/notis-random/lib/utils.ts +0 -25
  250. package/dist/scaffolds/notis-random/metadata/screenshot-1.png +0 -0
  251. package/dist/scaffolds/notis-random/metadata/screenshot-2.png +0 -0
  252. package/dist/scaffolds/notis-random/metadata/screenshot-3.png +0 -0
  253. package/dist/scaffolds/notis-random/metadata/screenshot-4.png +0 -0
  254. package/dist/scaffolds/notis-random/metadata/screenshot-5.png +0 -0
  255. package/dist/scaffolds/notis-random/metadata/screenshot-fixtures.json +0 -753
  256. package/dist/scaffolds/notis-random/notis.config.ts +0 -86
  257. package/dist/scaffolds/notis-random/package-lock.json +0 -4513
  258. package/dist/scaffolds/notis-random/package.json +0 -36
  259. package/dist/scaffolds/notis-random/packages/sdk/package.json +0 -36
  260. package/dist/scaffolds/notis-random/packages/sdk/src/components/DocumentEditor.tsx +0 -93
  261. package/dist/scaffolds/notis-random/packages/sdk/src/components/Markdown.tsx +0 -60
  262. package/dist/scaffolds/notis-random/packages/sdk/src/components/MultiSelectActionBar.tsx +0 -278
  263. package/dist/scaffolds/notis-random/packages/sdk/src/components/MultiSelectCheckbox.tsx +0 -91
  264. package/dist/scaffolds/notis-random/packages/sdk/src/components/MultiSelectDragOverlay.tsx +0 -39
  265. package/dist/scaffolds/notis-random/packages/sdk/src/config.ts +0 -234
  266. package/dist/scaffolds/notis-random/packages/sdk/src/documents.ts +0 -250
  267. package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useBackend.ts +0 -41
  268. package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useCloudComputer.ts +0 -97
  269. package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useDatabaseSchema.ts +0 -85
  270. package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useDatabaseSubscription.ts +0 -76
  271. package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useDocument.ts +0 -78
  272. package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useDocuments.ts +0 -121
  273. package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useHandover.ts +0 -75
  274. package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useMultiSelect.ts +0 -539
  275. package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useNotis.ts +0 -34
  276. package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useNotisNavigation.ts +0 -49
  277. package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useTool.ts +0 -64
  278. package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useTools.ts +0 -56
  279. package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useTopBarSearch.ts +0 -73
  280. package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useUpsertDocument.ts +0 -95
  281. package/dist/scaffolds/notis-random/packages/sdk/src/index.ts +0 -100
  282. package/dist/scaffolds/notis-random/packages/sdk/src/provider.tsx +0 -43
  283. package/dist/scaffolds/notis-random/packages/sdk/src/runtime.ts +0 -351
  284. package/dist/scaffolds/notis-random/packages/sdk/src/styles.css +0 -186
  285. package/dist/scaffolds/notis-random/packages/sdk/src/ui.ts +0 -15
  286. package/dist/scaffolds/notis-random/packages/sdk/src/vite.ts +0 -56
  287. package/dist/scaffolds/notis-random/packages/sdk/tsconfig.json +0 -15
  288. package/dist/scaffolds/notis-random/postcss.config.mjs +0 -6
  289. package/dist/scaffolds/notis-random/src/dev-main.tsx +0 -70
  290. package/dist/scaffolds/notis-random/src/mock-runtime.ts +0 -129
  291. package/dist/scaffolds/notis-random/tailwind.config.ts +0 -50
  292. package/dist/scaffolds/notis-random/tsconfig.json +0 -23
  293. package/dist/scaffolds/notis-random/vite.config.ts +0 -11
  294. 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
+ }