@andre.li/memoark 0.3.1 → 0.3.2

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 (497) hide show
  1. package/README.en.md +388 -69
  2. package/README.md +674 -309
  3. package/README.zh-CN.md +816 -0
  4. package/bin/memoark.mjs +7 -2
  5. package/dist/adapters/store.d.ts.map +1 -1
  6. package/dist/adapters/store.js +6 -5
  7. package/dist/adapters/store.js.map +1 -1
  8. package/dist/cli-helpers.d.ts +12 -0
  9. package/dist/cli-helpers.d.ts.map +1 -0
  10. package/dist/cli-helpers.js +11 -0
  11. package/dist/cli-helpers.js.map +1 -0
  12. package/dist/cli.js +682 -126
  13. package/dist/cli.js.map +1 -1
  14. package/dist/collectors/feishu/chat-name-resolver.d.ts +33 -0
  15. package/dist/collectors/feishu/chat-name-resolver.d.ts.map +1 -0
  16. package/dist/collectors/feishu/chat-name-resolver.js +63 -0
  17. package/dist/collectors/feishu/chat-name-resolver.js.map +1 -0
  18. package/dist/collectors/feishu/collector.d.ts +4 -0
  19. package/dist/collectors/feishu/collector.d.ts.map +1 -1
  20. package/dist/collectors/feishu/collector.js +27 -4
  21. package/dist/collectors/feishu/collector.js.map +1 -1
  22. package/dist/collectors/feishu/docs/blocks.d.ts +25 -0
  23. package/dist/collectors/feishu/docs/blocks.d.ts.map +1 -0
  24. package/dist/collectors/feishu/docs/blocks.js +34 -0
  25. package/dist/collectors/feishu/docs/blocks.js.map +1 -0
  26. package/dist/collectors/feishu/docs/candidate.d.ts +17 -0
  27. package/dist/collectors/feishu/docs/candidate.d.ts.map +1 -0
  28. package/dist/collectors/feishu/docs/candidate.js +36 -0
  29. package/dist/collectors/feishu/docs/candidate.js.map +1 -0
  30. package/dist/collectors/feishu/docs/config.d.ts +41 -0
  31. package/dist/collectors/feishu/docs/config.d.ts.map +1 -0
  32. package/dist/collectors/feishu/docs/config.js +30 -0
  33. package/dist/collectors/feishu/docs/config.js.map +1 -0
  34. package/dist/collectors/feishu/docs/decision.d.ts +14 -0
  35. package/dist/collectors/feishu/docs/decision.d.ts.map +1 -0
  36. package/dist/collectors/feishu/docs/decision.js +43 -0
  37. package/dist/collectors/feishu/docs/decision.js.map +1 -0
  38. package/dist/collectors/feishu/docs/full-builder.d.ts +17 -0
  39. package/dist/collectors/feishu/docs/full-builder.d.ts.map +1 -0
  40. package/dist/collectors/feishu/docs/full-builder.js +136 -0
  41. package/dist/collectors/feishu/docs/full-builder.js.map +1 -0
  42. package/dist/collectors/feishu/docs/hash.d.ts +8 -0
  43. package/dist/collectors/feishu/docs/hash.d.ts.map +1 -0
  44. package/dist/collectors/feishu/docs/hash.js +15 -0
  45. package/dist/collectors/feishu/docs/hash.js.map +1 -0
  46. package/dist/collectors/feishu/docs/ingest.d.ts +54 -0
  47. package/dist/collectors/feishu/docs/ingest.d.ts.map +1 -0
  48. package/dist/collectors/feishu/docs/ingest.js +120 -0
  49. package/dist/collectors/feishu/docs/ingest.js.map +1 -0
  50. package/dist/collectors/feishu/docs/llm-json.d.ts +13 -0
  51. package/dist/collectors/feishu/docs/llm-json.d.ts.map +1 -0
  52. package/dist/collectors/feishu/docs/llm-json.js +40 -0
  53. package/dist/collectors/feishu/docs/llm-json.js.map +1 -0
  54. package/dist/collectors/feishu/docs/pointer-builder.d.ts +7 -0
  55. package/dist/collectors/feishu/docs/pointer-builder.d.ts.map +1 -0
  56. package/dist/collectors/feishu/docs/pointer-builder.js +11 -0
  57. package/dist/collectors/feishu/docs/pointer-builder.js.map +1 -0
  58. package/dist/collectors/feishu/docs/render.d.ts +8 -0
  59. package/dist/collectors/feishu/docs/render.d.ts.map +1 -0
  60. package/dist/collectors/feishu/docs/render.js +105 -0
  61. package/dist/collectors/feishu/docs/render.js.map +1 -0
  62. package/dist/collectors/feishu/docs/run.d.ts +37 -0
  63. package/dist/collectors/feishu/docs/run.d.ts.map +1 -0
  64. package/dist/collectors/feishu/docs/run.js +143 -0
  65. package/dist/collectors/feishu/docs/run.js.map +1 -0
  66. package/dist/collectors/feishu/docs/status.d.ts +16 -0
  67. package/dist/collectors/feishu/docs/status.d.ts.map +1 -0
  68. package/dist/collectors/feishu/docs/status.js +24 -0
  69. package/dist/collectors/feishu/docs/status.js.map +1 -0
  70. package/dist/collectors/feishu/docs/store-writer.d.ts +32 -0
  71. package/dist/collectors/feishu/docs/store-writer.d.ts.map +1 -0
  72. package/dist/collectors/feishu/docs/store-writer.js +71 -0
  73. package/dist/collectors/feishu/docs/store-writer.js.map +1 -0
  74. package/dist/collectors/feishu/docs/toc.d.ts +3 -0
  75. package/dist/collectors/feishu/docs/toc.d.ts.map +1 -0
  76. package/dist/collectors/feishu/docs/toc.js +19 -0
  77. package/dist/collectors/feishu/docs/toc.js.map +1 -0
  78. package/dist/collectors/feishu/docs/triggers.d.ts +7 -0
  79. package/dist/collectors/feishu/docs/triggers.d.ts.map +1 -0
  80. package/dist/collectors/feishu/docs/triggers.js +31 -0
  81. package/dist/collectors/feishu/docs/triggers.js.map +1 -0
  82. package/dist/collectors/feishu/docs/types.d.ts +109 -0
  83. package/dist/collectors/feishu/docs/types.d.ts.map +1 -0
  84. package/dist/collectors/feishu/docs/types.js +2 -0
  85. package/dist/collectors/feishu/docs/types.js.map +1 -0
  86. package/dist/collectors/feishu/docs/upgrade-queue.d.ts +19 -0
  87. package/dist/collectors/feishu/docs/upgrade-queue.d.ts.map +1 -0
  88. package/dist/collectors/feishu/docs/upgrade-queue.js +36 -0
  89. package/dist/collectors/feishu/docs/upgrade-queue.js.map +1 -0
  90. package/dist/collectors/feishu/docs/url-parser.d.ts +3 -0
  91. package/dist/collectors/feishu/docs/url-parser.d.ts.map +1 -0
  92. package/dist/collectors/feishu/docs/url-parser.js +39 -0
  93. package/dist/collectors/feishu/docs/url-parser.js.map +1 -0
  94. package/dist/collectors/feishu/docs/walkers.d.ts +24 -0
  95. package/dist/collectors/feishu/docs/walkers.d.ts.map +1 -0
  96. package/dist/collectors/feishu/docs/walkers.js +90 -0
  97. package/dist/collectors/feishu/docs/walkers.js.map +1 -0
  98. package/dist/collectors/feishu/docs/wiki-resolver.d.ts +15 -0
  99. package/dist/collectors/feishu/docs/wiki-resolver.d.ts.map +1 -0
  100. package/dist/collectors/feishu/docs/wiki-resolver.js +24 -0
  101. package/dist/collectors/feishu/docs/wiki-resolver.js.map +1 -0
  102. package/dist/collectors/feishu/lark-cli-client.d.ts +12 -0
  103. package/dist/collectors/feishu/lark-cli-client.d.ts.map +1 -1
  104. package/dist/collectors/feishu/lark-cli-client.js +37 -2
  105. package/dist/collectors/feishu/lark-cli-client.js.map +1 -1
  106. package/dist/collectors/feishu/lark-cli-identity-backend.d.ts +28 -0
  107. package/dist/collectors/feishu/lark-cli-identity-backend.d.ts.map +1 -0
  108. package/dist/collectors/feishu/lark-cli-identity-backend.js +110 -0
  109. package/dist/collectors/feishu/lark-cli-identity-backend.js.map +1 -0
  110. package/dist/collectors/feishu/self-open-id.d.ts +20 -0
  111. package/dist/collectors/feishu/self-open-id.d.ts.map +1 -0
  112. package/dist/collectors/feishu/self-open-id.js +31 -0
  113. package/dist/collectors/feishu/self-open-id.js.map +1 -0
  114. package/dist/collectors/feishu/sources/dm.d.ts.map +1 -1
  115. package/dist/collectors/feishu/sources/dm.js +4 -0
  116. package/dist/collectors/feishu/sources/dm.js.map +1 -1
  117. package/dist/collectors/feishu/sources/mail.d.ts.map +1 -1
  118. package/dist/collectors/feishu/sources/mail.js +18 -20
  119. package/dist/collectors/feishu/sources/mail.js.map +1 -1
  120. package/dist/collectors/feishu/sources/messages.d.ts +2 -0
  121. package/dist/collectors/feishu/sources/messages.d.ts.map +1 -1
  122. package/dist/collectors/feishu/sources/messages.js +28 -1
  123. package/dist/collectors/feishu/sources/messages.js.map +1 -1
  124. package/dist/collectors/feishu/types.d.ts +37 -3
  125. package/dist/collectors/feishu/types.d.ts.map +1 -1
  126. package/dist/collectors/feishu/types.js.map +1 -1
  127. package/dist/config-center/connection-checks.d.ts.map +1 -1
  128. package/dist/config-center/connection-checks.js +1 -1
  129. package/dist/config-center/connection-checks.js.map +1 -1
  130. package/dist/config-center/schema.d.ts.map +1 -1
  131. package/dist/config-center/schema.js +77 -0
  132. package/dist/config-center/schema.js.map +1 -1
  133. package/dist/consolidator/consolidator.d.ts +13 -1
  134. package/dist/consolidator/consolidator.d.ts.map +1 -1
  135. package/dist/consolidator/consolidator.js +12 -2
  136. package/dist/consolidator/consolidator.js.map +1 -1
  137. package/dist/core/canonicalize.js +5 -1
  138. package/dist/core/canonicalize.js.map +1 -1
  139. package/dist/core/config.d.ts +52 -9
  140. package/dist/core/config.d.ts.map +1 -1
  141. package/dist/core/config.js +86 -24
  142. package/dist/core/config.js.map +1 -1
  143. package/dist/core/env-validation.d.ts +9 -0
  144. package/dist/core/env-validation.d.ts.map +1 -0
  145. package/dist/core/env-validation.js +94 -0
  146. package/dist/core/env-validation.js.map +1 -0
  147. package/dist/core/identity-resolver.d.ts +24 -3
  148. package/dist/core/identity-resolver.d.ts.map +1 -1
  149. package/dist/core/identity-resolver.js +147 -2
  150. package/dist/core/identity-resolver.js.map +1 -1
  151. package/dist/core/person-identity.d.ts +124 -0
  152. package/dist/core/person-identity.d.ts.map +1 -0
  153. package/dist/core/person-identity.js +342 -0
  154. package/dist/core/person-identity.js.map +1 -0
  155. package/dist/core/person-slug.d.ts +21 -0
  156. package/dist/core/person-slug.d.ts.map +1 -0
  157. package/dist/core/person-slug.js +93 -0
  158. package/dist/core/person-slug.js.map +1 -0
  159. package/dist/core/pipeline-factory.d.ts +1 -1
  160. package/dist/core/pipeline-factory.d.ts.map +1 -1
  161. package/dist/core/pipeline-factory.js +8 -3
  162. package/dist/core/pipeline-factory.js.map +1 -1
  163. package/dist/core/pipeline.d.ts +16 -0
  164. package/dist/core/pipeline.d.ts.map +1 -1
  165. package/dist/core/pipeline.js +49 -1
  166. package/dist/core/pipeline.js.map +1 -1
  167. package/dist/core/resource-loader.d.ts +2 -0
  168. package/dist/core/resource-loader.d.ts.map +1 -0
  169. package/dist/core/resource-loader.js +12 -0
  170. package/dist/core/resource-loader.js.map +1 -0
  171. package/dist/core/schemas.d.ts +2981 -309
  172. package/dist/core/schemas.d.ts.map +1 -1
  173. package/dist/core/schemas.js +27 -0
  174. package/dist/core/schemas.js.map +1 -1
  175. package/dist/core/signal-scoring.d.ts.map +1 -1
  176. package/dist/core/signal-scoring.js +5 -1
  177. package/dist/core/signal-scoring.js.map +1 -1
  178. package/dist/core/source-ref.d.ts +4 -0
  179. package/dist/core/source-ref.d.ts.map +1 -0
  180. package/dist/core/source-ref.js +24 -0
  181. package/dist/core/source-ref.js.map +1 -0
  182. package/dist/core/state.d.ts +1 -1
  183. package/dist/core/state.d.ts.map +1 -1
  184. package/dist/core/state.js +3 -2
  185. package/dist/core/state.js.map +1 -1
  186. package/dist/core/types.d.ts +34 -6
  187. package/dist/core/types.d.ts.map +1 -1
  188. package/dist/daemon/reload-manager.d.ts +27 -0
  189. package/dist/daemon/reload-manager.d.ts.map +1 -0
  190. package/dist/daemon/reload-manager.js +67 -0
  191. package/dist/daemon/reload-manager.js.map +1 -0
  192. package/dist/daemon/scheduler.d.ts +9 -1
  193. package/dist/daemon/scheduler.d.ts.map +1 -1
  194. package/dist/daemon/scheduler.js +113 -38
  195. package/dist/daemon/scheduler.js.map +1 -1
  196. package/dist/daemon/serve-runtime.d.ts +31 -0
  197. package/dist/daemon/serve-runtime.d.ts.map +1 -0
  198. package/dist/daemon/serve-runtime.js +230 -0
  199. package/dist/daemon/serve-runtime.js.map +1 -0
  200. package/dist/embedded-assets.generated.d.ts.map +1 -1
  201. package/dist/embedded-assets.generated.js +3 -3
  202. package/dist/embedded-assets.generated.js.map +1 -1
  203. package/dist/extractors/playbook-extractor.d.ts +35 -0
  204. package/dist/extractors/playbook-extractor.d.ts.map +1 -0
  205. package/dist/extractors/playbook-extractor.js +98 -0
  206. package/dist/extractors/playbook-extractor.js.map +1 -0
  207. package/dist/extractors/prompts/examples/agent-session.md +257 -0
  208. package/dist/extractors/prompts/signal-extract.md +199 -0
  209. package/dist/extractors/prompts/system.md +45 -0
  210. package/dist/extractors/signal-extractor.d.ts +2 -1
  211. package/dist/extractors/signal-extractor.d.ts.map +1 -1
  212. package/dist/extractors/signal-extractor.js +99 -7
  213. package/dist/extractors/signal-extractor.js.map +1 -1
  214. package/dist/hooks/handlers.d.ts +21 -0
  215. package/dist/hooks/handlers.d.ts.map +1 -0
  216. package/dist/hooks/handlers.js +22 -0
  217. package/dist/hooks/handlers.js.map +1 -0
  218. package/dist/hooks/inject.d.ts +7 -0
  219. package/dist/hooks/inject.d.ts.map +1 -0
  220. package/dist/hooks/inject.js +18 -0
  221. package/dist/hooks/inject.js.map +1 -0
  222. package/dist/hooks/install.d.ts +14 -0
  223. package/dist/hooks/install.d.ts.map +1 -0
  224. package/dist/hooks/install.js +34 -0
  225. package/dist/hooks/install.js.map +1 -0
  226. package/dist/hooks/output.d.ts +19 -0
  227. package/dist/hooks/output.d.ts.map +1 -0
  228. package/dist/hooks/output.js +13 -0
  229. package/dist/hooks/output.js.map +1 -0
  230. package/dist/hooks/recall-client.d.ts +25 -0
  231. package/dist/hooks/recall-client.d.ts.map +1 -0
  232. package/dist/hooks/recall-client.js +56 -0
  233. package/dist/hooks/recall-client.js.map +1 -0
  234. package/dist/hooks/run-event.d.ts +13 -0
  235. package/dist/hooks/run-event.d.ts.map +1 -0
  236. package/dist/hooks/run-event.js +20 -0
  237. package/dist/hooks/run-event.js.map +1 -0
  238. package/dist/hooks/settings-edit.d.ts +10 -0
  239. package/dist/hooks/settings-edit.d.ts.map +1 -0
  240. package/dist/hooks/settings-edit.js +44 -0
  241. package/dist/hooks/settings-edit.js.map +1 -0
  242. package/dist/hooks/writeback.d.ts +12 -0
  243. package/dist/hooks/writeback.d.ts.map +1 -0
  244. package/dist/hooks/writeback.js +48 -0
  245. package/dist/hooks/writeback.js.map +1 -0
  246. package/dist/install/clients/claude-code.d.ts +3 -0
  247. package/dist/install/clients/claude-code.d.ts.map +1 -0
  248. package/dist/install/clients/claude-code.js +30 -0
  249. package/dist/install/clients/claude-code.js.map +1 -0
  250. package/dist/install/clients/claude-desktop.d.ts +3 -0
  251. package/dist/install/clients/claude-desktop.d.ts.map +1 -0
  252. package/dist/install/clients/claude-desktop.js +31 -0
  253. package/dist/install/clients/claude-desktop.js.map +1 -0
  254. package/dist/install/clients/codex.d.ts +3 -0
  255. package/dist/install/clients/codex.d.ts.map +1 -0
  256. package/dist/install/clients/codex.js +32 -0
  257. package/dist/install/clients/codex.js.map +1 -0
  258. package/dist/install/clients/cursor.d.ts +3 -0
  259. package/dist/install/clients/cursor.d.ts.map +1 -0
  260. package/dist/install/clients/cursor.js +35 -0
  261. package/dist/install/clients/cursor.js.map +1 -0
  262. package/dist/install/clients/hermes.d.ts +3 -0
  263. package/dist/install/clients/hermes.d.ts.map +1 -0
  264. package/dist/install/clients/hermes.js +35 -0
  265. package/dist/install/clients/hermes.js.map +1 -0
  266. package/dist/install/clients/index.d.ts +4 -0
  267. package/dist/install/clients/index.d.ts.map +1 -0
  268. package/dist/install/clients/index.js +18 -0
  269. package/dist/install/clients/index.js.map +1 -0
  270. package/dist/install/clients/windsurf.d.ts +3 -0
  271. package/dist/install/clients/windsurf.d.ts.map +1 -0
  272. package/dist/install/clients/windsurf.js +32 -0
  273. package/dist/install/clients/windsurf.js.map +1 -0
  274. package/dist/install/command.d.ts +14 -0
  275. package/dist/install/command.d.ts.map +1 -0
  276. package/dist/install/command.js +34 -0
  277. package/dist/install/command.js.map +1 -0
  278. package/dist/install/directive.d.ts +7 -0
  279. package/dist/install/directive.d.ts.map +1 -0
  280. package/dist/install/directive.js +31 -0
  281. package/dist/install/directive.js.map +1 -0
  282. package/dist/install/index.d.ts +26 -0
  283. package/dist/install/index.d.ts.map +1 -0
  284. package/dist/install/index.js +117 -0
  285. package/dist/install/index.js.map +1 -0
  286. package/dist/install/json-config.d.ts +11 -0
  287. package/dist/install/json-config.d.ts.map +1 -0
  288. package/dist/install/json-config.js +39 -0
  289. package/dist/install/json-config.js.map +1 -0
  290. package/dist/install/marked-block.d.ts +11 -0
  291. package/dist/install/marked-block.d.ts.map +1 -0
  292. package/dist/install/marked-block.js +34 -0
  293. package/dist/install/marked-block.js.map +1 -0
  294. package/dist/install/skill.d.ts +4 -0
  295. package/dist/install/skill.d.ts.map +1 -0
  296. package/dist/install/skill.js +61 -0
  297. package/dist/install/skill.js.map +1 -0
  298. package/dist/install/toml-config.d.ts +4 -0
  299. package/dist/install/toml-config.d.ts.map +1 -0
  300. package/dist/install/toml-config.js +51 -0
  301. package/dist/install/toml-config.js.map +1 -0
  302. package/dist/install/types.d.ts +37 -0
  303. package/dist/install/types.d.ts.map +1 -0
  304. package/dist/install/types.js +4 -0
  305. package/dist/install/types.js.map +1 -0
  306. package/dist/install/yaml-config.d.ts +4 -0
  307. package/dist/install/yaml-config.d.ts.map +1 -0
  308. package/dist/install/yaml-config.js +18 -0
  309. package/dist/install/yaml-config.js.map +1 -0
  310. package/dist/processors/privacy.d.ts +4 -1
  311. package/dist/processors/privacy.d.ts.map +1 -1
  312. package/dist/processors/privacy.js +5 -3
  313. package/dist/processors/privacy.js.map +1 -1
  314. package/dist/profile/accumulate.d.ts +28 -0
  315. package/dist/profile/accumulate.d.ts.map +1 -0
  316. package/dist/profile/accumulate.js +48 -0
  317. package/dist/profile/accumulate.js.map +1 -0
  318. package/dist/profile/behavior.d.ts +32 -0
  319. package/dist/profile/behavior.d.ts.map +1 -0
  320. package/dist/profile/behavior.js +122 -0
  321. package/dist/profile/behavior.js.map +1 -0
  322. package/dist/profile/four-color.d.ts +12 -0
  323. package/dist/profile/four-color.d.ts.map +1 -0
  324. package/dist/profile/four-color.js +39 -0
  325. package/dist/profile/four-color.js.map +1 -0
  326. package/dist/profile/profile-synth.d.ts +27 -0
  327. package/dist/profile/profile-synth.d.ts.map +1 -0
  328. package/dist/profile/profile-synth.js +174 -0
  329. package/dist/profile/profile-synth.js.map +1 -0
  330. package/dist/profile/types.d.ts +84 -0
  331. package/dist/profile/types.d.ts.map +1 -0
  332. package/dist/profile/types.js +11 -0
  333. package/dist/profile/types.js.map +1 -0
  334. package/dist/server/api.d.ts +8 -2
  335. package/dist/server/api.d.ts.map +1 -1
  336. package/dist/server/api.js +69 -34
  337. package/dist/server/api.js.map +1 -1
  338. package/dist/server/backfill-routes.d.ts.map +1 -1
  339. package/dist/server/backfill-routes.js +14 -1
  340. package/dist/server/backfill-routes.js.map +1 -1
  341. package/dist/server/chat-name-refresh-job.d.ts +33 -0
  342. package/dist/server/chat-name-refresh-job.d.ts.map +1 -0
  343. package/dist/server/chat-name-refresh-job.js +120 -0
  344. package/dist/server/chat-name-refresh-job.js.map +1 -0
  345. package/dist/server/chat-name-routes.d.ts +13 -0
  346. package/dist/server/chat-name-routes.d.ts.map +1 -0
  347. package/dist/server/chat-name-routes.js +81 -0
  348. package/dist/server/chat-name-routes.js.map +1 -0
  349. package/dist/server/config-routes.d.ts +2 -0
  350. package/dist/server/config-routes.d.ts.map +1 -1
  351. package/dist/server/config-routes.js +2 -1
  352. package/dist/server/config-routes.js.map +1 -1
  353. package/dist/server/mcp-http.d.ts +26 -0
  354. package/dist/server/mcp-http.d.ts.map +1 -0
  355. package/dist/server/mcp-http.js +102 -0
  356. package/dist/server/mcp-http.js.map +1 -0
  357. package/dist/server/mcp.d.ts +191 -25
  358. package/dist/server/mcp.d.ts.map +1 -1
  359. package/dist/server/mcp.js +1111 -68
  360. package/dist/server/mcp.js.map +1 -1
  361. package/dist/server/open-browser.d.ts +3 -0
  362. package/dist/server/open-browser.d.ts.map +1 -0
  363. package/dist/server/open-browser.js +12 -0
  364. package/dist/server/open-browser.js.map +1 -0
  365. package/dist/server/runtime.d.ts +13 -0
  366. package/dist/server/runtime.d.ts.map +1 -0
  367. package/dist/server/runtime.js +27 -0
  368. package/dist/server/runtime.js.map +1 -0
  369. package/dist/server/setup-server.d.ts.map +1 -1
  370. package/dist/server/setup-server.js +8 -11
  371. package/dist/server/setup-server.js.map +1 -1
  372. package/dist/setup/connection-tests.d.ts +1 -1
  373. package/dist/setup/connection-tests.d.ts.map +1 -1
  374. package/dist/setup/connection-tests.js +4 -2
  375. package/dist/setup/connection-tests.js.map +1 -1
  376. package/dist/setup/generate-config.d.ts.map +1 -1
  377. package/dist/setup/generate-config.js +31 -1
  378. package/dist/setup/generate-config.js.map +1 -1
  379. package/dist/setup/init-wizard.d.ts +1 -0
  380. package/dist/setup/init-wizard.d.ts.map +1 -1
  381. package/dist/setup/init-wizard.js +38 -21
  382. package/dist/setup/init-wizard.js.map +1 -1
  383. package/dist/setup/validate-config.d.ts +5 -1
  384. package/dist/setup/validate-config.d.ts.map +1 -1
  385. package/dist/setup/validate-config.js +18 -0
  386. package/dist/setup/validate-config.js.map +1 -1
  387. package/dist/store/data-dir-lock.d.ts +15 -0
  388. package/dist/store/data-dir-lock.d.ts.map +1 -0
  389. package/dist/store/data-dir-lock.js +96 -0
  390. package/dist/store/data-dir-lock.js.map +1 -0
  391. package/dist/store/database.d.ts +2 -0
  392. package/dist/store/database.d.ts.map +1 -1
  393. package/dist/store/database.js +23 -10
  394. package/dist/store/database.js.map +1 -1
  395. package/dist/store/graph.d.ts +22 -0
  396. package/dist/store/graph.d.ts.map +1 -1
  397. package/dist/store/graph.js +97 -7
  398. package/dist/store/graph.js.map +1 -1
  399. package/dist/store/migrations/index.d.ts.map +1 -1
  400. package/dist/store/migrations/index.js +52 -0
  401. package/dist/store/migrations/index.js.map +1 -1
  402. package/dist/store/pages.d.ts +7 -0
  403. package/dist/store/pages.d.ts.map +1 -1
  404. package/dist/store/pages.js +56 -1
  405. package/dist/store/pages.js.map +1 -1
  406. package/dist/store/person-behavior.d.ts +28 -0
  407. package/dist/store/person-behavior.d.ts.map +1 -0
  408. package/dist/store/person-behavior.js +127 -0
  409. package/dist/store/person-behavior.js.map +1 -0
  410. package/dist/store/pglite-assets.d.ts +15 -0
  411. package/dist/store/pglite-assets.d.ts.map +1 -0
  412. package/dist/store/pglite-assets.js +47 -0
  413. package/dist/store/pglite-assets.js.map +1 -0
  414. package/dist/store/query-rewrite.d.ts +28 -0
  415. package/dist/store/query-rewrite.d.ts.map +1 -0
  416. package/dist/store/query-rewrite.js +97 -0
  417. package/dist/store/query-rewrite.js.map +1 -0
  418. package/dist/store/schema.sql +107 -0
  419. package/dist/store/search.d.ts +36 -8
  420. package/dist/store/search.d.ts.map +1 -1
  421. package/dist/store/search.js +188 -76
  422. package/dist/store/search.js.map +1 -1
  423. package/dist/store/tags.d.ts.map +1 -1
  424. package/dist/store/tags.js +6 -2
  425. package/dist/store/tags.js.map +1 -1
  426. package/dist/store/timeline.d.ts +13 -1
  427. package/dist/store/timeline.d.ts.map +1 -1
  428. package/dist/store/timeline.js +134 -4
  429. package/dist/store/timeline.js.map +1 -1
  430. package/dist/store/trgm-search.d.ts +13 -0
  431. package/dist/store/trgm-search.d.ts.map +1 -0
  432. package/dist/store/trgm-search.js +53 -0
  433. package/dist/store/trgm-search.js.map +1 -0
  434. package/dist/store/wikilink.d.ts +17 -0
  435. package/dist/store/wikilink.d.ts.map +1 -0
  436. package/dist/store/wikilink.js +58 -0
  437. package/dist/store/wikilink.js.map +1 -0
  438. package/dist/sync/obsidian.d.ts.map +1 -1
  439. package/dist/sync/obsidian.js +5 -1
  440. package/dist/sync/obsidian.js.map +1 -1
  441. package/dist/synth/cache.d.ts +17 -0
  442. package/dist/synth/cache.d.ts.map +1 -0
  443. package/dist/synth/cache.js +67 -0
  444. package/dist/synth/cache.js.map +1 -0
  445. package/dist/synth/citations.d.ts +12 -0
  446. package/dist/synth/citations.d.ts.map +1 -0
  447. package/dist/synth/citations.js +28 -0
  448. package/dist/synth/citations.js.map +1 -0
  449. package/dist/synth/context.d.ts +10 -0
  450. package/dist/synth/context.d.ts.map +1 -0
  451. package/dist/synth/context.js +67 -0
  452. package/dist/synth/context.js.map +1 -0
  453. package/dist/synth/engine.d.ts +16 -0
  454. package/dist/synth/engine.d.ts.map +1 -0
  455. package/dist/synth/engine.js +111 -0
  456. package/dist/synth/engine.js.map +1 -0
  457. package/dist/synth/gaps.d.ts +17 -0
  458. package/dist/synth/gaps.d.ts.map +1 -0
  459. package/dist/synth/gaps.js +57 -0
  460. package/dist/synth/gaps.js.map +1 -0
  461. package/dist/synth/index.d.ts +6 -0
  462. package/dist/synth/index.d.ts.map +1 -0
  463. package/dist/synth/index.js +6 -0
  464. package/dist/synth/index.js.map +1 -0
  465. package/dist/synth/intent.d.ts +6 -0
  466. package/dist/synth/intent.d.ts.map +1 -0
  467. package/dist/synth/intent.js +13 -0
  468. package/dist/synth/intent.js.map +1 -0
  469. package/dist/synth/intents/daily-report.d.ts +7 -0
  470. package/dist/synth/intents/daily-report.d.ts.map +1 -0
  471. package/dist/synth/intents/daily-report.js +37 -0
  472. package/dist/synth/intents/daily-report.js.map +1 -0
  473. package/dist/synth/intents/index.d.ts +2 -0
  474. package/dist/synth/intents/index.d.ts.map +1 -0
  475. package/dist/synth/intents/index.js +12 -0
  476. package/dist/synth/intents/index.js.map +1 -0
  477. package/dist/synth/intents/person-strategy.d.ts +40 -0
  478. package/dist/synth/intents/person-strategy.d.ts.map +1 -0
  479. package/dist/synth/intents/person-strategy.js +66 -0
  480. package/dist/synth/intents/person-strategy.js.map +1 -0
  481. package/dist/synth/intents/recall.d.ts +8 -0
  482. package/dist/synth/intents/recall.d.ts.map +1 -0
  483. package/dist/synth/intents/recall.js +26 -0
  484. package/dist/synth/intents/recall.js.map +1 -0
  485. package/dist/synth/intents/troubleshoot.d.ts +11 -0
  486. package/dist/synth/intents/troubleshoot.d.ts.map +1 -0
  487. package/dist/synth/intents/troubleshoot.js +66 -0
  488. package/dist/synth/intents/troubleshoot.js.map +1 -0
  489. package/dist/synth/scope.d.ts +13 -0
  490. package/dist/synth/scope.d.ts.map +1 -0
  491. package/dist/synth/scope.js +139 -0
  492. package/dist/synth/scope.js.map +1 -0
  493. package/dist/synth/types.d.ts +117 -0
  494. package/dist/synth/types.d.ts.map +1 -0
  495. package/dist/synth/types.js +2 -0
  496. package/dist/synth/types.js.map +1 -0
  497. package/package.json +18 -6
package/dist/cli.js CHANGED
@@ -1,28 +1,62 @@
1
1
  #!/usr/bin/env node
2
2
  import { existsSync, mkdirSync } from "node:fs";
3
3
  import { homedir } from "node:os";
4
- import { resolve } from "node:path";
4
+ import { dirname, isAbsolute, join, resolve } from "node:path";
5
+ import { fileURLToPath } from "node:url";
5
6
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6
7
  import { Command } from "commander";
8
+ import { planStartup, shouldOpenBrowserOnServe } from "./cli-helpers.js";
9
+ import { normalizeDocsConfig } from "./collectors/feishu/docs/config.js";
10
+ import { FullCardBuilder } from "./collectors/feishu/docs/full-builder.js";
11
+ import { runDocSource } from "./collectors/feishu/docs/run.js";
12
+ import { failedCards, summarizeCards } from "./collectors/feishu/docs/status.js";
13
+ import { loadExistingCard, writeCard } from "./collectors/feishu/docs/store-writer.js";
14
+ import { LarkCliHttpClient } from "./collectors/feishu/lark-cli-client.js";
15
+ import { resolveSelfOpenId } from "./collectors/feishu/self-open-id.js";
7
16
  import { createClaudeCodeCollector, createCodexCollector, createFeishuCollector, createHermesCollector, getAllCollectors, getCollector, registerCollector, resetRegistry, } from "./collectors/index.js";
8
17
  import { Consolidator } from "./consolidator/consolidator.js";
9
- import { loadConfig } from "./core/config.js";
18
+ import { loadConfig, resolveConfigPath, } from "./core/config.js";
19
+ import { CursorStore } from "./core/cursors.js";
20
+ import { getMissingEnvVarsForCommand, validateEnvForCommand } from "./core/env-validation.js";
21
+ import { PersonIdentityStore } from "./core/person-identity.js";
10
22
  import { runPipeline } from "./core/pipeline.js";
23
+ import { buildPipelineConfig } from "./core/pipeline-factory.js";
11
24
  import { ensureStateDir, statePath } from "./core/state.js";
12
- import { Scheduler } from "./daemon/scheduler.js";
25
+ import { ReloadManager } from "./daemon/reload-manager.js";
26
+ import { buildServeRuntime, ServeRuntimeHolder } from "./daemon/serve-runtime.js";
13
27
  import { VERSION } from "./embedded-assets.generated.js";
14
28
  import { createLLMProvider, createMockProvider } from "./extractors/providers/index.js";
29
+ import { hooksInstall, hooksUninstall } from "./hooks/install.js";
30
+ import { runHookEvent } from "./hooks/run-event.js";
31
+ import { runInstall, runUninstall } from "./install/index.js";
32
+ import { scaffoldSkill } from "./install/skill.js";
15
33
  import { createApiApp } from "./server/api.js";
34
+ import { getSessionContext } from "./server/context.js";
16
35
  import { createMcpServer } from "./server/mcp.js";
36
+ import { createMcpHttpApp } from "./server/mcp-http.js";
37
+ import { openBrowser } from "./server/open-browser.js";
38
+ import { startServer } from "./server/runtime.js";
17
39
  import { ChunkStore } from "./store/chunks.js";
18
40
  import { Database } from "./store/database.js";
19
41
  import { EmbeddingService } from "./store/embedding.js";
20
42
  import { GraphStore } from "./store/graph.js";
21
43
  import { PageStore } from "./store/pages.js";
44
+ import { PersonBehaviorStore } from "./store/person-behavior.js";
22
45
  import { SearchEngine } from "./store/search.js";
23
46
  import { TagStore } from "./store/tags.js";
24
47
  import { TimelineStore } from "./store/timeline.js";
25
- function bootstrapCollectors(sources) {
48
+ function resolveProjectPath(path, projectRoot) {
49
+ if (!path)
50
+ return undefined;
51
+ if (path.startsWith("~/"))
52
+ return resolve(homedir(), path.slice(2));
53
+ if (path === "~")
54
+ return homedir();
55
+ if (isAbsolute(path))
56
+ return path;
57
+ return resolve(projectRoot, path);
58
+ }
59
+ function bootstrapCollectors(sources, projectRoot) {
26
60
  resetRegistry();
27
61
  const agentConfigs = {
28
62
  "claude-code": { factory: createClaudeCodeCollector, config: sources["claude-code"] },
@@ -31,22 +65,33 @@ function bootstrapCollectors(sources) {
31
65
  };
32
66
  for (const [_id, { factory, config }] of Object.entries(agentConfigs)) {
33
67
  if (config?.enabled !== false) {
34
- registerCollector(factory(config?.base_dir));
68
+ registerCollector(factory(resolveProjectPath(config?.base_dir, projectRoot)));
35
69
  }
36
70
  }
37
71
  if (sources.feishu?.enabled !== false && sources.feishu?.app_id) {
38
72
  registerCollector(createFeishuCollector(sources.feishu));
39
73
  }
40
74
  }
41
- function expandDataDir(dir) {
42
- if (dir.startsWith("~/"))
43
- return resolve(homedir(), dir.slice(2));
44
- if (dir === "~")
45
- return homedir();
46
- return dir;
75
+ function expandDataDir(dir, projectRoot) {
76
+ return resolveProjectPath(dir, projectRoot) ?? dir;
77
+ }
78
+ // Try to extract feishu.lark_bin from an existing config so the setup UI's
79
+ // "Feishu — Test Connection" button doesn't fall through to the hardcoded
80
+ // ~/.local/bin/lark path. Silent on missing file or parse errors (the wizard
81
+ // may be running because the YAML doesn't exist yet).
82
+ function readLarkBinFromConfig(configPath) {
83
+ const path = configPath ?? resolve(process.cwd(), "memoark.yaml");
84
+ if (!existsSync(path))
85
+ return undefined;
86
+ try {
87
+ return loadConfig(path).sources?.feishu?.lark_bin;
88
+ }
89
+ catch {
90
+ return undefined;
91
+ }
47
92
  }
48
93
  async function createStores(config) {
49
- const dataDir = expandDataDir(config.store.data_dir);
94
+ const dataDir = expandDataDir(config.store.data_dir, config.__context.projectRoot);
50
95
  mkdirSync(dataDir, { recursive: true });
51
96
  const db = await Database.create(dataDir, {
52
97
  embeddingDimensions: config.embedding.dimensions,
@@ -60,7 +105,13 @@ async function createStores(config) {
60
105
  apiKey: config.embedding.api_key ?? process.env.OPENAI_API_KEY,
61
106
  baseUrl: config.embedding.base_url,
62
107
  });
63
- const search = new SearchEngine(db.pg, { embedText: (q) => embedding.embedText(q) });
108
+ const search = new SearchEngine(db.pg, {
109
+ embedText: (q) => embedding.embedText(q),
110
+ search: {
111
+ pool_by_page: config.search.pool_by_page,
112
+ llm_rewrite: config.search.llm_rewrite,
113
+ },
114
+ });
64
115
  return {
65
116
  db,
66
117
  pages,
@@ -72,6 +123,20 @@ async function createStores(config) {
72
123
  embedding,
73
124
  };
74
125
  }
126
+ /**
127
+ * Lightweight store for identity operations: opens only the Database + a
128
+ * PersonIdentityStore. Deliberately avoids EmbeddingService so that person
129
+ * alias/merge/rename never requires an LLM or embedding API key.
130
+ */
131
+ async function openIdentityStore(config) {
132
+ const dataDir = expandDataDir(config.store.data_dir, config.__context.projectRoot);
133
+ mkdirSync(dataDir, { recursive: true });
134
+ const db = await Database.create(dataDir, {
135
+ embeddingDimensions: config.embedding.dimensions,
136
+ });
137
+ const identity = new PersonIdentityStore(db.pg, { pages: new PageStore(db.pg) }, { behavior: new PersonBehaviorStore(db.pg) });
138
+ return { db, identity };
139
+ }
75
140
  const program = new Command();
76
141
  program
77
142
  .name("memoark")
@@ -88,7 +153,10 @@ program
88
153
  .action(async (options) => {
89
154
  if (options.web) {
90
155
  const { startSetupServer } = await import("./server/setup-server.js");
91
- await startSetupServer({ configPath: options.config });
156
+ await startSetupServer({
157
+ configPath: options.config,
158
+ larkBin: readLarkBinFromConfig(options.config),
159
+ });
92
160
  return;
93
161
  }
94
162
  try {
@@ -123,10 +191,11 @@ program
123
191
  try {
124
192
  // Load configuration
125
193
  const config = loadConfig(options.config);
194
+ const { projectRoot } = config.__context;
126
195
  // Ensure state directory exists
127
- ensureStateDir();
196
+ ensureStateDir(projectRoot);
128
197
  // Bootstrap collectors from config
129
- bootstrapCollectors(config.sources);
198
+ bootstrapCollectors(config.sources, projectRoot);
130
199
  // Determine which sources to process
131
200
  let sourceIds;
132
201
  if (options.source === "all") {
@@ -138,6 +207,7 @@ program
138
207
  // Create LLM provider based on config (shared across all sources)
139
208
  let provider;
140
209
  if (!options.dryRun) {
210
+ validateEnvForCommand(config, "extract");
141
211
  const llmConfig = config.llm;
142
212
  const envKey = llmConfig.provider === "anthropic"
143
213
  ? process.env.ANTHROPIC_API_KEY
@@ -156,16 +226,7 @@ program
156
226
  provider = createMockProvider(new Map());
157
227
  }
158
228
  // Build pipeline configuration
159
- const pipelineConfig = {
160
- dedup_checkpoint: statePath("dedup.jsonl"),
161
- cursor_checkpoint: statePath("cursors.yaml"),
162
- block_gap_minutes: config.block_builder.block_gap_minutes,
163
- max_block_tokens: config.block_builder.max_block_tokens,
164
- max_block_messages: config.block_builder.max_block_messages,
165
- privacy: config.privacy,
166
- output_dir: options.output || process.cwd(),
167
- block_concurrency: config.pipeline?.block_concurrency,
168
- };
229
+ const pipelineConfig = buildPipelineConfig(config, options.output || process.cwd(), projectRoot);
169
230
  // Parse options
170
231
  const format = ["json", "markdown"].includes(options.format) ? options.format : "json";
171
232
  const adapter = ["store", "file", "gbrain", "stdout"].includes(options.adapter)
@@ -276,7 +337,7 @@ program
276
337
  const warnings = [];
277
338
  const ok = [];
278
339
  // Check config file
279
- const configPath = options.config || resolve(process.cwd(), "memoark.yaml");
340
+ const configPath = resolveConfigPath(options.config);
280
341
  let config = null;
281
342
  if (existsSync(configPath)) {
282
343
  ok.push(`Configuration file found: ${configPath}`);
@@ -293,7 +354,8 @@ program
293
354
  warnings.push("Create one with: memoark init");
294
355
  }
295
356
  // Check state directory
296
- const stateDir = resolve(process.cwd(), ".memoark");
357
+ const projectRoot = config?.__context.projectRoot ?? dirname(configPath);
358
+ const stateDir = resolve(projectRoot, ".memoark");
297
359
  if (existsSync(stateDir)) {
298
360
  ok.push(`State directory exists: ${stateDir}`);
299
361
  }
@@ -301,12 +363,23 @@ program
301
363
  warnings.push(`State directory does not exist: ${stateDir}`);
302
364
  warnings.push("It will be created automatically on first extract");
303
365
  }
366
+ const cwdStateDir = resolve(process.cwd(), ".memoark");
367
+ if (cwdStateDir !== stateDir && existsSync(cwdStateDir)) {
368
+ warnings.push(`Legacy state directory found at current cwd: ${cwdStateDir}`);
369
+ warnings.push(`Current config-root state directory is: ${stateDir}`);
370
+ warnings.push("Move cursor/dedup files manually if you intended to reuse the old state.");
371
+ }
304
372
  // Check LLM configuration
305
373
  if (config) {
374
+ const missingEnvVars = getMissingEnvVarsForCommand(config, "doctor");
375
+ if (missingEnvVars.length > 0) {
376
+ warnings.push(`Missing environment variables: ${missingEnvVars.join(", ")}`);
377
+ warnings.push(`Referenced by: ${config.__context.configPath}`);
378
+ }
306
379
  if (config.llm?.provider && config.llm?.model) {
307
380
  ok.push(`LLM provider configured: ${config.llm.provider} / ${config.llm.model}`);
308
381
  const envKey = config.llm.provider === "anthropic" ? "ANTHROPIC_API_KEY" : "OPENAI_API_KEY";
309
- if (process.env[envKey] || config.llm.api_key) {
382
+ if (!missingEnvVars.includes(envKey)) {
310
383
  ok.push(`${config.llm.provider} API key configured`);
311
384
  }
312
385
  else {
@@ -318,7 +391,7 @@ program
318
391
  issues.push("LLM provider or model not configured");
319
392
  }
320
393
  // Check sources
321
- bootstrapCollectors(config.sources);
394
+ bootstrapCollectors(config.sources, config.__context.projectRoot);
322
395
  for (const collector of getAllCollectors()) {
323
396
  const health = await collector.healthCheck();
324
397
  if (health.ok) {
@@ -385,9 +458,13 @@ configCmd
385
458
  .command("edit")
386
459
  .description("Edit configuration in browser UI")
387
460
  .option("--web", "Launch browser-based settings UI (default behavior)")
388
- .action(async () => {
461
+ .option("-c, --config <path>", "Path to config file (default: memoark.yaml)")
462
+ .action(async (options) => {
389
463
  const { startSetupServer } = await import("./server/setup-server.js");
390
- await startSetupServer();
464
+ await startSetupServer({
465
+ configPath: options.config,
466
+ larkBin: readLarkBinFromConfig(options.config),
467
+ });
391
468
  });
392
469
  /**
393
470
  * Sources subcommand group
@@ -399,7 +476,7 @@ sourcesCmd
399
476
  .option("-c, --config <path>", "Path to config file")
400
477
  .action((options) => {
401
478
  const config = loadConfig(options.config);
402
- bootstrapCollectors(config.sources);
479
+ bootstrapCollectors(config.sources, config.__context.projectRoot);
403
480
  const collectors = getAllCollectors();
404
481
  console.log("Available sources:\n");
405
482
  for (const c of collectors) {
@@ -415,7 +492,7 @@ sourcesCmd
415
492
  .action(async (name, options) => {
416
493
  try {
417
494
  const config = loadConfig(options.config);
418
- bootstrapCollectors(config.sources);
495
+ bootstrapCollectors(config.sources, config.__context.projectRoot);
419
496
  const collector = getCollector(name);
420
497
  if (!collector) {
421
498
  console.error(`Error: Unknown source '${name}'`);
@@ -436,104 +513,321 @@ sourcesCmd
436
513
  process.exit(1);
437
514
  }
438
515
  });
516
+ async function runServe(options) {
517
+ {
518
+ const serveConfigPath = options.config ?? resolve(process.cwd(), "memoark.yaml");
519
+ if (!existsSync(serveConfigPath)) {
520
+ console.error("No configuration file found.\n" +
521
+ "Run `memoark start` for one-step setup + launch, or `memoark init --web` to configure first.");
522
+ process.exit(1);
523
+ }
524
+ const config = loadConfig(options.config);
525
+ // Anchor the .memoark state dir to the config's project root, not process.cwd().
526
+ // A Finder-launched sidecar has cwd=/, so the default would try to mkdir /.memoark
527
+ // (EROFS on macOS). projectRoot = dirname(configPath), so it lives beside the config.
528
+ const stateDir = ensureStateDir(config.__context.projectRoot);
529
+ const missingEnvVars = getMissingEnvVarsForCommand(config, "serve");
530
+ if (missingEnvVars.length > 0) {
531
+ console.warn(`[warn] Missing env vars: ${missingEnvVars.join(", ")} (referenced by ${config.__context.configPath})`);
532
+ }
533
+ const stores = await createStores(config);
534
+ const initialRuntime = await buildServeRuntime(config, stores, stateDir);
535
+ const holder = new ServeRuntimeHolder(initialRuntime);
536
+ if (config.scheduler?.enabled)
537
+ await holder.current.scheduler?.start();
538
+ const reloadManager = new ReloadManager({
539
+ holder,
540
+ // only read once at construction for the initial signature; ReloadManager tracks lastConfig internally afterward
541
+ currentConfig: () => config,
542
+ buildRuntime: (next) => buildServeRuntime(next, stores, stateDir),
543
+ });
544
+ const storesWithDaemon = {
545
+ ...stores,
546
+ getDaemonStatus: () => holder.current.getDaemonStatus(),
547
+ // getter: always reads the current runtime, so a Tier-2 swap is seen by routes
548
+ get chatNameRefreshJob() {
549
+ return holder.current.chatNameRefreshJob;
550
+ },
551
+ };
552
+ let shuttingDown = false;
553
+ const shutdown = async () => {
554
+ if (shuttingDown)
555
+ return; // 防重入:连按 Ctrl-C 不会二次 db.close()
556
+ shuttingDown = true;
557
+ await holder.current.dispose();
558
+ try {
559
+ await stores.db.close(); // 触发锁 release
560
+ }
561
+ finally {
562
+ process.exit(0); // db.close() 抛错也必须退出
563
+ }
564
+ };
565
+ process.on("SIGTERM", shutdown);
566
+ process.on("SIGINT", shutdown);
567
+ if (options.mcp) {
568
+ const llmConfig = { ...config.llm };
569
+ const envKey = llmConfig.provider === "anthropic"
570
+ ? process.env.ANTHROPIC_API_KEY
571
+ : process.env.OPENAI_API_KEY;
572
+ if (!llmConfig.api_key && envKey)
573
+ llmConfig.api_key = envKey;
574
+ const synthProvider = llmConfig.api_key
575
+ ? createLLMProvider(llmConfig)
576
+ : createMockProvider(new Map());
577
+ let ingestDeps;
578
+ const feishu = config.sources.feishu;
579
+ if (feishu?.enabled && feishu.sources?.docs?.enabled) {
580
+ const client = new LarkCliHttpClient(feishu.lark_bin);
581
+ ingestDeps = {
582
+ client,
583
+ stores: storesWithDaemon,
584
+ provider: synthProvider,
585
+ model: feishu.sources.docs.llm?.model ?? llmConfig.model,
586
+ nowIso: () => new Date().toISOString(),
587
+ };
588
+ }
589
+ const server = createMcpServer(storesWithDaemon, { provider: synthProvider, synthModel: llmConfig.model }, ingestDeps);
590
+ await server.connect(new StdioServerTransport());
591
+ return;
592
+ }
593
+ if (options.mcpHttp ||
594
+ config.mcp.http.enabled ||
595
+ config.server.mcp_transport === "streamable_http") {
596
+ const tokenEnv = config.mcp.http.auth_token_env;
597
+ const app = createMcpHttpApp(storesWithDaemon, {
598
+ allowedOrigins: config.mcp.http.allowed_origins,
599
+ allowedHosts: config.mcp.http.allowed_hosts,
600
+ authToken: tokenEnv ? process.env[tokenEnv] : undefined,
601
+ exposeLegacyTools: config.mcp.expose_legacy_tools,
602
+ readOnly: config.mcp.http.read_only,
603
+ });
604
+ const server = await startServer(app, {
605
+ hostname: config.mcp.http.bind_host,
606
+ port: config.mcp.http.port,
607
+ });
608
+ console.log(`Memoark MCP Streamable HTTP listening on http://${server.hostname}:${server.port}/mcp`);
609
+ return;
610
+ }
611
+ const app = createApiApp(storesWithDaemon, {
612
+ onConfigSaved: () => {
613
+ try {
614
+ void reloadManager.run(loadConfig(options.config)).catch((err) => {
615
+ console.error("[reload] Runtime reload failed:", err);
616
+ });
617
+ }
618
+ catch (err) {
619
+ console.error("[reload] Failed to load config after save:", err);
620
+ }
621
+ },
622
+ });
623
+ // In a `bun --compile` sidecar, import.meta.url lives under $bunfs and web/dist is
624
+ // NOT embedded, so the default path can't be served. The Tauri shell ships web/dist
625
+ // as a resource and injects its real path via MEMOARK_WEB_DIST (mirrors pglite-assets).
626
+ const webDist = process.env.MEMOARK_WEB_DIST ?? join(fileURLToPath(import.meta.url), "../../web/dist");
627
+ // `--port 0` (used by the Tauri shell) binds an OS-assigned free port so the desktop
628
+ // app never collides with a CLI `memoark serve`, a stale instance, or anything else
629
+ // on the default port. The actual port is reported below for the webview to read.
630
+ const requestedPort = options.port !== undefined ? Number(options.port) : config.server.http_port;
631
+ const server = Bun.serve({
632
+ port: requestedPort,
633
+ fetch: async (req) => {
634
+ const url = new URL(req.url);
635
+ if (url.pathname.startsWith("/api"))
636
+ return app.fetch(req);
637
+ const filePath = url.pathname === "/" ? "index.html" : url.pathname.replace(/^\//, "");
638
+ const candidate = Bun.file(join(webDist, filePath));
639
+ if (await candidate.exists())
640
+ return new Response(candidate);
641
+ return new Response(Bun.file(join(webDist, "index.html")));
642
+ },
643
+ });
644
+ console.log(`Memoark HTTP API listening on http://localhost:${server.port}`);
645
+ // Stdout contract for the Tauri shell: the URL after the marker is where the webview
646
+ // navigates (the port may be OS-assigned, so report the real one — never hardcode).
647
+ console.log(`MEMOARK_READY http://localhost:${server.port}`);
648
+ if (shouldOpenBrowserOnServe({
649
+ open: options.open !== false,
650
+ mcp: !!options.mcp,
651
+ mcpHttp: !!options.mcpHttp,
652
+ })) {
653
+ openBrowser(`http://localhost:${server.port}`);
654
+ }
655
+ const activeScheduler = holder.current.scheduler;
656
+ if (activeScheduler && config.scheduler?.enabled) {
657
+ console.log(`Scheduler running — tick every ${config.scheduler.tick_interval_secs}s, sources: ${activeScheduler.getSourceIds().join(", ")}`);
658
+ }
659
+ }
660
+ }
439
661
  program
440
662
  .command("serve")
441
663
  .description("Start Memoark HTTP API or MCP stdio server")
442
664
  .option("-c, --config <path>", "Path to config file")
443
665
  .option("--mcp", "Run MCP stdio transport instead of HTTP")
444
- .action(async (options) => {
445
- const serveConfigPath = options.config ?? resolve(process.cwd(), "memoark.yaml");
446
- if (!existsSync(serveConfigPath)) {
447
- console.error("No configuration file found.\nRun `memoark init` (TUI) or `memoark init --web` (browser) to set up Memoark.");
448
- process.exit(1);
449
- }
450
- const config = loadConfig(options.config);
451
- const stateDir = ensureStateDir();
452
- const stores = await createStores(config);
453
- let scheduler;
454
- if (config.scheduler?.enabled) {
455
- bootstrapCollectors(config.sources);
456
- const llmConfig = config.llm;
457
- const envKey = llmConfig.provider === "anthropic"
458
- ? process.env.ANTHROPIC_API_KEY
459
- : process.env.OPENAI_API_KEY;
460
- if (!llmConfig.api_key && envKey)
461
- llmConfig.api_key = envKey;
462
- const provider = llmConfig.api_key
463
- ? createLLMProvider(llmConfig)
464
- : createMockProvider(new Map());
465
- const pipelineConfig = {
466
- dedup_checkpoint: statePath("dedup.jsonl"),
467
- cursor_checkpoint: statePath("cursors.yaml"),
468
- block_gap_minutes: config.block_builder.block_gap_minutes,
469
- max_block_tokens: config.block_builder.max_block_tokens,
470
- max_block_messages: config.block_builder.max_block_messages,
471
- privacy: config.privacy,
472
- output_dir: process.cwd(),
473
- block_concurrency: config.pipeline?.block_concurrency,
474
- };
475
- scheduler = new Scheduler(config.scheduler, stateDir);
476
- scheduler.setRunSource(async (sourceId) => {
477
- const collector = getCollector(sourceId);
478
- if (!collector)
479
- throw new Error(`Unknown source: ${sourceId}`);
480
- return runPipeline(pipelineConfig, {
481
- source: collector,
482
- provider,
483
- format: "json",
484
- adapter: "store",
485
- stores,
486
- dryRun: false,
487
- });
488
- });
489
- scheduler.setOnTick((sourceId, result, duration_ms) => {
490
- const status = result.fatal ? "failed" : "ok";
491
- console.log(`[scheduler] ${sourceId}: ${status} (${duration_ms}ms)`);
666
+ .option("--mcp-http", "Run MCP Streamable HTTP transport instead of the HTTP API")
667
+ .option("--no-open", "Do not auto-open the browser after starting")
668
+ .option("--pglite-assets <dir>", "Directory holding bundled PGLite assets (compiled-sidecar mode; injected by the Tauri shell)")
669
+ .option("--web-dist <dir>", "Directory holding the built web UI (compiled-sidecar mode; injected by the Tauri shell)")
670
+ .option("--port <n>", "Override the HTTP port; 0 binds an OS-assigned free port (used by the Tauri shell)")
671
+ .action((options) => {
672
+ if (options.pgliteAssets)
673
+ process.env.MEMOARK_PGLITE_ASSETS = options.pgliteAssets;
674
+ if (options.webDist)
675
+ process.env.MEMOARK_WEB_DIST = options.webDist;
676
+ return runServe(options);
677
+ });
678
+ async function runStart(options) {
679
+ const configPath = options.config ?? resolve(process.cwd(), "memoark.yaml");
680
+ const plan = planStartup(existsSync(configPath));
681
+ if (plan.runSetup) {
682
+ console.log("No configuration found — launching setup wizard...");
683
+ const { startSetupServer } = await import("./server/setup-server.js");
684
+ await startSetupServer({
685
+ configPath: options.config,
686
+ larkBin: readLarkBinFromConfig(options.config),
492
687
  });
493
- await scheduler.start();
494
- }
495
- const getDaemonStatus = scheduler
496
- ? () => {
497
- const hb = scheduler?.getHeartbeat();
498
- const now = Date.now();
499
- let lastRunAt = null;
500
- let nextAt = null;
501
- for (const id of scheduler?.getSourceIds() ?? []) {
502
- const s = scheduler?.getSourceState(id);
503
- if (!s)
504
- continue;
505
- if (s.last_run_at !== null && (lastRunAt === null || s.last_run_at > lastRunAt)) {
506
- lastRunAt = s.last_run_at;
507
- }
508
- const next = s.last_run_at !== null ? s.last_run_at + s.interval_secs * 1000 : now;
509
- if (nextAt === null || next < nextAt)
510
- nextAt = next;
511
- }
512
- return {
513
- running: true,
514
- uptime_seconds: Math.floor((now - (hb?.daemon_started_at ?? now)) / 1000),
515
- last_run: lastRunAt ? new Date(lastRunAt).toISOString() : null,
516
- next_scheduled: nextAt !== null ? new Date(nextAt).toISOString() : null,
517
- };
518
- }
519
- : undefined;
520
- const storesWithDaemon = { ...stores, getDaemonStatus };
521
- const shutdown = () => {
522
- scheduler?.stop();
523
- };
524
- process.on("SIGTERM", shutdown);
525
- process.on("SIGINT", shutdown);
526
- if (options.mcp) {
527
- const server = createMcpServer(storesWithDaemon);
528
- await server.connect(new StdioServerTransport());
688
+ }
689
+ await runServe({ config: options.config });
690
+ }
691
+ program
692
+ .command("start")
693
+ .description("One-step launch: setup if needed, then serve + open browser")
694
+ .option("-c, --config <path>", "Path to config file")
695
+ .action((options) => runStart(options));
696
+ program.action(() => runStart({}));
697
+ function reportPlan(planned, verb, dryRun) {
698
+ if (planned.length === 0) {
699
+ console.log("No AI agents detected. Specify one with --agent <id> (claude-code, claude-desktop, cursor, codex, windsurf).");
529
700
  return;
530
701
  }
531
- const app = createApiApp(storesWithDaemon);
532
- const server = Bun.serve({ port: config.server.http_port, fetch: app.fetch });
533
- console.log(`Memoark HTTP API listening on http://localhost:${server.port}`);
534
- if (scheduler) {
535
- console.log(`Scheduler running — tick every ${config.scheduler?.tick_interval_secs}s, sources: ${scheduler.getSourceIds().join(", ")}`);
702
+ for (const client of planned) {
703
+ console.log(`\n${verb} ${client.displayName}:`);
704
+ for (const op of client.ops) {
705
+ const where = "path" in op ? op.path : `cli: ${op.args.join(" ")}`;
706
+ console.log(` - ${op.kind} ${op.action} ${where}`);
707
+ }
708
+ }
709
+ if (!dryRun)
710
+ console.log("\nRestart / reopen your agent for changes to take effect.");
711
+ }
712
+ program
713
+ .command("install")
714
+ .description("Register Memoark (MCP config + memory directive) into your AI agents")
715
+ .option("--agent <ids...>", "Target client(s): claude-code, claude-desktop, cursor, codex, windsurf (default: all detected)")
716
+ .option("--project", "Install into the current project instead of globally")
717
+ .option("--http", "Register the Streamable HTTP transport instead of stdio")
718
+ .option("--dry-run", "Preview changes without writing")
719
+ .action((options) => {
720
+ const scope = options.project ? "project" : "global";
721
+ const planned = runInstall({
722
+ agent: options.agent,
723
+ scope,
724
+ http: !!options.http,
725
+ dryRun: !!options.dryRun,
726
+ });
727
+ reportPlan(planned, options.dryRun ? "Would install" : "Installed", !!options.dryRun);
728
+ });
729
+ program
730
+ .command("uninstall")
731
+ .description("Remove Memoark MCP config + memory directive from your AI agents")
732
+ .option("--agent <ids...>", "Target client(s) (default: all detected)")
733
+ .option("--project", "Operate on the current project instead of globally")
734
+ .option("--dry-run", "Preview changes without writing")
735
+ .action((options) => {
736
+ const scope = options.project ? "project" : "global";
737
+ const planned = runUninstall({ agent: options.agent, scope, dryRun: !!options.dryRun });
738
+ reportPlan(planned, options.dryRun ? "Would remove" : "Removed", !!options.dryRun);
739
+ });
740
+ const hooksCmd = program
741
+ .command("hooks")
742
+ .description("Manage Claude Code hooks for automatic recall / write-back");
743
+ hooksCmd
744
+ .command("install")
745
+ .description("Install SessionStart + UserPromptSubmit (read) hooks; write-back is opt-in")
746
+ .option("--write-back", "Also install the SessionEnd auto write-back hook (opt-in)")
747
+ .option("--project", "Write to ./.claude/settings.json instead of the global one")
748
+ .option("--dry-run", "Preview without writing")
749
+ .action((options) => {
750
+ const res = hooksInstall({
751
+ writeBack: !!options.writeBack,
752
+ project: !!options.project,
753
+ dryRun: !!options.dryRun,
754
+ });
755
+ console.log(`${options.dryRun ? "Would install" : "Installed"} hooks [${res.events.join(", ")}] → ${res.path}`);
756
+ if (!options.writeBack) {
757
+ console.log("Tip: add --write-back to also auto-capture memory at session end (opt-in).");
758
+ }
759
+ if (!options.dryRun)
760
+ console.log("Reopen Claude Code for the hooks to take effect.");
761
+ });
762
+ hooksCmd
763
+ .command("uninstall")
764
+ .description("Remove all Memoark hooks from settings.json")
765
+ .option("--project", "Operate on ./.claude/settings.json instead of the global one")
766
+ .option("--dry-run", "Preview without writing")
767
+ .action((options) => {
768
+ const res = hooksUninstall({ project: !!options.project, dryRun: !!options.dryRun });
769
+ console.log(`${options.dryRun ? "Would remove" : "Removed"} Memoark hooks → ${res.path}`);
770
+ });
771
+ const skillCmd = program.command("skill").description("Manage the Memoark agent skill");
772
+ skillCmd
773
+ .command("scaffold")
774
+ .description("Write the memoark skill (SKILL.md) into a skills directory")
775
+ .option("--dir <path>", "Target skills directory (default: ./.claude/skills)")
776
+ .action((options) => {
777
+ const dir = options.dir ?? join(process.cwd(), ".claude", "skills");
778
+ const path = scaffoldSkill(dir);
779
+ console.log(`Wrote ${path}`);
780
+ });
781
+ async function readStdinJson() {
782
+ const chunks = [];
783
+ for await (const chunk of process.stdin)
784
+ chunks.push(chunk);
785
+ const raw = Buffer.concat(chunks).toString("utf8").trim();
786
+ if (!raw)
787
+ return {};
788
+ try {
789
+ return JSON.parse(raw);
790
+ }
791
+ catch {
792
+ return {};
793
+ }
794
+ }
795
+ program
796
+ .command("hook <event>")
797
+ .description("Internal: Claude Code hook entrypoint (session-start|user-prompt|session-end)")
798
+ .action(async (event) => {
799
+ try {
800
+ const input = await readStdinJson();
801
+ const config = loadConfig(undefined);
802
+ const port = config.server.http_port;
803
+ const out = await runHookEvent(event, input, {
804
+ port,
805
+ sessionContext: async () => {
806
+ const stores = await createStores(config);
807
+ try {
808
+ return await getSessionContext(stores);
809
+ }
810
+ finally {
811
+ await stores.db.close();
812
+ }
813
+ },
814
+ ftsSearch: async (q, opts) => {
815
+ const stores = await createStores(config);
816
+ try {
817
+ return await stores.search.search(q, opts);
818
+ }
819
+ finally {
820
+ await stores.db.close();
821
+ }
822
+ },
823
+ });
824
+ if (out && Object.keys(out).length > 0)
825
+ process.stdout.write(JSON.stringify(out));
826
+ }
827
+ catch {
828
+ // Never break the host session: emit nothing on any failure.
536
829
  }
830
+ process.exit(0);
537
831
  });
538
832
  program
539
833
  .command("search <query>")
@@ -542,7 +836,9 @@ program
542
836
  .option("--mode <mode>", "Search mode (hybrid|fts)", "hybrid")
543
837
  .option("--limit <n>", "Limit results", "20")
544
838
  .action(async (query, options) => {
545
- const stores = await createStores(loadConfig(options.config));
839
+ const config = loadConfig(options.config);
840
+ validateEnvForCommand(config, "search", { searchMode: options.mode });
841
+ const stores = await createStores(config);
546
842
  const limit = Number(options.limit);
547
843
  const results = options.mode === "fts"
548
844
  ? await stores.search.search(query, { limit })
@@ -558,7 +854,9 @@ program
558
854
  .option("-c, --config <path>", "Path to config file")
559
855
  .option("--limit <n>", "Limit chunks")
560
856
  .action(async (options) => {
561
- const stores = await createStores(loadConfig(options.config));
857
+ const config = loadConfig(options.config);
858
+ validateEnvForCommand(config, "embed");
859
+ const stores = await createStores(config);
562
860
  const result = await stores.embedding.embedStale({
563
861
  limit: options.limit ? Number(options.limit) : undefined,
564
862
  });
@@ -666,7 +964,15 @@ program
666
964
  graph: stores.graph,
667
965
  tags: stores.tags,
668
966
  timeline: stores.timeline,
669
- }, llmProvider);
967
+ }, llmProvider, {
968
+ profile: config.profile,
969
+ profileStores: {
970
+ pages: stores.pages,
971
+ graph: stores.graph,
972
+ timeline: stores.timeline,
973
+ behavior: new PersonBehaviorStore(stores.db.pg),
974
+ },
975
+ });
670
976
  const mode = options.hot
671
977
  ? "hot"
672
978
  : options.warm
@@ -683,6 +989,7 @@ program
683
989
  console.log(` warm→cold pages archived: ${result.warmToCold}`);
684
990
  console.log(` dead links checked: ${result.deadLinksChecked}`);
685
991
  console.log(` preferences inferred: ${result.preferencesInferred}`);
992
+ console.log(` profiles synthesized: ${result.profilesSynthesized}`);
686
993
  await stores.db.close();
687
994
  }
688
995
  catch (error) {
@@ -690,5 +997,254 @@ program
690
997
  process.exit(1);
691
998
  }
692
999
  });
1000
+ // ── Person identity (Layer 1: aliases / merge / rename) ────────────────────
1001
+ const HANDLE_KINDS = ["feishu_open_id", "email", "name", "nickname", "slug"];
1002
+ const identityCmd = program
1003
+ .command("identity")
1004
+ .description("Manage person identity: aliases, merge, and rename");
1005
+ identityCmd
1006
+ .command("alias <canonical_slug> <kind> <value>")
1007
+ .description(`Attach an alias/handle to a person. kind: ${HANDLE_KINDS.join(" | ")}`)
1008
+ .option("-c, --config <path>", "Path to config file")
1009
+ .option("--strong", "Force strong strength (auto-resolvable)")
1010
+ .option("--weak", "Force weak strength (explicit-only)")
1011
+ .action(async (canonicalSlug, kind, value, options) => {
1012
+ if (!HANDLE_KINDS.includes(kind)) {
1013
+ console.error(`Error: invalid kind '${kind}'. Expected one of: ${HANDLE_KINDS.join(", ")}`);
1014
+ process.exit(1);
1015
+ }
1016
+ const { db, identity } = await openIdentityStore(loadConfig(options.config));
1017
+ try {
1018
+ const strength = options.strong ? "strong" : options.weak ? "weak" : undefined;
1019
+ await identity.addAlias(canonicalSlug, kind, value, strength);
1020
+ console.log(`Linked ${kind}:${value} → ${canonicalSlug}`);
1021
+ for (const h of await identity.listHandles(canonicalSlug)) {
1022
+ console.log(` ${h.kind}\t${h.value}\t(${h.strength})`);
1023
+ }
1024
+ }
1025
+ catch (error) {
1026
+ console.error("alias failed:", error instanceof Error ? error.message : String(error));
1027
+ process.exit(1);
1028
+ }
1029
+ finally {
1030
+ await db.close();
1031
+ }
1032
+ });
1033
+ identityCmd
1034
+ .command("handles <canonical_slug>")
1035
+ .description("List all handles/aliases attached to a person")
1036
+ .option("-c, --config <path>", "Path to config file")
1037
+ .action(async (canonicalSlug, options) => {
1038
+ const { db, identity } = await openIdentityStore(loadConfig(options.config));
1039
+ try {
1040
+ const handles = await identity.listHandles(canonicalSlug);
1041
+ if (handles.length === 0) {
1042
+ console.log(`No handles for ${canonicalSlug}`);
1043
+ }
1044
+ else {
1045
+ for (const h of handles)
1046
+ console.log(`${h.kind}\t${h.value}\t(${h.strength})`);
1047
+ }
1048
+ }
1049
+ finally {
1050
+ await db.close();
1051
+ }
1052
+ });
1053
+ identityCmd
1054
+ .command("merge <from> <into>")
1055
+ .description("Merge person page <from> into <into> (re-points links/timeline/tags + aliases)")
1056
+ .option("-c, --config <path>", "Path to config file")
1057
+ .action(async (from, into, options) => {
1058
+ const { db, identity } = await openIdentityStore(loadConfig(options.config));
1059
+ try {
1060
+ await identity.merge(from, into);
1061
+ console.log(`Merged ${from} → ${into}`);
1062
+ console.log("Note: run `memoark embed` to re-embed the folded content.");
1063
+ }
1064
+ catch (error) {
1065
+ console.error("merge failed:", error instanceof Error ? error.message : String(error));
1066
+ process.exit(1);
1067
+ }
1068
+ finally {
1069
+ await db.close();
1070
+ }
1071
+ });
1072
+ identityCmd
1073
+ .command("rename <from> <to>")
1074
+ .description("Rename a person's canonical slug (correct a wrong canonicalization)")
1075
+ .option("-c, --config <path>", "Path to config file")
1076
+ .action(async (from, to, options) => {
1077
+ const { db, identity } = await openIdentityStore(loadConfig(options.config));
1078
+ try {
1079
+ await identity.recanonicalize(from, to);
1080
+ console.log(`Renamed ${from} → ${to}`);
1081
+ }
1082
+ catch (error) {
1083
+ console.error("rename failed:", error instanceof Error ? error.message : String(error));
1084
+ process.exit(1);
1085
+ }
1086
+ finally {
1087
+ await db.close();
1088
+ }
1089
+ });
1090
+ const docsCmd = program.command("docs").description("Feishu doc summary cards (DocSource v2)");
1091
+ docsCmd
1092
+ .command("sync")
1093
+ .description("Scan Feishu docs, build pointer cards, upgrade triggered docs to full cards")
1094
+ .option("-c, --config <path>", "Path to config file (default: memoark.yaml)")
1095
+ .action(async (options) => {
1096
+ try {
1097
+ const config = loadConfig(options.config);
1098
+ ensureStateDir();
1099
+ const feishu = config.sources.feishu;
1100
+ if (!feishu?.enabled || !feishu.sources?.docs?.enabled) {
1101
+ console.error("Feishu docs source is not enabled in config (sources.feishu.sources.docs.enabled).");
1102
+ process.exit(1);
1103
+ }
1104
+ const stores = await createStores(config);
1105
+ const client = new LarkCliHttpClient(feishu.lark_bin);
1106
+ const docsConfig = normalizeDocsConfig(feishu.sources.docs);
1107
+ // self_open_id: config override else resolve via lark-cli whoami helper used elsewhere
1108
+ const selfOpenId = docsConfig.self_open_id ??
1109
+ (await resolveSelfOpenId(client, feishu.sources?.dm?.self_open_id)) ??
1110
+ "";
1111
+ const llmConfig = { ...config.llm };
1112
+ if (docsConfig.llm.model)
1113
+ llmConfig.model = docsConfig.llm.model;
1114
+ const envKey = llmConfig.provider === "anthropic"
1115
+ ? process.env.ANTHROPIC_API_KEY
1116
+ : process.env.OPENAI_API_KEY;
1117
+ if (!llmConfig.api_key && envKey)
1118
+ llmConfig.api_key = envKey;
1119
+ const provider = llmConfig.api_key
1120
+ ? createLLMProvider(llmConfig)
1121
+ : createMockProvider(new Map());
1122
+ const cursor = new CursorStore(statePath("cursors.yaml"));
1123
+ cursor.load();
1124
+ // Identity layer for canonicalizing action_item owners → person slugs and
1125
+ // detecting self-ownership, so doc/meeting action_items become task signals
1126
+ // the daily report can surface (Spec 9 §3.3).
1127
+ const identity = new PersonIdentityStore(stores.db.pg, { pages: stores.pages }, { behavior: new PersonBehaviorStore(stores.db.pg) });
1128
+ const stats = await runDocSource({
1129
+ client,
1130
+ stores,
1131
+ provider,
1132
+ config: docsConfig,
1133
+ cursor,
1134
+ selfOpenId,
1135
+ nowMs: Date.now(),
1136
+ nowIso: () => new Date().toISOString(),
1137
+ actionItemDeps: {
1138
+ graph: stores.graph,
1139
+ resolveOwner: async (ownerRaw) => {
1140
+ if (!ownerRaw)
1141
+ return null;
1142
+ // best-effort: map a name/@mention to a canonical person slug
1143
+ return ((await identity.resolveHandle("name", ownerRaw)) ??
1144
+ (await identity.resolveHandle("nickname", ownerRaw)) ??
1145
+ null);
1146
+ },
1147
+ isMe: (slug) => identity.isMe(slug),
1148
+ },
1149
+ });
1150
+ console.log(`[docs] scanned=${stats.candidates_scanned} pointer=${stats.pointer_saved} full=${stats.full_card_generated} skipped=${stats.skipped} queue=${stats.upgrade_queue_size} llm_failed=${stats.llm_failed}`);
1151
+ await stores.db.close();
1152
+ }
1153
+ catch (error) {
1154
+ console.error("docs sync failed:", error instanceof Error ? error.message : String(error));
1155
+ process.exit(1);
1156
+ }
1157
+ });
1158
+ docsCmd
1159
+ .command("status")
1160
+ .description("Show Feishu doc card counts")
1161
+ .option("-c, --config <path>", "Path to config file")
1162
+ .option("--failed", "List cards whose last extraction failed")
1163
+ .action(async (options) => {
1164
+ try {
1165
+ const config = loadConfig(options.config);
1166
+ const stores = await createStores(config);
1167
+ const pages = await stores.pages.listPages({ type: "feishu_doc_card", limit: 100000 });
1168
+ if (options.failed) {
1169
+ for (const f of failedCards(pages))
1170
+ console.log(`${f.doc_token}\t${f.error}`);
1171
+ }
1172
+ else {
1173
+ const s = summarizeCards(pages);
1174
+ console.log(`total=${s.total} full=${s.full} pointer=${s.pointer} failed=${s.failed}`);
1175
+ }
1176
+ await stores.db.close();
1177
+ }
1178
+ catch (error) {
1179
+ console.error("docs status failed:", error instanceof Error ? error.message : String(error));
1180
+ process.exit(1);
1181
+ }
1182
+ });
1183
+ docsCmd
1184
+ .command("retry [doc_token]")
1185
+ .description("Retry full-card extraction for a failed doc (or --all-failed)")
1186
+ .option("-c, --config <path>", "Path to config file")
1187
+ .option("--all-failed", "Retry every card with an extract_error")
1188
+ .action(async (docToken, options) => {
1189
+ try {
1190
+ const config = loadConfig(options.config);
1191
+ const feishu = config.sources.feishu;
1192
+ if (!feishu?.sources?.docs?.enabled) {
1193
+ console.error("Feishu docs source not enabled.");
1194
+ process.exit(1);
1195
+ }
1196
+ const stores = await createStores(config);
1197
+ const client = new LarkCliHttpClient(feishu.lark_bin);
1198
+ const docsConfig = normalizeDocsConfig(feishu.sources.docs);
1199
+ const llmConfig = { ...config.llm };
1200
+ if (docsConfig.llm.model)
1201
+ llmConfig.model = docsConfig.llm.model;
1202
+ const envKey = llmConfig.provider === "anthropic"
1203
+ ? process.env.ANTHROPIC_API_KEY
1204
+ : process.env.OPENAI_API_KEY;
1205
+ if (!llmConfig.api_key && envKey)
1206
+ llmConfig.api_key = envKey;
1207
+ const provider = llmConfig.api_key
1208
+ ? createLLMProvider(llmConfig)
1209
+ : createMockProvider(new Map());
1210
+ const builder = new FullCardBuilder(client, provider, docsConfig.llm.model ?? "unknown", () => new Date().toISOString());
1211
+ const tokens = [];
1212
+ if (options.allFailed) {
1213
+ const pages = await stores.pages.listPages({ type: "feishu_doc_card", limit: 100000 });
1214
+ for (const f of failedCards(pages))
1215
+ tokens.push(f.doc_token);
1216
+ }
1217
+ else if (docToken) {
1218
+ tokens.push(docToken);
1219
+ }
1220
+ else {
1221
+ console.error("Provide a doc_token or --all-failed.");
1222
+ process.exit(1);
1223
+ }
1224
+ for (const token of tokens) {
1225
+ const existing = await loadExistingCard(stores, token);
1226
+ if (!existing) {
1227
+ console.warn(`skip ${token}: no existing card`);
1228
+ continue;
1229
+ }
1230
+ // retry intentionally re-evaluates the gate (no force); short/empty docs
1231
+ // stay pointers by design — unlike MCP ingest which forces.
1232
+ const card = await builder.build(existing);
1233
+ await writeCard(stores, card);
1234
+ if (card.extract_level === "pointer") {
1235
+ const reason = card.extract_error ?? card.extract_skipped ?? "unknown";
1236
+ console.log(`${token}: pointer (not upgraded — ${reason})`);
1237
+ }
1238
+ else {
1239
+ console.log(`${token}: full`);
1240
+ }
1241
+ }
1242
+ await stores.db.close();
1243
+ }
1244
+ catch (error) {
1245
+ console.error("docs retry failed:", error instanceof Error ? error.message : String(error));
1246
+ process.exit(1);
1247
+ }
1248
+ });
693
1249
  program.parse(process.argv);
694
1250
  //# sourceMappingURL=cli.js.map