hammoc 1.4.0 → 1.6.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 (353) hide show
  1. package/README.md +428 -405
  2. package/bin/hammoc.js +0 -6
  3. package/package.json +100 -94
  4. package/packages/client/dist/assets/agentExampleHighlight-ltj9ce0U.js +1 -0
  5. package/packages/client/dist/assets/commandTokenHighlight-ji_ViMb4.js +1 -0
  6. package/packages/client/dist/assets/index-B-DiRGuz.js +2 -0
  7. package/packages/client/dist/assets/index-B09doO8H.js +139 -0
  8. package/packages/client/dist/assets/index-BT4RIi0U.js +1523 -0
  9. package/packages/client/dist/assets/index-DyNJ5jEW.css +32 -0
  10. package/packages/client/dist/assets/snippetTokenHighlight-CP3v4o2g.js +1 -0
  11. package/packages/client/dist/index.html +2 -2
  12. package/packages/client/dist/sw.js +1 -1
  13. package/packages/server/dist/app.d.ts.map +1 -1
  14. package/packages/server/dist/app.js +13 -21
  15. package/packages/server/dist/app.js.map +1 -1
  16. package/packages/server/dist/controllers/bmadCoreConfigController.d.ts +41 -0
  17. package/packages/server/dist/controllers/bmadCoreConfigController.d.ts.map +1 -0
  18. package/packages/server/dist/controllers/bmadCoreConfigController.js +172 -0
  19. package/packages/server/dist/controllers/bmadCoreConfigController.js.map +1 -0
  20. package/packages/server/dist/controllers/claudeMdController.d.ts +26 -0
  21. package/packages/server/dist/controllers/claudeMdController.d.ts.map +1 -0
  22. package/packages/server/dist/controllers/claudeMdController.js +158 -0
  23. package/packages/server/dist/controllers/claudeMdController.js.map +1 -0
  24. package/packages/server/dist/controllers/contextBuilderController.d.ts +43 -0
  25. package/packages/server/dist/controllers/contextBuilderController.d.ts.map +1 -0
  26. package/packages/server/dist/controllers/contextBuilderController.js +159 -0
  27. package/packages/server/dist/controllers/contextBuilderController.js.map +1 -0
  28. package/packages/server/dist/controllers/harnessAgentController.d.ts +35 -0
  29. package/packages/server/dist/controllers/harnessAgentController.d.ts.map +1 -0
  30. package/packages/server/dist/controllers/harnessAgentController.js +372 -0
  31. package/packages/server/dist/controllers/harnessAgentController.js.map +1 -0
  32. package/packages/server/dist/controllers/harnessBundleController.d.ts +37 -0
  33. package/packages/server/dist/controllers/harnessBundleController.d.ts.map +1 -0
  34. package/packages/server/dist/controllers/harnessBundleController.js +312 -0
  35. package/packages/server/dist/controllers/harnessBundleController.js.map +1 -0
  36. package/packages/server/dist/controllers/harnessCommandController.d.ts +35 -0
  37. package/packages/server/dist/controllers/harnessCommandController.d.ts.map +1 -0
  38. package/packages/server/dist/controllers/harnessCommandController.js +415 -0
  39. package/packages/server/dist/controllers/harnessCommandController.js.map +1 -0
  40. package/packages/server/dist/controllers/harnessController.d.ts +21 -0
  41. package/packages/server/dist/controllers/harnessController.d.ts.map +1 -0
  42. package/packages/server/dist/controllers/harnessController.js +176 -0
  43. package/packages/server/dist/controllers/harnessController.js.map +1 -0
  44. package/packages/server/dist/controllers/harnessHookController.d.ts +32 -0
  45. package/packages/server/dist/controllers/harnessHookController.d.ts.map +1 -0
  46. package/packages/server/dist/controllers/harnessHookController.js +406 -0
  47. package/packages/server/dist/controllers/harnessHookController.js.map +1 -0
  48. package/packages/server/dist/controllers/harnessLintController.d.ts +18 -0
  49. package/packages/server/dist/controllers/harnessLintController.d.ts.map +1 -0
  50. package/packages/server/dist/controllers/harnessLintController.js +72 -0
  51. package/packages/server/dist/controllers/harnessLintController.js.map +1 -0
  52. package/packages/server/dist/controllers/harnessMcpController.d.ts +28 -0
  53. package/packages/server/dist/controllers/harnessMcpController.d.ts.map +1 -0
  54. package/packages/server/dist/controllers/harnessMcpController.js +371 -0
  55. package/packages/server/dist/controllers/harnessMcpController.js.map +1 -0
  56. package/packages/server/dist/controllers/harnessPluginController.d.ts +17 -0
  57. package/packages/server/dist/controllers/harnessPluginController.d.ts.map +1 -0
  58. package/packages/server/dist/controllers/harnessPluginController.js +115 -0
  59. package/packages/server/dist/controllers/harnessPluginController.js.map +1 -0
  60. package/packages/server/dist/controllers/harnessShareScopeController.d.ts +24 -0
  61. package/packages/server/dist/controllers/harnessShareScopeController.d.ts.map +1 -0
  62. package/packages/server/dist/controllers/harnessShareScopeController.js +120 -0
  63. package/packages/server/dist/controllers/harnessShareScopeController.js.map +1 -0
  64. package/packages/server/dist/controllers/harnessSkillController.d.ts +32 -0
  65. package/packages/server/dist/controllers/harnessSkillController.d.ts.map +1 -0
  66. package/packages/server/dist/controllers/harnessSkillController.js +453 -0
  67. package/packages/server/dist/controllers/harnessSkillController.js.map +1 -0
  68. package/packages/server/dist/controllers/marketplaceController.d.ts +19 -0
  69. package/packages/server/dist/controllers/marketplaceController.d.ts.map +1 -0
  70. package/packages/server/dist/controllers/marketplaceController.js +74 -0
  71. package/packages/server/dist/controllers/marketplaceController.js.map +1 -0
  72. package/packages/server/dist/controllers/observabilityController.d.ts +32 -0
  73. package/packages/server/dist/controllers/observabilityController.d.ts.map +1 -0
  74. package/packages/server/dist/controllers/observabilityController.js +148 -0
  75. package/packages/server/dist/controllers/observabilityController.js.map +1 -0
  76. package/packages/server/dist/controllers/projectController.d.ts.map +1 -1
  77. package/packages/server/dist/controllers/projectController.js +11 -0
  78. package/packages/server/dist/controllers/projectController.js.map +1 -1
  79. package/packages/server/dist/controllers/snippetController.d.ts +35 -0
  80. package/packages/server/dist/controllers/snippetController.d.ts.map +1 -0
  81. package/packages/server/dist/controllers/snippetController.js +294 -0
  82. package/packages/server/dist/controllers/snippetController.js.map +1 -0
  83. package/packages/server/dist/handlers/streamCallbacks.d.ts +8 -0
  84. package/packages/server/dist/handlers/streamCallbacks.d.ts.map +1 -1
  85. package/packages/server/dist/handlers/streamCallbacks.js +8 -0
  86. package/packages/server/dist/handlers/streamCallbacks.js.map +1 -1
  87. package/packages/server/dist/handlers/websocket.d.ts +15 -0
  88. package/packages/server/dist/handlers/websocket.d.ts.map +1 -1
  89. package/packages/server/dist/handlers/websocket.js +103 -2
  90. package/packages/server/dist/handlers/websocket.js.map +1 -1
  91. package/packages/server/dist/index.js +5 -0
  92. package/packages/server/dist/index.js.map +1 -1
  93. package/packages/server/dist/locales/en/server.json +37 -4
  94. package/packages/server/dist/locales/es/server.json +0 -4
  95. package/packages/server/dist/locales/ja/server.json +0 -4
  96. package/packages/server/dist/locales/ko/server.json +0 -4
  97. package/packages/server/dist/locales/pt/server.json +0 -4
  98. package/packages/server/dist/locales/zh-CN/server.json +0 -4
  99. package/packages/server/dist/routes/harness.d.ts +8 -0
  100. package/packages/server/dist/routes/harness.d.ts.map +1 -0
  101. package/packages/server/dist/routes/harness.js +150 -0
  102. package/packages/server/dist/routes/harness.js.map +1 -0
  103. package/packages/server/dist/routes/projects.d.ts.map +1 -1
  104. package/packages/server/dist/routes/projects.js +5 -60
  105. package/packages/server/dist/routes/projects.js.map +1 -1
  106. package/packages/server/dist/routes/snippets.d.ts +14 -0
  107. package/packages/server/dist/routes/snippets.d.ts.map +1 -0
  108. package/packages/server/dist/routes/snippets.js +27 -0
  109. package/packages/server/dist/routes/snippets.js.map +1 -0
  110. package/packages/server/dist/services/bmadCoreConfigService.d.ts +86 -0
  111. package/packages/server/dist/services/bmadCoreConfigService.d.ts.map +1 -0
  112. package/packages/server/dist/services/bmadCoreConfigService.js +175 -0
  113. package/packages/server/dist/services/bmadCoreConfigService.js.map +1 -0
  114. package/packages/server/dist/services/bmadStatusService.d.ts +15 -2
  115. package/packages/server/dist/services/bmadStatusService.d.ts.map +1 -1
  116. package/packages/server/dist/services/bmadStatusService.js +146 -37
  117. package/packages/server/dist/services/bmadStatusService.js.map +1 -1
  118. package/packages/server/dist/services/chatService.d.ts +3 -0
  119. package/packages/server/dist/services/chatService.d.ts.map +1 -1
  120. package/packages/server/dist/services/chatService.js +28 -7
  121. package/packages/server/dist/services/chatService.js.map +1 -1
  122. package/packages/server/dist/services/claudeMdService.d.ts +48 -0
  123. package/packages/server/dist/services/claudeMdService.d.ts.map +1 -0
  124. package/packages/server/dist/services/claudeMdService.js +240 -0
  125. package/packages/server/dist/services/claudeMdService.js.map +1 -0
  126. package/packages/server/dist/services/commandService.d.ts +10 -0
  127. package/packages/server/dist/services/commandService.d.ts.map +1 -1
  128. package/packages/server/dist/services/commandService.js +129 -4
  129. package/packages/server/dist/services/commandService.js.map +1 -1
  130. package/packages/server/dist/services/contextBuilderScriptTemplate.d.ts +24 -0
  131. package/packages/server/dist/services/contextBuilderScriptTemplate.d.ts.map +1 -0
  132. package/packages/server/dist/services/contextBuilderScriptTemplate.js +181 -0
  133. package/packages/server/dist/services/contextBuilderScriptTemplate.js.map +1 -0
  134. package/packages/server/dist/services/contextBuilderService.d.ts +68 -0
  135. package/packages/server/dist/services/contextBuilderService.d.ts.map +1 -0
  136. package/packages/server/dist/services/contextBuilderService.js +345 -0
  137. package/packages/server/dist/services/contextBuilderService.js.map +1 -0
  138. package/packages/server/dist/services/fileWatcherService.d.ts +24 -0
  139. package/packages/server/dist/services/fileWatcherService.d.ts.map +1 -1
  140. package/packages/server/dist/services/fileWatcherService.js +232 -1
  141. package/packages/server/dist/services/fileWatcherService.js.map +1 -1
  142. package/packages/server/dist/services/harnessAgentService.d.ts +97 -0
  143. package/packages/server/dist/services/harnessAgentService.d.ts.map +1 -0
  144. package/packages/server/dist/services/harnessAgentService.js +988 -0
  145. package/packages/server/dist/services/harnessAgentService.js.map +1 -0
  146. package/packages/server/dist/services/harnessBundleService.d.ts +145 -0
  147. package/packages/server/dist/services/harnessBundleService.d.ts.map +1 -0
  148. package/packages/server/dist/services/harnessBundleService.js +1318 -0
  149. package/packages/server/dist/services/harnessBundleService.js.map +1 -0
  150. package/packages/server/dist/services/harnessCommandService.d.ts +81 -0
  151. package/packages/server/dist/services/harnessCommandService.d.ts.map +1 -0
  152. package/packages/server/dist/services/harnessCommandService.js +917 -0
  153. package/packages/server/dist/services/harnessCommandService.js.map +1 -0
  154. package/packages/server/dist/services/harnessHookService.d.ts +82 -0
  155. package/packages/server/dist/services/harnessHookService.d.ts.map +1 -0
  156. package/packages/server/dist/services/harnessHookService.js +1112 -0
  157. package/packages/server/dist/services/harnessHookService.js.map +1 -0
  158. package/packages/server/dist/services/harnessLintService.d.ts +49 -0
  159. package/packages/server/dist/services/harnessLintService.d.ts.map +1 -0
  160. package/packages/server/dist/services/harnessLintService.js +628 -0
  161. package/packages/server/dist/services/harnessLintService.js.map +1 -0
  162. package/packages/server/dist/services/harnessMcpService.d.ts +100 -0
  163. package/packages/server/dist/services/harnessMcpService.d.ts.map +1 -0
  164. package/packages/server/dist/services/harnessMcpService.js +884 -0
  165. package/packages/server/dist/services/harnessMcpService.js.map +1 -0
  166. package/packages/server/dist/services/harnessPluginService.d.ts +66 -0
  167. package/packages/server/dist/services/harnessPluginService.d.ts.map +1 -0
  168. package/packages/server/dist/services/harnessPluginService.js +559 -0
  169. package/packages/server/dist/services/harnessPluginService.js.map +1 -0
  170. package/packages/server/dist/services/harnessService.d.ts +40 -0
  171. package/packages/server/dist/services/harnessService.d.ts.map +1 -0
  172. package/packages/server/dist/services/harnessService.js +222 -0
  173. package/packages/server/dist/services/harnessService.js.map +1 -0
  174. package/packages/server/dist/services/harnessShareScopeService.d.ts +50 -0
  175. package/packages/server/dist/services/harnessShareScopeService.d.ts.map +1 -0
  176. package/packages/server/dist/services/harnessShareScopeService.js +158 -0
  177. package/packages/server/dist/services/harnessShareScopeService.js.map +1 -0
  178. package/packages/server/dist/services/harnessSkillService.d.ts +70 -0
  179. package/packages/server/dist/services/harnessSkillService.d.ts.map +1 -0
  180. package/packages/server/dist/services/harnessSkillService.js +636 -0
  181. package/packages/server/dist/services/harnessSkillService.js.map +1 -0
  182. package/packages/server/dist/services/issueService.d.ts.map +1 -1
  183. package/packages/server/dist/services/issueService.js +3 -1
  184. package/packages/server/dist/services/issueService.js.map +1 -1
  185. package/packages/server/dist/services/manualSyncService.d.ts +19 -0
  186. package/packages/server/dist/services/manualSyncService.d.ts.map +1 -0
  187. package/packages/server/dist/services/manualSyncService.js +110 -0
  188. package/packages/server/dist/services/manualSyncService.js.map +1 -0
  189. package/packages/server/dist/services/marketplaceService.d.ts +50 -0
  190. package/packages/server/dist/services/marketplaceService.d.ts.map +1 -0
  191. package/packages/server/dist/services/marketplaceService.js +326 -0
  192. package/packages/server/dist/services/marketplaceService.js.map +1 -0
  193. package/packages/server/dist/services/observabilityService.d.ts +87 -0
  194. package/packages/server/dist/services/observabilityService.d.ts.map +1 -0
  195. package/packages/server/dist/services/observabilityService.js +0 -0
  196. package/packages/server/dist/services/observabilityService.js.map +1 -0
  197. package/packages/server/dist/services/queueService.d.ts.map +1 -1
  198. package/packages/server/dist/services/queueService.js +48 -2
  199. package/packages/server/dist/services/queueService.js.map +1 -1
  200. package/packages/server/dist/services/sessionService.d.ts +16 -0
  201. package/packages/server/dist/services/sessionService.d.ts.map +1 -1
  202. package/packages/server/dist/services/sessionService.js +125 -0
  203. package/packages/server/dist/services/sessionService.js.map +1 -1
  204. package/packages/server/dist/services/snippetService.d.ts +54 -0
  205. package/packages/server/dist/services/snippetService.d.ts.map +1 -0
  206. package/packages/server/dist/services/snippetService.js +371 -0
  207. package/packages/server/dist/services/snippetService.js.map +1 -0
  208. package/packages/server/dist/services/tokenCountService.d.ts +71 -0
  209. package/packages/server/dist/services/tokenCountService.d.ts.map +1 -0
  210. package/packages/server/dist/services/tokenCountService.js +313 -0
  211. package/packages/server/dist/services/tokenCountService.js.map +1 -0
  212. package/packages/server/dist/services/utils/applyYamlFrontmatterPatch.d.ts +46 -0
  213. package/packages/server/dist/services/utils/applyYamlFrontmatterPatch.d.ts.map +1 -0
  214. package/packages/server/dist/services/utils/applyYamlFrontmatterPatch.js +125 -0
  215. package/packages/server/dist/services/utils/applyYamlFrontmatterPatch.js.map +1 -0
  216. package/packages/server/dist/snippets/apply-qa-fixes +7 -5
  217. package/packages/server/dist/snippets/qa-review +5 -1
  218. package/packages/server/dist/snippets/split-commit +9 -0
  219. package/packages/server/dist/utils/applySecretsPolicy.d.ts +53 -0
  220. package/packages/server/dist/utils/applySecretsPolicy.d.ts.map +1 -0
  221. package/packages/server/dist/utils/applySecretsPolicy.js +204 -0
  222. package/packages/server/dist/utils/applySecretsPolicy.js.map +1 -0
  223. package/packages/server/dist/utils/assertNoSecretOnShared.d.ts +40 -0
  224. package/packages/server/dist/utils/assertNoSecretOnShared.d.ts.map +1 -0
  225. package/packages/server/dist/utils/assertNoSecretOnShared.js +47 -0
  226. package/packages/server/dist/utils/assertNoSecretOnShared.js.map +1 -0
  227. package/packages/server/dist/utils/assertSafeBundlePath.d.ts +29 -0
  228. package/packages/server/dist/utils/assertSafeBundlePath.d.ts.map +1 -0
  229. package/packages/server/dist/utils/assertSafeBundlePath.js +53 -0
  230. package/packages/server/dist/utils/assertSafeBundlePath.js.map +1 -0
  231. package/packages/server/dist/utils/bundledBinaryModelSupport.d.ts +7 -0
  232. package/packages/server/dist/utils/bundledBinaryModelSupport.d.ts.map +1 -0
  233. package/packages/server/dist/utils/bundledBinaryModelSupport.js +107 -0
  234. package/packages/server/dist/utils/bundledBinaryModelSupport.js.map +1 -0
  235. package/packages/server/dist/utils/effortUtils.d.ts +2 -2
  236. package/packages/server/dist/utils/effortUtils.js +5 -5
  237. package/packages/server/dist/utils/effortUtils.js.map +1 -1
  238. package/packages/server/dist/utils/errors.d.ts +1 -0
  239. package/packages/server/dist/utils/errors.d.ts.map +1 -1
  240. package/packages/server/dist/utils/errors.js +17 -0
  241. package/packages/server/dist/utils/errors.js.map +1 -1
  242. package/packages/server/dist/utils/gitignoreFilter.d.ts +23 -0
  243. package/packages/server/dist/utils/gitignoreFilter.d.ts.map +1 -0
  244. package/packages/server/dist/utils/gitignoreFilter.js +42 -0
  245. package/packages/server/dist/utils/gitignoreFilter.js.map +1 -0
  246. package/packages/server/dist/utils/harnessBundleSchema.d.ts +107 -0
  247. package/packages/server/dist/utils/harnessBundleSchema.d.ts.map +1 -0
  248. package/packages/server/dist/utils/harnessBundleSchema.js +89 -0
  249. package/packages/server/dist/utils/harnessBundleSchema.js.map +1 -0
  250. package/packages/server/dist/utils/harnessPaths.d.ts +74 -0
  251. package/packages/server/dist/utils/harnessPaths.d.ts.map +1 -0
  252. package/packages/server/dist/utils/harnessPaths.js +247 -0
  253. package/packages/server/dist/utils/harnessPaths.js.map +1 -0
  254. package/packages/server/dist/utils/secretHeuristic.d.ts +72 -0
  255. package/packages/server/dist/utils/secretHeuristic.d.ts.map +1 -0
  256. package/packages/server/dist/utils/secretHeuristic.js +163 -0
  257. package/packages/server/dist/utils/secretHeuristic.js.map +1 -0
  258. package/packages/server/dist/utils/secretPlaceholderNamer.d.ts +41 -0
  259. package/packages/server/dist/utils/secretPlaceholderNamer.d.ts.map +1 -0
  260. package/packages/server/dist/utils/secretPlaceholderNamer.js +81 -0
  261. package/packages/server/dist/utils/secretPlaceholderNamer.js.map +1 -0
  262. package/packages/server/dist/utils/serverPathResolver.d.ts +29 -0
  263. package/packages/server/dist/utils/serverPathResolver.d.ts.map +1 -0
  264. package/packages/server/dist/utils/serverPathResolver.js +59 -0
  265. package/packages/server/dist/utils/serverPathResolver.js.map +1 -0
  266. package/packages/server/dist/utils/snippetPaths.d.ts +61 -0
  267. package/packages/server/dist/utils/snippetPaths.d.ts.map +1 -0
  268. package/packages/server/dist/utils/snippetPaths.js +123 -0
  269. package/packages/server/dist/utils/snippetPaths.js.map +1 -0
  270. package/packages/server/dist/utils/structuredEditor.d.ts +34 -0
  271. package/packages/server/dist/utils/structuredEditor.d.ts.map +1 -0
  272. package/packages/server/dist/utils/structuredEditor.js +111 -0
  273. package/packages/server/dist/utils/structuredEditor.js.map +1 -0
  274. package/packages/server/package.json +6 -2
  275. package/packages/server/resources/internals/INDEX.md +25 -0
  276. package/packages/server/resources/internals/bmad-qa-fix-marker.md +32 -0
  277. package/packages/server/resources/internals/harness-files.md +85 -0
  278. package/packages/server/resources/internals/image-storage.md +43 -0
  279. package/packages/server/resources/internals/observability-storage.md +23 -0
  280. package/packages/server/resources/manual/01-getting-started.md +104 -0
  281. package/packages/server/resources/manual/02-chat.md +285 -0
  282. package/packages/server/resources/manual/03-sessions.md +48 -0
  283. package/packages/server/resources/manual/04-slash-commands-favorites.md +152 -0
  284. package/packages/server/resources/manual/05-projects.md +76 -0
  285. package/packages/server/resources/manual/06-file-explorer-editor.md +90 -0
  286. package/packages/server/resources/manual/07-git.md +94 -0
  287. package/packages/server/resources/manual/08-terminal.md +59 -0
  288. package/packages/server/resources/manual/09-queue-runner.md +262 -0
  289. package/packages/server/resources/manual/10-project-board.md +194 -0
  290. package/packages/server/resources/manual/11-bmad-method-integration.md +130 -0
  291. package/packages/server/resources/manual/12-harness-workbench.md +256 -0
  292. package/packages/server/resources/manual/13-settings.md +241 -0
  293. package/packages/server/resources/manual/14-keyboard-shortcuts.md +68 -0
  294. package/packages/server/resources/manual/15-environment-variables.md +28 -0
  295. package/packages/server/resources/manual/16-troubleshooting.md +110 -0
  296. package/packages/server/resources/manual/INDEX.md +60 -0
  297. package/packages/shared/dist/index.d.ts +7 -0
  298. package/packages/shared/dist/index.d.ts.map +1 -1
  299. package/packages/shared/dist/index.js +14 -0
  300. package/packages/shared/dist/index.js.map +1 -1
  301. package/packages/shared/dist/types/bmadCoreConfig.d.ts +71 -0
  302. package/packages/shared/dist/types/bmadCoreConfig.d.ts.map +1 -0
  303. package/packages/shared/dist/types/bmadCoreConfig.js +30 -0
  304. package/packages/shared/dist/types/bmadCoreConfig.js.map +1 -0
  305. package/packages/shared/dist/types/bmadStatus.d.ts +10 -0
  306. package/packages/shared/dist/types/bmadStatus.d.ts.map +1 -1
  307. package/packages/shared/dist/types/bmadStatus.js.map +1 -1
  308. package/packages/shared/dist/types/board.d.ts +6 -0
  309. package/packages/shared/dist/types/board.d.ts.map +1 -1
  310. package/packages/shared/dist/types/command.d.ts +3 -3
  311. package/packages/shared/dist/types/command.d.ts.map +1 -1
  312. package/packages/shared/dist/types/contextBuilder.d.ts +102 -0
  313. package/packages/shared/dist/types/contextBuilder.d.ts.map +1 -0
  314. package/packages/shared/dist/types/contextBuilder.js +55 -0
  315. package/packages/shared/dist/types/contextBuilder.js.map +1 -0
  316. package/packages/shared/dist/types/harness.d.ts +1211 -0
  317. package/packages/shared/dist/types/harness.d.ts.map +1 -0
  318. package/packages/shared/dist/types/harness.js +107 -0
  319. package/packages/shared/dist/types/harness.js.map +1 -0
  320. package/packages/shared/dist/types/harnessBundle.d.ts +205 -0
  321. package/packages/shared/dist/types/harnessBundle.d.ts.map +1 -0
  322. package/packages/shared/dist/types/harnessBundle.js +18 -0
  323. package/packages/shared/dist/types/harnessBundle.js.map +1 -0
  324. package/packages/shared/dist/types/marketplace.d.ts +83 -0
  325. package/packages/shared/dist/types/marketplace.d.ts.map +1 -0
  326. package/packages/shared/dist/types/marketplace.js +18 -0
  327. package/packages/shared/dist/types/marketplace.js.map +1 -0
  328. package/packages/shared/dist/types/observability.d.ts +148 -0
  329. package/packages/shared/dist/types/observability.d.ts.map +1 -0
  330. package/packages/shared/dist/types/observability.js +24 -0
  331. package/packages/shared/dist/types/observability.js.map +1 -0
  332. package/packages/shared/dist/types/preferences.d.ts +4 -0
  333. package/packages/shared/dist/types/preferences.d.ts.map +1 -1
  334. package/packages/shared/dist/types/preferences.js.map +1 -1
  335. package/packages/shared/dist/types/queue.d.ts +9 -0
  336. package/packages/shared/dist/types/queue.d.ts.map +1 -1
  337. package/packages/shared/dist/types/sdk.d.ts +1 -1
  338. package/packages/shared/dist/types/sdk.d.ts.map +1 -1
  339. package/packages/shared/dist/types/sdk.js +1 -1
  340. package/packages/shared/dist/types/sdk.js.map +1 -1
  341. package/packages/shared/dist/types/websocket.d.ts +10 -0
  342. package/packages/shared/dist/types/websocket.d.ts.map +1 -1
  343. package/packages/shared/dist/utils/markdownSections.d.ts +50 -0
  344. package/packages/shared/dist/utils/markdownSections.d.ts.map +1 -0
  345. package/packages/shared/dist/utils/markdownSections.js +111 -0
  346. package/packages/shared/dist/utils/markdownSections.js.map +1 -0
  347. package/packages/shared/dist/utils/queueParser.d.ts.map +1 -1
  348. package/packages/shared/dist/utils/queueParser.js +104 -0
  349. package/packages/shared/dist/utils/queueParser.js.map +1 -1
  350. package/scripts/build-manual-shards.mjs +100 -0
  351. package/packages/client/dist/assets/index-6jREnVYd.js +0 -2
  352. package/packages/client/dist/assets/index-BFF0iqyW.css +0 -32
  353. package/packages/client/dist/assets/index-BcI4y-fU.js +0 -1454
@@ -0,0 +1,222 @@
1
+ /**
2
+ * Story 28.0.5: Harness workbench service.
3
+ *
4
+ * Domain: the project's `.claude/` subtree and the global `~/.claude/` subtree
5
+ * — the files that drive plugins, skills, MCP servers, hooks, commands, and
6
+ * subagents. Responsibilities are intentionally separated from
7
+ * `fileSystemService` (which serves arbitrary project files) so that path
8
+ * resolution, scope routing, and the structured-edit surface can evolve
9
+ * independently for harness consumers.
10
+ *
11
+ * Method contract surfaces (all throw typed errors from `HARNESS_ERRORS`):
12
+ * list — directory contents for a `.claude/` subtree
13
+ * read — single file read with binary detection + 1MB truncation
14
+ * write — raw write with expectedMtime / STALE_WRITE guard
15
+ * patchStructured — YAML/JSONC AST-level patch preserving comments & order
16
+ */
17
+ import fs from 'fs/promises';
18
+ import path from 'path';
19
+ import { HARNESS_ERRORS, } from '@hammoc/shared';
20
+ import { resolveHarnessPath } from '../utils/harnessPaths.js';
21
+ import { isBinaryFile, getMimeType, MAX_FILE_SIZE } from '../utils/pathUtils.js';
22
+ import { applyYamlPatch, applyJsoncPatch } from '../utils/structuredEditor.js';
23
+ import { fileWatcherService } from './fileWatcherService.js';
24
+ function throwMapped(code, message, extras) {
25
+ const err = new Error(message);
26
+ err.code = code;
27
+ if (extras)
28
+ Object.assign(err, extras);
29
+ throw err;
30
+ }
31
+ class HarnessService {
32
+ /** List entries of a harness subtree. Missing root → empty list (AC1/AC2). */
33
+ async list(ref) {
34
+ const { resolvedRoot, absolutePath } = await resolveHarnessPath(ref);
35
+ let stat;
36
+ try {
37
+ stat = await fs.stat(absolutePath);
38
+ }
39
+ catch (error) {
40
+ const code = error.code;
41
+ if (code === 'ENOENT') {
42
+ // Harness root (or any subdir) that has never been created yet —
43
+ // return an empty list instead of 404 so the UI can render an empty
44
+ // state without a blocking error.
45
+ return {
46
+ scope: ref.scope,
47
+ projectSlug: ref.projectSlug,
48
+ resolvedRoot,
49
+ path: ref.relativePath ?? '',
50
+ entries: [],
51
+ };
52
+ }
53
+ if (code === 'EACCES') {
54
+ throwMapped(HARNESS_ERRORS.HARNESS_FORBIDDEN.code, 'permission denied');
55
+ }
56
+ throw error;
57
+ }
58
+ if (!stat.isDirectory()) {
59
+ throwMapped(HARNESS_ERRORS.HARNESS_NOT_A_FILE.code, 'path is not a directory');
60
+ }
61
+ const names = await fs.readdir(absolutePath);
62
+ const entries = [];
63
+ for (const name of names) {
64
+ try {
65
+ const entryPath = path.join(absolutePath, name);
66
+ const entryStat = await fs.stat(entryPath);
67
+ entries.push({
68
+ name,
69
+ type: entryStat.isDirectory() ? 'directory' : 'file',
70
+ size: entryStat.isDirectory() ? 0 : entryStat.size,
71
+ modifiedAt: entryStat.mtime.toISOString(),
72
+ });
73
+ }
74
+ catch {
75
+ // Skip broken symlinks / unstat-able entries — same policy as fileSystemService.
76
+ continue;
77
+ }
78
+ }
79
+ return {
80
+ scope: ref.scope,
81
+ projectSlug: ref.projectSlug,
82
+ resolvedRoot,
83
+ path: ref.relativePath ?? '',
84
+ entries,
85
+ };
86
+ }
87
+ /** Read a single harness file. Binary → content:null, >1MB → truncated. */
88
+ async read(ref) {
89
+ const { absolutePath } = await resolveHarnessPath(ref);
90
+ let stat;
91
+ try {
92
+ stat = await fs.stat(absolutePath);
93
+ }
94
+ catch (error) {
95
+ const code = error.code;
96
+ if (code === 'ENOENT') {
97
+ throwMapped(HARNESS_ERRORS.HARNESS_FILE_NOT_FOUND.code, 'file not found');
98
+ }
99
+ if (code === 'EACCES') {
100
+ throwMapped(HARNESS_ERRORS.HARNESS_FORBIDDEN.code, 'permission denied');
101
+ }
102
+ throw error;
103
+ }
104
+ if (!stat.isFile()) {
105
+ throwMapped(HARNESS_ERRORS.HARNESS_NOT_A_FILE.code, 'path is not a file');
106
+ }
107
+ const size = stat.size;
108
+ const mtime = stat.mtime.toISOString();
109
+ const mimeType = getMimeType(absolutePath);
110
+ const base = {
111
+ scope: ref.scope,
112
+ projectSlug: ref.projectSlug,
113
+ path: ref.relativePath ?? '',
114
+ size,
115
+ mtime,
116
+ mimeType,
117
+ };
118
+ if (size > 0) {
119
+ const binary = await isBinaryFile(absolutePath);
120
+ if (binary) {
121
+ return { ...base, content: null, isBinary: true, isTruncated: false };
122
+ }
123
+ }
124
+ if (size > MAX_FILE_SIZE) {
125
+ const handle = await fs.open(absolutePath, 'r');
126
+ try {
127
+ const buffer = Buffer.alloc(MAX_FILE_SIZE);
128
+ await handle.read(buffer, 0, MAX_FILE_SIZE, 0);
129
+ return { ...base, content: buffer.toString('utf-8'), isBinary: false, isTruncated: true };
130
+ }
131
+ finally {
132
+ await handle.close();
133
+ }
134
+ }
135
+ const content = await fs.readFile(absolutePath, 'utf-8');
136
+ return { ...base, content, isBinary: false, isTruncated: false };
137
+ }
138
+ /**
139
+ * Write a harness file with optional ETag/mtime conflict check.
140
+ * - expectedMtime present + file exists + mtime differs → STALE_WRITE
141
+ * - expectedMtime present + file missing → STALE_WRITE (currentMtime: '')
142
+ * - expectedMtime absent → force overwrite/create
143
+ * - parent directory missing → HARNESS_PARENT_NOT_FOUND (no auto-mkdir)
144
+ */
145
+ async write(ref, body) {
146
+ const { absolutePath } = await resolveHarnessPath(ref);
147
+ const parentDir = path.dirname(absolutePath);
148
+ try {
149
+ const parentStat = await fs.stat(parentDir);
150
+ if (!parentStat.isDirectory()) {
151
+ throwMapped(HARNESS_ERRORS.HARNESS_PARENT_NOT_FOUND.code, 'parent is not a directory');
152
+ }
153
+ }
154
+ catch (error) {
155
+ const code = error.code;
156
+ if (code === HARNESS_ERRORS.HARNESS_PARENT_NOT_FOUND.code)
157
+ throw error;
158
+ if (code === 'ENOENT') {
159
+ throwMapped(HARNESS_ERRORS.HARNESS_PARENT_NOT_FOUND.code, 'parent directory not found');
160
+ }
161
+ if (code === 'EACCES') {
162
+ throwMapped(HARNESS_ERRORS.HARNESS_FORBIDDEN.code, 'permission denied on parent');
163
+ }
164
+ throwMapped(HARNESS_ERRORS.HARNESS_WRITE_ERROR.code, 'failed to stat parent directory');
165
+ }
166
+ if (body.expectedMtime !== undefined) {
167
+ try {
168
+ const existing = await fs.stat(absolutePath);
169
+ const currentMtime = existing.mtime.toISOString();
170
+ if (currentMtime !== body.expectedMtime) {
171
+ throwMapped(HARNESS_ERRORS.HARNESS_STALE_WRITE.code, 'file changed on disk', { currentMtime });
172
+ }
173
+ }
174
+ catch (error) {
175
+ const code = error.code;
176
+ if (code === 'ENOENT') {
177
+ // Caller asserted a specific mtime but the file is gone — treat as stale.
178
+ throwMapped(HARNESS_ERRORS.HARNESS_STALE_WRITE.code, 'file missing on disk', { currentMtime: '' });
179
+ }
180
+ if (code === HARNESS_ERRORS.HARNESS_STALE_WRITE.code)
181
+ throw error;
182
+ if (code === 'EACCES') {
183
+ throwMapped(HARNESS_ERRORS.HARNESS_FORBIDDEN.code, 'permission denied');
184
+ }
185
+ // Any other stat failure: fall through and let writeFile surface it.
186
+ }
187
+ }
188
+ try {
189
+ await fs.writeFile(absolutePath, body.content, 'utf-8');
190
+ }
191
+ catch (error) {
192
+ const code = error.code;
193
+ if (code === 'EACCES') {
194
+ throwMapped(HARNESS_ERRORS.HARNESS_FORBIDDEN.code, 'permission denied');
195
+ }
196
+ throwMapped(HARNESS_ERRORS.HARNESS_WRITE_ERROR.code, 'failed to write file');
197
+ }
198
+ const stat = await fs.stat(absolutePath);
199
+ // Share the existing self-write suppression map so the harness watcher
200
+ // (Task 7) swallows our own-write echo on the same path.
201
+ fileWatcherService.noteLocalWrite(absolutePath);
202
+ return { success: true, size: stat.size, mtime: stat.mtime.toISOString() };
203
+ }
204
+ /**
205
+ * Read-patch-write an AST-level edit. Running the round trip through
206
+ * `write` means an external mutation that sneaks in between our read and
207
+ * write will surface as STALE_WRITE (AC5).
208
+ */
209
+ async patchStructured(ref, body) {
210
+ const current = await this.read(ref);
211
+ if (current.isBinary || current.content == null) {
212
+ throwMapped(HARNESS_ERRORS.HARNESS_PARSE_ERROR.code, 'cannot patch binary file');
213
+ }
214
+ const patched = body.format === 'yaml'
215
+ ? applyYamlPatch(current.content, body.ops)
216
+ : applyJsoncPatch(current.content, body.ops);
217
+ const expectedMtime = body.expectedMtime ?? current.mtime;
218
+ return this.write(ref, { content: patched, expectedMtime });
219
+ }
220
+ }
221
+ export const harnessService = new HarnessService();
222
+ //# sourceMappingURL=harnessService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"harnessService.js","sourceRoot":"","sources":["../../src/services/harnessService.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Story 30.1 (Task 2.1): share-scope evaluator.
3
+ *
4
+ * Given a project slug and a set of project-relative POSIX paths, classify
5
+ * each path as `shared` / `local` / `fullyIgnored` based on the project's
6
+ * `.gitignore`. Also derives the project-level Mode A / Mode B classification
7
+ * by evaluating the virtual path `.claude/settings.json` (file may not exist
8
+ * physically — `isIgnored()` is a pattern test, not a stat call).
9
+ *
10
+ * Single source of truth: `gitignoreFilter.loadGitignore` + `isIgnored`. No
11
+ * direct `.gitignore` parsing.
12
+ */
13
+ import { type HarnessShareScopeResponse } from '@hammoc/shared';
14
+ export interface EvaluateShareScopeRequest {
15
+ projectSlug: string;
16
+ /** Project-relative POSIX paths. May include sibling files outside `.claude/` (e.g. `.mcp.json`). */
17
+ relativePaths: string[];
18
+ }
19
+ declare class HarnessShareScopeService {
20
+ /**
21
+ * Evaluate share-scope for each path + the project-level mode in one
22
+ * `.gitignore` load. Returns `cards: {}` if no paths were requested but
23
+ * still computes `mode` (so a freshly mounted workbench can render the
24
+ * banner before any file is asked about).
25
+ */
26
+ evaluate(req: EvaluateShareScopeRequest): Promise<HarnessShareScopeResponse>;
27
+ /**
28
+ * Story 30.7 (Task D.3): append `pattern` to `<projectRoot>/.gitignore`,
29
+ * creating the file if missing. Idempotent: when the trimmed pattern
30
+ * already appears as a non-comment line, the method returns
31
+ * `{ appended: false }` without writing. Used by the client's
32
+ * `SecretOnSharedDialog → Move to local` flow when the sibling pre-check
33
+ * detects that `**\/.claude/**\/*.local.*` is missing.
34
+ *
35
+ * The target file sits outside the `.claude/` whitelist that
36
+ * `harnessService.write` clamps to, so the method (a) resolves the
37
+ * canonical path via `resolveProjectGitignorePath` (a defense-in-depth
38
+ * helper that rejects traversal-bearing slugs) and (b) registers the
39
+ * write with `fileWatcherService.noteLocalWrite` directly so the chokidar
40
+ * watcher does not surface our own change as an external edit.
41
+ */
42
+ appendGitignorePattern(projectSlug: string, pattern: string): Promise<{
43
+ success: true;
44
+ appended: boolean;
45
+ }>;
46
+ }
47
+ export declare const harnessShareScopeService: HarnessShareScopeService;
48
+ export declare function getProjectRootForShareScope(projectSlug: string): Promise<string>;
49
+ export {};
50
+ //# sourceMappingURL=harnessShareScopeService.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"harnessShareScopeService.d.ts","sourceRoot":"","sources":["../../src/services/harnessShareScopeService.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAKH,OAAO,EAAkB,KAAK,yBAAyB,EAAmB,MAAM,gBAAgB,CAAC;AAoBjG,MAAM,WAAW,yBAAyB;IACxC,WAAW,EAAE,MAAM,CAAC;IACpB,qGAAqG;IACrG,aAAa,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,cAAM,wBAAwB;IAC5B;;;;;OAKG;IACG,QAAQ,CAAC,GAAG,EAAE,yBAAyB,GAAG,OAAO,CAAC,yBAAyB,CAAC;IAsClF;;;;;;;;;;;;;;OAcG;IACG,sBAAsB,CAC1B,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC;QAAE,OAAO,EAAE,IAAI,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAA;KAAE,CAAC;CA+CjD;AA6BD,eAAO,MAAM,wBAAwB,0BAAiC,CAAC;AAIvE,wBAAsB,2BAA2B,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAGtF"}
@@ -0,0 +1,158 @@
1
+ /**
2
+ * Story 30.1 (Task 2.1): share-scope evaluator.
3
+ *
4
+ * Given a project slug and a set of project-relative POSIX paths, classify
5
+ * each path as `shared` / `local` / `fullyIgnored` based on the project's
6
+ * `.gitignore`. Also derives the project-level Mode A / Mode B classification
7
+ * by evaluating the virtual path `.claude/settings.json` (file may not exist
8
+ * physically — `isIgnored()` is a pattern test, not a stat call).
9
+ *
10
+ * Single source of truth: `gitignoreFilter.loadGitignore` + `isIgnored`. No
11
+ * direct `.gitignore` parsing.
12
+ */
13
+ import path from 'path';
14
+ import fs from 'fs/promises';
15
+ import { HARNESS_ERRORS } from '@hammoc/shared';
16
+ import { projectService } from './projectService.js';
17
+ import { loadGitignore, isIgnored } from '../utils/gitignoreFilter.js';
18
+ import { resolveProjectGitignorePath } from '../utils/harnessPaths.js';
19
+ import { fileWatcherService } from './fileWatcherService.js';
20
+ const CLAUDE_DIR = '.claude';
21
+ /**
22
+ * Virtual path used for Mode A/B classification. The file may not exist on
23
+ * disk — `isIgnored()` evaluates the pattern alone, so a brand-new project
24
+ * with an empty `.claude/` still gets a deterministic mode verdict.
25
+ */
26
+ const MODE_PROBE_PATH = `${CLAUDE_DIR}/settings.json`;
27
+ /**
28
+ * Probe used to detect "directory itself ignored" patterns like `.claude/`
29
+ * (which auto-propagates to descendants). When the directory itself is
30
+ * matched, every leaf gets the `fullyIgnored` verdict.
31
+ */
32
+ const FULL_IGNORE_PROBE_PATH = `${CLAUDE_DIR}/`;
33
+ class HarnessShareScopeService {
34
+ /**
35
+ * Evaluate share-scope for each path + the project-level mode in one
36
+ * `.gitignore` load. Returns `cards: {}` if no paths were requested but
37
+ * still computes `mode` (so a freshly mounted workbench can render the
38
+ * banner before any file is asked about).
39
+ */
40
+ async evaluate(req) {
41
+ if (!req.projectSlug) {
42
+ const err = new Error('projectSlug is required');
43
+ err.code = HARNESS_ERRORS.HARNESS_ROOT_MISSING.code;
44
+ throw err;
45
+ }
46
+ let projectRoot;
47
+ try {
48
+ projectRoot = await projectService.resolveOriginalPath(req.projectSlug);
49
+ }
50
+ catch (cause) {
51
+ const wrapped = new Error(`Unable to resolve project root for "${req.projectSlug}": ${cause.message}`);
52
+ wrapped.code = HARNESS_ERRORS.HARNESS_ROOT_MISSING.code;
53
+ throw wrapped;
54
+ }
55
+ const matcher = await loadGitignore(projectRoot);
56
+ // Mode A/B verdict: probe `.claude/settings.json`. The file may be missing
57
+ // — `isIgnored` is a pattern test that returns the same verdict either way.
58
+ const mode = isIgnored(matcher, MODE_PROBE_PATH) ? 'B' : 'A';
59
+ // When `.claude/` itself is matched, the directory's children all become
60
+ // `fullyIgnored` regardless of their own paths. We still evaluate each
61
+ // requested path individually so callers can mix in sibling files
62
+ // (e.g. `.mcp.json`) that escape the directory pattern.
63
+ const claudeDirIgnored = isIgnored(matcher, FULL_IGNORE_PROBE_PATH);
64
+ const cards = {};
65
+ for (const rel of req.relativePaths) {
66
+ cards[rel] = classifyPath(matcher, rel, claudeDirIgnored);
67
+ }
68
+ return { mode, cards };
69
+ }
70
+ /**
71
+ * Story 30.7 (Task D.3): append `pattern` to `<projectRoot>/.gitignore`,
72
+ * creating the file if missing. Idempotent: when the trimmed pattern
73
+ * already appears as a non-comment line, the method returns
74
+ * `{ appended: false }` without writing. Used by the client's
75
+ * `SecretOnSharedDialog → Move to local` flow when the sibling pre-check
76
+ * detects that `**\/.claude/**\/*.local.*` is missing.
77
+ *
78
+ * The target file sits outside the `.claude/` whitelist that
79
+ * `harnessService.write` clamps to, so the method (a) resolves the
80
+ * canonical path via `resolveProjectGitignorePath` (a defense-in-depth
81
+ * helper that rejects traversal-bearing slugs) and (b) registers the
82
+ * write with `fileWatcherService.noteLocalWrite` directly so the chokidar
83
+ * watcher does not surface our own change as an external edit.
84
+ */
85
+ async appendGitignorePattern(projectSlug, pattern) {
86
+ const trimmedPattern = pattern.trim();
87
+ if (trimmedPattern.length === 0) {
88
+ const err = new Error('pattern must be a non-empty string');
89
+ err.code = HARNESS_ERRORS.HARNESS_PARSE_ERROR.code;
90
+ throw err;
91
+ }
92
+ const { absolutePath } = await resolveProjectGitignorePath(projectSlug);
93
+ let existing = '';
94
+ try {
95
+ existing = await fs.readFile(absolutePath, 'utf-8');
96
+ }
97
+ catch (err) {
98
+ if (err.code !== 'ENOENT') {
99
+ throw err;
100
+ }
101
+ // File missing → create it with just the pattern below.
102
+ }
103
+ // Idempotency check: scan non-comment, non-blank lines for an exact
104
+ // match. Comments (lines starting with `#`) are skipped because
105
+ // `# **/.claude/**/*.local.*` is not the same as the live pattern.
106
+ const alreadyPresent = existing
107
+ .split(/\r?\n/)
108
+ .map((l) => l.trim())
109
+ .filter((l) => l.length > 0 && !l.startsWith('#'))
110
+ .some((l) => l === trimmedPattern);
111
+ if (alreadyPresent) {
112
+ return { success: true, appended: false };
113
+ }
114
+ const sep = existing.length === 0 || existing.endsWith('\n') ? '' : '\n';
115
+ const next = `${existing}${sep}${trimmedPattern}\n`;
116
+ try {
117
+ await fs.writeFile(absolutePath, next, 'utf-8');
118
+ }
119
+ catch (err) {
120
+ if (err.code === 'EACCES') {
121
+ const denied = new Error('permission denied on .gitignore');
122
+ denied.code = HARNESS_ERRORS.HARNESS_FORBIDDEN.code;
123
+ throw denied;
124
+ }
125
+ const wrapped = new Error('failed to write .gitignore');
126
+ wrapped.code = HARNESS_ERRORS.HARNESS_WRITE_ERROR.code;
127
+ throw wrapped;
128
+ }
129
+ fileWatcherService.noteLocalWrite(absolutePath);
130
+ return { success: true, appended: true };
131
+ }
132
+ }
133
+ function classifyPath(matcher, relativePath, claudeDirIgnored) {
134
+ // Normalize separators so Windows callers don't accidentally bypass `ignore`.
135
+ const posix = relativePath.replace(/\\/g, '/');
136
+ const fileIgnored = isIgnored(matcher, posix);
137
+ if (!fileIgnored) {
138
+ return 'shared';
139
+ }
140
+ // The path is ignored. If `.claude/` itself is the source of the rule, treat
141
+ // the verdict as the project-wide `fullyIgnored` state so Mode B projects
142
+ // get a coherent badge across every harness file.
143
+ if (claudeDirIgnored && isUnderClaudeDir(posix)) {
144
+ return 'fullyIgnored';
145
+ }
146
+ return 'local';
147
+ }
148
+ function isUnderClaudeDir(posixPath) {
149
+ return posixPath === CLAUDE_DIR || posixPath.startsWith(`${CLAUDE_DIR}/`);
150
+ }
151
+ export const harnessShareScopeService = new HarnessShareScopeService();
152
+ // Re-export so consumers (e.g. the .gitignore append fallback in Task 6.4)
153
+ // can also resolve the standard project-root path the matcher was built from.
154
+ export async function getProjectRootForShareScope(projectSlug) {
155
+ const root = await projectService.resolveOriginalPath(projectSlug);
156
+ return path.resolve(root);
157
+ }
158
+ //# sourceMappingURL=harnessShareScopeService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"harnessShareScopeService.js","sourceRoot":"","sources":["../../src/services/harnessShareScopeService.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Story 28.2: Harness skill service.
3
+ *
4
+ * Combines three sources of `<root>/skills/<name>/` directories — the active
5
+ * project, the user-scope home, and every installed plugin bundle — into a
6
+ * single card list used by the "Harness Workbench → Skills" panel. File I/O,
7
+ * traversal guards, STALE_WRITE detection, and binary detection are delegated
8
+ * to `harnessService`. The plugin source enumerates from
9
+ * `installed_plugins.json` and supports both in-tree (~/.claude/plugins/...)
10
+ * and dev-installed (out-of-tree) install paths — the latter goes through a
11
+ * direct `fs` read after a manual containment check so it never escapes its
12
+ * own `installPath`.
13
+ *
14
+ * frontmatter parsing uses the `yaml` (eemeli) parser so the round-trip story
15
+ * stays consistent with `structuredEditor.ts` (no `js-yaml` for harness code).
16
+ */
17
+ import { type HarnessSkillCopyRequest, type HarnessSkillCopyResponse, type HarnessSkillListResponse, type HarnessSkillReadResponse, type HarnessSkillSourceLocation, type HarnessSkillUpdateRequest } from '@hammoc/shared';
18
+ declare class HarnessSkillService {
19
+ /**
20
+ * Enumerate skills across project / user / plugin sources and merge entries
21
+ * sharing the same directory name into a single card.
22
+ */
23
+ listCards(currentProjectSlug?: string): Promise<HarnessSkillListResponse>;
24
+ /**
25
+ * Read a single skill's SKILL.md plus the bundle file tree. Returns the
26
+ * frontmatter, the body separately, the raw text (for the Raw editor) and a
27
+ * flat list of bundle entries with binary / truncation flags pre-resolved.
28
+ */
29
+ readSkill(source: HarnessSkillSourceLocation): Promise<HarnessSkillReadResponse>;
30
+ /**
31
+ * Apply a frontmatter / body / raw update against an existing SKILL.md.
32
+ * Plugin scope is read-only — any update against a plugin source is denied
33
+ * with HARNESS_FORBIDDEN.
34
+ */
35
+ updateSkill(source: HarnessSkillSourceLocation, body: HarnessSkillUpdateRequest): Promise<{
36
+ success: true;
37
+ mtime: string;
38
+ }>;
39
+ /**
40
+ * Recursively copy a skill folder. The destination is always a project or
41
+ * user scope — copies *into* a plugin bundle are blocked by Zod at the
42
+ * controller layer, but we re-assert here as a defensive belt.
43
+ */
44
+ copySkill(req: HarnessSkillCopyRequest): Promise<HarnessSkillCopyResponse>;
45
+ private enumerateProjectSkills;
46
+ private enumerateUserSkills;
47
+ private enumeratePluginSkills;
48
+ private collectSourceFromRoot;
49
+ private malformedEntry;
50
+ private countBundle;
51
+ private collectBundleEntries;
52
+ /**
53
+ * Build the harnessService path reference for an editable (project or user)
54
+ * source. Plugin scope is rejected by the caller before this runs.
55
+ */
56
+ private refForEditableSource;
57
+ /**
58
+ * Apply a frontmatter form patch by routing through the YAML round-trip
59
+ * editor on the frontmatter block alone. Comments / quoting / key order are
60
+ * preserved exactly the same way `harnessService.patchStructured` would for
61
+ * a pure-YAML file — but since SKILL.md is a hybrid file, we run the patch
62
+ * on the extracted block and recompose the full text afterwards.
63
+ */
64
+ private applyFrontmatterPatch;
65
+ private resolveSkillRoot;
66
+ private resolvePluginInstallPath;
67
+ }
68
+ export declare const harnessSkillService: HarnessSkillService;
69
+ export {};
70
+ //# sourceMappingURL=harnessSkillService.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"harnessSkillService.d.ts","sourceRoot":"","sources":["../../src/services/harnessSkillService.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAKH,OAAO,EAML,KAAK,uBAAuB,EAC5B,KAAK,wBAAwB,EAE7B,KAAK,wBAAwB,EAE7B,KAAK,wBAAwB,EAE7B,KAAK,0BAA0B,EAE/B,KAAK,yBAAyB,EAC/B,MAAM,gBAAgB,CAAC;AAmCxB,cAAM,mBAAmB;IAGvB;;;OAGG;IACG,SAAS,CAAC,kBAAkB,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,wBAAwB,CAAC;IA8C/E;;;;OAIG;IACG,SAAS,CAAC,MAAM,EAAE,0BAA0B,GAAG,OAAO,CAAC,wBAAwB,CAAC;IAiDtF;;;;OAIG;IACG,WAAW,CACf,MAAM,EAAE,0BAA0B,EAClC,IAAI,EAAE,yBAAyB,GAC9B,OAAO,CAAC;QAAE,OAAO,EAAE,IAAI,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IA+C5C;;;;OAIG;IACG,SAAS,CAAC,GAAG,EAAE,uBAAuB,GAAG,OAAO,CAAC,wBAAwB,CAAC;YAmFlE,sBAAsB;YAsBtB,mBAAmB;YAiBnB,qBAAqB;YA0DrB,qBAAqB;IAqDnC,OAAO,CAAC,cAAc;YAeR,WAAW;YAaX,oBAAoB;IAyDlC;;;OAGG;IACH,OAAO,CAAC,oBAAoB;IA2B5B;;;;;;OAMG;IACH,OAAO,CAAC,qBAAqB;YAyBf,gBAAgB;YAiChB,wBAAwB;CAmBvC;AA2FD,eAAO,MAAM,mBAAmB,qBAA4B,CAAC"}