mixdog 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (404) hide show
  1. package/.claude-plugin/marketplace.json +31 -0
  2. package/.claude-plugin/plugin.json +20 -0
  3. package/.gitattributes +34 -0
  4. package/.mcp.json +14 -0
  5. package/ARCHITECTURE.md +77 -0
  6. package/CHANGELOG.md +7 -0
  7. package/CONTRIBUTING.md +45 -0
  8. package/DATA-FLOW.md +79 -0
  9. package/LICENSE +21 -0
  10. package/README.md +389 -0
  11. package/SECURITY.md +138 -0
  12. package/UNINSTALL.md +112 -0
  13. package/agents/maintenance.md +5 -0
  14. package/agents/memory-classification.md +30 -0
  15. package/agents/scheduler-task.md +18 -0
  16. package/agents/webhook-handler.md +27 -0
  17. package/agents/worker.md +24 -0
  18. package/bin/bridge +133 -0
  19. package/bin/statusline-launcher.mjs +78 -0
  20. package/bin/statusline-lib.mjs +550 -0
  21. package/bin/statusline.mjs +607 -0
  22. package/bun.lock +802 -0
  23. package/commands/config.md +16 -0
  24. package/commands/doctor.md +13 -0
  25. package/commands/setup.md +17 -0
  26. package/defaults/cycle3-review-prompt.md +90 -0
  27. package/defaults/hidden-roles.json +65 -0
  28. package/defaults/memory-chunk-prompt.md +63 -0
  29. package/defaults/memory-promote-prompt.md +135 -0
  30. package/defaults/mixdog-config.template.json +27 -0
  31. package/defaults/user-workflow.json +8 -0
  32. package/defaults/user-workflow.md +12 -0
  33. package/hooks/hooks.json +73 -0
  34. package/hooks/lib/active-instance.cjs +77 -0
  35. package/hooks/lib/permission-evaluator.cjs +411 -0
  36. package/hooks/lib/permission-route.cjs +63 -0
  37. package/hooks/lib/permission-rules.cjs +170 -0
  38. package/hooks/lib/settings-loader.cjs +116 -0
  39. package/hooks/post-tool-use.cjs +84 -0
  40. package/hooks/pre-mcp-sandbox.cjs +158 -0
  41. package/hooks/pre-tool-subagent.cjs +253 -0
  42. package/hooks/session-start.cjs +1372 -0
  43. package/hooks/turn-timer.cjs +82 -0
  44. package/lib/claude-md-writer.cjs +386 -0
  45. package/lib/config-cjs.cjs +61 -0
  46. package/lib/hook-pipe-path.cjs +10 -0
  47. package/lib/keychain-cjs.cjs +263 -0
  48. package/lib/plugin-paths.cjs +61 -0
  49. package/lib/rules-builder.cjs +241 -0
  50. package/lib/text-utils.cjs +61 -0
  51. package/native/README.md +117 -0
  52. package/native/prebuilt/linux-aarch64/mixdog-shim +0 -0
  53. package/native/prebuilt/linux-x86_64/mixdog-shim +0 -0
  54. package/native/prebuilt/macos-aarch64/mixdog-shim +0 -0
  55. package/native/prebuilt/macos-x86_64/mixdog-shim +0 -0
  56. package/native/prebuilt/windows-x86_64/mixdog-shim.exe +0 -0
  57. package/package.json +107 -0
  58. package/prompts/code-review.txt +16 -0
  59. package/prompts/security-audit.txt +17 -0
  60. package/rules/bridge/00-common.md +39 -0
  61. package/rules/bridge/20-skip-protocol.md +18 -0
  62. package/rules/bridge/30-explorer.md +33 -0
  63. package/rules/bridge/40-cycle1-agent.md +52 -0
  64. package/rules/bridge/41-cycle2-agent.md +62 -0
  65. package/rules/bridge/42-cycle3-agent.md +44 -0
  66. package/rules/lead/00-tool-lead.md +61 -0
  67. package/rules/lead/01-general.md +23 -0
  68. package/rules/lead/02-channels.md +49 -0
  69. package/rules/lead/03-team.md +27 -0
  70. package/rules/lead/04-workflow.md +20 -0
  71. package/rules/shared/00-language.md +14 -0
  72. package/rules/shared/01-tool.md +138 -0
  73. package/scripts/bootstrap.mjs +184 -0
  74. package/scripts/bridge-unify-smoke.mjs +308 -0
  75. package/scripts/build-runtime-linux.sh +348 -0
  76. package/scripts/build-runtime-macos.sh +217 -0
  77. package/scripts/build-runtime-windows.ps1 +242 -0
  78. package/scripts/builtin-utils-smoke.mjs +392 -0
  79. package/scripts/check-json.mjs +45 -0
  80. package/scripts/check-syntax-changed.mjs +102 -0
  81. package/scripts/check-syntax.mjs +58 -0
  82. package/scripts/code-graph-batch.test.mjs +33 -0
  83. package/scripts/config-preserve-smoke.mjs +180 -0
  84. package/scripts/doctor.mjs +484 -0
  85. package/scripts/edit-normalize-fuzz.mjs +130 -0
  86. package/scripts/edit-normalize-smoke.mjs +401 -0
  87. package/scripts/edit-operation-smoke.mjs +369 -0
  88. package/scripts/edit2-smoke.mjs +63 -0
  89. package/scripts/fuzzy-e2e.mjs +28 -0
  90. package/scripts/fuzzy-smoke.mjs +26 -0
  91. package/scripts/generate-runtime-manifest.mjs +166 -0
  92. package/scripts/guard-smoke.mjs +66 -0
  93. package/scripts/hidden-role-schema-smoke.mjs +162 -0
  94. package/scripts/hook-routing-smoke.mjs +29 -0
  95. package/scripts/inject-input.ps1 +204 -0
  96. package/scripts/io-complex-smoke.mjs +667 -0
  97. package/scripts/io-explore-bench.mjs +424 -0
  98. package/scripts/io-guardrails-smoke.mjs +205 -0
  99. package/scripts/io-mini-bench-baseline.json +11 -0
  100. package/scripts/io-mini-bench.mjs +216 -0
  101. package/scripts/io-route-harness.mjs +933 -0
  102. package/scripts/io-telemetry-report.mjs +691 -0
  103. package/scripts/mutation-bench.mjs +564 -0
  104. package/scripts/mutation-io-smoke.mjs +1081 -0
  105. package/scripts/native-patch-bridge-smoke.mjs +288 -0
  106. package/scripts/native-patch-smoke.mjs +304 -0
  107. package/scripts/patch-interior-context-smoke.mjs +49 -0
  108. package/scripts/patch-newline-utf8-smoke.mjs +157 -0
  109. package/scripts/perf-hook-smoke.mjs +71 -0
  110. package/scripts/permission-eval-smoke.mjs +426 -0
  111. package/scripts/prep-patch.mjs +53 -0
  112. package/scripts/prep-shim.mjs +96 -0
  113. package/scripts/provider-cache-smoke.mjs +687 -0
  114. package/scripts/report-runtime-health.mjs +132 -0
  115. package/scripts/run-mcp.mjs +1547 -0
  116. package/scripts/salvage-v4a-shatter.test.mjs +58 -0
  117. package/scripts/scoped-cache-io-smoke.mjs +103 -0
  118. package/scripts/shell-policy-round3-smoke.mjs +46 -0
  119. package/scripts/smoke-runtime-negative.ps1 +100 -0
  120. package/scripts/smoke-runtime-negative.sh +95 -0
  121. package/scripts/stall-policy-smoke.mjs +50 -0
  122. package/scripts/start-memory-worker.mjs +23 -0
  123. package/scripts/statusline-launcher-smoke.mjs +82 -0
  124. package/scripts/stress-atomic-write.mjs +1028 -0
  125. package/scripts/test-config-rmw-restore.mjs +122 -0
  126. package/scripts/test-fault-inject.mjs +164 -0
  127. package/scripts/test-large-file.mjs +174 -0
  128. package/scripts/tool-edge-smoke.mjs +209 -0
  129. package/scripts/uninstall.mjs +201 -0
  130. package/scripts/webhook-selfheal-smoke.mjs +29 -0
  131. package/scripts/write-overwrite-guard-smoke.mjs +56 -0
  132. package/server-main.mjs +3055 -0
  133. package/server.mjs +468 -0
  134. package/setup/config-merge.mjs +254 -0
  135. package/setup/install.mjs +120 -0
  136. package/setup/launch-core.mjs +507 -0
  137. package/setup/launch.mjs +101 -0
  138. package/setup/setup-server.mjs +3206 -0
  139. package/setup/setup.html +3693 -0
  140. package/skills/retro-skill-proposer/SKILL.md +92 -0
  141. package/skills/schedule-add/SKILL.md +77 -0
  142. package/skills/setup/SKILL.md +346 -0
  143. package/skills/webhook-add/SKILL.md +81 -0
  144. package/src/agent/bridge-stall-watchdog.mjs +337 -0
  145. package/src/agent/index.mjs +2138 -0
  146. package/src/agent/orchestrator/activity-bus.mjs +38 -0
  147. package/src/agent/orchestrator/ai-wrapped-dispatch.mjs +1010 -0
  148. package/src/agent/orchestrator/bridge-retry.mjs +220 -0
  149. package/src/agent/orchestrator/bridge-trace.mjs +583 -0
  150. package/src/agent/orchestrator/cache-mtime.mjs +58 -0
  151. package/src/agent/orchestrator/config.mjs +358 -0
  152. package/src/agent/orchestrator/context/collect.mjs +651 -0
  153. package/src/agent/orchestrator/dispatch-persist.mjs +549 -0
  154. package/src/agent/orchestrator/drain-registry.mjs +50 -0
  155. package/src/agent/orchestrator/explore-validator.mjs +8 -0
  156. package/src/agent/orchestrator/internal-roles.mjs +118 -0
  157. package/src/agent/orchestrator/internal-tools.mjs +88 -0
  158. package/src/agent/orchestrator/jobs.mjs +116 -0
  159. package/src/agent/orchestrator/mcp/client.mjs +364 -0
  160. package/src/agent/orchestrator/providers/anthropic-betas.mjs +21 -0
  161. package/src/agent/orchestrator/providers/anthropic-oauth.mjs +1745 -0
  162. package/src/agent/orchestrator/providers/anthropic.mjs +437 -0
  163. package/src/agent/orchestrator/providers/gemini.mjs +1175 -0
  164. package/src/agent/orchestrator/providers/grok-oauth.mjs +782 -0
  165. package/src/agent/orchestrator/providers/model-catalog.mjs +241 -0
  166. package/src/agent/orchestrator/providers/openai-compat.mjs +1467 -0
  167. package/src/agent/orchestrator/providers/openai-oauth-ws.mjs +1890 -0
  168. package/src/agent/orchestrator/providers/openai-oauth.mjs +1307 -0
  169. package/src/agent/orchestrator/providers/openai-ws.mjs +104 -0
  170. package/src/agent/orchestrator/providers/registry.mjs +192 -0
  171. package/src/agent/orchestrator/providers/retry-classifier.mjs +325 -0
  172. package/src/agent/orchestrator/session/abort-lookup.mjs +13 -0
  173. package/src/agent/orchestrator/session/cache/post-edit-marks.mjs +42 -0
  174. package/src/agent/orchestrator/session/cache/prefetch-cache.mjs +142 -0
  175. package/src/agent/orchestrator/session/cache/read-cache.mjs +319 -0
  176. package/src/agent/orchestrator/session/cache/scoped-cache-outcome.mjs +11 -0
  177. package/src/agent/orchestrator/session/cache/scoped-cache.mjs +361 -0
  178. package/src/agent/orchestrator/session/cache/util.mjs +49 -0
  179. package/src/agent/orchestrator/session/loop.mjs +1478 -0
  180. package/src/agent/orchestrator/session/manager.mjs +1975 -0
  181. package/src/agent/orchestrator/session/read-dedup.mjs +6 -0
  182. package/src/agent/orchestrator/session/result-classification.mjs +65 -0
  183. package/src/agent/orchestrator/session/save-session-worker.mjs +18 -0
  184. package/src/agent/orchestrator/session/store.mjs +624 -0
  185. package/src/agent/orchestrator/session/stream-watchdog.mjs +130 -0
  186. package/src/agent/orchestrator/session/tool-result-offload.mjs +166 -0
  187. package/src/agent/orchestrator/session/trim.mjs +491 -0
  188. package/src/agent/orchestrator/smart-bridge/CACHE-SHARD.md +115 -0
  189. package/src/agent/orchestrator/smart-bridge/bridge-llm.mjs +327 -0
  190. package/src/agent/orchestrator/smart-bridge/cache-obs.mjs +150 -0
  191. package/src/agent/orchestrator/smart-bridge/cache-strategy.mjs +228 -0
  192. package/src/agent/orchestrator/smart-bridge/index.mjs +215 -0
  193. package/src/agent/orchestrator/smart-bridge/profiles.mjs +37 -0
  194. package/src/agent/orchestrator/smart-bridge/registry.mjs +348 -0
  195. package/src/agent/orchestrator/smart-bridge/session-builder.mjs +116 -0
  196. package/src/agent/orchestrator/stall-policy.mjs +195 -0
  197. package/src/agent/orchestrator/tool-loop-guard.mjs +75 -0
  198. package/src/agent/orchestrator/tools/bash-policy-scan.mjs +77 -0
  199. package/src/agent/orchestrator/tools/bash-session.mjs +721 -0
  200. package/src/agent/orchestrator/tools/builtin/advisory-lock.mjs +171 -0
  201. package/src/agent/orchestrator/tools/builtin/arg-guard.mjs +455 -0
  202. package/src/agent/orchestrator/tools/builtin/atomic-write.mjs +236 -0
  203. package/src/agent/orchestrator/tools/builtin/bash-tool.mjs +480 -0
  204. package/src/agent/orchestrator/tools/builtin/binary-file.mjs +76 -0
  205. package/src/agent/orchestrator/tools/builtin/builtin-tools.mjs +256 -0
  206. package/src/agent/orchestrator/tools/builtin/cache-layers.mjs +386 -0
  207. package/src/agent/orchestrator/tools/builtin/cwd-utils.mjs +37 -0
  208. package/src/agent/orchestrator/tools/builtin/device-paths.mjs +154 -0
  209. package/src/agent/orchestrator/tools/builtin/diagnostics-tool.mjs +292 -0
  210. package/src/agent/orchestrator/tools/builtin/diff-utils.mjs +109 -0
  211. package/src/agent/orchestrator/tools/builtin/edit-base-guard.mjs +58 -0
  212. package/src/agent/orchestrator/tools/builtin/edit-byte-plan.mjs +240 -0
  213. package/src/agent/orchestrator/tools/builtin/edit-byte-utils.mjs +113 -0
  214. package/src/agent/orchestrator/tools/builtin/edit-commit.mjs +74 -0
  215. package/src/agent/orchestrator/tools/builtin/edit-context-utils.mjs +242 -0
  216. package/src/agent/orchestrator/tools/builtin/edit-diagnostics.mjs +211 -0
  217. package/src/agent/orchestrator/tools/builtin/edit-engine.mjs +1364 -0
  218. package/src/agent/orchestrator/tools/builtin/edit-failure-context.mjs +126 -0
  219. package/src/agent/orchestrator/tools/builtin/edit-hint.mjs +141 -0
  220. package/src/agent/orchestrator/tools/builtin/edit-match-utils.mjs +194 -0
  221. package/src/agent/orchestrator/tools/builtin/edit-partial-write.mjs +60 -0
  222. package/src/agent/orchestrator/tools/builtin/edit-stale-refresh.mjs +168 -0
  223. package/src/agent/orchestrator/tools/builtin/edit-tool.mjs +173 -0
  224. package/src/agent/orchestrator/tools/builtin/edit-utf8-guard.mjs +48 -0
  225. package/src/agent/orchestrator/tools/builtin/fs-reachability.mjs +48 -0
  226. package/src/agent/orchestrator/tools/builtin/fuzzy-match.mjs +99 -0
  227. package/src/agent/orchestrator/tools/builtin/glob-walk.mjs +170 -0
  228. package/src/agent/orchestrator/tools/builtin/grep-formatting.mjs +113 -0
  229. package/src/agent/orchestrator/tools/builtin/hash-utils.mjs +6 -0
  230. package/src/agent/orchestrator/tools/builtin/list-formatting.mjs +7 -0
  231. package/src/agent/orchestrator/tools/builtin/list-tool.mjs +593 -0
  232. package/src/agent/orchestrator/tools/builtin/native-edit-runner.mjs +89 -0
  233. package/src/agent/orchestrator/tools/builtin/notebook-edit-tool.mjs +300 -0
  234. package/src/agent/orchestrator/tools/builtin/open-config-tool.mjs +26 -0
  235. package/src/agent/orchestrator/tools/builtin/path-diagnostics.mjs +152 -0
  236. package/src/agent/orchestrator/tools/builtin/path-locks.mjs +35 -0
  237. package/src/agent/orchestrator/tools/builtin/path-utils.mjs +201 -0
  238. package/src/agent/orchestrator/tools/builtin/read-args.mjs +103 -0
  239. package/src/agent/orchestrator/tools/builtin/read-batch.mjs +172 -0
  240. package/src/agent/orchestrator/tools/builtin/read-constants.mjs +40 -0
  241. package/src/agent/orchestrator/tools/builtin/read-formatting.mjs +118 -0
  242. package/src/agent/orchestrator/tools/builtin/read-image-resize.mjs +189 -0
  243. package/src/agent/orchestrator/tools/builtin/read-image.mjs +88 -0
  244. package/src/agent/orchestrator/tools/builtin/read-lines.mjs +12 -0
  245. package/src/agent/orchestrator/tools/builtin/read-mode-tool.mjs +455 -0
  246. package/src/agent/orchestrator/tools/builtin/read-open.mjs +190 -0
  247. package/src/agent/orchestrator/tools/builtin/read-range-index.mjs +271 -0
  248. package/src/agent/orchestrator/tools/builtin/read-ranges.mjs +26 -0
  249. package/src/agent/orchestrator/tools/builtin/read-single-tool.mjs +728 -0
  250. package/src/agent/orchestrator/tools/builtin/read-snapshot-runtime.mjs +173 -0
  251. package/src/agent/orchestrator/tools/builtin/read-special-files.mjs +268 -0
  252. package/src/agent/orchestrator/tools/builtin/read-streaming.mjs +602 -0
  253. package/src/agent/orchestrator/tools/builtin/read-tool.mjs +530 -0
  254. package/src/agent/orchestrator/tools/builtin/read-windows.mjs +107 -0
  255. package/src/agent/orchestrator/tools/builtin/rename-tool.mjs +196 -0
  256. package/src/agent/orchestrator/tools/builtin/rg-runner.mjs +422 -0
  257. package/src/agent/orchestrator/tools/builtin/search-builders.mjs +158 -0
  258. package/src/agent/orchestrator/tools/builtin/search-tool.mjs +869 -0
  259. package/src/agent/orchestrator/tools/builtin/shell-analysis.mjs +653 -0
  260. package/src/agent/orchestrator/tools/builtin/shell-jobs.mjs +936 -0
  261. package/src/agent/orchestrator/tools/builtin/shell-output.mjs +36 -0
  262. package/src/agent/orchestrator/tools/builtin/shell-runtime.mjs +214 -0
  263. package/src/agent/orchestrator/tools/builtin/snapshot-helpers.mjs +143 -0
  264. package/src/agent/orchestrator/tools/builtin/snapshot-store.mjs +206 -0
  265. package/src/agent/orchestrator/tools/builtin/snapshot-validation.mjs +98 -0
  266. package/src/agent/orchestrator/tools/builtin/text-stats.mjs +69 -0
  267. package/src/agent/orchestrator/tools/builtin/windows-roots.mjs +23 -0
  268. package/src/agent/orchestrator/tools/builtin/write-tool.mjs +401 -0
  269. package/src/agent/orchestrator/tools/builtin.mjs +500 -0
  270. package/src/agent/orchestrator/tools/code-graph-prewarm-worker.mjs +39 -0
  271. package/src/agent/orchestrator/tools/code-graph-tool-defs.mjs +24 -0
  272. package/src/agent/orchestrator/tools/code-graph.mjs +4095 -0
  273. package/src/agent/orchestrator/tools/cwd-tool.mjs +298 -0
  274. package/src/agent/orchestrator/tools/destructive-warning.mjs +323 -0
  275. package/src/agent/orchestrator/tools/edit-normalize.mjs +603 -0
  276. package/src/agent/orchestrator/tools/env-scrub.mjs +100 -0
  277. package/src/agent/orchestrator/tools/graph-binary-fetcher.mjs +144 -0
  278. package/src/agent/orchestrator/tools/graph-manifest.json +26 -0
  279. package/src/agent/orchestrator/tools/host-input.mjs +204 -0
  280. package/src/agent/orchestrator/tools/mutation-content-cache.mjs +67 -0
  281. package/src/agent/orchestrator/tools/mutation-planner.mjs +75 -0
  282. package/src/agent/orchestrator/tools/next-call-utils.mjs +48 -0
  283. package/src/agent/orchestrator/tools/patch-binary-fetcher.mjs +133 -0
  284. package/src/agent/orchestrator/tools/patch-manifest.json +26 -0
  285. package/src/agent/orchestrator/tools/patch-tool-defs.mjs +20 -0
  286. package/src/agent/orchestrator/tools/patch.mjs +2754 -0
  287. package/src/agent/orchestrator/tools/progress-message.mjs +118 -0
  288. package/src/agent/orchestrator/tools/result-compression.mjs +279 -0
  289. package/src/agent/orchestrator/tools/shell-command.mjs +865 -0
  290. package/src/agent/orchestrator/tools/shell-exec-policy.mjs +89 -0
  291. package/src/agent/orchestrator/tools/shell-policy-danger-target.mjs +27 -0
  292. package/src/agent/orchestrator/tools/shell-policy-imports.mjs +7 -0
  293. package/src/agent/orchestrator/tools/shell-policy.mjs +345 -0
  294. package/src/agent/orchestrator/tools/shell-snapshot.mjs +313 -0
  295. package/src/agent/orchestrator/workflow-store.mjs +93 -0
  296. package/src/agent/tool-defs.mjs +103 -0
  297. package/src/channels/backends/discord.mjs +784 -0
  298. package/src/channels/data/voice-runtime-manifest.json +138 -0
  299. package/src/channels/index.mjs +3229 -0
  300. package/src/channels/lib/cli-worker-host.mjs +12 -0
  301. package/src/channels/lib/config-lock.mjs +13 -0
  302. package/src/channels/lib/config.mjs +292 -0
  303. package/src/channels/lib/drop-trace.mjs +71 -0
  304. package/src/channels/lib/event-pipeline.mjs +81 -0
  305. package/src/channels/lib/event-queue.mjs +345 -0
  306. package/src/channels/lib/executor.mjs +168 -0
  307. package/src/channels/lib/format.mjs +188 -0
  308. package/src/channels/lib/holidays.mjs +138 -0
  309. package/src/channels/lib/hook-pipe-server.mjs +802 -0
  310. package/src/channels/lib/interaction-workflows.mjs +184 -0
  311. package/src/channels/lib/memory-client.mjs +149 -0
  312. package/src/channels/lib/output-forwarder.mjs +765 -0
  313. package/src/channels/lib/runtime-paths.mjs +479 -0
  314. package/src/channels/lib/scheduler.mjs +723 -0
  315. package/src/channels/lib/session-control.mjs +36 -0
  316. package/src/channels/lib/session-discovery.mjs +103 -0
  317. package/src/channels/lib/settings.mjs +11 -0
  318. package/src/channels/lib/state-file.mjs +68 -0
  319. package/src/channels/lib/status-snapshot.mjs +219 -0
  320. package/src/channels/lib/tool-format.mjs +140 -0
  321. package/src/channels/lib/transcript-discovery.mjs +195 -0
  322. package/src/channels/lib/voice-runtime-fetcher.mjs +734 -0
  323. package/src/channels/lib/webhook.mjs +1179 -0
  324. package/src/channels/lib/whisper-server.mjs +477 -0
  325. package/src/channels/tool-defs.mjs +170 -0
  326. package/src/daemon/host.mjs +118 -0
  327. package/src/daemon/mcp-transport.mjs +47 -0
  328. package/src/daemon/session.mjs +100 -0
  329. package/src/daemon/thin-client.mjs +71 -0
  330. package/src/daemon/transport.mjs +163 -0
  331. package/src/memory/data/runtime-manifest.json +40 -0
  332. package/src/memory/index.mjs +3305 -0
  333. package/src/memory/lib/agent-ipc.mjs +93 -0
  334. package/src/memory/lib/bridge-trace-queries.mjs +120 -0
  335. package/src/memory/lib/core-memory-store.mjs +330 -0
  336. package/src/memory/lib/embedding-provider.mjs +269 -0
  337. package/src/memory/lib/embedding-worker.mjs +323 -0
  338. package/src/memory/lib/llm-worker-host.mjs +17 -0
  339. package/src/memory/lib/memory-cycle.mjs +11 -0
  340. package/src/memory/lib/memory-cycle1.mjs +641 -0
  341. package/src/memory/lib/memory-cycle2.mjs +1284 -0
  342. package/src/memory/lib/memory-cycle3.mjs +540 -0
  343. package/src/memory/lib/memory-embed.mjs +299 -0
  344. package/src/memory/lib/memory-extraction.mjs +5 -0
  345. package/src/memory/lib/memory-maintenance-store.mjs +32 -0
  346. package/src/memory/lib/memory-ops-policy.mjs +190 -0
  347. package/src/memory/lib/memory-recall-id-patch.mjs +15 -0
  348. package/src/memory/lib/memory-recall-read-query.mjs +7 -0
  349. package/src/memory/lib/memory-recall-scope-filter.mjs +63 -0
  350. package/src/memory/lib/memory-recall-store.mjs +621 -0
  351. package/src/memory/lib/memory-retrievers.mjs +112 -0
  352. package/src/memory/lib/memory-score.mjs +71 -0
  353. package/src/memory/lib/memory-text-utils.mjs +58 -0
  354. package/src/memory/lib/memory.mjs +412 -0
  355. package/src/memory/lib/model-profile.mjs +85 -0
  356. package/src/memory/lib/pg/adapter.mjs +308 -0
  357. package/src/memory/lib/pg/process.mjs +360 -0
  358. package/src/memory/lib/pg/supervisor.mjs +396 -0
  359. package/src/memory/lib/project-id-resolver.mjs +86 -0
  360. package/src/memory/lib/runtime-fetcher.mjs +442 -0
  361. package/src/memory/lib/trace-store.mjs +728 -0
  362. package/src/memory/tool-defs.mjs +79 -0
  363. package/src/search/index.mjs +1173 -0
  364. package/src/search/lib/backends/anthropic-oauth.mjs +98 -0
  365. package/src/search/lib/backends/exa.mjs +50 -0
  366. package/src/search/lib/backends/firecrawl.mjs +61 -0
  367. package/src/search/lib/backends/gemini-api.mjs +83 -0
  368. package/src/search/lib/backends/grok-oauth.mjs +86 -0
  369. package/src/search/lib/backends/index.mjs +150 -0
  370. package/src/search/lib/backends/openai-api.mjs +144 -0
  371. package/src/search/lib/backends/openai-oauth.mjs +98 -0
  372. package/src/search/lib/backends/openai-web-search.mjs +76 -0
  373. package/src/search/lib/backends/tavily.mjs +55 -0
  374. package/src/search/lib/backends/xai-api.mjs +113 -0
  375. package/src/search/lib/cache.mjs +131 -0
  376. package/src/search/lib/config.mjs +192 -0
  377. package/src/search/lib/formatter.mjs +115 -0
  378. package/src/search/lib/provider-usage.mjs +67 -0
  379. package/src/search/lib/providers.mjs +47 -0
  380. package/src/search/lib/search-intent.mjs +109 -0
  381. package/src/search/lib/setup-handler.mjs +261 -0
  382. package/src/search/lib/state.mjs +201 -0
  383. package/src/search/lib/web-tools.mjs +1207 -0
  384. package/src/search/tool-defs.mjs +83 -0
  385. package/src/setup/defender-exclusion.mjs +183 -0
  386. package/src/shared/abort-controller.mjs +15 -0
  387. package/src/shared/atomic-file.mjs +420 -0
  388. package/src/shared/config.mjs +350 -0
  389. package/src/shared/daemon-recycle.mjs +108 -0
  390. package/src/shared/disable-claude-builtins.mjs +88 -0
  391. package/src/shared/err-text.mjs +12 -0
  392. package/src/shared/llm/cost.mjs +66 -0
  393. package/src/shared/llm/http-agent.mjs +123 -0
  394. package/src/shared/llm/index.mjs +41 -0
  395. package/src/shared/llm/pid-cleanup.mjs +27 -0
  396. package/src/shared/llm/usage-log.mjs +47 -0
  397. package/src/shared/plugin-paths.mjs +58 -0
  398. package/src/shared/schedules-store.mjs +70 -0
  399. package/src/shared/seed.mjs +119 -0
  400. package/src/shared/user-cwd.mjs +213 -0
  401. package/src/shared/user-data-guard.mjs +238 -0
  402. package/src/status/aggregator.mjs +584 -0
  403. package/src/status/server.mjs +413 -0
  404. package/tools.json +1653 -0
@@ -0,0 +1,263 @@
1
+ 'use strict';
2
+
3
+ const { spawnSync } = require('child_process');
4
+ const path = require('path');
5
+ const fs = require('fs');
6
+ const { resolvePluginData } = require('./plugin-paths.cjs');
7
+
8
+ const SERVICE = 'mixdog';
9
+ const POWERSHELL_TIMEOUT_MS = Number(process.env.MIXDOG_KEYCHAIN_TIMEOUT_MS || 15000);
10
+
11
+ // ---------------------------------------------------------------------------
12
+ // Helpers
13
+ // ---------------------------------------------------------------------------
14
+
15
+ function platform() {
16
+ return process.platform;
17
+ }
18
+
19
+ function run(cmd, args, opts) {
20
+ // windowsHide + stdio:['ignore','pipe','pipe'] keeps powershell.exe
21
+ // consoleless during DPAPI ops. Without these flags every keychain
22
+ // read/write flashes a conhost window: setup-html's loaders call
23
+ // /agent/config, /memory/auth, /search/config which each invoke
24
+ // hasOpenAIOAuthCredentials / hasAnthropicOAuthCredentials /
25
+ // getSearchApiKey, and each of those triggers one powershell.exe
26
+ // spawn — users saw 8-15 console flashes during config-UI page load.
27
+ // Default opts go BEFORE the spread so callers can still override.
28
+ const result = spawnSync(cmd, args, {
29
+ encoding: 'utf8',
30
+ shell: false,
31
+ windowsHide: true,
32
+ stdio: ['ignore', 'pipe', 'pipe'],
33
+ ...opts,
34
+ });
35
+ return result;
36
+ }
37
+
38
+ // PS 5.1(powershell.exe) first: pwsh(PS 7) does not auto-load ProtectedData assembly,
39
+ // so DPAPI calls silently fail there. Windows always ships powershell.exe.
40
+ function powershell(script) {
41
+ // Bound DPAPI PowerShell calls with a timeout: a hung powershell.exe
42
+ // (AV scan stall, profile loader, transient cert chain lookup) would
43
+ // otherwise block hook/server callers synchronously. The default is long
44
+ // enough for cold PowerShell startup on Windows without silently dropping
45
+ // setup UI credential saves.
46
+ // Resolve powershell.exe to its deterministic System32 location rather than
47
+ // a PATH lookup, so a shadow `powershell.exe` earlier in PATH cannot hijack
48
+ // secret encryption/decryption. (pwsh/PS7 is intentionally not used: it does
49
+ // not auto-load the ProtectedData assembly, so DPAPI silently fails there.)
50
+ const _sysRoot = process.env.SystemRoot || process.env.windir;
51
+ if (!_sysRoot) {
52
+ throw new Error('[keychain] cannot resolve SystemRoot to locate powershell.exe for DPAPI');
53
+ }
54
+ const _psHosts = [path.join(_sysRoot, 'System32', 'WindowsPowerShell', 'v1.0', 'powershell.exe')];
55
+ let timedOut = null;
56
+ for (const exe of _psHosts) {
57
+ const r = run(exe, ['-NonInteractive', '-NoProfile', '-Command', script], { timeout: POWERSHELL_TIMEOUT_MS });
58
+ if (r.error && r.error.code === 'ENOENT') continue;
59
+ if (r.error && r.error.code === 'ETIMEDOUT') {
60
+ timedOut = { exe, timeoutMs: POWERSHELL_TIMEOUT_MS };
61
+ continue;
62
+ }
63
+ return r;
64
+ }
65
+ if (timedOut) {
66
+ throw new Error(`[keychain] PowerShell DPAPI command timed out after ${timedOut.timeoutMs}ms (last host: ${timedOut.exe})`);
67
+ }
68
+ const err = new Error('[keychain] PowerShell not found (tried powershell, pwsh)');
69
+ throw err;
70
+ }
71
+
72
+ function secretsDir() {
73
+ return path.join(resolvePluginData(), 'secrets');
74
+ }
75
+
76
+ function dpApiFile(account) {
77
+ return path.join(secretsDir(), account + '.dpapi');
78
+ }
79
+
80
+ // ---------------------------------------------------------------------------
81
+ // darwin — security(1)
82
+ // ---------------------------------------------------------------------------
83
+
84
+ function darwinGet(account) {
85
+ const r = run('security', ['find-generic-password', '-a', account, '-s', SERVICE, '-w']);
86
+ if (r.status !== 0) return null;
87
+ return r.stdout.trimEnd();
88
+ }
89
+
90
+ function darwinSet(account, value) {
91
+ const r = run('security', ['add-generic-password', '-a', account, '-s', SERVICE, '-w', value, '-U']);
92
+ if (r.status !== 0) throw new Error(`[keychain] security set failed: ${r.stderr || r.stdout}`);
93
+ }
94
+
95
+ function darwinDelete(account) {
96
+ const r = run('security', ['delete-generic-password', '-a', account, '-s', SERVICE]);
97
+ if (r.status !== 0) throw new Error(`[keychain] security delete failed: ${r.stderr || r.stdout}`);
98
+ }
99
+
100
+ // ---------------------------------------------------------------------------
101
+ // linux/WSL — keytar (libsecret binding, optionalDependency)
102
+ // ---------------------------------------------------------------------------
103
+ // keytar must be installed: npm install keytar (requires libsecret-dev on
104
+ // Debian/Ubuntu, or libsecret on other distros). If not installed, every
105
+ // call throws immediately — silent credential loss is not permitted.
106
+
107
+ let _keytarMod = null;
108
+ function loadKeytar() {
109
+ if (_keytarMod !== null) return _keytarMod;
110
+ try { _keytarMod = require('keytar'); }
111
+ catch {
112
+ throw new Error(
113
+ '[keychain] keytar is not installed — run: npm install keytar\n' +
114
+ ' Requires libsecret-dev (Debian/Ubuntu) or libsecret (other distros).\n' +
115
+ ' Cannot access credentials on this platform without it.'
116
+ );
117
+ }
118
+ return _keytarMod;
119
+ }
120
+
121
+ // keytar is Promise-based; bridge to sync via spawnSync of a child Node process.
122
+ // Avoids Atomics.wait on main thread (which hangs when SAB is not forwarded to worker).
123
+ function keytarSync(method, ...args) {
124
+ loadKeytar(); // throws if not installed — before spawning child
125
+ const { spawnSync } = require('child_process');
126
+ // Pass service/account/value via env to avoid shell injection entirely.
127
+ const env = {
128
+ ...process.env,
129
+ _KEYTAR_METHOD: method,
130
+ _KEYTAR_ARGS: JSON.stringify(args),
131
+ };
132
+ const script = [
133
+ 'const kt = require("keytar");',
134
+ 'const method = process.env._KEYTAR_METHOD;',
135
+ 'const args = JSON.parse(process.env._KEYTAR_ARGS);',
136
+ 'kt[method](...args)',
137
+ ' .then(v => { process.stdout.write(JSON.stringify({ ok: true, value: v })); })',
138
+ ' .catch(e => { process.stdout.write(JSON.stringify({ ok: false, error: e.message })); });',
139
+ ].join(' ');
140
+ const r = spawnSync(process.execPath, ['-e', script], {
141
+ env,
142
+ stdio: ['ignore', 'pipe', 'pipe'],
143
+ timeout: 5000,
144
+ encoding: 'utf8',
145
+ windowsHide: true,
146
+ });
147
+ if (r.error) throw new Error(`[keychain] keytar.${method} spawnSync failed: ${r.error.message}`);
148
+ if (r.status !== 0) {
149
+ const detail = (r.stderr || '').trim() || `exit ${r.status}`;
150
+ throw new Error(`[keychain] keytar.${method} child exited with error: ${detail}`);
151
+ }
152
+ let parsed;
153
+ try { parsed = JSON.parse(r.stdout); } catch {
154
+ throw new Error(`[keychain] keytar.${method} child returned unparseable output: ${String(r.stdout).slice(0, 200)}`);
155
+ }
156
+ if (!parsed.ok) throw new Error(`[keychain] keytar.${method} failed: ${parsed.error}`);
157
+ return parsed.value ?? null;
158
+ }
159
+
160
+ function linuxGet(account) {
161
+ return keytarSync('getPassword', SERVICE, account);
162
+ }
163
+
164
+ function linuxSet(account, value) {
165
+ keytarSync('setPassword', SERVICE, account, value);
166
+ }
167
+
168
+ function linuxDelete(account) {
169
+ keytarSync('deletePassword', SERVICE, account);
170
+ }
171
+
172
+ // ---------------------------------------------------------------------------
173
+ // win32 — DPAPI via PowerShell
174
+ // ---------------------------------------------------------------------------
175
+
176
+ // Add-Type loads System.Security on PS 5.1 hosts where the assembly is not
177
+ // auto-loaded — ProtectedData/DataProtectionScope resolve to TypeNotFound
178
+ // otherwise, and the host returns exit 0 with empty stdout, which then falls
179
+ // through win32Set's empty-output guard. PS 7+ already has the type loaded;
180
+ // SilentlyContinue covers the benign "already loaded" case on repeat calls.
181
+ const PS_PROTECT = [
182
+ 'Add-Type -AssemblyName System.Security -ErrorAction SilentlyContinue;',
183
+ '[Console]::OutputEncoding = [System.Text.Encoding]::UTF8;',
184
+ '$value = $args[0];',
185
+ '$bytes = [System.Text.Encoding]::UTF8.GetBytes($value);',
186
+ '$scope = [System.Security.Cryptography.DataProtectionScope]::CurrentUser;',
187
+ '$enc = [System.Security.Cryptography.ProtectedData]::Protect($bytes, $null, $scope);',
188
+ '[Convert]::ToBase64String($enc)',
189
+ ].join(' ');
190
+
191
+ const PS_UNPROTECT = [
192
+ 'Add-Type -AssemblyName System.Security -ErrorAction SilentlyContinue;',
193
+ '[Console]::OutputEncoding = [System.Text.Encoding]::UTF8;',
194
+ '$b64 = $args[0];',
195
+ '$enc = [Convert]::FromBase64String($b64);',
196
+ '$scope = [System.Security.Cryptography.DataProtectionScope]::CurrentUser;',
197
+ '$dec = [System.Security.Cryptography.ProtectedData]::Unprotect($enc, $null, $scope);',
198
+ '[System.Text.Encoding]::UTF8.GetString($dec)',
199
+ ].join(' ');
200
+
201
+ function win32Get(account) {
202
+ const file = dpApiFile(account);
203
+ if (!fs.existsSync(file)) return null;
204
+ const b64 = fs.readFileSync(file, 'utf8').trim();
205
+ if (!b64) throw new Error(`[keychain] DPAPI ciphertext file is empty: ${file}`);
206
+ const r = powershell(`& { ${PS_UNPROTECT} } '${b64}'`);
207
+ if (r.status !== 0) throw new Error(`[keychain] DPAPI decrypt failed (exit ${r.status}): ${r.stderr || r.stdout}`);
208
+ const out = (r.stdout || '').trim();
209
+ if (!out) throw new Error(`[keychain] DPAPI decrypt returned empty output (stderr: ${(r.stderr || '').trim()})`);
210
+ return out;
211
+ }
212
+
213
+ function win32Set(account, value) {
214
+ const dir = secretsDir();
215
+ fs.mkdirSync(dir, { recursive: true });
216
+ // Pass value as a PS argument via -Command inline to avoid shell injection;
217
+ // value is embedded as a PS single-quoted string literal with ' escaped.
218
+ const escaped = value.replace(/'/g, "''");
219
+ const r = powershell(`& { ${PS_PROTECT} } '${escaped}'`);
220
+ if (r.status !== 0) throw new Error(`[keychain] DPAPI encrypt failed (exit ${r.status}): ${r.stderr || r.stdout}`);
221
+ const out = (r.stdout || '').trim();
222
+ if (!out) throw new Error(`[keychain] DPAPI encrypt returned empty output (stderr: ${(r.stderr || '').trim()})`);
223
+ fs.writeFileSync(dpApiFile(account), out, 'utf8');
224
+ }
225
+
226
+ function win32Delete(account) {
227
+ const file = dpApiFile(account);
228
+ if (!fs.existsSync(file)) throw new Error(`[keychain] secret not found: ${account}`);
229
+ fs.unlinkSync(file);
230
+ }
231
+
232
+ // ---------------------------------------------------------------------------
233
+ // Public API
234
+ // ---------------------------------------------------------------------------
235
+
236
+ function getSecret(account) {
237
+ switch (platform()) {
238
+ case 'darwin': return darwinGet(account);
239
+ case 'linux': return linuxGet(account);
240
+ case 'win32': return win32Get(account);
241
+ default: throw new Error(`[keychain] unsupported platform: ${process.platform}`);
242
+ }
243
+ }
244
+
245
+ function setSecret(account, value) {
246
+ switch (platform()) {
247
+ case 'darwin': return darwinSet(account, value);
248
+ case 'linux': return linuxSet(account, value);
249
+ case 'win32': return win32Set(account, value);
250
+ default: throw new Error(`[keychain] unsupported platform: ${process.platform}`);
251
+ }
252
+ }
253
+
254
+ function deleteSecret(account) {
255
+ switch (platform()) {
256
+ case 'darwin': return darwinDelete(account);
257
+ case 'linux': return linuxDelete(account);
258
+ case 'win32': return win32Delete(account);
259
+ default: throw new Error(`[keychain] unsupported platform: ${process.platform}`);
260
+ }
261
+ }
262
+
263
+ module.exports = { getSecret, setSecret, deleteSecret, SERVICE };
@@ -0,0 +1,61 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Canonical resolver for CLAUDE_PLUGIN_DATA (plugin data dir).
5
+ *
6
+ * Resolution order:
7
+ * 1. process.env.CLAUDE_PLUGIN_DATA — set by Claude Code when spawning
8
+ * the MCP server or a hook.
9
+ * 2. Derive from CLAUDE_PLUGIN_ROOT — works both for cache layout
10
+ * (.../cache/{marketplace}/{plugin}/{version}/)
11
+ * and marketplace layout
12
+ * (.../marketplaces/{marketplace}/external_plugins/{plugin}/).
13
+ *
14
+ * Throws if neither env var is present — the plugin always runs under
15
+ * Claude Code, which sets one of them. Callers must not silently fall
16
+ * back to a hardcoded path.
17
+ *
18
+ * DEFAULT_PLUGIN / DEFAULT_MARKETPLACE are exported so a handful of
19
+ * callers (MCP client spawning sibling plugins, session-manager building
20
+ * PLUGIN_ROOT for rule injection) can reference the canonical names
21
+ * without re-hardcoding the strings. Update both in lockstep with
22
+ * `.claude-plugin/marketplace.json` if the marketplace is ever renamed.
23
+ */
24
+
25
+ const path = require('path');
26
+ const os = require('os');
27
+ const fs = require('fs');
28
+
29
+ const DEFAULT_PLUGIN = 'mixdog';
30
+ const DEFAULT_MARKETPLACE = 'trib-plugin';
31
+
32
+ function readPluginManifestName(root) {
33
+ try {
34
+ const manifest = JSON.parse(fs.readFileSync(path.join(root, '.claude-plugin', 'plugin.json'), 'utf8'));
35
+ if (manifest && typeof manifest.name === 'string' && manifest.name.trim()) return manifest.name.trim();
36
+ } catch { /* fall through to default */ }
37
+ return DEFAULT_PLUGIN;
38
+ }
39
+
40
+ function resolvePluginData() {
41
+ if (process.env.CLAUDE_PLUGIN_DATA) return process.env.CLAUDE_PLUGIN_DATA;
42
+ const root = process.env.CLAUDE_PLUGIN_ROOT;
43
+ if (root) {
44
+ const dirName = path.basename(root);
45
+ // Cache layout schema: <plugin>/mixdog/<semver>/ — regex matches the semver dir prefix in the path.
46
+ if (/^\d+\.\d+\.\d+/.test(dirName)) {
47
+ const pluginName = path.basename(path.join(root, '..'));
48
+ const marketplace = path.basename(path.join(root, '..', '..'));
49
+ return path.join(os.homedir(), '.claude', 'plugins', 'data', `${pluginName}-${marketplace}`);
50
+ }
51
+ // Marketplace layout: .../marketplaces/{marketplace}/
52
+ // The root dir itself is the marketplace. Plugin name lives in the
53
+ // manifest; fall back to DEFAULT_PLUGIN when it's unreadable.
54
+ const marketplace = dirName;
55
+ const pluginName = readPluginManifestName(root);
56
+ return path.join(os.homedir(), '.claude', 'plugins', 'data', `${pluginName}-${marketplace}`);
57
+ }
58
+ throw new Error('[plugin-paths] CLAUDE_PLUGIN_DATA and CLAUDE_PLUGIN_ROOT are both unset — cannot resolve plugin data dir outside of Claude Code.');
59
+ }
60
+
61
+ module.exports = { resolvePluginData, DEFAULT_PLUGIN, DEFAULT_MARKETPLACE };
@@ -0,0 +1,241 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * mixdog rules builder.
5
+ *
6
+ * Three surfaces:
7
+ * - buildInjectionContent — Lead (Claude Code main session)
8
+ * - buildBridgeInjectionContent — bridge session BP1 (true cross-role common)
9
+ * - buildBridgeRoleSpecificContent — bridge session BP3 (role-specific instructions)
10
+ *
11
+ * 4-BP cache layout (composeSystemPrompt):
12
+ * BP1 = bridge BP1 content (this file's buildBridgeInjectionContent) — every role identical
13
+ * BP2 = scoped role catalog (collect.mjs loadScopedRoleCatalog) — role family / self
14
+ * BP3 = project context + role marker + permission + role-specific instructions
15
+ * BP4 = task brief + memory recap (5m volatile)
16
+ *
17
+ * Source files (rules/):
18
+ * - shared/01-tool.md — universal tool policy (Lead + bridge BP1, identical full set)
19
+ * - lead/00-tool-lead.md — Lead-specific control-tower / delegation / ToolSearch guidance
20
+ * - lead/01-04 — Lead workflow / channels / team / general
21
+ * - bridge/00-common.md — bridge common behavior + universal worker contract (BP1)
22
+ * - bridge/10..50-*.md — per-hidden-role bodies (consumed by loadScopedRoleCatalog)
23
+ *
24
+ * Core memory snapshot and session recap are injected separately by
25
+ * hooks/session-start.cjs from the memory worker (pgdata) (Lead only).
26
+ */
27
+
28
+ const fs = require('fs');
29
+ const path = require('path');
30
+
31
+ /**
32
+ * Read a single section from mixdog-config.json (unified config).
33
+ *
34
+ * @param {string} dataDir — DATA_DIR passed into build* functions
35
+ * @param {string} section — top-level key ('memory' | 'search' | …)
36
+ * @returns {object}
37
+ */
38
+ function readConfigSection(dataDir, section) {
39
+ try {
40
+ const unified = JSON.parse(fs.readFileSync(path.join(dataDir, 'mixdog-config.json'), 'utf8'));
41
+ if (unified && typeof unified === 'object') return unified[section] || {};
42
+ } catch {}
43
+ return {};
44
+ }
45
+
46
+ function readOptional(filePath) {
47
+ try { return fs.readFileSync(filePath, 'utf8').trim(); } catch { return ''; }
48
+ }
49
+
50
+ /**
51
+ * Recursively collect all `.md` files under `dir`. Returns absolute paths
52
+ * in stack-DFS order (callers sort before use). Missing/unreadable `dir`
53
+ * yields an empty array — matches the previous inline `try {} catch {}`
54
+ * behavior at every call site.
55
+ */
56
+ function collectMarkdownFilesRecursive(dir) {
57
+ const collected = [];
58
+ try {
59
+ const stack = [dir];
60
+ while (stack.length) {
61
+ const current = stack.pop();
62
+ const entries = fs.readdirSync(current, { withFileTypes: true });
63
+ for (const entry of entries) {
64
+ const full = path.join(current, entry.name);
65
+ if (entry.isDirectory()) stack.push(full);
66
+ else if (entry.isFile() && entry.name.endsWith('.md')) collected.push(full);
67
+ }
68
+ }
69
+ } catch {}
70
+ return collected;
71
+ }
72
+
73
+ // Address-form rule. Reads only `user.title` — `user.name` is intentionally
74
+ // not consumed; the user is addressed solely by the configured form.
75
+ // Returns '' when title is empty so nothing is injected.
76
+ function composeUserAddressBullet(memoryConfig) {
77
+ const userTitle = (memoryConfig.user && memoryConfig.user.title || '').trim();
78
+ if (!userTitle) return '';
79
+ return `- User address form: ${userTitle} (use this address form exactly as written; do not combine it with any other name or honorific)`;
80
+ }
81
+
82
+ /**
83
+ * Build the Lead injection content.
84
+ */
85
+ function buildInjectionContent({ PLUGIN_ROOT, DATA_DIR }) {
86
+ const RULES_DIR = path.join(PLUGIN_ROOT, 'rules');
87
+ const SHARED_DIR = path.join(RULES_DIR, 'shared');
88
+ const LEAD_DIR = path.join(RULES_DIR, 'lead');
89
+ const HISTORY_DIR = path.join(DATA_DIR, 'history');
90
+
91
+ const memoryConfig = readConfigSection(DATA_DIR, 'memory');
92
+ const parts = [];
93
+
94
+ // Language policy injects first — global default, applied to every Lead reply
95
+ // and to plugin-internal communication regardless of locale.
96
+ const language = readOptional(path.join(SHARED_DIR, '00-language.md'));
97
+ if (language) parts.push(language);
98
+
99
+ const general = readOptional(path.join(LEAD_DIR, '01-general.md'));
100
+ if (general) {
101
+ const addressBullet = composeUserAddressBullet(memoryConfig);
102
+ parts.push(addressBullet ? `${general}\n${addressBullet}` : general);
103
+ }
104
+
105
+ const tool = readOptional(path.join(SHARED_DIR, '01-tool.md'));
106
+ if (tool) parts.push(tool);
107
+
108
+ const toolLead = readOptional(path.join(LEAD_DIR, '00-tool-lead.md'));
109
+ if (toolLead) parts.push(toolLead);
110
+
111
+ const channels = readOptional(path.join(LEAD_DIR, '02-channels.md'));
112
+ if (channels) parts.push(channels);
113
+
114
+ const team = readOptional(path.join(LEAD_DIR, '03-team.md'));
115
+ if (team) parts.push(team);
116
+
117
+ const workflow = readOptional(path.join(LEAD_DIR, '04-workflow.md'));
118
+ if (workflow) parts.push(workflow);
119
+
120
+ const userWorkflowJsonPath = path.join(DATA_DIR, 'user-workflow.json');
121
+ let userWorkflow = { roles: [] };
122
+ try {
123
+ if (fs.existsSync(userWorkflowJsonPath)) {
124
+ userWorkflow = JSON.parse(fs.readFileSync(userWorkflowJsonPath, 'utf8'));
125
+ }
126
+ } catch {}
127
+ if (Array.isArray(userWorkflow.roles) && userWorkflow.roles.length > 0) {
128
+ const roleLines = ['# Roles', ''];
129
+ for (const role of userWorkflow.roles) {
130
+ roleLines.push(`- ${role.name}: ${role.preset}`);
131
+ }
132
+ parts.push(roleLines.join('\n'));
133
+ }
134
+
135
+ const userWorkflowMdPath = path.join(DATA_DIR, 'user-workflow.md');
136
+ const userWorkflowMd = readOptional(userWorkflowMdPath);
137
+ if (userWorkflowMd) {
138
+ const startsWithHeader = /^#\s+User Workflow/i.test(userWorkflowMd);
139
+ parts.push(startsWithHeader ? userWorkflowMd : `# User Workflow\n\n${userWorkflowMd}`);
140
+ }
141
+
142
+ const userProfile = readOptional(path.join(HISTORY_DIR, 'user.md'));
143
+ if (userProfile) parts.push(`# User Profile\n\n${userProfile}`);
144
+
145
+ const botPersona = readOptional(path.join(HISTORY_DIR, 'bot.md'));
146
+ if (botPersona) parts.push(`# Bot Persona\n\n${botPersona}`);
147
+
148
+ return parts.join('\n\n');
149
+ }
150
+
151
+ /**
152
+ * BP1 — true cross-role common. Identical for every bridge role; the
153
+ * role-specific stuff (per-event webhook instructions, per-task schedule
154
+ * instructions, hidden role tool detail) lives in BP3 instead.
155
+ *
156
+ * @param {object} opts
157
+ * @param {string} opts.PLUGIN_ROOT
158
+ * @param {string} opts.DATA_DIR
159
+ * @returns {string}
160
+ */
161
+ function buildBridgeInjectionContent({ PLUGIN_ROOT, DATA_DIR }) {
162
+ const RULES_DIR = path.join(PLUGIN_ROOT, 'rules');
163
+ const SHARED_DIR = path.join(RULES_DIR, 'shared');
164
+ const BRIDGE_DIR = path.join(RULES_DIR, 'bridge');
165
+ const parts = [];
166
+
167
+ // 0. Language policy — global default, language-neutral plugin behavior.
168
+ const language = readOptional(path.join(SHARED_DIR, '00-language.md'));
169
+ if (language) parts.push(language);
170
+
171
+ // 1. Universal tool policy — same full set Lead receives.
172
+ const tool = readOptional(path.join(SHARED_DIR, '01-tool.md'));
173
+ if (tool) parts.push(tool);
174
+
175
+ // 2. Bridge common behavior.
176
+ const common = readOptional(path.join(BRIDGE_DIR, '00-common.md'));
177
+ if (common) parts.push(common);
178
+
179
+ // 3. User-defined work-role overrides (DATA_DIR/roles/*.md). Pool-wide.
180
+ const rolesDir = path.join(DATA_DIR, 'roles');
181
+ const collected = collectMarkdownFilesRecursive(rolesDir);
182
+ if (collected.length > 0) {
183
+ collected.sort();
184
+ const blocks = collected.map(f => readOptional(f)).filter(Boolean);
185
+ if (blocks.length > 0) {
186
+ parts.push(['# Agent roles', '', blocks.join('\n\n')].join('\n'));
187
+ }
188
+ }
189
+
190
+ // userTitle / address form is intentionally NOT injected here — bridge
191
+ // workers produce tool I/O, not user-facing replies, so the persona signal
192
+ // only biases response language/tone without serving a purpose. Lead BP1
193
+ // (buildInjectionContent above) still carries it.
194
+
195
+ return parts.join('\n\n');
196
+ }
197
+
198
+ /**
199
+ * BP3 role-specific instructions. Only the calling role's own task / tool
200
+ * detail body emits — webhook-handler gets webhooks/<all-events>/, scheduler
201
+ * gets schedules/<all-tasks>/, hidden retrieval roles get their own tool
202
+ * detail. Other roles return ''.
203
+ *
204
+ * NOTE: webhook-event narrowing (one event per call) requires the inbound
205
+ * payload's event id at compose time; not implemented yet, so all 4 webhook
206
+ * instructions still bake into webhook-handler BP3 for now.
207
+ *
208
+ * @param {object} opts
209
+ * @param {string} opts.DATA_DIR
210
+ * @param {string|null} opts.currentRole
211
+ * @returns {string}
212
+ */
213
+ function buildBridgeRoleSpecificContent({ DATA_DIR, currentRole }) {
214
+ if (!currentRole) return '';
215
+ const parts = [];
216
+
217
+ // webhook-handler / scheduler-task — pull their respective instruction trees.
218
+ const subdirForRole =
219
+ currentRole === 'webhook-handler' ? 'webhooks'
220
+ : currentRole === 'scheduler-task' ? 'schedules'
221
+ : null;
222
+ if (subdirForRole) {
223
+ const dir = path.join(DATA_DIR, subdirForRole);
224
+ const collected = collectMarkdownFilesRecursive(dir);
225
+ if (collected.length > 0) {
226
+ collected.sort();
227
+ const blocks = collected.map(f => readOptional(f)).filter(Boolean);
228
+ if (blocks.length > 0) {
229
+ parts.push([`# Agent ${subdirForRole}`, '', blocks.join('\n\n')].join('\n'));
230
+ }
231
+ }
232
+ }
233
+
234
+ return parts.join('\n\n');
235
+ }
236
+
237
+ module.exports = {
238
+ buildInjectionContent,
239
+ buildBridgeInjectionContent,
240
+ buildBridgeRoleSpecificContent,
241
+ };
@@ -0,0 +1,61 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Shared text-cleaning utilities. Used by both:
5
+ * - hooks/session-start.cjs (CJS hook)
6
+ * - src/memory/lib/memory-extraction.mjs (ESM, via createRequire re-export)
7
+ *
8
+ * Single source of truth for the regex set that strips:
9
+ * - markdown fences, headers, list markers, bold
10
+ * - tool/system tags emitted by Claude Code (system-reminder, tool-use-id,
11
+ * local-command-*, etc.)
12
+ * - mixdog tags (channel, schedule-context, teammate-message)
13
+ * - URLs, emoji, "Ran X", "Process exited", and other transcript noise
14
+ */
15
+
16
+ function cleanMemoryText(text) {
17
+ return String(text ?? '')
18
+ .replace(/```[\s\S]*?```/g, '')
19
+ .replace(/<memory-context>[\s\S]*?<\/memory-context>/gi, '')
20
+ .replace(/<local-command-caveat>[\s\S]*?<\/local-command-caveat>/gi, '')
21
+ .replace(/<local-command-stdout>[\s\S]*?<\/local-command-stdout>/gi, '')
22
+ .replace(/<command-name>[\s\S]*?<\/command-name>/gi, '')
23
+ .replace(/<command-message>[\s\S]*?<\/command-message>/gi, '')
24
+ .replace(/<command-args>[\s\S]*?<\/command-args>/gi, '')
25
+ .replace(/<task-notification>[\s\S]*?<\/task-notification>/gi, '')
26
+ .replace(/<tool-use-id>[\s\S]*?<\/tool-use-id>/gi, '')
27
+ .replace(/<output-file>[\s\S]*?<\/output-file>/gi, '')
28
+ .replace(/<system-reminder>[\s\S]*?<\/system-reminder>/gi, '')
29
+ .replace(/<schedule-context>[\s\S]*?<\/schedule-context>/gi, '')
30
+ .replace(/<teammate-message[\s\S]*?<\/teammate-message>/gi, '')
31
+ .replace(/<channel[^>]*>\n?([\s\S]*?)\n?<\/channel>/g, '$1')
32
+ .replace(/^[ \t]*\|.*\|[ \t]*$/gm, '')
33
+ .replace(/`([^`]+)`/g, '$1')
34
+ .replace(/\*\*/g, '')
35
+ .replace(/^#{1,4}\s+/gm, '')
36
+ .replace(/^>\s?/gm, '')
37
+ .replace(/^[-*]\s+/gm, '')
38
+ .replace(/https?:\/\/\S+/g, '')
39
+ .replace(/^This session is being continued from a previous conversation[\s\S]*?(?=\n\n|$)/gim, '')
40
+ .replace(/^\[[^\]\n]{1,140}\]\s*$/gm, '')
41
+ .replace(/^\s*●\s.*$/gm, '')
42
+ .replace(/^\s*Ran .*$/gm, '')
43
+ // stable best-effort sanitization, do not extend without justification
44
+ .replace(/^\s*Command: .*$/gm, '')
45
+ .replace(/^\s*Process exited .*$/gm, '')
46
+ .replace(/^\s*Full transcript available at: .*$/gm, '')
47
+ .replace(/^\s*Read the output file to retrieve the result: .*$/gm, '')
48
+ .replace(/^\s*Original token count: .*$/gm, '')
49
+ .replace(/^\s*Wall time: .*$/gm, '')
50
+ .replace(/^\s*Chunk ID: .*$/gm, '')
51
+ .replace(/^\s*tool_uses: .*$/gm, '')
52
+ .replace(/^\s*menu item .*$/gm, '')
53
+ .replace(/<\/?[a-z][-a-z]*(?:\s[^>]*)?\/?>/gi, '')
54
+ .replace(/[\u{1F300}-\u{1FAD6}\u{2600}-\u{27BF}]/gu, '')
55
+ .replace(/[ \t]+/g, ' ')
56
+ .replace(/\n{2,}/g, '\n')
57
+ .replace(/^\s+|\s+$/gm, '')
58
+ .trim();
59
+ }
60
+
61
+ module.exports = { cleanMemoryText };