@goondocks/myco 0.20.2 → 0.21.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 (216) hide show
  1. package/bin/myco-run +68 -7
  2. package/dist/agent-eval-YK2VP2S4.js +356 -0
  3. package/dist/agent-eval-YK2VP2S4.js.map +1 -0
  4. package/dist/{agent-run-X25Q2A6T.js → agent-run-GEJBD2YD.js} +10 -8
  5. package/dist/{agent-run-X25Q2A6T.js.map → agent-run-GEJBD2YD.js.map} +1 -1
  6. package/dist/{agent-tasks-7B6OFERB.js → agent-tasks-5XSRGTRX.js} +10 -8
  7. package/dist/{agent-tasks-7B6OFERB.js.map → agent-tasks-5XSRGTRX.js.map} +1 -1
  8. package/dist/{chunk-OD4AA7PV.js → chunk-53RPGOEN.js} +56 -8
  9. package/dist/chunk-53RPGOEN.js.map +1 -0
  10. package/dist/chunk-54SXG5HF.js +26 -0
  11. package/dist/chunk-54SXG5HF.js.map +1 -0
  12. package/dist/{chunk-XATDZX7U.js → chunk-6ALVMIB4.js} +19 -5
  13. package/dist/{chunk-XATDZX7U.js.map → chunk-6ALVMIB4.js.map} +1 -1
  14. package/dist/{chunk-FLLBJLHM.js → chunk-6C6QZ4PM.js} +9 -5
  15. package/dist/chunk-6C6QZ4PM.js.map +1 -0
  16. package/dist/{chunk-CCRGY3QW.js → chunk-AUIXX33A.js} +24 -95
  17. package/dist/chunk-AUIXX33A.js.map +1 -0
  18. package/dist/chunk-CISWUP5W.js +101 -0
  19. package/dist/chunk-CISWUP5W.js.map +1 -0
  20. package/dist/{chunk-MYOZLMB2.js → chunk-DTWUHHFI.js} +576 -20
  21. package/dist/chunk-DTWUHHFI.js.map +1 -0
  22. package/dist/chunk-EEOJWLMP.js +582 -0
  23. package/dist/chunk-EEOJWLMP.js.map +1 -0
  24. package/dist/{chunk-XG5RRUYF.js → chunk-ENZR5NG7.js} +2 -2
  25. package/dist/{chunk-6RFZWV4R.js → chunk-FCJ5JV54.js} +1 -1
  26. package/dist/{chunk-6RFZWV4R.js.map → chunk-FCJ5JV54.js.map} +1 -1
  27. package/dist/{chunk-US4LNCAT.js → chunk-IPPMYQ2Y.js} +5 -1
  28. package/dist/chunk-IPPMYQ2Y.js.map +1 -0
  29. package/dist/{chunk-VVNL26WX.js → chunk-KTTSXYEK.js} +22 -10
  30. package/dist/chunk-KTTSXYEK.js.map +1 -0
  31. package/dist/chunk-LQIPXVDH.js +17 -0
  32. package/dist/chunk-LQIPXVDH.js.map +1 -0
  33. package/dist/{chunk-DCSGJ7W4.js → chunk-N2DGFACQ.js} +3 -3
  34. package/dist/chunk-N7Z3LUEZ.js +858 -0
  35. package/dist/chunk-N7Z3LUEZ.js.map +1 -0
  36. package/dist/{chunk-2PDWCDKY.js → chunk-NFO7BRCO.js} +10 -7
  37. package/dist/{chunk-2PDWCDKY.js.map → chunk-NFO7BRCO.js.map} +1 -1
  38. package/dist/{chunk-Q36VMZST.js → chunk-OTQH5KZW.js} +89 -38
  39. package/dist/chunk-OTQH5KZW.js.map +1 -0
  40. package/dist/chunk-OUJSQSKE.js +113 -0
  41. package/dist/chunk-OUJSQSKE.js.map +1 -0
  42. package/dist/chunk-OZ3FBAK5.js +50 -0
  43. package/dist/chunk-OZ3FBAK5.js.map +1 -0
  44. package/dist/chunk-QATYARI5.js +408 -0
  45. package/dist/chunk-QATYARI5.js.map +1 -0
  46. package/dist/{chunk-KESLPBKV.js → chunk-QDLVIW2O.js} +4 -4
  47. package/dist/{chunk-5XIVBO25.js → chunk-QLLBJEM7.js} +6 -2
  48. package/dist/chunk-QLLBJEM7.js.map +1 -0
  49. package/dist/{chunk-EVDQKYCG.js → chunk-RQSJLWP4.js} +13 -2
  50. package/dist/chunk-RQSJLWP4.js.map +1 -0
  51. package/dist/{chunk-BPRIYNLE.js → chunk-TKAJ3JVF.js} +3 -3
  52. package/dist/chunk-TSM6VESW.js +25 -0
  53. package/dist/chunk-TSM6VESW.js.map +1 -0
  54. package/dist/{chunk-6X2ERTQV.js → chunk-USVFEWYL.js} +6 -4
  55. package/dist/{chunk-6X2ERTQV.js.map → chunk-USVFEWYL.js.map} +1 -1
  56. package/dist/{chunk-JZGN33AY.js → chunk-VRI56337.js} +4 -4
  57. package/dist/chunk-X2IRGXGF.js +14103 -0
  58. package/dist/chunk-X2IRGXGF.js.map +1 -0
  59. package/dist/{chunk-FMRZ26U5.js → chunk-X3IGT5RV.js} +5 -2
  60. package/dist/{chunk-FMRZ26U5.js.map → chunk-X3IGT5RV.js.map} +1 -1
  61. package/dist/{chunk-KHT24OWC.js → chunk-YDUOSRGD.js} +8 -94
  62. package/dist/{chunk-KHT24OWC.js.map → chunk-YDUOSRGD.js.map} +1 -1
  63. package/dist/{chunk-NGROSFOH.js → chunk-Z66IT5KL.js} +14 -9
  64. package/dist/chunk-Z66IT5KL.js.map +1 -0
  65. package/dist/{cli-GGPWH4UO.js → cli-HSLIG7EX.js} +50 -43
  66. package/dist/cli-HSLIG7EX.js.map +1 -0
  67. package/dist/{client-YXQUTXVZ.js → client-Z43DNLJH.js} +4 -4
  68. package/dist/{config-OMCYHG2S.js → config-VC4ACP42.js} +6 -4
  69. package/dist/{config-OMCYHG2S.js.map → config-VC4ACP42.js.map} +1 -1
  70. package/dist/{detect-PXNM6TA7.js → detect-7NUD5B5R.js} +2 -2
  71. package/dist/{detect-providers-5KOPZ7J2.js → detect-providers-ILLQZROY.js} +4 -4
  72. package/dist/{doctor-5JXJ36KA.js → doctor-HJCWHAU4.js} +49 -16
  73. package/dist/doctor-HJCWHAU4.js.map +1 -0
  74. package/dist/executor-DO6QFC6G.js +45 -0
  75. package/dist/{init-LMYOVZAV.js → init-4KVK7W2E.js} +16 -14
  76. package/dist/{init-LMYOVZAV.js.map → init-4KVK7W2E.js.map} +1 -1
  77. package/dist/{installer-FS257JRZ.js → installer-N4UTEACX.js} +6 -4
  78. package/dist/{llm-TH4NLIRM.js → llm-AGVEF5XD.js} +5 -4
  79. package/dist/{loader-CQYTFHEW.js → loader-LX7TFRM6.js} +5 -3
  80. package/dist/{loader-NOMBJUPW.js → loader-UDNUMEDA.js} +5 -3
  81. package/dist/{main-YTBVRTBI.js → main-4J4QZZTZ.js} +2518 -656
  82. package/dist/main-4J4QZZTZ.js.map +1 -0
  83. package/dist/{open-HG2DX6RN.js → open-7TXJQM3H.js} +10 -8
  84. package/dist/{open-HG2DX6RN.js.map → open-7TXJQM3H.js.map} +1 -1
  85. package/dist/{post-compact-JSECI44W.js → post-compact-7AEFVCZS.js} +8 -8
  86. package/dist/{post-tool-use-POGPTJBA.js → post-tool-use-TZINWWDH.js} +11 -9
  87. package/dist/post-tool-use-TZINWWDH.js.map +1 -0
  88. package/dist/{post-tool-use-failure-OT7BFWQW.js → post-tool-use-failure-TCFEU2GI.js} +8 -8
  89. package/dist/{pre-compact-OXVODKH4.js → pre-compact-LO2VZCGR.js} +8 -8
  90. package/dist/{provider-check-43LAMSMH.js → provider-check-ZEV5P4KM.js} +4 -4
  91. package/dist/{registry-U4CHXK6R.js → registry-F3THYC5M.js} +6 -4
  92. package/dist/{remove-N7ZPELFU.js → remove-F77AAALE.js} +12 -10
  93. package/dist/{remove-N7ZPELFU.js.map → remove-F77AAALE.js.map} +1 -1
  94. package/dist/{restart-ADG5GBTB.js → restart-UEFDPMLT.js} +11 -9
  95. package/dist/{restart-ADG5GBTB.js.map → restart-UEFDPMLT.js.map} +1 -1
  96. package/dist/{search-AHZEUNRR.js → search-NHNVUAQQ.js} +11 -9
  97. package/dist/{search-AHZEUNRR.js.map → search-NHNVUAQQ.js.map} +1 -1
  98. package/dist/{server-AGVYZVP5.js → server-AZJSTQEK.js} +369 -270
  99. package/dist/server-AZJSTQEK.js.map +1 -0
  100. package/dist/{session-6IU4AXYP.js → session-3HLC5KOD.js} +11 -9
  101. package/dist/{session-6IU4AXYP.js.map → session-3HLC5KOD.js.map} +1 -1
  102. package/dist/{session-end-FT27DWYZ.js → session-end-FS46UARX.js} +7 -7
  103. package/dist/session-start-46KPFV2H.js +134 -0
  104. package/dist/session-start-46KPFV2H.js.map +1 -0
  105. package/dist/{setup-llm-77MP4I2G.js → setup-llm-JMWSNQ2C.js} +11 -9
  106. package/dist/{setup-llm-77MP4I2G.js.map → setup-llm-JMWSNQ2C.js.map} +1 -1
  107. package/dist/src/agent/definitions/agent.yaml +9 -5
  108. package/dist/src/agent/definitions/tasks/cortex-instructions.yaml +115 -0
  109. package/dist/src/agent/definitions/tasks/cortex-prompt-builder.yaml +67 -0
  110. package/dist/src/agent/definitions/tasks/digest-only.yaml +1 -1
  111. package/dist/src/agent/definitions/tasks/extract-only.yaml +1 -1
  112. package/dist/src/agent/definitions/tasks/review-session.yaml +10 -39
  113. package/dist/src/agent/definitions/tasks/skill-evolve.yaml +181 -25
  114. package/dist/src/agent/definitions/tasks/skill-generate.yaml +21 -7
  115. package/dist/src/agent/definitions/tasks/skill-survey.yaml +2 -6
  116. package/dist/src/agent/definitions/tasks/supersession-sweep.yaml +1 -1
  117. package/dist/src/agent/definitions/tasks/title-summary.yaml +12 -19
  118. package/dist/src/agent/definitions/tasks/{full-intelligence.yaml → vault-evolve.yaml} +74 -129
  119. package/dist/src/agent/definitions/tasks/vault-seed.yaml +370 -0
  120. package/dist/src/agent/prompts/agent.md +12 -38
  121. package/dist/src/cli.js +1 -1
  122. package/dist/src/daemon/main.js +1 -1
  123. package/dist/src/hooks/post-tool-use.js +1 -1
  124. package/dist/src/hooks/session-end.js +1 -1
  125. package/dist/src/hooks/session-start.js +1 -1
  126. package/dist/src/hooks/stop.js +1 -1
  127. package/dist/src/hooks/user-prompt-submit.js +1 -1
  128. package/dist/src/mcp/server.js +1 -1
  129. package/dist/src/symbionts/manifests/claude-code.yaml +4 -0
  130. package/dist/src/symbionts/manifests/opencode.yaml +7 -0
  131. package/dist/src/symbionts/manifests/pi.yaml +22 -0
  132. package/dist/src/symbionts/templates/agents-starter.md +1 -1
  133. package/dist/src/symbionts/templates/pi/package.json +6 -0
  134. package/dist/src/symbionts/templates/pi/plugin.ts +559 -0
  135. package/dist/{stats-NVPWOYTE.js → stats-MKMETHMA.js} +11 -9
  136. package/dist/{stats-NVPWOYTE.js.map → stats-MKMETHMA.js.map} +1 -1
  137. package/dist/{stop-ZPIKVLH4.js → stop-OUEX6KA4.js} +7 -7
  138. package/dist/{stop-failure-2PX67YJC.js → stop-failure-2BWVNZEG.js} +8 -8
  139. package/dist/{subagent-start-UUE6EHQD.js → subagent-start-J4VV6DEE.js} +8 -8
  140. package/dist/{subagent-stop-KQWWWPE6.js → subagent-stop-JMLVEPIA.js} +8 -8
  141. package/dist/{task-completed-WMHOFQ7B.js → task-completed-65CHMMKA.js} +8 -8
  142. package/dist/{team-LRZ6GTQK.js → team-U2LDKIS4.js} +7 -5
  143. package/dist/{turns-YFNI5CQC.js → turns-HU2CTZAP.js} +2 -2
  144. package/dist/ui/assets/index-BUGor9dk.js +842 -0
  145. package/dist/ui/assets/index-_OP4ifzH.css +1 -0
  146. package/dist/ui/index.html +2 -2
  147. package/dist/{update-O6V4RC4W.js → update-ZSHVXWSQ.js} +12 -10
  148. package/dist/{update-O6V4RC4W.js.map → update-ZSHVXWSQ.js.map} +1 -1
  149. package/dist/{user-prompt-submit-N36KUPHI.js → user-prompt-submit-APMO6FVU.js} +10 -9
  150. package/dist/{user-prompt-submit-N36KUPHI.js.map → user-prompt-submit-APMO6FVU.js.map} +1 -1
  151. package/dist/{verify-LXPV7NYG.js → verify-R76ZFJSZ.js} +8 -5
  152. package/dist/{verify-LXPV7NYG.js.map → verify-R76ZFJSZ.js.map} +1 -1
  153. package/dist/{version-XMPPJQHR.js → version-TXPPS3L5.js} +2 -2
  154. package/dist/version-TXPPS3L5.js.map +1 -0
  155. package/package.json +3 -1
  156. package/skills/myco/SKILL.md +16 -1
  157. package/skills/myco/references/cli-usage.md +1 -1
  158. package/skills/myco-rules/SKILL.md +94 -0
  159. package/skills/{rules → myco-rules}/references/rules-bad-example.md +1 -1
  160. package/skills/{rules → myco-rules}/references/rules-good-example.md +1 -1
  161. package/dist/chunk-4YFKBL3F.js +0 -195
  162. package/dist/chunk-4YFKBL3F.js.map +0 -1
  163. package/dist/chunk-5XIVBO25.js.map +0 -1
  164. package/dist/chunk-CCRGY3QW.js.map +0 -1
  165. package/dist/chunk-CUDIZJY7.js +0 -36
  166. package/dist/chunk-CUDIZJY7.js.map +0 -1
  167. package/dist/chunk-EVDQKYCG.js.map +0 -1
  168. package/dist/chunk-FLLBJLHM.js.map +0 -1
  169. package/dist/chunk-MYOZLMB2.js.map +0 -1
  170. package/dist/chunk-NGROSFOH.js.map +0 -1
  171. package/dist/chunk-OD4AA7PV.js.map +0 -1
  172. package/dist/chunk-Q36VMZST.js.map +0 -1
  173. package/dist/chunk-US4LNCAT.js.map +0 -1
  174. package/dist/chunk-UYMFCYBF.js +0 -2326
  175. package/dist/chunk-UYMFCYBF.js.map +0 -1
  176. package/dist/chunk-VVNL26WX.js.map +0 -1
  177. package/dist/cli-GGPWH4UO.js.map +0 -1
  178. package/dist/doctor-5JXJ36KA.js.map +0 -1
  179. package/dist/executor-HWW2QNZQ.js +0 -2472
  180. package/dist/executor-HWW2QNZQ.js.map +0 -1
  181. package/dist/main-YTBVRTBI.js.map +0 -1
  182. package/dist/post-tool-use-POGPTJBA.js.map +0 -1
  183. package/dist/server-AGVYZVP5.js.map +0 -1
  184. package/dist/session-start-LAFICHII.js +0 -189
  185. package/dist/session-start-LAFICHII.js.map +0 -1
  186. package/dist/src/agent/definitions/tasks/graph-maintenance.yaml +0 -93
  187. package/dist/ui/assets/index-C2JuNtRB.css +0 -1
  188. package/dist/ui/assets/index-JLVaQKV2.js +0 -832
  189. package/skills/myco-curate/SKILL.md +0 -86
  190. package/skills/rules/SKILL.md +0 -214
  191. /package/dist/{chunk-XG5RRUYF.js.map → chunk-ENZR5NG7.js.map} +0 -0
  192. /package/dist/{chunk-DCSGJ7W4.js.map → chunk-N2DGFACQ.js.map} +0 -0
  193. /package/dist/{chunk-KESLPBKV.js.map → chunk-QDLVIW2O.js.map} +0 -0
  194. /package/dist/{chunk-BPRIYNLE.js.map → chunk-TKAJ3JVF.js.map} +0 -0
  195. /package/dist/{chunk-JZGN33AY.js.map → chunk-VRI56337.js.map} +0 -0
  196. /package/dist/{client-YXQUTXVZ.js.map → client-Z43DNLJH.js.map} +0 -0
  197. /package/dist/{detect-PXNM6TA7.js.map → detect-7NUD5B5R.js.map} +0 -0
  198. /package/dist/{detect-providers-5KOPZ7J2.js.map → detect-providers-ILLQZROY.js.map} +0 -0
  199. /package/dist/{installer-FS257JRZ.js.map → executor-DO6QFC6G.js.map} +0 -0
  200. /package/dist/{llm-TH4NLIRM.js.map → installer-N4UTEACX.js.map} +0 -0
  201. /package/dist/{loader-CQYTFHEW.js.map → llm-AGVEF5XD.js.map} +0 -0
  202. /package/dist/{loader-NOMBJUPW.js.map → loader-LX7TFRM6.js.map} +0 -0
  203. /package/dist/{provider-check-43LAMSMH.js.map → loader-UDNUMEDA.js.map} +0 -0
  204. /package/dist/{post-compact-JSECI44W.js.map → post-compact-7AEFVCZS.js.map} +0 -0
  205. /package/dist/{post-tool-use-failure-OT7BFWQW.js.map → post-tool-use-failure-TCFEU2GI.js.map} +0 -0
  206. /package/dist/{pre-compact-OXVODKH4.js.map → pre-compact-LO2VZCGR.js.map} +0 -0
  207. /package/dist/{registry-U4CHXK6R.js.map → provider-check-ZEV5P4KM.js.map} +0 -0
  208. /package/dist/{team-LRZ6GTQK.js.map → registry-F3THYC5M.js.map} +0 -0
  209. /package/dist/{session-end-FT27DWYZ.js.map → session-end-FS46UARX.js.map} +0 -0
  210. /package/dist/{stop-ZPIKVLH4.js.map → stop-OUEX6KA4.js.map} +0 -0
  211. /package/dist/{stop-failure-2PX67YJC.js.map → stop-failure-2BWVNZEG.js.map} +0 -0
  212. /package/dist/{subagent-start-UUE6EHQD.js.map → subagent-start-J4VV6DEE.js.map} +0 -0
  213. /package/dist/{subagent-stop-KQWWWPE6.js.map → subagent-stop-JMLVEPIA.js.map} +0 -0
  214. /package/dist/{task-completed-WMHOFQ7B.js.map → task-completed-65CHMMKA.js.map} +0 -0
  215. /package/dist/{turns-YFNI5CQC.js.map → team-U2LDKIS4.js.map} +0 -0
  216. /package/dist/{version-XMPPJQHR.js.map → turns-HU2CTZAP.js.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/db/schema-ddl.ts","../src/constants/skill-candidate-status.ts","../src/db/migrations.ts","../src/db/schema.ts"],"sourcesContent":["/**\n * DDL constants for all Myco vault tables, FTS5 virtual tables,\n * sync triggers, and secondary indexes.\n *\n * Extracted from schema.ts for readability -- these are pure string\n * constants with no runtime behaviour.\n */\n\n// ---------------------------------------------------------------------------\n// Table DDL statements\n// ---------------------------------------------------------------------------\n\nconst SCHEMA_VERSION_TABLE = `\n CREATE TABLE IF NOT EXISTS schema_version (\n version INTEGER PRIMARY KEY,\n applied_at INTEGER NOT NULL\n )`;\n\n// -- Capture Layer ----------------------------------------------------------\n\nconst SESSIONS_TABLE = `\n CREATE TABLE IF NOT EXISTS sessions (\n id TEXT PRIMARY KEY,\n agent TEXT NOT NULL,\n \"user\" TEXT,\n project_root TEXT,\n branch TEXT,\n started_at INTEGER NOT NULL,\n ended_at INTEGER,\n status TEXT DEFAULT 'active',\n prompt_count INTEGER DEFAULT 0,\n tool_count INTEGER DEFAULT 0,\n title TEXT,\n summary TEXT,\n transcript_path TEXT,\n parent_session_id TEXT,\n parent_session_reason TEXT,\n processed INTEGER DEFAULT 0,\n content_hash TEXT UNIQUE,\n created_at INTEGER NOT NULL,\n embedded INTEGER DEFAULT 0,\n machine_id TEXT NOT NULL DEFAULT 'local',\n synced_at INTEGER\n )`;\n\nconst PROMPT_BATCHES_TABLE = `\n CREATE TABLE IF NOT EXISTS prompt_batches (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n session_id TEXT NOT NULL REFERENCES sessions(id),\n prompt_number INTEGER,\n user_prompt TEXT,\n response_summary TEXT,\n classification TEXT,\n started_at INTEGER,\n ended_at INTEGER,\n status TEXT DEFAULT 'active',\n activity_count INTEGER DEFAULT 0,\n processed INTEGER DEFAULT 0,\n content_hash TEXT UNIQUE,\n created_at INTEGER NOT NULL,\n machine_id TEXT NOT NULL DEFAULT 'local',\n synced_at INTEGER\n )`;\n\nconst ACTIVITIES_TABLE = `\n CREATE TABLE IF NOT EXISTS activities (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n session_id TEXT NOT NULL REFERENCES sessions(id),\n prompt_batch_id INTEGER REFERENCES prompt_batches(id),\n tool_name TEXT NOT NULL,\n tool_input TEXT,\n tool_output_summary TEXT,\n file_path TEXT,\n files_affected TEXT,\n duration_ms INTEGER,\n success INTEGER DEFAULT 1,\n error_message TEXT,\n timestamp INTEGER NOT NULL,\n processed INTEGER DEFAULT 0,\n content_hash TEXT UNIQUE,\n created_at INTEGER NOT NULL\n )`;\n\nconst PLANS_TABLE = `\n CREATE TABLE IF NOT EXISTS plans (\n id TEXT PRIMARY KEY,\n status TEXT DEFAULT 'active',\n author TEXT,\n title TEXT,\n content TEXT,\n source_path TEXT,\n tags TEXT,\n session_id TEXT REFERENCES sessions(id),\n prompt_batch_id INTEGER REFERENCES prompt_batches(id),\n content_hash TEXT,\n processed INTEGER DEFAULT 0,\n created_at INTEGER NOT NULL,\n updated_at INTEGER,\n embedded INTEGER DEFAULT 0,\n machine_id TEXT NOT NULL DEFAULT 'local',\n synced_at INTEGER\n )`;\n\nconst ARTIFACTS_TABLE = `\n CREATE TABLE IF NOT EXISTS artifacts (\n id TEXT PRIMARY KEY,\n artifact_type TEXT,\n source_path TEXT NOT NULL,\n title TEXT NOT NULL,\n content TEXT,\n last_captured_by TEXT,\n tags TEXT,\n created_at INTEGER NOT NULL,\n updated_at INTEGER,\n embedded INTEGER DEFAULT 0,\n machine_id TEXT NOT NULL DEFAULT 'local',\n synced_at INTEGER\n )`;\n\nconst TEAM_MEMBERS_TABLE = `\n CREATE TABLE IF NOT EXISTS team_members (\n id TEXT PRIMARY KEY,\n \"user\" TEXT NOT NULL,\n role TEXT,\n joined TEXT,\n tags TEXT,\n machine_id TEXT NOT NULL DEFAULT 'local',\n synced_at INTEGER\n )`;\n\nconst ATTACHMENTS_TABLE = `\n CREATE TABLE IF NOT EXISTS attachments (\n id TEXT PRIMARY KEY,\n session_id TEXT REFERENCES sessions(id),\n prompt_batch_id INTEGER REFERENCES prompt_batches(id),\n file_path TEXT NOT NULL,\n media_type TEXT,\n description TEXT,\n data BLOB,\n content_hash TEXT,\n created_at INTEGER NOT NULL\n )`;\n\n// -- Intelligence Layer -----------------------------------------------------\n\nconst AGENTS_TABLE = `\n CREATE TABLE IF NOT EXISTS agents (\n id TEXT PRIMARY KEY,\n name TEXT NOT NULL,\n provider TEXT,\n model TEXT,\n system_prompt_hash TEXT,\n config TEXT,\n source TEXT NOT NULL DEFAULT 'built-in',\n system_prompt TEXT,\n max_turns INTEGER,\n timeout_seconds INTEGER,\n tool_access TEXT,\n enabled INTEGER NOT NULL DEFAULT 1,\n created_at INTEGER NOT NULL,\n updated_at INTEGER\n )`;\n\nconst SPORES_TABLE = `\n CREATE TABLE IF NOT EXISTS spores (\n id TEXT PRIMARY KEY,\n agent_id TEXT NOT NULL REFERENCES agents(id),\n session_id TEXT REFERENCES sessions(id),\n prompt_batch_id INTEGER REFERENCES prompt_batches(id),\n observation_type TEXT NOT NULL,\n status TEXT DEFAULT 'active',\n content TEXT NOT NULL,\n context TEXT,\n importance INTEGER DEFAULT 5,\n file_path TEXT,\n tags TEXT,\n content_hash TEXT UNIQUE,\n properties TEXT,\n created_at INTEGER NOT NULL,\n updated_at INTEGER,\n embedded INTEGER DEFAULT 0,\n machine_id TEXT NOT NULL DEFAULT 'local',\n synced_at INTEGER\n )`;\n\nconst ENTITIES_TABLE = `\n CREATE TABLE IF NOT EXISTS entities (\n id TEXT PRIMARY KEY,\n agent_id TEXT NOT NULL REFERENCES agents(id),\n type TEXT NOT NULL,\n name TEXT NOT NULL,\n properties TEXT,\n first_seen INTEGER NOT NULL,\n last_seen INTEGER NOT NULL,\n status TEXT DEFAULT 'active',\n machine_id TEXT NOT NULL DEFAULT 'local',\n synced_at INTEGER,\n UNIQUE (agent_id, type, name)\n )`;\n\nconst GRAPH_EDGES_TABLE = `\n CREATE TABLE IF NOT EXISTS graph_edges (\n id TEXT PRIMARY KEY,\n agent_id TEXT NOT NULL REFERENCES agents(id),\n source_id TEXT NOT NULL,\n source_type TEXT NOT NULL,\n target_id TEXT NOT NULL,\n target_type TEXT NOT NULL,\n type TEXT NOT NULL,\n session_id TEXT,\n confidence REAL DEFAULT 1.0,\n properties TEXT,\n created_at INTEGER NOT NULL,\n machine_id TEXT NOT NULL DEFAULT 'local',\n synced_at INTEGER\n )`;\n\nconst ENTITY_MENTIONS_TABLE = `\n CREATE TABLE IF NOT EXISTS entity_mentions (\n entity_id TEXT NOT NULL REFERENCES entities(id),\n note_id TEXT NOT NULL,\n note_type TEXT NOT NULL,\n agent_id TEXT NOT NULL REFERENCES agents(id),\n machine_id TEXT NOT NULL DEFAULT 'local',\n synced_at INTEGER,\n UNIQUE (entity_id, note_id, note_type, agent_id)\n )`;\n\nconst RESOLUTION_EVENTS_TABLE = `\n CREATE TABLE IF NOT EXISTS resolution_events (\n id TEXT PRIMARY KEY,\n agent_id TEXT NOT NULL REFERENCES agents(id),\n spore_id TEXT NOT NULL REFERENCES spores(id),\n action TEXT NOT NULL,\n new_spore_id TEXT,\n reason TEXT,\n session_id TEXT,\n created_at INTEGER NOT NULL,\n machine_id TEXT NOT NULL DEFAULT 'local',\n synced_at INTEGER\n )`;\n\nconst DIGEST_EXTRACTS_TABLE = `\n CREATE TABLE IF NOT EXISTS digest_extracts (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n agent_id TEXT NOT NULL REFERENCES agents(id),\n tier INTEGER NOT NULL,\n content TEXT NOT NULL,\n substrate_hash TEXT,\n generated_at INTEGER NOT NULL,\n machine_id TEXT NOT NULL DEFAULT 'local',\n synced_at INTEGER,\n UNIQUE (agent_id, tier)\n )`;\n\n// -- Agent State Layer ------------------------------------------------------\n\nconst AGENT_RUNS_TABLE = `\n CREATE TABLE IF NOT EXISTS agent_runs (\n id TEXT PRIMARY KEY,\n agent_id TEXT NOT NULL REFERENCES agents(id),\n task TEXT,\n instruction TEXT,\n status TEXT DEFAULT 'pending',\n started_at INTEGER,\n completed_at INTEGER,\n tokens_used INTEGER,\n cost_usd REAL,\n actions_taken TEXT,\n error TEXT\n )`;\n\nconst AGENT_REPORTS_TABLE = `\n CREATE TABLE IF NOT EXISTS agent_reports (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n run_id TEXT NOT NULL REFERENCES agent_runs(id),\n agent_id TEXT NOT NULL REFERENCES agents(id),\n action TEXT NOT NULL,\n summary TEXT NOT NULL,\n details TEXT,\n created_at INTEGER NOT NULL\n )`;\n\nconst AGENT_TURNS_TABLE = `\n CREATE TABLE IF NOT EXISTS agent_turns (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n run_id TEXT NOT NULL REFERENCES agent_runs(id),\n agent_id TEXT NOT NULL REFERENCES agents(id),\n turn_number INTEGER NOT NULL,\n tool_name TEXT NOT NULL,\n tool_input TEXT,\n tool_output_summary TEXT,\n started_at INTEGER,\n completed_at INTEGER\n )`;\n\nconst AGENT_TASKS_TABLE = `\n CREATE TABLE IF NOT EXISTS agent_tasks (\n id TEXT PRIMARY KEY,\n agent_id TEXT NOT NULL REFERENCES agents(id),\n source TEXT NOT NULL DEFAULT 'built-in',\n display_name TEXT,\n description TEXT,\n prompt TEXT NOT NULL,\n is_default INTEGER DEFAULT 0,\n tool_overrides TEXT,\n model TEXT,\n config TEXT,\n created_at INTEGER NOT NULL,\n updated_at INTEGER\n )`;\n\nconst AGENT_STATE_TABLE = `\n CREATE TABLE IF NOT EXISTS agent_state (\n agent_id TEXT NOT NULL REFERENCES agents(id),\n key TEXT NOT NULL,\n value TEXT NOT NULL,\n updated_at INTEGER NOT NULL,\n PRIMARY KEY (agent_id, key)\n )`;\n\n// -- Sync Layer -------------------------------------------------------------\n\nexport const TEAM_OUTBOX_TABLE = `\n CREATE TABLE IF NOT EXISTS team_outbox (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n table_name TEXT NOT NULL,\n row_id TEXT NOT NULL,\n operation TEXT NOT NULL DEFAULT 'upsert',\n payload TEXT NOT NULL,\n machine_id TEXT NOT NULL,\n created_at INTEGER NOT NULL,\n sent_at INTEGER,\n retry_count INTEGER NOT NULL DEFAULT 0,\n last_attempt_at INTEGER\n )`;\n\n// -- Logging Layer ----------------------------------------------------------\n\nexport const LOG_ENTRIES_TABLE = `\n CREATE TABLE IF NOT EXISTS log_entries (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n timestamp TEXT NOT NULL,\n level TEXT NOT NULL,\n component TEXT NOT NULL,\n kind TEXT NOT NULL,\n message TEXT NOT NULL,\n data TEXT,\n session_id TEXT\n )`;\n\n// -- Skills Layer -----------------------------------------------------------\n\nexport const SKILL_CANDIDATES_TABLE = `\n CREATE TABLE IF NOT EXISTS skill_candidates (\n id TEXT PRIMARY KEY,\n agent_id TEXT NOT NULL REFERENCES agents(id),\n machine_id TEXT NOT NULL DEFAULT 'local',\n topic TEXT NOT NULL,\n rationale TEXT NOT NULL,\n confidence REAL NOT NULL DEFAULT 0.0,\n status TEXT NOT NULL DEFAULT 'identified',\n source_ids TEXT NOT NULL DEFAULT '[]',\n skill_id TEXT,\n supersedes TEXT,\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL,\n approved_at INTEGER,\n synced_at INTEGER\n )`;\n\nexport const SKILL_RECORDS_TABLE = `\n CREATE TABLE IF NOT EXISTS skill_records (\n id TEXT PRIMARY KEY,\n agent_id TEXT NOT NULL REFERENCES agents(id),\n machine_id TEXT NOT NULL DEFAULT 'local',\n name TEXT NOT NULL UNIQUE,\n display_name TEXT NOT NULL,\n description TEXT NOT NULL,\n status TEXT NOT NULL DEFAULT 'active',\n embedded INTEGER DEFAULT 0,\n generation INTEGER NOT NULL DEFAULT 1,\n candidate_id TEXT REFERENCES skill_candidates(id),\n source_ids TEXT NOT NULL DEFAULT '[]',\n path TEXT NOT NULL,\n usage_count INTEGER NOT NULL DEFAULT 0,\n last_used_at INTEGER,\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL,\n properties TEXT NOT NULL DEFAULT '{}',\n synced_at INTEGER\n )`;\n\nexport const SKILL_LINEAGE_TABLE = `\n CREATE TABLE IF NOT EXISTS skill_lineage (\n id TEXT PRIMARY KEY,\n skill_id TEXT NOT NULL REFERENCES skill_records(id),\n generation INTEGER NOT NULL,\n action TEXT NOT NULL,\n rationale TEXT NOT NULL,\n source_ids_added TEXT NOT NULL DEFAULT '[]',\n content_snapshot TEXT NOT NULL,\n created_at INTEGER NOT NULL\n )`;\n\nexport const SKILL_USAGE_TABLE = `\n CREATE TABLE IF NOT EXISTS skill_usage (\n id TEXT PRIMARY KEY,\n skill_id TEXT NOT NULL REFERENCES skill_records(id),\n session_id TEXT NOT NULL REFERENCES sessions(id),\n machine_id TEXT NOT NULL DEFAULT 'local',\n detected_at INTEGER NOT NULL\n )`;\n\n// -- Notifications Layer ----------------------------------------------------\n\nexport const NOTIFICATIONS_TABLE = `\n CREATE TABLE IF NOT EXISTS notifications (\n id TEXT PRIMARY KEY,\n domain TEXT NOT NULL,\n type TEXT NOT NULL,\n level TEXT NOT NULL DEFAULT 'info',\n title TEXT NOT NULL,\n message TEXT,\n mode TEXT NOT NULL DEFAULT 'banner',\n status TEXT NOT NULL DEFAULT 'unread',\n link TEXT,\n metadata TEXT,\n created_at INTEGER NOT NULL\n )`;\n\n// -- FTS5 Virtual Tables ----------------------------------------------------\n\nexport const FTS_TABLES = [\n `CREATE VIRTUAL TABLE IF NOT EXISTS prompt_batches_fts\n USING fts5(user_prompt, response_summary, content='prompt_batches', content_rowid='id')`,\n\n `CREATE VIRTUAL TABLE IF NOT EXISTS activities_fts\n USING fts5(tool_name, tool_input, file_path, content='activities', content_rowid='id')`,\n\n `CREATE VIRTUAL TABLE IF NOT EXISTS log_entries_fts\n USING fts5(message, content='log_entries', content_rowid='id')`,\n\n `CREATE VIRTUAL TABLE IF NOT EXISTS spores_fts\n USING fts5(content, content='spores', content_rowid='rowid')`,\n\n `CREATE VIRTUAL TABLE IF NOT EXISTS sessions_fts\n USING fts5(title, summary, content='sessions', content_rowid='rowid')`,\n\n // FTS5 sync triggers for log_entries (external-content table)\n `CREATE TRIGGER IF NOT EXISTS log_entries_ai AFTER INSERT ON log_entries BEGIN\n INSERT INTO log_entries_fts(rowid, message) VALUES (new.id, new.message);\n END`,\n\n `CREATE TRIGGER IF NOT EXISTS log_entries_ad AFTER DELETE ON log_entries BEGIN\n INSERT INTO log_entries_fts(log_entries_fts, rowid, message) VALUES('delete', old.id, old.message);\n END`,\n\n // FTS5 sync triggers for prompt_batches\n `CREATE TRIGGER IF NOT EXISTS prompt_batches_fts_ai AFTER INSERT ON prompt_batches BEGIN\n INSERT INTO prompt_batches_fts(rowid, user_prompt, response_summary) VALUES (new.id, new.user_prompt, new.response_summary);\n END`,\n\n `CREATE TRIGGER IF NOT EXISTS prompt_batches_fts_au AFTER UPDATE OF user_prompt, response_summary ON prompt_batches BEGIN\n INSERT INTO prompt_batches_fts(prompt_batches_fts, rowid, user_prompt, response_summary) VALUES('delete', old.id, old.user_prompt, old.response_summary);\n INSERT INTO prompt_batches_fts(rowid, user_prompt, response_summary) VALUES (new.id, new.user_prompt, new.response_summary);\n END`,\n\n `CREATE TRIGGER IF NOT EXISTS prompt_batches_fts_ad AFTER DELETE ON prompt_batches BEGIN\n INSERT INTO prompt_batches_fts(prompt_batches_fts, rowid, user_prompt, response_summary) VALUES('delete', old.id, old.user_prompt, old.response_summary);\n END`,\n\n // FTS5 sync triggers for spores\n `CREATE TRIGGER IF NOT EXISTS spores_fts_ai AFTER INSERT ON spores BEGIN\n INSERT INTO spores_fts(rowid, content) VALUES (new.rowid, new.content);\n END`,\n\n `CREATE TRIGGER IF NOT EXISTS spores_fts_au AFTER UPDATE OF content ON spores BEGIN\n INSERT INTO spores_fts(spores_fts, rowid, content) VALUES('delete', old.rowid, old.content);\n INSERT INTO spores_fts(rowid, content) VALUES (new.rowid, new.content);\n END`,\n\n `CREATE TRIGGER IF NOT EXISTS spores_fts_ad AFTER DELETE ON spores BEGIN\n INSERT INTO spores_fts(spores_fts, rowid, content) VALUES('delete', old.rowid, old.content);\n END`,\n\n // FTS5 sync triggers for sessions\n `CREATE TRIGGER IF NOT EXISTS sessions_fts_ai AFTER INSERT ON sessions BEGIN\n INSERT INTO sessions_fts(rowid, title, summary) VALUES (new.rowid, new.title, new.summary);\n END`,\n\n `CREATE TRIGGER IF NOT EXISTS sessions_fts_au AFTER UPDATE OF title, summary ON sessions BEGIN\n INSERT INTO sessions_fts(sessions_fts, rowid, title, summary) VALUES('delete', old.rowid, old.title, old.summary);\n INSERT INTO sessions_fts(rowid, title, summary) VALUES (new.rowid, new.title, new.summary);\n END`,\n\n `CREATE TRIGGER IF NOT EXISTS sessions_fts_ad AFTER DELETE ON sessions BEGIN\n INSERT INTO sessions_fts(sessions_fts, rowid, title, summary) VALUES('delete', old.rowid, old.title, old.summary);\n END`,\n];\n\n// -- Indexes ----------------------------------------------------------------\n\nexport const SECONDARY_INDEXES = [\n // Sessions\n 'CREATE INDEX IF NOT EXISTS idx_sessions_status ON sessions (status)',\n 'CREATE INDEX IF NOT EXISTS idx_sessions_processed ON sessions (processed)',\n 'CREATE INDEX IF NOT EXISTS idx_sessions_started_at ON sessions (started_at)',\n 'CREATE INDEX IF NOT EXISTS idx_sessions_agent ON sessions (agent)',\n 'CREATE INDEX IF NOT EXISTS idx_sessions_created_at ON sessions (created_at)',\n\n // Prompt batches\n 'CREATE INDEX IF NOT EXISTS idx_prompt_batches_session_id ON prompt_batches (session_id)',\n 'CREATE INDEX IF NOT EXISTS idx_prompt_batches_processed ON prompt_batches (processed)',\n 'CREATE INDEX IF NOT EXISTS idx_prompt_batches_status ON prompt_batches (status)',\n\n // Activities\n 'CREATE INDEX IF NOT EXISTS idx_activities_session_id ON activities (session_id)',\n 'CREATE INDEX IF NOT EXISTS idx_activities_prompt_batch_id ON activities (prompt_batch_id)',\n 'CREATE INDEX IF NOT EXISTS idx_activities_tool_name ON activities (tool_name)',\n 'CREATE INDEX IF NOT EXISTS idx_activities_timestamp ON activities (timestamp)',\n 'CREATE INDEX IF NOT EXISTS idx_activities_processed ON activities (processed)',\n\n // Spores\n 'CREATE INDEX IF NOT EXISTS idx_spores_agent_id ON spores (agent_id)',\n 'CREATE INDEX IF NOT EXISTS idx_spores_session_id ON spores (session_id)',\n 'CREATE INDEX IF NOT EXISTS idx_spores_status ON spores (status)',\n 'CREATE INDEX IF NOT EXISTS idx_spores_observation_type ON spores (observation_type)',\n 'CREATE INDEX IF NOT EXISTS idx_spores_created_at ON spores (created_at)',\n\n // Entities\n 'CREATE INDEX IF NOT EXISTS idx_entities_agent_id ON entities (agent_id)',\n 'CREATE INDEX IF NOT EXISTS idx_entities_type ON entities (type)',\n\n // Graph edges\n 'CREATE INDEX IF NOT EXISTS idx_graph_edges_source ON graph_edges (source_id, source_type)',\n 'CREATE INDEX IF NOT EXISTS idx_graph_edges_target ON graph_edges (target_id, target_type)',\n 'CREATE INDEX IF NOT EXISTS idx_graph_edges_type ON graph_edges (type)',\n 'CREATE INDEX IF NOT EXISTS idx_graph_edges_agent ON graph_edges (agent_id)',\n 'CREATE INDEX IF NOT EXISTS idx_graph_edges_source_type ON graph_edges (source_id, type)',\n\n // Entity mentions\n 'CREATE INDEX IF NOT EXISTS idx_entity_mentions_entity_id ON entity_mentions (entity_id)',\n 'CREATE INDEX IF NOT EXISTS idx_entity_mentions_agent_id ON entity_mentions (agent_id)',\n\n // Resolution events\n 'CREATE INDEX IF NOT EXISTS idx_resolution_events_spore_id ON resolution_events (spore_id)',\n 'CREATE INDEX IF NOT EXISTS idx_resolution_events_agent_id ON resolution_events (agent_id)',\n\n // Digest extracts\n 'CREATE INDEX IF NOT EXISTS idx_digest_extracts_agent_id ON digest_extracts (agent_id)',\n\n // Agent runs\n 'CREATE INDEX IF NOT EXISTS idx_agent_runs_agent_id ON agent_runs (agent_id)',\n 'CREATE INDEX IF NOT EXISTS idx_agent_runs_status ON agent_runs (status)',\n 'CREATE INDEX IF NOT EXISTS idx_agent_runs_agent_status ON agent_runs (agent_id, status)',\n 'CREATE INDEX IF NOT EXISTS idx_agent_runs_task_completed ON agent_runs (task, status, completed_at)',\n\n // Agent reports\n 'CREATE INDEX IF NOT EXISTS idx_agent_reports_run_id ON agent_reports (run_id)',\n\n // Agent turns\n 'CREATE INDEX IF NOT EXISTS idx_agent_turns_run_id ON agent_turns (run_id)',\n\n // Agent tasks\n 'CREATE INDEX IF NOT EXISTS idx_agent_tasks_agent_id ON agent_tasks (agent_id)',\n\n // Plans\n 'CREATE INDEX IF NOT EXISTS idx_plans_session_id ON plans (session_id)',\n 'CREATE INDEX IF NOT EXISTS idx_plans_source_path ON plans (source_path)',\n 'CREATE INDEX IF NOT EXISTS idx_plans_content_hash ON plans (content_hash)',\n // Attachments\n 'CREATE INDEX IF NOT EXISTS idx_attachments_file_path ON attachments (file_path)',\n\n // Team outbox\n 'CREATE INDEX IF NOT EXISTS idx_team_outbox_pending ON team_outbox (sent_at, created_at)',\n 'CREATE INDEX IF NOT EXISTS idx_team_outbox_table_name ON team_outbox (table_name)',\n 'CREATE INDEX IF NOT EXISTS idx_team_outbox_row_lookup ON team_outbox (table_name, row_id)',\n\n // Machine ID (synced tables)\n 'CREATE INDEX IF NOT EXISTS idx_sessions_machine_id ON sessions (machine_id)',\n 'CREATE INDEX IF NOT EXISTS idx_spores_machine_id ON spores (machine_id)',\n 'CREATE INDEX IF NOT EXISTS idx_graph_edges_machine_id ON graph_edges (machine_id)',\n\n // Skill candidates\n 'CREATE INDEX IF NOT EXISTS idx_skill_candidates_agent_id ON skill_candidates (agent_id)',\n 'CREATE INDEX IF NOT EXISTS idx_skill_candidates_status ON skill_candidates (status)',\n 'CREATE INDEX IF NOT EXISTS idx_skill_candidates_machine_id ON skill_candidates (machine_id)',\n 'CREATE INDEX IF NOT EXISTS idx_skill_candidates_agent_status ON skill_candidates (agent_id, status)',\n\n // Skill records\n 'CREATE INDEX IF NOT EXISTS idx_skill_records_agent_id ON skill_records (agent_id)',\n 'CREATE INDEX IF NOT EXISTS idx_skill_records_status ON skill_records (status)',\n 'CREATE INDEX IF NOT EXISTS idx_skill_records_name ON skill_records (name)',\n 'CREATE INDEX IF NOT EXISTS idx_skill_records_machine_id ON skill_records (machine_id)',\n 'CREATE INDEX IF NOT EXISTS idx_skill_records_agent_status ON skill_records (agent_id, status)',\n\n // Skill lineage\n 'CREATE INDEX IF NOT EXISTS idx_skill_lineage_skill_id ON skill_lineage (skill_id)',\n\n // Skill usage\n 'CREATE INDEX IF NOT EXISTS idx_skill_usage_skill_id ON skill_usage (skill_id)',\n 'CREATE INDEX IF NOT EXISTS idx_skill_usage_session_id ON skill_usage (session_id)',\n 'CREATE INDEX IF NOT EXISTS idx_skill_usage_skill_session ON skill_usage (skill_id, session_id)',\n\n // Log entries\n 'CREATE INDEX IF NOT EXISTS idx_log_entries_timestamp ON log_entries (timestamp)',\n 'CREATE INDEX IF NOT EXISTS idx_log_entries_level ON log_entries (level)',\n 'CREATE INDEX IF NOT EXISTS idx_log_entries_component ON log_entries (component)',\n 'CREATE INDEX IF NOT EXISTS idx_log_entries_kind ON log_entries (kind)',\n 'CREATE INDEX IF NOT EXISTS idx_log_entries_session_id ON log_entries (session_id)',\n\n // Notifications\n 'CREATE INDEX IF NOT EXISTS idx_notifications_status ON notifications (status)',\n 'CREATE INDEX IF NOT EXISTS idx_notifications_domain ON notifications (domain)',\n 'CREATE INDEX IF NOT EXISTS idx_notifications_created_at ON notifications (created_at)',\n 'CREATE INDEX IF NOT EXISTS idx_notifications_status_created ON notifications (status, created_at)',\n];\n\n// -- Ordered table creation -------------------------------------------------\n\nexport const TABLE_DDLS = [\n SCHEMA_VERSION_TABLE,\n // Capture layer (order matters for FK references)\n SESSIONS_TABLE,\n PROMPT_BATCHES_TABLE,\n ACTIVITIES_TABLE,\n PLANS_TABLE,\n ARTIFACTS_TABLE,\n TEAM_MEMBERS_TABLE,\n ATTACHMENTS_TABLE,\n // Intelligence layer\n AGENTS_TABLE,\n SPORES_TABLE,\n ENTITIES_TABLE,\n GRAPH_EDGES_TABLE,\n ENTITY_MENTIONS_TABLE,\n RESOLUTION_EVENTS_TABLE,\n DIGEST_EXTRACTS_TABLE,\n // Agent state layer\n AGENT_RUNS_TABLE,\n AGENT_REPORTS_TABLE,\n AGENT_TURNS_TABLE,\n AGENT_TASKS_TABLE,\n AGENT_STATE_TABLE,\n // Skills layer\n SKILL_CANDIDATES_TABLE,\n SKILL_RECORDS_TABLE,\n SKILL_LINEAGE_TABLE,\n SKILL_USAGE_TABLE,\n // Sync layer\n TEAM_OUTBOX_TABLE,\n // Logging layer\n LOG_ENTRIES_TABLE,\n // Notifications layer\n NOTIFICATIONS_TABLE,\n];\n","/**\n * Canonical string values for skill_candidates.status.\n *\n * The skill lifecycle uses four states:\n * - identified: discovered by skill-survey, awaiting human review\n * - approved: human approved; queued for skill-generate\n * - generated: vault_finalize_skill promoted the staged skill to live\n * - dismissed: retired (human or agent)\n *\n * See docs/superpowers/plans/2026-04-08-skill-lifecycle-audit-and-staging.md\n * for the lifecycle transitions and who is allowed to make each one.\n */\nexport const CANDIDATE_STATUS = {\n IDENTIFIED: 'identified',\n APPROVED: 'approved',\n GENERATED: 'generated',\n DISMISSED: 'dismissed',\n} as const;\n\nexport type SkillCandidateStatus = (typeof CANDIDATE_STATUS)[keyof typeof CANDIDATE_STATUS];\n\n/**\n * Statuses the agent-facing vault_skill_candidates tool is allowed to set\n * on an update. Human-only transitions (approved) and internal-only\n * transitions (generated, via vault_finalize_skill) are excluded.\n */\nexport const AGENT_SETTABLE_STATUSES: readonly SkillCandidateStatus[] = [\n CANDIDATE_STATUS.IDENTIFIED,\n CANDIDATE_STATUS.DISMISSED,\n];\n\n/**\n * Statuses REST callers (UI + MCP myco_skill_candidates) are allowed to\n * set. 'generated' is internal — only vault_finalize_skill sets it, and\n * that path calls updateCandidate directly rather than going through REST.\n */\nexport const REST_SETTABLE_STATUSES: readonly SkillCandidateStatus[] = [\n CANDIDATE_STATUS.IDENTIFIED,\n CANDIDATE_STATUS.APPROVED,\n CANDIDATE_STATUS.DISMISSED,\n];\n\n/**\n * Composite UI filter value that the REST handler translates into a\n * multi-status query (`status IN ('approved', 'generated')`). Kept here\n * so the UI and backend share a single source of truth for the wire\n * encoding.\n */\nexport const PIPELINE_FILTER_VALUE = `${CANDIDATE_STATUS.APPROVED},${CANDIDATE_STATUS.GENERATED}`;\n","/**\n * Schema migrations for the Myco vault database.\n *\n * Each migration is a function that upgrades the database from version N-1 to N.\n * The MIGRATIONS registry provides a declarative list that createSchema() can\n * iterate over instead of hand-coding version checks.\n */\n\nimport type { Database } from 'better-sqlite3';\nimport { epochSeconds, DEFAULT_MACHINE_ID } from '@myco/constants.js';\nimport { CANDIDATE_STATUS } from '@myco/constants/skill-candidate-status.js';\nimport {\n LOG_ENTRIES_TABLE,\n TEAM_OUTBOX_TABLE,\n SKILL_CANDIDATES_TABLE,\n SKILL_RECORDS_TABLE,\n SKILL_LINEAGE_TABLE,\n SKILL_USAGE_TABLE,\n NOTIFICATIONS_TABLE,\n} from './schema-ddl.js';\n\n// ---------------------------------------------------------------------------\n// Migration interface + registry\n// ---------------------------------------------------------------------------\n\nexport interface Migration {\n version: number;\n migrate: (db: Database, machineId: string) => void;\n}\n\nexport const MIGRATIONS: Migration[] = [\n { version: 2, migrate: (db) => migrateV1ToV2(db) },\n { version: 3, migrate: (db) => migrateV2ToV3(db) },\n { version: 4, migrate: migrateV3ToV4 },\n { version: 5, migrate: (db) => migrateV4ToV5(db) },\n { version: 6, migrate: (db) => migrateV5ToV6(db) },\n { version: 7, migrate: migrateV6ToV7 },\n { version: 8, migrate: (db) => migrateV7ToV8(db) },\n { version: 9, migrate: (db) => migrateV8ToV9(db) },\n { version: 10, migrate: (db) => migrateV9ToV10(db) },\n { version: 11, migrate: (db) => migrateV10ToV11(db) },\n { version: 12, migrate: (db) => migrateV11ToV12(db) },\n];\n\n// ---------------------------------------------------------------------------\n// Individual migration functions\n// ---------------------------------------------------------------------------\n\n/**\n * Migrate a version-1 database to version-2.\n *\n * Version 2 adds:\n * - plans.session_id, plans.prompt_batch_id, plans.content_hash\n * - attachments.data, attachments.content_hash\n * - indexes: idx_plans_session_id, idx_plans_source_path, idx_plans_content_hash\n *\n * Each ALTER TABLE is wrapped in try/catch so re-running is safe -- SQLite\n * throws \"duplicate column name\" if the column already exists, which we ignore.\n */\nfunction migrateV1ToV2(db: Database): void {\n db.exec('BEGIN');\n try {\n const alterStatements = [\n 'ALTER TABLE plans ADD COLUMN session_id TEXT REFERENCES sessions(id)',\n 'ALTER TABLE plans ADD COLUMN prompt_batch_id INTEGER REFERENCES prompt_batches(id)',\n 'ALTER TABLE plans ADD COLUMN content_hash TEXT',\n 'ALTER TABLE attachments ADD COLUMN data BLOB',\n 'ALTER TABLE attachments ADD COLUMN content_hash TEXT',\n ];\n\n for (const stmt of alterStatements) {\n try {\n db.exec(stmt);\n } catch {\n // Column already exists -- safe to ignore on re-run\n }\n }\n\n // Indexes use IF NOT EXISTS so they are idempotent\n const newIndexes = [\n 'CREATE INDEX IF NOT EXISTS idx_plans_session_id ON plans (session_id)',\n 'CREATE INDEX IF NOT EXISTS idx_plans_source_path ON plans (source_path)',\n 'CREATE INDEX IF NOT EXISTS idx_plans_content_hash ON plans (content_hash)',\n 'CREATE INDEX IF NOT EXISTS idx_attachments_file_path ON attachments (file_path)',\n ];\n\n for (const idx of newIndexes) {\n db.exec(idx);\n }\n\n db.prepare(\n `INSERT INTO schema_version (version, applied_at)\n VALUES (?, ?)\n ON CONFLICT (version) DO NOTHING`\n ).run(2, epochSeconds());\n\n db.exec('COMMIT');\n } catch (err) {\n db.exec('ROLLBACK');\n throw err;\n }\n}\n\n/**\n * Migrate a version-2 database to version-3.\n *\n * Version 3 adds:\n * - log_entries table\n * - log_entries_fts virtual table (FTS5)\n * - indexes: idx_log_entries_timestamp, _level, _component, _kind, _session_id\n *\n * Uses `CREATE ... IF NOT EXISTS` throughout for idempotency.\n */\nfunction migrateV2ToV3(db: Database): void {\n db.exec('BEGIN');\n try {\n db.exec(LOG_ENTRIES_TABLE);\n\n db.exec(\n `CREATE VIRTUAL TABLE IF NOT EXISTS log_entries_fts\n USING fts5(message, content='log_entries', content_rowid='id')`\n );\n\n // FTS5 sync triggers for log_entries\n db.exec(\n `CREATE TRIGGER IF NOT EXISTS log_entries_ai AFTER INSERT ON log_entries BEGIN\n INSERT INTO log_entries_fts(rowid, message) VALUES (new.id, new.message);\n END`\n );\n db.exec(\n `CREATE TRIGGER IF NOT EXISTS log_entries_ad AFTER DELETE ON log_entries BEGIN\n INSERT INTO log_entries_fts(log_entries_fts, rowid, message) VALUES('delete', old.id, old.message);\n END`\n );\n\n const newIndexes = [\n 'CREATE INDEX IF NOT EXISTS idx_log_entries_timestamp ON log_entries (timestamp)',\n 'CREATE INDEX IF NOT EXISTS idx_log_entries_level ON log_entries (level)',\n 'CREATE INDEX IF NOT EXISTS idx_log_entries_component ON log_entries (component)',\n 'CREATE INDEX IF NOT EXISTS idx_log_entries_kind ON log_entries (kind)',\n 'CREATE INDEX IF NOT EXISTS idx_log_entries_session_id ON log_entries (session_id)',\n ];\n\n for (const idx of newIndexes) {\n db.exec(idx);\n }\n\n db.prepare(\n `INSERT INTO schema_version (version, applied_at)\n VALUES (?, ?)\n ON CONFLICT (version) DO NOTHING`\n ).run(3, epochSeconds());\n\n db.exec('COMMIT');\n } catch (err) {\n db.exec('ROLLBACK');\n throw err;\n }\n}\n\n/**\n * Migrate a version-3 database to version-4.\n *\n * Version 4 adds multi-machine support:\n * - machine_id TEXT NOT NULL DEFAULT 'local' on all synced tables\n * - synced_at INTEGER on all synced tables\n * - team_outbox table + indexes\n * - machine_id indexes on high-traffic tables\n *\n * Backfills existing rows with the provided machineId.\n */\nfunction migrateV3ToV4(db: Database, machineId: string): void {\n db.exec('BEGIN');\n try {\n // Tables that need machine_id + synced_at columns\n const syncedTables = [\n 'sessions',\n 'prompt_batches',\n 'spores',\n 'entities',\n 'graph_edges',\n 'entity_mentions',\n 'resolution_events',\n 'plans',\n 'artifacts',\n 'digest_extracts',\n 'team_members',\n ];\n\n for (const table of syncedTables) {\n try {\n db.exec(`ALTER TABLE ${table} ADD COLUMN machine_id TEXT NOT NULL DEFAULT 'local'`);\n } catch {\n // Column already exists -- safe to ignore on re-run\n }\n try {\n db.exec(`ALTER TABLE ${table} ADD COLUMN synced_at INTEGER`);\n } catch {\n // Column already exists -- safe to ignore on re-run\n }\n }\n\n // Backfill machine_id on existing rows\n for (const table of syncedTables) {\n db.prepare(`UPDATE ${table} SET machine_id = ? WHERE machine_id = 'local'`).run(machineId);\n }\n\n // Create team_outbox table\n db.exec(TEAM_OUTBOX_TABLE);\n\n // Create new indexes (IF NOT EXISTS for idempotency)\n const newIndexes = [\n 'CREATE INDEX IF NOT EXISTS idx_team_outbox_pending ON team_outbox (sent_at, created_at)',\n 'CREATE INDEX IF NOT EXISTS idx_team_outbox_table_name ON team_outbox (table_name)',\n 'CREATE INDEX IF NOT EXISTS idx_team_outbox_row_lookup ON team_outbox (table_name, row_id)',\n 'CREATE INDEX IF NOT EXISTS idx_sessions_machine_id ON sessions (machine_id)',\n 'CREATE INDEX IF NOT EXISTS idx_spores_machine_id ON spores (machine_id)',\n 'CREATE INDEX IF NOT EXISTS idx_graph_edges_machine_id ON graph_edges (machine_id)',\n ];\n\n for (const idx of newIndexes) {\n db.exec(idx);\n }\n\n db.prepare(\n `INSERT INTO schema_version (version, applied_at)\n VALUES (?, ?)\n ON CONFLICT (version) DO NOTHING`\n ).run(4, epochSeconds());\n\n db.exec('COMMIT');\n } catch (err) {\n db.exec('ROLLBACK');\n throw err;\n }\n}\n\n/**\n * Migrate a version-4 database to version-5.\n *\n * Version 5 adds the Skills layer:\n * - skill_candidates table\n * - skill_records table\n * - skill_lineage table\n * - skill_usage table\n * - indexes for all new tables\n *\n * Uses `CREATE TABLE IF NOT EXISTS` throughout for idempotency.\n */\nfunction migrateV4ToV5(db: Database): void {\n db.exec('BEGIN');\n try {\n db.exec(SKILL_CANDIDATES_TABLE);\n db.exec(SKILL_RECORDS_TABLE);\n db.exec(SKILL_LINEAGE_TABLE);\n db.exec(SKILL_USAGE_TABLE);\n\n const newIndexes = [\n 'CREATE INDEX IF NOT EXISTS idx_skill_candidates_agent_id ON skill_candidates (agent_id)',\n 'CREATE INDEX IF NOT EXISTS idx_skill_candidates_status ON skill_candidates (status)',\n 'CREATE INDEX IF NOT EXISTS idx_skill_candidates_machine_id ON skill_candidates (machine_id)',\n 'CREATE INDEX IF NOT EXISTS idx_skill_candidates_agent_status ON skill_candidates (agent_id, status)',\n 'CREATE INDEX IF NOT EXISTS idx_skill_records_agent_id ON skill_records (agent_id)',\n 'CREATE INDEX IF NOT EXISTS idx_skill_records_status ON skill_records (status)',\n 'CREATE INDEX IF NOT EXISTS idx_skill_records_name ON skill_records (name)',\n 'CREATE INDEX IF NOT EXISTS idx_skill_records_machine_id ON skill_records (machine_id)',\n 'CREATE INDEX IF NOT EXISTS idx_skill_records_agent_status ON skill_records (agent_id, status)',\n 'CREATE INDEX IF NOT EXISTS idx_skill_lineage_skill_id ON skill_lineage (skill_id)',\n 'CREATE INDEX IF NOT EXISTS idx_skill_usage_skill_id ON skill_usage (skill_id)',\n 'CREATE INDEX IF NOT EXISTS idx_skill_usage_session_id ON skill_usage (session_id)',\n 'CREATE INDEX IF NOT EXISTS idx_skill_usage_skill_session ON skill_usage (skill_id, session_id)',\n 'CREATE INDEX IF NOT EXISTS idx_agent_runs_task_completed ON agent_runs (task, status, completed_at)',\n ];\n\n for (const idx of newIndexes) {\n db.exec(idx);\n }\n\n db.prepare(\n `INSERT INTO schema_version (version, applied_at)\n VALUES (?, ?)\n ON CONFLICT (version) DO NOTHING`\n ).run(5, epochSeconds());\n\n db.exec('COMMIT');\n } catch (err) {\n db.exec('ROLLBACK');\n throw err;\n }\n}\n\n/**\n * Migrate a version-5 database to version-6.\n *\n * Version 6 expands FTS5 coverage:\n * - prompt_batches_fts gains response_summary column (drop + recreate)\n * - spores_fts new virtual table (content column, hidden rowid)\n * - sessions_fts new virtual table (title + summary, hidden rowid)\n * - sync triggers for all three tables (insert / update / delete)\n * - backfills FTS from existing data\n *\n * Uses `IF NOT EXISTS` throughout for idempotency where possible.\n * The prompt_batches_fts table must be dropped first since its column\n * definition changed.\n */\nfunction migrateV5ToV6(db: Database): void {\n db.exec('BEGIN');\n try {\n // Drop old prompt_batches_fts (column definition changed)\n db.exec('DROP TABLE IF EXISTS prompt_batches_fts');\n\n // Recreate with response_summary added\n db.exec(\n `CREATE VIRTUAL TABLE IF NOT EXISTS prompt_batches_fts\n USING fts5(user_prompt, response_summary, content='prompt_batches', content_rowid='id')`,\n );\n\n // New FTS tables\n db.exec(\n `CREATE VIRTUAL TABLE IF NOT EXISTS spores_fts\n USING fts5(content, content='spores', content_rowid='rowid')`,\n );\n db.exec(\n `CREATE VIRTUAL TABLE IF NOT EXISTS sessions_fts\n USING fts5(title, summary, content='sessions', content_rowid='rowid')`,\n );\n\n // Triggers for prompt_batches\n db.exec(\n `CREATE TRIGGER IF NOT EXISTS prompt_batches_fts_ai AFTER INSERT ON prompt_batches BEGIN\n INSERT INTO prompt_batches_fts(rowid, user_prompt, response_summary) VALUES (new.id, new.user_prompt, new.response_summary);\n END`,\n );\n db.exec(\n `CREATE TRIGGER IF NOT EXISTS prompt_batches_fts_au AFTER UPDATE OF user_prompt, response_summary ON prompt_batches BEGIN\n INSERT INTO prompt_batches_fts(prompt_batches_fts, rowid, user_prompt, response_summary) VALUES('delete', old.id, old.user_prompt, old.response_summary);\n INSERT INTO prompt_batches_fts(rowid, user_prompt, response_summary) VALUES (new.id, new.user_prompt, new.response_summary);\n END`,\n );\n db.exec(\n `CREATE TRIGGER IF NOT EXISTS prompt_batches_fts_ad AFTER DELETE ON prompt_batches BEGIN\n INSERT INTO prompt_batches_fts(prompt_batches_fts, rowid, user_prompt, response_summary) VALUES('delete', old.id, old.user_prompt, old.response_summary);\n END`,\n );\n\n // Triggers for spores\n db.exec(\n `CREATE TRIGGER IF NOT EXISTS spores_fts_ai AFTER INSERT ON spores BEGIN\n INSERT INTO spores_fts(rowid, content) VALUES (new.rowid, new.content);\n END`,\n );\n db.exec(\n `CREATE TRIGGER IF NOT EXISTS spores_fts_au AFTER UPDATE OF content ON spores BEGIN\n INSERT INTO spores_fts(spores_fts, rowid, content) VALUES('delete', old.rowid, old.content);\n INSERT INTO spores_fts(rowid, content) VALUES (new.rowid, new.content);\n END`,\n );\n db.exec(\n `CREATE TRIGGER IF NOT EXISTS spores_fts_ad AFTER DELETE ON spores BEGIN\n INSERT INTO spores_fts(spores_fts, rowid, content) VALUES('delete', old.rowid, old.content);\n END`,\n );\n\n // Triggers for sessions\n db.exec(\n `CREATE TRIGGER IF NOT EXISTS sessions_fts_ai AFTER INSERT ON sessions BEGIN\n INSERT INTO sessions_fts(rowid, title, summary) VALUES (new.rowid, new.title, new.summary);\n END`,\n );\n db.exec(\n `CREATE TRIGGER IF NOT EXISTS sessions_fts_au AFTER UPDATE OF title, summary ON sessions BEGIN\n INSERT INTO sessions_fts(sessions_fts, rowid, title, summary) VALUES('delete', old.rowid, old.title, old.summary);\n INSERT INTO sessions_fts(rowid, title, summary) VALUES (new.rowid, new.title, new.summary);\n END`,\n );\n db.exec(\n `CREATE TRIGGER IF NOT EXISTS sessions_fts_ad AFTER DELETE ON sessions BEGIN\n INSERT INTO sessions_fts(sessions_fts, rowid, title, summary) VALUES('delete', old.rowid, old.title, old.summary);\n END`,\n );\n\n // Backfill FTS from existing data\n db.exec(\n `INSERT INTO prompt_batches_fts(rowid, user_prompt, response_summary)\n SELECT rowid, user_prompt, response_summary FROM prompt_batches`,\n );\n db.exec(\n `INSERT INTO spores_fts(rowid, content)\n SELECT rowid, content FROM spores`,\n );\n db.exec(\n `INSERT INTO sessions_fts(rowid, title, summary)\n SELECT rowid, title, summary FROM sessions`,\n );\n\n db.prepare(\n `INSERT INTO schema_version (version, applied_at)\n VALUES (?, ?)\n ON CONFLICT (version) DO NOTHING`,\n ).run(6, epochSeconds());\n\n db.exec('COMMIT');\n } catch (err) {\n db.exec('ROLLBACK');\n throw err;\n }\n}\n\n/**\n * Migrate v6 -> v7: fix stale 'local' machine_id on ALL synced tables.\n *\n * The agent vault tools historically used DEFAULT_MACHINE_ID ('local')\n * instead of the resolved machine identity. This one-time data migration\n * fixes all affected records and re-queues them for team sync.\n */\nfunction migrateV6ToV7(db: Database, machineId: string): void {\n if (machineId === 'local' || machineId === DEFAULT_MACHINE_ID) return; // Nothing to fix\n\n db.exec('BEGIN');\n try {\n // entity_mentions excluded -- no `id` column (composite key: entity_id, note_id, note_type)\n const tables = [\n 'sessions', 'prompt_batches', 'spores', 'entities', 'graph_edges',\n 'resolution_events', 'plans', 'artifacts',\n 'digest_extracts', 'skill_candidates', 'skill_records',\n ];\n\n for (const table of tables) {\n try {\n // Find rows that need fixing BEFORE updating\n const staleRows = db.prepare(\n `SELECT id FROM ${table} WHERE machine_id = 'local'`,\n ).all() as Array<{ id: string }>;\n\n if (staleRows.length === 0) continue;\n\n // Fix machine_id and clear synced_at\n db.prepare(\n `UPDATE ${table} SET machine_id = ?, synced_at = NULL WHERE machine_id = 'local'`,\n ).run(machineId);\n\n // Clear stale outbox entries for affected rows only\n for (const row of staleRows) {\n db.prepare(\n `DELETE FROM team_outbox WHERE table_name = ? AND row_id = ?`,\n ).run(table, String(row.id));\n }\n\n // Re-enqueue only the fixed rows with full payload\n const enqueueStmt = db.prepare(\n `INSERT INTO team_outbox (table_name, row_id, operation, payload, machine_id, created_at)\n VALUES (?, ?, 'upsert', ?, ?, ?)`,\n );\n const now = epochSeconds();\n for (const stale of staleRows) {\n const fresh = db.prepare(`SELECT * FROM ${table} WHERE id = ?`).get(stale.id) as Record<string, unknown>;\n if (fresh) {\n enqueueStmt.run(table, String(stale.id), JSON.stringify(fresh), machineId, now);\n }\n }\n } catch (tableErr) {\n // Skip if table doesn't exist; re-throw for other errors (I/O, constraint)\n const msg = tableErr instanceof Error ? tableErr.message : String(tableErr);\n if (!msg.includes('no such table')) throw tableErr;\n }\n }\n\n db.prepare(\n `INSERT INTO schema_version (version, applied_at)\n VALUES (?, ?)\n ON CONFLICT (version) DO NOTHING`,\n ).run(7, epochSeconds());\n\n db.exec('COMMIT');\n } catch (err) {\n db.exec('ROLLBACK');\n throw err;\n }\n}\n\n/**\n * Migrate v7 -> v8: add notifications table.\n *\n * Uses `CREATE TABLE IF NOT EXISTS` for idempotency.\n */\nfunction migrateV7ToV8(db: Database): void {\n db.exec('BEGIN');\n try {\n db.exec(NOTIFICATIONS_TABLE);\n\n const newIndexes = [\n 'CREATE INDEX IF NOT EXISTS idx_notifications_status ON notifications (status)',\n 'CREATE INDEX IF NOT EXISTS idx_notifications_domain ON notifications (domain)',\n 'CREATE INDEX IF NOT EXISTS idx_notifications_created_at ON notifications (created_at)',\n 'CREATE INDEX IF NOT EXISTS idx_notifications_status_created ON notifications (status, created_at)',\n ];\n\n for (const idx of newIndexes) {\n db.exec(idx);\n }\n\n db.prepare(\n `INSERT INTO schema_version (version, applied_at)\n VALUES (?, ?)\n ON CONFLICT (version) DO NOTHING`,\n ).run(8, epochSeconds());\n\n db.exec('COMMIT');\n } catch (err) {\n db.exec('ROLLBACK');\n throw err;\n }\n}\n\n/**\n * Version 9 adds retry tracking to the team outbox:\n * - retry_count INTEGER NOT NULL DEFAULT 0\n * - last_attempt_at INTEGER\n *\n * Records exceeding the max retry count are dead-lettered (excluded from\n * pending queries) so they don't block the sync flush or deep sleep.\n */\nfunction migrateV8ToV9(db: Database): void {\n db.exec('BEGIN');\n try {\n db.exec('ALTER TABLE team_outbox ADD COLUMN retry_count INTEGER NOT NULL DEFAULT 0');\n db.exec('ALTER TABLE team_outbox ADD COLUMN last_attempt_at INTEGER');\n\n db.prepare(\n `INSERT INTO schema_version (version, applied_at)\n VALUES (?, ?)\n ON CONFLICT (version) DO NOTHING`,\n ).run(9, epochSeconds());\n\n db.exec('COMMIT');\n } catch (err) {\n db.exec('ROLLBACK');\n throw err;\n }\n}\n\n/**\n * Version 10 adds an audit trail for skill candidate approvals.\n *\n * - skill_candidates.approved_at INTEGER (nullable) — timestamp of the\n * first transition into status='approved'. Auto-managed by\n * updateCandidate going forward.\n *\n * Backfill: rows currently in status 'approved' or 'generated' get\n * approved_at set to the migration timestamp. This is a one-time,\n * deliberately-imprecise assumption — the true approval time is lost\n * for existing rows, so we record \"as of the migration, these were\n * considered approved\" rather than inventing timestamps.\n *\n * Rows in 'identified' or 'dismissed' state keep approved_at = NULL.\n *\n * Idempotent: the ALTER is wrapped in try/catch so re-runs tolerate the\n * existing column; the backfill uses `WHERE approved_at IS NULL` so it\n * never overwrites a previously-recorded timestamp.\n */\nfunction migrateV9ToV10(db: Database): void {\n db.exec('BEGIN');\n try {\n try {\n db.exec('ALTER TABLE skill_candidates ADD COLUMN approved_at INTEGER');\n } catch {\n // Column already exists -- safe to ignore on re-run\n }\n\n const now = epochSeconds();\n db.prepare(\n `UPDATE skill_candidates\n SET approved_at = ?\n WHERE approved_at IS NULL\n AND status IN (?, ?)`,\n ).run(now, CANDIDATE_STATUS.APPROVED, CANDIDATE_STATUS.GENERATED);\n\n db.prepare(\n `INSERT INTO schema_version (version, applied_at)\n VALUES (?, ?)\n ON CONFLICT (version) DO NOTHING`,\n ).run(10, epochSeconds());\n\n db.exec('COMMIT');\n } catch (err) {\n db.exec('ROLLBACK');\n throw err;\n }\n}\n\n/**\n * Version 11 adds supersedes tracking to skill candidates.\n *\n * - skill_candidates.supersedes TEXT (nullable) — JSON array of skill\n * record names that this candidate would replace. Used by the skill\n * survey task to create domain-level candidates that explicitly\n * subsume existing narrow skills.\n *\n * Idempotent: the ALTER is wrapped in try/catch so re-runs tolerate the\n * existing column.\n */\nfunction migrateV10ToV11(db: Database): void {\n db.exec('BEGIN');\n try {\n try {\n db.exec('ALTER TABLE skill_candidates ADD COLUMN supersedes TEXT');\n } catch {\n // Column already exists -- safe to ignore on re-run\n }\n\n db.prepare(\n `INSERT INTO schema_version (version, applied_at)\n VALUES (?, ?)\n ON CONFLICT (version) DO NOTHING`,\n ).run(11, epochSeconds());\n\n db.exec('COMMIT');\n } catch (err) {\n db.exec('ROLLBACK');\n throw err;\n }\n}\n\n/**\n * Version 12 adds embedding support for skill records.\n *\n * - skill_records.embedded INTEGER DEFAULT 0 — flag for the embedding\n * pipeline to know which rows still need vectors.\n *\n * Idempotent: the ALTER is wrapped in try/catch so re-runs tolerate the\n * existing column.\n */\nfunction migrateV11ToV12(db: Database): void {\n db.exec('BEGIN');\n try {\n try {\n db.exec('ALTER TABLE skill_records ADD COLUMN embedded INTEGER DEFAULT 0');\n } catch {\n // Column already exists\n }\n\n db.prepare(\n `INSERT INTO schema_version (version, applied_at)\n VALUES (?, ?)\n ON CONFLICT (version) DO NOTHING`,\n ).run(12, epochSeconds());\n\n db.exec('COMMIT');\n } catch (err) {\n db.exec('ROLLBACK');\n throw err;\n }\n}\n","/**\n * SQLite database schema -- all capture, intelligence, and agent state tables.\n *\n * Uses `CREATE TABLE IF NOT EXISTS` and `CREATE INDEX IF NOT EXISTS` throughout\n * for idempotency. Running `createSchema()` multiple times is always safe.\n *\n * Timestamp convention: all timestamps are INTEGER (Unix epoch seconds).\n * Content hashing: all `content_hash` columns are TEXT with UNIQUE constraint.\n * Embedding dimensions: 1024 (bge-m3 default) -- used by external sqlite-vec store.\n *\n * Vector columns live in a separate sqlite-vec virtual table, not inline.\n * Tables that participate in vector search carry an `embedded INTEGER DEFAULT 0`\n * flag so the embedder knows which rows still need vectors.\n */\n\nimport type { Database } from 'better-sqlite3';\nimport { epochSeconds, DEFAULT_MACHINE_ID } from '@myco/constants.js';\nimport { TABLE_DDLS, FTS_TABLES, SECONDARY_INDEXES } from './schema-ddl.js';\nimport { MIGRATIONS } from './migrations.js';\n\n/** Current schema version -- fresh start for the SQLite era. */\nexport const SCHEMA_VERSION = 12;\n\n// Re-export for backwards compat (other modules import from schema.ts)\nexport { DEFAULT_MACHINE_ID };\n\n/** Embedding vector dimensions (bge-m3 default). */\nexport const EMBEDDING_DIMENSIONS = 1024;\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction getCurrentVersion(db: Database): number {\n const row = db.prepare(\n 'SELECT version FROM schema_version ORDER BY version DESC LIMIT 1'\n ).get() as { version: number } | undefined;\n return row?.version ?? 0;\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Create all database tables, indexes, and record the schema version.\n *\n * Fully idempotent -- safe to call on every startup. Uses `IF NOT EXISTS`\n * for all DDL and `ON CONFLICT DO NOTHING` for the version row.\n *\n * @param db -- better-sqlite3 Database instance.\n * @param machineId -- machine identifier for backfilling existing rows during\n * v3->v4 and v6->v7 migrations. Defaults to `'local'` (tests, init).\n */\nexport function createSchema(db: Database, machineId: string = DEFAULT_MACHINE_ID): void {\n // Fast-path: skip if already at current version\n try {\n const currentVersion = getCurrentVersion(db);\n if (currentVersion === SCHEMA_VERSION) return;\n\n // Run pending migrations in order\n for (const migration of MIGRATIONS) {\n const version = getCurrentVersion(db);\n if (version < migration.version) {\n migration.migrate(db, machineId);\n }\n }\n return;\n } catch {\n // Table doesn't exist yet -- first run\n }\n\n // Fresh install: create all tables, FTS, indexes\n for (const ddl of TABLE_DDLS) { db.exec(ddl); }\n for (const ddl of FTS_TABLES) { db.exec(ddl); }\n for (const idx of SECONDARY_INDEXES) { db.exec(idx); }\n\n db.prepare(\n `INSERT INTO schema_version (version, applied_at)\n VALUES (?, ?)\n ON CONFLICT (version) DO NOTHING`\n ).run(SCHEMA_VERSION, epochSeconds());\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/symbionts/manifest-schema.ts","../src/symbionts/detect.ts"],"sourcesContent":["import { z } from 'zod';\n\n/**\n * Declarative capture rules owned per-symbiont in its YAML manifest.\n *\n * Rules let each symbiont describe how Myco should filter or rewrite\n * captured events *without* adding symbiont-specific branches inside\n * the generic hook handlers. The hook loads the rules, a generic\n * evaluator decides the action, and the hook acts on the result.\n *\n * Condition types (in `when`):\n * - `transcript_path_missing`: structural. Fires when the hook's\n * transcript_path field is absent/empty. A legitimate user-facing\n * session records a transcript; an ephemeral sub-invocation (e.g.,\n * an agent's internal title-generation call) does not. Preferred\n * over text matching because it doesn't drift as UIs evolve.\n * - `transcript_meta_field_equals`: structural. Fires when a dot-path\n * field in the transcript's first JSON line equals a specific scalar\n * value (for example, `source = \"exec\"`). Use for source-kind or mode\n * filters that are stable in transcript metadata but not represented\n * by field presence alone.\n * - `prompt_starts_with` / `prompt_contains`: text fallback. Use\n * when no structural signal is available. Document the upgrade path\n * in the YAML so future maintainers can replace it when a better\n * signal appears.\n *\n * Scope semantics:\n * - `this_agent` (default): rule fires only when the detected agent\n * matches the manifest that owns the rule. Use for behavior that\n * is specific to the symbiont and can rely on detection working.\n * - `any_agent`: rule fires regardless of detected agent. Use for\n * patterns where detection itself might fail — e.g., an internal\n * sub-invocation that omits the fields agent detection keys on.\n *\n * Events:\n * - `session_start`: fires on SessionStart, before any prompts or\n * tools are captured. The same rules are also reusable at later\n * lifecycle boundaries that could still materialize a session row\n * (for example, transcript-backed stop processing after a missed\n * SessionStart). This is the durable \"session capture rules\"\n * contract for every symbiont.\n * - `user_prompt`: fires on UserPromptSubmit. Safety net for anything\n * that slips past session_start, and the only layer where\n * `rewrite_prompt` makes sense (prompt text doesn't exist until\n * the prompt is submitted).\n *\n * Actions:\n * - `drop`: discard the event entirely. For session_start, the hook\n * skips registering the session row. For user_prompt, the hook\n * skips posting the event and cascade-deletes any session row that\n * may have been registered before the drop rule could fire.\n * - `rewrite_prompt`: replace the captured prompt with the substring\n * after `extract_after`. Only valid for `user_prompt` events.\n */\nconst CaptureRuleSchema = z.object({\n event: z.enum(['session_start', 'user_prompt']),\n scope: z.enum(['this_agent', 'any_agent']).default('this_agent'),\n when: z.object({\n prompt_starts_with: z.string().optional(),\n prompt_contains: z.string().optional(),\n /** Structural: fires when transcript_path is absent or empty. */\n transcript_path_missing: z.boolean().optional(),\n /**\n * Structural: fires when a dot-path field exists (and is truthy) in the\n * transcript's first JSON line (session_meta). Use for detecting sub-agent\n * sessions that have real transcript files but are not user-initiated.\n *\n * Example: `source.subagent` matches a Codex thread-spawn session whose\n * session_meta has `\"source\": {\"subagent\": {...}}` but would NOT match\n * a user session with `\"source\": \"vscode\"`.\n *\n * The hook handler reads the transcript and passes the parsed meta to\n * the evaluator — the evaluator itself does no file I/O.\n */\n transcript_meta_field_exists: z.string().optional(),\n /**\n * Structural: fires when a dot-path field in session_meta exactly equals\n * a scalar value (string / number / boolean / null). Use for stable\n * source-kind markers like `source = \"exec\"`.\n */\n transcript_meta_field_equals: z.object({\n path: z.string(),\n value: z.union([z.string(), z.number(), z.boolean(), z.null()]),\n }).optional(),\n }),\n action: z.enum(['drop', 'rewrite_prompt']),\n /** Short audit string logged when the rule matches (e.g., \"codex-internal-title-gen\"). */\n reason: z.string().optional(),\n /** For rewrite_prompt: keep only the substring after this marker (first occurrence). */\n extract_after: z.string().optional(),\n /** For rewrite_prompt: trim whitespace from the extracted substring. Default true. */\n trim: z.boolean().default(true),\n});\n\nexport type CaptureRule = z.infer<typeof CaptureRuleSchema>;\n\nconst CaptureManifestSchema = z.object({\n planDirs: z.array(z.string()).default([]),\n planTags: z.array(z.string()).default([]),\n rules: z.array(CaptureRuleSchema).default([]),\n});\n\nconst RegistrationSchema = z.object({\n hooksTarget: z.string().optional(),\n /**\n * Format of the hooks target.\n * - 'json' (default): hooks template is merged into a JSON settings file.\n * - 'plugin-file': the hooks template is a verbatim file (e.g., an opencode TS plugin)\n * copied to hooksTarget without JSON parsing. Used for agents with plugin-based hook\n * systems rather than JSON hook entries.\n */\n hooksFormat: z.enum(['json', 'plugin-file']).default('json'),\n /**\n * Optional file path for a plugin deps package.json. When set, the installer writes\n * a package.json declaring the plugin SDK dependency so the agent's package manager\n * (e.g., opencode's Bun) can install it at startup. Preserved on uninstall so\n * contributors can keep their own deps.\n */\n pluginPackageTarget: z.string().optional(),\n mcpTarget: z.string().optional(),\n mcpFormat: z.enum(['json', 'toml']).default('json'),\n /**\n * Optional working directory injected into the Myco MCP server entry. Values\n * may use `{projectRoot}` and `{vaultDir}` placeholders, or remain relative\n * (for example `.` in a project-local config file) when the host agent\n * resolves paths against the config file location.\n */\n mcpCwd: z.string().optional(),\n /**\n * Optional env vars injected into the Myco MCP server entry. Values may use\n * `{projectRoot}` and `{vaultDir}` placeholders so symbionts whose MCP child\n * starts outside the repo can still anchor Myco to the correct project.\n */\n mcpEnv: z.record(z.string(), z.string()).default({}),\n /**\n * JSON key under which MCP server entries are stored in the MCP config file.\n * Defaults to 'mcpServers' (used by Claude Code, Cursor, etc.). opencode uses 'mcp'.\n */\n mcpServersKey: z.string().default('mcpServers'),\n skillsTarget: z.string().optional(),\n settingsTarget: z.string().optional(),\n /** Format of the settings file. TOML-format agents (e.g., Codex) emit top-level template keys as TOML sections. */\n settingsFormat: z.enum(['json', 'toml']).default('json'),\n /** Instruction file that stubs out to AGENTS.md. Only for agents that don't read AGENTS.md natively. */\n instructionsFile: z.string().optional(),\n});\n\nexport const SymbiontManifestSchema = z.object({\n name: z.string(),\n displayName: z.string(),\n binary: z.string(),\n configDir: z.string(),\n pluginRootEnvVar: z.string(),\n settingsPath: z.string().optional(),\n hookFields: z.object({\n sessionId: z.string(),\n transcriptPath: z.string(),\n lastResponse: z.string(),\n prompt: z.string().default('prompt'),\n toolName: z.string().default('tool_name'),\n toolInput: z.string().default('tool_input'),\n toolOutput: z.string().default('tool_output'),\n /** Env var fallback for session ID (e.g., GEMINI_SESSION_ID). */\n sessionIdEnv: z.string().optional(),\n }),\n /** Resume command template with {sessionId} placeholder. Omit for IDE-based agents. */\n resumeCommand: z.string().optional(),\n capture: CaptureManifestSchema.optional(),\n registration: RegistrationSchema.optional(),\n});\n\nexport type SymbiontManifest = z.infer<typeof SymbiontManifestSchema>;\nexport type SymbiontRegistration = z.infer<typeof RegistrationSchema>;\n","import { SymbiontManifestSchema, type SymbiontManifest } from './manifest-schema.js';\nimport { findPackageRoot } from '../utils/find-package-root.js';\nimport { execFileSync } from 'node:child_process';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport YAML from 'yaml';\n\nexport interface DetectedSymbiont {\n manifest: SymbiontManifest;\n binaryFound: boolean;\n configDirFound: boolean;\n}\n\nconst MANIFESTS_SUBDIR = 'symbionts/manifests';\n\n/** Cached manifests — static files that never change at runtime. */\nlet manifestCache: SymbiontManifest[] | null = null;\n\n/** Load all symbiont manifests from the package's dist directory. */\nexport function loadManifests(): SymbiontManifest[] {\n if (manifestCache) return manifestCache;\n const candidates = [\n // Source layout: src/symbionts/detect.ts → src/symbionts/manifests/\n path.resolve(import.meta.dirname, MANIFESTS_SUBDIR),\n // Dist layout: dist/src/symbionts/ → dist/src/symbionts/manifests/\n // (or dist/src/daemon/ → dist/src/symbionts/manifests/)\n path.resolve(import.meta.dirname, '..', MANIFESTS_SUBDIR),\n path.resolve(import.meta.dirname, '..', '..', MANIFESTS_SUBDIR),\n // Chunk layout: dist/chunk-*.js → dist/src/symbionts/manifests/\n path.resolve(import.meta.dirname, 'src', MANIFESTS_SUBDIR),\n ];\n\n for (const dir of candidates) {\n if (!fs.existsSync(dir)) continue;\n const files = fs.readdirSync(dir).filter(f => f.endsWith('.yaml'));\n if (files.length === 0) continue;\n manifestCache = files.map(f => {\n const raw = YAML.parse(fs.readFileSync(path.join(dir, f), 'utf-8'));\n return SymbiontManifestSchema.parse(raw);\n });\n return manifestCache;\n }\n return [];\n}\n\n/** Check if a binary is available on PATH. */\nfunction isBinaryOnPath(binary: string): boolean {\n try {\n execFileSync('which', [binary], { stdio: 'pipe' });\n return true;\n } catch {\n return false;\n }\n}\n\n/** Detect which symbionts are available for a project. */\nexport function detectSymbionts(projectRoot: string): DetectedSymbiont[] {\n const manifests = loadManifests();\n return manifests.map(manifest => ({\n manifest,\n binaryFound: isBinaryOnPath(manifest.binary),\n configDirFound: fs.existsSync(path.join(projectRoot, manifest.configDir)),\n })).filter(d => d.binaryFound || d.configDirFound);\n}\n\n/** Find the Myco package root (where package.json lives). */\nexport function resolvePackageRoot(): string {\n return findPackageRoot(import.meta.dirname) ?? process.cwd();\n}\n"],"mappings":"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","names":["YAML"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/config/loader.ts","../src/config/appearance-values.ts","../src/config/schema.ts","../src/config/migrations.ts","../src/utils/deep-merge.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport YAML from 'yaml';\nimport { MycoConfigSchema, type MycoConfig, type BackupConfig, type TeamConfig } from './schema.js';\nimport { runMigrations, CURRENT_MIGRATION_VERSION } from './migrations.js';\nimport { deepMerge } from '../utils/deep-merge.js';\n\nexport const CONFIG_FILENAME = 'myco.yaml';\nexport const LOCAL_CONFIG_FILENAME = 'local.yaml';\n\nfunction localConfigPath(vaultDir: string): string {\n // vaultDir already points at `.myco/` (see resolveVaultDir), so local.yaml\n // sits alongside myco.yaml in the vault — no extra `.myco/` prefix.\n return path.join(vaultDir, LOCAL_CONFIG_FILENAME);\n}\n\n/** Config overlay uses replace semantics: arrays in source overwrite arrays in target. */\nexport function deepMergeConfig<T extends Record<string, unknown>>(target: T, source: Partial<T>): T {\n return deepMerge(target, source, { arrayStrategy: 'replace' });\n}\n\nexport function loadConfig(vaultDir: string): MycoConfig {\n const configPath = path.join(vaultDir, CONFIG_FILENAME);\n\n if (!fs.existsSync(configPath)) {\n throw new Error(`myco.yaml not found in ${vaultDir}`);\n }\n\n const raw = fs.readFileSync(configPath, 'utf-8');\n const parsed = YAML.parse(raw) as Record<string, unknown>;\n\n // Detect v1 config and guide migration\n if (parsed.version === 1 || (parsed.intelligence as Record<string, unknown>)?.backend) {\n throw new Error(\n 'Myco config uses v1 format. Run /myco:setup-llm to reconfigure for v2.',\n );\n }\n\n // --- v2 → v3 migration ---\n let v2Migrated = false;\n if (parsed.version === 2) {\n // Extract intelligence.embedding to top-level embedding\n const intel = parsed.intelligence as Record<string, unknown> | undefined;\n const embeddingConfig = intel?.embedding as Record<string, unknown> | undefined;\n if (embeddingConfig && !parsed.embedding) {\n // Map v2 'lm-studio' to v3 'openai-compatible' for embedding provider\n if (embeddingConfig.provider === 'lm-studio') {\n embeddingConfig.provider = 'openai-compatible';\n }\n parsed.embedding = embeddingConfig;\n }\n\n // Keep daemon.port and daemon.log_level, drop grace_period and max_log_size\n const daemon = parsed.daemon as Record<string, unknown> | undefined;\n if (daemon) {\n const { port, log_level } = daemon;\n parsed.daemon = { port: port ?? null, log_level: log_level ?? 'info' };\n }\n\n // Keep capture basics, drop token-related fields; migrate artifact_watch → plan_dirs\n const capture = parsed.capture as Record<string, unknown> | undefined;\n if (capture) {\n const { transcript_paths, artifact_watch, plan_dirs, artifact_extensions, buffer_max_events } = capture;\n parsed.capture = {\n transcript_paths,\n plan_dirs: plan_dirs ?? artifact_watch,\n artifact_extensions,\n buffer_max_events,\n };\n }\n\n // Drop removed top-level sections\n delete parsed.intelligence;\n delete parsed.context;\n delete parsed.team;\n delete parsed.digest;\n delete parsed.pipeline;\n\n // Set version to 3\n parsed.version = 3;\n v2Migrated = true;\n\n process.stderr.write('[myco migration] Migrated config from v2 to v3\\n');\n }\n\n // Run numbered migrations (for v3+ forward migrations)\n const migrationsRan = runMigrations(parsed, vaultDir, (msg) => {\n process.stderr.write(`[myco migration] ${msg}\\n`);\n });\n\n // Parse with Zod to fill in defaults for new config sections\n const config = MycoConfigSchema.parse(parsed);\n\n // Write back if v2→v3 migration ran, numbered migrations ran, or new defaults were added\n const needsWrite = v2Migrated\n || migrationsRan\n || (parsed.config_version as number ?? 0) < CURRENT_MIGRATION_VERSION\n || parsed.version !== config.version;\n\n if (needsWrite) {\n const fullConfig = JSON.parse(JSON.stringify(config)) as Record<string, unknown>;\n fs.writeFileSync(configPath, YAML.stringify(fullConfig), 'utf-8');\n }\n\n return config;\n}\n\nexport function saveConfig(vaultDir: string, config: MycoConfig): void {\n // Validate before writing — OAK lesson: validate on write, not just read.\n // This is the single parse point for all write paths.\n const validated = MycoConfigSchema.parse(config);\n\n const configPath = path.join(vaultDir, CONFIG_FILENAME);\n fs.mkdirSync(vaultDir, { recursive: true });\n fs.writeFileSync(configPath, YAML.stringify(validated), 'utf-8');\n}\n\nexport function updateConfig(\n vaultDir: string,\n fn: (config: MycoConfig) => MycoConfig,\n): MycoConfig {\n const current = loadConfig(vaultDir);\n const updated = fn(current);\n saveConfig(vaultDir, updated);\n return updated;\n}\n\nexport function updateBackupConfig(\n vaultDir: string,\n backup: Partial<BackupConfig>,\n): MycoConfig {\n return updateConfig(vaultDir, (config) => ({\n ...config,\n backup: { ...config.backup, ...backup },\n }));\n}\n\n/**\n * Extract the set of enabled symbiont names from config.\n * Returns null when the `symbionts` section is absent (pre-existing installs),\n * signalling callers to fall back to their own heuristic.\n */\nexport function getEnabledSymbiontNames(config: MycoConfig): Set<string> | null {\n if (!config.symbionts) return null;\n return new Set(\n Object.entries(config.symbionts)\n .filter(([, entry]) => entry.enabled)\n .map(([name]) => name),\n );\n}\n\nexport function updateTeamConfig(\n vaultDir: string,\n team: Partial<TeamConfig>,\n): MycoConfig {\n return updateConfig(vaultDir, (config) => ({\n ...config,\n team: { ...config.team, ...team },\n }));\n}\n\n/** Return raw local overrides, or `{}` if the file is missing, empty, malformed, or not a mapping. */\nexport function loadLocalConfig(vaultDir: string): Partial<MycoConfig> {\n const filePath = localConfigPath(vaultDir);\n if (!fs.existsSync(filePath)) return {};\n const raw = fs.readFileSync(filePath, 'utf-8').trim();\n if (!raw) return {};\n\n let parsed: unknown;\n try {\n parsed = YAML.parse(raw);\n } catch (err) {\n process.stderr.write(`[myco config] Failed to parse ${filePath}; ignoring local overrides. ${(err as Error).message}\\n`);\n return {};\n }\n\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n process.stderr.write(`[myco config] ${filePath} must contain a YAML mapping; ignoring local overrides.\\n`);\n return {};\n }\n\n return parsed as Partial<MycoConfig>;\n}\n\n/** Load project config and overlay local overrides on top (leaf-level deep merge). */\nexport function loadMergedConfig(vaultDir: string): MycoConfig {\n const project = loadConfig(vaultDir);\n const local = loadLocalConfig(vaultDir);\n const merged = deepMergeConfig(project as Record<string, unknown>, local as Record<string, unknown>);\n return MycoConfigSchema.parse(merged);\n}\n\n/**\n * Write local.yaml only when the serialized contents differ from `current`.\n * Skips the write (and mkdirSync) on a no-op to avoid noisy file mtimes.\n */\nfunction writeLocalYamlIfChanged<T>(vaultDir: string, current: T, next: T): T {\n const existingYaml = YAML.stringify(current);\n const nextYaml = YAML.stringify(next);\n if (existingYaml === nextYaml) return next;\n\n fs.mkdirSync(vaultDir, { recursive: true });\n fs.writeFileSync(localConfigPath(vaultDir), nextYaml, 'utf-8');\n return next;\n}\n\n/** Write a partial to <vaultDir>/local.yaml, deep-merging with existing local content. */\nexport function saveLocalConfig(vaultDir: string, patch: Partial<MycoConfig>): Partial<MycoConfig> {\n const existing = loadLocalConfig(vaultDir);\n const next = deepMergeConfig(existing as Record<string, unknown>, patch as Record<string, unknown>) as Partial<MycoConfig>;\n return writeLocalYamlIfChanged(vaultDir, existing, next);\n}\n\n/**\n * Callback-style update for local config. Replace-semantics: the value\n * returned by `fn` is written verbatim (so callers can remove keys by\n * omitting them). Callers that want merge-semantics should spread\n * `...local` inside their callback, or use `saveLocalConfig` directly.\n */\nexport function updateLocalConfig(\n vaultDir: string,\n fn: (local: Partial<MycoConfig>) => Partial<MycoConfig>,\n): Partial<MycoConfig> {\n const current = loadLocalConfig(vaultDir);\n return writeLocalYamlIfChanged(vaultDir, current, fn(current));\n}\n","/**\n * Appearance enum values shared between the Zod schema (server) and the UI.\n *\n * This file MUST stay free of Node built-ins and other server-only imports\n * so it can be consumed by browser code without pulling in the full schema\n * dependency graph.\n */\n\nexport const APPEARANCE_THEMES = ['sage', 'moss', 'terracotta', 'dusk', 'plum', 'slate'] as const;\nexport const APPEARANCE_MODES = ['light', 'dark', 'system'] as const;\nexport const APPEARANCE_FONTS = ['default', 'geist-mono', 'system', 'sf-mono', 'fira-code', 'jetbrains-mono'] as const;\nexport const APPEARANCE_DENSITIES = ['compact', 'normal', 'comfy'] as const;\n\nexport type AppearanceTheme = typeof APPEARANCE_THEMES[number];\nexport type AppearanceMode = typeof APPEARANCE_MODES[number];\nexport type AppearanceFont = typeof APPEARANCE_FONTS[number];\nexport type AppearanceDensity = typeof APPEARANCE_DENSITIES[number];\n\nexport interface AppearanceValues {\n theme: AppearanceTheme;\n mode: AppearanceMode;\n font: AppearanceFont;\n density: AppearanceDensity;\n}\n","import { z } from 'zod';\nimport { SCHEDULABLE_POWER_STATES } from '@myco/constants.js';\n\nconst EmbeddingProviderSchema = z.object({\n provider: z.enum(['ollama', 'openai-compatible', 'openrouter', 'openai']).default('ollama'),\n model: z.string().default('bge-m3'),\n base_url: z.string().url().optional(),\n});\n\nconst DaemonSchema = z.object({\n port: z.number().int().min(1024).max(65535).nullable().default(null),\n log_level: z.enum(['debug', 'info', 'warn', 'error']).default('info'),\n log_retention_days: z.number().int().min(1).max(365).default(30),\n /**\n * Time without new prompts before an active session is auto-completed (ms).\n * Intelligence tasks (full-intelligence, skill-survey, etc.) only process\n * settled sessions, so this threshold directly controls how fresh their\n * inputs are. Defaults to 1 hour.\n */\n stale_session_threshold_ms: z.number().int().min(60_000).default(60 * 60 * 1000),\n});\n\nconst CaptureSchema = z.object({\n transcript_paths: z.array(z.string()).default([]),\n plan_dirs: z.array(z.string()).default([]),\n ignore_plan_dirs_in_git: z.boolean().default(false),\n artifact_extensions: z.array(z.string()).default(['.md']),\n buffer_max_events: z.number().int().positive().default(500),\n});\n\n/** Provider config shape used in both task-level and phase-level overrides. */\nconst ProviderOverrideSchema = z.object({\n type: z.enum(['anthropic', 'ollama', 'lmstudio']),\n base_url: z.string().optional(),\n model: z.string().optional(),\n /** Context window size for local models (Ollama num_ctx, LM Studio context_length). */\n context_length: z.number().int().positive().optional(),\n});\n\n/** Per-phase overrides within a task — keyed by phase name. */\nconst PhaseOverrideSchema = z.object({\n provider: ProviderOverrideSchema.optional(),\n model: z.string().optional(),\n maxTurns: z.number().int().positive().optional(),\n});\n\n/** Per-task schedule override — partial, merges with YAML defaults. */\nconst ScheduleOverrideSchema = z.object({\n enabled: z.boolean().optional(),\n intervalSeconds: z.number().int().positive().optional(),\n runIn: z.array(z.enum([...SCHEDULABLE_POWER_STATES])).optional(),\n preCondition: z.enum(['has-unprocessed-batches', 'has-active-skills', 'has-approved-candidates', 'has-skill-survey-evidence']).optional(),\n}).optional();\n\n/** Per-task config override — stored in myco.yaml under agent.tasks. */\nconst TaskProviderOverrideSchema = z.object({\n provider: ProviderOverrideSchema.optional(),\n model: z.string().optional(),\n maxTurns: z.number().int().positive().optional(),\n timeoutSeconds: z.number().int().positive().optional(),\n phases: z.record(z.string(), PhaseOverrideSchema).optional(),\n schedule: ScheduleOverrideSchema,\n /** Task-specific params — keys and types vary per task. */\n params: z.record(z.string(), z.union([z.string(), z.number(), z.boolean()])).optional(),\n});\n\nconst ContextSchema = z.object({\n /** Which digest tier to inject at session start. */\n digest_tier: z.number().int().default(5000),\n /** Enable semantic spore search on each user prompt. */\n prompt_search: z.boolean().default(true),\n /** Max spores to inject per prompt (0-10). */\n prompt_max_spores: z.number().int().min(0).max(10).default(3),\n});\n\nconst AgentSchema = z.object({\n /** Number of batches between event-driven summary triggers (0 to disable). */\n summary_batch_interval: z.number().int().min(0).default(5),\n /** Global toggle for PowerManager-scheduled agent tasks. */\n scheduled_tasks_enabled: z.boolean().default(true),\n /** Global toggle for event-driven agent tasks (title-summary). */\n event_tasks_enabled: z.boolean().default(true),\n /** Global default provider — applies to all tasks unless overridden per-task. */\n provider: ProviderOverrideSchema.optional(),\n /** Global default model — applies to all tasks unless overridden per-task. */\n model: z.string().optional(),\n /** Per-task overrides keyed by task name. */\n tasks: z.record(z.string(), TaskProviderOverrideSchema).optional(),\n});\n\nconst BackupSchema = z.object({\n /** Override directory for backup files. Supports ~ for home directory. When unset, defaults to .myco/backups. */\n dir: z.string().optional(),\n});\n\nconst MaintenanceSchema = z.object({\n /** Automatically run PRAGMA optimize on a schedule. */\n auto_optimize: z.boolean().default(true),\n /** How often to run auto-optimize, in hours (1–720). */\n auto_optimize_interval_hours: z.number().int().min(1).max(720).default(24),\n});\n\nconst TeamSchema = z.object({\n /** Whether team sync is enabled. */\n enabled: z.boolean().default(false),\n /** Cloudflare Worker URL for team sync. */\n worker_url: z.string().url().optional(),\n /** Team identifier for sync grouping. */\n team_id: z.string().optional(),\n /** Sync interval in minutes. */\n interval_minutes: z.number().int().min(1).max(1440).default(15),\n});\n\nconst SkillsSchema = z.object({\n /** Auto-generate candidates above this confidence score. */\n confidence_threshold: z.number().min(0).max(1).default(0.7),\n /** Flag unused skills after this many days. */\n usage_stale_days: z.number().int().positive().default(30),\n});\n\nconst NotificationsSchema = z.object({\n /** Master switch — disables all notifications when false. */\n enabled: z.boolean().default(true),\n /** Allow browser system notifications (Notification API). */\n system_notifications: z.boolean().default(false),\n /** Default display mode for new notification types. */\n default_mode: z.enum(['banner', 'summary']).default('summary'),\n /** Per-domain settings. Keys are domain names from the registry. */\n domains: z.record(z.string(), z.object({\n enabled: z.boolean().default(true),\n /** Override display mode for this domain. Omit to use global default_mode. */\n mode: z.enum(['banner', 'summary']).optional(),\n })).default({}),\n});\n\nconst SymbiontEntrySchema = z.object({\n enabled: z.boolean().default(true),\n});\n\nexport {\n APPEARANCE_THEMES,\n APPEARANCE_MODES,\n APPEARANCE_FONTS,\n APPEARANCE_DENSITIES,\n type AppearanceValues,\n} from './appearance-values.js';\n\nimport {\n APPEARANCE_THEMES,\n APPEARANCE_MODES,\n APPEARANCE_FONTS,\n APPEARANCE_DENSITIES,\n} from './appearance-values.js';\n\nexport const AppearanceConfigSchema = z.object({\n theme: z.enum(APPEARANCE_THEMES).default('sage'),\n mode: z.enum(APPEARANCE_MODES).default('dark'),\n font: z.enum(APPEARANCE_FONTS).default('default'),\n density: z.enum(APPEARANCE_DENSITIES).default('normal'),\n}).default({ theme: 'sage', mode: 'dark', font: 'default', density: 'normal' });\n\nexport type AppearanceConfig = z.infer<typeof AppearanceConfigSchema>;\n\nexport const MycoConfigSchema = z.preprocess(\n (raw: unknown) => {\n if (raw && typeof raw === 'object' && 'curation' in raw && !('agent' in raw)) {\n const { curation, ...rest } = raw as Record<string, unknown>;\n return { ...rest, agent: curation };\n }\n return raw;\n },\n z.object({\n version: z.literal(3),\n config_version: z.number().int().nonnegative().default(0),\n embedding: EmbeddingProviderSchema.default(() => EmbeddingProviderSchema.parse({})),\n daemon: DaemonSchema.default(() => DaemonSchema.parse({})),\n capture: CaptureSchema.default(() => CaptureSchema.parse({})),\n agent: AgentSchema.default(() => AgentSchema.parse({})),\n context: ContextSchema.default(() => ContextSchema.parse({})),\n backup: BackupSchema.default(() => BackupSchema.parse({})),\n maintenance: MaintenanceSchema.default(() => MaintenanceSchema.parse({})),\n team: TeamSchema.default(() => TeamSchema.parse({})),\n skills: SkillsSchema.default(() => SkillsSchema.parse({})),\n notifications: NotificationsSchema.default(() => NotificationsSchema.parse({})),\n appearance: AppearanceConfigSchema,\n symbionts: z.record(z.string(), SymbiontEntrySchema).optional(),\n }),\n);\n\nexport type MycoConfig = z.output<typeof MycoConfigSchema>;\nexport type EmbeddingProviderConfig = z.infer<typeof EmbeddingProviderSchema>;\nexport type TaskProviderOverride = z.infer<typeof TaskProviderOverrideSchema>;\nexport type PhaseOverride = z.infer<typeof PhaseOverrideSchema>;\nexport type ScheduleOverride = z.infer<typeof ScheduleOverrideSchema>;\nexport type ContextConfig = z.infer<typeof ContextSchema>;\nexport type BackupConfig = z.infer<typeof BackupSchema>;\nexport type TeamConfig = z.infer<typeof TeamSchema>;\nexport type SkillsConfig = z.infer<typeof SkillsSchema>;\nexport type NotificationsConfig = z.infer<typeof NotificationsSchema>;\nexport type SymbiontEntry = z.infer<typeof SymbiontEntrySchema>;\n","/**\n * Config and vault migrations — run once per version, tracked by config_version.\n *\n * Each migration has a version number, a name, and a function that receives\n * the raw parsed YAML doc and the vault directory. Migrations run in order\n * and are skipped if config_version is already past them.\n *\n * To add a new migration:\n * 1. Add an entry to MIGRATIONS with the next version number\n * 2. Write the migrate function — it receives the mutable doc and vaultDir\n * 3. The framework handles version tracking and writing the config back\n */\n\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nexport interface Migration {\n version: number;\n name: string;\n migrate: (doc: Record<string, unknown>, vaultDir: string) => void;\n}\n\n/** Regex matching both quoted and unquoted YAML: type: memory, type: \"memory\", type: 'memory' */\nconst MEMORY_TYPE_PATTERN = /type:\\s*[\"']?memory[\"']?/g;\n\nexport const MIGRATIONS: Migration[] = [\n {\n version: 1,\n name: 'rename-memories-to-spores',\n migrate: (doc, vaultDir) => {\n // Config: rename context.layers.memories → context.layers.spores\n const context = doc.context as Record<string, unknown> | undefined;\n const layers = context?.layers as Record<string, unknown> | undefined;\n if (layers && 'memories' in layers && !('spores' in layers)) {\n layers.spores = layers.memories;\n delete layers.memories;\n }\n\n // Vault: rename memories/ directory → spores/\n const memoriesDir = path.join(vaultDir, 'memories');\n const sporesDir = path.join(vaultDir, 'spores');\n\n if (!fs.existsSync(memoriesDir)) return;\n\n if (fs.existsSync(sporesDir)) {\n // Both exist (interrupted migration) — merge remaining files\n const moveRemaining = (srcDir: string, destDir: string): void => {\n for (const entry of fs.readdirSync(srcDir, { withFileTypes: true })) {\n const srcPath = path.join(srcDir, entry.name);\n const destPath = path.join(destDir, entry.name);\n if (entry.isDirectory()) {\n if (!fs.existsSync(destPath)) fs.mkdirSync(destPath, { recursive: true });\n moveRemaining(srcPath, destPath);\n } else if (!fs.existsSync(destPath)) {\n fs.renameSync(srcPath, destPath);\n }\n }\n };\n moveRemaining(memoriesDir, sporesDir);\n fs.rmSync(memoriesDir, { recursive: true, force: true });\n } else {\n fs.renameSync(memoriesDir, sporesDir);\n }\n\n // Update frontmatter type: memory → type: spore (handles quoted and unquoted)\n const walkUpdate = (dir: string): void => {\n for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {\n const fullPath = path.join(dir, entry.name);\n if (entry.isDirectory()) { walkUpdate(fullPath); continue; }\n if (!entry.name.endsWith('.md')) continue;\n const content = fs.readFileSync(fullPath, 'utf-8');\n MEMORY_TYPE_PATTERN.lastIndex = 0;\n if (MEMORY_TYPE_PATTERN.test(content)) {\n MEMORY_TYPE_PATTERN.lastIndex = 0;\n fs.writeFileSync(fullPath, content.replace(MEMORY_TYPE_PATTERN, 'type: spore'));\n }\n }\n };\n walkUpdate(sporesDir);\n\n // Legacy: update wikilink references in Markdown files (pre-SQLite migration): [[memories/...]] → [[spores/...]]\n const walkLinks = (dir: string): void => {\n for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {\n const fullPath = path.join(dir, entry.name);\n if (entry.isDirectory()) { walkLinks(fullPath); continue; }\n if (!entry.name.endsWith('.md')) continue;\n const content = fs.readFileSync(fullPath, 'utf-8');\n if (content.includes('memories/')) {\n fs.writeFileSync(fullPath, content.replace(/memories\\//g, 'spores/'));\n }\n }\n };\n walkLinks(vaultDir);\n },\n },\n {\n version: 2,\n name: 'consolidation-boolean-to-object',\n migrate: (doc) => {\n const digest = doc.digest as Record<string, unknown> | undefined;\n if (!digest) return;\n\n const consolidation = digest.consolidation;\n if (typeof consolidation === 'boolean') {\n digest.consolidation = { enabled: consolidation, max_tokens: 2048 };\n }\n },\n },\n {\n version: 3,\n name: 'schedule-to-task-level',\n migrate(doc: Record<string, unknown>, _vaultDir: string): void {\n const agent = (doc.agent ?? {}) as Record<string, unknown>;\n const skills = (doc.skills ?? {}) as Record<string, unknown>;\n const tasks = ((agent.tasks ?? {}) as Record<string, Record<string, unknown>>);\n\n /** Default interval for full-intelligence task (5 minutes). */\n const DEFAULT_INTELLIGENCE_INTERVAL_SECONDS = 300;\n\n const VALID_SCHEDULE_STATES = ['active', 'idle', 'sleep'] as const;\n\n // Migrate agent.auto_run + interval_seconds → full-intelligence schedule\n if ('auto_run' in agent || 'interval_seconds' in agent) {\n const fiTask = tasks['full-intelligence'] ?? {};\n fiTask.schedule = {\n enabled: agent.auto_run ?? true,\n intervalSeconds: agent.interval_seconds ?? DEFAULT_INTELLIGENCE_INTERVAL_SECONDS,\n };\n tasks['full-intelligence'] = fiTask;\n delete agent.auto_run;\n delete agent.interval_seconds;\n }\n\n // Migrate skills.auto_survey → skill-survey schedule\n if ('auto_survey' in skills) {\n const ssTask = tasks['skill-survey'] ?? {};\n ssTask.schedule = {\n enabled: skills.auto_survey ?? false,\n };\n tasks['skill-survey'] = ssTask;\n delete skills.auto_survey;\n }\n\n // Migrate skills.auto_evolve + evolve_cadence → skill-evolve schedule\n if ('auto_evolve' in skills || 'evolve_cadence' in skills) {\n const seTask = tasks['skill-evolve'] ?? {};\n const schedule: Record<string, unknown> = {\n enabled: skills.auto_evolve ?? false,\n };\n if ('evolve_cadence' in skills) {\n const cadence = String(skills.evolve_cadence);\n schedule.runIn = VALID_SCHEDULE_STATES.includes(cadence as typeof VALID_SCHEDULE_STATES[number])\n ? [cadence]\n : ['idle']; // fallback to safe default\n }\n seTask.schedule = schedule;\n tasks['skill-evolve'] = seTask;\n delete skills.auto_evolve;\n delete skills.evolve_cadence;\n }\n\n // Write back tasks if any were created\n if (Object.keys(tasks).length > 0) {\n agent.tasks = tasks;\n }\n doc.agent = agent;\n doc.skills = skills;\n },\n },\n {\n version: 4,\n name: 'rename-cloud-provider-to-anthropic',\n migrate(doc: Record<string, unknown>, _vaultDir: string): void {\n // Rename `provider.type: cloud` -> `provider.type: anthropic` everywhere\n // it appears in the agent config: global default, per-task, per-phase.\n const renameProvider = (provider: unknown): void => {\n if (\n provider &&\n typeof provider === 'object' &&\n (provider as Record<string, unknown>).type === 'cloud'\n ) {\n (provider as Record<string, unknown>).type = 'anthropic';\n }\n };\n\n const agent = doc.agent as Record<string, unknown> | undefined;\n if (!agent) return;\n\n // Global default provider\n renameProvider(agent.provider);\n\n // Per-task overrides\n const tasks = agent.tasks as Record<string, Record<string, unknown>> | undefined;\n if (tasks) {\n for (const taskConfig of Object.values(tasks)) {\n renameProvider(taskConfig.provider);\n\n // Per-phase overrides within a task\n const phases = taskConfig.phases as Record<string, Record<string, unknown>> | undefined;\n if (phases) {\n for (const phaseConfig of Object.values(phases)) {\n renameProvider(phaseConfig.provider);\n }\n }\n }\n }\n },\n },\n {\n version: 5,\n name: 'seed-settings-notification-domain-default',\n migrate(doc: Record<string, unknown>, _vaultDir: string): void {\n const notifications = (doc.notifications ??= {}) as Record<string, unknown>;\n const domains = (notifications.domains ??= {}) as Record<string, unknown>;\n const settings = (domains.settings ??= {}) as Record<string, unknown>;\n\n if (!('enabled' in settings)) settings.enabled = true;\n if (!('mode' in settings)) settings.mode = 'banner';\n },\n },\n];\n\n/** Current migration version — the highest version in MIGRATIONS. */\nexport const CURRENT_MIGRATION_VERSION = MIGRATIONS[MIGRATIONS.length - 1]?.version ?? 0;\n\n/**\n * Run all pending migrations on the raw config doc.\n * Returns true if any migrations ran (caller should reindex).\n */\nexport function runMigrations(\n doc: Record<string, unknown>,\n vaultDir: string,\n log?: (message: string) => void,\n): boolean {\n const currentVersion = (doc.config_version as number) ?? 0;\n let ran = false;\n\n for (const migration of MIGRATIONS) {\n if (migration.version <= currentVersion) continue;\n\n migration.migrate(doc, vaultDir);\n doc.config_version = migration.version;\n ran = true;\n }\n\n if (ran) {\n const from = currentVersion;\n const to = (doc.config_version as number) ?? 0;\n log?.(`Migrated config from v${from} to v${to}`);\n }\n\n return ran;\n}\n","export interface DeepMergeOptions {\n /** How to reconcile arrays found at the same key in both target and source. */\n arrayStrategy: 'replace' | 'union';\n}\n\nexport function isPlainObject(val: unknown): val is Record<string, unknown> {\n return typeof val === 'object' && val !== null && !Array.isArray(val);\n}\n\n/**\n * Deep-merge source into target at the leaf.\n *\n * - `arrayStrategy: 'replace'` — arrays in source overwrite arrays in target (config overlay semantics).\n * - `arrayStrategy: 'union'` — arrays are concatenated and deduplicated (symbiont settings merge semantics).\n *\n * Non-object, non-array leaves: source overwrites target.\n * `undefined` values in source are skipped (target value preserved).\n */\nexport function deepMerge<T extends Record<string, unknown>>(\n target: T,\n source: Partial<T>,\n options: DeepMergeOptions,\n): T {\n const result: Record<string, unknown> = { ...target };\n for (const [key, value] of Object.entries(source)) {\n if (value === undefined) continue;\n const existing = result[key];\n if (Array.isArray(value) && Array.isArray(existing)) {\n result[key] = options.arrayStrategy === 'union'\n ? [...new Set([...existing, ...value])]\n : value;\n } else if (isPlainObject(value) && isPlainObject(existing)) {\n result[key] = deepMerge(\n existing,\n value,\n options,\n );\n } else {\n result[key] = value;\n }\n }\n return result as T;\n}\n"],"mappings":"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","names":["fs","path","path","fs","YAML"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/symbionts/installer.ts","../src/symbionts/toml-helpers.ts","../src/symbionts/settings-merge.ts","../src/symbionts/json-helpers.ts","../src/symbionts/install-helpers.ts"],"sourcesContent":["import type { SymbiontManifest } from './manifest-schema.js';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { findTomlSectionEnd, buildTomlMcpSection, upsertTomlSection, removeTomlSectionKeys } from './toml-helpers.js';\nimport { deepMergeSettings, deepRemoveSettings } from './settings-merge.js';\nimport { readJsonFile, writeJsonFile, writeOrDeleteJsonFile } from './json-helpers.js';\nimport { ensureAgentsMd, ensureSymlink, isMycoHookGroup } from './install-helpers.js';\nimport { loadMergedConfig } from '../config/loader.js';\n\n/** Current comment header for Myco-managed .gitignore block. */\nconst GITIGNORE_COMMENT = '# Myco managed (machine-specific)';\n\n/** Legacy comment header — recognized for cleanup during reconciliation. */\nconst GITIGNORE_SKILLS_COMMENT_LEGACY = '# Myco skill symlinks (machine-specific)';\n\n/** Wrangler cache directory created by team sync operations. */\nconst WRANGLER_CACHE_DIR = '.wrangler/';\nconst AGENTS_MANAGED_START = '<!-- myco:managed:start -->';\nconst AGENTS_MANAGED_END = '<!-- myco:managed:end -->';\nconst AGENTS_MANAGED_BLOCK = `${AGENTS_MANAGED_START}\n## Myco Managed Guidance\n\n- When \\`capture.ignore_plan_dirs_in_git\\` is enabled, custom directories in \\`capture.plan_dirs\\` may be intentionally gitignored after capture into Myco.\n- Do not force-add files from intentionally gitignored custom plan directories unless the user explicitly asks.\n${AGENTS_MANAGED_END}\n`;\n\n/** Subdirectory within the package where symbiont templates live. */\nconst TEMPLATES_SUBDIR = 'src/symbionts/templates';\n\n/** Filename of the hook guard template in the templates directory. */\nconst HOOK_GUARD_TEMPLATE_FILENAME = 'myco-run.cjs';\n\n/** Filename when installed into the project .agents/ directory. */\nconst HOOK_GUARD_INSTALLED_FILENAME = 'myco-run.cjs';\n\n/** Project-relative path where the hook guard is installed. */\nconst HOOK_GUARD_PROJECT_PATH = `.agents/${HOOK_GUARD_INSTALLED_FILENAME}`;\n\n/**\n * Legacy guard filename we still delete on install to clean up previous\n * installations that used `.agents/myco-hook.cjs` before the rename.\n */\nconst LEGACY_HOOK_GUARD_PATH = '.agents/myco-hook.cjs';\n\n/** Subdirectory within the package where skills live. */\nconst SKILLS_SUBDIR = 'skills';\n\n/** Canonical cross-agent skills directory. */\nconst CANONICAL_SKILLS_DIR = '.agents/skills';\n\n/** MCP server name used by Myco in all symbiont configurations. */\nexport const MYCO_MCP_SERVER_NAME = 'myco';\nconst MCP_ENV_PROJECT_ROOT_TOKEN = '{projectRoot}';\nconst MCP_ENV_VAULT_DIR_TOKEN = '{vaultDir}';\n\ninterface McpLaunchOverrides {\n cwd?: string;\n env: Record<string, string>;\n}\n\n/**\n * Marker substring written into plugin-file hook templates (e.g., opencode's plugin.ts).\n * Uninstall only deletes plugin files whose content contains this marker, so\n * contributors who hand-edit a plugin file without removing the marker are protected.\n */\nconst MYCO_PLUGIN_FILE_MARKER = 'myco:plugin-marker';\n\n/** `hooksFormat` value selecting verbatim plugin-file install over JSON merge. */\nconst HOOKS_FORMAT_PLUGIN_FILE = 'plugin-file';\n\n/** Marker text used to identify unmodified instruction stubs. */\nconst INSTRUCTIONS_STUB_MARKER = 'Edit AGENTS.md, not this file';\n\n/** Start/end markers for the reference block prepended to existing instruction files. */\nconst INSTRUCTIONS_REF_START = '<!-- myco:agents-ref:start -->';\nconst INSTRUCTIONS_REF_END = '<!-- myco:agents-ref:end -->';\n\n/** Reference block prepended to existing instruction files. */\nconst INSTRUCTIONS_REF_BLOCK = `${INSTRUCTIONS_REF_START}\n> **Project intelligence:** This project uses [Myco](https://myco.sh). The canonical project rules are in [\\`AGENTS.md\\`](AGENTS.md) — read and follow it alongside this file.\n${INSTRUCTIONS_REF_END}\n\n`;\n\nexport interface InstallResult {\n hooks: boolean;\n mcp: boolean;\n skills: boolean;\n settings: boolean;\n instructions: boolean;\n /**\n * Plugin deps package.json (e.g., .opencode/package.json). Only present for agents\n * with `registration.pluginPackageTarget` set. False otherwise.\n */\n pluginPackage: boolean;\n}\n\nexport class SymbiontInstaller {\n constructor(\n private manifest: SymbiontManifest,\n private projectRoot: string,\n private packageRoot: string,\n ) {}\n\n /**\n * Read a template file as raw text, checking both source and dist layouts.\n * `relPath` is relative to `TEMPLATES_SUBDIR` — e.g. `'hook-guard.cjs'` for\n * a shared template or `'opencode/plugin.ts'` for a per-agent template.\n */\n private readTemplateFile(relPath: string): string | null {\n const candidates = [\n path.join(this.packageRoot, TEMPLATES_SUBDIR, relPath),\n // tsup preserves the src/ prefix under dist/, so the same subdir works in both layouts\n path.join(this.packageRoot, 'dist', TEMPLATES_SUBDIR, relPath),\n ];\n for (const filePath of candidates) {\n try { return fs.readFileSync(filePath, 'utf-8'); } catch { /* try next */ }\n }\n return null;\n }\n\n /**\n * Write a Myco-managed file with a content-diff gate. Creates parent dirs as\n * needed. Returns `true` if the file was written (new or updated), `false` if\n * the on-disk content already matches and the write was skipped.\n */\n private writeManagedFile(absPath: string, content: string): boolean {\n try {\n if (fs.readFileSync(absPath, 'utf-8') === content) return false;\n } catch { /* doesn't exist — proceed */ }\n fs.mkdirSync(path.dirname(absPath), { recursive: true });\n fs.writeFileSync(absPath, content, 'utf-8');\n return true;\n }\n\n /**\n * Copy the hook guard script into .agents/myco-run.cjs and delete the\n * legacy .agents/myco-hook.cjs if present.\n *\n * The guard is the cross-platform entry point for lifecycle hooks.\n * Hook commands invoke `node .agents/myco-run.cjs …`, and the guard\n * resolves which myco binary to exec via `.myco/runtime.command`.\n * MCP server spawn continues to use the published `myco-run` binary.\n * Returns true if the file was written (or updated); false if skipped\n * or N/A.\n */\n installHookGuard(): boolean {\n const reg = this.manifest.registration;\n if (!reg?.hooksTarget) return false;\n\n const guardTemplate = this.readTemplateFile(HOOK_GUARD_TEMPLATE_FILENAME);\n if (!guardTemplate) return false;\n\n // Sweep legacy guard file on every install — harmless no-op if absent.\n // Prevents the old and new guard files coexisting for projects that\n // were last installed under the `myco-hook.cjs` naming.\n try {\n fs.unlinkSync(path.join(this.projectRoot, LEGACY_HOOK_GUARD_PATH));\n } catch { /* no legacy file present */ }\n\n return this.writeManagedFile(\n path.join(this.projectRoot, HOOK_GUARD_PROJECT_PATH),\n guardTemplate,\n );\n }\n\n /**\n * Remove the hook guard script from .agents/myco-run.cjs.\n * Also deletes the legacy .agents/myco-hook.cjs if present.\n * Returns true if any file was removed; false otherwise.\n */\n uninstallHookGuard(): boolean {\n const reg = this.manifest.registration;\n if (!reg?.hooksTarget) return false;\n\n let removed = false;\n for (const relPath of [HOOK_GUARD_PROJECT_PATH, LEGACY_HOOK_GUARD_PATH]) {\n try {\n fs.unlinkSync(path.join(this.projectRoot, relPath));\n removed = true;\n } catch { /* not present */ }\n }\n return removed;\n }\n\n /** Load a JSON template file for this symbiont. Returns null if not found. */\n loadTemplate(name: string): Record<string, unknown> | null {\n const raw = this.readTemplateFile(path.join(this.manifest.name, `${name}.json`));\n if (raw === null) return null;\n try { return JSON.parse(raw); } catch { return null; }\n }\n\n /**\n * Load a template file verbatim (no JSON parsing).\n * Used for plugin-file hook templates (e.g., opencode's plugin.ts) and any\n * other template that is copied to the project without structural merging.\n */\n loadTemplateRaw(filename: string): string | null {\n return this.readTemplateFile(path.join(this.manifest.name, filename));\n }\n\n /** Run all registration steps. */\n install(): InstallResult {\n const reg = this.manifest.registration;\n this.reconcileAgentsMd();\n // Install hook guard before hooks so the guard script is in place when hooks reference it\n this.installHookGuard();\n // One-time migration: sweep legacy MYCO_CMD / myco-run entries that\n // the pre-runtime.command dispatch pattern wrote into symbiont config\n // files. Idempotent — no-op on clean files. Runs before installSettings\n // so the stale entries don't survive a deep-merge into the new template.\n this.cleanupLegacyMycoCmdEntries();\n const result = this.shouldBatchJsonTargets(reg)\n ? this.installBatchedJson(reg!)\n : {\n hooks: this.installHooks(),\n mcp: this.installMcp(),\n skills: this.installSkills(),\n settings: this.installSettings(),\n instructions: this.installInstructions(),\n pluginPackage: this.installPluginPackage(),\n };\n this.updateGitignore();\n return result;\n }\n\n private reconcileAgentsMd(): void {\n ensureAgentsMd(this.projectRoot, this.packageRoot);\n const agentsPath = path.join(this.projectRoot, 'AGENTS.md');\n let content = '';\n try {\n content = fs.readFileSync(agentsPath, 'utf-8');\n } catch {\n return;\n }\n\n const stripped = this.stripManagedAgentsBlock(content);\n const separator = stripped.length > 0 && !stripped.endsWith('\\n') ? '\\n' : '';\n const spacer = stripped.trimEnd().length > 0 ? '\\n\\n' : '';\n const result = `${stripped}${separator}${spacer}${AGENTS_MANAGED_BLOCK}`;\n if (result === content) return;\n fs.writeFileSync(agentsPath, result, 'utf-8');\n }\n\n private stripManagedAgentsBlock(content: string): string {\n const startIdx = content.indexOf(AGENTS_MANAGED_START);\n if (startIdx === -1) return content.trimEnd();\n const endIdx = content.indexOf(AGENTS_MANAGED_END, startIdx);\n if (endIdx === -1) return content.trimEnd();\n const afterEnd = endIdx + AGENTS_MANAGED_END.length;\n return (content.slice(0, startIdx) + content.slice(afterEnd)).trimEnd();\n }\n\n private getCustomPlanGitignoreEntries(): string[] {\n const config = this.loadProjectConfig();\n if (!config?.capture.ignore_plan_dirs_in_git) return [];\n\n return [...new Set(\n config.capture.plan_dirs\n .map((dir) => this.normalizeProjectRelativeDir(dir))\n .filter((dir): dir is string => dir !== null),\n )];\n }\n\n private loadProjectConfig() {\n try {\n return loadMergedConfig(path.join(this.projectRoot, '.myco'));\n } catch {\n return null;\n }\n }\n\n private normalizeProjectRelativeDir(dir: string): string | null {\n const slashNormalized = dir.trim().replaceAll('\\\\', '/');\n if (!slashNormalized) return null;\n if (slashNormalized.startsWith('~/')) return null;\n if (path.posix.isAbsolute(slashNormalized) || path.win32.isAbsolute(slashNormalized)) return null;\n\n const withoutDotPrefix = slashNormalized.replace(/^\\.\\//, '');\n const normalized = path.posix.normalize(withoutDotPrefix);\n if (normalized === '.' || normalized === '..' || normalized.startsWith('../')) return null;\n return normalized.endsWith('/') ? normalized : `${normalized}/`;\n }\n\n /**\n * Sweep legacy `MYCO_CMD` env-var writes and `myco-run` command-name\n * entries from this symbiont's installed config files.\n *\n * Background: prior to the `.myco/runtime.command` refactor, `make\n * dev-link` injected `MYCO_CMD=myco-dev` into each symbiont's env\n * block (`.claude/settings.json` → `env`, `.cursor/mcp.json` →\n * `mcp.myco.env`, `.codex/config.toml` →\n * `[shell_environment_policy.set]`), and each symbiont's template\n * permission allowlist listed `myco-run` as a callable command. The\n * env-var pattern is now obsolete — `.myco/runtime.command` is the\n * hook-side source of truth — while the old allowlist entries remain\n * legacy noise after the permissions refactor.\n *\n * This cleanup runs automatically on every install/update pass so\n * contributors upgrading across this refactor don't need to manually\n * edit any config file. Idempotent: a second run after cleanup is a\n * no-op. Safe to remove from the install pipeline once every known\n * contributor has updated at least once.\n */\n private cleanupLegacyMycoCmdEntries(): void {\n const reg = this.manifest.registration;\n if (!reg) return;\n\n if (reg.settingsTarget) {\n const settingsPath = path.join(this.projectRoot, reg.settingsTarget);\n const format = reg.settingsFormat ?? 'json';\n if (format === 'toml') {\n this.stripLegacyFromToml(settingsPath);\n } else {\n this.stripLegacyFromJson(settingsPath);\n }\n }\n\n if (reg.mcpTarget && reg.mcpFormat !== 'toml') {\n // MCP server env blocks — cursor writes MYCO_CMD here under\n // `mcp.myco.env` / `mcpServers.myco.env`. TOML MCP targets live\n // inside the same config.toml already handled above.\n this.stripLegacyFromJson(path.join(this.projectRoot, reg.mcpTarget));\n }\n }\n\n /**\n * Walk a JSON settings/MCP file and delete legacy MYCO_CMD + myco-run\n * entries. Writes back only if something changed.\n *\n * Removes:\n * - `MYCO_CMD` key from any object named `env` anywhere in the tree\n * - `myco-run` / `myco-run *` / `myco-run:*` / `Bash(myco-run *)` /\n * `Bash(myco-run:*)` / `ShellTool(myco-run *)` from string arrays\n * - `myco-run` / `myco-run *` keys from object-boolean maps like\n * `chat.tools.terminal.autoApprove`\n */\n private stripLegacyFromJson(filePath: string): void {\n let raw: string;\n try { raw = fs.readFileSync(filePath, 'utf-8'); } catch { return; }\n let data: unknown;\n try { data = JSON.parse(raw); } catch { return; }\n\n let changed = false;\n const LEGACY_STRINGS = new Set([\n 'myco-run',\n 'myco-run *',\n 'myco-run:*',\n 'Bash(myco-run *)',\n 'Bash(myco-run:*)',\n 'ShellTool(myco-run *)',\n ]);\n const LEGACY_OBJECT_KEYS = ['myco-run', 'myco-run *'];\n // Fields whose array values are exec argvs (process invocation arrays),\n // NOT allowlist tokens. The cleanup sweep must not touch these because\n // `myco-run` remains the PUBLISHED MCP launcher command — stripping it\n // from an opencode-style `command: [\"myco-run\", \"mcp\"]` array would\n // corrupt the MCP spawn. Only works today because installMcp() deep-\n // merges the template back in after cleanup; don't rely on that mask.\n const EXEC_ARGV_KEYS = new Set(['command', 'args']);\n\n const walk = (node: unknown, parentKey?: string): void => {\n if (node === null || typeof node !== 'object') return;\n if (Array.isArray(node)) {\n // Exec argv arrays are process invocations (e.g. opencode's\n // `command: [\"myco-run\", \"mcp\"]`). Never strip tokens from these\n // — `myco-run` is the intended MCP command.\n if (parentKey !== undefined && EXEC_ARGV_KEYS.has(parentKey)) return;\n // String arrays: filter out legacy allowlist tokens in place.\n for (let i = node.length - 1; i >= 0; i--) {\n if (typeof node[i] === 'string' && LEGACY_STRINGS.has(node[i] as string)) {\n node.splice(i, 1);\n changed = true;\n } else {\n walk(node[i]);\n }\n }\n return;\n }\n const obj = node as Record<string, unknown>;\n // Env blocks: strip MYCO_CMD specifically. We check by key name so\n // we match any `env` object at any nesting level.\n if (parentKey === 'env' && 'MYCO_CMD' in obj) {\n delete obj.MYCO_CMD;\n changed = true;\n }\n // Object-boolean maps keyed on command name: strip legacy keys.\n // Skip inside exec argv scalar fields (e.g. `command: \"myco-run\"`)\n // would be caught by the LEGACY_OBJECT_KEYS lookup only when the\n // key is literally `myco-run`, which is a command-name key in\n // allowlist objects — exec argv scalars are fine because the\n // scalar value `\"myco-run\"` is walked as a string and skipped.\n for (const key of LEGACY_OBJECT_KEYS) {\n if (key in obj) {\n delete obj[key];\n changed = true;\n }\n }\n for (const [k, v] of Object.entries(obj)) {\n walk(v, k);\n }\n };\n\n walk(data);\n\n if (changed) {\n fs.writeFileSync(filePath, JSON.stringify(data, null, 2) + '\\n', 'utf-8');\n }\n }\n\n /**\n * Strip `MYCO_CMD = \"...\"` from the `[shell_environment_policy.set]`\n * section of a TOML settings file. Leaves the rest of the file\n * untouched. Drops the `[shell_environment_policy.set]` header entirely\n * when the section becomes empty.\n */\n private stripLegacyFromToml(filePath: string): void {\n let raw: string;\n try { raw = fs.readFileSync(filePath, 'utf-8'); } catch { return; }\n const next = removeTomlSectionKeys(raw, 'shell_environment_policy.set', ['MYCO_CMD']);\n if (next !== raw) {\n fs.writeFileSync(filePath, next, 'utf-8');\n }\n }\n\n /**\n * Check if ALL non-null JSON targets share the same file (e.g., Gemini).\n * Only batches when every target resolves to one path — partial overlaps\n * (e.g., Claude Code: hooks+settings share but MCP is separate) use normal path.\n *\n * Plugin-file hooks (e.g., opencode) naturally fall out of batching because their\n * hooksTarget is a distinct .ts file path, yielding a Set size ≥ 2.\n */\n private shouldBatchJsonTargets(reg: typeof this.manifest.registration): boolean {\n if (!reg) return false;\n const mcpFormat = reg.mcpFormat ?? 'json';\n if (mcpFormat !== 'json') return false;\n const targets = [reg.hooksTarget, reg.mcpTarget, reg.settingsTarget].filter(Boolean);\n return targets.length > 1 && new Set(targets).size === 1;\n }\n\n /**\n * Batched install for agents where hooks, MCP, and settings share one JSON file.\n * Single read → apply all transforms in memory → single write.\n */\n private installBatchedJson(reg: NonNullable<typeof this.manifest.registration>): InstallResult {\n const targetPath = path.join(this.projectRoot, reg.hooksTarget ?? reg.mcpTarget ?? reg.settingsTarget!);\n let data = readJsonFile(targetPath);\n let hooks = false, mcp = false, settings = false;\n\n // Apply hooks transform\n const hooksTemplate = reg.hooksTarget ? this.loadTemplate('hooks') : null;\n if (hooksTemplate) {\n const existingHooks = (data.hooks ?? {}) as Record<string, unknown[]>;\n const mergedHooks: Record<string, unknown[]> = {};\n for (const [event, groups] of Object.entries(existingHooks)) {\n const nonMyco = (groups as Array<Record<string, unknown>>).filter((g) => !isMycoHookGroup(g));\n if (nonMyco.length > 0) mergedHooks[event] = nonMyco;\n }\n for (const [event, groups] of Object.entries(hooksTemplate)) {\n mergedHooks[event] = [...(mergedHooks[event] ?? []), ...(groups as unknown[])];\n }\n data.hooks = mergedHooks;\n hooks = true;\n }\n\n // Apply MCP transform\n const mcpTemplate = reg.mcpTarget ? this.loadTemplate('mcp') : null;\n if (mcpTemplate) {\n const serversKey = reg.mcpServersKey ?? 'mcpServers';\n const servers = (data[serversKey] ?? {}) as Record<string, unknown>;\n for (const [name, def] of Object.entries(mcpTemplate)) {\n servers[name] = def;\n }\n data[serversKey] = servers;\n mcp = true;\n }\n\n // Apply settings transform\n const settingsTemplate = reg.settingsTarget ? this.loadTemplate('settings') : null;\n if (settingsTemplate) {\n data = deepMergeSettings(data, settingsTemplate);\n settings = true;\n }\n\n writeJsonFile(targetPath, data);\n\n return {\n hooks,\n mcp,\n skills: this.installSkills(),\n settings,\n instructions: this.installInstructions(),\n pluginPackage: this.installPluginPackage(),\n };\n }\n\n /** Remove all Myco registration from this symbiont's project files. */\n uninstall(): InstallResult {\n const reg = this.manifest.registration;\n const result = this.shouldBatchJsonTargets(reg)\n ? this.uninstallBatchedJson(reg!)\n : {\n hooks: this.uninstallHooks(),\n mcp: this.uninstallMcp(),\n skills: this.uninstallSkills(),\n settings: this.uninstallSettings(),\n instructions: this.uninstallInstructions(),\n pluginPackage: false,\n };\n // Remove hook guard after hooks/settings so the file is cleaned up last\n this.uninstallHookGuard();\n this.cleanGitignore();\n return result;\n }\n\n /**\n * Batched uninstall for agents where hooks, MCP, and settings share one JSON file.\n */\n private uninstallBatchedJson(reg: NonNullable<typeof this.manifest.registration>): InstallResult {\n const targetPath = path.join(this.projectRoot, reg.hooksTarget ?? reg.mcpTarget ?? reg.settingsTarget!);\n const data = readJsonFile(targetPath);\n if (Object.keys(data).length === 0) {\n return {\n hooks: false,\n mcp: false,\n skills: this.uninstallSkills(),\n settings: false,\n instructions: this.uninstallInstructions(),\n pluginPackage: false,\n };\n }\n\n let hooks = false, mcp = false, settings = false;\n\n // Remove hooks\n if (reg.hooksTarget) {\n const existingHooks = (data.hooks ?? {}) as Record<string, unknown[]>;\n if (Object.keys(existingHooks).length > 0) {\n const cleaned: Record<string, unknown[]> = {};\n for (const [event, groups] of Object.entries(existingHooks)) {\n const nonMyco = (groups as Array<Record<string, unknown>>).filter((g) => !isMycoHookGroup(g));\n if (nonMyco.length > 0) cleaned[event] = nonMyco;\n }\n if (Object.keys(cleaned).length === 0) {\n delete data.hooks;\n } else {\n data.hooks = cleaned;\n }\n hooks = true;\n }\n }\n\n // Remove MCP\n if (reg.mcpTarget) {\n const serversKey = reg.mcpServersKey ?? 'mcpServers';\n const servers = (data[serversKey] ?? {}) as Record<string, unknown>;\n if (servers[MYCO_MCP_SERVER_NAME]) {\n delete servers[MYCO_MCP_SERVER_NAME];\n if (Object.keys(servers).length === 0) delete data[serversKey];\n else data[serversKey] = servers;\n mcp = true;\n }\n }\n\n // Remove settings\n const settingsTemplate = reg.settingsTarget ? this.loadTemplate('settings') : null;\n if (settingsTemplate) {\n settings = deepRemoveSettings(data, settingsTemplate);\n }\n\n writeOrDeleteJsonFile(targetPath, data);\n\n return {\n hooks,\n mcp,\n skills: this.uninstallSkills(),\n settings,\n instructions: this.uninstallInstructions(),\n pluginPackage: false,\n };\n }\n\n /**\n * Ensure the instruction file references AGENTS.md.\n * - File doesn't exist: write the full stub template.\n * - File exists without reference: prepend a reference block.\n * - File already has reference: skip (idempotent).\n *\n * Also ensures AGENTS.md exists — creates a starter if missing.\n */\n installInstructions(): boolean {\n const reg = this.manifest.registration;\n if (!reg?.instructionsFile) return false;\n\n // Ensure AGENTS.md exists before creating stubs that reference it\n ensureAgentsMd(this.projectRoot, this.packageRoot);\n\n const targetPath = path.join(this.projectRoot, reg.instructionsFile);\n\n // Check if file already exists\n let existing: string | null = null;\n try { existing = fs.readFileSync(targetPath, 'utf-8'); } catch { /* doesn't exist */ }\n\n if (existing !== null) {\n // File exists — check if it already has our reference\n if (existing.includes(INSTRUCTIONS_REF_START) || existing.includes(INSTRUCTIONS_STUB_MARKER)) {\n return false; // Already has reference — idempotent\n }\n // Prepend reference block to existing content\n fs.writeFileSync(targetPath, INSTRUCTIONS_REF_BLOCK + existing, 'utf-8');\n return true;\n }\n\n // File doesn't exist — write the full stub template\n const templateCandidates = [\n path.join(this.packageRoot, 'src/symbionts/templates/instructions-stub.md'),\n path.join(this.packageRoot, 'dist/src/symbionts/templates/instructions-stub.md'),\n ];\n let stub: string | null = null;\n for (const p of templateCandidates) {\n try { stub = fs.readFileSync(p, 'utf-8'); break; } catch { /* try next */ }\n }\n if (!stub) return false;\n\n stub = stub.replace('{agentDisplayName}', this.manifest.displayName);\n fs.mkdirSync(path.dirname(targetPath), { recursive: true });\n fs.writeFileSync(targetPath, stub, 'utf-8');\n return true;\n }\n\n /**\n * Remove Myco's instruction file reference.\n * - If file is the full stub (only Myco content): delete it.\n * - If file has user content + prepended reference: remove just the reference block.\n */\n uninstallInstructions(): boolean {\n const reg = this.manifest.registration;\n if (!reg?.instructionsFile) return false;\n\n const targetPath = path.join(this.projectRoot, reg.instructionsFile);\n let content: string;\n try { content = fs.readFileSync(targetPath, 'utf-8'); } catch { return false; }\n\n // Case 1: Full stub — delete the file entirely\n if (content.includes(INSTRUCTIONS_STUB_MARKER)) {\n fs.unlinkSync(targetPath);\n return true;\n }\n\n // Case 2: Prepended reference block — remove just the block\n if (content.includes(INSTRUCTIONS_REF_START)) {\n const startIdx = content.indexOf(INSTRUCTIONS_REF_START);\n const endIdx = content.indexOf(INSTRUCTIONS_REF_END);\n if (endIdx > startIdx) {\n // Remove from start marker through end marker + trailing whitespace\n const afterEnd = endIdx + INSTRUCTIONS_REF_END.length;\n const cleaned = (content.slice(0, startIdx) + content.slice(afterEnd)).replace(/^\\n+/, '');\n fs.writeFileSync(targetPath, cleaned, 'utf-8');\n return true;\n }\n }\n\n return false;\n }\n\n /** List skill directory names from the package root. Returns empty array if not found. */\n private listSkillDirs(): string[] {\n try {\n return fs.readdirSync(path.join(this.packageRoot, SKILLS_SUBDIR), { withFileTypes: true })\n .filter((d) => d.isDirectory())\n .map((d) => d.name);\n } catch { return []; }\n }\n\n /**\n * Reconcile Myco-owned skill entries in project .gitignore.\n * Computes the desired entry set, strips any existing Myco block\n * (and legacy entries), then writes the current block if changed.\n */\n private updateGitignore(): void {\n const reg = this.manifest.registration;\n if (!reg?.skillsTarget) return;\n\n const skillNames = this.listSkillDirs();\n\n // Desired state: canonical per-skill entries + infrastructure artifacts.\n // Agent-specific targets (e.g. .claude/skills/) use local .gitignore files\n // instead of polluting the project-level .gitignore.\n const desired = [\n ...skillNames.map((name) => `${CANONICAL_SKILLS_DIR}/${name}`),\n ...this.getCustomPlanGitignoreEntries(),\n WRANGLER_CACHE_DIR,\n ];\n\n const gitignorePath = path.join(this.projectRoot, '.gitignore');\n let content = '';\n try { content = fs.readFileSync(gitignorePath, 'utf-8'); } catch { /* doesn't exist yet */ }\n\n // Strip existing Myco block and any legacy entries\n const stripped = this.stripMycoGitignoreBlock(content, skillNames);\n\n // Build the new block\n const desiredBlock = desired.length > 0\n ? `${GITIGNORE_COMMENT}\\n${desired.join('\\n')}\\n`\n : '';\n\n // Check if anything changed\n if (stripped === content && desiredBlock === '') return;\n const separator = stripped.length > 0 && !stripped.endsWith('\\n') ? '\\n' : '';\n const spacer = stripped.length > 0 && desiredBlock.length > 0 ? '\\n' : '';\n const result = stripped + separator + spacer + desiredBlock;\n if (result === content) return;\n\n fs.writeFileSync(gitignorePath, result, 'utf-8');\n }\n\n /**\n * Remove all Myco-owned gitignore entries: the comment header, per-skill\n * entries for both canonical and agent-specific paths, and legacy blanket\n * directory entries. Returns the cleaned content.\n */\n private stripMycoGitignoreBlock(content: string, skillNames: string[]): string {\n let stripped = content;\n\n const managedStart = stripped.indexOf(GITIGNORE_COMMENT);\n if (managedStart !== -1) {\n const managedEndMatch = stripped.slice(managedStart).match(/\\n\\n/);\n const managedEnd = managedEndMatch\n ? managedStart + managedEndMatch.index! + managedEndMatch[0].length\n : stripped.length;\n stripped = stripped.slice(0, managedStart) + stripped.slice(managedEnd);\n }\n\n const reg = this.manifest.registration;\n const legacyOwnedLines = new Set<string>([\n GITIGNORE_SKILLS_COMMENT_LEGACY,\n `${CANONICAL_SKILLS_DIR}/`,\n WRANGLER_CACHE_DIR,\n ]);\n for (const name of skillNames) {\n legacyOwnedLines.add(`${CANONICAL_SKILLS_DIR}/${name}`);\n if (reg?.skillsTarget && reg.skillsTarget !== CANONICAL_SKILLS_DIR) {\n legacyOwnedLines.add(`${reg.skillsTarget}/${name}`);\n }\n }\n\n const filtered = stripped\n .split('\\n')\n .filter((line) => !legacyOwnedLines.has(line));\n return filtered.join('\\n').replace(/\\n{3,}/g, '\\n\\n').trimEnd() + (filtered.length > 0 ? '\\n' : '');\n }\n\n /**\n * Merge hooks template into the target settings file.\n * Replaces all Myco-owned hook groups; preserves non-Myco hooks.\n *\n * For plugin-file agents (e.g., opencode) this dispatches to `installPluginHookFile()`\n * which writes a verbatim .ts plugin source to hooksTarget instead of merging JSON.\n */\n installHooks(): boolean {\n const reg = this.manifest.registration;\n if (!reg?.hooksTarget) return false;\n\n if (reg.hooksFormat === HOOKS_FORMAT_PLUGIN_FILE) return this.installPluginHookFile();\n\n const template = this.loadTemplate('hooks');\n if (!template) return false;\n\n const targetPath = path.join(this.projectRoot, reg.hooksTarget);\n const settings = readJsonFile(targetPath);\n const existingHooks = (settings.hooks ?? {}) as Record<string, unknown[]>;\n\n // Build merged hooks: for each event, keep non-Myco groups + add template groups\n const mergedHooks: Record<string, unknown[]> = {};\n\n // Preserve non-Myco hooks from existing config\n for (const [event, groups] of Object.entries(existingHooks)) {\n const nonMycoGroups = (groups as Array<Record<string, unknown>>).filter(\n (group) => !isMycoHookGroup(group),\n );\n if (nonMycoGroups.length > 0) {\n mergedHooks[event] = nonMycoGroups;\n }\n }\n\n // Add template hooks\n for (const [event, groups] of Object.entries(template)) {\n mergedHooks[event] = [...(mergedHooks[event] ?? []), ...(groups as unknown[])];\n }\n\n settings.hooks = mergedHooks;\n writeJsonFile(targetPath, settings);\n return true;\n }\n\n /**\n * Install a plugin-file hook target by copying a verbatim template.\n * Used for agents whose hook system is plugin-based rather than JSON entry-based\n * (e.g., opencode's TypeScript plugin system).\n */\n private installPluginHookFile(): boolean {\n const reg = this.manifest.registration;\n if (!reg?.hooksTarget) return false;\n\n const templateContent = this.loadTemplateRaw('plugin.ts');\n if (templateContent === null) return false;\n\n return this.writeManagedFile(\n path.join(this.projectRoot, reg.hooksTarget),\n templateContent,\n );\n }\n\n /**\n * Remove a plugin-file hook target.\n * Only deletes files whose content contains the Myco plugin marker — contributors\n * who hand-edit the plugin file without removing the marker are protected.\n */\n private uninstallPluginHookFile(): boolean {\n const reg = this.manifest.registration;\n if (!reg?.hooksTarget) return false;\n\n const targetPath = path.join(this.projectRoot, reg.hooksTarget);\n let content: string;\n try { content = fs.readFileSync(targetPath, 'utf-8'); } catch { return false; }\n\n if (!content.includes(MYCO_PLUGIN_FILE_MARKER)) return false;\n\n try {\n fs.unlinkSync(targetPath);\n // Remove parent plugins dir if now empty\n try { fs.rmdirSync(path.dirname(targetPath)); } catch { /* not empty or missing */ }\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * Install a plugin deps package.json for plugin-file agents (e.g., opencode).\n * Writes the template verbatim so the agent's package manager can install the SDK.\n */\n private installPluginPackage(): boolean {\n const reg = this.manifest.registration;\n if (!reg?.pluginPackageTarget) return false;\n\n const templateContent = this.loadTemplateRaw('package.json');\n if (templateContent === null) return false;\n\n return this.writeManagedFile(\n path.join(this.projectRoot, reg.pluginPackageTarget),\n templateContent,\n );\n }\n\n /**\n * Merge MCP server template into the target config file.\n * Replaces the `myco` server entry; preserves other servers.\n */\n installMcp(): boolean {\n const reg = this.manifest.registration;\n if (!reg?.mcpTarget) return false;\n\n const template = this.buildMcpTemplate(this.loadTemplate('mcp'));\n if (!template) return false;\n\n const targetPath = path.join(this.projectRoot, reg.mcpTarget);\n if (reg.mcpFormat === 'toml') {\n return this.installMcpToml(targetPath, template);\n }\n return this.installMcpJson(targetPath, template);\n }\n\n private buildMcpTemplate(\n template: Record<string, unknown> | null,\n ): Record<string, unknown> | null {\n if (!template) return null;\n\n const overrides = this.resolveMcpLaunchOverrides();\n if (!overrides.cwd && Object.keys(overrides.env).length === 0) return template;\n\n return Object.fromEntries(\n Object.entries(template).map(([name, def]) => {\n if (!def || typeof def !== 'object' || Array.isArray(def)) return [name, def];\n const server = def as Record<string, unknown>;\n const mergedEnv = {\n ...(\n server.env && typeof server.env === 'object' && !Array.isArray(server.env)\n ? server.env\n : {}\n ),\n ...overrides.env,\n } as Record<string, unknown>;\n return [\n name,\n {\n ...server,\n ...(overrides.cwd ? { cwd: overrides.cwd } : {}),\n ...(Object.keys(mergedEnv).length > 0 ? { env: mergedEnv } : {}),\n },\n ];\n }),\n );\n }\n\n private resolveMcpLaunchOverrides(): McpLaunchOverrides {\n const vaultDir = path.join(this.projectRoot, '.myco');\n const registration = this.manifest.registration;\n const envEntries = Object.entries(registration?.mcpEnv ?? {});\n return {\n cwd: this.resolveMcpPlaceholderValue(registration?.mcpCwd, vaultDir),\n env: Object.fromEntries(\n envEntries.flatMap(([key, value]) => {\n const resolved = this.resolveMcpPlaceholderValue(value, vaultDir);\n return resolved ? [[key, resolved] as const] : [];\n }),\n ),\n };\n }\n\n private resolveMcpPlaceholderValue(\n value: string | undefined,\n vaultDir: string,\n ): string | undefined {\n if (!value) return value;\n return value\n .replaceAll(MCP_ENV_PROJECT_ROOT_TOKEN, this.projectRoot)\n .replaceAll(MCP_ENV_VAULT_DIR_TOKEN, vaultDir);\n }\n\n /**\n * Write MCP servers to a JSON config file under the manifest-configured key.\n * Most agents use the canonical `mcpServers` key; opencode uses `mcp`.\n *\n * The `?? 'mcpServers'` fallback protects against test fixtures that construct\n * manifests as plain object literals and bypass the schema's default.\n */\n private installMcpJson(targetPath: string, template: Record<string, unknown>): boolean {\n const serversKey = this.manifest.registration!.mcpServersKey ?? 'mcpServers';\n const config = readJsonFile(targetPath);\n const servers = (config[serversKey] ?? {}) as Record<string, unknown>;\n\n for (const [name, def] of Object.entries(template)) {\n servers[name] = def;\n }\n\n config[serversKey] = servers;\n writeJsonFile(targetPath, config);\n return true;\n }\n\n /** Write MCP servers to a TOML config file. */\n private installMcpToml(targetPath: string, template: Record<string, unknown>): boolean {\n let raw = '';\n try { raw = fs.readFileSync(targetPath, 'utf-8'); } catch { /* doesn't exist */ }\n\n for (const [name, def] of Object.entries(template)) {\n raw = buildTomlMcpSection(raw, name, def as Record<string, unknown>);\n }\n\n fs.mkdirSync(path.dirname(targetPath), { recursive: true });\n fs.writeFileSync(targetPath, raw, 'utf-8');\n return true;\n }\n\n /**\n * Create symlinks for skills through .agents/skills/ canonical layer.\n * Canonical: .agents/skills/<name> -> <packageRoot>/skills/<name>\n * Agent-specific: <skillsTarget>/<name> -> ../../.agents/skills/<name>\n */\n installSkills(): boolean {\n const reg = this.manifest.registration;\n if (!reg?.skillsTarget) return false;\n\n const skillNames = this.listSkillDirs();\n if (skillNames.length === 0) return false;\n\n const skillsSrc = path.join(this.packageRoot, SKILLS_SUBDIR);\n\n // Create canonical symlinks: .agents/skills/<name> -> package skills\n const canonicalDir = path.join(this.projectRoot, CANONICAL_SKILLS_DIR);\n fs.mkdirSync(canonicalDir, { recursive: true });\n\n for (const name of skillNames) {\n const canonicalLink = path.join(canonicalDir, name);\n const target = path.join(skillsSrc, name);\n ensureSymlink(canonicalLink, target);\n }\n\n // Create agent-specific symlinks if skillsTarget differs from canonical\n const agentSkillsDir = path.join(this.projectRoot, reg.skillsTarget);\n const canonicalRel = path.relative(agentSkillsDir, canonicalDir);\n\n if (reg.skillsTarget !== CANONICAL_SKILLS_DIR) {\n fs.mkdirSync(agentSkillsDir, { recursive: true });\n for (const name of skillNames) {\n const agentLink = path.join(agentSkillsDir, name);\n const relTarget = path.join(canonicalRel, name);\n ensureSymlink(agentLink, relTarget);\n }\n ensureLocalSkillsGitignore(agentSkillsDir);\n }\n\n return true;\n }\n\n /**\n * Merge settings template into the target settings file.\n * JSON targets: deep-merges objects and deduplicates arrays.\n * TOML targets: emits each top-level template key as a [section] with scalar children.\n */\n installSettings(): boolean {\n const reg = this.manifest.registration;\n if (!reg?.settingsTarget) return false;\n\n const template = this.loadTemplate('settings');\n if (!template) return false;\n\n const targetPath = path.join(this.projectRoot, reg.settingsTarget);\n const settingsFormat = reg.settingsFormat ?? 'json';\n\n if (settingsFormat === 'toml') {\n return this.installSettingsToml(targetPath, template);\n }\n\n const existing = readJsonFile(targetPath);\n const merged = deepMergeSettings(existing, template);\n writeJsonFile(targetPath, merged);\n return true;\n }\n\n /**\n * Merge a settings template into a TOML config file.\n * Each top-level key in the template becomes a [section] header, with its\n * children written as scalar key = value lines. Existing sections and keys\n * outside the template (including unrelated sections like [mcp_servers.*])\n * are preserved.\n */\n private installSettingsToml(targetPath: string, template: Record<string, unknown>): boolean {\n let raw = '';\n try { raw = fs.readFileSync(targetPath, 'utf-8'); } catch { /* doesn't exist */ }\n\n for (const [sectionName, values] of Object.entries(template)) {\n if (!values || typeof values !== 'object' || Array.isArray(values)) continue;\n raw = upsertTomlSection(raw, sectionName, values as Record<string, unknown>);\n }\n\n fs.mkdirSync(path.dirname(targetPath), { recursive: true });\n fs.writeFileSync(targetPath, raw, 'utf-8');\n return true;\n }\n\n /**\n * Remove Myco entries from the target settings file.\n * Template-driven: loads the settings template and removes matching values.\n * JSON: arrays filtered by template values, object keys deleted by name.\n * TOML: removes each template key from its section; empty sections are dropped.\n */\n uninstallSettings(): boolean {\n const reg = this.manifest.registration;\n if (!reg?.settingsTarget) return false;\n\n const template = this.loadTemplate('settings');\n if (!template) return false;\n\n const targetPath = path.join(this.projectRoot, reg.settingsTarget);\n const settingsFormat = reg.settingsFormat ?? 'json';\n\n if (settingsFormat === 'toml') {\n return this.uninstallSettingsToml(targetPath, template);\n }\n\n const settings = readJsonFile(targetPath);\n if (Object.keys(settings).length === 0) return false;\n\n const changed = deepRemoveSettings(settings, template);\n if (!changed) return false;\n\n writeOrDeleteJsonFile(targetPath, settings);\n return true;\n }\n\n /**\n * Remove template-defined keys from TOML settings file.\n * For each section in the template, deletes only the keys the template owns;\n * other keys and unrelated sections stay intact. Empty sections are stripped.\n * Deletes the file entirely if no TOML content remains.\n */\n private uninstallSettingsToml(targetPath: string, template: Record<string, unknown>): boolean {\n let raw = '';\n try { raw = fs.readFileSync(targetPath, 'utf-8'); } catch { return false; }\n if (!raw.trim()) return false;\n\n let changed = false;\n for (const [sectionName, values] of Object.entries(template)) {\n if (!values || typeof values !== 'object' || Array.isArray(values)) continue;\n const keys = Object.keys(values as Record<string, unknown>);\n const next = removeTomlSectionKeys(raw, sectionName, keys);\n if (next !== raw) {\n raw = next;\n changed = true;\n }\n }\n\n if (!changed) return false;\n\n if (!raw.trim()) {\n try { fs.unlinkSync(targetPath); } catch { /* ignore */ }\n } else {\n fs.writeFileSync(targetPath, raw, 'utf-8');\n }\n return true;\n }\n\n /**\n * Remove Myco hook groups from the target settings file.\n *\n * For plugin-file agents (e.g., opencode) this dispatches to `uninstallPluginHookFile()`\n * which deletes the verbatim plugin file (guarded by the Myco plugin marker).\n */\n uninstallHooks(): boolean {\n const reg = this.manifest.registration;\n if (!reg?.hooksTarget) return false;\n\n if (reg.hooksFormat === HOOKS_FORMAT_PLUGIN_FILE) return this.uninstallPluginHookFile();\n\n const targetPath = path.join(this.projectRoot, reg.hooksTarget);\n const settings = readJsonFile(targetPath);\n const existingHooks = (settings.hooks ?? {}) as Record<string, unknown[]>;\n if (Object.keys(existingHooks).length === 0) return false;\n\n const cleaned: Record<string, unknown[]> = {};\n for (const [event, groups] of Object.entries(existingHooks)) {\n const nonMyco = (groups as Array<Record<string, unknown>>).filter(\n (group) => !isMycoHookGroup(group),\n );\n if (nonMyco.length > 0) {\n cleaned[event] = nonMyco;\n }\n }\n\n if (Object.keys(cleaned).length === 0) {\n delete settings.hooks;\n } else {\n settings.hooks = cleaned;\n }\n\n writeOrDeleteJsonFile(targetPath, settings);\n return true;\n }\n\n /** Remove Myco MCP server entry from the target config file. */\n uninstallMcp(): boolean {\n const reg = this.manifest.registration;\n if (!reg?.mcpTarget) return false;\n\n const targetPath = path.join(this.projectRoot, reg.mcpTarget);\n if (reg.mcpFormat === 'toml') {\n return this.uninstallMcpToml(targetPath);\n }\n return this.uninstallMcpJson(targetPath);\n }\n\n private uninstallMcpJson(targetPath: string): boolean {\n // Fallback matches the schema default; protects test fixtures that bypass .parse().\n const serversKey = this.manifest.registration!.mcpServersKey ?? 'mcpServers';\n const config = readJsonFile(targetPath);\n const servers = (config[serversKey] ?? {}) as Record<string, unknown>;\n if (!servers[MYCO_MCP_SERVER_NAME]) return false;\n\n delete servers[MYCO_MCP_SERVER_NAME];\n\n if (Object.keys(servers).length === 0) {\n delete config[serversKey];\n } else {\n config[serversKey] = servers;\n }\n\n writeOrDeleteJsonFile(targetPath, config);\n return true;\n }\n\n private uninstallMcpToml(targetPath: string): boolean {\n let raw = '';\n try { raw = fs.readFileSync(targetPath, 'utf-8'); } catch { return false; }\n\n const sectionHeader = `[mcp_servers.${MYCO_MCP_SERVER_NAME}]`;\n if (!raw.includes(sectionHeader)) return false;\n\n const startIdx = raw.indexOf(sectionHeader);\n const endIdx = findTomlSectionEnd(raw, startIdx + sectionHeader.length, `mcp_servers.${MYCO_MCP_SERVER_NAME}`);\n const before = raw.slice(0, startIdx).trimEnd();\n const after = raw.slice(endIdx).trimStart();\n const updated = (before + (before && after ? '\\n\\n' : '') + after).trimEnd();\n\n if (!updated.trim()) {\n try { fs.unlinkSync(targetPath); } catch { /* ignore */ }\n } else {\n fs.writeFileSync(targetPath, updated + '\\n', 'utf-8');\n }\n return true;\n }\n\n /** Remove skill symlinks (canonical + agent-specific). */\n uninstallSkills(): boolean {\n const reg = this.manifest.registration;\n if (!reg?.skillsTarget) return false;\n\n const skillNames = this.listSkillDirs();\n if (skillNames.length === 0) return false;\n\n let removed = false;\n\n // Remove agent-specific symlinks\n if (reg.skillsTarget !== CANONICAL_SKILLS_DIR) {\n for (const name of skillNames) {\n const link = path.join(this.projectRoot, reg.skillsTarget, name);\n try { fs.unlinkSync(link); removed = true; } catch { /* doesn't exist */ }\n }\n // Remove agent skills dir if now empty (rmdirSync fails atomically if non-empty)\n try { fs.rmdirSync(path.join(this.projectRoot, reg.skillsTarget)); } catch { /* not empty or missing */ }\n }\n\n // Remove canonical symlinks\n const canonicalDir = path.join(this.projectRoot, CANONICAL_SKILLS_DIR);\n for (const name of skillNames) {\n const link = path.join(canonicalDir, name);\n try { fs.unlinkSync(link); removed = true; } catch { /* doesn't exist */ }\n }\n // Remove empty dirs (rmdirSync fails atomically if non-empty)\n try { fs.rmdirSync(canonicalDir); } catch { /* not empty or missing */ }\n try { fs.rmdirSync(path.join(this.projectRoot, '.agents')); } catch { /* not empty or missing */ }\n\n return removed;\n }\n\n /** Remove Myco entries from project .gitignore. */\n private cleanGitignore(): void {\n const gitignorePath = path.join(this.projectRoot, '.gitignore');\n let content = '';\n try { content = fs.readFileSync(gitignorePath, 'utf-8'); } catch { return; }\n\n const cleaned = this.stripMycoGitignoreBlock(content, this.listSkillDirs()).trim();\n if (cleaned) {\n fs.writeFileSync(gitignorePath, cleaned + '\\n', 'utf-8');\n } else {\n try { fs.unlinkSync(gitignorePath); } catch { /* ignore */ }\n }\n }\n}\n\n/**\n * Create agent-specific symlinks for a skill in `.agents/skills/<name>`.\n *\n * Reads all symbiont manifests to find skillsTarget paths that differ\n * from the canonical `.agents/skills/` directory, then creates relative\n * symlinks from each target to the canonical location.\n *\n * Called by vault_write_skill after writing a generated skill to disk.\n * Also handles removal: when `remove` is true, deletes the symlinks.\n */\nexport function syncSkillSymlinks(\n projectRoot: string,\n skillName: string,\n opts?: { remove?: boolean },\n): void {\n // Resolve manifests dir — try sibling (source layout) then dist layout\n // (tsup bundles into dist/chunk-*.js, but manifests are at dist/src/symbionts/manifests/)\n const selfDir = path.dirname(new URL(import.meta.url).pathname);\n const candidates = [\n path.join(selfDir, 'manifests'),\n path.join(selfDir, 'src', 'symbionts', 'manifests'),\n ];\n const manifestDir = candidates.find((d) => fs.existsSync(d));\n if (!manifestDir) return;\n\n const targets = new Set<string>();\n for (const file of fs.readdirSync(manifestDir).filter((f) => f.endsWith('.yaml'))) {\n try {\n const content = fs.readFileSync(path.join(manifestDir, file), 'utf-8');\n const match = content.match(/skillsTarget:\\s*(.+)/);\n if (match) targets.add(match[1].trim());\n } catch { /* skip unreadable manifests */ }\n }\n\n for (const target of targets) {\n if (target === CANONICAL_SKILLS_DIR) continue; // canonical is the source, not a link target\n\n const agentSkillsDir = path.join(projectRoot, target);\n const linkPath = path.join(agentSkillsDir, skillName);\n\n if (opts?.remove) {\n try { fs.unlinkSync(linkPath); } catch { /* doesn't exist */ }\n try { fs.rmdirSync(agentSkillsDir); } catch { /* not empty or missing */ }\n } else {\n fs.mkdirSync(agentSkillsDir, { recursive: true });\n const canonicalDir = path.join(projectRoot, CANONICAL_SKILLS_DIR);\n const relTarget = path.join(path.relative(agentSkillsDir, canonicalDir), skillName);\n ensureSymlink(linkPath, relTarget);\n // Ensure a local .gitignore ignores all symlinks in this directory.\n // Localized to the agent's skills dir — doesn't pollute the project .gitignore.\n ensureLocalSkillsGitignore(agentSkillsDir);\n }\n }\n}\n\n/** Content for the local .gitignore that ignores Myco-created symlinks. */\nconst LOCAL_SKILLS_GITIGNORE = `# Myco-managed symlinks — generated skills are symlinked here automatically.\n# The canonical location for all skills is .agents/skills/.\n#\n# To add your own skill to this directory, un-ignore it:\n# !my-skill\n*\n!.gitignore\n`;\n\n/**\n * Write a .gitignore inside an agent's skills directory that ignores all\n * symlinks Myco creates there. Idempotent — skips if already present.\n */\nfunction ensureLocalSkillsGitignore(agentSkillsDir: string): void {\n const gitignorePath = path.join(agentSkillsDir, '.gitignore');\n try {\n if (fs.readFileSync(gitignorePath, 'utf-8') === LOCAL_SKILLS_GITIGNORE) return;\n } catch { /* doesn't exist — proceed */ }\n fs.writeFileSync(gitignorePath, LOCAL_SKILLS_GITIGNORE, 'utf-8');\n}\n","/** TOML section header pattern. */\nconst TOML_SECTION_RE = /^\\[([^\\]]+)\\]/;\n\n/**\n * Find where a named TOML section ends.\n *\n * Walks forward from `searchStart` until it hits a section header that is\n * NEITHER the named section itself NOR any of its subtables (headers that\n * start with `${sectionName}.`). Returns `raw.length` if the section extends\n * to the end of the file.\n */\nexport function findTomlSectionEnd(\n raw: string,\n searchStart: number,\n sectionName: string,\n): number {\n const subsectionPrefix = `${sectionName}.`;\n const rawLines = raw.slice(searchStart).split('\\n');\n let offset = searchStart;\n for (const line of rawLines) {\n offset += line.length + 1;\n const m = line.match(TOML_SECTION_RE);\n if (m && !m[1].startsWith(subsectionPrefix) && m[1] !== sectionName) {\n return offset - line.length - 1;\n }\n }\n return raw.length;\n}\n\n/** Format a single TOML key = value line from a JS value. Skips unsupported types. */\nfunction formatTomlScalarLine(key: string, val: unknown): string | null {\n if (typeof val === 'string') return `${key} = \"${val}\"`;\n if (typeof val === 'boolean') return `${key} = ${val}`;\n if (typeof val === 'number' && Number.isFinite(val)) return `${key} = ${val}`;\n if (Array.isArray(val)) {\n return `${key} = [${val.map((v: unknown) => `\"${v}\"`).join(', ')}]`;\n }\n return null;\n}\n\n/**\n * Insert or replace a top-level TOML section with the given scalar key/value pairs.\n *\n * - Scalar values (string, boolean, number, string[]) are written as `key = value`.\n * - Nested objects are ignored — use {@link buildTomlMcpSection} or call\n * `upsertTomlSection` once per subtable if you need nested tables.\n * - Idempotent: running twice with the same inputs produces identical output.\n * - Preserves content before and after the section.\n */\nexport function upsertTomlSection(\n raw: string,\n sectionName: string,\n values: Record<string, unknown>,\n): string {\n const sectionHeader = `[${sectionName}]`;\n\n const lines: string[] = [sectionHeader];\n for (const [key, val] of Object.entries(values)) {\n const line = formatTomlScalarLine(key, val);\n if (line !== null) lines.push(line);\n }\n const block = lines.join('\\n');\n\n if (raw.includes(sectionHeader)) {\n const startIdx = raw.indexOf(sectionHeader);\n const endIdx = findTomlSectionEnd(raw, startIdx + sectionHeader.length, sectionName);\n const before = raw.slice(0, startIdx).trimEnd();\n const after = raw.slice(endIdx);\n const separator = before ? '\\n\\n' : '';\n return (before + separator + block + after).trimEnd() + '\\n';\n }\n\n const separator = raw.trim() ? '\\n\\n' : '';\n return (raw.trimEnd() + separator + block).trimEnd() + '\\n';\n}\n\n/**\n * Remove specific keys from a top-level TOML section.\n *\n * - Only the listed keys are removed. Other keys in the section are preserved.\n * - If the section ends up with no content after removal, the whole section\n * (header and body) is stripped.\n * - Returns the updated string; equal to the input if no changes were made.\n */\nexport function removeTomlSectionKeys(\n raw: string,\n sectionName: string,\n keys: string[],\n): string {\n const sectionHeader = `[${sectionName}]`;\n if (!raw.includes(sectionHeader)) return raw;\n\n const startIdx = raw.indexOf(sectionHeader);\n const endIdx = findTomlSectionEnd(raw, startIdx + sectionHeader.length, sectionName);\n const sectionBody = raw.slice(startIdx + sectionHeader.length, endIdx);\n\n const keyRes = keys.map((k) => new RegExp(`^\\\\s*${escapeRegExp(k)}\\\\s*=`));\n const bodyLines = sectionBody.split('\\n');\n const kept: string[] = [];\n let removedAny = false;\n let subtableSeen = false;\n for (const line of bodyLines) {\n // Once we hit a subtable header, everything after stays (keys belong to the parent)\n if (subtableSeen || TOML_SECTION_RE.test(line.trim())) {\n subtableSeen = true;\n kept.push(line);\n continue;\n }\n if (keyRes.some((re) => re.test(line))) {\n removedAny = true;\n continue;\n }\n kept.push(line);\n }\n if (!removedAny) return raw;\n\n // Determine whether the parent section still has any key = value lines.\n const hasRemainingKeys = kept.some((line) => {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith('#')) return false;\n if (TOML_SECTION_RE.test(trimmed)) return false;\n return /=/.test(trimmed);\n });\n\n const before = raw.slice(0, startIdx).trimEnd();\n let rebuilt: string;\n if (hasRemainingKeys) {\n const newBody = kept.join('\\n');\n const separator = before ? '\\n\\n' : '';\n rebuilt = before + separator + sectionHeader + newBody;\n } else {\n // Drop the header entirely; keep any trailing subtables we preserved above.\n const trailing = kept.join('\\n').trimStart();\n const separator = before && trailing ? '\\n\\n' : '';\n rebuilt = before + separator + trailing;\n }\n return rebuilt.trimEnd() + (rebuilt.trim() ? '\\n' : '');\n}\n\nfunction escapeRegExp(s: string): string {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\n/**\n * Build/update a specific mcp_servers entry in a TOML string.\n * Pure transformation — returns updated content without writing to disk.\n *\n * Handles the MCP-specific `env` subtable pattern; for plain sections use\n * {@link upsertTomlSection}.\n */\nexport function buildTomlMcpSection(\n raw: string,\n serverName: string,\n server: Record<string, unknown>,\n): string {\n const sectionName = `mcp_servers.${serverName}`;\n const sectionHeader = `[${sectionName}]`;\n\n // Build the TOML block for this server\n const lines: string[] = [sectionHeader];\n for (const [key, val] of Object.entries(server)) {\n if (key === 'env' && typeof val === 'object' && val !== null) continue; // Handle env as subtable\n const line = formatTomlScalarLine(key, val);\n if (line !== null) lines.push(line);\n }\n\n // Add env subtable if present\n const env = server.env as Record<string, string> | undefined;\n if (env && Object.keys(env).length > 0) {\n lines.push('');\n lines.push(`[${sectionName}.env]`);\n for (const [key, val] of Object.entries(env)) {\n lines.push(`${key} = \"${val}\"`);\n }\n }\n\n const block = lines.join('\\n');\n\n let updated: string;\n if (raw.includes(sectionHeader)) {\n const startIdx = raw.indexOf(sectionHeader);\n const endIdx = findTomlSectionEnd(raw, startIdx + sectionHeader.length, sectionName);\n const before = raw.slice(0, startIdx).trimEnd();\n const after = raw.slice(endIdx);\n const separator = before ? '\\n\\n' : '';\n updated = (before + separator + block + after).trimEnd() + '\\n';\n } else {\n // Append new section\n const separator = raw.trim() ? '\\n\\n' : '';\n updated = (raw.trimEnd() + separator + block).trimEnd() + '\\n';\n }\n\n return updated;\n}\n","import { deepMerge, isPlainObject } from '../utils/deep-merge.js';\n\n/** Symbiont settings merge uses union semantics: arrays are concatenated and deduped. */\nexport function deepMergeSettings(\n target: Record<string, unknown>,\n source: Record<string, unknown>,\n): Record<string, unknown> {\n return deepMerge(target, source, { arrayStrategy: 'union' });\n}\n\nexport { isPlainObject };\n\n/**\n * Remove values from target that match the template structure.\n * Arrays: filter out values present in the template array.\n * Objects: delete keys present in the template object, recurse into nested objects.\n * Returns true if anything was removed.\n */\nexport function deepRemoveSettings(\n target: Record<string, unknown>,\n template: Record<string, unknown>,\n): boolean {\n let changed = false;\n for (const [key, templateVal] of Object.entries(template)) {\n const targetVal = target[key];\n if (targetVal === undefined) continue;\n\n if (Array.isArray(templateVal) && Array.isArray(targetVal)) {\n // Filter out values that appear in the template array\n const templateSet = new Set(templateVal.map(String));\n const filtered = targetVal.filter((v) => !templateSet.has(String(v)));\n if (filtered.length !== targetVal.length) {\n if (filtered.length > 0) {\n target[key] = filtered;\n } else {\n delete target[key];\n }\n changed = true;\n }\n } else if (isPlainObject(templateVal) && isPlainObject(targetVal)) {\n // Recurse into nested objects, then prune if empty\n if (deepRemoveSettings(targetVal, templateVal)) {\n if (Object.keys(targetVal).length === 0) {\n delete target[key];\n }\n changed = true;\n }\n } else {\n // Scalar: delete if value matches\n if (String(targetVal) === String(templateVal)) {\n delete target[key];\n changed = true;\n }\n }\n }\n return changed;\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\n\nexport function readJsonFile(filePath: string): Record<string, unknown> {\n try {\n return JSON.parse(fs.readFileSync(filePath, 'utf-8'));\n } catch {\n return {};\n }\n}\n\nexport function writeJsonFile(filePath: string, data: Record<string, unknown>): void {\n fs.mkdirSync(path.dirname(filePath), { recursive: true });\n fs.writeFileSync(filePath, JSON.stringify(data, null, 2) + '\\n', 'utf-8');\n}\n\n/** Write a JSON file, or delete it if the object is empty. */\nexport function writeOrDeleteJsonFile(filePath: string, data: Record<string, unknown>): void {\n if (Object.keys(data).length === 0) {\n try { fs.unlinkSync(filePath); } catch { /* ignore */ }\n } else {\n writeJsonFile(filePath, data);\n }\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\n\nconst DEFAULT_AGENTS_STARTER = `# Project Rules\n\n<!-- This starter file was created by Myco. Replace it with your project's rules and conventions. -->\n\nRules haven't been defined yet. Use the /rules skill to generate project rules, or edit this file directly.\n`;\n\n/**\n * Check if a command string belongs to Myco.\n *\n * Three legacy shapes we still need to recognize so uninstall / re-install\n * can strip old entries cleanly:\n * 1. `.agents/myco-run.cjs` — current hook guard entry point\n * 2. `.agents/myco-hook.cjs` — prior cross-platform guard (pre-rename)\n * 3. `myco-run` bare — published MCP entry point and old shell shim\n *\n * Any of these signals \"this is our group, safe to replace on reinstall.\"\n */\nexport function isMycoHookCommand(command: string): boolean {\n return (\n command.includes('.agents/myco-run.cjs') ||\n command.includes('.agents/myco-hook.cjs') ||\n command.startsWith('myco-run')\n );\n}\n\n/**\n * Check if a hook group is Myco-owned.\n * Handles both nested format (Claude Code, Codex, etc.) and flat format (Windsurf).\n *\n * Nested: { hooks: [{ command: \"cd \\\"$(git rev-parse ...)\\\" && node .agents/myco-run.cjs ...\" }] }\n * Flat: { command: \"cd \\\"$(git rev-parse ...)\\\" && node .agents/myco-run.cjs ...\" }\n */\nexport function isMycoHookGroup(group: Record<string, unknown>): boolean {\n // Nested format: { hooks: [{ command: \"...\" }] }\n if (Array.isArray(group.hooks) && group.hooks.some((h: { command?: string }) => h.command && isMycoHookCommand(h.command))) return true;\n // Flat format: { command: \"...\" }\n if (typeof group.command === 'string' && isMycoHookCommand(group.command)) return true;\n return false;\n}\n\n/**\n * Create a starter AGENTS.md if the project doesn't have one.\n * Idempotent — skips if AGENTS.md already exists.\n */\nexport function ensureAgentsMd(projectRoot: string, packageRoot: string): void {\n const agentsMdPath = path.join(projectRoot, 'AGENTS.md');\n if (fs.existsSync(agentsMdPath)) return;\n\n const candidates = [\n path.join(packageRoot, 'src/symbionts/templates/agents-starter.md'),\n path.join(packageRoot, 'dist/src/symbionts/templates/agents-starter.md'),\n ];\n for (const p of candidates) {\n try {\n const content = fs.readFileSync(p, 'utf-8');\n fs.writeFileSync(agentsMdPath, content, 'utf-8');\n return;\n } catch { /* try next */ }\n }\n\n fs.writeFileSync(agentsMdPath, DEFAULT_AGENTS_STARTER, 'utf-8');\n}\n\nexport function ensureSymlink(linkPath: string, target: string): void {\n try {\n if (fs.readlinkSync(linkPath) === target) return;\n } catch { /* does not exist or is not a symlink — proceed */ }\n try { fs.rmSync(linkPath, { recursive: true, force: true }); } catch { /* ignore */ }\n fs.symlinkSync(target, linkPath);\n}\n"],"mappings":"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","names":["fs","path","separator","fs","path","path","fs"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/config/updates.ts"],"sourcesContent":["import type { MycoConfig, EmbeddingProviderConfig, ContextConfig, TaskProviderOverride, PhaseOverride } from './schema.js';\nimport { setAtPath } from '../utils/dot-path.js';\n\n/**\n * Set a value at a dot-separated path, returning a new config object.\n * Creates intermediate objects along the path as needed.\n */\nexport function withValue(config: MycoConfig, dotPath: string, value: unknown): MycoConfig {\n const clone = structuredClone(config) as MycoConfig & Record<string, unknown>;\n setAtPath(clone, dotPath, value);\n return clone as MycoConfig;\n}\n\n/** Provider override shape used in task config updates. Null means delete. */\ninterface ProviderInput {\n type: 'anthropic' | 'ollama' | 'lmstudio';\n model?: string;\n base_url?: string;\n context_length?: number;\n}\n\n/** Phase override input. Null fields mean delete. */\ninterface PhaseInput {\n provider?: ProviderInput | null;\n model?: string | null;\n maxTurns?: number | null;\n}\n\n/** Input shape for task config updates. Null values mean \"delete this field\". */\nexport interface TaskConfigUpdate {\n provider?: ProviderInput | null;\n model?: string | null;\n maxTurns?: number | null;\n timeoutSeconds?: number | null;\n phases?: Record<string, PhaseInput | null> | null;\n schedule?: { enabled?: boolean; intervalSeconds?: number; runIn?: ('active' | 'idle' | 'sleep')[] } | null;\n}\n\n/**\n * Apply partial task config updates, returning a new config object.\n * Null values delete fields. Empty task entries and phase maps are cleaned up.\n */\nexport function withTaskConfig(\n config: MycoConfig,\n taskId: string,\n update: TaskConfigUpdate,\n): MycoConfig {\n const tasks = { ...(config.agent.tasks ?? {}) };\n const entry: TaskProviderOverride = { ...(tasks[taskId] ?? {}) };\n\n // Apply top-level fields\n if ('provider' in update) {\n if (update.provider === null) {\n delete entry.provider;\n } else if (update.provider !== undefined) {\n entry.provider = { ...update.provider };\n }\n }\n\n if ('model' in update) {\n if (update.model === null) delete entry.model;\n else if (update.model !== undefined) entry.model = update.model;\n }\n\n if ('maxTurns' in update) {\n if (update.maxTurns === null) delete entry.maxTurns;\n else if (update.maxTurns !== undefined) entry.maxTurns = update.maxTurns;\n }\n\n if ('timeoutSeconds' in update) {\n if (update.timeoutSeconds === null) delete entry.timeoutSeconds;\n else if (update.timeoutSeconds !== undefined) entry.timeoutSeconds = update.timeoutSeconds;\n }\n\n // Handle schedule\n if ('schedule' in update) {\n if (update.schedule === null) {\n delete entry.schedule;\n } else if (update.schedule !== undefined) {\n entry.schedule = { ...entry.schedule, ...update.schedule };\n }\n }\n\n // Apply phase overrides\n if ('phases' in update) {\n if (update.phases === null) {\n delete entry.phases;\n } else if (update.phases !== undefined) {\n const phases: Record<string, PhaseOverride> = { ...(entry.phases ?? {}) };\n\n for (const [phaseName, phaseValue] of Object.entries(update.phases)) {\n if (phaseValue === null) {\n delete phases[phaseName];\n } else {\n const pe: PhaseOverride = { ...(phases[phaseName] ?? {}) };\n if ('provider' in phaseValue) {\n if (phaseValue.provider === null) delete pe.provider;\n else if (phaseValue.provider !== undefined) pe.provider = { ...phaseValue.provider };\n }\n if ('model' in phaseValue) {\n if (phaseValue.model === null) delete pe.model;\n else if (phaseValue.model !== undefined) pe.model = phaseValue.model;\n }\n if ('maxTurns' in phaseValue) {\n if (phaseValue.maxTurns === null) delete pe.maxTurns;\n else if (phaseValue.maxTurns !== undefined) pe.maxTurns = phaseValue.maxTurns;\n }\n phases[phaseName] = pe;\n }\n }\n\n // Clean up empty phases map\n if (Object.keys(phases).length === 0) {\n delete entry.phases;\n } else {\n entry.phases = phases;\n }\n }\n }\n\n // Clean up empty task entry\n if (Object.keys(entry).length === 0) {\n delete tasks[taskId];\n } else {\n tasks[taskId] = entry;\n }\n\n return {\n ...config,\n agent: {\n ...config.agent,\n tasks: Object.keys(tasks).length > 0 ? tasks : undefined,\n },\n };\n}\n\n/**\n * Merge partial embedding updates into config, returning a new config object.\n */\nexport function withEmbedding(\n config: MycoConfig,\n updates: Partial<EmbeddingProviderConfig>,\n): MycoConfig {\n return {\n ...config,\n embedding: { ...config.embedding, ...updates },\n };\n}\n\n/**\n * Merge partial context injection updates into config, returning a new config object.\n */\nexport function withContext(\n config: MycoConfig,\n updates: Partial<ContextConfig>,\n): MycoConfig {\n return {\n ...config,\n context: { ...config.context, ...updates },\n };\n}\n"],"mappings":"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","names":[]}