hammoc 1.3.0 → 1.5.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 (336) hide show
  1. package/README.md +422 -403
  2. package/bin/hammoc.js +0 -6
  3. package/package.json +100 -93
  4. package/packages/client/dist/assets/agentExampleHighlight-BgwTm15v.js +1 -0
  5. package/packages/client/dist/assets/commandTokenHighlight-BljHwnrK.js +1 -0
  6. package/packages/client/dist/assets/index-CjyjnXB8.css +32 -0
  7. package/packages/client/dist/assets/index-D3LxqW3f.js +2 -0
  8. package/packages/client/dist/assets/index-NqJdhlek.js +1498 -0
  9. package/packages/client/dist/assets/snippetTokenHighlight-DWsaQXX0.js +1 -0
  10. package/packages/client/dist/index.html +2 -2
  11. package/packages/client/dist/sw.js +1 -1
  12. package/packages/server/dist/app.d.ts.map +1 -1
  13. package/packages/server/dist/app.js +24 -24
  14. package/packages/server/dist/app.js.map +1 -1
  15. package/packages/server/dist/controllers/boardController.d.ts.map +1 -1
  16. package/packages/server/dist/controllers/boardController.js +0 -5
  17. package/packages/server/dist/controllers/boardController.js.map +1 -1
  18. package/packages/server/dist/controllers/claudeMdController.d.ts +26 -0
  19. package/packages/server/dist/controllers/claudeMdController.d.ts.map +1 -0
  20. package/packages/server/dist/controllers/claudeMdController.js +158 -0
  21. package/packages/server/dist/controllers/claudeMdController.js.map +1 -0
  22. package/packages/server/dist/controllers/fileSystemController.d.ts +4 -0
  23. package/packages/server/dist/controllers/fileSystemController.d.ts.map +1 -1
  24. package/packages/server/dist/controllers/fileSystemController.js +20 -2
  25. package/packages/server/dist/controllers/fileSystemController.js.map +1 -1
  26. package/packages/server/dist/controllers/harnessAgentController.d.ts +28 -0
  27. package/packages/server/dist/controllers/harnessAgentController.d.ts.map +1 -0
  28. package/packages/server/dist/controllers/harnessAgentController.js +339 -0
  29. package/packages/server/dist/controllers/harnessAgentController.js.map +1 -0
  30. package/packages/server/dist/controllers/harnessCommandController.d.ts +28 -0
  31. package/packages/server/dist/controllers/harnessCommandController.d.ts.map +1 -0
  32. package/packages/server/dist/controllers/harnessCommandController.js +382 -0
  33. package/packages/server/dist/controllers/harnessCommandController.js.map +1 -0
  34. package/packages/server/dist/controllers/harnessController.d.ts +21 -0
  35. package/packages/server/dist/controllers/harnessController.d.ts.map +1 -0
  36. package/packages/server/dist/controllers/harnessController.js +176 -0
  37. package/packages/server/dist/controllers/harnessController.js.map +1 -0
  38. package/packages/server/dist/controllers/harnessHookController.d.ts +32 -0
  39. package/packages/server/dist/controllers/harnessHookController.d.ts.map +1 -0
  40. package/packages/server/dist/controllers/harnessHookController.js +363 -0
  41. package/packages/server/dist/controllers/harnessHookController.js.map +1 -0
  42. package/packages/server/dist/controllers/harnessLintController.d.ts +18 -0
  43. package/packages/server/dist/controllers/harnessLintController.d.ts.map +1 -0
  44. package/packages/server/dist/controllers/harnessLintController.js +72 -0
  45. package/packages/server/dist/controllers/harnessLintController.js.map +1 -0
  46. package/packages/server/dist/controllers/harnessMcpController.d.ts +28 -0
  47. package/packages/server/dist/controllers/harnessMcpController.d.ts.map +1 -0
  48. package/packages/server/dist/controllers/harnessMcpController.js +310 -0
  49. package/packages/server/dist/controllers/harnessMcpController.js.map +1 -0
  50. package/packages/server/dist/controllers/harnessPluginController.d.ts +17 -0
  51. package/packages/server/dist/controllers/harnessPluginController.d.ts.map +1 -0
  52. package/packages/server/dist/controllers/harnessPluginController.js +115 -0
  53. package/packages/server/dist/controllers/harnessPluginController.js.map +1 -0
  54. package/packages/server/dist/controllers/harnessShareScopeController.d.ts +15 -0
  55. package/packages/server/dist/controllers/harnessShareScopeController.d.ts.map +1 -0
  56. package/packages/server/dist/controllers/harnessShareScopeController.js +73 -0
  57. package/packages/server/dist/controllers/harnessShareScopeController.js.map +1 -0
  58. package/packages/server/dist/controllers/harnessSkillController.d.ts +32 -0
  59. package/packages/server/dist/controllers/harnessSkillController.d.ts.map +1 -0
  60. package/packages/server/dist/controllers/harnessSkillController.js +453 -0
  61. package/packages/server/dist/controllers/harnessSkillController.js.map +1 -0
  62. package/packages/server/dist/controllers/projectController.d.ts.map +1 -1
  63. package/packages/server/dist/controllers/projectController.js +11 -0
  64. package/packages/server/dist/controllers/projectController.js.map +1 -1
  65. package/packages/server/dist/controllers/serverController.d.ts.map +1 -1
  66. package/packages/server/dist/controllers/serverController.js +84 -49
  67. package/packages/server/dist/controllers/serverController.js.map +1 -1
  68. package/packages/server/dist/controllers/snippetController.d.ts +35 -0
  69. package/packages/server/dist/controllers/snippetController.d.ts.map +1 -0
  70. package/packages/server/dist/controllers/snippetController.js +294 -0
  71. package/packages/server/dist/controllers/snippetController.js.map +1 -0
  72. package/packages/server/dist/handlers/websocket.d.ts +16 -0
  73. package/packages/server/dist/handlers/websocket.d.ts.map +1 -1
  74. package/packages/server/dist/handlers/websocket.js +221 -8
  75. package/packages/server/dist/handlers/websocket.js.map +1 -1
  76. package/packages/server/dist/index.js +66 -0
  77. package/packages/server/dist/index.js.map +1 -1
  78. package/packages/server/dist/locales/en/server.json +41 -6
  79. package/packages/server/dist/locales/es/server.json +3 -5
  80. package/packages/server/dist/locales/ja/server.json +3 -5
  81. package/packages/server/dist/locales/ko/server.json +4 -6
  82. package/packages/server/dist/locales/pt/server.json +3 -5
  83. package/packages/server/dist/locales/zh-CN/server.json +3 -5
  84. package/packages/server/dist/routes/account.d.ts +7 -0
  85. package/packages/server/dist/routes/account.d.ts.map +1 -0
  86. package/packages/server/dist/routes/account.js +35 -0
  87. package/packages/server/dist/routes/account.js.map +1 -0
  88. package/packages/server/dist/routes/debug.d.ts +1 -1
  89. package/packages/server/dist/routes/debug.d.ts.map +1 -1
  90. package/packages/server/dist/routes/debug.js +60 -1
  91. package/packages/server/dist/routes/debug.js.map +1 -1
  92. package/packages/server/dist/routes/harness.d.ts +8 -0
  93. package/packages/server/dist/routes/harness.d.ts.map +1 -0
  94. package/packages/server/dist/routes/harness.js +92 -0
  95. package/packages/server/dist/routes/harness.js.map +1 -0
  96. package/packages/server/dist/routes/preferences.d.ts.map +1 -1
  97. package/packages/server/dist/routes/preferences.js +11 -2
  98. package/packages/server/dist/routes/preferences.js.map +1 -1
  99. package/packages/server/dist/routes/projects.d.ts.map +1 -1
  100. package/packages/server/dist/routes/projects.js +5 -60
  101. package/packages/server/dist/routes/projects.js.map +1 -1
  102. package/packages/server/dist/routes/snippets.d.ts +14 -0
  103. package/packages/server/dist/routes/snippets.d.ts.map +1 -0
  104. package/packages/server/dist/routes/snippets.js +27 -0
  105. package/packages/server/dist/routes/snippets.js.map +1 -0
  106. package/packages/server/dist/services/accountInfoService.d.ts +38 -0
  107. package/packages/server/dist/services/accountInfoService.d.ts.map +1 -0
  108. package/packages/server/dist/services/accountInfoService.js +118 -0
  109. package/packages/server/dist/services/accountInfoService.js.map +1 -0
  110. package/packages/server/dist/services/bmadStatusService.d.ts +6 -2
  111. package/packages/server/dist/services/bmadStatusService.d.ts.map +1 -1
  112. package/packages/server/dist/services/bmadStatusService.js +88 -32
  113. package/packages/server/dist/services/bmadStatusService.js.map +1 -1
  114. package/packages/server/dist/services/chatService.d.ts +3 -0
  115. package/packages/server/dist/services/chatService.d.ts.map +1 -1
  116. package/packages/server/dist/services/chatService.js +36 -8
  117. package/packages/server/dist/services/chatService.js.map +1 -1
  118. package/packages/server/dist/services/claudeMdService.d.ts +48 -0
  119. package/packages/server/dist/services/claudeMdService.d.ts.map +1 -0
  120. package/packages/server/dist/services/claudeMdService.js +240 -0
  121. package/packages/server/dist/services/claudeMdService.js.map +1 -0
  122. package/packages/server/dist/services/commandService.d.ts +10 -0
  123. package/packages/server/dist/services/commandService.d.ts.map +1 -1
  124. package/packages/server/dist/services/commandService.js +129 -4
  125. package/packages/server/dist/services/commandService.js.map +1 -1
  126. package/packages/server/dist/services/fileSystemService.d.ts +7 -1
  127. package/packages/server/dist/services/fileSystemService.d.ts.map +1 -1
  128. package/packages/server/dist/services/fileSystemService.js +67 -8
  129. package/packages/server/dist/services/fileSystemService.js.map +1 -1
  130. package/packages/server/dist/services/fileWatcherService.d.ts +59 -0
  131. package/packages/server/dist/services/fileWatcherService.d.ts.map +1 -0
  132. package/packages/server/dist/services/fileWatcherService.js +329 -0
  133. package/packages/server/dist/services/fileWatcherService.js.map +1 -0
  134. package/packages/server/dist/services/gitService.d.ts.map +1 -1
  135. package/packages/server/dist/services/gitService.js +67 -7
  136. package/packages/server/dist/services/gitService.js.map +1 -1
  137. package/packages/server/dist/services/harnessAgentService.d.ts +79 -0
  138. package/packages/server/dist/services/harnessAgentService.d.ts.map +1 -0
  139. package/packages/server/dist/services/harnessAgentService.js +933 -0
  140. package/packages/server/dist/services/harnessAgentService.js.map +1 -0
  141. package/packages/server/dist/services/harnessCommandService.d.ts +60 -0
  142. package/packages/server/dist/services/harnessCommandService.d.ts.map +1 -0
  143. package/packages/server/dist/services/harnessCommandService.js +853 -0
  144. package/packages/server/dist/services/harnessCommandService.js.map +1 -0
  145. package/packages/server/dist/services/harnessHookService.d.ts +55 -0
  146. package/packages/server/dist/services/harnessHookService.d.ts.map +1 -0
  147. package/packages/server/dist/services/harnessHookService.js +1060 -0
  148. package/packages/server/dist/services/harnessHookService.js.map +1 -0
  149. package/packages/server/dist/services/harnessLintService.d.ts +49 -0
  150. package/packages/server/dist/services/harnessLintService.d.ts.map +1 -0
  151. package/packages/server/dist/services/harnessLintService.js +628 -0
  152. package/packages/server/dist/services/harnessLintService.js.map +1 -0
  153. package/packages/server/dist/services/harnessMcpService.d.ts +77 -0
  154. package/packages/server/dist/services/harnessMcpService.d.ts.map +1 -0
  155. package/packages/server/dist/services/harnessMcpService.js +814 -0
  156. package/packages/server/dist/services/harnessMcpService.js.map +1 -0
  157. package/packages/server/dist/services/harnessPluginService.d.ts +66 -0
  158. package/packages/server/dist/services/harnessPluginService.d.ts.map +1 -0
  159. package/packages/server/dist/services/harnessPluginService.js +559 -0
  160. package/packages/server/dist/services/harnessPluginService.js.map +1 -0
  161. package/packages/server/dist/services/harnessService.d.ts +40 -0
  162. package/packages/server/dist/services/harnessService.d.ts.map +1 -0
  163. package/packages/server/dist/services/harnessService.js +222 -0
  164. package/packages/server/dist/services/harnessService.js.map +1 -0
  165. package/packages/server/dist/services/harnessShareScopeService.d.ts +31 -0
  166. package/packages/server/dist/services/harnessShareScopeService.d.ts.map +1 -0
  167. package/packages/server/dist/services/harnessShareScopeService.js +93 -0
  168. package/packages/server/dist/services/harnessShareScopeService.js.map +1 -0
  169. package/packages/server/dist/services/harnessSkillService.d.ts +70 -0
  170. package/packages/server/dist/services/harnessSkillService.d.ts.map +1 -0
  171. package/packages/server/dist/services/harnessSkillService.js +636 -0
  172. package/packages/server/dist/services/harnessSkillService.js.map +1 -0
  173. package/packages/server/dist/services/historyParser.d.ts +4 -14
  174. package/packages/server/dist/services/historyParser.d.ts.map +1 -1
  175. package/packages/server/dist/services/historyParser.js +60 -5
  176. package/packages/server/dist/services/historyParser.js.map +1 -1
  177. package/packages/server/dist/services/issueService.d.ts.map +1 -1
  178. package/packages/server/dist/services/issueService.js +10 -2
  179. package/packages/server/dist/services/issueService.js.map +1 -1
  180. package/packages/server/dist/services/manualSyncService.d.ts +19 -0
  181. package/packages/server/dist/services/manualSyncService.d.ts.map +1 -0
  182. package/packages/server/dist/services/manualSyncService.js +110 -0
  183. package/packages/server/dist/services/manualSyncService.js.map +1 -0
  184. package/packages/server/dist/services/notificationService.d.ts.map +1 -1
  185. package/packages/server/dist/services/notificationService.js +34 -9
  186. package/packages/server/dist/services/notificationService.js.map +1 -1
  187. package/packages/server/dist/services/preferencesService.d.ts.map +1 -1
  188. package/packages/server/dist/services/preferencesService.js +8 -1
  189. package/packages/server/dist/services/preferencesService.js.map +1 -1
  190. package/packages/server/dist/services/projectService.d.ts +5 -0
  191. package/packages/server/dist/services/projectService.d.ts.map +1 -1
  192. package/packages/server/dist/services/projectService.js +42 -2
  193. package/packages/server/dist/services/projectService.js.map +1 -1
  194. package/packages/server/dist/services/ptyService.d.ts +1 -0
  195. package/packages/server/dist/services/ptyService.d.ts.map +1 -1
  196. package/packages/server/dist/services/ptyService.js +36 -5
  197. package/packages/server/dist/services/ptyService.js.map +1 -1
  198. package/packages/server/dist/services/queueService.d.ts.map +1 -1
  199. package/packages/server/dist/services/queueService.js +83 -14
  200. package/packages/server/dist/services/queueService.js.map +1 -1
  201. package/packages/server/dist/services/sessionBufferManager.d.ts.map +1 -1
  202. package/packages/server/dist/services/sessionBufferManager.js +26 -0
  203. package/packages/server/dist/services/sessionBufferManager.js.map +1 -1
  204. package/packages/server/dist/services/sessionService.d.ts +4 -3
  205. package/packages/server/dist/services/sessionService.d.ts.map +1 -1
  206. package/packages/server/dist/services/sessionService.js +5 -4
  207. package/packages/server/dist/services/sessionService.js.map +1 -1
  208. package/packages/server/dist/services/snippetService.d.ts +54 -0
  209. package/packages/server/dist/services/snippetService.d.ts.map +1 -0
  210. package/packages/server/dist/services/snippetService.js +371 -0
  211. package/packages/server/dist/services/snippetService.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/services/webPushService.d.ts.map +1 -1
  217. package/packages/server/dist/services/webPushService.js +8 -1
  218. package/packages/server/dist/services/webPushService.js.map +1 -1
  219. package/packages/server/dist/snippets/split-commit +9 -0
  220. package/packages/server/dist/utils/applySecretsPolicy.d.ts +53 -0
  221. package/packages/server/dist/utils/applySecretsPolicy.d.ts.map +1 -0
  222. package/packages/server/dist/utils/applySecretsPolicy.js +204 -0
  223. package/packages/server/dist/utils/applySecretsPolicy.js.map +1 -0
  224. package/packages/server/dist/utils/assertNoSecretOnShared.d.ts +40 -0
  225. package/packages/server/dist/utils/assertNoSecretOnShared.d.ts.map +1 -0
  226. package/packages/server/dist/utils/assertNoSecretOnShared.js +47 -0
  227. package/packages/server/dist/utils/assertNoSecretOnShared.js.map +1 -0
  228. package/packages/server/dist/utils/effortUtils.d.ts +21 -0
  229. package/packages/server/dist/utils/effortUtils.d.ts.map +1 -0
  230. package/packages/server/dist/utils/effortUtils.js +36 -0
  231. package/packages/server/dist/utils/effortUtils.js.map +1 -0
  232. package/packages/server/dist/utils/gitignoreFilter.d.ts +23 -0
  233. package/packages/server/dist/utils/gitignoreFilter.d.ts.map +1 -0
  234. package/packages/server/dist/utils/gitignoreFilter.js +42 -0
  235. package/packages/server/dist/utils/gitignoreFilter.js.map +1 -0
  236. package/packages/server/dist/utils/harnessBundleSchema.d.ts +105 -0
  237. package/packages/server/dist/utils/harnessBundleSchema.d.ts.map +1 -0
  238. package/packages/server/dist/utils/harnessBundleSchema.js +79 -0
  239. package/packages/server/dist/utils/harnessBundleSchema.js.map +1 -0
  240. package/packages/server/dist/utils/harnessPaths.d.ts +34 -0
  241. package/packages/server/dist/utils/harnessPaths.d.ts.map +1 -0
  242. package/packages/server/dist/utils/harnessPaths.js +124 -0
  243. package/packages/server/dist/utils/harnessPaths.js.map +1 -0
  244. package/packages/server/dist/utils/pathUtils.d.ts +3 -2
  245. package/packages/server/dist/utils/pathUtils.d.ts.map +1 -1
  246. package/packages/server/dist/utils/pathUtils.js +26 -2
  247. package/packages/server/dist/utils/pathUtils.js.map +1 -1
  248. package/packages/server/dist/utils/secretHeuristic.d.ts +72 -0
  249. package/packages/server/dist/utils/secretHeuristic.d.ts.map +1 -0
  250. package/packages/server/dist/utils/secretHeuristic.js +163 -0
  251. package/packages/server/dist/utils/secretHeuristic.js.map +1 -0
  252. package/packages/server/dist/utils/secretPlaceholderNamer.d.ts +41 -0
  253. package/packages/server/dist/utils/secretPlaceholderNamer.d.ts.map +1 -0
  254. package/packages/server/dist/utils/secretPlaceholderNamer.js +81 -0
  255. package/packages/server/dist/utils/secretPlaceholderNamer.js.map +1 -0
  256. package/packages/server/dist/utils/serverPathResolver.d.ts +29 -0
  257. package/packages/server/dist/utils/serverPathResolver.d.ts.map +1 -0
  258. package/packages/server/dist/utils/serverPathResolver.js +59 -0
  259. package/packages/server/dist/utils/serverPathResolver.js.map +1 -0
  260. package/packages/server/dist/utils/snippetPaths.d.ts +61 -0
  261. package/packages/server/dist/utils/snippetPaths.d.ts.map +1 -0
  262. package/packages/server/dist/utils/snippetPaths.js +123 -0
  263. package/packages/server/dist/utils/snippetPaths.js.map +1 -0
  264. package/packages/server/dist/utils/structuredEditor.d.ts +34 -0
  265. package/packages/server/dist/utils/structuredEditor.d.ts.map +1 -0
  266. package/packages/server/dist/utils/structuredEditor.js +111 -0
  267. package/packages/server/dist/utils/structuredEditor.js.map +1 -0
  268. package/packages/server/package.json +6 -2
  269. package/packages/server/resources/internals/INDEX.md +23 -0
  270. package/packages/server/resources/internals/harness-files.md +63 -0
  271. package/packages/server/resources/internals/image-storage.md +43 -0
  272. package/packages/server/resources/manual/01-getting-started.md +104 -0
  273. package/packages/server/resources/manual/02-chat.md +285 -0
  274. package/packages/server/resources/manual/03-sessions.md +48 -0
  275. package/packages/server/resources/manual/04-slash-commands-favorites.md +152 -0
  276. package/packages/server/resources/manual/05-projects.md +74 -0
  277. package/packages/server/resources/manual/06-file-explorer-editor.md +90 -0
  278. package/packages/server/resources/manual/07-git.md +94 -0
  279. package/packages/server/resources/manual/08-terminal.md +59 -0
  280. package/packages/server/resources/manual/09-queue-runner.md +262 -0
  281. package/packages/server/resources/manual/10-project-board.md +193 -0
  282. package/packages/server/resources/manual/11-bmad-method-integration.md +128 -0
  283. package/packages/server/resources/manual/12-harness-workbench.md +175 -0
  284. package/packages/server/resources/manual/13-settings.md +241 -0
  285. package/packages/server/resources/manual/14-keyboard-shortcuts.md +68 -0
  286. package/packages/server/resources/manual/15-environment-variables.md +28 -0
  287. package/packages/server/resources/manual/16-troubleshooting.md +110 -0
  288. package/packages/server/resources/manual/INDEX.md +60 -0
  289. package/packages/shared/dist/index.d.ts +3 -0
  290. package/packages/shared/dist/index.d.ts.map +1 -1
  291. package/packages/shared/dist/index.js +6 -0
  292. package/packages/shared/dist/index.js.map +1 -1
  293. package/packages/shared/dist/types/command.d.ts +3 -3
  294. package/packages/shared/dist/types/command.d.ts.map +1 -1
  295. package/packages/shared/dist/types/fileSystem.d.ts +19 -0
  296. package/packages/shared/dist/types/fileSystem.d.ts.map +1 -1
  297. package/packages/shared/dist/types/fileSystem.js +5 -0
  298. package/packages/shared/dist/types/fileSystem.js.map +1 -1
  299. package/packages/shared/dist/types/git.d.ts +6 -1
  300. package/packages/shared/dist/types/git.d.ts.map +1 -1
  301. package/packages/shared/dist/types/git.js.map +1 -1
  302. package/packages/shared/dist/types/harness.d.ts +1211 -0
  303. package/packages/shared/dist/types/harness.d.ts.map +1 -0
  304. package/packages/shared/dist/types/harness.js +107 -0
  305. package/packages/shared/dist/types/harness.js.map +1 -0
  306. package/packages/shared/dist/types/harnessBundle.d.ts +170 -0
  307. package/packages/shared/dist/types/harnessBundle.d.ts.map +1 -0
  308. package/packages/shared/dist/types/harnessBundle.js +18 -0
  309. package/packages/shared/dist/types/harnessBundle.js.map +1 -0
  310. package/packages/shared/dist/types/history.d.ts +7 -0
  311. package/packages/shared/dist/types/history.d.ts.map +1 -1
  312. package/packages/shared/dist/types/preferences.d.ts +4 -1
  313. package/packages/shared/dist/types/preferences.d.ts.map +1 -1
  314. package/packages/shared/dist/types/preferences.js +1 -0
  315. package/packages/shared/dist/types/preferences.js.map +1 -1
  316. package/packages/shared/dist/types/queue.d.ts +9 -0
  317. package/packages/shared/dist/types/queue.d.ts.map +1 -1
  318. package/packages/shared/dist/types/sdk.d.ts +42 -1
  319. package/packages/shared/dist/types/sdk.d.ts.map +1 -1
  320. package/packages/shared/dist/types/sdk.js +26 -2
  321. package/packages/shared/dist/types/sdk.js.map +1 -1
  322. package/packages/shared/dist/types/websocket.d.ts +24 -0
  323. package/packages/shared/dist/types/websocket.d.ts.map +1 -1
  324. package/packages/shared/dist/utils/markdownSections.d.ts +50 -0
  325. package/packages/shared/dist/utils/markdownSections.d.ts.map +1 -0
  326. package/packages/shared/dist/utils/markdownSections.js +111 -0
  327. package/packages/shared/dist/utils/markdownSections.js.map +1 -0
  328. package/packages/shared/dist/utils/queueParser.d.ts.map +1 -1
  329. package/packages/shared/dist/utils/queueParser.js +104 -0
  330. package/packages/shared/dist/utils/queueParser.js.map +1 -1
  331. package/scripts/build-manual-shards.mjs +100 -0
  332. package/scripts/mock-telegram.mjs +172 -0
  333. package/scripts/run-integration-test.mjs +362 -0
  334. package/packages/client/dist/assets/index-Bf0D9oVJ.css +0 -32
  335. package/packages/client/dist/assets/index-CRmzoqHy.js +0 -2
  336. package/packages/client/dist/assets/index-CszGQ29O.js +0 -1432
@@ -0,0 +1,163 @@
1
+ /**
2
+ * Story 30.1 (Task 1): single source of truth for secret-pattern heuristics
3
+ * used by the harness write paths.
4
+ *
5
+ * Replaces four drifted SECRET_PATTERNS definitions (harnessAgentService,
6
+ * harnessCommandService, harnessHookService, harnessMcpService). Drift
7
+ * before this module:
8
+ *
9
+ * - agent / command / hook : 32-char base64 unanchored, Bearer unanchored
10
+ * - mcp : 40-char base64 anchored, Bearer anchored
11
+ *
12
+ * Canonical chosen here (Story 30.1 sub-spike 1.0, option (b)):
13
+ * - 32-char base64 unanchored, Bearer unanchored
14
+ *
15
+ * Rationale: detection-rate first; Story 30.1 AC4.c provides a per-save
16
+ * "mark not a secret" opt-out that absorbs false positives, whereas missed
17
+ * detections silently leak credentials. MCP's stricter anchors were a local
18
+ * accident, not a deliberate spec decision.
19
+ *
20
+ * Two entry points are exposed because the four services walk two distinct
21
+ * shapes:
22
+ *
23
+ * - detectSecretsInText → string body (agent / command / hook commands)
24
+ * returns matched line numbers (1-based)
25
+ * - detectSecretsInValue → arbitrary JSON-like value (mcp config)
26
+ * returns matched dot-paths
27
+ *
28
+ * Both strip `${ENV_VAR}` references first so legitimate env-var indirection
29
+ * (`Authorization: 'Bearer ${GH_TOKEN}'`) does not raise false positives.
30
+ */
31
+ export const SECRET_PATTERNS = [
32
+ { name: 'bearer', re: /Bearer\s+[A-Za-z0-9._-]{16,}/ },
33
+ { name: 'sk', re: /sk-[A-Za-z0-9]{20,}/ },
34
+ { name: 'aws', re: /AKIA[0-9A-Z]{16}/ },
35
+ { name: 'slack', re: /xox[baprs]-[A-Za-z0-9-]{10,}/ },
36
+ { name: 'base64', re: /[A-Za-z0-9+/=]{32,}/g, minEntropy: 4.0 },
37
+ ];
38
+ export const ENV_REF_RE = /\$\{[A-Za-z_][A-Za-z0-9_]*\}/g;
39
+ /**
40
+ * Shannon entropy in bits/char of an arbitrary string. Used by patterns that
41
+ * opt in via `minEntropy` (currently the loose base64 pattern only).
42
+ */
43
+ export function shannonEntropy(s) {
44
+ if (!s)
45
+ return 0;
46
+ const counts = new Map();
47
+ for (const ch of s)
48
+ counts.set(ch, (counts.get(ch) ?? 0) + 1);
49
+ const len = s.length;
50
+ let h = 0;
51
+ for (const c of counts.values()) {
52
+ const p = c / len;
53
+ h -= p * Math.log2(p);
54
+ }
55
+ return h;
56
+ }
57
+ // Story 30.3 spike #2 — the loose base64 pattern adds two combined gates so
58
+ // natural English compounds (e.g. PascalCase identifiers) cannot trigger a
59
+ // false positive:
60
+ // (i) Shannon entropy ≥ 4.0 bits/char — filters single-symbol runs
61
+ // (ii) ≥ 1 character outside [A-Za-z] — filters English compounds that
62
+ // otherwise meet the entropy bar (real base64 of random bytes almost
63
+ // always contains digits or '+'/'/'/'=' over 32+ chars)
64
+ // Both gates run only for patterns that opt in via `minEntropy`.
65
+ const BASE64_NON_ALPHA_RE = /[0-9+/=]/;
66
+ /**
67
+ * Tests whether `text` matches a single SecretPattern, applying the optional
68
+ * `minEntropy` + non-alpha gates to each individual regex match. The pattern's
69
+ * `re` must be either non-global (single test) or global (per-match gating).
70
+ */
71
+ function patternMatches(text, pat) {
72
+ if (pat.minEntropy === undefined) {
73
+ return pat.re.test(text);
74
+ }
75
+ const re = pat.re.global ? pat.re : new RegExp(pat.re.source, pat.re.flags + 'g');
76
+ re.lastIndex = 0;
77
+ let m;
78
+ while ((m = re.exec(text)) !== null) {
79
+ const s = m[0];
80
+ if (shannonEntropy(s) >= pat.minEntropy && BASE64_NON_ALPHA_RE.test(s)) {
81
+ return true;
82
+ }
83
+ if (m.index === re.lastIndex)
84
+ re.lastIndex += 1;
85
+ }
86
+ return false;
87
+ }
88
+ function stripEnvRefs(text) {
89
+ // Reset is required because the regex is /g — but `replace` with a /g regex
90
+ // re-creates state each call. Still, clone-replace pattern is safest.
91
+ return text.replace(ENV_REF_RE, '');
92
+ }
93
+ /**
94
+ * Line-based scanner for textual command / prompt bodies (agent system
95
+ * prompts, slash command bodies, hook command/prompt strings).
96
+ */
97
+ export function detectSecretsInText(text) {
98
+ if (!text || typeof text !== 'string') {
99
+ return { matched: false, patternNames: [], lines: [] };
100
+ }
101
+ const stripped = stripEnvRefs(text);
102
+ const matchedNames = new Set();
103
+ for (const pat of SECRET_PATTERNS) {
104
+ if (patternMatches(stripped, pat)) {
105
+ matchedNames.add(pat.name);
106
+ }
107
+ }
108
+ if (matchedNames.size === 0) {
109
+ return { matched: false, patternNames: [], lines: [] };
110
+ }
111
+ const lines = [];
112
+ const split = text.split(/\r?\n/);
113
+ for (let i = 0; i < split.length; i += 1) {
114
+ const lineStripped = stripEnvRefs(split[i]);
115
+ for (const pat of SECRET_PATTERNS) {
116
+ if (patternMatches(lineStripped, pat)) {
117
+ lines.push(i + 1);
118
+ break;
119
+ }
120
+ }
121
+ }
122
+ return { matched: true, patternNames: Array.from(matchedNames), lines };
123
+ }
124
+ /**
125
+ * Object-walk scanner for JSON-like values (mcp server config). Each string
126
+ * leaf is evaluated against the canonical patterns; matches are reported as
127
+ * dot-paths (`env.GITHUB_TOKEN`, `headers.Authorization`, ...).
128
+ */
129
+ export function detectSecretsInValue(value, basePath = []) {
130
+ const paths = [];
131
+ const matchedNames = new Set();
132
+ const walk = (v, p) => {
133
+ if (typeof v === 'string') {
134
+ const stripped = stripEnvRefs(v);
135
+ if (!stripped)
136
+ return;
137
+ for (const pat of SECRET_PATTERNS) {
138
+ if (patternMatches(stripped, pat)) {
139
+ paths.push(p.join('.'));
140
+ matchedNames.add(pat.name);
141
+ return;
142
+ }
143
+ }
144
+ return;
145
+ }
146
+ if (Array.isArray(v)) {
147
+ v.forEach((item, i) => walk(item, [...p, String(i)]));
148
+ return;
149
+ }
150
+ if (v && typeof v === 'object') {
151
+ for (const [k, child] of Object.entries(v)) {
152
+ walk(child, [...p, k]);
153
+ }
154
+ }
155
+ };
156
+ walk(value, basePath);
157
+ return {
158
+ matched: paths.length > 0,
159
+ patternNames: Array.from(matchedNames),
160
+ paths,
161
+ };
162
+ }
163
+ //# sourceMappingURL=secretHeuristic.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secretHeuristic.js","sourceRoot":"","sources":["../../src/utils/secretHeuristic.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Story 30.3 (Task 2.1): single source of truth for placeholder ENV names
3
+ * used when `secretsPolicy === 'placeholder'`.
4
+ *
5
+ * The export pipeline walks each domain's payload, calls `detectSecretsInValue`
6
+ * / `detectSecretsInText` from secretHeuristic.ts to locate every secret, and
7
+ * for each match asks this module to produce a stable ENV reference name.
8
+ * The name is what gets written to disk as `${ENV_REF_NAME}` so the importer
9
+ * can wire it up via environment variables.
10
+ *
11
+ * Naming policy (AC2.c):
12
+ * - mcp `env.<KEY>` → `<UPPER_NAME>_<UPPER_KEY>`
13
+ * - mcp `headers.Authorization` → `BEARER_TOKEN_<UPPER_NAME>`
14
+ * - mcp other paths → `<UPPER_NAME>_<UPPER_LAST_PATH_SEG>`
15
+ * - hook `command` / `prompt` → `HOOK_<EVENT>_TOKEN`
16
+ * - command body match → `COMMAND_<UPPER_SLASH_PATH>_TOKEN`
17
+ * - agent body match → `AGENT_<UPPER_NAME>_TOKEN`
18
+ * - claude-md body match → `CLAUDE_MD_TOKEN_<INDEX>`
19
+ *
20
+ * The names are intentionally not collision-free across multiple secrets in
21
+ * the same card — the export pipeline appends a numeric suffix when the same
22
+ * name is already emitted in that bundle (handled by the caller, not here),
23
+ * so this module stays a pure-function lookup table.
24
+ */
25
+ import type { BundleItemDomain } from '@hammoc/shared';
26
+ export interface NamePlaceholderInput {
27
+ /** Card domain — disambiguates the naming rule. */
28
+ domain: BundleItemDomain;
29
+ /**
30
+ * For mcp/hook: the dot-path returned by `detectSecretsInValue` (e.g.
31
+ * `mcpServers.context7.env.API_KEY`, `mcpServers.gh.headers.Authorization`).
32
+ * For command/agent/claude-md: an empty string or `body:<line>`.
33
+ */
34
+ fieldPath: string;
35
+ /** Active card name (skill: skillName, mcp: serverName, agent: name, etc.). */
36
+ cardName: string;
37
+ /** For hook entries — the matched HarnessHookEvent. */
38
+ hookEvent?: string;
39
+ }
40
+ export declare function namePlaceholder(input: NamePlaceholderInput): string;
41
+ //# sourceMappingURL=secretPlaceholderNamer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secretPlaceholderNamer.d.ts","sourceRoot":"","sources":["../../src/utils/secretPlaceholderNamer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAEvD,MAAM,WAAW,oBAAoB;IACnC,mDAAmD;IACnD,MAAM,EAAE,gBAAgB,CAAC;IACzB;;;;OAIG;IACH,SAAS,EAAE,MAAM,CAAC;IAClB,+EAA+E;IAC/E,QAAQ,EAAE,MAAM,CAAC;IACjB,uDAAuD;IACvD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAiBD,wBAAgB,eAAe,CAAC,KAAK,EAAE,oBAAoB,GAAG,MAAM,CA4CnE"}
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Story 30.3 (Task 2.1): single source of truth for placeholder ENV names
3
+ * used when `secretsPolicy === 'placeholder'`.
4
+ *
5
+ * The export pipeline walks each domain's payload, calls `detectSecretsInValue`
6
+ * / `detectSecretsInText` from secretHeuristic.ts to locate every secret, and
7
+ * for each match asks this module to produce a stable ENV reference name.
8
+ * The name is what gets written to disk as `${ENV_REF_NAME}` so the importer
9
+ * can wire it up via environment variables.
10
+ *
11
+ * Naming policy (AC2.c):
12
+ * - mcp `env.<KEY>` → `<UPPER_NAME>_<UPPER_KEY>`
13
+ * - mcp `headers.Authorization` → `BEARER_TOKEN_<UPPER_NAME>`
14
+ * - mcp other paths → `<UPPER_NAME>_<UPPER_LAST_PATH_SEG>`
15
+ * - hook `command` / `prompt` → `HOOK_<EVENT>_TOKEN`
16
+ * - command body match → `COMMAND_<UPPER_SLASH_PATH>_TOKEN`
17
+ * - agent body match → `AGENT_<UPPER_NAME>_TOKEN`
18
+ * - claude-md body match → `CLAUDE_MD_TOKEN_<INDEX>`
19
+ *
20
+ * The names are intentionally not collision-free across multiple secrets in
21
+ * the same card — the export pipeline appends a numeric suffix when the same
22
+ * name is already emitted in that bundle (handled by the caller, not here),
23
+ * so this module stays a pure-function lookup table.
24
+ */
25
+ const NON_ALNUM_RE = /[^A-Za-z0-9]+/g;
26
+ function toUpperSnake(input) {
27
+ return input
28
+ .trim()
29
+ .replace(NON_ALNUM_RE, '_')
30
+ .replace(/^_+|_+$/g, '')
31
+ .toUpperCase();
32
+ }
33
+ function lastPathSegment(fieldPath) {
34
+ const segments = fieldPath.split('.').filter((s) => s.length > 0);
35
+ return segments[segments.length - 1] ?? '';
36
+ }
37
+ export function namePlaceholder(input) {
38
+ const card = toUpperSnake(input.cardName) || 'UNNAMED';
39
+ switch (input.domain) {
40
+ case 'mcp': {
41
+ const fp = input.fieldPath;
42
+ // Authorization header → BEARER_TOKEN_<CARD>
43
+ if (/(^|\.)headers\.Authorization$/i.test(fp)) {
44
+ return `BEARER_TOKEN_${card}`;
45
+ }
46
+ // env.<KEY> → <CARD>_<KEY>
47
+ const envMatch = fp.match(/(^|\.)env\.([^.]+)$/);
48
+ if (envMatch) {
49
+ return `${card}_${toUpperSnake(envMatch[2])}`;
50
+ }
51
+ // Fallback: last segment
52
+ const seg = toUpperSnake(lastPathSegment(fp)) || 'SECRET';
53
+ return `${card}_${seg}`;
54
+ }
55
+ case 'hook': {
56
+ const evt = toUpperSnake(input.hookEvent ?? 'GENERIC');
57
+ return `HOOK_${evt}_${card}_TOKEN`;
58
+ }
59
+ case 'command': {
60
+ return `COMMAND_${card}_TOKEN`;
61
+ }
62
+ case 'agent': {
63
+ return `AGENT_${card}_TOKEN`;
64
+ }
65
+ case 'claude-md': {
66
+ return `CLAUDE_MD_${card || 'TOKEN'}_${toUpperSnake(input.fieldPath) || 'TOKEN'}`
67
+ .replace(/_+$/, '');
68
+ }
69
+ case 'skill': {
70
+ return `SKILL_${card}_TOKEN`;
71
+ }
72
+ case 'bmad': {
73
+ return `BMAD_${card}_TOKEN`;
74
+ }
75
+ default: {
76
+ const _exhaustive = input.domain;
77
+ return _exhaustive;
78
+ }
79
+ }
80
+ }
81
+ //# sourceMappingURL=secretPlaceholderNamer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secretPlaceholderNamer.js","sourceRoot":"","sources":["../../src/utils/secretPlaceholderNamer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAmBH,MAAM,YAAY,GAAG,gBAAgB,CAAC;AAEtC,SAAS,YAAY,CAAC,KAAa;IACjC,OAAO,KAAK;SACT,IAAI,EAAE;SACN,OAAO,CAAC,YAAY,EAAE,GAAG,CAAC;SAC1B,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;SACvB,WAAW,EAAE,CAAC;AACnB,CAAC;AAED,SAAS,eAAe,CAAC,SAAiB;IACxC,MAAM,QAAQ,GAAG,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAClE,OAAO,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;AAC7C,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,KAA2B;IACzD,MAAM,IAAI,GAAG,YAAY,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,SAAS,CAAC;IAEvD,QAAQ,KAAK,CAAC,MAAM,EAAE,CAAC;QACrB,KAAK,KAAK,CAAC,CAAC,CAAC;YACX,MAAM,EAAE,GAAG,KAAK,CAAC,SAAS,CAAC;YAC3B,6CAA6C;YAC7C,IAAI,gCAAgC,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;gBAC9C,OAAO,gBAAgB,IAAI,EAAE,CAAC;YAChC,CAAC;YACD,2BAA2B;YAC3B,MAAM,QAAQ,GAAG,EAAE,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAC;YACjD,IAAI,QAAQ,EAAE,CAAC;gBACb,OAAO,GAAG,IAAI,IAAI,YAAY,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAChD,CAAC;YACD,yBAAyB;YACzB,MAAM,GAAG,GAAG,YAAY,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC,IAAI,QAAQ,CAAC;YAC1D,OAAO,GAAG,IAAI,IAAI,GAAG,EAAE,CAAC;QAC1B,CAAC;QACD,KAAK,MAAM,CAAC,CAAC,CAAC;YACZ,MAAM,GAAG,GAAG,YAAY,CAAC,KAAK,CAAC,SAAS,IAAI,SAAS,CAAC,CAAC;YACvD,OAAO,QAAQ,GAAG,IAAI,IAAI,QAAQ,CAAC;QACrC,CAAC;QACD,KAAK,SAAS,CAAC,CAAC,CAAC;YACf,OAAO,WAAW,IAAI,QAAQ,CAAC;QACjC,CAAC;QACD,KAAK,OAAO,CAAC,CAAC,CAAC;YACb,OAAO,SAAS,IAAI,QAAQ,CAAC;QAC/B,CAAC;QACD,KAAK,WAAW,CAAC,CAAC,CAAC;YACjB,OAAO,aAAa,IAAI,IAAI,OAAO,IAAI,YAAY,CAAC,KAAK,CAAC,SAAS,CAAC,IAAI,OAAO,EAAE;iBAC9E,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QACxB,CAAC;QACD,KAAK,OAAO,CAAC,CAAC,CAAC;YACb,OAAO,SAAS,IAAI,QAAQ,CAAC;QAC/B,CAAC;QACD,KAAK,MAAM,CAAC,CAAC,CAAC;YACZ,OAAO,QAAQ,IAAI,QAAQ,CAAC;QAC9B,CAAC;QACD,OAAO,CAAC,CAAC,CAAC;YACR,MAAM,WAAW,GAAU,KAAK,CAAC,MAAM,CAAC;YACxC,OAAO,WAAW,CAAC;QACrB,CAAC;IACH,CAAC;AACH,CAAC"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Story 30.2 (Task 1.5): server-side PATH resolver for the
3
+ * `mcp/command-not-on-path` lint rule.
4
+ *
5
+ * Mirrors the `which`/`where` + 5s timeout pattern used by
6
+ * serverController.ts:14-31 for npm path resolution. Absolute paths skip the
7
+ * shell call entirely and fall through to a simple `fs.existsSync` check —
8
+ * faster and avoids spurious `which: not found` lines on stderr for inputs
9
+ * that obviously don't need PATH resolution.
10
+ *
11
+ * The result is intentionally shaped as `{ resolved: string | null }` (not a
12
+ * boolean) so the caller can surface the resolved absolute path in tooltips
13
+ * without re-running the lookup.
14
+ */
15
+ export interface ResolveCommandResult {
16
+ resolved: string | null;
17
+ }
18
+ /**
19
+ * Resolve `cmd` against the current process PATH.
20
+ *
21
+ * - Absolute path → `fs.existsSync` only (no shell).
22
+ * - Relative path containing a separator → resolved relative to `cwd`
23
+ * (uncommon for MCP `command` fields but covered for completeness).
24
+ * - Bare name → `where.exe` on Windows / `which` elsewhere.
25
+ *
26
+ * Never throws — failures collapse to `{ resolved: null }`.
27
+ */
28
+ export declare function resolveCommandOnServerPath(cmd: string): ResolveCommandResult;
29
+ //# sourceMappingURL=serverPathResolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serverPathResolver.d.ts","sourceRoot":"","sources":["../../src/utils/serverPathResolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAMH,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAID;;;;;;;;;GASG;AACH,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,MAAM,GAAG,oBAAoB,CA8B5E"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Story 30.2 (Task 1.5): server-side PATH resolver for the
3
+ * `mcp/command-not-on-path` lint rule.
4
+ *
5
+ * Mirrors the `which`/`where` + 5s timeout pattern used by
6
+ * serverController.ts:14-31 for npm path resolution. Absolute paths skip the
7
+ * shell call entirely and fall through to a simple `fs.existsSync` check —
8
+ * faster and avoids spurious `which: not found` lines on stderr for inputs
9
+ * that obviously don't need PATH resolution.
10
+ *
11
+ * The result is intentionally shaped as `{ resolved: string | null }` (not a
12
+ * boolean) so the caller can surface the resolved absolute path in tooltips
13
+ * without re-running the lookup.
14
+ */
15
+ import { execFileSync } from 'child_process';
16
+ import fs from 'fs';
17
+ import path from 'path';
18
+ const RESOLVE_TIMEOUT_MS = 5000;
19
+ /**
20
+ * Resolve `cmd` against the current process PATH.
21
+ *
22
+ * - Absolute path → `fs.existsSync` only (no shell).
23
+ * - Relative path containing a separator → resolved relative to `cwd`
24
+ * (uncommon for MCP `command` fields but covered for completeness).
25
+ * - Bare name → `where.exe` on Windows / `which` elsewhere.
26
+ *
27
+ * Never throws — failures collapse to `{ resolved: null }`.
28
+ */
29
+ export function resolveCommandOnServerPath(cmd) {
30
+ if (!cmd || typeof cmd !== 'string')
31
+ return { resolved: null };
32
+ const trimmed = cmd.trim();
33
+ if (!trimmed)
34
+ return { resolved: null };
35
+ if (path.isAbsolute(trimmed)) {
36
+ return { resolved: fs.existsSync(trimmed) ? trimmed : null };
37
+ }
38
+ // Path with a separator (e.g. "./bin/foo" or "scripts/run.sh") — resolve
39
+ // against cwd. Server PATH is irrelevant in this case.
40
+ if (trimmed.includes('/') || trimmed.includes('\\')) {
41
+ const abs = path.resolve(process.cwd(), trimmed);
42
+ return { resolved: fs.existsSync(abs) ? abs : null };
43
+ }
44
+ const finder = process.platform === 'win32' ? 'where.exe' : 'which';
45
+ try {
46
+ const stdout = execFileSync(finder, [trimmed], {
47
+ encoding: 'utf-8',
48
+ timeout: RESOLVE_TIMEOUT_MS,
49
+ stdio: ['ignore', 'pipe', 'ignore'],
50
+ });
51
+ const first = stdout.trim().split(/\r?\n/)[0]?.trim();
52
+ return { resolved: first ? first : null };
53
+ }
54
+ catch {
55
+ // Non-zero exit (not found) or timeout — both surface as "unresolved".
56
+ return { resolved: null };
57
+ }
58
+ }
59
+ //# sourceMappingURL=serverPathResolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serverPathResolver.js","sourceRoot":"","sources":["../../src/utils/serverPathResolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AAMxB,MAAM,kBAAkB,GAAG,IAAI,CAAC;AAEhC;;;;;;;;;GASG;AACH,MAAM,UAAU,0BAA0B,CAAC,GAAW;IACpD,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAE/D,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;IAC3B,IAAI,CAAC,OAAO;QAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAExC,IAAI,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;QAC7B,OAAO,EAAE,QAAQ,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAC/D,CAAC;IAED,yEAAyE;IACzE,uDAAuD;IACvD,IAAI,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACpD,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,OAAO,CAAC,CAAC;QACjD,OAAO,EAAE,QAAQ,EAAE,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IACvD,CAAC;IAED,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC;IACpE,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,YAAY,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE;YAC7C,QAAQ,EAAE,OAAO;YACjB,OAAO,EAAE,kBAAkB;YAC3B,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC;SACpC,CAAC,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC;QACtD,OAAO,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAC5C,CAAC;IAAC,MAAM,CAAC;QACP,uEAAuE;QACvE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC5B,CAAC;AACH,CAAC"}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Story 29.2: Snippet path resolver.
3
+ *
4
+ * The snippet system has three scopes that map to different on-disk roots:
5
+ *
6
+ * - project → `<projectRoot>/.hammoc/snippets/`
7
+ * - user → `~/.hammoc/snippets/`
8
+ * - bundled → server-bundled snippets (read-only, located alongside the
9
+ * server build at `<serverDist>/snippets/`)
10
+ *
11
+ * `resolveSnippetPath` is the only path-shaping entry point used by
12
+ * `snippetService` — it accepts `{ scope, projectSlug?, name }` and never lets
13
+ * a caller-supplied relative path through. NAME_RE rejects anything outside
14
+ * `[A-Za-z0-9._-]+` so directory traversal (`..`, `/`, `\`, `\0`, drive
15
+ * letters, UNC prefixes) is impossible by construction.
16
+ *
17
+ * On-disk filename normalization: writes always land at `<root>/<name>.md`
18
+ * regardless of input form. Reads accept both `<name>.md` and the legacy
19
+ * extension-less `<name>` (snippetResolver supports both for back-compat) so
20
+ * existing bundled and user files continue to load.
21
+ */
22
+ /** Snippet name regex — same shape as snippetResolver's NAME_RE. */
23
+ export declare const SNIPPET_NAME_RE: RegExp;
24
+ /** Return the absolute path of `~/.hammoc/snippets/`. */
25
+ export declare function getUserSnippetsDir(): string;
26
+ /** Return the absolute path of the server-bundled snippets directory. */
27
+ export declare function getBundledSnippetsDir(): string;
28
+ /** Return the absolute path of `<projectRoot>/.hammoc/snippets/`. */
29
+ export declare function getProjectSnippetsDir(projectSlug: string): Promise<string>;
30
+ export interface ResolvedSnippetPath {
31
+ /** Root directory (the snippets dir itself, not the project root). */
32
+ resolvedRoot: string;
33
+ /** `<resolvedRoot>/<name>.md`. */
34
+ absolutePath: string;
35
+ /** Same path but without `.md` — legacy format used for back-compat reads. */
36
+ legacyAbsolutePath: string;
37
+ /** True when this path is read-only (bundled). */
38
+ readOnly: boolean;
39
+ }
40
+ export type SnippetScope = 'project' | 'user' | 'bundled';
41
+ export interface SnippetPathRef {
42
+ scope: SnippetScope;
43
+ /** Required when scope === 'project'. */
44
+ projectSlug?: string;
45
+ name: string;
46
+ }
47
+ /**
48
+ * Resolve a snippet path with NAME_RE validation and bundled read-only flagging.
49
+ * Throws an Error with `code: 'HARNESS_PATH_DENIED'` for invalid names.
50
+ */
51
+ export declare function resolveSnippetPath(ref: SnippetPathRef): Promise<ResolvedSnippetPath>;
52
+ /**
53
+ * Throws an HARNESS_PATH_DENIED Error when the name is invalid.
54
+ *
55
+ * Note: SNIPPET_NAME_RE allows `.` so single-dot names (`a.b`) pass through.
56
+ * That also means literal `..` would pass the regex — we reject it
57
+ * separately below, mirroring snippetResolver.resolveSnippet's defensive
58
+ * filter.
59
+ */
60
+ export declare function validateSnippetName(name: string): void;
61
+ //# sourceMappingURL=snippetPaths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snippetPaths.d.ts","sourceRoot":"","sources":["../../src/utils/snippetPaths.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAaH,oEAAoE;AACpE,eAAO,MAAM,eAAe,QAAsB,CAAC;AAOnD,yDAAyD;AACzD,wBAAgB,kBAAkB,IAAI,MAAM,CAM3C;AAED,yEAAyE;AACzE,wBAAgB,qBAAqB,IAAI,MAAM,CAM9C;AAED,qEAAqE;AACrE,wBAAsB,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAchF;AAED,MAAM,WAAW,mBAAmB;IAClC,sEAAsE;IACtE,YAAY,EAAE,MAAM,CAAC;IACrB,kCAAkC;IAClC,YAAY,EAAE,MAAM,CAAC;IACrB,8EAA8E;IAC9E,kBAAkB,EAAE,MAAM,CAAC;IAC3B,kDAAkD;IAClD,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAAC;AAE1D,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,YAAY,CAAC;IACpB,yCAAyC;IACzC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,mBAAmB,CAAC,CA4B1F;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAatD"}
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Story 29.2: Snippet path resolver.
3
+ *
4
+ * The snippet system has three scopes that map to different on-disk roots:
5
+ *
6
+ * - project → `<projectRoot>/.hammoc/snippets/`
7
+ * - user → `~/.hammoc/snippets/`
8
+ * - bundled → server-bundled snippets (read-only, located alongside the
9
+ * server build at `<serverDist>/snippets/`)
10
+ *
11
+ * `resolveSnippetPath` is the only path-shaping entry point used by
12
+ * `snippetService` — it accepts `{ scope, projectSlug?, name }` and never lets
13
+ * a caller-supplied relative path through. NAME_RE rejects anything outside
14
+ * `[A-Za-z0-9._-]+` so directory traversal (`..`, `/`, `\`, `\0`, drive
15
+ * letters, UNC prefixes) is impossible by construction.
16
+ *
17
+ * On-disk filename normalization: writes always land at `<root>/<name>.md`
18
+ * regardless of input form. Reads accept both `<name>.md` and the legacy
19
+ * extension-less `<name>` (snippetResolver supports both for back-compat) so
20
+ * existing bundled and user files continue to load.
21
+ */
22
+ import os from 'os';
23
+ import path from 'path';
24
+ import { fileURLToPath } from 'node:url';
25
+ import { projectService } from '../services/projectService.js';
26
+ /** Hammoc home root override for tests. Mirrors `HAMMOC_HARNESS_HOME_OVERRIDE`. */
27
+ const HOME_OVERRIDE_ENV = 'HAMMOC_HOME_OVERRIDE';
28
+ /** Bundled snippet directory override for tests (e.g. point at a tmp dir). */
29
+ const BUNDLED_OVERRIDE_ENV = 'HAMMOC_BUNDLED_SNIPPETS_DIR';
30
+ /** Snippet name regex — same shape as snippetResolver's NAME_RE. */
31
+ export const SNIPPET_NAME_RE = /^[a-zA-Z0-9._-]+$/;
32
+ const SNIPPETS_SUBDIR = '.hammoc/snippets';
33
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
34
+ const DEFAULT_BUNDLED_DIR = path.resolve(__dirname, '..', 'snippets');
35
+ /** Return the absolute path of `~/.hammoc/snippets/`. */
36
+ export function getUserSnippetsDir() {
37
+ const override = process.env[HOME_OVERRIDE_ENV];
38
+ if (override && override.length > 0) {
39
+ return path.join(override, '.hammoc', 'snippets');
40
+ }
41
+ return path.join(os.homedir(), '.hammoc', 'snippets');
42
+ }
43
+ /** Return the absolute path of the server-bundled snippets directory. */
44
+ export function getBundledSnippetsDir() {
45
+ const override = process.env[BUNDLED_OVERRIDE_ENV];
46
+ if (override && override.length > 0) {
47
+ return override;
48
+ }
49
+ return DEFAULT_BUNDLED_DIR;
50
+ }
51
+ /** Return the absolute path of `<projectRoot>/.hammoc/snippets/`. */
52
+ export async function getProjectSnippetsDir(projectSlug) {
53
+ if (!projectSlug) {
54
+ throw makePathDeniedError('projectSlug is required for project scope');
55
+ }
56
+ if (projectSlug.includes('\0') ||
57
+ projectSlug.includes('..') ||
58
+ projectSlug.includes('/') ||
59
+ projectSlug.includes('\\')) {
60
+ throw makePathDeniedError('projectSlug must not contain path separators');
61
+ }
62
+ const projectRoot = await projectService.resolveOriginalPath(projectSlug);
63
+ return path.join(projectRoot, SNIPPETS_SUBDIR);
64
+ }
65
+ /**
66
+ * Resolve a snippet path with NAME_RE validation and bundled read-only flagging.
67
+ * Throws an Error with `code: 'HARNESS_PATH_DENIED'` for invalid names.
68
+ */
69
+ export async function resolveSnippetPath(ref) {
70
+ validateSnippetName(ref.name);
71
+ let resolvedRoot;
72
+ let readOnly = false;
73
+ if (ref.scope === 'project') {
74
+ if (!ref.projectSlug) {
75
+ throw makePathDeniedError('projectSlug is required for project scope');
76
+ }
77
+ resolvedRoot = path.resolve(await getProjectSnippetsDir(ref.projectSlug));
78
+ }
79
+ else if (ref.scope === 'user') {
80
+ resolvedRoot = path.resolve(getUserSnippetsDir());
81
+ }
82
+ else {
83
+ resolvedRoot = path.resolve(getBundledSnippetsDir());
84
+ readOnly = true;
85
+ }
86
+ // Belt + suspenders: even though NAME_RE forbids separators, double-check
87
+ // the resolved file stays inside the root.
88
+ const absolutePath = path.resolve(resolvedRoot, `${ref.name}.md`);
89
+ if (absolutePath !== resolvedRoot &&
90
+ !absolutePath.startsWith(resolvedRoot + path.sep)) {
91
+ throw makePathDeniedError('snippet path escapes root');
92
+ }
93
+ const legacyAbsolutePath = path.resolve(resolvedRoot, ref.name);
94
+ return { resolvedRoot, absolutePath, legacyAbsolutePath, readOnly };
95
+ }
96
+ /**
97
+ * Throws an HARNESS_PATH_DENIED Error when the name is invalid.
98
+ *
99
+ * Note: SNIPPET_NAME_RE allows `.` so single-dot names (`a.b`) pass through.
100
+ * That also means literal `..` would pass the regex — we reject it
101
+ * separately below, mirroring snippetResolver.resolveSnippet's defensive
102
+ * filter.
103
+ */
104
+ export function validateSnippetName(name) {
105
+ if (!name || typeof name !== 'string') {
106
+ throw makePathDeniedError('snippet name is required');
107
+ }
108
+ if (name.includes('\0')) {
109
+ throw makePathDeniedError('null byte in snippet name');
110
+ }
111
+ if (name === '..' || name === '.' || name.includes('/') || name.includes('\\')) {
112
+ throw makePathDeniedError(`path traversal denied: ${name}`);
113
+ }
114
+ if (!SNIPPET_NAME_RE.test(name)) {
115
+ throw makePathDeniedError(`invalid snippet name: ${name}`);
116
+ }
117
+ }
118
+ function makePathDeniedError(message) {
119
+ const err = new Error(message);
120
+ err.code = 'HARNESS_PATH_DENIED';
121
+ return err;
122
+ }
123
+ //# sourceMappingURL=snippetPaths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snippetPaths.js","sourceRoot":"","sources":["../../src/utils/snippetPaths.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Story 28.0.5: YAML / JSONC round-trip editor.
3
+ *
4
+ * The goal is to mutate a single key inside a structured config file while
5
+ * preserving the author's comments, blank lines, key order, and quoting style.
6
+ *
7
+ * - YAML → `yaml` (eemeli) `parseDocument` + `doc.setIn/deleteIn` + `doc.toString()`.
8
+ * The Document AST keeps comment & blank-line metadata attached to
9
+ * each node, and `toString()` re-emits them.
10
+ * NOTE: `js-yaml@4` remains a coexisting dependency for session-meta parsing
11
+ * elsewhere in the codebase, but has no comment-preservation path and must
12
+ * not be used for harness edits.
13
+ *
14
+ * - JSONC → `jsonc-parser` `modify` + `applyEdits`. This is the same path VS
15
+ * Code uses when it edits user `settings.json` — comments and
16
+ * formatting are preserved.
17
+ *
18
+ * Both entry points throw `HARNESS_PARSE_ERROR` on unparseable input so the
19
+ * harness controller can surface the envelope and the client can fall back to
20
+ * raw editing.
21
+ */
22
+ import { type HarnessStructuredPatchOp } from '@hammoc/shared';
23
+ /**
24
+ * Apply structured patches to a YAML source string while preserving comments,
25
+ * blank lines, and key order.
26
+ */
27
+ export declare function applyYamlPatch(source: string, ops: HarnessStructuredPatchOp[]): string;
28
+ /**
29
+ * Apply structured patches to a JSONC source string while preserving comments
30
+ * and formatting. Inserts missing intermediate objects as needed (matches
31
+ * `jsonc-parser` `modify` default behavior).
32
+ */
33
+ export declare function applyJsoncPatch(source: string, ops: HarnessStructuredPatchOp[]): string;
34
+ //# sourceMappingURL=structuredEditor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"structuredEditor.d.ts","sourceRoot":"","sources":["../../src/utils/structuredEditor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAaH,OAAO,EAAkB,KAAK,wBAAwB,EAAE,MAAM,gBAAgB,CAAC;AAQ/E;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,wBAAwB,EAAE,GAAG,MAAM,CAmCtF;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,wBAAwB,EAAE,GAAG,MAAM,CAqCvF"}