@agenthippo-ai/agenthippo 0.1.0

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 (480) hide show
  1. package/LICENSE +21 -0
  2. package/bin/agenthippo.js +2 -0
  3. package/out/agentpack/github.js +547 -0
  4. package/out/agentpack/index.js +66 -0
  5. package/out/agentpack/install.js +297 -0
  6. package/out/agentpack/manager.js +441 -0
  7. package/out/agentpack/manifest.js +135 -0
  8. package/out/agentpack/paths.js +475 -0
  9. package/out/agentpack/publish.js +248 -0
  10. package/out/agentpack/stores.js +554 -0
  11. package/out/agentpack/types.js +5 -0
  12. package/out/analytics/dashboard/artifactManager.js +439 -0
  13. package/out/analytics/dashboard/fleetDisplayUtils.js +159 -0
  14. package/out/analytics/dashboard/fleetOrchestrator.js +142 -0
  15. package/out/analytics/dashboard/fleetRegistry.js +79 -0
  16. package/out/analytics/dashboard/fleetRowDefaults.js +87 -0
  17. package/out/analytics/dashboard/fleetScriptAdapter.js +146 -0
  18. package/out/analytics/dashboard/fleetStatusUtils.js +30 -0
  19. package/out/analytics/dashboard/fleetTitleUtils.js +68 -0
  20. package/out/analytics/dashboard/fleetTypes.js +29 -0
  21. package/out/analytics/dashboard/index.js +17 -0
  22. package/out/analytics/dashboard/indexingService.js +422 -0
  23. package/out/analytics/dashboard/localFleetAdapter.js +222 -0
  24. package/out/analytics/dashboard/performanceMonitor.js +76 -0
  25. package/out/analytics/dashboard/processManager.js +209 -0
  26. package/out/analytics/dashboard/provider.js +3885 -0
  27. package/out/analytics/dashboard/queryRunner.js +994 -0
  28. package/out/analytics/dashboard/refreshPolicy.js +16 -0
  29. package/out/analytics/dashboard/scheduledFleetFingerprint.js +22 -0
  30. package/out/analytics/dashboard/scheduledFleetStorage.js +241 -0
  31. package/out/analytics/dashboard/scheduledFleetUtils.js +22 -0
  32. package/out/analytics/dashboard/sessionEventFleetStats.js +118 -0
  33. package/out/analytics/dashboard/sessionJsonlFleet.js +204 -0
  34. package/out/analytics/dashboard/teamViewDiscovery.js +106 -0
  35. package/out/analytics/dashboard/viewsConfigMerge.js +221 -0
  36. package/out/analytics/dashboard/webview/analytics-panel.html +155 -0
  37. package/out/analytics/dashboard/webview/codicon.ttf +0 -0
  38. package/out/analytics/dashboard/webview/index.html +78 -0
  39. package/out/analytics/dashboard/webview/panel.js +2295 -0
  40. package/out/analytics/dashboard/webview/runtime-bridge.js +82 -0
  41. package/out/analytics/dashboard/webview/styles.css +1009 -0
  42. package/out/analytics/lifecycle.js +43 -0
  43. package/out/analytics/service.js +1279 -0
  44. package/out/analytics/sessionEvents.js +231 -0
  45. package/out/auth/bootstrapApply.js +72 -0
  46. package/out/auth/brokerClient.js +126 -0
  47. package/out/auth/callbackUri.js +59 -0
  48. package/out/auth/callbackUriParse.js +59 -0
  49. package/out/auth/githubStoreGate.js +70 -0
  50. package/out/auth/loopbackAuthServer.js +165 -0
  51. package/out/auth/pkce.js +9 -0
  52. package/out/auth/provider.js +239 -0
  53. package/out/auth/sessionStore.js +193 -0
  54. package/out/auth/signInFlow.js +182 -0
  55. package/out/auth/types.js +12 -0
  56. package/out/auth-broker/config.js +62 -0
  57. package/out/auth-broker/jwt.js +105 -0
  58. package/out/auth-broker/pkce.js +57 -0
  59. package/out/auth-broker/server.js +539 -0
  60. package/out/auth-broker/store.js +273 -0
  61. package/out/base-prompts/gateway/openclaw-starter.json5 +144 -0
  62. package/out/base-prompts/gateway/slack-socket-mode-minimal.manifest.json +44 -0
  63. package/out/base-prompts/mcp-servers/.mcp.json +13 -0
  64. package/out/base-prompts/mcp-servers/langfuse-analytics/README.md +142 -0
  65. package/out/base-prompts/mcp-servers/langfuse-analytics/mcp.json +13 -0
  66. package/out/base-prompts/mcp-servers/langfuse-analytics/package-lock.json +1192 -0
  67. package/out/base-prompts/mcp-servers/langfuse-analytics/package.json +23 -0
  68. package/out/base-prompts/mcp-servers/langfuse-analytics/scripts/run-with-env.sh +12 -0
  69. package/out/base-prompts/mcp-servers/langfuse-analytics/scripts/sdk-test.js +102 -0
  70. package/out/base-prompts/mcp-servers/langfuse-analytics/scripts/simple-test.js +137 -0
  71. package/out/base-prompts/mcp-servers/langfuse-analytics/scripts/test-client.js +118 -0
  72. package/out/base-prompts/mcp-servers/langfuse-analytics/scripts/test-server.js +209 -0
  73. package/out/base-prompts/mcp-servers/langfuse-analytics/src/client.ts +431 -0
  74. package/out/base-prompts/mcp-servers/langfuse-analytics/src/index.ts +270 -0
  75. package/out/base-prompts/mcp-servers/langfuse-analytics/tsconfig.json +18 -0
  76. package/out/base-prompts/rules/AGENTS.md +204 -0
  77. package/out/base-prompts/skills/agent-browser/SKILL.md +102 -0
  78. package/out/base-prompts/skills/agent-builder/SKILL.md +73 -0
  79. package/out/base-prompts/skills/agent-builder/references/agent-pack.md +63 -0
  80. package/out/base-prompts/skills/agent-builder/references/crewai.md +66 -0
  81. package/out/base-prompts/skills/agent-builder/references/langchain.md +60 -0
  82. package/out/base-prompts/skills/agent-builder/references/raw-sdk.md +73 -0
  83. package/out/base-prompts/skills/agent-engine-creator/SKILL.md +586 -0
  84. package/out/base-prompts/skills/agent-engine-installer/SKILL.md +106 -0
  85. package/out/base-prompts/skills/agent-engine-installer/scripts/install-engine.mjs +259 -0
  86. package/out/base-prompts/skills/agent-engine-installer/scripts/install-engine.ps1 +189 -0
  87. package/out/base-prompts/skills/agenthippo-cli/SKILL.md +69 -0
  88. package/out/base-prompts/skills/agenthippo-cli/scripts/install.sh +63 -0
  89. package/out/base-prompts/skills/agentpack-creator/SKILL.md +375 -0
  90. package/out/base-prompts/skills/agentpack-creator/references/agent-templates.md +261 -0
  91. package/out/base-prompts/skills/agentpack-creator/references/agents-md-guide.md +195 -0
  92. package/out/base-prompts/skills/agentpack-creator/scripts/init_pack.py +391 -0
  93. package/out/base-prompts/skills/agentpack-creator/scripts/init_pack_test.py +294 -0
  94. package/out/base-prompts/skills/agentpack-testbench/SKILL.md +135 -0
  95. package/out/base-prompts/skills/algorithmic-art/LICENSE.txt +202 -0
  96. package/out/base-prompts/skills/algorithmic-art/SKILL.md +405 -0
  97. package/out/base-prompts/skills/algorithmic-art/templates/generator_template.js +223 -0
  98. package/out/base-prompts/skills/algorithmic-art/templates/viewer.html +599 -0
  99. package/out/base-prompts/skills/debug-agent/SKILL.md +83 -0
  100. package/out/base-prompts/skills/desktop-automation/SKILL.md +166 -0
  101. package/out/base-prompts/skills/desktop-automation/references/agenthippo-ui-recipes.md +441 -0
  102. package/out/base-prompts/skills/desktop-automation/scripts/windows-desktop-uia.psm1 +280 -0
  103. package/out/base-prompts/skills/docx/LICENSE.txt +30 -0
  104. package/out/base-prompts/skills/docx/SKILL.md +197 -0
  105. package/out/base-prompts/skills/docx/docx-js.md +350 -0
  106. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
  107. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
  108. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
  109. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
  110. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
  111. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
  112. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
  113. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
  114. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
  115. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
  116. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
  117. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
  118. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
  119. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
  120. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
  121. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
  122. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
  123. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
  124. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
  125. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
  126. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
  127. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
  128. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
  129. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
  130. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
  131. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
  132. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
  133. package/out/base-prompts/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
  134. package/out/base-prompts/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
  135. package/out/base-prompts/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
  136. package/out/base-prompts/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
  137. package/out/base-prompts/skills/docx/ooxml/schemas/mce/mc.xsd +75 -0
  138. package/out/base-prompts/skills/docx/ooxml/schemas/microsoft/wml-2010.xsd +560 -0
  139. package/out/base-prompts/skills/docx/ooxml/schemas/microsoft/wml-2012.xsd +67 -0
  140. package/out/base-prompts/skills/docx/ooxml/schemas/microsoft/wml-2018.xsd +14 -0
  141. package/out/base-prompts/skills/docx/ooxml/schemas/microsoft/wml-cex-2018.xsd +20 -0
  142. package/out/base-prompts/skills/docx/ooxml/schemas/microsoft/wml-cid-2016.xsd +13 -0
  143. package/out/base-prompts/skills/docx/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
  144. package/out/base-prompts/skills/docx/ooxml/schemas/microsoft/wml-symex-2015.xsd +8 -0
  145. package/out/base-prompts/skills/docx/ooxml/scripts/pack.py +159 -0
  146. package/out/base-prompts/skills/docx/ooxml/scripts/unpack.py +29 -0
  147. package/out/base-prompts/skills/docx/ooxml/scripts/validate.py +69 -0
  148. package/out/base-prompts/skills/docx/ooxml/scripts/validation/__init__.py +15 -0
  149. package/out/base-prompts/skills/docx/ooxml/scripts/validation/base.py +951 -0
  150. package/out/base-prompts/skills/docx/ooxml/scripts/validation/docx.py +274 -0
  151. package/out/base-prompts/skills/docx/ooxml/scripts/validation/pptx.py +315 -0
  152. package/out/base-prompts/skills/docx/ooxml/scripts/validation/redlining.py +279 -0
  153. package/out/base-prompts/skills/docx/ooxml.md +610 -0
  154. package/out/base-prompts/skills/docx/scripts/__init__.py +1 -0
  155. package/out/base-prompts/skills/docx/scripts/document.py +1276 -0
  156. package/out/base-prompts/skills/docx/scripts/templates/comments.xml +3 -0
  157. package/out/base-prompts/skills/docx/scripts/templates/commentsExtended.xml +3 -0
  158. package/out/base-prompts/skills/docx/scripts/templates/commentsExtensible.xml +3 -0
  159. package/out/base-prompts/skills/docx/scripts/templates/commentsIds.xml +3 -0
  160. package/out/base-prompts/skills/docx/scripts/templates/people.xml +3 -0
  161. package/out/base-prompts/skills/docx/scripts/utilities.py +374 -0
  162. package/out/base-prompts/skills/gateway-outbound/SKILL.md +136 -0
  163. package/out/base-prompts/skills/mcp-builder/LICENSE.txt +202 -0
  164. package/out/base-prompts/skills/mcp-builder/SKILL.md +328 -0
  165. package/out/base-prompts/skills/mcp-builder/reference/evaluation.md +602 -0
  166. package/out/base-prompts/skills/mcp-builder/reference/mcp_best_practices.md +249 -0
  167. package/out/base-prompts/skills/mcp-builder/reference/node_mcp_server.md +970 -0
  168. package/out/base-prompts/skills/mcp-builder/reference/python_mcp_server.md +719 -0
  169. package/out/base-prompts/skills/mcp-builder/scripts/connections.py +151 -0
  170. package/out/base-prompts/skills/mcp-builder/scripts/evaluation.py +373 -0
  171. package/out/base-prompts/skills/mcp-builder/scripts/example_evaluation.xml +22 -0
  172. package/out/base-prompts/skills/mcp-builder/scripts/requirements.txt +2 -0
  173. package/out/base-prompts/skills/openclaw/SKILL.md +406 -0
  174. package/out/base-prompts/skills/openclaw/scripts/run-smoke.sh +61 -0
  175. package/out/base-prompts/skills/pdf/LICENSE.txt +30 -0
  176. package/out/base-prompts/skills/pdf/SKILL.md +294 -0
  177. package/out/base-prompts/skills/pdf/forms.md +205 -0
  178. package/out/base-prompts/skills/pdf/reference.md +612 -0
  179. package/out/base-prompts/skills/pdf/scripts/check_bounding_boxes.py +70 -0
  180. package/out/base-prompts/skills/pdf/scripts/check_bounding_boxes_test.py +226 -0
  181. package/out/base-prompts/skills/pdf/scripts/check_fillable_fields.py +12 -0
  182. package/out/base-prompts/skills/pdf/scripts/convert_pdf_to_images.py +35 -0
  183. package/out/base-prompts/skills/pdf/scripts/create_validation_image.py +41 -0
  184. package/out/base-prompts/skills/pdf/scripts/extract_form_field_info.py +152 -0
  185. package/out/base-prompts/skills/pdf/scripts/fill_fillable_fields.py +114 -0
  186. package/out/base-prompts/skills/pdf/scripts/fill_pdf_form_with_annotations.py +108 -0
  187. package/out/base-prompts/skills/pptx/LICENSE.txt +30 -0
  188. package/out/base-prompts/skills/pptx/SKILL.md +484 -0
  189. package/out/base-prompts/skills/pptx/html2pptx.md +625 -0
  190. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
  191. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
  192. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
  193. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
  194. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
  195. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
  196. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
  197. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
  198. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
  199. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
  200. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
  201. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
  202. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
  203. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
  204. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
  205. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
  206. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
  207. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
  208. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
  209. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
  210. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
  211. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
  212. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
  213. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
  214. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
  215. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
  216. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
  217. package/out/base-prompts/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
  218. package/out/base-prompts/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
  219. package/out/base-prompts/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
  220. package/out/base-prompts/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
  221. package/out/base-prompts/skills/pptx/ooxml/schemas/mce/mc.xsd +75 -0
  222. package/out/base-prompts/skills/pptx/ooxml/schemas/microsoft/wml-2010.xsd +560 -0
  223. package/out/base-prompts/skills/pptx/ooxml/schemas/microsoft/wml-2012.xsd +67 -0
  224. package/out/base-prompts/skills/pptx/ooxml/schemas/microsoft/wml-2018.xsd +14 -0
  225. package/out/base-prompts/skills/pptx/ooxml/schemas/microsoft/wml-cex-2018.xsd +20 -0
  226. package/out/base-prompts/skills/pptx/ooxml/schemas/microsoft/wml-cid-2016.xsd +13 -0
  227. package/out/base-prompts/skills/pptx/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
  228. package/out/base-prompts/skills/pptx/ooxml/schemas/microsoft/wml-symex-2015.xsd +8 -0
  229. package/out/base-prompts/skills/pptx/ooxml/scripts/pack.py +159 -0
  230. package/out/base-prompts/skills/pptx/ooxml/scripts/unpack.py +29 -0
  231. package/out/base-prompts/skills/pptx/ooxml/scripts/validate.py +69 -0
  232. package/out/base-prompts/skills/pptx/ooxml/scripts/validation/__init__.py +15 -0
  233. package/out/base-prompts/skills/pptx/ooxml/scripts/validation/base.py +951 -0
  234. package/out/base-prompts/skills/pptx/ooxml/scripts/validation/docx.py +274 -0
  235. package/out/base-prompts/skills/pptx/ooxml/scripts/validation/pptx.py +315 -0
  236. package/out/base-prompts/skills/pptx/ooxml/scripts/validation/redlining.py +279 -0
  237. package/out/base-prompts/skills/pptx/ooxml.md +427 -0
  238. package/out/base-prompts/skills/pptx/scripts/html2pptx.js +984 -0
  239. package/out/base-prompts/skills/pptx/scripts/inventory.py +1020 -0
  240. package/out/base-prompts/skills/pptx/scripts/rearrange.py +231 -0
  241. package/out/base-prompts/skills/pptx/scripts/replace.py +385 -0
  242. package/out/base-prompts/skills/pptx/scripts/thumbnail.py +450 -0
  243. package/out/base-prompts/skills/prompt-lab/SKILL.md +149 -0
  244. package/out/base-prompts/skills/prompt-lab/scripts/query-analytics.sh +2551 -0
  245. package/out/base-prompts/skills/skill-creator/LICENSE.txt +202 -0
  246. package/out/base-prompts/skills/skill-creator/SKILL.md +371 -0
  247. package/out/base-prompts/skills/skill-creator/references/output-patterns.md +82 -0
  248. package/out/base-prompts/skills/skill-creator/references/workflows.md +28 -0
  249. package/out/base-prompts/skills/skill-creator/scripts/init_skill.py +303 -0
  250. package/out/base-prompts/skills/skill-creator/scripts/package_skill.py +110 -0
  251. package/out/base-prompts/skills/skill-creator/scripts/quick_validate.py +95 -0
  252. package/out/base-prompts/skills/skill-installer/SKILL.md +251 -0
  253. package/out/base-prompts/skills/skill-installer/references/store-api.md +219 -0
  254. package/out/base-prompts/skills/spotlight-dashboard/SKILL.md +83 -0
  255. package/out/base-prompts/skills/spotlight-insights/SKILL.md +101 -0
  256. package/out/base-prompts/skills/spotlight-optimize/SKILL.md +93 -0
  257. package/out/base-prompts/skills/spotlight-optimize/scripts/query-analytics.sh +2551 -0
  258. package/out/base-prompts/skills/spotlight-query/SKILL.md +314 -0
  259. package/out/base-prompts/skills/spotlight-query/references/otel-schema.md +163 -0
  260. package/out/base-prompts/skills/spotlight-query/scripts/query-analytics.sh +2551 -0
  261. package/out/base-prompts/skills/spotlight-query/scripts/query-analytics.test.sh +744 -0
  262. package/out/base-prompts/skills/spotlight-query/scripts/test-fixtures/analytics/sessions/sess-test-1.jsonl +14 -0
  263. package/out/base-prompts/skills/spotlight-query/scripts/test-fixtures/analytics/traces/.duckdb-index.json +7 -0
  264. package/out/base-prompts/skills/spotlight-query/scripts/test-fixtures/analytics/traces/sample.jsonl +5 -0
  265. package/out/base-prompts/skills/spotlight-report/SKILL.md +86 -0
  266. package/out/base-prompts/skills/spotlight-visualize/SKILL.md +92 -0
  267. package/out/base-prompts/skills/spotlight-visualize/scripts/query-analytics.sh +2551 -0
  268. package/out/base-prompts/skills/xlsx/LICENSE.txt +30 -0
  269. package/out/base-prompts/skills/xlsx/SKILL.md +289 -0
  270. package/out/base-prompts/skills/xlsx/recalc.py +178 -0
  271. package/out/branding.js +23 -0
  272. package/out/cli/commands/anywhere.js +66 -0
  273. package/out/cli/commands/ask.js +243 -0
  274. package/out/cli/commands/auth-broker.js +24 -0
  275. package/out/cli/commands/auth.js +219 -0
  276. package/out/cli/commands/chat.js +354 -0
  277. package/out/cli/commands/deploy.js +223 -0
  278. package/out/cli/commands/gateway.js +160 -0
  279. package/out/cli/commands/pack.js +220 -0
  280. package/out/cli/commands/probe-custom-engine.js +82 -0
  281. package/out/cli/commands/serve.js +583 -0
  282. package/out/cli/commands/store.js +919 -0
  283. package/out/cli/credential-context.js +113 -0
  284. package/out/cli/deploy-init.js +2087 -0
  285. package/out/cli/deps.js +238 -0
  286. package/out/cli/index.js +390 -0
  287. package/out/cli/pack-loader.js +376 -0
  288. package/out/cli/run-custom-engine-runner.js +318 -0
  289. package/out/cli/runtime.js +409 -0
  290. package/out/cli/serve-hardening.js +325 -0
  291. package/out/cli/state-sync.js +135 -0
  292. package/out/cli/utils.js +220 -0
  293. package/out/cli/vscode-shim.js +86 -0
  294. package/out/engines/base.js +159 -0
  295. package/out/engines/claude.js +1215 -0
  296. package/out/engines/codex.js +759 -0
  297. package/out/engines/custom.js +632 -0
  298. package/out/engines/customEngineIpc.js +95 -0
  299. package/out/engines/customEngineRunnerEmbed.js +107 -0
  300. package/out/engines/customRegistry.js +274 -0
  301. package/out/engines/embeddedCustomEngineRunner.generated.js +9 -0
  302. package/out/engines/engineApiKey.js +138 -0
  303. package/out/engines/factory.js +45 -0
  304. package/out/engines/install.js +372 -0
  305. package/out/engines/persistentEngineWorker.js +320 -0
  306. package/out/engines/publish.js +341 -0
  307. package/out/engines/types.js +17 -0
  308. package/out/examples/run-headless-repl.js +291 -0
  309. package/out/examples/run-headless.js +187 -0
  310. package/out/extension.js +4472 -0
  311. package/out/gateway/bridge.js +805 -0
  312. package/out/gateway/cli-agent-id.js +36 -0
  313. package/out/gateway/client.js +303 -0
  314. package/out/gateway/client.test.js +395 -0
  315. package/out/gateway/gateway-scope-errors.js +14 -0
  316. package/out/gateway/gatewayBackup.js +76 -0
  317. package/out/gateway/gatewayRouteIndex.js +371 -0
  318. package/out/gateway/gatewaySessionStore.js +243 -0
  319. package/out/gateway/index.js +31 -0
  320. package/out/gateway/lifecycle.js +3056 -0
  321. package/out/gateway/openclaw-channel-routing.js +317 -0
  322. package/out/gateway/outboundHandles.js +373 -0
  323. package/out/gateway/outboundPaths.js +72 -0
  324. package/out/gateway/outboundSend.js +254 -0
  325. package/out/gateway/protocol.js +48 -0
  326. package/out/gateway/resetMappings.js +52 -0
  327. package/out/gateway/service.js +1244 -0
  328. package/out/gateway/sessionRouting.js +77 -0
  329. package/out/headless/index.js +505 -0
  330. package/out/headless/sync.js +473 -0
  331. package/out/mcp/publish.js +143 -0
  332. package/out/mcp.js +597 -0
  333. package/out/permissions.js +108 -0
  334. package/out/permissions.mapping.yaml +68 -0
  335. package/out/registry/index.js +332 -0
  336. package/out/remote/handleRemoteChat.js +122 -0
  337. package/out/remote/remotePackStatusBar.js +103 -0
  338. package/out/remote/serveClient.js +216 -0
  339. package/out/remoteSkills.js +191 -0
  340. package/out/runtime/index.js +32 -0
  341. package/out/runtime/interfaces/config-provider.js +32 -0
  342. package/out/runtime/interfaces/edit-tracker.js +22 -0
  343. package/out/runtime/interfaces/index.js +29 -0
  344. package/out/runtime/interfaces/logger.js +40 -0
  345. package/out/runtime/interfaces/permission-handler.js +66 -0
  346. package/out/runtime/interfaces/session-store.js +342 -0
  347. package/out/runtime/vscode/index.js +20 -0
  348. package/out/runtime/vscode/vscode-config-provider.js +77 -0
  349. package/out/runtime/vscode/vscode-edit-tracker.js +120 -0
  350. package/out/runtime/vscode/vscode-logger.js +30 -0
  351. package/out/runtime/vscode/vscode-permission-handler.js +72 -0
  352. package/out/runtime/vscode/vscode-session-store.js +22 -0
  353. package/out/scripts/agentide-litellm.ps1 +501 -0
  354. package/out/scripts/agentide-litellm.sh +700 -0
  355. package/out/scripts/agentide_litellm_hooks_v1.py +604 -0
  356. package/out/scripts/analytics/agent-fleet-widget.js +954 -0
  357. package/out/scripts/analytics/agentide-litellm-otel.config.yaml +160 -0
  358. package/out/scripts/analytics/default-seed-scripts.json +12 -0
  359. package/out/scripts/analytics/duckdbLocator.js +29 -0
  360. package/out/scripts/analytics/fleet-local-adapter.js +246 -0
  361. package/out/scripts/analytics/fleet-workflow.sh +263 -0
  362. package/out/scripts/analytics/generate-default-charts.py +685 -0
  363. package/out/scripts/analytics/hotspot-analysis-widget.js +909 -0
  364. package/out/scripts/analytics/index-traces.py +1394 -0
  365. package/out/scripts/analytics/install-analytics.sh +286 -0
  366. package/out/scripts/analytics/live-fleet-4-collector.cjs +315 -0
  367. package/out/scripts/analytics/live-fleet-4-daemon.mjs +134 -0
  368. package/out/scripts/analytics/live-fleet-4-widget.cjs +433 -0
  369. package/out/scripts/analytics/otel-collector.yaml +55 -0
  370. package/out/scripts/analytics/query-analytics.ps1 +396 -0
  371. package/out/scripts/analytics/query-analytics.sh +2551 -0
  372. package/out/scripts/analytics/session-comparison-widget.js +1468 -0
  373. package/out/scripts/analytics/session-timeline-widget.js +2137 -0
  374. package/out/scripts/analytics/setup-analytics.ps1 +432 -0
  375. package/out/scripts/analytics/setup-analytics.sh +854 -0
  376. package/out/scripts/analytics/start-analytics.ps1 +169 -0
  377. package/out/scripts/analytics/start-analytics.sh +323 -0
  378. package/out/scripts/analytics/token-cost-flamegraph-widget.js +954 -0
  379. package/out/scripts/analytics/token-cost-trend-widget.js +1062 -0
  380. package/out/scripts/analytics/workflows-collector.cjs +315 -0
  381. package/out/scripts/analytics/workflows-daemon.mjs +186 -0
  382. package/out/scripts/analytics/workflows-widget.cjs +433 -0
  383. package/out/scripts/analytics/wow-model-widget.js +191 -0
  384. package/out/scripts/gateway/Dockerfile.serve +45 -0
  385. package/out/scripts/gateway/docker-serve.sh +248 -0
  386. package/out/scripts/gateway/setup-gateway-server.ps1 +953 -0
  387. package/out/scripts/gateway/setup-gateway-server.sh +935 -0
  388. package/out/scripts/gateway/setup-gateway.ps1 +1813 -0
  389. package/out/scripts/gateway/setup-gateway.sh +1541 -0
  390. package/out/scripts/setup-cli.ps1 +86 -0
  391. package/out/scripts/setup-cli.sh +117 -0
  392. package/out/scripts/tools-manifest.json +83 -0
  393. package/out/scripts/vendor-tools.mjs +234 -0
  394. package/out/session.js +272 -0
  395. package/out/shared/agentEngines.js +170 -0
  396. package/out/shared/index.js +109 -0
  397. package/out/shared/models.js +235 -0
  398. package/out/shared/providers.js +202 -0
  399. package/out/skills/publish.js +178 -0
  400. package/out/spotlight/install.js +339 -0
  401. package/out/spotlight/publish.js +544 -0
  402. package/out/spotlight/yaml.js +31 -0
  403. package/out/store/github/setup.js +235 -0
  404. package/out/store/github/templates/build-index.ts +339 -0
  405. package/out/store/github/templates/build-index.yml +97 -0
  406. package/out/store/github/templates/package.json +11 -0
  407. package/out/store/index.js +755 -0
  408. package/out/store/oramaIndex.js +884 -0
  409. package/out/store/shared.js +241 -0
  410. package/out/sync.js +1827 -0
  411. package/out/terminalLogger.js +358 -0
  412. package/out/test/agentEnvFile.test.js +97 -0
  413. package/out/test/agentYaml.test.js +373 -0
  414. package/out/test/agentpack.test.js +639 -0
  415. package/out/test/analytics-lifecycle.test.js +174 -0
  416. package/out/test/analytics.test.js +524 -0
  417. package/out/test/auth-bootstrapApply.test.js +102 -0
  418. package/out/test/auth-broker.test.js +434 -0
  419. package/out/test/auth-callback.test.js +62 -0
  420. package/out/test/branding.test.js +229 -0
  421. package/out/test/cli-agent-id.test.js +55 -0
  422. package/out/test/credential-context.test.js +105 -0
  423. package/out/test/customEngineInstall.test.js +100 -0
  424. package/out/test/customRegistry.test.js +122 -0
  425. package/out/test/deploy-init.test.js +532 -0
  426. package/out/test/deps-auto-install.test.js +81 -0
  427. package/out/test/engine-integration.test.js +297 -0
  428. package/out/test/enginePaths.test.js +195 -0
  429. package/out/test/enginePublish.test.js +82 -0
  430. package/out/test/fleetDisplayUtils.test.js +96 -0
  431. package/out/test/gateway-client.test.js +381 -0
  432. package/out/test/gateway-lifecycle.test.js +608 -0
  433. package/out/test/gateway-outbound.test.js +531 -0
  434. package/out/test/gateway-reset-mappings.test.js +221 -0
  435. package/out/test/gateway-route-index.test.js +235 -0
  436. package/out/test/gateway-session-routing.test.js +47 -0
  437. package/out/test/gateway-session-store.test.js +216 -0
  438. package/out/test/github.test.js +232 -0
  439. package/out/test/headless-sync.test.js +279 -0
  440. package/out/test/helpers/manifest-fixtures.js +27 -0
  441. package/out/test/mcp-obo.test.js +82 -0
  442. package/out/test/mcp.test.js +307 -0
  443. package/out/test/openclaw-channel-routing.test.js +191 -0
  444. package/out/test/pathSafety.test.js +63 -0
  445. package/out/test/permissions.test.js +175 -0
  446. package/out/test/persistent-engine-worker.test.js +220 -0
  447. package/out/test/refreshPolicy.test.js +60 -0
  448. package/out/test/registry.test.js +351 -0
  449. package/out/test/runtime.test.js +349 -0
  450. package/out/test/scheduledFleetStorage.test.js +72 -0
  451. package/out/test/serve-hardening.test.js +192 -0
  452. package/out/test/serveClient.test.js +173 -0
  453. package/out/test/sessionJsonlFleet.test.js +78 -0
  454. package/out/test/shared.test.js +173 -0
  455. package/out/test/skills-mcp.test.js +299 -0
  456. package/out/test/spotlightPublish.test.js +299 -0
  457. package/out/test/spotlightYaml.test.js +58 -0
  458. package/out/test/state-sync-peer.test.js +74 -0
  459. package/out/test/store.test.js +331 -0
  460. package/out/test/stores.test.js +504 -0
  461. package/out/test/sync-switch.test.js +251 -0
  462. package/out/test/teamViewDiscovery.test.js +90 -0
  463. package/out/test/toolPath.test.js +120 -0
  464. package/out/test/utils.test.js +308 -0
  465. package/out/test/viewsConfigMerge.test.js +180 -0
  466. package/out/test/windows-command-line.test.js +75 -0
  467. package/out/utils/agentEnvFile.js +131 -0
  468. package/out/utils/binary.js +175 -0
  469. package/out/utils/index.js +37 -0
  470. package/out/utils/network.js +74 -0
  471. package/out/utils/pathSafety.js +60 -0
  472. package/out/utils/paths.js +116 -0
  473. package/out/utils/pid.js +180 -0
  474. package/out/utils/pythonRunner.js +258 -0
  475. package/out/utils/resolveNodeBinary.js +168 -0
  476. package/out/utils/toolPath.js +83 -0
  477. package/out/utils/windowsCommandLine.js +29 -0
  478. package/out/utils.js +915 -0
  479. package/out/workspaceFolders.js +59 -0
  480. package/package.json +41 -0
package/out/sync.js ADDED
@@ -0,0 +1,1827 @@
1
+ "use strict";
2
+ /*---------------------------------------------------------------------------------------------
3
+ * Copyright (c) AgentHippo.ai. All rights reserved.
4
+ *--------------------------------------------------------------------------------------------*/
5
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
6
+ if (k2 === undefined) k2 = k;
7
+ var desc = Object.getOwnPropertyDescriptor(m, k);
8
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
9
+ desc = { enumerable: true, get: function() { return m[k]; } };
10
+ }
11
+ Object.defineProperty(o, k2, desc);
12
+ }) : (function(o, m, k, k2) {
13
+ if (k2 === undefined) k2 = k;
14
+ o[k2] = m[k];
15
+ }));
16
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
17
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
18
+ }) : function(o, v) {
19
+ o["default"] = v;
20
+ });
21
+ var __importStar = (this && this.__importStar) || (function () {
22
+ var ownKeys = function(o) {
23
+ ownKeys = Object.getOwnPropertyNames || function (o) {
24
+ var ar = [];
25
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
26
+ return ar;
27
+ };
28
+ return ownKeys(o);
29
+ };
30
+ return function (mod) {
31
+ if (mod && mod.__esModule) return mod;
32
+ var result = {};
33
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
34
+ __setModuleDefault(result, mod);
35
+ return result;
36
+ };
37
+ })();
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.SyncService = void 0;
40
+ const vscode = __importStar(require("vscode"));
41
+ const fs_1 = require("fs");
42
+ const path = __importStar(require("path"));
43
+ const os = __importStar(require("os"));
44
+ const utils_1 = require("./utils");
45
+ const remoteSkills_1 = require("./remoteSkills");
46
+ const branding_1 = require("./branding");
47
+ const paths_1 = require("./agentpack/paths");
48
+ const manifest_1 = require("./agentpack/manifest");
49
+ const mcp_1 = require("./mcp");
50
+ const customRegistry_1 = require("./engines/customRegistry");
51
+ // -----------------------------------------------------------------------------
52
+ // Constants
53
+ // -----------------------------------------------------------------------------
54
+ const AGENTIDE_DERIVED_DIR_README = 'AGENTIDE_README.md';
55
+ const AGENTIDE_CONTEXT_FILE = 'context.md';
56
+ const AGENTIDE_MANIFEST_FILE = 'agentide.manifest.json';
57
+ const AGENTIDE_INSTALLED_AGENTS_FILE = 'installed-agents.json';
58
+ const AGENTIDE_MANIFEST_DEBOUNCE_MS = 2000;
59
+ const AGENTIDE_AGENT_YAML = 'agent.yaml';
60
+ const AGENTIDE_AGENTS_DIR = 'agents';
61
+ const AGENTIDE_DEFAULT_AGENT_ID = 'default';
62
+ const AGENTIDE_DIR_NAME = paths_1.AGENT_CONSTANTS.AGENTIDE_DIR_NAME;
63
+ // Use unified AGENT_ENGINES from shared
64
+ const shared_1 = require("./shared");
65
+ function getConfiguredAdditionalEngineDir() {
66
+ return vscode.workspace
67
+ .getConfiguration('agentide')
68
+ .get('additionalEngineDir');
69
+ }
70
+ function getDerivedEngineSyncTargets(workspaceRoot) {
71
+ const builtins = Object.values(shared_1.AGENT_ENGINES).map(engine => ({
72
+ id: engine.id,
73
+ displayName: engine.displayName,
74
+ workspaceDir: engine.workspaceDir,
75
+ rulesFile: engine.rulesFile,
76
+ managedPaths: engine.managedPaths,
77
+ includeSkills: true,
78
+ includeRules: true,
79
+ }));
80
+ const custom = (0, customRegistry_1.discoverCustomEngineManifests)(workspaceRoot, getConfiguredAdditionalEngineDir())
81
+ .filter(manifest => manifest.workspaceSync?.enabled)
82
+ .map(manifest => ({
83
+ id: manifest.id,
84
+ displayName: manifest.displayName ?? manifest.id,
85
+ workspaceDir: manifest.workspaceSync.dir,
86
+ rulesFile: manifest.workspaceSync.rulesFile,
87
+ includeSkills: manifest.workspaceSync.includeSkills,
88
+ includeRules: manifest.workspaceSync.includeRules,
89
+ }));
90
+ return [...builtins, ...custom];
91
+ }
92
+ function getSkillDirNameFromRelPath(relPath) {
93
+ const normalized = relPath.replace(/\\/g, '/');
94
+ const parts = normalized.split('/').filter(Boolean);
95
+ const skillsIndex = parts.indexOf('skills');
96
+ if (skillsIndex !== -1 && parts.length > skillsIndex + 1) {
97
+ return parts[skillsIndex + 1];
98
+ }
99
+ // Common case: skills/<dir>/SKILL.md
100
+ if (parts.length >= 3 && parts[0] === 'skills') {
101
+ return parts[1];
102
+ }
103
+ return undefined;
104
+ }
105
+ /**
106
+ * Parse YAML front matter from a SKILL.md file.
107
+ * Expects format:
108
+ * ```
109
+ * ---
110
+ * name: skill-name
111
+ * description: A description of the skill
112
+ * ---
113
+ * # Content...
114
+ * ```
115
+ */
116
+ function parseSkillYamlHeader(content) {
117
+ const result = {};
118
+ // Check for YAML front matter (starts with ---)
119
+ if (!content.startsWith('---')) {
120
+ return result;
121
+ }
122
+ // Find the closing ---
123
+ const lines = content.split('\n');
124
+ let endIndex = -1;
125
+ for (let i = 1; i < lines.length; i++) {
126
+ if (lines[i].trim() === '---') {
127
+ endIndex = i;
128
+ break;
129
+ }
130
+ }
131
+ if (endIndex === -1) {
132
+ return result;
133
+ }
134
+ // Parse YAML content (simple key: value parsing, no full YAML library needed)
135
+ for (let i = 1; i < endIndex; i++) {
136
+ const line = lines[i];
137
+ const colonIndex = line.indexOf(':');
138
+ if (colonIndex === -1) {
139
+ continue;
140
+ }
141
+ const key = line.substring(0, colonIndex).trim().toLowerCase();
142
+ let value = line.substring(colonIndex + 1).trim();
143
+ // Remove surrounding quotes if present
144
+ if ((value.startsWith('"') && value.endsWith('"')) ||
145
+ (value.startsWith('\'') && value.endsWith('\''))) {
146
+ value = value.slice(1, -1);
147
+ }
148
+ if (key === 'name' && value) {
149
+ result.name = value;
150
+ }
151
+ else if (key === 'description' && value) {
152
+ result.description = value;
153
+ }
154
+ }
155
+ return result;
156
+ }
157
+ function parseYaml(content) {
158
+ const yaml = require('js-yaml');
159
+ return yaml.load(content);
160
+ }
161
+ function dumpYaml(doc) {
162
+ const yaml = require('js-yaml');
163
+ return yaml.dump(doc, { lineWidth: 120, noRefs: true });
164
+ }
165
+ async function isDirEmpty(targetDir) {
166
+ const entries = await fs_1.promises.readdir(targetDir);
167
+ return entries.length === 0;
168
+ }
169
+ // -----------------------------------------------------------------------------
170
+ // Sync Service
171
+ // -----------------------------------------------------------------------------
172
+ class SyncService {
173
+ getBundledBasePromptsDir;
174
+ // Cache for tracking .agentide/ directory state to avoid redundant syncs
175
+ lastAgentideSyncSignatureByRoot = new Map();
176
+ agentideSignatureByRoot = new Map();
177
+ agentideSkillsSignatureByRoot = new Map();
178
+ agentideRootUpdateState = new Map();
179
+ agentideWatchersByRoot = new Map();
180
+ // Track active agent turns to avoid syncing while agent is running
181
+ activeAgentTurns = 0;
182
+ // Track active pack per workspace root (for pack-aware file watching)
183
+ activePackIdByRoot = new Map();
184
+ // mtime cache for getActiveAgentManifest — handleChatCommand reads this on every turn just
185
+ // to check spec.remote.url, so avoid a stat+read+parse per message on the hot chat path.
186
+ activeAgentManifestCache = new Map();
187
+ constructor(getBundledBasePromptsDir) {
188
+ this.getBundledBasePromptsDir = getBundledBasePromptsDir;
189
+ }
190
+ /**
191
+ * Set the active pack ID for a workspace root.
192
+ * This is used by the file watcher to determine which pack to sync.
193
+ */
194
+ setActivePackId(workspaceRoot, packId) {
195
+ this.activePackIdByRoot.set(workspaceRoot, packId);
196
+ }
197
+ /**
198
+ * Get the active pack ID for a workspace root.
199
+ */
200
+ getActivePackId(workspaceRoot) {
201
+ return this.activePackIdByRoot.get(workspaceRoot);
202
+ }
203
+ incrementActiveAgentTurns() {
204
+ this.activeAgentTurns++;
205
+ }
206
+ decrementActiveAgentTurns() {
207
+ this.activeAgentTurns = Math.max(0, this.activeAgentTurns - 1);
208
+ }
209
+ getSkillsSignature(workspaceRoot) {
210
+ return this.agentideSkillsSignatureByRoot.get(workspaceRoot);
211
+ }
212
+ async computeWorkspaceAgentideSignature(workspaceRoot, additionalDirectories) {
213
+ const workspaceFolderRoots = vscode.workspace.workspaceFolders?.map(f => f.uri.fsPath) ?? [];
214
+ const additionalWorkspaceRoots = (additionalDirectories ?? []).filter(d => workspaceFolderRoots.includes(d));
215
+ const roots = Array.from(new Set([workspaceRoot, ...additionalWorkspaceRoots].map(r => r.trim()).filter(Boolean)))
216
+ .filter(r => r !== os.homedir());
217
+ const pairs = [];
218
+ for (const root of roots) {
219
+ let sig = this.agentideSignatureByRoot.get(root);
220
+ if (!sig) {
221
+ sig = (await this.readAgentideManifest(root))?.agentideSignature;
222
+ if (sig) {
223
+ this.agentideSignatureByRoot.set(root, sig);
224
+ }
225
+ }
226
+ pairs.push([root, sig ?? 'missing']);
227
+ }
228
+ pairs.sort((a, b) => a[0].localeCompare(b[0]));
229
+ return (0, utils_1.shortSig)(JSON.stringify(pairs));
230
+ }
231
+ // -------------------------------------------------------------------------
232
+ // Managed Resources
233
+ // -------------------------------------------------------------------------
234
+ getManagedResources(codexHome, claudeConfigDir, workspaceRoots) {
235
+ const resources = [];
236
+ const roots = Array.from(new Set(workspaceRoots.map(r => r.trim()).filter(Boolean)))
237
+ .filter(r => r !== os.homedir());
238
+ // Build config dir lookup from EngineConfig
239
+ const configDirs = {
240
+ codexHome,
241
+ claudeConfigDir,
242
+ };
243
+ // Map engine configEnvVar to config key
244
+ const envVarToKey = {
245
+ 'CODEX_HOME': 'codexHome',
246
+ 'CLAUDE_CONFIG_DIR': 'claudeConfigDir',
247
+ };
248
+ // Add engine-specific managed resources from AGENT_ENGINES
249
+ for (const engine of Object.values(shared_1.AGENT_ENGINES)) {
250
+ const configKey = engine.configEnvVar ? envVarToKey[engine.configEnvVar] : undefined;
251
+ const configDir = configKey ? configDirs[configKey] : undefined;
252
+ if (!configDir) {
253
+ continue;
254
+ }
255
+ if (engine.managedPaths?.length) {
256
+ // Engine specifies individual managed paths
257
+ for (const managedPath of engine.managedPaths) {
258
+ const isDir = !managedPath.includes('.');
259
+ resources.push({
260
+ path: path.join(configDir, managedPath),
261
+ type: isDir ? 'directory' : 'generated',
262
+ description: `${engine.displayName} ${managedPath} (managed by AgentIDE)`,
263
+ safeToDelete: true,
264
+ });
265
+ }
266
+ }
267
+ else {
268
+ // Engine manages the entire config dir
269
+ resources.push({
270
+ path: configDir,
271
+ type: 'directory',
272
+ description: `${engine.displayName} config dir (managed by AgentIDE)`,
273
+ safeToDelete: true,
274
+ });
275
+ }
276
+ }
277
+ // Add workspace-level resources
278
+ for (const root of roots) {
279
+ resources.push({
280
+ path: path.join(root, AGENTIDE_DIR_NAME),
281
+ type: 'directory',
282
+ description: 'AgentIDE project rules & skills (source-of-truth)',
283
+ safeToDelete: false,
284
+ });
285
+ // Add derived engine directories from built-in and custom engine manifests.
286
+ for (const engine of getDerivedEngineSyncTargets(root)) {
287
+ resources.push({
288
+ path: path.join(root, engine.workspaceDir),
289
+ type: 'directory',
290
+ description: `${engine.displayName} project folder (derived/overwritten from ${AGENTIDE_DIR_NAME}/)`,
291
+ safeToDelete: true,
292
+ });
293
+ }
294
+ }
295
+ return resources;
296
+ }
297
+ async writeManagedPathsManifest(codexHome, claudeConfigDir, workspaceRoot, additionalDirectories, outputChannel) {
298
+ if (!workspaceRoot || workspaceRoot === os.homedir()) {
299
+ return;
300
+ }
301
+ const agentideDir = this.getAgentideProjectDir(workspaceRoot);
302
+ const manifestPath = path.join(agentideDir, 'paths.md');
303
+ const resources = this.getManagedResources(codexHome, claudeConfigDir, [workspaceRoot, ...(additionalDirectories ?? [])]);
304
+ const lines = [
305
+ '# AgentIDE Managed Resources',
306
+ '',
307
+ 'This file lists all files and directories managed by the AgentIDE extension.',
308
+ '**Auto-generated** - Do not edit manually.',
309
+ '',
310
+ `**Workspace:** \`${workspaceRoot}\``,
311
+ codexHome ? `**Codex Home:** \`${codexHome}\`` : '',
312
+ claudeConfigDir ? `**Claude Config Dir:** \`${claudeConfigDir}\`` : '',
313
+ '',
314
+ '## Resources',
315
+ '',
316
+ '| Path | Type | Safe to Delete | Description |',
317
+ '|------|------|----------------|-------------|',
318
+ ];
319
+ for (const r of resources) {
320
+ const exists = await (0, utils_1.pathExists)(r.path);
321
+ const status = exists ? '[x]' : '[ ]';
322
+ lines.push(`| ${status} \`${r.path}\` | ${r.type} | ${r.safeToDelete ? 'Yes' : 'No'} | ${r.description} |`);
323
+ }
324
+ lines.push('');
325
+ lines.push('## How to Reset');
326
+ lines.push('');
327
+ lines.push('Run the command: **AgentIDE: Clear Managed Resources**');
328
+ lines.push('');
329
+ lines.push('This will:');
330
+ lines.push('- Remove symlinks created by the extension');
331
+ lines.push('- Delete generated files (context.md, config.toml, etc.)');
332
+ lines.push(`- **NOT** delete user-managed rules/skills inside \`${AGENTIDE_DIR_NAME}/\``);
333
+ lines.push('');
334
+ await fs_1.promises.mkdir(agentideDir, { recursive: true });
335
+ await (0, utils_1.writeFileIfChanged)(manifestPath, lines.filter(Boolean).join('\n'));
336
+ outputChannel.appendLine(`[AgentIDE] Wrote manifest: ${manifestPath}`);
337
+ }
338
+ async clearManagedResources(codexHome, claudeConfigDir, workspaceRoot, additionalDirectories, outputChannel) {
339
+ const resources = this.getManagedResources(codexHome, claudeConfigDir, [workspaceRoot ?? '', ...(additionalDirectories ?? [])]);
340
+ const cleared = [];
341
+ const skipped = [];
342
+ const errors = [];
343
+ for (const r of resources) {
344
+ if (!r.safeToDelete) {
345
+ skipped.push(r.path);
346
+ continue;
347
+ }
348
+ try {
349
+ const stat = await fs_1.promises.lstat(r.path);
350
+ if (r.type === 'symlink') {
351
+ if (stat.isSymbolicLink()) {
352
+ await fs_1.promises.unlink(r.path);
353
+ cleared.push(r.path);
354
+ outputChannel.appendLine(`[AgentIDE] Removed symlink: ${r.path}`);
355
+ }
356
+ else {
357
+ skipped.push(r.path);
358
+ outputChannel.appendLine(`[AgentIDE] Skipped (not a symlink): ${r.path}`);
359
+ }
360
+ }
361
+ else if (r.type === 'generated') {
362
+ if (stat.isFile()) {
363
+ await fs_1.promises.unlink(r.path);
364
+ cleared.push(r.path);
365
+ outputChannel.appendLine(`[AgentIDE] Removed generated file: ${r.path}`);
366
+ }
367
+ }
368
+ else if (r.type === 'directory' && r.safeToDelete) {
369
+ if (stat.isDirectory()) {
370
+ await fs_1.promises.rm(r.path, { recursive: true, force: true });
371
+ cleared.push(r.path);
372
+ outputChannel.appendLine(`[AgentIDE] Removed directory: ${r.path}`);
373
+ }
374
+ }
375
+ }
376
+ catch (e) {
377
+ if ((0, utils_1.getErrorCode)(e) !== 'ENOENT') {
378
+ errors.push(`${r.path}: ${e instanceof Error ? e.message : String(e)}`);
379
+ }
380
+ }
381
+ }
382
+ // Reset internal state
383
+ this.lastAgentideSyncSignatureByRoot.clear();
384
+ this.agentideSignatureByRoot.clear();
385
+ this.agentideSkillsSignatureByRoot.clear();
386
+ return { cleared, skipped, errors };
387
+ }
388
+ // -------------------------------------------------------------------------
389
+ // Manifest Management
390
+ // -------------------------------------------------------------------------
391
+ getAgentideProjectDir(workspaceRoot) {
392
+ return (0, paths_1.getAgentideDir)(workspaceRoot);
393
+ }
394
+ /**
395
+ * Get the agents directory path (.agentide/agents/).
396
+ */
397
+ getAgentsDir(workspaceRoot) {
398
+ return path.join(this.getAgentideProjectDir(workspaceRoot), AGENTIDE_AGENTS_DIR);
399
+ }
400
+ /**
401
+ * Get the root directory for a specific agent.
402
+ */
403
+ getAgentRoot(workspaceRoot, agentId) {
404
+ return path.join(this.getAgentsDir(workspaceRoot), agentId);
405
+ }
406
+ /**
407
+ * Get skills directory for an agent.
408
+ */
409
+ getAgentSkillsDir(workspaceRoot, agentId) {
410
+ return path.join(this.getAgentRoot(workspaceRoot, agentId), 'skills');
411
+ }
412
+ /**
413
+ * Get rules directory for an agent.
414
+ */
415
+ getAgentRulesDir(workspaceRoot, agentId) {
416
+ return path.join(this.getAgentRoot(workspaceRoot, agentId), 'rules');
417
+ }
418
+ /**
419
+ * Get AGENTS.md file path for an agent.
420
+ */
421
+ getAgentAgentsFile(workspaceRoot, agentId) {
422
+ return path.join(this.getAgentRoot(workspaceRoot, agentId), 'AGENTS.md');
423
+ }
424
+ /**
425
+ * Get context.md file path for an agent.
426
+ */
427
+ getAgentContextFile(workspaceRoot, agentId) {
428
+ return path.join(this.getAgentRoot(workspaceRoot, agentId), AGENTIDE_CONTEXT_FILE);
429
+ }
430
+ /**
431
+ * Get the active agent ID, defaulting to 'default'.
432
+ */
433
+ getActiveAgentId(workspaceRoot) {
434
+ return this.getActivePackId(workspaceRoot) ?? AGENTIDE_DEFAULT_AGENT_ID;
435
+ }
436
+ async resolveManifestSource(workspaceRoot, agentIdOverride) {
437
+ // All agents (including default) live in .agentide/agents/<agentId>/
438
+ const agentId = agentIdOverride ?? this.getActiveAgentId(workspaceRoot);
439
+ const agentRoot = this.getAgentRoot(workspaceRoot, agentId);
440
+ return {
441
+ agentId,
442
+ isPack: agentId !== AGENTIDE_DEFAULT_AGENT_ID,
443
+ agentRoot,
444
+ agentsFile: this.getAgentAgentsFile(workspaceRoot, agentId),
445
+ contextFile: this.getAgentContextFile(workspaceRoot, agentId),
446
+ rulesDir: this.getAgentRulesDir(workspaceRoot, agentId),
447
+ skillsDir: this.getAgentSkillsDir(workspaceRoot, agentId),
448
+ agentYamlPath: path.join(agentRoot, AGENTIDE_AGENT_YAML),
449
+ };
450
+ }
451
+ async getDataPathsForAgent(workspaceRoot, agentId, outputChannel) {
452
+ const agentRoot = this.getAgentRoot(workspaceRoot, agentId);
453
+ const agentYamlPath = path.join(agentRoot, AGENTIDE_AGENT_YAML);
454
+ if (!(await (0, utils_1.pathExists)(agentYamlPath))) {
455
+ return undefined;
456
+ }
457
+ try {
458
+ const manifest = await (0, manifest_1.loadAgentManifest)(agentYamlPath);
459
+ const data = manifest.spec?.data;
460
+ if (!data) {
461
+ return undefined;
462
+ }
463
+ const sourcePath = (data.sourcePath?.trim() || './data');
464
+ const workspacePath = (data.workspacePath?.trim() || `.${path.sep}${AGENTIDE_DIR_NAME}${path.sep}data${path.sep}${agentId}`)
465
+ .replace(/^(?:\.\/|\.\\)/, '');
466
+ const sourceDir = path.resolve(agentRoot, sourcePath);
467
+ const targetDir = path.resolve(workspaceRoot, workspacePath);
468
+ return { sourceDir, targetDir };
469
+ }
470
+ catch (e) {
471
+ (0, utils_1.logInfo)(outputChannel, `[AgentIDE] Skipping data sync for ${agentId}: failed to parse data config (${e instanceof Error ? e.message : String(e)})`);
472
+ return undefined;
473
+ }
474
+ }
475
+ async seedAgentDataIfNeeded(workspaceRoot, agentId, outputChannel) {
476
+ const dataPaths = await this.getDataPathsForAgent(workspaceRoot, agentId, outputChannel);
477
+ if (!dataPaths) {
478
+ return;
479
+ }
480
+ const { sourceDir, targetDir } = dataPaths;
481
+ if (!(await (0, utils_1.pathExists)(sourceDir))) {
482
+ return;
483
+ }
484
+ if (!(await (0, utils_1.pathExists)(targetDir))) {
485
+ await (0, utils_1.copyDirRecursive)(sourceDir, targetDir);
486
+ (0, utils_1.logInfo)(outputChannel, `[AgentIDE] Seeded pack data for ${agentId}: ${targetDir}`);
487
+ return;
488
+ }
489
+ try {
490
+ const st = await fs_1.promises.stat(targetDir);
491
+ if (!st.isDirectory()) {
492
+ return;
493
+ }
494
+ }
495
+ catch {
496
+ return;
497
+ }
498
+ if (await isDirEmpty(targetDir)) {
499
+ await (0, utils_1.copyDirRecursive)(sourceDir, targetDir);
500
+ (0, utils_1.logInfo)(outputChannel, `[AgentIDE] Seeded pack data for ${agentId}: ${targetDir}`);
501
+ }
502
+ }
503
+ async syncAgentYamlSkills(workspaceRoot, agentId, manifest, outputChannel) {
504
+ const agentYamlPath = path.join(this.getAgentRoot(workspaceRoot, agentId), AGENTIDE_AGENT_YAML);
505
+ if (!(await (0, utils_1.pathExists)(agentYamlPath))) {
506
+ return;
507
+ }
508
+ try {
509
+ const raw = await fs_1.promises.readFile(agentYamlPath, 'utf8');
510
+ const parsed = parseYaml(raw);
511
+ if (!parsed || typeof parsed !== 'object') {
512
+ return;
513
+ }
514
+ const doc = parsed;
515
+ const skillPaths = manifest.skills.map(skill => ({ path: `./skills/${skill.dirName}` }));
516
+ doc.spec = { ...(doc.spec ?? {}), skills: skillPaths };
517
+ const next = dumpYaml(doc);
518
+ await (0, utils_1.writeFileIfChanged)(agentYamlPath, next);
519
+ }
520
+ catch (e) {
521
+ outputChannel.appendLine(`[AgentIDE] Warning: failed to update agent.yaml skills for ${agentId}: ${e instanceof Error ? e.message : String(e)}`);
522
+ }
523
+ }
524
+ /**
525
+ * Get the active agent's manifest (agent.yaml) for permission resolution.
526
+ * Returns undefined if the manifest doesn't exist or can't be parsed.
527
+ *
528
+ * Cached by file mtime/size: handleChatCommand calls this on every turn to check
529
+ * spec.remote.url, so we avoid a stat + read + full YAML parse per message. The
530
+ * "no agent.yaml" / "invalid" log lines fire only on state transitions, not every
531
+ * cached return, to keep the output channel clean.
532
+ */
533
+ async getActiveAgentManifest(workspaceRoot, outputChannel, agentIdOverride) {
534
+ const agentId = agentIdOverride ?? this.getActiveAgentId(workspaceRoot);
535
+ const agentYamlPath = path.join(this.getAgentRoot(workspaceRoot, agentId), AGENTIDE_AGENT_YAML);
536
+ const stats = await (0, utils_1.getFileStats)(agentYamlPath);
537
+ if (!stats) {
538
+ const cached = this.activeAgentManifestCache.get(agentYamlPath);
539
+ if (!cached?.loggedNoManifest) {
540
+ outputChannel.appendLine(`[AgentIDE] No agent.yaml found for agent ${agentId}`);
541
+ }
542
+ this.activeAgentManifestCache.set(agentYamlPath, {
543
+ mtimeMs: -1, size: -1, manifest: undefined,
544
+ loggedNoManifest: true, loggedInvalid: cached?.loggedInvalid ?? false,
545
+ });
546
+ return undefined;
547
+ }
548
+ const cached = this.activeAgentManifestCache.get(agentYamlPath);
549
+ if (cached && cached.mtimeMs === stats.mtimeMs && cached.size === stats.size) {
550
+ return cached.manifest;
551
+ }
552
+ try {
553
+ const raw = await fs_1.promises.readFile(agentYamlPath, 'utf8');
554
+ const parsed = parseYaml(raw);
555
+ // Basic validation
556
+ if (!parsed?.metadata?.name || !parsed?.spec?.engine) {
557
+ if (!cached?.loggedInvalid) {
558
+ outputChannel.appendLine(`[AgentIDE] Invalid agent.yaml for agent ${agentId}: missing required fields`);
559
+ }
560
+ this.activeAgentManifestCache.set(agentYamlPath, {
561
+ mtimeMs: stats.mtimeMs, size: stats.size, manifest: undefined,
562
+ loggedNoManifest: false, loggedInvalid: true,
563
+ });
564
+ return undefined;
565
+ }
566
+ this.activeAgentManifestCache.set(agentYamlPath, {
567
+ mtimeMs: stats.mtimeMs, size: stats.size, manifest: parsed,
568
+ loggedNoManifest: false, loggedInvalid: false,
569
+ });
570
+ return parsed;
571
+ }
572
+ catch (e) {
573
+ if (!cached?.loggedInvalid) {
574
+ outputChannel.appendLine(`[AgentIDE] Failed to parse agent.yaml for agent ${agentId}: ${e instanceof Error ? e.message : String(e)}`);
575
+ }
576
+ this.activeAgentManifestCache.set(agentYamlPath, {
577
+ mtimeMs: stats.mtimeMs, size: stats.size, manifest: undefined,
578
+ loggedNoManifest: false, loggedInvalid: true,
579
+ });
580
+ return undefined;
581
+ }
582
+ }
583
+ getAgentideManifestFilePath(workspaceRoot) {
584
+ return path.join(this.getAgentideProjectDir(workspaceRoot), AGENTIDE_MANIFEST_FILE);
585
+ }
586
+ /**
587
+ * Check if all derived provider directories exist for the given workspace root.
588
+ */
589
+ async allDerivedDirsPresent(workspaceRoot) {
590
+ const results = await Promise.all(getDerivedEngineSyncTargets(workspaceRoot).map(engine => (0, utils_1.isDirOrSymlink)(path.join(workspaceRoot, engine.workspaceDir))));
591
+ return results.every(Boolean);
592
+ }
593
+ async readAgentideManifest(workspaceRoot) {
594
+ const manifestPath = this.getAgentideManifestFilePath(workspaceRoot);
595
+ try {
596
+ const raw = await fs_1.promises.readFile(manifestPath, 'utf8');
597
+ const parsed = JSON.parse(raw);
598
+ if (parsed.version !== 1 || typeof parsed.agentideSignature !== 'string') {
599
+ return undefined;
600
+ }
601
+ return parsed;
602
+ }
603
+ catch (e) {
604
+ if ((0, utils_1.getErrorCode)(e) === 'ENOENT') {
605
+ return undefined;
606
+ }
607
+ return undefined;
608
+ }
609
+ }
610
+ async computeAgentideManifest(workspaceRoot, source, cachedManifest) {
611
+ const rulesDir = source.rulesDir;
612
+ const skillsDir = source.skillsDir;
613
+ // Build lookup maps from cached manifest for fast path (mtime caching)
614
+ const cachedRulesByPath = new Map();
615
+ const cachedSkillsByDirName = new Map();
616
+ if (cachedManifest) {
617
+ for (const rule of cachedManifest.rules) {
618
+ cachedRulesByPath.set(rule.relPath, rule);
619
+ }
620
+ for (const skill of cachedManifest.skills) {
621
+ const dirName = skill.dirName || getSkillDirNameFromRelPath(skill.skillMdRelPath) || skill.name;
622
+ cachedSkillsByDirName.set(dirName, skill);
623
+ }
624
+ }
625
+ const rules = [];
626
+ const skills = [];
627
+ // Helper to get file hash with mtime caching (avoids re-hashing unchanged files)
628
+ const getFileHashWithCache = async (filePath, relPath) => {
629
+ const stats = await (0, utils_1.getFileStats)(filePath);
630
+ if (!stats) {
631
+ return undefined;
632
+ }
633
+ const cached = cachedRulesByPath.get(relPath);
634
+ const validCache = (0, utils_1.isCachedHashValid)(cached, stats);
635
+ if (validCache) {
636
+ return validCache; // Fast path: file unchanged, reuse cached hash
637
+ }
638
+ // Slow path: file changed, compute new hash
639
+ const { sha256, size, mtimeMs } = await (0, utils_1.readFileSha256)(filePath);
640
+ return { relPath, sha256, size, mtimeMs };
641
+ };
642
+ const agentsFile = source.agentsFile;
643
+ const agentsEntry = await getFileHashWithCache(agentsFile, 'AGENTS.md');
644
+ if (agentsEntry) {
645
+ rules.push(agentsEntry);
646
+ }
647
+ const contextFile = source.contextFile;
648
+ const contextEntry = await getFileHashWithCache(contextFile, AGENTIDE_CONTEXT_FILE);
649
+ if (contextEntry) {
650
+ rules.push(contextEntry);
651
+ }
652
+ try {
653
+ const entries = await fs_1.promises.readdir(rulesDir, { withFileTypes: true });
654
+ for (const entry of entries) {
655
+ if (!entry.isFile() || !entry.name.toLowerCase().endsWith('.md')) {
656
+ continue;
657
+ }
658
+ const abs = path.join(rulesDir, entry.name);
659
+ const relPath = path.join('rules', entry.name).replace(/\\/g, '/');
660
+ const ruleEntry = await getFileHashWithCache(abs, relPath);
661
+ if (ruleEntry) {
662
+ rules.push(ruleEntry);
663
+ }
664
+ }
665
+ }
666
+ catch {
667
+ // ignore
668
+ }
669
+ try {
670
+ const entries = await fs_1.promises.readdir(skillsDir, { withFileTypes: true });
671
+ for (const entry of entries) {
672
+ if (!entry.isDirectory()) {
673
+ continue;
674
+ }
675
+ if (entry.name.startsWith('.')) {
676
+ continue;
677
+ }
678
+ const skillMd = path.join(skillsDir, entry.name, 'SKILL.md');
679
+ const stats = await (0, utils_1.getFileStats)(skillMd);
680
+ if (!stats) {
681
+ continue;
682
+ }
683
+ const cached = cachedSkillsByDirName.get(entry.name);
684
+ const validCache = cached && cached.skillMdMtimeMs === stats.mtimeMs && cached.skillMdSize === stats.size;
685
+ if (validCache) {
686
+ // Fast path: skill unchanged, reuse cached entry entirely (including name/description from manifest)
687
+ skills.push({
688
+ ...cached,
689
+ dirName: entry.name, // Ensure dirName is set (for old manifests that might not have it)
690
+ });
691
+ }
692
+ else {
693
+ // Slow path: skill changed, compute new hash and parse YAML header
694
+ const { sha256, size, mtimeMs } = await (0, utils_1.readFileSha256)(skillMd);
695
+ // Read file content to parse YAML header for name/description
696
+ let yamlName;
697
+ let yamlDescription;
698
+ try {
699
+ const content = await fs_1.promises.readFile(skillMd, 'utf-8');
700
+ const header = parseSkillYamlHeader(content);
701
+ yamlName = header.name;
702
+ yamlDescription = header.description;
703
+ }
704
+ catch {
705
+ // Ignore read errors, use directory name as fallback
706
+ }
707
+ skills.push({
708
+ dirName: entry.name,
709
+ name: yamlName || entry.name, // Prefer YAML name, fallback to directory name
710
+ description: yamlDescription,
711
+ skillMdRelPath: path.join('skills', entry.name, 'SKILL.md').replace(/\\/g, '/'),
712
+ skillMdSha256: sha256,
713
+ skillMdSize: size,
714
+ skillMdMtimeMs: mtimeMs,
715
+ });
716
+ }
717
+ }
718
+ }
719
+ catch {
720
+ // ignore
721
+ }
722
+ rules.sort((a, b) => a.relPath.localeCompare(b.relPath));
723
+ skills.sort((a, b) => a.dirName.localeCompare(b.dirName));
724
+ const rulesSignature = (0, utils_1.shortSig)(JSON.stringify(rules.map(r => [r.relPath, r.sha256])));
725
+ // Use dirName (stable folder identifier) to ensure signature changes when a skill folder is renamed.
726
+ const skillsSignature = (0, utils_1.shortSig)(JSON.stringify(skills.map(s => [s.dirName, s.skillMdSha256])));
727
+ const agentideSignature = (0, utils_1.shortSig)(`${rulesSignature}:${skillsSignature}`);
728
+ return {
729
+ version: 1,
730
+ workspaceRoot,
731
+ generatedAt: new Date().toISOString(),
732
+ activeAgentId: source.agentId,
733
+ rules,
734
+ skills,
735
+ rulesSignature,
736
+ skillsSignature,
737
+ agentideSignature,
738
+ };
739
+ }
740
+ async writeAgentideManifest(workspaceRoot, outputChannel, options) {
741
+ const agentideDir = this.getAgentideProjectDir(workspaceRoot);
742
+ if (!(await (0, utils_1.pathExists)(agentideDir))) {
743
+ await fs_1.promises.mkdir(agentideDir, { recursive: true });
744
+ }
745
+ const existing = await this.readAgentideManifest(workspaceRoot);
746
+ const source = await this.resolveManifestSource(workspaceRoot, options?.packIdOverride);
747
+ const useCache = existing?.activeAgentId === source.agentId;
748
+ // Pass existing manifest for mtime caching (avoids re-hashing unchanged files)
749
+ const manifest = await this.computeAgentideManifest(workspaceRoot, source, useCache ? existing : undefined);
750
+ manifest.derivedSkillIngestState = useCache ? (options?.derivedSkillIngestState ?? existing?.derivedSkillIngestState) : options?.derivedSkillIngestState;
751
+ // Note: installedAgents is now stored in separate installed-agents.json file
752
+ const manifestPath = this.getAgentideManifestFilePath(workspaceRoot);
753
+ await (0, utils_1.writeFileIfChanged)(manifestPath, JSON.stringify(manifest, null, 2) + '\n');
754
+ this.agentideSignatureByRoot.set(workspaceRoot, manifest.agentideSignature);
755
+ this.agentideSkillsSignatureByRoot.set(workspaceRoot, manifest.skillsSignature);
756
+ outputChannel.appendLine(`[AgentIDE] Updated manifest: ${manifestPath} (sig=${manifest.agentideSignature})`);
757
+ if (!source.isPack) {
758
+ await this.syncAgentYamlSkills(workspaceRoot, source.agentId, manifest, outputChannel);
759
+ }
760
+ return manifest;
761
+ }
762
+ // -------------------------------------------------------------------------
763
+ // File Watchers
764
+ // -------------------------------------------------------------------------
765
+ ensureAgentideWatchers(outputChannel) {
766
+ const roots = vscode.workspace.workspaceFolders?.map(f => f.uri.fsPath) ?? [];
767
+ const existingRoots = new Set(this.agentideWatchersByRoot.keys());
768
+ for (const root of existingRoots) {
769
+ if (!roots.includes(root)) {
770
+ this.disposeAgentideWatchersForRoot(root);
771
+ this.agentideRootUpdateState.delete(root);
772
+ this.lastAgentideSyncSignatureByRoot.delete(root);
773
+ this.agentideSignatureByRoot.delete(root);
774
+ this.agentideSkillsSignatureByRoot.delete(root);
775
+ }
776
+ }
777
+ for (const root of roots) {
778
+ if (this.agentideWatchersByRoot.has(root)) {
779
+ continue;
780
+ }
781
+ // Watch all agents in the agent root
782
+ const patterns = [
783
+ { pattern: new vscode.RelativePattern(root, `${AGENTIDE_DIR_NAME}/${AGENTIDE_AGENTS_DIR}/*/agent.yaml`), reason: 'source' },
784
+ { pattern: new vscode.RelativePattern(root, `${AGENTIDE_DIR_NAME}/${AGENTIDE_AGENTS_DIR}/*/AGENTS.md`), reason: 'source' },
785
+ { pattern: new vscode.RelativePattern(root, `${AGENTIDE_DIR_NAME}/${AGENTIDE_AGENTS_DIR}/*/context.md`), reason: 'source' },
786
+ { pattern: new vscode.RelativePattern(root, `${AGENTIDE_DIR_NAME}/${AGENTIDE_AGENTS_DIR}/*/rules/**/*.md`), reason: 'source' },
787
+ { pattern: new vscode.RelativePattern(root, `${AGENTIDE_DIR_NAME}/${AGENTIDE_AGENTS_DIR}/*/skills/**/*`), reason: 'source' },
788
+ // Also watch agents/ for pack changes (to detect new/updated packs)
789
+ { pattern: new vscode.RelativePattern(root, `${AGENTIDE_DIR_NAME}/${AGENTIDE_AGENTS_DIR}/*/AGENTS.md`), reason: 'source' },
790
+ { pattern: new vscode.RelativePattern(root, `${AGENTIDE_DIR_NAME}/${AGENTIDE_AGENTS_DIR}/*/context.md`), reason: 'source' },
791
+ { pattern: new vscode.RelativePattern(root, `${AGENTIDE_DIR_NAME}/${AGENTIDE_AGENTS_DIR}/*/rules/**/*.md`), reason: 'source' },
792
+ { pattern: new vscode.RelativePattern(root, `${AGENTIDE_DIR_NAME}/${AGENTIDE_AGENTS_DIR}/*/skills/**/*`), reason: 'source' },
793
+ { pattern: new vscode.RelativePattern(root, `${AGENTIDE_DIR_NAME}/${AGENTIDE_AGENTS_DIR}/*/agent.yaml`), reason: 'source' },
794
+ // Watch derived engine directories for ingest back to .agentide
795
+ ...getDerivedEngineSyncTargets(root).flatMap(engine => [
796
+ ...(engine.includeSkills
797
+ ? [{ pattern: new vscode.RelativePattern(root, `${engine.workspaceDir}/skills/**/SKILL.md`), reason: 'derived' }]
798
+ : []),
799
+ { pattern: new vscode.RelativePattern(root, `${engine.workspaceDir}/${engine.rulesFile}`), reason: 'derived' },
800
+ ...(engine.includeRules
801
+ ? [{ pattern: new vscode.RelativePattern(root, `${engine.workspaceDir}/rules/**/*.md`), reason: 'derived' }]
802
+ : []),
803
+ ]),
804
+ ];
805
+ const disposables = [];
806
+ for (const { pattern, reason } of patterns) {
807
+ const watcher = vscode.workspace.createFileSystemWatcher(pattern);
808
+ const onAny = () => this.scheduleAgentideRootUpdate(root, outputChannel, reason);
809
+ disposables.push(watcher, watcher.onDidChange(onAny), watcher.onDidCreate(onAny), watcher.onDidDelete(onAny));
810
+ }
811
+ this.agentideWatchersByRoot.set(root, disposables);
812
+ this.scheduleAgentideRootUpdate(root, outputChannel, 'source');
813
+ }
814
+ }
815
+ disposeAgentideWatchersForRoot(workspaceRoot) {
816
+ const disposables = this.agentideWatchersByRoot.get(workspaceRoot);
817
+ if (!disposables) {
818
+ return;
819
+ }
820
+ for (const d of disposables) {
821
+ try {
822
+ d.dispose();
823
+ }
824
+ catch { /* ignore */ }
825
+ }
826
+ this.agentideWatchersByRoot.delete(workspaceRoot);
827
+ }
828
+ disposeAllWatchers() {
829
+ for (const root of this.agentideWatchersByRoot.keys()) {
830
+ this.disposeAgentideWatchersForRoot(root);
831
+ }
832
+ this.agentideRootUpdateState.clear();
833
+ }
834
+ scheduleAgentideRootUpdate(workspaceRoot, outputChannel, reason) {
835
+ const state = this.agentideRootUpdateState.get(workspaceRoot) ?? {
836
+ dirty: false,
837
+ dirtySource: false,
838
+ dirtyDerived: false,
839
+ timer: undefined,
840
+ deferred: undefined,
841
+ };
842
+ state.dirty = true;
843
+ if (reason === 'source') {
844
+ state.dirtySource = true;
845
+ }
846
+ else {
847
+ state.dirtyDerived = true;
848
+ }
849
+ if (!state.deferred) {
850
+ state.deferred = (0, utils_1.createDeferred)();
851
+ }
852
+ if (state.timer) {
853
+ clearTimeout(state.timer);
854
+ }
855
+ state.timer = setTimeout(() => {
856
+ void this.flushAgentideRootUpdate(workspaceRoot, outputChannel, { allowDuringActiveTurn: false, force: false });
857
+ }, AGENTIDE_MANIFEST_DEBOUNCE_MS);
858
+ this.agentideRootUpdateState.set(workspaceRoot, state);
859
+ }
860
+ async flushAgentideRootUpdate(workspaceRoot, outputChannel, options) {
861
+ const allowDuringActiveTurn = options?.allowDuringActiveTurn ?? false;
862
+ const force = options?.force ?? false;
863
+ if (!allowDuringActiveTurn && this.activeAgentTurns > 0) {
864
+ const existing = this.agentideRootUpdateState.get(workspaceRoot);
865
+ const reason = existing?.dirtySource
866
+ ? 'source'
867
+ : existing?.dirtyDerived
868
+ ? 'derived'
869
+ : 'source';
870
+ this.scheduleAgentideRootUpdate(workspaceRoot, outputChannel, reason);
871
+ return;
872
+ }
873
+ const state = this.agentideRootUpdateState.get(workspaceRoot);
874
+ if (!force && (!state || (!state.dirty && !state.deferred))) {
875
+ return;
876
+ }
877
+ const dirtySourceBefore = force || (state?.dirtySource ?? false);
878
+ const dirtyDerivedBefore = force || (state?.dirtyDerived ?? false);
879
+ if (state?.timer) {
880
+ clearTimeout(state.timer);
881
+ state.timer = undefined;
882
+ }
883
+ const deferred = state?.deferred;
884
+ if (state) {
885
+ state.dirty = false;
886
+ state.dirtySource = false;
887
+ state.dirtyDerived = false;
888
+ state.deferred = undefined;
889
+ this.agentideRootUpdateState.set(workspaceRoot, state);
890
+ }
891
+ try {
892
+ let manifest = await this.readAgentideManifest(workspaceRoot);
893
+ // Import new skills from derived dirs if they changed
894
+ const ingestResult = dirtyDerivedBefore
895
+ ? await this.importNewSkillsFromDerivedIntoAgentide(workspaceRoot, outputChannel, manifest?.derivedSkillIngestState)
896
+ : {
897
+ agentideContentChanged: false,
898
+ ingestStateChanged: false,
899
+ nextIngestState: manifest?.derivedSkillIngestState ?? {},
900
+ };
901
+ if (!manifest
902
+ || force
903
+ || dirtySourceBefore
904
+ || ingestResult.agentideContentChanged
905
+ || ingestResult.ingestStateChanged) {
906
+ manifest = await this.writeAgentideManifest(workspaceRoot, outputChannel, { derivedSkillIngestState: ingestResult.nextIngestState });
907
+ // Auto-discover new packs when source files change (e.g., skill creates a new pack)
908
+ // Only refresh if the number of agents on disk differs from registered count
909
+ if (dirtySourceBefore) {
910
+ try {
911
+ const agentsDir = path.join(workspaceRoot, AGENTIDE_DIR_NAME, AGENTIDE_AGENTS_DIR);
912
+ if (await (0, utils_1.pathExists)(agentsDir)) {
913
+ const entries = await fs_1.promises.readdir(agentsDir, { withFileTypes: true });
914
+ const diskAgentCount = entries.filter(e => e.isDirectory() && !e.name.startsWith('.')).length;
915
+ const registeredAgents = await this.loadInstalledAgents(workspaceRoot);
916
+ if (diskAgentCount > registeredAgents.length) {
917
+ await vscode.commands.executeCommand('agentide.pack.refresh');
918
+ }
919
+ }
920
+ }
921
+ catch {
922
+ // Ignore errors - this is a best-effort optimization
923
+ }
924
+ }
925
+ }
926
+ else {
927
+ this.agentideSignatureByRoot.set(workspaceRoot, manifest.agentideSignature);
928
+ this.agentideSkillsSignatureByRoot.set(workspaceRoot, manifest.skillsSignature);
929
+ }
930
+ if (!manifest) {
931
+ return;
932
+ }
933
+ const lastSynced = this.lastAgentideSyncSignatureByRoot.get(workspaceRoot);
934
+ const derivedPresent = await this.allDerivedDirsPresent(workspaceRoot);
935
+ if (dirtySourceBefore || ingestResult.agentideContentChanged || lastSynced !== manifest.agentideSignature || !derivedPresent) {
936
+ const activePackId = this.getActivePackId(workspaceRoot);
937
+ const appliedPackId = await this.switchToPack(workspaceRoot, activePackId, outputChannel);
938
+ if (appliedPackId !== activePackId) {
939
+ this.setActivePackId(workspaceRoot, appliedPackId);
940
+ }
941
+ const activeAgentId = appliedPackId ?? AGENTIDE_DEFAULT_AGENT_ID;
942
+ await this.seedAgentDataIfNeeded(workspaceRoot, activeAgentId, outputChannel);
943
+ this.lastAgentideSyncSignatureByRoot.set(workspaceRoot, manifest.agentideSignature);
944
+ outputChannel.appendLine(`[AgentIDE] Derived dirs synced for ${workspaceRoot} (sig=${manifest.agentideSignature}, pack=${appliedPackId ?? 'default'})`);
945
+ }
946
+ deferred?.resolve();
947
+ }
948
+ catch (e) {
949
+ deferred?.reject(e);
950
+ outputChannel.appendLine(`[AgentIDE] Warning: failed agentide update for ${workspaceRoot}: ${e instanceof Error ? e.message : String(e)}`);
951
+ }
952
+ }
953
+ // -------------------------------------------------------------------------
954
+ // Project Preparation
955
+ // -------------------------------------------------------------------------
956
+ async ensureAgentideProjectPrepared(workspaceRoot, additionalDirectories, outputChannel) {
957
+ if (!workspaceRoot) {
958
+ return;
959
+ }
960
+ const workspaceFolderRoots = vscode.workspace.workspaceFolders?.map(f => f.uri.fsPath) ?? [];
961
+ const additionalWorkspaceRoots = (additionalDirectories ?? []).filter(d => workspaceFolderRoots.includes(d));
962
+ const roots = Array.from(new Set([workspaceRoot, ...additionalWorkspaceRoots].map(r => r.trim()).filter(Boolean))).filter(r => r !== os.homedir());
963
+ for (const root of roots) {
964
+ // Ensure git is initialized and root .gitignore has AgentIDE entries
965
+ await (0, utils_1.ensureWorkspaceGitAndGitignore)(root, outputChannel);
966
+ const agentideDir = this.getAgentideProjectDir(root);
967
+ await fs_1.promises.mkdir(agentideDir, { recursive: true });
968
+ await this.ensureAgentideGitignore(root, outputChannel);
969
+ await this.ensureAgentideBundledPromptsCopiedToProject(root, outputChannel);
970
+ const otherRoots = roots.filter(r => r !== root);
971
+ await this.ensureAgentideContextTemplate(root, otherRoots, outputChannel);
972
+ await this.ensureGhostTextPrompt(root, outputChannel);
973
+ await this.ensureTerminalAssistPrompt(root, outputChannel);
974
+ // Best-effort remote skills sync (optional, startup path)
975
+ await this.syncRemoteSkillsIfEnabled(root, outputChannel, { force: false });
976
+ }
977
+ for (const root of roots) {
978
+ await this.flushAgentideRootUpdate(root, outputChannel, { force: false, allowDuringActiveTurn: true });
979
+ const derivedPresent = await this.allDerivedDirsPresent(root);
980
+ let manifest = await this.readAgentideManifest(root);
981
+ if (!manifest) {
982
+ manifest = await this.writeAgentideManifest(root, outputChannel);
983
+ }
984
+ else {
985
+ this.agentideSignatureByRoot.set(root, manifest.agentideSignature);
986
+ this.agentideSkillsSignatureByRoot.set(root, manifest.skillsSignature);
987
+ }
988
+ const lastSig = this.lastAgentideSyncSignatureByRoot.get(root);
989
+ if (lastSig === manifest.agentideSignature && derivedPresent) {
990
+ outputChannel.appendLine(`[AgentIDE] .agentide/ unchanged for ${root}, skipping sync`);
991
+ continue;
992
+ }
993
+ // Use pack-aware sync: switchToPack respects active pack ID
994
+ // Import derived skills first in case agent created them while extension was stopped
995
+ const activePackId = this.getActivePackId(root);
996
+ const appliedPackId = await this.switchToPack(root, activePackId, outputChannel, { importDerivedSkills: true });
997
+ if (appliedPackId !== activePackId) {
998
+ this.setActivePackId(root, appliedPackId);
999
+ }
1000
+ this.lastAgentideSyncSignatureByRoot.set(root, manifest.agentideSignature);
1001
+ outputChannel.appendLine(`[AgentIDE] Sync complete for ${root} (sig: ${manifest.agentideSignature}, pack: ${appliedPackId ?? 'default'})`);
1002
+ }
1003
+ }
1004
+ /**
1005
+ * Sync remote skills into `.agentide/agents/default/skills` for the given workspace root.
1006
+ *
1007
+ * - When `force=false`, this respects `agentide.skills.remote.syncOnStartup`.
1008
+ * - When `force=true`, it runs as long as remote skills are enabled and a registryUrl is configured.
1009
+ */
1010
+ async syncRemoteSkillsIfEnabled(workspaceRoot, outputChannel, opts) {
1011
+ const cfg = vscode.workspace.getConfiguration('agentide.skills.remote');
1012
+ const enabled = cfg.get('enabled', false);
1013
+ if (!enabled) {
1014
+ return;
1015
+ }
1016
+ const syncOnStartup = cfg.get('syncOnStartup', true);
1017
+ if (!opts.force && !syncOnStartup) {
1018
+ return;
1019
+ }
1020
+ const registryUrl = cfg.get('registryUrl', '').trim();
1021
+ if (!registryUrl) {
1022
+ outputChannel.appendLine('[AgentIDE] Remote skills: enabled but agentide.skills.remote.registryUrl is empty');
1023
+ return;
1024
+ }
1025
+ const timeoutMs = cfg.get('timeoutMs', 8000);
1026
+ const allowOverwriteLocal = cfg.get('allowOverwriteLocal', false);
1027
+ try {
1028
+ const agentideSkillsDir = this.getAgentSkillsDir(workspaceRoot, AGENTIDE_DEFAULT_AGENT_ID);
1029
+ const result = await (0, remoteSkills_1.syncRemoteSkillsToAgentideSkillsDir)(agentideSkillsDir, { registryUrl, timeoutMs, allowOverwriteLocal }, line => outputChannel.appendLine(line));
1030
+ if (result.updated > 0) {
1031
+ // Mark source dirty so the manifest/signatures are refreshed and derived dirs re-synced.
1032
+ this.scheduleAgentideRootUpdate(workspaceRoot, outputChannel, 'source');
1033
+ }
1034
+ }
1035
+ catch (e) {
1036
+ outputChannel.appendLine(`[AgentIDE] Remote skills: unexpected error: ${e instanceof Error ? e.message : String(e)}`);
1037
+ }
1038
+ }
1039
+ async ensureAgentideBundledPromptsCopiedToProject(workspaceRoot, outputChannel) {
1040
+ const bundledDir = this.getBundledBasePromptsDir();
1041
+ if (!bundledDir) {
1042
+ return;
1043
+ }
1044
+ // All agents live in .agentide/agents/<agentId>/
1045
+ const defaultAgentRoot = this.getAgentRoot(workspaceRoot, AGENTIDE_DEFAULT_AGENT_ID);
1046
+ const agentideAgentsFile = this.getAgentAgentsFile(workspaceRoot, AGENTIDE_DEFAULT_AGENT_ID);
1047
+ const agentideRulesDir = this.getAgentRulesDir(workspaceRoot, AGENTIDE_DEFAULT_AGENT_ID);
1048
+ const agentideSkillsDir = this.getAgentSkillsDir(workspaceRoot, AGENTIDE_DEFAULT_AGENT_ID);
1049
+ await fs_1.promises.mkdir(defaultAgentRoot, { recursive: true });
1050
+ await fs_1.promises.mkdir(agentideRulesDir, { recursive: true });
1051
+ await fs_1.promises.mkdir(agentideSkillsDir, { recursive: true });
1052
+ const bundledAgentsFile = path.join(bundledDir, 'rules', 'AGENTS.md');
1053
+ if (!(await (0, utils_1.pathExists)(agentideAgentsFile)) && (await (0, utils_1.pathExists)(bundledAgentsFile))) {
1054
+ try {
1055
+ const bundledAgentsContent = await fs_1.promises.readFile(bundledAgentsFile, 'utf8');
1056
+ await fs_1.promises.writeFile(agentideAgentsFile, (0, branding_1.applyBrandingTokens)(bundledAgentsContent));
1057
+ outputChannel.appendLine(`[AgentIDE] Bootstrapped project rules: ${agentideAgentsFile}`);
1058
+ }
1059
+ catch (e) {
1060
+ outputChannel.appendLine(`[AgentIDE] Warning: failed to bootstrap project rules: ${e instanceof Error ? e.message : String(e)}`);
1061
+ }
1062
+ }
1063
+ const bundledSkillsDir = path.join(bundledDir, 'skills');
1064
+ if (await (0, utils_1.pathExists)(bundledSkillsDir)) {
1065
+ try {
1066
+ const entries = await fs_1.promises.readdir(bundledSkillsDir, { withFileTypes: true });
1067
+ for (const entry of entries) {
1068
+ if (!entry.isDirectory()) {
1069
+ continue;
1070
+ }
1071
+ const srcSkill = path.join(bundledSkillsDir, entry.name);
1072
+ const dstSkill = path.join(agentideSkillsDir, entry.name);
1073
+ if (await (0, utils_1.pathExists)(dstSkill)) {
1074
+ continue;
1075
+ }
1076
+ // Apply branding tokens to markdown files in bundled skills
1077
+ await (0, utils_1.copyDirRecursiveWithTransform)(srcSkill, dstSkill, branding_1.applyBrandingTokens);
1078
+ outputChannel.appendLine(`[AgentIDE] Bootstrapped project skill: ${entry.name}`);
1079
+ }
1080
+ }
1081
+ catch (e) {
1082
+ outputChannel.appendLine(`[AgentIDE] Warning: failed to bootstrap project skills: ${e instanceof Error ? e.message : String(e)}`);
1083
+ }
1084
+ }
1085
+ // Sync MCP config to default agent's mcp/.mcp.json
1086
+ const agentideMcpDir = path.join(defaultAgentRoot, paths_1.AGENT_CONSTANTS.MCP_DIR);
1087
+ const mcpConfigPath = path.join(agentideMcpDir, '.mcp.json');
1088
+ if (!(await (0, utils_1.pathExists)(mcpConfigPath))) {
1089
+ try {
1090
+ const mcpConfig = await (0, mcp_1.getDefaultMcpConfig)(bundledDir);
1091
+ if (mcpConfig) {
1092
+ await fs_1.promises.mkdir(agentideMcpDir, { recursive: true });
1093
+ await fs_1.promises.writeFile(mcpConfigPath, mcpConfig);
1094
+ outputChannel.appendLine(`[AgentIDE] Bootstrapped MCP config: ${mcpConfigPath}`);
1095
+ }
1096
+ }
1097
+ catch (e) {
1098
+ outputChannel.appendLine(`[AgentIDE] Warning: failed to bootstrap MCP config: ${e instanceof Error ? e.message : String(e)}`);
1099
+ }
1100
+ }
1101
+ // Ensure default agent has an agent.yaml
1102
+ await this.ensureDefaultAgentYaml(workspaceRoot, outputChannel);
1103
+ }
1104
+ /**
1105
+ * Ensure default agent has an agent.yaml file.
1106
+ * All agents have the same structure under .agentide/agents/<agentId>/
1107
+ */
1108
+ async ensureDefaultAgentYaml(workspaceRoot, outputChannel) {
1109
+ const defaultAgentRoot = this.getAgentRoot(workspaceRoot, AGENTIDE_DEFAULT_AGENT_ID);
1110
+ const agentYamlPath = path.join(defaultAgentRoot, AGENTIDE_AGENT_YAML);
1111
+ if (await (0, utils_1.pathExists)(agentYamlPath)) {
1112
+ return; // Already exists
1113
+ }
1114
+ await fs_1.promises.mkdir(defaultAgentRoot, { recursive: true });
1115
+ const defaultAgentYaml = (0, paths_1.generateDefaultAgentYaml)({
1116
+ includeModelHintComment: true,
1117
+ });
1118
+ try {
1119
+ await fs_1.promises.writeFile(agentYamlPath, defaultAgentYaml, 'utf8');
1120
+ outputChannel.appendLine(`[AgentIDE] Bootstrapped default agent configuration: ${agentYamlPath}`);
1121
+ }
1122
+ catch (e) {
1123
+ outputChannel.appendLine(`[AgentIDE] Warning: failed to create default agent.yaml: ${e instanceof Error ? e.message : String(e)}`);
1124
+ }
1125
+ }
1126
+ async ensureAgentideGitignore(workspaceRoot, outputChannel) {
1127
+ const agentideDir = this.getAgentideProjectDir(workspaceRoot);
1128
+ const gitignorePath = path.join(agentideDir, '.gitignore');
1129
+ const managedBlockLines = [
1130
+ '# AgentHippo (managed)',
1131
+ '# Keep terminal logs and large per-session terminal attachments out of source control.',
1132
+ 'terminals/',
1133
+ 'agents/default',
1134
+ 'sessions',
1135
+ 'paths.md',
1136
+ 'sessions/**/terminal.md',
1137
+ 'sessions/**/terminal.md.1',
1138
+ ];
1139
+ let existing = '';
1140
+ try {
1141
+ existing = await fs_1.promises.readFile(gitignorePath, 'utf8');
1142
+ }
1143
+ catch (e) {
1144
+ if ((0, utils_1.getErrorCode)(e) !== 'ENOENT') {
1145
+ outputChannel.appendLine(`[AgentIDE] Warning: failed to read .gitignore: ${e instanceof Error ? e.message : String(e)}`);
1146
+ return;
1147
+ }
1148
+ }
1149
+ const hasAll = managedBlockLines.every(line => existing.includes(line));
1150
+ if (hasAll) {
1151
+ return;
1152
+ }
1153
+ const next = [
1154
+ existing.trimEnd(),
1155
+ '',
1156
+ managedBlockLines.join('\n'),
1157
+ '',
1158
+ ].join('\n').replace(/^\n+/, '');
1159
+ try {
1160
+ const changed = await (0, utils_1.writeFileIfChanged)(gitignorePath, next);
1161
+ if (changed) {
1162
+ outputChannel.appendLine(`[AgentIDE] Updated ${gitignorePath} to ignore terminal logs`);
1163
+ }
1164
+ }
1165
+ catch (e) {
1166
+ outputChannel.appendLine(`[AgentIDE] Warning: failed to write .gitignore: ${e instanceof Error ? e.message : String(e)}`);
1167
+ }
1168
+ }
1169
+ async ensureAgentideContextTemplate(workspaceRoot, additionalDirectories, outputChannel) {
1170
+ const agentideDir = this.getAgentideProjectDir(workspaceRoot);
1171
+ if (!(await (0, utils_1.pathExists)(agentideDir))) {
1172
+ return;
1173
+ }
1174
+ const activeAgentId = this.getActiveAgentId(workspaceRoot);
1175
+ const contextFilePath = this.getAgentContextFile(workspaceRoot, activeAgentId);
1176
+ if (await (0, utils_1.pathExists)(contextFilePath)) {
1177
+ return;
1178
+ }
1179
+ const bundledDir = this.getBundledBasePromptsDir();
1180
+ const bundledTemplatePath = bundledDir ? path.join(bundledDir, 'templates', 'context.md') : undefined;
1181
+ if (!bundledTemplatePath || !(await (0, utils_1.pathExists)(bundledTemplatePath))) {
1182
+ outputChannel.appendLine('[AgentIDE] Warning: bundled context.md template not found');
1183
+ return;
1184
+ }
1185
+ try {
1186
+ let content = await fs_1.promises.readFile(bundledTemplatePath, 'utf8');
1187
+ content = content.replace(/\{\{workspaceRoot\}\}/g, workspaceRoot);
1188
+ const stableDirs = additionalDirectories.slice().filter(Boolean).sort();
1189
+ const dirBlockRegex = /\{\{#additionalDirectories\}\}([\s\S]*?)\{\{\/additionalDirectories\}\}/;
1190
+ const match = content.match(dirBlockRegex);
1191
+ if (match) {
1192
+ const template = match[1];
1193
+ const expanded = stableDirs.length > 0
1194
+ ? stableDirs.map(dir => template.replace(/\{\{\.\}\}/g, dir)).join('')
1195
+ : '';
1196
+ content = content.replace(dirBlockRegex, expanded);
1197
+ }
1198
+ await fs_1.promises.mkdir(agentideDir, { recursive: true });
1199
+ await fs_1.promises.writeFile(contextFilePath, content, 'utf8');
1200
+ outputChannel.appendLine(`[AgentIDE] Created context template: ${contextFilePath}`);
1201
+ }
1202
+ catch (e) {
1203
+ outputChannel.appendLine(`[AgentIDE] Warning: failed to copy context template: ${e instanceof Error ? e.message : String(e)}`);
1204
+ }
1205
+ }
1206
+ /**
1207
+ * Ensure the active agent has a GHOST_TEXT.md (inline completions prompt).
1208
+ * Only copies from bundled templates if the file doesn't already exist.
1209
+ */
1210
+ async ensureGhostTextPrompt(workspaceRoot, outputChannel) {
1211
+ await this.ensureBundledPromptFile(workspaceRoot, paths_1.AGENT_CONSTANTS.GHOST_TEXT_FILE, outputChannel);
1212
+ }
1213
+ /**
1214
+ * Ensure the active agent has a TERMINAL_ASSIST.md (terminal command assist prompt).
1215
+ * Only copies from bundled templates if the file doesn't already exist.
1216
+ */
1217
+ async ensureTerminalAssistPrompt(workspaceRoot, outputChannel) {
1218
+ await this.ensureBundledPromptFile(workspaceRoot, paths_1.AGENT_CONSTANTS.TERMINAL_ASSIST_FILE, outputChannel);
1219
+ }
1220
+ /**
1221
+ * Ensure a bundled prompt file exists in the active agent's directory.
1222
+ * Copies from base-prompts/ if the file doesn't already exist.
1223
+ *
1224
+ * IMPORTANT: Uses getActiveAgentId() to respect the active pack.
1225
+ * See PLAN/ARCHITECTURE.md "Path Resolution Rules" for why this matters.
1226
+ */
1227
+ async ensureBundledPromptFile(workspaceRoot, fileName, outputChannel) {
1228
+ const agentideDir = this.getAgentideProjectDir(workspaceRoot);
1229
+ if (!(await (0, utils_1.pathExists)(agentideDir))) {
1230
+ return;
1231
+ }
1232
+ // Use active agent ID - never hardcode 'default' for pack-specific paths
1233
+ const activeAgentId = this.getActiveAgentId(workspaceRoot);
1234
+ const agentDir = path.join(agentideDir, 'agents', activeAgentId);
1235
+ const targetPath = path.join(agentDir, fileName);
1236
+ if (await (0, utils_1.pathExists)(targetPath)) {
1237
+ return; // Already exists - don't overwrite user edits
1238
+ }
1239
+ const bundledDir = this.getBundledBasePromptsDir();
1240
+ const bundledPath = bundledDir ? path.join(bundledDir, fileName) : undefined;
1241
+ if (!bundledPath || !(await (0, utils_1.pathExists)(bundledPath))) {
1242
+ return;
1243
+ }
1244
+ try {
1245
+ await fs_1.promises.mkdir(agentDir, { recursive: true });
1246
+ const content = await fs_1.promises.readFile(bundledPath, 'utf8');
1247
+ await fs_1.promises.writeFile(targetPath, content, 'utf8');
1248
+ outputChannel.appendLine(`[AgentIDE] Created ${fileName}: ${targetPath}`);
1249
+ }
1250
+ catch (e) {
1251
+ outputChannel.appendLine(`[AgentIDE] Warning: failed to copy ${fileName}: ${e instanceof Error ? e.message : String(e)}`);
1252
+ }
1253
+ }
1254
+ // -------------------------------------------------------------------------
1255
+ // Derived Directory Sync
1256
+ // -------------------------------------------------------------------------
1257
+ async importNewSkillsFromDerivedIntoAgentide(workspaceRoot, outputChannel, existingIngestState) {
1258
+ // Ingest skills to the active agent's skills dir
1259
+ const activeAgentId = this.getActiveAgentId(workspaceRoot);
1260
+ const agentideSkillsDir = this.getAgentSkillsDir(workspaceRoot, activeAgentId);
1261
+ const derivedSkillDirs = getDerivedEngineSyncTargets(workspaceRoot)
1262
+ .filter(engine => engine.includeSkills)
1263
+ .map(engine => path.join(workspaceRoot, engine.workspaceDir, 'skills'));
1264
+ await fs_1.promises.mkdir(agentideSkillsDir, { recursive: true });
1265
+ const ingestState = {
1266
+ ...(existingIngestState ?? {}),
1267
+ };
1268
+ let agentideContentChanged = false;
1269
+ let ingestStateChanged = false;
1270
+ for (const derivedRoot of derivedSkillDirs) {
1271
+ try {
1272
+ const entries = await fs_1.promises.readdir(derivedRoot, { withFileTypes: true });
1273
+ for (const entry of entries) {
1274
+ if (!entry.isDirectory()) {
1275
+ continue;
1276
+ }
1277
+ if (entry.name.startsWith('.')) {
1278
+ continue;
1279
+ }
1280
+ const srcSkillDir = path.join(derivedRoot, entry.name);
1281
+ const srcSkillFile = path.join(srcSkillDir, 'SKILL.md');
1282
+ if (!(await (0, utils_1.pathExists)(srcSkillFile))) {
1283
+ continue;
1284
+ }
1285
+ const dstSkillDir = path.join(agentideSkillsDir, entry.name);
1286
+ const dstSkillFile = path.join(dstSkillDir, 'SKILL.md');
1287
+ const { sha256: srcSha } = await (0, utils_1.readFileSha256)(srcSkillFile);
1288
+ if (!(await (0, utils_1.pathExists)(dstSkillDir))) {
1289
+ await (0, utils_1.copyDirRecursive)(srcSkillDir, dstSkillDir);
1290
+ agentideContentChanged = true;
1291
+ ingestState[entry.name] = {
1292
+ lastIngestedSkillMdSha256: srcSha,
1293
+ lastIngestedAt: new Date().toISOString(),
1294
+ };
1295
+ ingestStateChanged = true;
1296
+ outputChannel.appendLine(`[AgentIDE] Imported new skill from derived dir: ${entry.name} → ${activeAgentId}`);
1297
+ continue;
1298
+ }
1299
+ if (!(await (0, utils_1.pathExists)(dstSkillFile))) {
1300
+ continue;
1301
+ }
1302
+ const { sha256: dstSha } = await (0, utils_1.readFileSha256)(dstSkillFile);
1303
+ const state = ingestState[entry.name];
1304
+ if (!state && srcSha === dstSha) {
1305
+ ingestState[entry.name] = {
1306
+ lastIngestedSkillMdSha256: dstSha,
1307
+ lastIngestedAt: new Date().toISOString(),
1308
+ };
1309
+ ingestStateChanged = true;
1310
+ continue;
1311
+ }
1312
+ if (!state) {
1313
+ continue;
1314
+ }
1315
+ if (dstSha !== state.lastIngestedSkillMdSha256) {
1316
+ outputChannel.appendLine(`[AgentIDE] Skipped ingest for skill '${entry.name}' (conflict: .agentide diverged from last ingested state)`);
1317
+ continue;
1318
+ }
1319
+ if (srcSha !== dstSha) {
1320
+ await fs_1.promises.copyFile(srcSkillFile, dstSkillFile);
1321
+ agentideContentChanged = true;
1322
+ ingestState[entry.name] = {
1323
+ lastIngestedSkillMdSha256: srcSha,
1324
+ lastIngestedAt: new Date().toISOString(),
1325
+ };
1326
+ ingestStateChanged = true;
1327
+ outputChannel.appendLine(`[AgentIDE] Ingested updated SKILL.md from derived dir: ${entry.name}`);
1328
+ }
1329
+ }
1330
+ }
1331
+ catch {
1332
+ // ignore
1333
+ }
1334
+ }
1335
+ return { agentideContentChanged, ingestStateChanged, nextIngestState: ingestState };
1336
+ }
1337
+ async readAgentideContextMd(workspaceRoot) {
1338
+ const activeAgentId = this.getActiveAgentId(workspaceRoot);
1339
+ const contextFilePath = this.getAgentContextFile(workspaceRoot, activeAgentId);
1340
+ try {
1341
+ return await fs_1.promises.readFile(contextFilePath, 'utf8');
1342
+ }
1343
+ catch (e) {
1344
+ if ((0, utils_1.getErrorCode)(e) === 'ENOENT') {
1345
+ return undefined;
1346
+ }
1347
+ throw e;
1348
+ }
1349
+ }
1350
+ async writeDerivedDirReadme(targetDir, providerId, outputChannel, sourceOfTruthLines) {
1351
+ try {
1352
+ const readmePath = path.join(targetDir, AGENTIDE_DERIVED_DIR_README);
1353
+ const providerName = providerId.charAt(0).toUpperCase() + providerId.slice(1);
1354
+ const sourceLines = (sourceOfTruthLines && sourceOfTruthLines.length > 0)
1355
+ ? sourceOfTruthLines
1356
+ : [
1357
+ '- `.agentide/agents/<agentId>/AGENTS.md` (project rules)',
1358
+ '- `.agentide/agents/<agentId>/rules/*.md` (additional rule fragments)',
1359
+ '- `.agentide/agents/<agentId>/skills/*/SKILL.md` (project skills)',
1360
+ ];
1361
+ const contents = [
1362
+ `# Managed by ${branding_1.AGENTIDE_BRAND_NAME}`,
1363
+ '',
1364
+ `This directory is managed by the ${branding_1.AGENTIDE_BRAND_NAME} extension.`,
1365
+ '',
1366
+ '## How It Works',
1367
+ '',
1368
+ '- **Skills** created here ARE synced back to `.agentide/agents/<id>/skills/`',
1369
+ '- **Rules files** (`CLAUDE.md`, `AGENTS.md`) are generated from the source and **not synced back**',
1370
+ ' - Edit rules in `.agentide/agents/<id>/AGENTS.md` instead',
1371
+ '- The source-of-truth is in:',
1372
+ ...sourceLines,
1373
+ '',
1374
+ '## Tips',
1375
+ '',
1376
+ '- Create/edit skills here or in `.agentide/` - both will stay in sync',
1377
+ '- To change project rules, edit `.agentide/agents/<id>/AGENTS.md` (not the rules file here)',
1378
+ '- This folder exists for ' + providerName + ' auto-discovery compatibility',
1379
+ '',
1380
+ ].join('\n');
1381
+ await (0, utils_1.writeFileIfChanged)(readmePath, contents);
1382
+ }
1383
+ catch (e) {
1384
+ outputChannel.appendLine(`[AgentIDE] Warning: failed to write ${providerId} derived README: ${e instanceof Error ? e.message : String(e)}`);
1385
+ }
1386
+ }
1387
+ async cleanDerivedRulesDir(rulesDir) {
1388
+ try {
1389
+ const existing = await fs_1.promises.readdir(rulesDir, { withFileTypes: true });
1390
+ const rmOps = existing
1391
+ .filter(e => e.isFile() && e.name.toLowerCase().endsWith('.md'))
1392
+ .map(e => fs_1.promises.rm(path.join(rulesDir, e.name), { force: true }));
1393
+ await Promise.all(rmOps);
1394
+ }
1395
+ catch {
1396
+ // ignore
1397
+ }
1398
+ }
1399
+ async syncAgentideProjectToDerivedDirs(workspaceRoots, outputChannel, agentIdOverride) {
1400
+ const roots = Array.from(new Set(workspaceRoots.map(r => r.trim()).filter(Boolean)));
1401
+ for (const root of roots) {
1402
+ if (!root || root === os.homedir()) {
1403
+ continue;
1404
+ }
1405
+ const agentideDir = this.getAgentideProjectDir(root);
1406
+ // Use override if provided, otherwise read from manifest
1407
+ const activeAgentId = agentIdOverride ?? this.getActiveAgentId(root);
1408
+ const agentideSkillsDir = this.getAgentSkillsDir(root, activeAgentId);
1409
+ const agentideRulesDir = this.getAgentRulesDir(root, activeAgentId);
1410
+ const agentideAgentsFile = this.getAgentAgentsFile(root, activeAgentId);
1411
+ if (!(await (0, utils_1.pathExists)(agentideDir))) {
1412
+ continue;
1413
+ }
1414
+ // Build derived directory paths from built-in and custom engine manifests.
1415
+ const derivedDirs = getDerivedEngineSyncTargets(root).map(engine => ({
1416
+ id: engine.id,
1417
+ displayName: engine.displayName,
1418
+ workspaceDir: engine.workspaceDir,
1419
+ rulesFile: engine.rulesFile,
1420
+ managedPaths: engine.managedPaths,
1421
+ includeSkills: engine.includeSkills,
1422
+ includeRules: engine.includeRules,
1423
+ fullPath: path.join(root, engine.workspaceDir),
1424
+ rulesPath: path.join(root, engine.workspaceDir, 'rules'),
1425
+ skillsPath: path.join(root, engine.workspaceDir, 'skills'),
1426
+ }));
1427
+ const sourceOfTruthLines = [
1428
+ `- \`.agentide/agents/${activeAgentId}/AGENTS.md\` (project rules)`,
1429
+ `- \`.agentide/agents/${activeAgentId}/rules/*.md\` (additional rule fragments)`,
1430
+ `- \`.agentide/agents/${activeAgentId}/skills/*/SKILL.md\` (project skills)`,
1431
+ ];
1432
+ // Phase 1: Create base directories
1433
+ await Promise.all(derivedDirs.map(d => fs_1.promises.mkdir(d.fullPath, { recursive: true })));
1434
+ // Phase 2: Write READMEs and create rules dirs
1435
+ await Promise.all(derivedDirs.flatMap(d => [
1436
+ this.writeDerivedDirReadme(d.fullPath, d.id, outputChannel, sourceOfTruthLines),
1437
+ fs_1.promises.mkdir(d.rulesPath, { recursive: true }),
1438
+ ]));
1439
+ // Phase 3: Write combined rules files
1440
+ const writeAgentsOps = [];
1441
+ const agentsFileExists = await (0, utils_1.pathExists)(agentideAgentsFile);
1442
+ if (agentsFileExists) {
1443
+ const [agentsContent, contextContent] = await Promise.all([
1444
+ fs_1.promises.readFile(agentideAgentsFile, 'utf8'),
1445
+ this.readAgentideContextMd(root),
1446
+ ]);
1447
+ let combined = (0, branding_1.applyBrandingTokens)(agentsContent).trimEnd();
1448
+ if (contextContent) {
1449
+ combined += '\n\n---\n\n' + contextContent.trimEnd() + '\n';
1450
+ }
1451
+ else {
1452
+ combined += '\n';
1453
+ }
1454
+ // Write rules file to each derived provider directory
1455
+ for (const d of derivedDirs) {
1456
+ writeAgentsOps.push((0, utils_1.writeFileIfChanged)(path.join(d.fullPath, d.rulesFile), combined));
1457
+ }
1458
+ }
1459
+ await Promise.all([
1460
+ ...writeAgentsOps,
1461
+ ...derivedDirs.filter(d => d.includeRules).map(d => this.cleanDerivedRulesDir(d.rulesPath)),
1462
+ ]);
1463
+ // Phase 4: Mirror .agentide/rules/*.md to each derived provider
1464
+ if (await (0, utils_1.pathExists)(agentideRulesDir)) {
1465
+ const entries = await fs_1.promises.readdir(agentideRulesDir, { withFileTypes: true });
1466
+ const ruleOps = entries
1467
+ .filter(e => e.isFile() && e.name.toLowerCase().endsWith('.md'))
1468
+ .flatMap(entry => {
1469
+ const src = path.join(agentideRulesDir, entry.name);
1470
+ return derivedDirs.filter(d => d.includeRules).map(d => (0, utils_1.symlinkOrCopyFile)(src, path.join(d.rulesPath, entry.name), outputChannel, `Rule ${entry.name} -> ${d.workspaceDir}`, { force: true }));
1471
+ });
1472
+ await Promise.all(ruleOps);
1473
+ }
1474
+ // Phase 5: Ensure skills directories exist
1475
+ await Promise.all(derivedDirs.filter(d => d.includeSkills).map(d => (0, utils_1.ensureRealDirectory)(d.skillsPath)));
1476
+ // Phase 6: Sync skills via file-level links
1477
+ if (await (0, utils_1.pathExists)(agentideSkillsDir)) {
1478
+ const entries = await fs_1.promises.readdir(agentideSkillsDir, { withFileTypes: true });
1479
+ const expectedSkills = [];
1480
+ for (const entry of entries) {
1481
+ if (!entry.isDirectory() || entry.name.startsWith('.')) {
1482
+ continue;
1483
+ }
1484
+ const srcSkillMd = path.join(agentideSkillsDir, entry.name, 'SKILL.md');
1485
+ if (!(await (0, utils_1.pathExists)(srcSkillMd))) {
1486
+ continue;
1487
+ }
1488
+ expectedSkills.push(entry.name);
1489
+ }
1490
+ const expectedSet = new Set(expectedSkills);
1491
+ const syncOneDerivedSkillsDir = async (dstSkillsRoot, label) => {
1492
+ try {
1493
+ const existing = await fs_1.promises.readdir(dstSkillsRoot, { withFileTypes: true });
1494
+ const rmOps = [];
1495
+ for (const e of existing) {
1496
+ if (!e.isDirectory() && !e.isSymbolicLink()) {
1497
+ continue;
1498
+ }
1499
+ if (e.name.startsWith('.')) {
1500
+ continue;
1501
+ }
1502
+ if (!expectedSet.has(e.name)) {
1503
+ rmOps.push(fs_1.promises.rm(path.join(dstSkillsRoot, e.name), { recursive: true, force: true }));
1504
+ }
1505
+ }
1506
+ await Promise.all(rmOps);
1507
+ }
1508
+ catch {
1509
+ // ignore
1510
+ }
1511
+ for (const skillName of expectedSkills) {
1512
+ const srcSkill = path.join(agentideSkillsDir, skillName);
1513
+ const dstSkill = path.join(dstSkillsRoot, skillName);
1514
+ await (0, utils_1.syncDirByFileLinks)(srcSkill, dstSkill, outputChannel, `${label}/skills/${skillName}`);
1515
+ }
1516
+ };
1517
+ await Promise.all(derivedDirs.filter(d => d.includeSkills).map(d => syncOneDerivedSkillsDir(d.skillsPath, d.workspaceDir)));
1518
+ }
1519
+ }
1520
+ }
1521
+ // -------------------------------------------------------------------------
1522
+ // AgentPack Support
1523
+ // -------------------------------------------------------------------------
1524
+ /**
1525
+ * Sync derived directories for an agent pack.
1526
+ * Creates .codex/ and .claude/ within the pack folder with synced content.
1527
+ */
1528
+ async syncPackDerivedDirs(packRoot, outputChannel) {
1529
+ if (!packRoot || !(await (0, utils_1.pathExists)(packRoot))) {
1530
+ return;
1531
+ }
1532
+ const agentsFile = path.join(packRoot, 'AGENTS.md');
1533
+ const rulesDir = path.join(packRoot, 'rules');
1534
+ const skillsDir = path.join(packRoot, 'skills');
1535
+ // Build derived directory paths from built-in and custom engine manifests.
1536
+ const derivedDirs = getDerivedEngineSyncTargets(packRoot).map(engine => ({
1537
+ id: engine.id,
1538
+ workspaceDir: engine.workspaceDir,
1539
+ rulesFile: engine.rulesFile,
1540
+ includeSkills: engine.includeSkills,
1541
+ includeRules: engine.includeRules,
1542
+ fullPath: path.join(packRoot, engine.workspaceDir),
1543
+ rulesPath: path.join(packRoot, engine.workspaceDir, 'rules'),
1544
+ skillsPath: path.join(packRoot, engine.workspaceDir, 'skills'),
1545
+ }));
1546
+ // Phase 1: Create base directories
1547
+ await Promise.all(derivedDirs.map(d => fs_1.promises.mkdir(d.fullPath, { recursive: true })));
1548
+ // Phase 2: Create rules dirs
1549
+ await Promise.all(derivedDirs.map(d => fs_1.promises.mkdir(d.rulesPath, { recursive: true })));
1550
+ // Phase 3: Write rules files
1551
+ if (await (0, utils_1.pathExists)(agentsFile)) {
1552
+ const agentsContent = (0, branding_1.applyBrandingTokens)(await fs_1.promises.readFile(agentsFile, 'utf8'));
1553
+ const contextFile = path.join(packRoot, 'context.md');
1554
+ let combined = agentsContent.trimEnd();
1555
+ if (await (0, utils_1.pathExists)(contextFile)) {
1556
+ const contextContent = await fs_1.promises.readFile(contextFile, 'utf8');
1557
+ combined += '\n\n---\n\n' + contextContent.trimEnd() + '\n';
1558
+ }
1559
+ else {
1560
+ combined += '\n';
1561
+ }
1562
+ for (const d of derivedDirs) {
1563
+ await (0, utils_1.writeFileIfChanged)(path.join(d.fullPath, d.rulesFile), combined);
1564
+ }
1565
+ }
1566
+ // Phase 4: Symlink rules
1567
+ if (await (0, utils_1.pathExists)(rulesDir)) {
1568
+ const entries = await fs_1.promises.readdir(rulesDir, { withFileTypes: true });
1569
+ for (const entry of entries) {
1570
+ if (!entry.isFile() || !entry.name.toLowerCase().endsWith('.md')) {
1571
+ continue;
1572
+ }
1573
+ const src = path.join(rulesDir, entry.name);
1574
+ for (const d of derivedDirs.filter(d => d.includeRules)) {
1575
+ await (0, utils_1.symlinkOrCopyFile)(src, path.join(d.rulesPath, entry.name), outputChannel, `Pack rule ${entry.name}`, { force: true });
1576
+ }
1577
+ }
1578
+ }
1579
+ // Phase 5: Ensure skills directories exist
1580
+ await Promise.all(derivedDirs.filter(d => d.includeSkills).map(d => (0, utils_1.ensureRealDirectory)(d.skillsPath)));
1581
+ // Phase 6: Sync skills
1582
+ if (await (0, utils_1.pathExists)(skillsDir)) {
1583
+ const entries = await fs_1.promises.readdir(skillsDir, { withFileTypes: true });
1584
+ for (const entry of entries) {
1585
+ if (!entry.isDirectory() || entry.name.startsWith('.')) {
1586
+ continue;
1587
+ }
1588
+ const srcSkill = path.join(skillsDir, entry.name);
1589
+ for (const d of derivedDirs.filter(d => d.includeSkills)) {
1590
+ await (0, utils_1.syncDirByFileLinks)(srcSkill, path.join(d.skillsPath, entry.name), outputChannel, `Pack skill ${entry.name}`);
1591
+ }
1592
+ }
1593
+ }
1594
+ outputChannel.appendLine(`[AgentPack] Synced derived dirs for pack at ${packRoot}`);
1595
+ }
1596
+ /**
1597
+ * Switch the workspace's .claude/ and .codex/ to use content from the specified pack.
1598
+ * If packId is undefined or 'default', syncs from .agentide/ (default agent).
1599
+ * Otherwise, syncs from .agentide/agents/{packId}/.
1600
+ *
1601
+ * This replaces the content of workspace-root derived directories with the pack's content.
1602
+ *
1603
+ * @param importDerivedSkills If true, import any new skills from derived dirs to the
1604
+ * current pack's source before switching. This preserves agent-created skills.
1605
+ */
1606
+ async switchToPack(workspaceRoot, packId, outputChannel, options) {
1607
+ // Import any new skills from derived dirs before switching
1608
+ if (options?.importDerivedSkills) {
1609
+ const manifest = await this.readAgentideManifest(workspaceRoot);
1610
+ await this.importNewSkillsFromDerivedIntoAgentide(workspaceRoot, outputChannel, manifest?.derivedSkillIngestState);
1611
+ }
1612
+ if (!packId || packId === 'default') {
1613
+ // Default agent: sync from .agentide/agents/default/
1614
+ outputChannel.appendLine(`[AgentPack] Switching to default workspace agent`);
1615
+ await this.syncAgentideProjectToDerivedDirs([workspaceRoot], outputChannel, 'default');
1616
+ await this.seedAgentDataIfNeeded(workspaceRoot, AGENTIDE_DEFAULT_AGENT_ID, outputChannel);
1617
+ await this.writeAgentideManifest(workspaceRoot, outputChannel, { packIdOverride: 'default' });
1618
+ return undefined;
1619
+ }
1620
+ // Pack mode: sync from .agentide/agents/{packId}/
1621
+ const packRoot = path.join(workspaceRoot, AGENTIDE_DIR_NAME, 'agents', packId);
1622
+ if (!(await (0, utils_1.pathExists)(packRoot))) {
1623
+ outputChannel.appendLine(`[AgentPack] Warning: Pack not found at ${packRoot}, falling back to default`);
1624
+ await this.syncAgentideProjectToDerivedDirs([workspaceRoot], outputChannel, 'default');
1625
+ await this.writeAgentideManifest(workspaceRoot, outputChannel, { packIdOverride: 'default' });
1626
+ return undefined;
1627
+ }
1628
+ outputChannel.appendLine(`[AgentPack] Switching to pack: ${packId}`);
1629
+ const agentsFile = path.join(packRoot, 'AGENTS.md');
1630
+ const rulesDir = path.join(packRoot, 'rules');
1631
+ const skillsDir = path.join(packRoot, 'skills');
1632
+ // Build derived directory paths at WORKSPACE ROOT (not pack root)
1633
+ const derivedDirs = getDerivedEngineSyncTargets(workspaceRoot).map(engine => ({
1634
+ id: engine.id,
1635
+ workspaceDir: engine.workspaceDir,
1636
+ rulesFile: engine.rulesFile,
1637
+ includeSkills: engine.includeSkills,
1638
+ includeRules: engine.includeRules,
1639
+ fullPath: path.join(workspaceRoot, engine.workspaceDir),
1640
+ rulesPath: path.join(workspaceRoot, engine.workspaceDir, 'rules'),
1641
+ skillsPath: path.join(workspaceRoot, engine.workspaceDir, 'skills'),
1642
+ }));
1643
+ const sourceOfTruthLines = [
1644
+ `- \`.agentide/agents/${packId}/AGENTS.md\` (pack rules)`,
1645
+ `- \`.agentide/agents/${packId}/rules/*.md\` (additional rule fragments)`,
1646
+ `- \`.agentide/agents/${packId}/skills/*/SKILL.md\` (pack skills)`,
1647
+ ];
1648
+ // Phase 1: Create base directories
1649
+ await Promise.all(derivedDirs.map(d => fs_1.promises.mkdir(d.fullPath, { recursive: true })));
1650
+ // Phase 2: Write READMEs and create rules dirs
1651
+ await Promise.all(derivedDirs.flatMap(d => [
1652
+ this.writeDerivedDirReadme(d.fullPath, d.id, outputChannel, sourceOfTruthLines),
1653
+ fs_1.promises.mkdir(d.rulesPath, { recursive: true }),
1654
+ ]));
1655
+ // Phase 3: Write combined rules files from pack's AGENTS.md
1656
+ if (await (0, utils_1.pathExists)(agentsFile)) {
1657
+ const agentsContent = (0, branding_1.applyBrandingTokens)(await fs_1.promises.readFile(agentsFile, 'utf8'));
1658
+ const contextFile = path.join(packRoot, 'context.md');
1659
+ let combined = agentsContent.trimEnd();
1660
+ if (await (0, utils_1.pathExists)(contextFile)) {
1661
+ const contextContent = await fs_1.promises.readFile(contextFile, 'utf8');
1662
+ combined += '\n\n---\n\n' + contextContent.trimEnd() + '\n';
1663
+ }
1664
+ else {
1665
+ combined += '\n';
1666
+ }
1667
+ for (const d of derivedDirs) {
1668
+ await (0, utils_1.writeFileIfChanged)(path.join(d.fullPath, d.rulesFile), combined);
1669
+ }
1670
+ }
1671
+ // Clean rules dirs before syncing
1672
+ await Promise.all(derivedDirs.filter(d => d.includeRules).map(d => this.cleanDerivedRulesDir(d.rulesPath)));
1673
+ // Phase 4: Symlink rules from pack
1674
+ if (await (0, utils_1.pathExists)(rulesDir)) {
1675
+ const entries = await fs_1.promises.readdir(rulesDir, { withFileTypes: true });
1676
+ for (const entry of entries) {
1677
+ if (!entry.isFile() || !entry.name.toLowerCase().endsWith('.md')) {
1678
+ continue;
1679
+ }
1680
+ const src = path.join(rulesDir, entry.name);
1681
+ for (const d of derivedDirs.filter(d => d.includeRules)) {
1682
+ await (0, utils_1.symlinkOrCopyFile)(src, path.join(d.rulesPath, entry.name), outputChannel, `Pack rule ${entry.name}`, { force: true });
1683
+ }
1684
+ }
1685
+ }
1686
+ // Phase 5: Ensure skills directories exist and clean stale skills
1687
+ await Promise.all(derivedDirs.filter(d => d.includeSkills).map(d => (0, utils_1.ensureRealDirectory)(d.skillsPath)));
1688
+ // Phase 6: Sync skills from pack
1689
+ if (await (0, utils_1.pathExists)(skillsDir)) {
1690
+ const entries = await fs_1.promises.readdir(skillsDir, { withFileTypes: true });
1691
+ const expectedSkills = [];
1692
+ for (const entry of entries) {
1693
+ if (!entry.isDirectory() || entry.name.startsWith('.')) {
1694
+ continue;
1695
+ }
1696
+ const srcSkillMd = path.join(skillsDir, entry.name, 'SKILL.md');
1697
+ if (!(await (0, utils_1.pathExists)(srcSkillMd))) {
1698
+ continue;
1699
+ }
1700
+ expectedSkills.push(entry.name);
1701
+ }
1702
+ const expectedSet = new Set(expectedSkills);
1703
+ // Remove skills not in pack
1704
+ for (const d of derivedDirs.filter(d => d.includeSkills)) {
1705
+ try {
1706
+ const existing = await fs_1.promises.readdir(d.skillsPath, { withFileTypes: true });
1707
+ for (const e of existing) {
1708
+ if ((e.isDirectory() || e.isSymbolicLink()) && !expectedSet.has(e.name)) {
1709
+ await fs_1.promises.rm(path.join(d.skillsPath, e.name), { recursive: true, force: true });
1710
+ outputChannel.appendLine(`[AgentPack] Removed stale skill: ${e.name}`);
1711
+ }
1712
+ }
1713
+ }
1714
+ catch {
1715
+ // Skills dir might not exist yet
1716
+ }
1717
+ }
1718
+ // Sync pack skills
1719
+ for (const skillName of expectedSkills) {
1720
+ const srcSkill = path.join(skillsDir, skillName);
1721
+ for (const d of derivedDirs.filter(d => d.includeSkills)) {
1722
+ await (0, utils_1.syncDirByFileLinks)(srcSkill, path.join(d.skillsPath, skillName), outputChannel, `Pack skill ${skillName}`);
1723
+ }
1724
+ }
1725
+ }
1726
+ else {
1727
+ // Pack has no skills - clear derived skills dirs
1728
+ for (const d of derivedDirs.filter(d => d.includeSkills)) {
1729
+ try {
1730
+ const existing = await fs_1.promises.readdir(d.skillsPath, { withFileTypes: true });
1731
+ for (const e of existing) {
1732
+ if (e.isDirectory() || e.isSymbolicLink()) {
1733
+ await fs_1.promises.rm(path.join(d.skillsPath, e.name), { recursive: true, force: true });
1734
+ }
1735
+ }
1736
+ }
1737
+ catch {
1738
+ // Ignore
1739
+ }
1740
+ }
1741
+ }
1742
+ outputChannel.appendLine(`[AgentPack] Switched workspace to pack: ${packId}`);
1743
+ await this.seedAgentDataIfNeeded(workspaceRoot, packId, outputChannel);
1744
+ await this.writeAgentideManifest(workspaceRoot, outputChannel, { packIdOverride: packId });
1745
+ return packId;
1746
+ }
1747
+ /**
1748
+ * Load the agentide manifest for a workspace.
1749
+ */
1750
+ async loadAgentideManifest(workspaceRoot) {
1751
+ const manifestPath = path.join(workspaceRoot, AGENTIDE_DIR_NAME, AGENTIDE_MANIFEST_FILE);
1752
+ try {
1753
+ const content = await fs_1.promises.readFile(manifestPath, 'utf8');
1754
+ return JSON.parse(content);
1755
+ }
1756
+ catch {
1757
+ return undefined;
1758
+ }
1759
+ }
1760
+ // -------------------------------------------------------------------------
1761
+ // Installed Agents (separate file: installed-agents.json)
1762
+ // -------------------------------------------------------------------------
1763
+ getInstalledAgentsFilePath(workspaceRoot) {
1764
+ return path.join(workspaceRoot, AGENTIDE_DIR_NAME, AGENTIDE_INSTALLED_AGENTS_FILE);
1765
+ }
1766
+ /**
1767
+ * Load installed agents from the dedicated file.
1768
+ */
1769
+ async loadInstalledAgents(workspaceRoot) {
1770
+ const filePath = this.getInstalledAgentsFilePath(workspaceRoot);
1771
+ try {
1772
+ const content = await fs_1.promises.readFile(filePath, 'utf8');
1773
+ const data = JSON.parse(content);
1774
+ return data.agents ?? [];
1775
+ }
1776
+ catch {
1777
+ return [];
1778
+ }
1779
+ }
1780
+ /**
1781
+ * Save installed agents to the dedicated file.
1782
+ */
1783
+ async saveInstalledAgents(workspaceRoot, agents) {
1784
+ const filePath = this.getInstalledAgentsFilePath(workspaceRoot);
1785
+ const data = {
1786
+ version: 1,
1787
+ agents,
1788
+ };
1789
+ await fs_1.promises.mkdir(path.dirname(filePath), { recursive: true });
1790
+ await fs_1.promises.writeFile(filePath, JSON.stringify(data, null, 2));
1791
+ }
1792
+ /**
1793
+ * Update or add an installed agent.
1794
+ */
1795
+ async updateInstalledAgent(workspaceRoot, agent, outputChannel) {
1796
+ const agents = await this.loadInstalledAgents(workspaceRoot);
1797
+ // Remove existing entry if present
1798
+ const filtered = agents.filter(a => a.id !== agent.id);
1799
+ // Add new entry
1800
+ const entry = {
1801
+ id: agent.id,
1802
+ version: agent.version,
1803
+ source: agent.source,
1804
+ installedAt: new Date().toISOString(),
1805
+ featureSetHash: agent.featureSetHash,
1806
+ preferredEngine: agent.preferredEngine,
1807
+ preferredModel: agent.preferredModel,
1808
+ preferredCheapModel: agent.preferredCheapModel,
1809
+ status: 'active',
1810
+ };
1811
+ filtered.push(entry);
1812
+ await this.saveInstalledAgents(workspaceRoot, filtered);
1813
+ (0, utils_1.logInfo)(outputChannel, `[Agent] Registered agent: ${agent.id}`);
1814
+ }
1815
+ /**
1816
+ * Remove an installed agent.
1817
+ */
1818
+ async removeInstalledAgent(workspaceRoot, agentId, outputChannel) {
1819
+ const agents = await this.loadInstalledAgents(workspaceRoot);
1820
+ const filtered = agents.filter(a => a.id !== agentId);
1821
+ if (filtered.length !== agents.length) {
1822
+ await this.saveInstalledAgents(workspaceRoot, filtered);
1823
+ (0, utils_1.logInfo)(outputChannel, `[Agent] Removed agent: ${agentId}`);
1824
+ }
1825
+ }
1826
+ }
1827
+ exports.SyncService = SyncService;