claude-smart 0.2.41 → 0.2.43

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 (396) hide show
  1. package/.claude-plugin/marketplace.json +17 -0
  2. package/README.md +1 -1
  3. package/bin/claude-smart.js +86 -48
  4. package/package.json +10 -3
  5. package/plugin/.claude-plugin/plugin.json +9 -3
  6. package/plugin/.codex-plugin/plugin.json +1 -1
  7. package/plugin/README.md +2 -2
  8. package/plugin/dashboard/next.config.ts +9 -1
  9. package/plugin/pyproject.toml +2 -2
  10. package/plugin/scripts/_lib.sh +91 -0
  11. package/plugin/scripts/backend-service.sh +46 -15
  12. package/plugin/scripts/cli.sh +29 -1
  13. package/plugin/scripts/codex-hook.js +72 -4
  14. package/plugin/scripts/dashboard-build.sh +1 -0
  15. package/plugin/scripts/dashboard-service.sh +1 -0
  16. package/plugin/scripts/ensure-plugin-root.sh +7 -14
  17. package/plugin/scripts/hook_entry.sh +1 -0
  18. package/plugin/scripts/smart-install.sh +18 -2
  19. package/plugin/src/claude_smart/cli.py +72 -38
  20. package/plugin/src/claude_smart/context_format.py +11 -12
  21. package/plugin/src/claude_smart/cs_cite.py +26 -12
  22. package/plugin/src/claude_smart/ids.py +13 -5
  23. package/plugin/uv.lock +1 -1
  24. package/plugin/vendor/reflexio/.env.example +53 -0
  25. package/plugin/vendor/reflexio/LICENSE +201 -0
  26. package/plugin/vendor/reflexio/README.md +338 -0
  27. package/plugin/vendor/reflexio/pyproject.toml +271 -0
  28. package/plugin/vendor/reflexio/reflexio/README.md +184 -0
  29. package/plugin/vendor/reflexio/reflexio/__init__.py +166 -0
  30. package/plugin/vendor/reflexio/reflexio/benchmarks/__init__.py +1 -0
  31. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/README.md +109 -0
  32. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/__init__.py +1 -0
  33. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/backends.py +175 -0
  34. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/bench.py +642 -0
  35. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/embed_cache.py +330 -0
  36. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/report.py +317 -0
  37. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/results/report.md +43 -0
  38. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/results/results.json +4478 -0
  39. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/scenarios.py +134 -0
  40. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/seed.py +255 -0
  41. package/plugin/vendor/reflexio/reflexio/cli/README.md +287 -0
  42. package/plugin/vendor/reflexio/reflexio/cli/__init__.py +0 -0
  43. package/plugin/vendor/reflexio/reflexio/cli/__main__.py +56 -0
  44. package/plugin/vendor/reflexio/reflexio/cli/_client.py +86 -0
  45. package/plugin/vendor/reflexio/reflexio/cli/app.py +127 -0
  46. package/plugin/vendor/reflexio/reflexio/cli/bootstrap_config.py +266 -0
  47. package/plugin/vendor/reflexio/reflexio/cli/codex_auth.py +503 -0
  48. package/plugin/vendor/reflexio/reflexio/cli/commands/__init__.py +0 -0
  49. package/plugin/vendor/reflexio/reflexio/cli/commands/admin_cmd.py +65 -0
  50. package/plugin/vendor/reflexio/reflexio/cli/commands/agent_playbooks.py +503 -0
  51. package/plugin/vendor/reflexio/reflexio/cli/commands/api.py +114 -0
  52. package/plugin/vendor/reflexio/reflexio/cli/commands/auth.py +109 -0
  53. package/plugin/vendor/reflexio/reflexio/cli/commands/config_cmd.py +511 -0
  54. package/plugin/vendor/reflexio/reflexio/cli/commands/doctor.py +127 -0
  55. package/plugin/vendor/reflexio/reflexio/cli/commands/embeddings.py +53 -0
  56. package/plugin/vendor/reflexio/reflexio/cli/commands/interactions.py +478 -0
  57. package/plugin/vendor/reflexio/reflexio/cli/commands/profiles.py +303 -0
  58. package/plugin/vendor/reflexio/reflexio/cli/commands/services.py +289 -0
  59. package/plugin/vendor/reflexio/reflexio/cli/commands/setup_cmd.py +961 -0
  60. package/plugin/vendor/reflexio/reflexio/cli/commands/shortcuts.py +285 -0
  61. package/plugin/vendor/reflexio/reflexio/cli/commands/status_cmd.py +143 -0
  62. package/plugin/vendor/reflexio/reflexio/cli/commands/user_playbooks.py +373 -0
  63. package/plugin/vendor/reflexio/reflexio/cli/env_loader.py +284 -0
  64. package/plugin/vendor/reflexio/reflexio/cli/errors.py +217 -0
  65. package/plugin/vendor/reflexio/reflexio/cli/log_format.py +247 -0
  66. package/plugin/vendor/reflexio/reflexio/cli/output.py +867 -0
  67. package/plugin/vendor/reflexio/reflexio/cli/paths.py +41 -0
  68. package/plugin/vendor/reflexio/reflexio/cli/run_services.py +391 -0
  69. package/plugin/vendor/reflexio/reflexio/cli/state.py +204 -0
  70. package/plugin/vendor/reflexio/reflexio/cli/stop_services.py +96 -0
  71. package/plugin/vendor/reflexio/reflexio/cli/utils.py +329 -0
  72. package/plugin/vendor/reflexio/reflexio/client/__init__.py +3 -0
  73. package/plugin/vendor/reflexio/reflexio/client/cache.py +150 -0
  74. package/plugin/vendor/reflexio/reflexio/client/client.py +2613 -0
  75. package/plugin/vendor/reflexio/reflexio/defaults.py +23 -0
  76. package/plugin/vendor/reflexio/reflexio/integrations/__init__.py +0 -0
  77. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/.clawhubignore +7 -0
  78. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/README.md +274 -0
  79. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/TESTING.md +517 -0
  80. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/hook/handler.js +473 -0
  81. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/package-lock.json +2156 -0
  82. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/package.json +18 -0
  83. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/hook/handler.ts +241 -0
  84. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/hook/setup.ts +140 -0
  85. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/index.ts +130 -0
  86. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/publish.ts +113 -0
  87. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/search.ts +52 -0
  88. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/server.ts +103 -0
  89. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/sqlite-buffer.ts +156 -0
  90. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/user-id.ts +134 -0
  91. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/openclaw.plugin.json +41 -0
  92. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/package.json +17 -0
  93. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/rules/reflexio.md +24 -0
  94. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/reflexio/SKILL.md +48 -0
  95. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/publish_clawhub.sh +278 -0
  96. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/references/HOOK.md +164 -0
  97. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/scripts/install.sh +36 -0
  98. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/scripts/uninstall.sh +35 -0
  99. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/publish.test.ts +27 -0
  100. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/search.test.ts +31 -0
  101. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/server.test.ts +42 -0
  102. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/setup.test.ts +49 -0
  103. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/sqlite-buffer.test.ts +91 -0
  104. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/user-id.test.ts +50 -0
  105. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tsconfig.json +16 -0
  106. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/types/openclaw.d.ts +230 -0
  107. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/vitest.config.ts +13 -0
  108. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/README.md +120 -0
  109. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/TESTING.md +168 -0
  110. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/package-lock.json +1657 -0
  111. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/package.json +16 -0
  112. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/HEARTBEAT.md +6 -0
  113. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/README.md +84 -0
  114. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/SKILL.md +194 -0
  115. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/_meta.json +6 -0
  116. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/agents/reflexio-extractor.md +45 -0
  117. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/hook/handler.ts +214 -0
  118. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/hook/setup.ts +55 -0
  119. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/index.ts +327 -0
  120. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/consolidate.ts +233 -0
  121. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/dedup.ts +80 -0
  122. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/io.ts +155 -0
  123. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/openclaw-cli.ts +67 -0
  124. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/search.ts +33 -0
  125. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/write-playbook.ts +76 -0
  126. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/write-profile.ts +79 -0
  127. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/openclaw.plugin.json +46 -0
  128. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/package.json +18 -0
  129. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/prompts/README.md +36 -0
  130. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/prompts/full_consolidation.md +56 -0
  131. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/prompts/playbook_extraction.md +217 -0
  132. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/prompts/profile_extraction.md +132 -0
  133. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/skills/reflexio-consolidate/SKILL.md +33 -0
  134. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/skills/reflexio-embedded/SKILL.md +194 -0
  135. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/references/HOOK.md +18 -0
  136. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/references/architecture.md +49 -0
  137. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/references/comparison.md +31 -0
  138. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/references/future-work.md +47 -0
  139. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/references/porting-notes.md +52 -0
  140. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/scripts/install.sh +52 -0
  141. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/scripts/uninstall.sh +36 -0
  142. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/consolidate.test.ts +135 -0
  143. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/dedup.test.ts +104 -0
  144. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/io.test.ts +175 -0
  145. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/search.test.ts +66 -0
  146. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/smoke-test.ts +140 -0
  147. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/write-playbook.test.ts +93 -0
  148. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/write-profile.test.ts +174 -0
  149. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tsconfig.json +16 -0
  150. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/types/openclaw.d.ts +230 -0
  151. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/vitest.config.ts +7 -0
  152. package/plugin/vendor/reflexio/reflexio/lib/__init__.py +23 -0
  153. package/plugin/vendor/reflexio/reflexio/lib/_agent_playbook.py +310 -0
  154. package/plugin/vendor/reflexio/reflexio/lib/_base.py +225 -0
  155. package/plugin/vendor/reflexio/reflexio/lib/_config.py +83 -0
  156. package/plugin/vendor/reflexio/reflexio/lib/_dashboard.py +266 -0
  157. package/plugin/vendor/reflexio/reflexio/lib/_generation.py +176 -0
  158. package/plugin/vendor/reflexio/reflexio/lib/_interactions.py +334 -0
  159. package/plugin/vendor/reflexio/reflexio/lib/_operations.py +153 -0
  160. package/plugin/vendor/reflexio/reflexio/lib/_profiles.py +545 -0
  161. package/plugin/vendor/reflexio/reflexio/lib/_reflection.py +52 -0
  162. package/plugin/vendor/reflexio/reflexio/lib/_search.py +167 -0
  163. package/plugin/vendor/reflexio/reflexio/lib/_storage_labels.py +103 -0
  164. package/plugin/vendor/reflexio/reflexio/lib/_user_playbook.py +288 -0
  165. package/plugin/vendor/reflexio/reflexio/lib/reflexio_lib.py +27 -0
  166. package/plugin/vendor/reflexio/reflexio/models/__init__.py +0 -0
  167. package/plugin/vendor/reflexio/reflexio/models/api_schema/__init__.py +0 -0
  168. package/plugin/vendor/reflexio/reflexio/models/api_schema/braintrust_schema.py +141 -0
  169. package/plugin/vendor/reflexio/reflexio/models/api_schema/common.py +41 -0
  170. package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/__init__.py +3 -0
  171. package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/entities.py +1103 -0
  172. package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/enums.py +63 -0
  173. package/plugin/vendor/reflexio/reflexio/models/api_schema/eval_overview_schema.py +487 -0
  174. package/plugin/vendor/reflexio/reflexio/models/api_schema/internal_schema.py +28 -0
  175. package/plugin/vendor/reflexio/reflexio/models/api_schema/pending_tool_call_schema.py +83 -0
  176. package/plugin/vendor/reflexio/reflexio/models/api_schema/retriever_schema.py +766 -0
  177. package/plugin/vendor/reflexio/reflexio/models/api_schema/service_schemas.py +9 -0
  178. package/plugin/vendor/reflexio/reflexio/models/api_schema/stall_state_schema.py +32 -0
  179. package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/__init__.py +3 -0
  180. package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/converters.py +177 -0
  181. package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/entities.py +129 -0
  182. package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/enums.py +25 -0
  183. package/plugin/vendor/reflexio/reflexio/models/api_schema/validators.py +280 -0
  184. package/plugin/vendor/reflexio/reflexio/models/config_schema.py +908 -0
  185. package/plugin/vendor/reflexio/reflexio/models/py.typed +0 -0
  186. package/plugin/vendor/reflexio/reflexio/server/OVERVIEW.md +90 -0
  187. package/plugin/vendor/reflexio/reflexio/server/README.md +616 -0
  188. package/plugin/vendor/reflexio/reflexio/server/__init__.py +210 -0
  189. package/plugin/vendor/reflexio/reflexio/server/__main__.py +132 -0
  190. package/plugin/vendor/reflexio/reflexio/server/_auth.py +25 -0
  191. package/plugin/vendor/reflexio/reflexio/server/api.py +2714 -0
  192. package/plugin/vendor/reflexio/reflexio/server/api_endpoints/account_api.py +143 -0
  193. package/plugin/vendor/reflexio/reflexio/server/api_endpoints/health_api.py +91 -0
  194. package/plugin/vendor/reflexio/reflexio/server/api_endpoints/pending_tool_call_api.py +572 -0
  195. package/plugin/vendor/reflexio/reflexio/server/api_endpoints/precondition_checks.py +66 -0
  196. package/plugin/vendor/reflexio/reflexio/server/api_endpoints/publisher_api.py +540 -0
  197. package/plugin/vendor/reflexio/reflexio/server/api_endpoints/request_context.py +50 -0
  198. package/plugin/vendor/reflexio/reflexio/server/api_endpoints/stall_state_api.py +100 -0
  199. package/plugin/vendor/reflexio/reflexio/server/cache/__init__.py +15 -0
  200. package/plugin/vendor/reflexio/reflexio/server/cache/reflexio_cache.py +208 -0
  201. package/plugin/vendor/reflexio/reflexio/server/correlation.py +46 -0
  202. package/plugin/vendor/reflexio/reflexio/server/llm/__init__.py +30 -0
  203. package/plugin/vendor/reflexio/reflexio/server/llm/embedding_service.py +110 -0
  204. package/plugin/vendor/reflexio/reflexio/server/llm/image_utils.py +55 -0
  205. package/plugin/vendor/reflexio/reflexio/server/llm/litellm_client.py +1595 -0
  206. package/plugin/vendor/reflexio/reflexio/server/llm/llm_utils.py +112 -0
  207. package/plugin/vendor/reflexio/reflexio/server/llm/model_defaults.py +469 -0
  208. package/plugin/vendor/reflexio/reflexio/server/llm/providers/__init__.py +1 -0
  209. package/plugin/vendor/reflexio/reflexio/server/llm/providers/claude_code_provider.py +1122 -0
  210. package/plugin/vendor/reflexio/reflexio/server/llm/providers/claude_code_stream_parser.py +197 -0
  211. package/plugin/vendor/reflexio/reflexio/server/llm/providers/embedding_service_provider.py +210 -0
  212. package/plugin/vendor/reflexio/reflexio/server/llm/providers/local_embedding_provider.py +213 -0
  213. package/plugin/vendor/reflexio/reflexio/server/llm/providers/nomic_embedding_provider.py +255 -0
  214. package/plugin/vendor/reflexio/reflexio/server/llm/rerank/__init__.py +6 -0
  215. package/plugin/vendor/reflexio/reflexio/server/llm/rerank/cross_encoder_reranker.py +177 -0
  216. package/plugin/vendor/reflexio/reflexio/server/llm/rerank/llm_reranker.py +148 -0
  217. package/plugin/vendor/reflexio/reflexio/server/llm/tools.py +699 -0
  218. package/plugin/vendor/reflexio/reflexio/server/operation_limiter.py +179 -0
  219. package/plugin/vendor/reflexio/reflexio/server/prompt/__init__.py +0 -0
  220. package/plugin/vendor/reflexio/reflexio/server/prompt/_dispatchers.py +54 -0
  221. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/README.md +121 -0
  222. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/agent_success_evaluation/v1.0.0.prompt.md +58 -0
  223. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/agent_success_evaluation_with_comparison/v1.0.0.prompt.md +76 -0
  224. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/answer_synthesis/v1.5.2.prompt.md +88 -0
  225. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/compress_session_for_query/v1.3.0.prompt.md +31 -0
  226. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/document_expansion/v1.0.0.prompt.md +20 -0
  227. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.0.0.prompt.md +53 -0
  228. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.1.0.prompt.md +57 -0
  229. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.2.0.prompt.md +68 -0
  230. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.3.0.prompt.md +70 -0
  231. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.4.0.prompt.md +77 -0
  232. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.5.0.prompt.md +82 -0
  233. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.6.0.prompt.md +83 -0
  234. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_aggregation/v2.1.0.prompt.md +193 -0
  235. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_aggregation/v2.2.0.prompt.md +206 -0
  236. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v1.0.0-deprecated.prompt.md +66 -0
  237. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v1.0.0.prompt.md +43 -0
  238. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v1.1.0.prompt.md +46 -0
  239. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.0.0-deprecated.prompt.md +64 -0
  240. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.0.0.prompt.md +39 -0
  241. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.1.0.prompt.md +39 -0
  242. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.2.0.prompt.md +47 -0
  243. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.3.0.prompt.md +58 -0
  244. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.0.2.prompt.md +254 -0
  245. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.1.0.prompt.md +274 -0
  246. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.2.0.prompt.md +279 -0
  247. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v1.0.0.prompt.md +73 -0
  248. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v2.0.0.prompt.md +86 -0
  249. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v3.0.0.prompt.md +97 -0
  250. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v3.1.0.prompt.md +119 -0
  251. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v3.2.0.prompt.md +123 -0
  252. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v3.3.0.prompt.md +127 -0
  253. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main/v1.0.0.prompt.md +14 -0
  254. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main/v1.1.0.prompt.md +24 -0
  255. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main/v1.2.0.prompt.md +29 -0
  256. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main_expert/v1.0.0.prompt.md +11 -0
  257. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main_expert/v1.1.0.prompt.md +21 -0
  258. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main_expert/v1.2.0.prompt.md +25 -0
  259. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_optimizer_judge/v1.0.0.prompt.md +37 -0
  260. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_optimizer_judge/v1.1.0.prompt.md +40 -0
  261. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_optimizer_judge/v1.2.0.prompt.md +36 -0
  262. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_should_generate/v1.0.0.prompt.md +45 -0
  263. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_should_generate/v2.0.0.prompt.md +81 -0
  264. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_should_generate/v3.0.0.prompt.md +80 -0
  265. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_should_generate_expert/v1.0.0.prompt.md +34 -0
  266. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_deduplication/v1.0.0.prompt.md +116 -0
  267. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_should_generate/v1.0.0.prompt.md +33 -0
  268. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_should_generate_override/v1.0.0.prompt.md +16 -0
  269. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_update_instruction_start/v1.0.0.prompt.md +140 -0
  270. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_update_instruction_start/v1.1.0.prompt.md +160 -0
  271. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_update_main/v1.0.0.prompt.md +14 -0
  272. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/query_reformulation/v1.0.0.prompt.md +19 -0
  273. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/rerank_relevance/v1.1.0.prompt.md +44 -0
  274. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/shadow_comparison/v1.0.0.prompt.md +43 -0
  275. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/shadow_content_evaluation/v1.0.0.prompt.md +33 -0
  276. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_evaluation/prompt_evaluation_dataset/feedback_extraction_main_v1.jsonl +10 -0
  277. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_evaluation/prompt_evaluation_dataset/profile_update_main_v1.jsonl +10 -0
  278. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_manager.py +280 -0
  279. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_schema.py +11 -0
  280. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/_eval_health.py +131 -0
  281. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/agent_success_evaluation_constants.py +60 -0
  282. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/agent_success_evaluation_service.py +228 -0
  283. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/agent_success_evaluation_utils.py +87 -0
  284. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/agent_success_evaluator.py +372 -0
  285. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/delayed_group_evaluator.py +156 -0
  286. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/group_evaluation_runner.py +340 -0
  287. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/regen_jobs.py +471 -0
  288. package/plugin/vendor/reflexio/reflexio/server/services/base_generation_service.py +1626 -0
  289. package/plugin/vendor/reflexio/reflexio/server/services/braintrust/__init__.py +0 -0
  290. package/plugin/vendor/reflexio/reflexio/server/services/braintrust/_cron.py +196 -0
  291. package/plugin/vendor/reflexio/reflexio/server/services/braintrust/_encryption.py +101 -0
  292. package/plugin/vendor/reflexio/reflexio/server/services/braintrust/client.py +167 -0
  293. package/plugin/vendor/reflexio/reflexio/server/services/braintrust/service.py +281 -0
  294. package/plugin/vendor/reflexio/reflexio/server/services/configurator/base_configurator.py +179 -0
  295. package/plugin/vendor/reflexio/reflexio/server/services/configurator/config_storage.py +62 -0
  296. package/plugin/vendor/reflexio/reflexio/server/services/configurator/configurator.py +87 -0
  297. package/plugin/vendor/reflexio/reflexio/server/services/configurator/local_file_config_storage.py +187 -0
  298. package/plugin/vendor/reflexio/reflexio/server/services/configurator/test_config_storage.py +162 -0
  299. package/plugin/vendor/reflexio/reflexio/server/services/deduplication_utils.py +112 -0
  300. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/__init__.py +0 -0
  301. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/distribution.py +33 -0
  302. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/eval_sampler.py +126 -0
  303. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/group_aggregation.py +192 -0
  304. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/hero_state.py +75 -0
  305. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/rule_attribution.py +97 -0
  306. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/service.py +515 -0
  307. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/shadow_aggregation.py +90 -0
  308. package/plugin/vendor/reflexio/reflexio/server/services/extraction/__init__.py +0 -0
  309. package/plugin/vendor/reflexio/reflexio/server/services/extraction/agent_run_records.py +91 -0
  310. package/plugin/vendor/reflexio/reflexio/server/services/extraction/invariants.py +303 -0
  311. package/plugin/vendor/reflexio/reflexio/server/services/extraction/outcome.py +25 -0
  312. package/plugin/vendor/reflexio/reflexio/server/services/extraction/pending_tool_call_dispatch.py +351 -0
  313. package/plugin/vendor/reflexio/reflexio/server/services/extraction/plan.py +138 -0
  314. package/plugin/vendor/reflexio/reflexio/server/services/extraction/prior_answer_search.py +217 -0
  315. package/plugin/vendor/reflexio/reflexio/server/services/extraction/resumable_agent.py +468 -0
  316. package/plugin/vendor/reflexio/reflexio/server/services/extraction/resume_scheduler.py +171 -0
  317. package/plugin/vendor/reflexio/reflexio/server/services/extraction/resume_worker.py +777 -0
  318. package/plugin/vendor/reflexio/reflexio/server/services/extraction/tools.py +1125 -0
  319. package/plugin/vendor/reflexio/reflexio/server/services/extractor_config_utils.py +91 -0
  320. package/plugin/vendor/reflexio/reflexio/server/services/extractor_interaction_utils.py +251 -0
  321. package/plugin/vendor/reflexio/reflexio/server/services/generation_service.py +689 -0
  322. package/plugin/vendor/reflexio/reflexio/server/services/operation_state_utils.py +835 -0
  323. package/plugin/vendor/reflexio/reflexio/server/services/playbook/README.md +89 -0
  324. package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_aggregator.py +1388 -0
  325. package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_consolidator.py +960 -0
  326. package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_extractor.py +436 -0
  327. package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_generation_service.py +808 -0
  328. package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_service_constants.py +28 -0
  329. package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_service_utils.py +362 -0
  330. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/__init__.py +24 -0
  331. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/assistant_webhook.py +246 -0
  332. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/gepa_adapter.py +291 -0
  333. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/judge.py +97 -0
  334. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/models.py +96 -0
  335. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/optimizer.py +645 -0
  336. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/rollout.py +35 -0
  337. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/scenario_resolver.py +93 -0
  338. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/scheduler.py +174 -0
  339. package/plugin/vendor/reflexio/reflexio/server/services/pre_retrieval/__init__.py +26 -0
  340. package/plugin/vendor/reflexio/reflexio/server/services/pre_retrieval/_document_expander.py +179 -0
  341. package/plugin/vendor/reflexio/reflexio/server/services/pre_retrieval/_query_reformulator.py +297 -0
  342. package/plugin/vendor/reflexio/reflexio/server/services/profile/profile_deduplicator.py +741 -0
  343. package/plugin/vendor/reflexio/reflexio/server/services/profile/profile_extractor.py +462 -0
  344. package/plugin/vendor/reflexio/reflexio/server/services/profile/profile_generation_service.py +734 -0
  345. package/plugin/vendor/reflexio/reflexio/server/services/profile/profile_generation_service_utils.py +290 -0
  346. package/plugin/vendor/reflexio/reflexio/server/services/reflection/__init__.py +17 -0
  347. package/plugin/vendor/reflexio/reflexio/server/services/reflection/reflection_extractor.py +247 -0
  348. package/plugin/vendor/reflexio/reflexio/server/services/reflection/reflection_service.py +800 -0
  349. package/plugin/vendor/reflexio/reflexio/server/services/reflection/reflection_service_utils.py +146 -0
  350. package/plugin/vendor/reflexio/reflexio/server/services/retrieval/__init__.py +0 -0
  351. package/plugin/vendor/reflexio/reflexio/server/services/retrieval/relevance_floor.py +70 -0
  352. package/plugin/vendor/reflexio/reflexio/server/services/search/__init__.py +0 -0
  353. package/plugin/vendor/reflexio/reflexio/server/services/service_utils.py +671 -0
  354. package/plugin/vendor/reflexio/reflexio/server/services/shadow_comparison/__init__.py +1 -0
  355. package/plugin/vendor/reflexio/reflexio/server/services/shadow_comparison/judge.py +184 -0
  356. package/plugin/vendor/reflexio/reflexio/server/services/shadow_comparison/outcome.py +81 -0
  357. package/plugin/vendor/reflexio/reflexio/server/services/storage/constants.py +2 -0
  358. package/plugin/vendor/reflexio/reflexio/server/services/storage/error.py +11 -0
  359. package/plugin/vendor/reflexio/reflexio/server/services/storage/retention.py +154 -0
  360. package/plugin/vendor/reflexio/reflexio/server/services/storage/retention_mixin.py +155 -0
  361. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/__init__.py +59 -0
  362. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_agent_run.py +1253 -0
  363. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_base.py +1945 -0
  364. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_extras.py +600 -0
  365. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_operations.py +346 -0
  366. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_playbook.py +1378 -0
  367. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_profiles.py +747 -0
  368. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_requests.py +263 -0
  369. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_shadow_verdicts.py +193 -0
  370. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_share_links.py +166 -0
  371. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_stall_state.py +217 -0
  372. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/__init__.py +153 -0
  373. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_agent_run.py +372 -0
  374. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_base.py +71 -0
  375. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_extras.py +235 -0
  376. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_operations.py +170 -0
  377. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_playbook.py +677 -0
  378. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_profiles.py +250 -0
  379. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_requests.py +154 -0
  380. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_shadow_verdicts.py +130 -0
  381. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_share_links.py +93 -0
  382. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_stall_state.py +76 -0
  383. package/plugin/vendor/reflexio/reflexio/server/services/unified_search_service.py +568 -0
  384. package/plugin/vendor/reflexio/reflexio/server/site_var/README.md +77 -0
  385. package/plugin/vendor/reflexio/reflexio/server/site_var/feature_flags.py +116 -0
  386. package/plugin/vendor/reflexio/reflexio/server/site_var/site_var_manager.py +263 -0
  387. package/plugin/vendor/reflexio/reflexio/server/site_var/site_var_sources/feature_flags.json +13 -0
  388. package/plugin/vendor/reflexio/reflexio/server/site_var/site_var_sources/llm_model_setting.json +7 -0
  389. package/plugin/vendor/reflexio/reflexio/server/tracing.py +158 -0
  390. package/plugin/vendor/reflexio/reflexio/server/usage_metrics.py +113 -0
  391. package/plugin/vendor/reflexio/reflexio/server/uvicorn_logging.py +76 -0
  392. package/plugin/vendor/reflexio/reflexio/test_support/__init__.py +1 -0
  393. package/plugin/vendor/reflexio/reflexio/test_support/llm_fixtures.py +62 -0
  394. package/plugin/vendor/reflexio/reflexio/test_support/llm_mock.py +242 -0
  395. package/plugin/vendor/reflexio/reflexio/test_support/llm_model_registry.py +129 -0
  396. package/plugin/vendor/reflexio/reflexio/test_support/skip_decorators.py +43 -0
@@ -0,0 +1,31 @@
1
+ # openclaw-embedded vs integrations/openclaw
2
+
3
+ Two Openclaw integrations ship in the same repo. This doc explains when to pick which.
4
+
5
+ | Aspect | `integrations/openclaw/` (federated) | `integrations/openclaw-embedded/` (this plugin) |
6
+ |---|---|---|
7
+ | Reflexio server required | Yes | No |
8
+ | Storage | Reflexio server (SQLite / Supabase / pgvector) | Openclaw's native memory (`.md` files at `.reflexio/`) |
9
+ | Extraction LLM | Server-side (uses Reflexio's prompt bank on the server) | Openclaw's agent LLM + sub-agents (via `api.runtime.subagent.run()`) |
10
+ | Dedup | Server-side batch + cross-instance aggregation | Per-write shallow + daily full-sweep |
11
+ | Multi-user / multi-agent | Yes (each `agentId` → distinct `user_id`) | No (one instance = one user) |
12
+ | Cross-instance playbook sharing | Yes (aggregation → agent playbooks) | No (v1 out of scope) |
13
+ | External dependencies | `reflexio` CLI, LLM API key on the server, running server | Openclaw only (agent's own LLM) |
14
+ | CLI integration | Shells out to `reflexio search / publish / aggregate` | None — pure in-Openclaw |
15
+ | Target user | Teams with many agent instances behind a shared server | Solo user, no infrastructure |
16
+
17
+ ## Pick `openclaw-embedded` if:
18
+
19
+ - You don't want to run a Reflexio server.
20
+ - You only have one agent instance, or memory sharing across instances doesn't matter.
21
+ - You prefer fewer moving parts.
22
+
23
+ ## Pick `integrations/openclaw/` (federated) if:
24
+
25
+ - You have or want to run a Reflexio server.
26
+ - You have multiple agent instances and want cross-instance playbook sharing via aggregation.
27
+ - You want multi-user support (each human treated as a distinct Reflexio user).
28
+
29
+ ## Can both be installed?
30
+
31
+ Yes — no conflict. Different hook names, different skill names, different cron jobs, different extraPaths. But installing both is pointless: they serve the same purpose by different means. Pick one.
@@ -0,0 +1,47 @@
1
+ # Future Work
2
+
3
+ Items explicitly deferred from v1. Each has a rationale for why it's not in scope yet.
4
+
5
+ ## Convert to code-plugin (`definePluginEntry` + `registerCommand`)
6
+
7
+ **Rationale:** V1 uses the legacy "Hook-only / Non-capability" plugin pattern (matches the `self-improving-agent` example). Converting to a code-plugin would gain:
8
+ - Real slash commands (`/reflexio-consolidate` instead of `/skill reflexio-consolidate`)
9
+ - `api.runtime.config.write` for programmatic config changes (currently we use `exec` + `openclaw config set`)
10
+ - Potentially cleaner cron registration (if the plugin SDK exposes it)
11
+
12
+ **Cost:** TypeScript plugin scaffolding, SDK version pinning, build pipeline.
13
+
14
+ ## Cross-instance playbook sharing
15
+
16
+ **Rationale:** V1 treats one Openclaw instance as one user. Sharing playbooks across instances would require infrastructure (sync service, shared extraPath on network storage, or federation via Reflexio server). V1 punts this to `integrations/openclaw/` (federated) which already solves it.
17
+
18
+ **Option for v2:** Lightweight git-based sync — checkpoint `.reflexio/` to a shared git remote; other instances pull periodically.
19
+
20
+ ## Participation in Openclaw's native dreaming
21
+
22
+ **Rationale:** Openclaw's memory-core plugin has a built-in "dreaming" consolidation system (opt-in, runs daily at 3am). Our full-sweep consolidation is a parallel structure, not a participant. Participating would require a plugin API not yet exposed by memory-core.
23
+
24
+ **Watch for:** an `api.runtime.memoryCore.dreaming.registerConsolidator()` or similar in future Openclaw releases.
25
+
26
+ ## Ported expert / should-generate prompt variants
27
+
28
+ **Rationale:** Reflexio's prompt_bank has `playbook_extraction_main_expert` and `playbook_should_generate`. V1 skips these as YAGNI:
29
+ - Expert-mode is for advanced scenarios beyond v1.
30
+ - `should_generate` is a cost-saving pre-check; extraction returning an empty list achieves the same at small additional cost.
31
+
32
+ ## Removed frontmatter fields
33
+
34
+ - `source_sessions` — dead-pointer problem; LLM has no reliable way to recover session keys long after the fact.
35
+ - `confirmation_kind` — no v1 consumer.
36
+ - `confidence` — LLM self-assigned confidence is unreliable; omit rather than hallucinate precision.
37
+ - `tags` — agent-assigned, not normalized; semantic search on body content suffices.
38
+
39
+ Add any of these in v2 only if a concrete consumer materializes.
40
+
41
+ ## Native Windows support
42
+
43
+ **Rationale:** V1 used shell scripts that assumed Unix. V1.1 moved to registered tools (`reflexio_write_profile`, `reflexio_write_playbook`), which are platform-agnostic. This item is resolved.
44
+
45
+ ## Playbook TTL / expiration
46
+
47
+ **Rationale:** The 99% case is "playbooks never expire". V1 drops the field; occasional task-specific playbooks encode time bounds in the `## When` text. V2 could add structured playbook expiration if a concrete use case materializes.
@@ -0,0 +1,52 @@
1
+ # Porting Notes: Reflexio prompt_bank → openclaw-embedded prompts
2
+
3
+ Track every deviation from source prompts so maintainers can re-apply adaptations when upstream versions bump.
4
+
5
+ ## profile_extraction.md
6
+
7
+ **Source:** `open_source/reflexio/reflexio/server/prompt/prompt_bank/profile_update_instruction_start/v1.0.0.prompt.md`
8
+
9
+ **Initial port:** 2026-04-16
10
+
11
+ **Changes from source:**
12
+
13
+ - **Output format**: `StructuredProfilesOutput` JSON with `profiles: list[ProfileAddItem{content, ttl, metadata}]` → array of `{topic_kebab, content, ttl}` objects that drive `reflexio_write_profile` tool calls per item.
14
+ - **Dropped fields**: `custom_features` dict, `metadata` field. Our frontmatter doesn't carry these.
15
+ - **Added guidance**: slug generation rules — kebab-case, ≤48 chars, `[a-z0-9][a-z0-9-]*`.
16
+ - **Kept verbatim**: TTL enum semantics, "do NOT re-extract existing profiles" constraint, extraction criteria (what counts as a profile signal).
17
+ - **Variable substitution**: `existing_profiles` in source was populated by Reflexio server from SQL. In our port, the Flow C sub-agent runs `memory_search(top_k=10, filter={type: profile})` and injects results into the `{existing_profiles_context}` slot.
18
+
19
+ **On upstream upgrade (e.g., to v2.0.0):** diff the source file, re-apply the output-format, dropped-fields, added-guidance deltas.
20
+
21
+ ## playbook_extraction.md
22
+
23
+ **Source:** `open_source/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v2.0.0.prompt.md`
24
+
25
+ **Initial port:** 2026-04-16
26
+
27
+ **Changes from source:**
28
+
29
+ - **Output schema**: 6-field (`trigger`, `instruction`, `pitfall`, `rationale`, `blocking_issue`, `content`) → 3-field (`When`, `What`, `Why`).
30
+ - **Rationale (preserved in design spec section 9):** forcing DO+DON'T symmetry (instruction + pitfall) often leads the LLM to hallucinate a symmetric "don't" when only a "do" was actually observed. Collapsing to `## What` lets the content carry whichever was observed.
31
+ - **Autoregressive ordering adapted**: source generates rationale first, then structured fields, then content. Our port generates `Why → What → When` internally but emits `When → What → Why` in the document.
32
+ - **Dropped**: expert-mode branches (separate `playbook_extraction_context_expert/` source); `existing_feedbacks` variable (upstream v2 already removed it).
33
+ - **Strengthened**: explicit "no confirmation → no playbook" gate. Our skill and prompt both enforce that a correction without a following confirmation is NOT grounds for writing a playbook.
34
+ - **Variable substitution**: `{transcript}` is passed by the Flow C sub-agent; no other runtime variables.
35
+
36
+ **On upstream upgrade:** diff the source file, re-apply field collapse, ordering adaptation, expert-branch drop, confirmation gate.
37
+
38
+ ## shallow_dedup_pairwise.md
39
+
40
+ **Source:** New file; informed by `profile_deduplication/*.prompt.md` and `playbook_deduplication/*.prompt.md`.
41
+
42
+ - Simplified to strictly pairwise (candidate vs. top-1 neighbor). Reflexio's native dedup is N-way group-based.
43
+ - Output schema: `{decision: enum, merged_content?, merged_slug?, rationale}`.
44
+ - Our full-sweep cron handles N-way; shallow stays in the hot path.
45
+
46
+ ## full_consolidation.md
47
+
48
+ **Source:** New file; informed by `playbook_aggregation/v1.0.0.prompt.md`.
49
+
50
+ - Adapted for single-instance n-way clustering; Reflexio's aggregation is cross-instance.
51
+ - Output schema: `{action: enum, merged_content?, merged_slug?, ids_merged_in, ids_kept_separate, rationale}`.
52
+ - Cluster size capped at 10.
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env bash
2
+ # openclaw-embedded install.sh — plugin installation.
3
+ # Skills are served from the extension dir via the manifest.
4
+ # Agents are injected via extraSystemPrompt at runtime.
5
+ # HEARTBEAT.md is appended on first agent session by setup.ts.
6
+ set -euo pipefail
7
+
8
+ PLUGIN_DIR="$(cd "$(dirname "$0")/../plugin" && pwd)"
9
+ OPENCLAW_HOME="${OPENCLAW_HOME:-$HOME/.openclaw}"
10
+
11
+ die() { echo "error: $*" >&2; exit 1; }
12
+ info() { echo "==> $*"; }
13
+
14
+ # 1. Prereq checks
15
+ info "Checking prerequisites..."
16
+ command -v openclaw >/dev/null || die "openclaw CLI required but not found on PATH"
17
+ command -v node >/dev/null || die "node required but not found on PATH"
18
+
19
+ # 2. Install the plugin
20
+ info "Installing plugin..."
21
+ openclaw plugins uninstall --force reflexio-embedded 2>/dev/null || true
22
+ rm -rf "$OPENCLAW_HOME/extensions/reflexio-embedded"
23
+ openclaw plugins install "$PLUGIN_DIR"
24
+ openclaw plugins enable reflexio-embedded 2>/dev/null || true
25
+
26
+ # 3. Enable active-memory plugin and configure per-agent targeting
27
+ info "Enabling active-memory plugin..."
28
+ openclaw plugins enable active-memory || \
29
+ echo "warning: active-memory enable failed — plugin may already be enabled or unavailable; continuing"
30
+
31
+ info "Configuring active-memory agent targeting..."
32
+ openclaw config set plugins.entries.active-memory.config.agents '["*"]' || \
33
+ echo "warning: active-memory agent targeting config failed"
34
+
35
+ info "Registering .reflexio/ as memory extraPath..."
36
+ openclaw config set agents.defaults.memorySearch.extraPaths '[".reflexio/"]' --strict-json || \
37
+ echo "warning: extraPath registration failed"
38
+
39
+ # 4. Restart gateway
40
+ info "Restarting openclaw gateway..."
41
+ openclaw gateway restart
42
+
43
+ # 5. Verify
44
+ info "Verification:"
45
+ if openclaw plugins inspect reflexio-embedded 2>/dev/null | grep -q "Status: loaded"; then
46
+ info " ✓ plugin registered and loaded"
47
+ else
48
+ echo " ⚠ plugin did not reach 'loaded' status; run 'openclaw plugins inspect reflexio-embedded' to debug"
49
+ fi
50
+
51
+ info "Installation complete."
52
+ info "Skills are served from the extension dir. HEARTBEAT.md is set up on first agent session."
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env bash
2
+ # openclaw-embedded uninstall.sh — reverses install.sh.
3
+ # Leaves workspace/.reflexio/ user data intact unless --purge is passed.
4
+ set -euo pipefail
5
+
6
+ OPENCLAW_HOME="${OPENCLAW_HOME:-$HOME/.openclaw}"
7
+ PURGE_DATA="${1:-}"
8
+
9
+ info() { echo "==> $*"; }
10
+
11
+ info "Disabling plugin..."
12
+ openclaw plugins disable reflexio-embedded 2>/dev/null || echo "(already disabled)"
13
+
14
+ info "Uninstalling plugin..."
15
+ openclaw plugins uninstall --force reflexio-embedded 2>/dev/null || echo "(already uninstalled)"
16
+ rm -rf "$OPENCLAW_HOME/extensions/reflexio-embedded"
17
+
18
+ info "Cleaning up state files..."
19
+ rm -f "$OPENCLAW_HOME/reflexio-consolidation-state.json"
20
+
21
+ info "Removing heartbeat entry..."
22
+ if [[ -f "$OPENCLAW_HOME/workspace/HEARTBEAT.md" ]]; then
23
+ sed -i '' '/## Reflexio Consolidation Check/,/^$/d' "$OPENCLAW_HOME/workspace/HEARTBEAT.md" 2>/dev/null || true
24
+ fi
25
+
26
+ if [[ "$PURGE_DATA" == "--purge" ]]; then
27
+ info "Purging .reflexio/ user data per --purge flag..."
28
+ rm -rf "$PWD/.reflexio"
29
+ else
30
+ info "User data at .reflexio/ preserved. Use --purge to delete it too."
31
+ fi
32
+
33
+ info "Restarting openclaw gateway..."
34
+ openclaw gateway restart
35
+
36
+ info "Uninstall complete."
@@ -0,0 +1,135 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
+ import * as fs from "node:fs";
3
+ import * as os from "node:os";
4
+ import * as path from "node:path";
5
+
6
+ import { runConsolidation } from "../plugin/lib/consolidate.ts";
7
+ import type { CommandRunner, InferFn, MemorySearchResult } from "../plugin/lib/openclaw-cli.ts";
8
+
9
+ let workspace: string;
10
+
11
+ beforeEach(() => {
12
+ workspace = fs.mkdtempSync(path.join(os.tmpdir(), "rfx-cons-"));
13
+ fs.mkdirSync(path.join(workspace, ".reflexio", "profiles"), { recursive: true });
14
+ fs.mkdirSync(path.join(workspace, ".reflexio", "playbooks"), { recursive: true });
15
+ });
16
+
17
+ afterEach(() => {
18
+ fs.rmSync(workspace, { recursive: true, force: true });
19
+ });
20
+
21
+ function writeTestProfile(name: string, id: string, body: string, ttl = "infinity"): string {
22
+ const filePath = path.join(workspace, ".reflexio", "profiles", name);
23
+ const expires = ttl === "infinity" ? "never" : "2099-01-01";
24
+ fs.writeFileSync(filePath, `---\ntype: profile\nid: ${id}\ncreated: 2026-04-20T00:00:00Z\nttl: ${ttl}\nexpires: ${expires}\n---\n\n${body}\n`);
25
+ return filePath;
26
+ }
27
+
28
+ function createMockRunner(searchResults: MemorySearchResult[]): CommandRunner {
29
+ return async (argv) => {
30
+ if (argv.includes("memory") && argv.includes("search")) {
31
+ return { stdout: JSON.stringify({ results: searchResults }), stderr: "", code: 0 };
32
+ }
33
+ if (argv.includes("memory") && argv.includes("index")) {
34
+ return { stdout: "", stderr: "", code: 0 };
35
+ }
36
+ return { stdout: "", stderr: "unexpected command", code: 1 };
37
+ };
38
+ }
39
+
40
+ describe("runConsolidation", () => {
41
+ it("does nothing with fewer than 2 files", async () => {
42
+ writeTestProfile("single.md", "prof_aaa0", "Only one fact.");
43
+ const runner = createMockRunner([]);
44
+ const inferFn: InferFn = async () => null;
45
+
46
+ const result = await runConsolidation({ workspaceDir: workspace, runner, inferFn });
47
+ expect(result.filesDeleted).toBe(0);
48
+ expect(result.filesWritten).toBe(0);
49
+ });
50
+
51
+ it("clusters by relative path (real-world memory search format)", async () => {
52
+ const p1 = writeTestProfile("food-a.md", "prof_aaa0", "User likes Chinese food.");
53
+ const p2 = writeTestProfile("food-b.md", "prof_bbb0", "User likes Chinese cuisine.");
54
+
55
+ // Memory search returns RELATIVE paths (as in production)
56
+ const searchResults: MemorySearchResult[] = [
57
+ { path: ".reflexio/profiles/food-b.md", score: 0.8, snippet: "User likes Chinese cuisine.", startLine: 1, endLine: 5, source: "memory" },
58
+ ];
59
+ const runner = createMockRunner(searchResults);
60
+
61
+ const inferFn: InferFn = async () => JSON.stringify({
62
+ action: "consolidate",
63
+ facts: [{ slug: "food-preference-chinese", body: "User likes Chinese food and cuisine.", source_ids: ["prof_aaa0", "prof_bbb0"] }],
64
+ ids_to_delete: ["prof_aaa0", "prof_bbb0"],
65
+ rationale: "Merged duplicate Chinese food preferences.",
66
+ });
67
+
68
+ const result = await runConsolidation({ workspaceDir: workspace, runner, inferFn });
69
+ expect(result.filesWritten).toBe(1);
70
+ expect(result.filesDeleted).toBe(2);
71
+ expect(fs.existsSync(p1)).toBe(false);
72
+ expect(fs.existsSync(p2)).toBe(false);
73
+
74
+ const profileDir = path.join(workspace, ".reflexio", "profiles");
75
+ const remaining = fs.readdirSync(profileDir).filter((f) => f.endsWith(".md"));
76
+ expect(remaining).toHaveLength(1);
77
+ const content = fs.readFileSync(path.join(profileDir, remaining[0]), "utf8");
78
+ expect(content).toContain("User likes Chinese food and cuisine.");
79
+ });
80
+
81
+ it("keeps all when LLM returns keep_all", async () => {
82
+ const p1 = writeTestProfile("food.md", "prof_aaa0", "User likes Chinese food.");
83
+ const p2 = writeTestProfile("mode.md", "prof_bbb0", "User prefers dark mode.");
84
+
85
+ const searchResults: MemorySearchResult[] = [
86
+ { path: ".reflexio/profiles/mode.md", score: 0.5, snippet: "User prefers dark mode.", startLine: 1, endLine: 5, source: "memory" },
87
+ ];
88
+ const runner = createMockRunner(searchResults);
89
+ const inferFn: InferFn = async () => JSON.stringify({ action: "keep_all" });
90
+
91
+ const result = await runConsolidation({ workspaceDir: workspace, runner, inferFn });
92
+ expect(result.filesDeleted).toBe(0);
93
+ expect(result.filesWritten).toBe(0);
94
+ expect(fs.existsSync(p1)).toBe(true);
95
+ expect(fs.existsSync(p2)).toBe(true);
96
+ });
97
+
98
+ it("skips cluster when inferFn fails", async () => {
99
+ writeTestProfile("a.md", "prof_aaa0", "Fact A.");
100
+ writeTestProfile("b.md", "prof_bbb0", "Fact B.");
101
+
102
+ const searchResults: MemorySearchResult[] = [
103
+ { path: ".reflexio/profiles/b.md", score: 0.8, snippet: "Fact B.", startLine: 1, endLine: 5, source: "memory" },
104
+ ];
105
+ const runner = createMockRunner(searchResults);
106
+ const inferFn: InferFn = async () => null;
107
+
108
+ const result = await runConsolidation({ workspaceDir: workspace, runner, inferFn });
109
+ expect(result.filesDeleted).toBe(0);
110
+ });
111
+
112
+ it("filters out self-matches from search results", async () => {
113
+ writeTestProfile("food-a.md", "prof_aaa0", "User likes Chinese food.");
114
+ writeTestProfile("food-b.md", "prof_bbb0", "User likes Chinese cuisine.");
115
+
116
+ // Search returns the file itself as the top result (self-match) + a real neighbor
117
+ const searchResults: MemorySearchResult[] = [
118
+ { path: ".reflexio/profiles/food-a.md", score: 1.0, snippet: "User likes Chinese food.", startLine: 1, endLine: 5, source: "memory" },
119
+ { path: ".reflexio/profiles/food-b.md", score: 0.8, snippet: "User likes Chinese cuisine.", startLine: 1, endLine: 5, source: "memory" },
120
+ ];
121
+ const runner = createMockRunner(searchResults);
122
+
123
+ const inferFn: InferFn = async () => JSON.stringify({
124
+ action: "consolidate",
125
+ facts: [{ slug: "food-chinese", body: "User likes Chinese food.", source_ids: ["prof_aaa0", "prof_bbb0"] }],
126
+ ids_to_delete: ["prof_aaa0", "prof_bbb0"],
127
+ rationale: "Dedup.",
128
+ });
129
+
130
+ const result = await runConsolidation({ workspaceDir: workspace, runner, inferFn });
131
+ expect(result.profilesClustered).toBe(2);
132
+ expect(result.filesWritten).toBe(1);
133
+ expect(result.filesDeleted).toBe(2);
134
+ });
135
+ });
@@ -0,0 +1,104 @@
1
+ import { describe, it, expect } from "vitest";
2
+
3
+ import { preprocessQuery, judgeDedup, extractId } from "../plugin/lib/dedup.ts";
4
+ import type { InferFn } from "../plugin/lib/openclaw-cli.ts";
5
+
6
+ function createMockInferFn(result: string | null): InferFn {
7
+ return async () => {
8
+ if (result === null) return null;
9
+ return result;
10
+ };
11
+ }
12
+
13
+ describe("preprocessQuery", () => {
14
+ it("returns LLM-rewritten query on success", async () => {
15
+ const inferFn = createMockInferFn(
16
+ "User dietary preference vegan. Related: plant-based, no animal products"
17
+ );
18
+ const result = await preprocessQuery("Oh sorry I typed it wrong, I do like vegan food", inferFn);
19
+ expect(result).toBe(
20
+ "User dietary preference vegan. Related: plant-based, no animal products"
21
+ );
22
+ });
23
+
24
+ it("falls back to raw text when infer fails", async () => {
25
+ const inferFn = createMockInferFn(null);
26
+ const raw = "I like apple juice";
27
+ const result = await preprocessQuery(raw, inferFn);
28
+ expect(result).toBe(raw);
29
+ });
30
+
31
+ it("falls back to raw text when infer returns empty string", async () => {
32
+ const inferFn = createMockInferFn("");
33
+ const raw = "timezone is PST";
34
+ const result = await preprocessQuery(raw, inferFn);
35
+ expect(result).toBe(raw);
36
+ });
37
+ });
38
+
39
+ describe("judgeDedup", () => {
40
+ it("returns merge_and_resolve when LLM merges", async () => {
41
+ const inferFn = createMockInferFn('{"decision": "merge_and_resolve", "resolved": "User does not like Greek food."}');
42
+ const result = await judgeDedup("User does not like Greek food.", "User likes Greek food.", inferFn);
43
+ expect(result).toEqual({ decision: "merge_and_resolve", resolved: "User does not like Greek food." });
44
+ });
45
+
46
+ it("returns merge_and_resolve preserving non-contradicted facts", async () => {
47
+ const inferFn = createMockInferFn('{"decision": "merge_and_resolve", "resolved": "User does not like Chinese food. User likes Greek food."}');
48
+ const result = await judgeDedup(
49
+ "User does not like Chinese food.",
50
+ "User likes Chinese food. User likes Greek food.",
51
+ inferFn
52
+ );
53
+ expect(result).toEqual({
54
+ decision: "merge_and_resolve",
55
+ resolved: "User does not like Chinese food. User likes Greek food.",
56
+ });
57
+ });
58
+
59
+ it("returns keep_both for different topics", async () => {
60
+ const inferFn = createMockInferFn('{"decision": "keep_both"}');
61
+ const result = await judgeDedup("User likes dark mode", "User is a developer", inferFn);
62
+ expect(result).toEqual({ decision: "keep_both" });
63
+ });
64
+
65
+ it("defaults to keep_both when infer fails", async () => {
66
+ const inferFn = createMockInferFn(null);
67
+ const result = await judgeDedup("A", "B", inferFn);
68
+ expect(result).toEqual({ decision: "keep_both" });
69
+ });
70
+
71
+ it("defaults to keep_both on malformed JSON", async () => {
72
+ const inferFn = createMockInferFn("I think they are related");
73
+ const result = await judgeDedup("A", "B", inferFn);
74
+ expect(result).toEqual({ decision: "keep_both" });
75
+ });
76
+
77
+ it("defaults to keep_both on merge_and_resolve with empty resolved", async () => {
78
+ const inferFn = createMockInferFn('{"decision": "merge_and_resolve", "resolved": ""}');
79
+ const result = await judgeDedup("A", "B", inferFn);
80
+ expect(result).toEqual({ decision: "keep_both" });
81
+ });
82
+
83
+ it("defaults to keep_both on unexpected decision value", async () => {
84
+ const inferFn = createMockInferFn('{"decision": "supersede"}');
85
+ const result = await judgeDedup("A", "B", inferFn);
86
+ expect(result).toEqual({ decision: "keep_both" });
87
+ });
88
+ });
89
+
90
+ describe("extractId", () => {
91
+ it("extracts prof_ id from snippet", () => {
92
+ const snippet = "---\ntype: profile\nid: prof_sdtk\ncreated: 2026\n---\nContent";
93
+ expect(extractId(snippet)).toBe("prof_sdtk");
94
+ });
95
+
96
+ it("extracts pbk_ id from snippet", () => {
97
+ const snippet = "---\ntype: playbook\nid: pbk_az4k\n---\nContent";
98
+ expect(extractId(snippet)).toBe("pbk_az4k");
99
+ });
100
+
101
+ it("returns null when no id found", () => {
102
+ expect(extractId("no frontmatter here")).toBeNull();
103
+ });
104
+ });
@@ -0,0 +1,175 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
+ import { generateNanoid, validateSlug, validateTtl, computeExpires, writeProfileFile, writePlaybookFile, deleteFile } from "../plugin/lib/io.ts";
3
+ import * as fs from "node:fs";
4
+ import * as os from "node:os";
5
+ import * as path from "node:path";
6
+
7
+ describe("generateNanoid", () => {
8
+ it("returns a 4-character string of [a-z0-9]", () => {
9
+ const id = generateNanoid();
10
+ expect(id).toMatch(/^[a-z0-9]{4}$/);
11
+ });
12
+
13
+ it("produces different values across calls", () => {
14
+ const ids = new Set(Array.from({ length: 10 }, () => generateNanoid()));
15
+ expect(ids.size).toBeGreaterThan(1);
16
+ });
17
+ });
18
+
19
+ describe("validateSlug", () => {
20
+ it("accepts valid kebab-case slugs", () => {
21
+ expect(() => validateSlug("diet-vegetarian")).not.toThrow();
22
+ expect(() => validateSlug("abc")).not.toThrow();
23
+ expect(() => validateSlug("a1b2")).not.toThrow();
24
+ });
25
+
26
+ it("rejects empty string", () => {
27
+ expect(() => validateSlug("")).toThrow();
28
+ });
29
+
30
+ it("rejects uppercase", () => {
31
+ expect(() => validateSlug("Diet-Vegetarian")).toThrow();
32
+ });
33
+
34
+ it("rejects leading hyphen", () => {
35
+ expect(() => validateSlug("-diet")).toThrow();
36
+ });
37
+
38
+ it("rejects slashes", () => {
39
+ expect(() => validateSlug("foo/bar")).toThrow();
40
+ });
41
+
42
+ it("rejects strings longer than 48 chars", () => {
43
+ expect(() => validateSlug("a".repeat(49))).toThrow();
44
+ });
45
+ });
46
+
47
+ describe("validateTtl", () => {
48
+ it("accepts all valid TTL values", () => {
49
+ for (const ttl of ["one_day", "one_week", "one_month", "one_quarter", "one_year", "infinity"]) {
50
+ expect(() => validateTtl(ttl as any)).not.toThrow();
51
+ }
52
+ });
53
+
54
+ it("rejects invalid TTL", () => {
55
+ expect(() => validateTtl("one_millennium" as any)).toThrow();
56
+ });
57
+ });
58
+
59
+ describe("computeExpires", () => {
60
+ it("returns 'never' for infinity", () => {
61
+ expect(computeExpires("infinity", "2026-04-17T00:00:00Z")).toBe("never");
62
+ });
63
+
64
+ it("returns a date string for one_year", () => {
65
+ const result = computeExpires("one_year", "2026-04-17T00:00:00Z");
66
+ expect(result).toMatch(/^\d{4}-\d{2}-\d{2}$/);
67
+ expect(result).toBe("2027-04-17");
68
+ });
69
+
70
+ it("returns correct date for one_day", () => {
71
+ expect(computeExpires("one_day", "2026-04-17T00:00:00Z")).toBe("2026-04-18");
72
+ });
73
+ });
74
+
75
+ describe("writeProfileFile", () => {
76
+ let workspace: string;
77
+
78
+ beforeEach(() => {
79
+ workspace = fs.mkdtempSync(path.join(os.tmpdir(), "rfx-test-"));
80
+ fs.mkdirSync(path.join(workspace, ".reflexio", "profiles"), { recursive: true });
81
+ });
82
+
83
+ afterEach(() => {
84
+ fs.rmSync(workspace, { recursive: true, force: true });
85
+ });
86
+
87
+ it("creates a profile file with correct frontmatter", () => {
88
+ const result = writeProfileFile({
89
+ slug: "diet-vegan",
90
+ ttl: "infinity",
91
+ body: "User is vegan.",
92
+ workspace,
93
+ });
94
+ expect(fs.existsSync(result)).toBe(true);
95
+ const content = fs.readFileSync(result, "utf8");
96
+ expect(content).toContain("type: profile");
97
+ expect(content).toContain("id: prof_");
98
+ expect(content).toContain("ttl: infinity");
99
+ expect(content).toContain("expires: never");
100
+ expect(content).toContain("User is vegan.");
101
+ });
102
+
103
+ it("includes supersedes when provided", () => {
104
+ const result = writeProfileFile({
105
+ slug: "diet-vegan",
106
+ ttl: "infinity",
107
+ body: "User is vegan.",
108
+ supersedes: ["prof_abc1"],
109
+ workspace,
110
+ });
111
+ const content = fs.readFileSync(result, "utf8");
112
+ expect(content).toContain("supersedes: [prof_abc1]");
113
+ });
114
+
115
+ it("omits supersedes when not provided", () => {
116
+ const result = writeProfileFile({
117
+ slug: "diet-vegan",
118
+ ttl: "infinity",
119
+ body: "User is vegan.",
120
+ workspace,
121
+ });
122
+ const content = fs.readFileSync(result, "utf8");
123
+ expect(content).not.toContain("supersedes");
124
+ });
125
+
126
+ it("leaves no .tmp files on success", () => {
127
+ writeProfileFile({ slug: "test", ttl: "infinity", body: "x", workspace });
128
+ const tmps = fs.readdirSync(path.join(workspace, ".reflexio", "profiles"))
129
+ .filter((f) => f.includes(".tmp"));
130
+ expect(tmps).toHaveLength(0);
131
+ });
132
+ });
133
+
134
+ describe("writePlaybookFile", () => {
135
+ let workspace: string;
136
+
137
+ beforeEach(() => {
138
+ workspace = fs.mkdtempSync(path.join(os.tmpdir(), "rfx-test-"));
139
+ fs.mkdirSync(path.join(workspace, ".reflexio", "playbooks"), { recursive: true });
140
+ });
141
+
142
+ afterEach(() => {
143
+ fs.rmSync(workspace, { recursive: true, force: true });
144
+ });
145
+
146
+ it("creates a playbook file with correct frontmatter (no ttl/expires)", () => {
147
+ const result = writePlaybookFile({
148
+ slug: "commit-no-trailers",
149
+ body: "## When\nCommit message.\n\n## What\nNo trailers.\n\n## Why\nUser said so.",
150
+ workspace,
151
+ });
152
+ expect(fs.existsSync(result)).toBe(true);
153
+ const content = fs.readFileSync(result, "utf8");
154
+ expect(content).toContain("type: playbook");
155
+ expect(content).toContain("id: pbk_");
156
+ expect(content).not.toContain("ttl:");
157
+ expect(content).not.toContain("expires:");
158
+ expect(content).toContain("## When");
159
+ });
160
+ });
161
+
162
+ describe("deleteFile", () => {
163
+ it("deletes an existing file", () => {
164
+ const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "rfx-del-"));
165
+ const f = path.join(tmp, "test.md");
166
+ fs.writeFileSync(f, "x");
167
+ deleteFile(f);
168
+ expect(fs.existsSync(f)).toBe(false);
169
+ fs.rmSync(tmp, { recursive: true, force: true });
170
+ });
171
+
172
+ it("does not throw when file is missing", () => {
173
+ expect(() => deleteFile("/tmp/nonexistent-reflexio-test.md")).not.toThrow();
174
+ });
175
+ });