@pennyfarthing/core 10.4.0 → 11.0.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 (797) hide show
  1. package/README.md +4 -4
  2. package/package.json +16 -14
  3. package/packages/core/dist/benchmark/benchmark-integration.d.ts +182 -0
  4. package/packages/core/dist/benchmark/benchmark-integration.d.ts.map +1 -0
  5. package/packages/core/dist/benchmark/benchmark-integration.js +710 -0
  6. package/packages/core/dist/benchmark/benchmark-integration.js.map +1 -0
  7. package/packages/core/dist/benchmark/benchmark-integration.test.d.ts +6 -0
  8. package/packages/core/dist/benchmark/benchmark-integration.test.d.ts.map +1 -0
  9. package/packages/core/dist/benchmark/benchmark-integration.test.js +41 -0
  10. package/packages/core/dist/benchmark/benchmark-integration.test.js.map +1 -0
  11. package/packages/core/dist/benchmark/index.d.ts +3 -0
  12. package/packages/core/dist/benchmark/index.d.ts.map +1 -0
  13. package/packages/core/dist/benchmark/index.js +5 -0
  14. package/packages/core/dist/benchmark/index.js.map +1 -0
  15. package/packages/core/dist/benchmark/job-fair-aggregator.d.ts +150 -0
  16. package/packages/core/dist/benchmark/job-fair-aggregator.d.ts.map +1 -0
  17. package/packages/core/dist/benchmark/job-fair-aggregator.js +547 -0
  18. package/packages/core/dist/benchmark/job-fair-aggregator.js.map +1 -0
  19. package/packages/core/dist/benchmark/job-fair-aggregator.test.d.ts +6 -0
  20. package/packages/core/dist/benchmark/job-fair-aggregator.test.d.ts.map +1 -0
  21. package/packages/core/dist/benchmark/job-fair-aggregator.test.js +35 -0
  22. package/packages/core/dist/benchmark/job-fair-aggregator.test.js.map +1 -0
  23. package/packages/core/dist/cli/commands/hook-chaining.test.d.ts +17 -0
  24. package/packages/core/dist/cli/commands/hook-chaining.test.d.ts.map +1 -0
  25. package/packages/core/dist/cli/commands/hook-chaining.test.js +386 -0
  26. package/packages/core/dist/cli/commands/hook-chaining.test.js.map +1 -0
  27. package/packages/core/dist/cli/commands/init.d.ts +6 -1
  28. package/packages/core/dist/cli/commands/init.d.ts.map +1 -1
  29. package/packages/core/dist/cli/commands/init.js +83 -30
  30. package/packages/core/dist/cli/commands/init.js.map +1 -1
  31. package/packages/core/dist/cli/commands/uninstall.d.ts.map +1 -1
  32. package/packages/core/dist/cli/commands/uninstall.js +20 -3
  33. package/packages/core/dist/cli/commands/uninstall.js.map +1 -1
  34. package/packages/core/dist/cli/commands/uninstall.test.d.ts +8 -0
  35. package/packages/core/dist/cli/commands/uninstall.test.d.ts.map +1 -0
  36. package/packages/core/dist/cli/commands/uninstall.test.js +120 -0
  37. package/packages/core/dist/cli/commands/uninstall.test.js.map +1 -0
  38. package/packages/core/dist/cli/commands/update.js +4 -4
  39. package/packages/core/dist/cli/commands/update.js.map +1 -1
  40. package/packages/core/dist/cli/cyclist-migration.test.js +2 -1
  41. package/packages/core/dist/cli/cyclist-migration.test.js.map +1 -1
  42. package/packages/core/dist/cli/ocean-profiles.test.js +5 -4
  43. package/packages/core/dist/cli/ocean-profiles.test.js.map +1 -1
  44. package/packages/core/dist/cli/theme-maker.test.js +5 -4
  45. package/packages/core/dist/cli/theme-maker.test.js.map +1 -1
  46. package/packages/core/dist/cli/utils/008-sprint-shard-migration.test.d.ts +15 -0
  47. package/packages/core/dist/cli/utils/008-sprint-shard-migration.test.d.ts.map +1 -0
  48. package/packages/core/dist/cli/utils/008-sprint-shard-migration.test.js +295 -0
  49. package/packages/core/dist/cli/utils/008-sprint-shard-migration.test.js.map +1 -0
  50. package/packages/core/dist/cli/utils/settings-consolidation.test.js +26 -0
  51. package/packages/core/dist/cli/utils/settings-consolidation.test.js.map +1 -1
  52. package/packages/core/dist/cli/utils/settings-merge.d.ts +128 -0
  53. package/packages/core/dist/cli/utils/settings-merge.d.ts.map +1 -0
  54. package/packages/core/dist/cli/utils/settings-merge.js +303 -0
  55. package/packages/core/dist/cli/utils/settings-merge.js.map +1 -0
  56. package/packages/core/dist/cli/utils/settings-merge.test.d.ts +15 -0
  57. package/packages/core/dist/cli/utils/settings-merge.test.d.ts.map +1 -0
  58. package/packages/core/dist/cli/utils/settings-merge.test.js +781 -0
  59. package/packages/core/dist/cli/utils/settings-merge.test.js.map +1 -0
  60. package/packages/core/dist/cli/utils/settings.d.ts.map +1 -1
  61. package/packages/core/dist/cli/utils/settings.js +3 -0
  62. package/packages/core/dist/cli/utils/settings.js.map +1 -1
  63. package/packages/core/dist/cli/utils/symlinks.d.ts +6 -16
  64. package/packages/core/dist/cli/utils/symlinks.d.ts.map +1 -1
  65. package/packages/core/dist/cli/utils/symlinks.js +100 -171
  66. package/packages/core/dist/cli/utils/symlinks.js.map +1 -1
  67. package/packages/core/dist/cli/utils/symlinks.test.d.ts +10 -0
  68. package/packages/core/dist/cli/utils/symlinks.test.d.ts.map +1 -0
  69. package/packages/core/dist/cli/utils/symlinks.test.js +266 -0
  70. package/packages/core/dist/cli/utils/symlinks.test.js.map +1 -0
  71. package/packages/core/dist/cli/utils/themes.js +1 -1
  72. package/packages/core/dist/cli/workspace.test.js +2 -5
  73. package/packages/core/dist/cli/workspace.test.js.map +1 -1
  74. package/packages/core/dist/consolidation.test.d.ts +11 -0
  75. package/packages/core/dist/consolidation.test.d.ts.map +1 -0
  76. package/packages/core/dist/consolidation.test.js +249 -0
  77. package/packages/core/dist/consolidation.test.js.map +1 -0
  78. package/packages/core/dist/index.d.ts +2 -0
  79. package/packages/core/dist/index.d.ts.map +1 -1
  80. package/packages/core/dist/index.js +4 -0
  81. package/packages/core/dist/index.js.map +1 -1
  82. package/packages/core/dist/plugins/plugin-discovery.js +1 -1
  83. package/packages/core/dist/plugins/plugin-discovery.js.map +1 -1
  84. package/packages/core/dist/plugins/plugin-discovery.test.d.ts +1 -1
  85. package/packages/core/dist/plugins/plugin-discovery.test.js +47 -36
  86. package/packages/core/dist/plugins/plugin-discovery.test.js.map +1 -1
  87. package/packages/core/dist/public/css/react.css +1 -0
  88. package/packages/core/dist/public/js/react/react.js +82 -0
  89. package/packages/core/dist/scripts/generate-report.test.js +2 -2
  90. package/packages/core/dist/scripts/generate-spider-report.test.js +2 -2
  91. package/packages/core/dist/scripts/generate-spider.test.js +2 -1
  92. package/packages/core/dist/scripts/generate-spider.test.js.map +1 -1
  93. package/packages/core/dist/server/agent-context.d.ts +5 -0
  94. package/packages/core/dist/server/agent-context.d.ts.map +1 -0
  95. package/packages/core/dist/server/agent-context.js +5 -0
  96. package/packages/core/dist/server/agent-context.js.map +1 -0
  97. package/packages/core/dist/server/agent-evaluation.d.ts +17 -0
  98. package/packages/core/dist/server/agent-evaluation.d.ts.map +1 -0
  99. package/packages/core/dist/server/agent-evaluation.js +15 -0
  100. package/packages/core/dist/server/agent-evaluation.js.map +1 -0
  101. package/packages/core/dist/server/api/agent-load.d.ts +3 -0
  102. package/packages/core/dist/server/api/agent-load.d.ts.map +1 -0
  103. package/packages/core/dist/server/api/agent-load.js +19 -0
  104. package/packages/core/dist/server/api/agent-load.js.map +1 -0
  105. package/packages/core/dist/server/api/audit-log.d.ts +3 -0
  106. package/packages/core/dist/server/api/audit-log.d.ts.map +1 -0
  107. package/packages/core/dist/server/api/audit-log.js +31 -0
  108. package/packages/core/dist/server/api/audit-log.js.map +1 -0
  109. package/packages/core/dist/server/api/background-tasks.d.ts +27 -0
  110. package/packages/core/dist/server/api/background-tasks.d.ts.map +1 -0
  111. package/packages/core/dist/server/api/background-tasks.js +56 -0
  112. package/packages/core/dist/server/api/background-tasks.js.map +1 -0
  113. package/packages/core/dist/server/api/bell.d.ts +19 -0
  114. package/packages/core/dist/server/api/bell.d.ts.map +1 -0
  115. package/packages/core/dist/server/api/bell.js +34 -0
  116. package/packages/core/dist/server/api/bell.js.map +1 -0
  117. package/packages/core/dist/server/api/code-markers.d.ts +9 -0
  118. package/packages/core/dist/server/api/code-markers.d.ts.map +1 -0
  119. package/packages/core/dist/server/api/code-markers.js +62 -0
  120. package/packages/core/dist/server/api/code-markers.js.map +1 -0
  121. package/packages/core/dist/server/api/complexity.d.ts +3 -0
  122. package/packages/core/dist/server/api/complexity.d.ts.map +1 -0
  123. package/packages/core/dist/server/api/complexity.js +47 -0
  124. package/packages/core/dist/server/api/complexity.js.map +1 -0
  125. package/packages/core/dist/server/api/context.d.ts +38 -0
  126. package/packages/core/dist/server/api/context.d.ts.map +1 -0
  127. package/packages/core/dist/server/api/context.js +144 -0
  128. package/packages/core/dist/server/api/context.js.map +1 -0
  129. package/packages/core/dist/server/api/dead-code.d.ts +3 -0
  130. package/packages/core/dist/server/api/dead-code.d.ts.map +1 -0
  131. package/packages/core/dist/server/api/dead-code.js +70 -0
  132. package/packages/core/dist/server/api/dead-code.js.map +1 -0
  133. package/packages/core/dist/server/api/dependencies.d.ts +3 -0
  134. package/packages/core/dist/server/api/dependencies.d.ts.map +1 -0
  135. package/packages/core/dist/server/api/dependencies.js +43 -0
  136. package/packages/core/dist/server/api/dependencies.js.map +1 -0
  137. package/packages/core/dist/server/api/evaluation.d.ts +3 -0
  138. package/packages/core/dist/server/api/evaluation.d.ts.map +1 -0
  139. package/packages/core/dist/server/api/evaluation.js +33 -0
  140. package/packages/core/dist/server/api/evaluation.js.map +1 -0
  141. package/packages/core/dist/server/api/file-browser.d.ts +9 -0
  142. package/packages/core/dist/server/api/file-browser.d.ts.map +1 -0
  143. package/packages/core/dist/server/api/file-browser.js +133 -0
  144. package/packages/core/dist/server/api/file-browser.js.map +1 -0
  145. package/packages/core/dist/server/api/git-fetch-cooldown.test.d.ts +10 -0
  146. package/packages/core/dist/server/api/git-fetch-cooldown.test.d.ts.map +1 -0
  147. package/packages/core/dist/server/api/git-fetch-cooldown.test.js +30 -0
  148. package/packages/core/dist/server/api/git-fetch-cooldown.test.js.map +1 -0
  149. package/packages/core/dist/server/api/git.d.ts +55 -0
  150. package/packages/core/dist/server/api/git.d.ts.map +1 -0
  151. package/packages/core/dist/server/api/git.js +382 -0
  152. package/packages/core/dist/server/api/git.js.map +1 -0
  153. package/packages/core/dist/server/api/health-score.d.ts +3 -0
  154. package/packages/core/dist/server/api/health-score.d.ts.map +1 -0
  155. package/packages/core/dist/server/api/health-score.js +47 -0
  156. package/packages/core/dist/server/api/health-score.js.map +1 -0
  157. package/packages/core/dist/server/api/hook-request.d.ts +41 -0
  158. package/packages/core/dist/server/api/hook-request.d.ts.map +1 -0
  159. package/packages/core/dist/server/api/hook-request.js +278 -0
  160. package/packages/core/dist/server/api/hook-request.js.map +1 -0
  161. package/packages/core/dist/server/api/hotspots.d.ts +3 -0
  162. package/packages/core/dist/server/api/hotspots.d.ts.map +1 -0
  163. package/packages/core/dist/server/api/hotspots.js +62 -0
  164. package/packages/core/dist/server/api/hotspots.js.map +1 -0
  165. package/packages/core/dist/server/api/identity.d.ts +17 -0
  166. package/packages/core/dist/server/api/identity.d.ts.map +1 -0
  167. package/packages/core/dist/server/api/identity.js +79 -0
  168. package/packages/core/dist/server/api/identity.js.map +1 -0
  169. package/packages/core/dist/server/api/index.d.ts +36 -0
  170. package/packages/core/dist/server/api/index.d.ts.map +1 -0
  171. package/packages/core/dist/server/api/index.js +47 -0
  172. package/packages/core/dist/server/api/index.js.map +1 -0
  173. package/packages/core/dist/server/api/mode.d.ts +24 -0
  174. package/packages/core/dist/server/api/mode.d.ts.map +1 -0
  175. package/packages/core/dist/server/api/mode.js +40 -0
  176. package/packages/core/dist/server/api/mode.js.map +1 -0
  177. package/packages/core/dist/server/api/otlp.d.ts +3 -0
  178. package/packages/core/dist/server/api/otlp.d.ts.map +1 -0
  179. package/packages/core/dist/server/api/otlp.js +40 -0
  180. package/packages/core/dist/server/api/otlp.js.map +1 -0
  181. package/packages/core/dist/server/api/permissions.d.ts +16 -0
  182. package/packages/core/dist/server/api/permissions.d.ts.map +1 -0
  183. package/packages/core/dist/server/api/permissions.js +67 -0
  184. package/packages/core/dist/server/api/permissions.js.map +1 -0
  185. package/packages/core/dist/server/api/persona.d.ts +9 -0
  186. package/packages/core/dist/server/api/persona.d.ts.map +1 -0
  187. package/packages/core/dist/server/api/persona.js +68 -0
  188. package/packages/core/dist/server/api/persona.js.map +1 -0
  189. package/packages/core/dist/server/api/portrait.d.ts +6 -0
  190. package/packages/core/dist/server/api/portrait.d.ts.map +1 -0
  191. package/packages/core/dist/server/api/portrait.js +28 -0
  192. package/packages/core/dist/server/api/portrait.js.map +1 -0
  193. package/packages/core/dist/server/api/settings.d.ts +14 -0
  194. package/packages/core/dist/server/api/settings.d.ts.map +1 -0
  195. package/packages/core/dist/server/api/settings.js +68 -0
  196. package/packages/core/dist/server/api/settings.js.map +1 -0
  197. package/packages/core/dist/server/api/spans.d.ts +3 -0
  198. package/packages/core/dist/server/api/spans.d.ts.map +1 -0
  199. package/packages/core/dist/server/api/spans.js +33 -0
  200. package/packages/core/dist/server/api/spans.js.map +1 -0
  201. package/packages/core/dist/server/api/stats.d.ts +13 -0
  202. package/packages/core/dist/server/api/stats.d.ts.map +1 -0
  203. package/packages/core/dist/server/api/stats.js +85 -0
  204. package/packages/core/dist/server/api/stats.js.map +1 -0
  205. package/packages/core/dist/server/api/story.d.ts +3 -0
  206. package/packages/core/dist/server/api/story.d.ts.map +1 -0
  207. package/packages/core/dist/server/api/story.js +15 -0
  208. package/packages/core/dist/server/api/story.js.map +1 -0
  209. package/packages/core/dist/server/api/telemetry.d.ts +3 -0
  210. package/packages/core/dist/server/api/telemetry.d.ts.map +1 -0
  211. package/packages/core/dist/server/api/telemetry.js +30 -0
  212. package/packages/core/dist/server/api/telemetry.js.map +1 -0
  213. package/packages/core/dist/server/api/theme-agents.d.ts +10 -0
  214. package/packages/core/dist/server/api/theme-agents.d.ts.map +1 -0
  215. package/packages/core/dist/server/api/theme-agents.js +39 -0
  216. package/packages/core/dist/server/api/theme-agents.js.map +1 -0
  217. package/packages/core/dist/server/api/todos.d.ts +33 -0
  218. package/packages/core/dist/server/api/todos.d.ts.map +1 -0
  219. package/packages/core/dist/server/api/todos.js +44 -0
  220. package/packages/core/dist/server/api/todos.js.map +1 -0
  221. package/packages/core/dist/server/api/token-stats.d.ts +8 -0
  222. package/packages/core/dist/server/api/token-stats.d.ts.map +1 -0
  223. package/packages/core/dist/server/api/token-stats.js +36 -0
  224. package/packages/core/dist/server/api/token-stats.js.map +1 -0
  225. package/packages/core/dist/server/api/welcome.d.ts +22 -0
  226. package/packages/core/dist/server/api/welcome.d.ts.map +1 -0
  227. package/packages/core/dist/server/api/welcome.js +35 -0
  228. package/packages/core/dist/server/api/welcome.js.map +1 -0
  229. package/packages/core/dist/server/bell-mode.d.ts +7 -0
  230. package/packages/core/dist/server/bell-mode.d.ts.map +1 -0
  231. package/packages/core/dist/server/bell-mode.js +7 -0
  232. package/packages/core/dist/server/bell-mode.js.map +1 -0
  233. package/packages/core/dist/server/dangerous-path.d.ts +36 -0
  234. package/packages/core/dist/server/dangerous-path.d.ts.map +1 -0
  235. package/packages/core/dist/server/dangerous-path.js +159 -0
  236. package/packages/core/dist/server/dangerous-path.js.map +1 -0
  237. package/packages/core/dist/server/enriched-span-exporter.d.ts +17 -0
  238. package/packages/core/dist/server/enriched-span-exporter.d.ts.map +1 -0
  239. package/packages/core/dist/server/enriched-span-exporter.js +12 -0
  240. package/packages/core/dist/server/enriched-span-exporter.js.map +1 -0
  241. package/packages/core/dist/server/env.d.ts +6 -0
  242. package/packages/core/dist/server/env.d.ts.map +1 -0
  243. package/packages/core/dist/server/env.js +10 -0
  244. package/packages/core/dist/server/env.js.map +1 -0
  245. package/packages/core/dist/server/file-browser.d.ts +17 -0
  246. package/packages/core/dist/server/file-browser.d.ts.map +1 -0
  247. package/packages/core/dist/server/file-browser.js +11 -0
  248. package/packages/core/dist/server/file-browser.js.map +1 -0
  249. package/packages/core/dist/server/otlp-receiver.d.ts +87 -0
  250. package/packages/core/dist/server/otlp-receiver.d.ts.map +1 -0
  251. package/packages/core/dist/server/otlp-receiver.js +106 -0
  252. package/packages/core/dist/server/otlp-receiver.js.map +1 -0
  253. package/packages/core/dist/server/parser.d.ts +11 -0
  254. package/packages/core/dist/server/parser.d.ts.map +1 -0
  255. package/packages/core/dist/server/parser.js +6 -0
  256. package/packages/core/dist/server/parser.js.map +1 -0
  257. package/packages/core/dist/server/paths.d.ts +37 -0
  258. package/packages/core/dist/server/paths.d.ts.map +1 -0
  259. package/packages/core/dist/server/paths.js +227 -0
  260. package/packages/core/dist/server/paths.js.map +1 -0
  261. package/packages/core/dist/server/pennyfarthing.d.ts +56 -0
  262. package/packages/core/dist/server/pennyfarthing.d.ts.map +1 -0
  263. package/packages/core/dist/server/pennyfarthing.js +356 -0
  264. package/packages/core/dist/server/pennyfarthing.js.map +1 -0
  265. package/packages/core/dist/server/plugin-loader.d.ts +22 -0
  266. package/packages/core/dist/server/plugin-loader.d.ts.map +1 -0
  267. package/packages/core/dist/server/plugin-loader.js +67 -0
  268. package/packages/core/dist/server/plugin-loader.js.map +1 -0
  269. package/packages/core/dist/server/prime.d.ts +12 -0
  270. package/packages/core/dist/server/prime.d.ts.map +1 -0
  271. package/packages/core/dist/server/prime.js +8 -0
  272. package/packages/core/dist/server/prime.js.map +1 -0
  273. package/packages/core/dist/server/server.d.ts +47 -0
  274. package/packages/core/dist/server/server.d.ts.map +1 -0
  275. package/packages/core/dist/server/server.js +353 -0
  276. package/packages/core/dist/server/server.js.map +1 -0
  277. package/packages/core/dist/server/server.test.d.ts +26 -0
  278. package/packages/core/dist/server/server.test.d.ts.map +1 -0
  279. package/packages/core/dist/server/server.test.js +447 -0
  280. package/packages/core/dist/server/server.test.js.map +1 -0
  281. package/packages/core/dist/server/settings-store.d.ts +195 -0
  282. package/packages/core/dist/server/settings-store.d.ts.map +1 -0
  283. package/packages/core/dist/server/settings-store.js +398 -0
  284. package/packages/core/dist/server/settings-store.js.map +1 -0
  285. package/packages/core/dist/server/settings.d.ts +136 -0
  286. package/packages/core/dist/server/settings.d.ts.map +1 -0
  287. package/packages/core/dist/server/settings.js +417 -0
  288. package/packages/core/dist/server/settings.js.map +1 -0
  289. package/packages/core/dist/server/span-hierarchy.d.ts +5 -0
  290. package/packages/core/dist/server/span-hierarchy.d.ts.map +1 -0
  291. package/packages/core/dist/server/span-hierarchy.js +5 -0
  292. package/packages/core/dist/server/span-hierarchy.js.map +1 -0
  293. package/packages/core/dist/server/story-context.d.ts +5 -0
  294. package/packages/core/dist/server/story-context.d.ts.map +1 -0
  295. package/packages/core/dist/server/story-context.js +5 -0
  296. package/packages/core/dist/server/story-context.js.map +1 -0
  297. package/packages/core/dist/server/story-parser.d.ts +74 -0
  298. package/packages/core/dist/server/story-parser.d.ts.map +1 -0
  299. package/packages/core/dist/server/story-parser.js +748 -0
  300. package/packages/core/dist/server/story-parser.js.map +1 -0
  301. package/packages/core/dist/server/tdd-metrics.d.ts +5 -0
  302. package/packages/core/dist/server/tdd-metrics.d.ts.map +1 -0
  303. package/packages/core/dist/server/tdd-metrics.js +5 -0
  304. package/packages/core/dist/server/tdd-metrics.js.map +1 -0
  305. package/packages/core/dist/server/ui-build.test.d.ts +19 -0
  306. package/packages/core/dist/server/ui-build.test.d.ts.map +1 -0
  307. package/packages/core/dist/server/ui-build.test.js +152 -0
  308. package/packages/core/dist/server/ui-build.test.js.map +1 -0
  309. package/packages/core/dist/server/websocket.d.ts +7 -0
  310. package/packages/core/dist/server/websocket.d.ts.map +1 -0
  311. package/packages/core/dist/server/websocket.js +8 -0
  312. package/packages/core/dist/server/websocket.js.map +1 -0
  313. package/packages/core/dist/shared/browser.d.ts +6 -0
  314. package/packages/core/dist/shared/browser.d.ts.map +1 -0
  315. package/packages/core/dist/shared/browser.js +8 -0
  316. package/packages/core/dist/shared/browser.js.map +1 -0
  317. package/packages/core/dist/shared/generate-skill-docs.d.ts +35 -0
  318. package/packages/core/dist/shared/generate-skill-docs.d.ts.map +1 -0
  319. package/packages/core/dist/shared/generate-skill-docs.js +442 -0
  320. package/packages/core/dist/shared/generate-skill-docs.js.map +1 -0
  321. package/packages/core/dist/shared/generate-skill-docs.test.d.ts +13 -0
  322. package/packages/core/dist/shared/generate-skill-docs.test.d.ts.map +1 -0
  323. package/packages/core/dist/shared/generate-skill-docs.test.js +519 -0
  324. package/packages/core/dist/shared/generate-skill-docs.test.js.map +1 -0
  325. package/packages/core/dist/shared/index.d.ts +11 -0
  326. package/packages/core/dist/shared/index.d.ts.map +1 -0
  327. package/packages/core/dist/shared/index.js +13 -0
  328. package/packages/core/dist/shared/index.js.map +1 -0
  329. package/packages/core/dist/shared/marker/constants.d.ts +39 -0
  330. package/packages/core/dist/shared/marker/constants.d.ts.map +1 -0
  331. package/packages/core/dist/shared/marker/constants.js +46 -0
  332. package/packages/core/dist/shared/marker/constants.js.map +1 -0
  333. package/packages/core/dist/shared/marker/continue.test.d.ts +12 -0
  334. package/packages/core/dist/shared/marker/continue.test.d.ts.map +1 -0
  335. package/packages/core/dist/shared/marker/continue.test.js +76 -0
  336. package/packages/core/dist/shared/marker/continue.test.js.map +1 -0
  337. package/packages/core/dist/shared/marker/detect.d.ts +25 -0
  338. package/packages/core/dist/shared/marker/detect.d.ts.map +1 -0
  339. package/packages/core/dist/shared/marker/detect.js +53 -0
  340. package/packages/core/dist/shared/marker/detect.js.map +1 -0
  341. package/packages/core/dist/shared/marker/detect.test.d.ts +10 -0
  342. package/packages/core/dist/shared/marker/detect.test.d.ts.map +1 -0
  343. package/packages/core/dist/shared/marker/detect.test.js +418 -0
  344. package/packages/core/dist/shared/marker/detect.test.js.map +1 -0
  345. package/packages/core/dist/shared/marker/index.d.ts +14 -0
  346. package/packages/core/dist/shared/marker/index.d.ts.map +1 -0
  347. package/packages/core/dist/shared/marker/index.js +15 -0
  348. package/packages/core/dist/shared/marker/index.js.map +1 -0
  349. package/packages/core/dist/shared/marker/strip.d.ts +26 -0
  350. package/packages/core/dist/shared/marker/strip.d.ts.map +1 -0
  351. package/packages/core/dist/shared/marker/strip.js +39 -0
  352. package/packages/core/dist/shared/marker/strip.js.map +1 -0
  353. package/packages/core/dist/shared/marker/types.d.ts +23 -0
  354. package/packages/core/dist/shared/marker/types.d.ts.map +1 -0
  355. package/packages/core/dist/shared/marker/types.js +7 -0
  356. package/packages/core/dist/shared/marker/types.js.map +1 -0
  357. package/packages/core/dist/shared/migrate-theme-schema.test.d.ts +8 -0
  358. package/packages/core/dist/shared/migrate-theme-schema.test.d.ts.map +1 -0
  359. package/packages/core/dist/shared/migrate-theme-schema.test.js +100 -0
  360. package/packages/core/dist/shared/migrate-theme-schema.test.js.map +1 -0
  361. package/packages/core/dist/shared/portrait-resolver.d.ts +35 -0
  362. package/packages/core/dist/shared/portrait-resolver.d.ts.map +1 -0
  363. package/packages/core/dist/shared/portrait-resolver.js +259 -0
  364. package/packages/core/dist/shared/portrait-resolver.js.map +1 -0
  365. package/packages/core/dist/shared/portrait-resolver.test.d.ts +2 -0
  366. package/packages/core/dist/shared/portrait-resolver.test.d.ts.map +1 -0
  367. package/packages/core/dist/shared/portrait-resolver.test.js +157 -0
  368. package/packages/core/dist/shared/portrait-resolver.test.js.map +1 -0
  369. package/packages/core/dist/shared/repos-topology.d.ts +49 -0
  370. package/packages/core/dist/shared/repos-topology.d.ts.map +1 -0
  371. package/packages/core/dist/shared/repos-topology.js +101 -0
  372. package/packages/core/dist/shared/repos-topology.js.map +1 -0
  373. package/packages/core/dist/shared/repos-topology.test.d.ts +8 -0
  374. package/packages/core/dist/shared/repos-topology.test.d.ts.map +1 -0
  375. package/packages/core/dist/shared/repos-topology.test.js +250 -0
  376. package/packages/core/dist/shared/repos-topology.test.js.map +1 -0
  377. package/packages/core/dist/shared/skill-search.d.ts +36 -0
  378. package/packages/core/dist/shared/skill-search.d.ts.map +1 -0
  379. package/packages/core/dist/shared/skill-search.js +300 -0
  380. package/packages/core/dist/shared/skill-search.js.map +1 -0
  381. package/packages/core/dist/shared/skill-search.test.d.ts +16 -0
  382. package/packages/core/dist/shared/skill-search.test.d.ts.map +1 -0
  383. package/packages/core/dist/shared/skill-search.test.js +177 -0
  384. package/packages/core/dist/shared/skill-search.test.js.map +1 -0
  385. package/packages/core/dist/shared/skill-suggest.d.ts +76 -0
  386. package/packages/core/dist/shared/skill-suggest.d.ts.map +1 -0
  387. package/packages/core/dist/shared/skill-suggest.js +256 -0
  388. package/packages/core/dist/shared/skill-suggest.js.map +1 -0
  389. package/packages/core/dist/shared/skill-suggest.test.d.ts +12 -0
  390. package/packages/core/dist/shared/skill-suggest.test.d.ts.map +1 -0
  391. package/packages/core/dist/shared/skill-suggest.test.js +257 -0
  392. package/packages/core/dist/shared/skill-suggest.test.js.map +1 -0
  393. package/packages/core/dist/shared/theme-loader.d.ts +81 -0
  394. package/packages/core/dist/shared/theme-loader.d.ts.map +1 -0
  395. package/packages/core/dist/shared/theme-loader.js +491 -0
  396. package/packages/core/dist/shared/theme-loader.js.map +1 -0
  397. package/packages/core/dist/shared/theme-loader.test.d.ts +8 -0
  398. package/packages/core/dist/shared/theme-loader.test.d.ts.map +1 -0
  399. package/packages/core/dist/shared/theme-loader.test.js +62 -0
  400. package/packages/core/dist/shared/theme-loader.test.js.map +1 -0
  401. package/packages/core/dist/workflow/context-watch.test.js +1 -1
  402. package/packages/core/dist/workflow/context-watch.test.js.map +1 -1
  403. package/packages/core/dist/workflow/variable-resolver.test.js +1 -1
  404. package/packages/core/dist/workflow/variable-resolver.test.js.map +1 -1
  405. package/packages/core/dist/workflow/workflow-migration.test.js +4 -3
  406. package/packages/core/dist/workflow/workflow-migration.test.js.map +1 -1
  407. package/pennyfarthing-dist/agents/architect.md +1 -2
  408. package/pennyfarthing-dist/agents/ba.md +1 -1
  409. package/pennyfarthing-dist/agents/dev.md +2 -3
  410. package/pennyfarthing-dist/agents/devops.md +1 -1
  411. package/pennyfarthing-dist/agents/orchestrator.md +2 -3
  412. package/pennyfarthing-dist/agents/pm.md +2 -2
  413. package/pennyfarthing-dist/agents/reviewer.md +2 -2
  414. package/pennyfarthing-dist/agents/sm-setup.md +7 -7
  415. package/pennyfarthing-dist/agents/sm.md +19 -18
  416. package/pennyfarthing-dist/agents/tea.md +3 -2
  417. package/pennyfarthing-dist/agents/tech-writer.md +1 -1
  418. package/pennyfarthing-dist/agents/ux-designer.md +0 -1
  419. package/pennyfarthing-dist/commands/benchmark-control.md +69 -0
  420. package/pennyfarthing-dist/commands/benchmark.md +485 -0
  421. package/pennyfarthing-dist/commands/job-fair.md +102 -0
  422. package/pennyfarthing-dist/commands/{close-epic.md → pf-close-epic.md} +1 -1
  423. package/pennyfarthing-dist/commands/{new-work.md → pf-new-work.md} +4 -4
  424. package/pennyfarthing-dist/commands/{party-mode.md → pf-party-mode.md} +15 -2
  425. package/pennyfarthing-dist/commands/{patch.md → pf-patch.md} +3 -3
  426. package/pennyfarthing-dist/commands/{release.md → pf-release.md} +17 -10
  427. package/pennyfarthing-dist/commands/{setup.md → pf-setup.md} +2 -2
  428. package/pennyfarthing-dist/commands/{sprint.md → pf-sprint.md} +19 -19
  429. package/pennyfarthing-dist/commands/{standalone.md → pf-standalone.md} +2 -2
  430. package/pennyfarthing-dist/commands/{workflow.md → pf-workflow.md} +7 -7
  431. package/pennyfarthing-dist/commands/solo.md +447 -0
  432. package/pennyfarthing-dist/guides/agent-behavior.md +2 -2
  433. package/pennyfarthing-dist/guides/agent-tag-taxonomy.md +1 -2
  434. package/pennyfarthing-dist/guides/agent-template-strategic.md +1 -1
  435. package/pennyfarthing-dist/guides/agent-template-tactical.md +2 -2
  436. package/pennyfarthing-dist/guides/bikelane.md +6 -6
  437. package/pennyfarthing-dist/guides/permission-protocol.md +1 -1
  438. package/pennyfarthing-dist/guides/scale-levels.md +2 -2
  439. package/pennyfarthing-dist/guides/skill-schema.md +9 -9
  440. package/pennyfarthing-dist/guides/xml-tags.md +2 -2
  441. package/pennyfarthing-dist/personas/themes/dune.yaml +0 -1
  442. package/pennyfarthing-dist/personas/themes/hogans-heroes.yaml +260 -0
  443. package/pennyfarthing-dist/personas/themes/monty-python.yaml +312 -0
  444. package/pennyfarthing-dist/personas/themes/stephen-king.yaml +260 -0
  445. package/pennyfarthing-dist/scripts/core/agent-session.sh +0 -0
  446. package/pennyfarthing-dist/scripts/core/check-context.sh +0 -0
  447. package/pennyfarthing-dist/scripts/core/handoff-marker.sh +0 -0
  448. package/pennyfarthing-dist/scripts/core/phase-check-start.sh +0 -0
  449. package/pennyfarthing-dist/scripts/core/prime.sh +0 -0
  450. package/pennyfarthing-dist/scripts/cyclist/is-cyclist.sh +0 -0
  451. package/pennyfarthing-dist/scripts/git/create-feature-branches.sh +0 -0
  452. package/pennyfarthing-dist/scripts/git/git-status-all.sh +0 -0
  453. package/pennyfarthing-dist/scripts/git/install-git-hooks.sh +84 -21
  454. package/pennyfarthing-dist/scripts/git/release.sh +0 -0
  455. package/pennyfarthing-dist/scripts/git/worktree-manager.sh +0 -0
  456. package/pennyfarthing-dist/scripts/health/drift-detection.sh +0 -0
  457. package/pennyfarthing-dist/scripts/hooks/bell-mode-hook.sh +0 -0
  458. package/pennyfarthing-dist/scripts/hooks/context-circuit-breaker.sh +0 -0
  459. package/pennyfarthing-dist/scripts/hooks/context-warning.sh +0 -0
  460. package/pennyfarthing-dist/scripts/hooks/cyclist-pretooluse-hook.sh +0 -0
  461. package/pennyfarthing-dist/scripts/hooks/dispatcher-template.sh +45 -0
  462. package/pennyfarthing-dist/scripts/hooks/otel-auto-config.sh +0 -0
  463. package/pennyfarthing-dist/scripts/hooks/post-merge.sh +0 -0
  464. package/pennyfarthing-dist/scripts/hooks/pre-commit.sh +27 -2
  465. package/pennyfarthing-dist/scripts/hooks/pre-edit-check.sh +0 -0
  466. package/pennyfarthing-dist/scripts/hooks/pre-push.sh +0 -0
  467. package/pennyfarthing-dist/scripts/hooks/question-reflector-check.sh +0 -0
  468. package/pennyfarthing-dist/scripts/hooks/question_reflector_check.py +0 -0
  469. package/pennyfarthing-dist/scripts/hooks/schema-validation.sh +0 -0
  470. package/pennyfarthing-dist/scripts/hooks/session-start.sh +25 -89
  471. package/pennyfarthing-dist/scripts/hooks/session-stop.sh +0 -0
  472. package/pennyfarthing-dist/scripts/hooks/sprint-yaml-validation.sh +0 -0
  473. package/pennyfarthing-dist/scripts/hooks/welcome-hook.sh +0 -0
  474. package/pennyfarthing-dist/scripts/jira/README.md +1 -1
  475. package/pennyfarthing-dist/scripts/jira/create-jira-epic.sh +0 -0
  476. package/pennyfarthing-dist/scripts/jira/create-jira-story.sh +0 -0
  477. package/pennyfarthing-dist/scripts/jira/jira-claim-story.sh +0 -0
  478. package/pennyfarthing-dist/scripts/jira/jira-reconcile.sh +0 -0
  479. package/pennyfarthing-dist/scripts/jira/jira-sync-story.sh +0 -0
  480. package/pennyfarthing-dist/scripts/jira/sync-epic-jira.sh +0 -0
  481. package/pennyfarthing-dist/scripts/lib/background-tasks.sh +0 -0
  482. package/pennyfarthing-dist/scripts/lib/checkpoint.sh +0 -0
  483. package/pennyfarthing-dist/scripts/lib/common.sh +0 -0
  484. package/pennyfarthing-dist/scripts/lib/file-lock.sh +0 -0
  485. package/pennyfarthing-dist/scripts/lib/logging.sh +0 -0
  486. package/pennyfarthing-dist/scripts/lib/retry.sh +0 -0
  487. package/pennyfarthing-dist/scripts/maintenance/migrate-theme-schema.mjs +0 -0
  488. package/pennyfarthing-dist/scripts/maintenance/sidecar-health.sh +0 -0
  489. package/pennyfarthing-dist/scripts/misc/add-short-names.sh +0 -0
  490. package/pennyfarthing-dist/scripts/misc/add_short_names.py +0 -0
  491. package/pennyfarthing-dist/scripts/misc/backlog.sh +0 -0
  492. package/pennyfarthing-dist/scripts/misc/check-status.sh +0 -0
  493. package/pennyfarthing-dist/scripts/misc/find-related-work.sh +0 -0
  494. package/pennyfarthing-dist/scripts/misc/generate-skill-docs.sh +0 -0
  495. package/pennyfarthing-dist/scripts/misc/log-skill-usage.sh +0 -0
  496. package/pennyfarthing-dist/scripts/misc/migrate-bmad-workflow.sh +0 -0
  497. package/pennyfarthing-dist/scripts/misc/migrate_bmad_workflow.py +0 -0
  498. package/pennyfarthing-dist/scripts/misc/repo-scan.sh +0 -0
  499. package/pennyfarthing-dist/scripts/misc/repo-utils.sh +0 -0
  500. package/pennyfarthing-dist/scripts/misc/run-ci.sh +0 -0
  501. package/pennyfarthing-dist/scripts/misc/run-timestamp.sh +0 -0
  502. package/pennyfarthing-dist/scripts/misc/session-cleanup.sh +0 -0
  503. package/pennyfarthing-dist/scripts/misc/skill-usage-report.sh +0 -0
  504. package/pennyfarthing-dist/scripts/misc/statusline.sh +0 -0
  505. package/pennyfarthing-dist/scripts/misc/uninstall.sh +0 -0
  506. package/pennyfarthing-dist/scripts/misc/validate-subagent-frontmatter.sh +0 -0
  507. package/pennyfarthing-dist/scripts/portraits/generate-portraits.py +50 -26
  508. package/pennyfarthing-dist/scripts/portraits/generate-portraits.sh +18 -6
  509. package/pennyfarthing-dist/scripts/sprint/README.md +1 -1
  510. package/pennyfarthing-dist/scripts/story/README.md +1 -1
  511. package/pennyfarthing-dist/scripts/story/create-story.sh +0 -0
  512. package/pennyfarthing-dist/scripts/story/size-story.sh +0 -0
  513. package/pennyfarthing-dist/scripts/story/story-template.sh +0 -0
  514. package/pennyfarthing-dist/scripts/tests/check.test.sh +0 -0
  515. package/pennyfarthing-dist/scripts/tests/dev-story-workflow-import.test.sh +0 -0
  516. package/pennyfarthing-dist/scripts/tests/epics-and-stories-workflow-import.test.sh +0 -0
  517. package/pennyfarthing-dist/scripts/tests/handoff-phase-update.test.sh +0 -0
  518. package/pennyfarthing-dist/scripts/tests/implementation-readiness-workflow-import.test.sh +0 -0
  519. package/pennyfarthing-dist/scripts/tests/migrate-bmad-workflow.test.sh +0 -0
  520. package/pennyfarthing-dist/scripts/tests/prd-workflow-import.test.sh +0 -0
  521. package/pennyfarthing-dist/scripts/tests/project-context-workflow-import.test.sh +0 -0
  522. package/pennyfarthing-dist/scripts/tests/test-character-voice.sh +0 -0
  523. package/pennyfarthing-dist/scripts/tests/test-drift-detection.sh +0 -0
  524. package/pennyfarthing-dist/scripts/tests/test-post-merge-hook.sh +0 -0
  525. package/pennyfarthing-dist/scripts/tests/test-session-checkpoint.sh +0 -0
  526. package/pennyfarthing-dist/scripts/tests/test-solo-command.sh +0 -0
  527. package/pennyfarthing-dist/scripts/tests/ux-design-workflow-import.test.sh +0 -0
  528. package/pennyfarthing-dist/scripts/theme/list-themes.sh +0 -0
  529. package/pennyfarthing-dist/scripts/validation/validate-agent-schema.sh +0 -0
  530. package/pennyfarthing-dist/scripts/workflow/README.md +1 -1
  531. package/pennyfarthing-dist/scripts/workflow/check.py +0 -0
  532. package/pennyfarthing-dist/scripts/workflow/check.sh +0 -0
  533. package/pennyfarthing-dist/scripts/workflow/complete-step.py +0 -0
  534. package/pennyfarthing-dist/scripts/workflow/finish-story.sh +0 -0
  535. package/pennyfarthing-dist/scripts/workflow/fix-session-phase.sh +0 -0
  536. package/pennyfarthing-dist/scripts/workflow/get-workflow-type.py +0 -0
  537. package/pennyfarthing-dist/scripts/workflow/get-workflow-type.sh +0 -0
  538. package/pennyfarthing-dist/scripts/workflow/list-workflows.sh +0 -0
  539. package/pennyfarthing-dist/scripts/workflow/phase-owner.sh +0 -0
  540. package/pennyfarthing-dist/scripts/workflow/resume-workflow.sh +0 -0
  541. package/pennyfarthing-dist/scripts/workflow/show-workflow.sh +0 -0
  542. package/pennyfarthing-dist/scripts/workflow/start-workflow.sh +0 -0
  543. package/pennyfarthing-dist/scripts/workflow/workflow-status.sh +0 -0
  544. package/pennyfarthing-dist/skills/pf-bc/examples.md +83 -0
  545. package/pennyfarthing-dist/skills/pf-bc/skill.md +51 -0
  546. package/pennyfarthing-dist/skills/pf-bc/usage.md +113 -0
  547. package/pennyfarthing-dist/skills/{cyclist → pf-cyclist}/SKILL.md +5 -5
  548. package/pennyfarthing-dist/skills/pf-finalize-run/SKILL.md +261 -0
  549. package/pennyfarthing-dist/skills/pf-jira/SKILL.md +61 -0
  550. package/pennyfarthing-dist/skills/pf-jira/examples.md +163 -0
  551. package/pennyfarthing-dist/skills/pf-jira/usage.md +213 -0
  552. package/pennyfarthing-dist/skills/pf-judge/SKILL.md +644 -0
  553. package/pennyfarthing-dist/skills/{just → pf-just}/SKILL.md +23 -23
  554. package/pennyfarthing-dist/skills/{permissions → pf-permissions}/skill.md +8 -8
  555. package/pennyfarthing-dist/skills/pf-persona-benchmark/SKILL.md +187 -0
  556. package/pennyfarthing-dist/skills/pf-sprint/examples.md +189 -0
  557. package/pennyfarthing-dist/skills/pf-sprint/skill.md +85 -0
  558. package/pennyfarthing-dist/skills/pf-sprint/usage.md +293 -0
  559. package/pennyfarthing-dist/skills/{story → pf-story}/scripts/create-story.sh +0 -0
  560. package/pennyfarthing-dist/skills/{story → pf-story}/scripts/size-story.sh +0 -0
  561. package/pennyfarthing-dist/skills/{story → pf-story}/scripts/story-template.sh +0 -0
  562. package/pennyfarthing-dist/skills/pf-story/skill.md +27 -0
  563. package/pennyfarthing-dist/skills/{systematic-debugging → pf-systematic-debugging}/SKILL.md +5 -6
  564. package/pennyfarthing-dist/skills/pf-theme/examples.md +92 -0
  565. package/pennyfarthing-dist/skills/pf-theme/skill.md +42 -0
  566. package/pennyfarthing-dist/skills/pf-theme/usage.md +149 -0
  567. package/pennyfarthing-dist/skills/pf-workflow/examples.md +101 -0
  568. package/pennyfarthing-dist/skills/{workflow → pf-workflow}/scripts/list-workflows.sh +0 -0
  569. package/pennyfarthing-dist/skills/{workflow → pf-workflow}/scripts/resume-workflow.sh +0 -0
  570. package/pennyfarthing-dist/skills/{workflow → pf-workflow}/scripts/show-workflow.sh +0 -0
  571. package/pennyfarthing-dist/skills/{workflow → pf-workflow}/scripts/start-workflow.sh +0 -0
  572. package/pennyfarthing-dist/skills/{workflow → pf-workflow}/scripts/workflow-status.sh +0 -0
  573. package/pennyfarthing-dist/skills/pf-workflow/skill.md +49 -0
  574. package/pennyfarthing-dist/skills/pf-workflow/usage.md +145 -0
  575. package/pennyfarthing-dist/skills/skill-registry.yaml +63 -81
  576. package/pennyfarthing-dist/workflows/architecture/data/domain-complexity.csv +13 -0
  577. package/pennyfarthing-dist/workflows/architecture/data/project-types.csv +7 -0
  578. package/pennyfarthing-dist/workflows/epics-and-stories/steps/step-05-import-to-future.md +40 -11
  579. package/pennyfarthing-dist/workflows/party-mode-roleplay/steps/step-01-agent-loading.md +99 -0
  580. package/pennyfarthing-dist/workflows/party-mode-roleplay/steps/step-02-discussion.md +130 -0
  581. package/pennyfarthing-dist/workflows/party-mode-roleplay/steps/step-03-exit.md +100 -0
  582. package/pennyfarthing-dist/workflows/party-mode-roleplay/workflow.yaml +62 -0
  583. package/pennyfarthing-dist/workflows/prd/data/domain-complexity.csv +2 -0
  584. package/pennyfarthing-dist/workflows/prd/data/prd-purpose.md +2 -0
  585. package/pennyfarthing-dist/workflows/prd/steps-v/step-v-08-domain-compliance-validation.md +16 -0
  586. package/pennyfarthing-dist/workflows/project-setup/steps/step-04-claude-md.md +3 -3
  587. package/pennyfarthing-dist/workflows/quick-spec/steps/step-01-understand.md +193 -0
  588. package/pennyfarthing-dist/workflows/quick-spec/steps/step-02-investigate.md +148 -0
  589. package/pennyfarthing-dist/workflows/quick-spec/steps/step-03-generate.md +141 -0
  590. package/pennyfarthing-dist/workflows/quick-spec/steps/step-04-review.md +192 -0
  591. package/pennyfarthing-dist/workflows/quick-spec/tech-spec-template.md +74 -0
  592. package/pennyfarthing-dist/workflows/quick-spec/workflow.yaml +35 -0
  593. package/pennyfarthing-dist/workflows/release/steps/step-01-preflight.md +42 -3
  594. package/pennyfarthing-dist/workflows/release/steps/step-02-bump.md +10 -3
  595. package/pennyfarthing-dist/workflows/release/steps/step-03-changelog.md +1 -1
  596. package/pennyfarthing-dist/workflows/release/steps/step-07-commit.md +13 -4
  597. package/pennyfarthing-dist/workflows/release/steps/step-08-merge.md +31 -6
  598. package/pennyfarthing-dist/workflows/release/steps/step-09-push.md +12 -6
  599. package/pennyfarthing-dist/workflows/release/steps/step-10-publish.md +30 -5
  600. package/pennyfarthing-dist/workflows/release/steps/step-11-finalize.md +29 -0
  601. package/pennyfarthing-dist/workflows/release.yaml +7 -0
  602. package/pennyfarthing_scripts/CLAUDE.md +182 -0
  603. package/pennyfarthing_scripts/__pycache__/cli.cpython-314.pyc +0 -0
  604. package/pennyfarthing_scripts/__pycache__/patch_mode.cpython-314.pyc +0 -0
  605. package/pennyfarthing_scripts/__pycache__/session_start_hook.cpython-314.pyc +0 -0
  606. package/pennyfarthing_scripts/bc/__init__.py +1 -0
  607. package/pennyfarthing_scripts/bc/__pycache__/__init__.cpython-314.pyc +0 -0
  608. package/pennyfarthing_scripts/bc/__pycache__/cli.cpython-314.pyc +0 -0
  609. package/pennyfarthing_scripts/bc/__pycache__/focus.cpython-314.pyc +0 -0
  610. package/pennyfarthing_scripts/bc/cli.py +223 -0
  611. package/pennyfarthing_scripts/bc/focus.py +333 -0
  612. package/pennyfarthing_scripts/bikerack/__init__.py +8 -0
  613. package/pennyfarthing_scripts/bikerack/__pycache__/__init__.cpython-314.pyc +0 -0
  614. package/pennyfarthing_scripts/bikerack/__pycache__/background_panel.cpython-314.pyc +0 -0
  615. package/pennyfarthing_scripts/bikerack/__pycache__/base_panel.cpython-314.pyc +0 -0
  616. package/pennyfarthing_scripts/bikerack/__pycache__/changed_panel.cpython-314.pyc +0 -0
  617. package/pennyfarthing_scripts/bikerack/__pycache__/cli.cpython-314.pyc +0 -0
  618. package/pennyfarthing_scripts/bikerack/__pycache__/diffs_panel.cpython-314.pyc +0 -0
  619. package/pennyfarthing_scripts/bikerack/__pycache__/git_panel.cpython-314.pyc +0 -0
  620. package/pennyfarthing_scripts/bikerack/__pycache__/launcher.cpython-314.pyc +0 -0
  621. package/pennyfarthing_scripts/bikerack/__pycache__/sprint_panel.cpython-314.pyc +0 -0
  622. package/pennyfarthing_scripts/bikerack/__pycache__/tui.cpython-314.pyc +0 -0
  623. package/pennyfarthing_scripts/bikerack/__pycache__/ws_client.cpython-314.pyc +0 -0
  624. package/pennyfarthing_scripts/bikerack/background_panel.py +81 -0
  625. package/pennyfarthing_scripts/bikerack/base_panel.py +100 -0
  626. package/pennyfarthing_scripts/bikerack/changed_panel.py +105 -0
  627. package/pennyfarthing_scripts/bikerack/cli.py +22 -24
  628. package/pennyfarthing_scripts/bikerack/diffs_panel.py +146 -0
  629. package/pennyfarthing_scripts/bikerack/git_panel.py +69 -0
  630. package/pennyfarthing_scripts/bikerack/launcher.py +63 -4
  631. package/pennyfarthing_scripts/bikerack/sprint_panel.py +72 -0
  632. package/pennyfarthing_scripts/bikerack/tui.py +193 -0
  633. package/pennyfarthing_scripts/bikerack/ws_client.py +176 -0
  634. package/pennyfarthing_scripts/brownfield/__pycache__/__init__.cpython-314.pyc +0 -0
  635. package/pennyfarthing_scripts/brownfield/__pycache__/__main__.cpython-314.pyc +0 -0
  636. package/pennyfarthing_scripts/brownfield/__pycache__/cli.cpython-314.pyc +0 -0
  637. package/pennyfarthing_scripts/brownfield/__pycache__/discover.cpython-314.pyc +0 -0
  638. package/pennyfarthing_scripts/cli.py +60 -16
  639. package/pennyfarthing_scripts/codemarkers/__pycache__/formatters.cpython-314.pyc +0 -0
  640. package/pennyfarthing_scripts/complexity/__pycache__/__init__.cpython-314.pyc +0 -0
  641. package/pennyfarthing_scripts/complexity/__pycache__/analyze.cpython-314.pyc +0 -0
  642. package/pennyfarthing_scripts/deadcode/__pycache__/analyze.cpython-314.pyc +0 -0
  643. package/pennyfarthing_scripts/dependencies/__pycache__/__init__.cpython-314.pyc +0 -0
  644. package/pennyfarthing_scripts/dependencies/__pycache__/analyze.cpython-314.pyc +0 -0
  645. package/pennyfarthing_scripts/git/__pycache__/__init__.cpython-314.pyc +0 -0
  646. package/pennyfarthing_scripts/git/__pycache__/create_branches.cpython-314.pyc +0 -0
  647. package/pennyfarthing_scripts/git/__pycache__/status_all.cpython-314.pyc +0 -0
  648. package/pennyfarthing_scripts/healthscore/__pycache__/__main__.cpython-314.pyc +0 -0
  649. package/pennyfarthing_scripts/healthscore/__pycache__/analyze.cpython-314.pyc +0 -0
  650. package/pennyfarthing_scripts/healthscore/analyze.py +15 -1
  651. package/pennyfarthing_scripts/hooks/cyclist-pretooluse-hook.sh +0 -0
  652. package/pennyfarthing_scripts/jira/__pycache__/cli.cpython-314.pyc +0 -0
  653. package/pennyfarthing_scripts/jira/cli.py +10 -2
  654. package/pennyfarthing_scripts/launch/__init__.py +1 -0
  655. package/pennyfarthing_scripts/launch/__pycache__/__init__.cpython-314.pyc +0 -0
  656. package/pennyfarthing_scripts/launch/__pycache__/cli.cpython-314.pyc +0 -0
  657. package/pennyfarthing_scripts/launch/cli.py +231 -0
  658. package/pennyfarthing_scripts/preflight/__pycache__/__init__.cpython-314.pyc +0 -0
  659. package/pennyfarthing_scripts/preflight/__pycache__/finish.cpython-314.pyc +0 -0
  660. package/pennyfarthing_scripts/prime/__pycache__/version_sentinel.cpython-314.pyc +0 -0
  661. package/pennyfarthing_scripts/prime/version_sentinel.py +0 -1
  662. package/pennyfarthing_scripts/session_start_hook.py +192 -0
  663. package/pennyfarthing_scripts/sprint/__pycache__/__main__.cpython-314.pyc +0 -0
  664. package/pennyfarthing_scripts/sprint/__pycache__/archive_epic.cpython-314.pyc +0 -0
  665. package/pennyfarthing_scripts/sprint/__pycache__/cli.cpython-314.pyc +0 -0
  666. package/pennyfarthing_scripts/sprint/__pycache__/epic_add.cpython-314.pyc +0 -0
  667. package/pennyfarthing_scripts/sprint/__pycache__/epic_update.cpython-314.pyc +0 -0
  668. package/pennyfarthing_scripts/sprint/__pycache__/import_epic.cpython-314.pyc +0 -0
  669. package/pennyfarthing_scripts/sprint/__pycache__/loader.cpython-314.pyc +0 -0
  670. package/pennyfarthing_scripts/sprint/__pycache__/story_add.cpython-314.pyc +0 -0
  671. package/pennyfarthing_scripts/sprint/__pycache__/story_finish.cpython-314.pyc +0 -0
  672. package/pennyfarthing_scripts/sprint/__pycache__/story_update.cpython-314.pyc +0 -0
  673. package/pennyfarthing_scripts/sprint/__pycache__/validate_cmd.cpython-314.pyc +0 -0
  674. package/pennyfarthing_scripts/sprint/__pycache__/yaml_io.cpython-314.pyc +0 -0
  675. package/pennyfarthing_scripts/sprint/archive_epic.py +109 -1
  676. package/pennyfarthing_scripts/sprint/cli.py +30 -9
  677. package/pennyfarthing_scripts/sprint/epic_add.py +7 -1
  678. package/pennyfarthing_scripts/sprint/epic_update.py +148 -0
  679. package/pennyfarthing_scripts/sprint/loader.py +33 -0
  680. package/pennyfarthing_scripts/sprint/story_add.py +11 -1
  681. package/pennyfarthing_scripts/sprint/story_finish.py +14 -10
  682. package/pennyfarthing_scripts/sprint/story_update.py +7 -0
  683. package/pennyfarthing_scripts/sprint/validate_cmd.py +2 -1
  684. package/pennyfarthing_scripts/sprint/yaml_io.py +64 -6
  685. package/pennyfarthing_scripts/story/__pycache__/__init__.cpython-314.pyc +0 -0
  686. package/pennyfarthing_scripts/story/__pycache__/size.cpython-314.pyc +0 -0
  687. package/pennyfarthing_scripts/story/__pycache__/template.cpython-314.pyc +0 -0
  688. package/pennyfarthing_scripts/tests/__pycache__/test_archive_epic.cpython-314-pytest-9.0.2.pyc +0 -0
  689. package/pennyfarthing_scripts/tests/__pycache__/test_bc.cpython-314-pytest-9.0.2.pyc +0 -0
  690. package/pennyfarthing_scripts/tests/__pycache__/test_bikerack.cpython-314-pytest-9.0.2.pyc +0 -0
  691. package/pennyfarthing_scripts/tests/__pycache__/test_brownfield.cpython-314-pytest-9.0.2.pyc +0 -0
  692. package/pennyfarthing_scripts/tests/__pycache__/test_cli_modules.cpython-314-pytest-9.0.2.pyc +0 -0
  693. package/pennyfarthing_scripts/tests/__pycache__/test_cli_normalization.cpython-314-pytest-9.0.2.pyc +0 -0
  694. package/pennyfarthing_scripts/tests/__pycache__/test_codemarkers.cpython-314-pytest-9.0.2.pyc +0 -0
  695. package/pennyfarthing_scripts/tests/__pycache__/test_common.cpython-314-pytest-9.0.2.pyc +0 -0
  696. package/pennyfarthing_scripts/tests/__pycache__/test_epic_shard_validation.cpython-314-pytest-9.0.2.pyc +0 -0
  697. package/pennyfarthing_scripts/tests/__pycache__/test_git_utils.cpython-314-pytest-9.0.2.pyc +0 -0
  698. package/pennyfarthing_scripts/tests/__pycache__/test_healthscore.cpython-314-pytest-9.0.2.pyc +0 -0
  699. package/pennyfarthing_scripts/tests/__pycache__/test_jira_package.cpython-314-pytest-9.0.2.pyc +0 -0
  700. package/pennyfarthing_scripts/tests/__pycache__/test_package_structure.cpython-314-pytest-9.0.2.pyc +0 -0
  701. package/pennyfarthing_scripts/tests/__pycache__/test_patch_mode.cpython-314-pytest-9.0.2.pyc +0 -0
  702. package/pennyfarthing_scripts/tests/__pycache__/test_prime.cpython-314-pytest-9.0.2.pyc +0 -0
  703. package/pennyfarthing_scripts/tests/__pycache__/test_sprint_package.cpython-314-pytest-9.0.2.pyc +0 -0
  704. package/pennyfarthing_scripts/tests/__pycache__/test_sprint_panel.cpython-314-pytest-9.0.2.pyc +0 -0
  705. package/pennyfarthing_scripts/tests/__pycache__/test_sprint_validator.cpython-314-pytest-9.0.2.pyc +0 -0
  706. package/pennyfarthing_scripts/tests/__pycache__/test_story_add.cpython-314-pytest-9.0.2.pyc +0 -0
  707. package/pennyfarthing_scripts/tests/__pycache__/test_story_package.cpython-314-pytest-9.0.2.pyc +0 -0
  708. package/pennyfarthing_scripts/tests/__pycache__/test_story_update.cpython-314-pytest-9.0.2.pyc +0 -0
  709. package/pennyfarthing_scripts/tests/__pycache__/test_tiers.cpython-314-pytest-9.0.2.pyc +0 -0
  710. package/pennyfarthing_scripts/tests/__pycache__/test_token_counting.cpython-314-pytest-9.0.2.pyc +0 -0
  711. package/pennyfarthing_scripts/tests/__pycache__/test_topology_loader.cpython-314-pytest-9.0.2.pyc +0 -0
  712. package/pennyfarthing_scripts/tests/__pycache__/test_tui_focus.cpython-314-pytest-9.0.2.pyc +0 -0
  713. package/pennyfarthing_scripts/tests/__pycache__/test_tui_panel_persistence.cpython-314-pytest-9.0.2.pyc +0 -0
  714. package/pennyfarthing_scripts/tests/__pycache__/test_validate_cmd.cpython-314-pytest-9.0.2.pyc +0 -0
  715. package/pennyfarthing_scripts/tests/__pycache__/test_version_sentinel.cpython-314-pytest-9.0.2.pyc +0 -0
  716. package/pennyfarthing_scripts/tests/__pycache__/test_workflow_check.cpython-314-pytest-9.0.2.pyc +0 -0
  717. package/pennyfarthing_scripts/tests/__pycache__/test_yaml_io.cpython-314-pytest-9.0.2.pyc +0 -0
  718. package/pennyfarthing_scripts/tests/test_archive_epic.py +185 -0
  719. package/pennyfarthing_scripts/tests/test_bc.py +490 -0
  720. package/pennyfarthing_scripts/tests/test_cli_normalization.py +295 -0
  721. package/pennyfarthing_scripts/tests/test_sprint_panel.py +531 -0
  722. package/pennyfarthing_scripts/tests/test_tui_focus.py +411 -0
  723. package/pennyfarthing_scripts/tests/test_tui_panel_persistence.py +411 -0
  724. package/pennyfarthing_scripts/tests/test_version_sentinel.py +1 -5
  725. package/pennyfarthing_scripts/tests/test_yaml_io.py +5 -5
  726. package/pennyfarthing_scripts/theme/__pycache__/cli.cpython-314.pyc +0 -0
  727. package/pennyfarthing_scripts/theme/cli.py +25 -2
  728. package/scripts/README.md +41 -0
  729. package/packages/core/dist/scripts/benchmark-integration.test.d.ts +0 -13
  730. package/packages/core/dist/scripts/benchmark-integration.test.d.ts.map +0 -1
  731. package/packages/core/dist/scripts/benchmark-integration.test.js +0 -680
  732. package/packages/core/dist/scripts/benchmark-integration.test.js.map +0 -1
  733. package/packages/core/dist/scripts/debugging-scenarios.test.d.ts +0 -18
  734. package/packages/core/dist/scripts/debugging-scenarios.test.d.ts.map +0 -1
  735. package/packages/core/dist/scripts/debugging-scenarios.test.js +0 -317
  736. package/packages/core/dist/scripts/debugging-scenarios.test.js.map +0 -1
  737. package/packages/core/dist/scripts/job-fair-aggregator.test.d.ts +0 -14
  738. package/packages/core/dist/scripts/job-fair-aggregator.test.d.ts.map +0 -1
  739. package/packages/core/dist/scripts/job-fair-aggregator.test.js +0 -616
  740. package/packages/core/dist/scripts/job-fair-aggregator.test.js.map +0 -1
  741. package/packages/core/dist/scripts/theme-detail.test.d.ts +0 -10
  742. package/packages/core/dist/scripts/theme-detail.test.js +0 -199
  743. package/pennyfarthing-dist/personas/themes/star-trek-tng.yaml +0 -392
  744. package/pennyfarthing-dist/skills/dev-patterns/SKILL.md +0 -461
  745. package/pennyfarthing-dist/skills/jira/SKILL.md +0 -207
  746. package/pennyfarthing-dist/skills/sprint/skill.md +0 -806
  747. package/pennyfarthing-dist/skills/story/skill.md +0 -27
  748. package/pennyfarthing-dist/skills/theme/skill.md +0 -356
  749. package/pennyfarthing-dist/skills/workflow/skill.md +0 -368
  750. /package/pennyfarthing-dist/commands/{architect.md → pf-architect.md} +0 -0
  751. /package/pennyfarthing-dist/commands/{ba.md → pf-ba.md} +0 -0
  752. /package/pennyfarthing-dist/commands/{brainstorming.md → pf-brainstorming.md} +0 -0
  753. /package/pennyfarthing-dist/commands/{check.md → pf-check.md} +0 -0
  754. /package/pennyfarthing-dist/commands/{chore.md → pf-chore.md} +0 -0
  755. /package/pennyfarthing-dist/commands/{continue-session.md → pf-continue-session.md} +0 -0
  756. /package/pennyfarthing-dist/commands/{create-branches-from-story.md → pf-create-branches-from-story.md} +0 -0
  757. /package/pennyfarthing-dist/commands/{create-theme.md → pf-create-theme.md} +0 -0
  758. /package/pennyfarthing-dist/commands/{dev.md → pf-dev.md} +0 -0
  759. /package/pennyfarthing-dist/commands/{devops.md → pf-devops.md} +0 -0
  760. /package/pennyfarthing-dist/commands/{fix-blocker.md → pf-fix-blocker.md} +0 -0
  761. /package/pennyfarthing-dist/commands/{git-cleanup.md → pf-git-cleanup.md} +0 -0
  762. /package/pennyfarthing-dist/commands/{health-check.md → pf-health-check.md} +0 -0
  763. /package/pennyfarthing-dist/commands/{help.md → pf-help.md} +0 -0
  764. /package/pennyfarthing-dist/commands/{list-themes.md → pf-list-themes.md} +0 -0
  765. /package/pennyfarthing-dist/commands/{orchestrator.md → pf-orchestrator.md} +0 -0
  766. /package/pennyfarthing-dist/commands/{parallel-work.md → pf-parallel-work.md} +0 -0
  767. /package/pennyfarthing-dist/commands/{permissions.md → pf-permissions.md} +0 -0
  768. /package/pennyfarthing-dist/commands/{pm.md → pf-pm.md} +0 -0
  769. /package/pennyfarthing-dist/commands/{prime.md → pf-prime.md} +0 -0
  770. /package/pennyfarthing-dist/commands/{repo-status.md → pf-repo-status.md} +0 -0
  771. /package/pennyfarthing-dist/commands/{retro.md → pf-retro.md} +0 -0
  772. /package/pennyfarthing-dist/commands/{reviewer.md → pf-reviewer.md} +0 -0
  773. /package/pennyfarthing-dist/commands/{run-ci.md → pf-run-ci.md} +0 -0
  774. /package/pennyfarthing-dist/commands/{set-theme.md → pf-set-theme.md} +0 -0
  775. /package/pennyfarthing-dist/commands/{show-theme.md → pf-show-theme.md} +0 -0
  776. /package/pennyfarthing-dist/commands/{sm.md → pf-sm.md} +0 -0
  777. /package/pennyfarthing-dist/commands/{sprint-planning.md → pf-sprint-planning.md} +0 -0
  778. /package/pennyfarthing-dist/commands/{start-epic.md → pf-start-epic.md} +0 -0
  779. /package/pennyfarthing-dist/commands/{sync-epic-to-jira.md → pf-sync-epic-to-jira.md} +0 -0
  780. /package/pennyfarthing-dist/commands/{sync-work-with-sprint.md → pf-sync-work-with-sprint.md} +0 -0
  781. /package/pennyfarthing-dist/commands/{tea.md → pf-tea.md} +0 -0
  782. /package/pennyfarthing-dist/commands/{tech-writer.md → pf-tech-writer.md} +0 -0
  783. /package/pennyfarthing-dist/commands/{theme-maker.md → pf-theme-maker.md} +0 -0
  784. /package/pennyfarthing-dist/commands/{theme.md → pf-theme.md} +0 -0
  785. /package/pennyfarthing-dist/commands/{update-domain-docs.md → pf-update-domain-docs.md} +0 -0
  786. /package/pennyfarthing-dist/commands/{ux-designer.md → pf-ux-designer.md} +0 -0
  787. /package/pennyfarthing-dist/commands/{work.md → pf-work.md} +0 -0
  788. /package/pennyfarthing-dist/skills/{agentic-patterns → pf-agentic-patterns}/SKILL.md +0 -0
  789. /package/pennyfarthing-dist/skills/{changelog → pf-changelog}/SKILL.md +0 -0
  790. /package/pennyfarthing-dist/skills/{code-review → pf-code-review}/SKILL.md +0 -0
  791. /package/pennyfarthing-dist/skills/{context-engineering → pf-context-engineering}/SKILL.md +0 -0
  792. /package/pennyfarthing-dist/skills/{mermaid → pf-mermaid}/SKILL.md +0 -0
  793. /package/pennyfarthing-dist/skills/{otel → pf-otel}/skill.md +0 -0
  794. /package/pennyfarthing-dist/skills/{testing → pf-testing}/SKILL.md +0 -0
  795. /package/pennyfarthing-dist/skills/{testing → pf-testing}/references/troubleshooting.md +0 -0
  796. /package/pennyfarthing-dist/skills/{theme-creation → pf-theme-creation}/SKILL.md +0 -0
  797. /package/pennyfarthing-dist/skills/{yq → pf-yq}/SKILL.md +0 -0
@@ -0,0 +1,386 @@
1
+ /**
2
+ * Tests for Story 98-12: Git hook chaining with .d/ dispatcher pattern
3
+ *
4
+ * These tests verify that git hooks use a .d/ dispatcher pattern allowing
5
+ * multiple hook scripts to coexist without conflicts.
6
+ *
7
+ * Acceptance Criteria:
8
+ * AC1: .d/ dispatcher architecture — hooks installed via dispatcher + .d/ directory
9
+ * AC2: Hooks can be extended and composed without conflicts
10
+ * AC3: Support pre-commit, pre-push, and post-merge hooks
11
+ * AC4: Integrate with the framework's install/upgrade flow
12
+ * AC5: Maintain backward compatibility with existing hooks
13
+ *
14
+ * Run with: cd packages/core && pnpm build && pnpm test
15
+ */
16
+ import { describe, it, beforeEach, afterEach } from 'node:test';
17
+ import assert from 'node:assert';
18
+ import { existsSync, mkdirSync, writeFileSync, rmSync, readFileSync, readdirSync, statSync, } from 'fs';
19
+ import { join } from 'path';
20
+ import { tmpdir } from 'os';
21
+ import { execSync } from 'child_process';
22
+ import { installGitHooks } from './init.js';
23
+ // Marker string used by pennyfarthing to identify its own hooks
24
+ const PF_MARKER = 'pennyfarthing';
25
+ // Dispatcher marker to identify the dispatcher script
26
+ const DISPATCHER_MARKER = 'pennyfarthing-dispatcher';
27
+ describe('Story 98-12: Git hook chaining with .d/ dispatcher pattern', () => {
28
+ let testDir;
29
+ let gitDir;
30
+ let gitHooksDir;
31
+ let nodeModulesPath;
32
+ let hooksSourceDir;
33
+ beforeEach(() => {
34
+ testDir = join(tmpdir(), `pf-hook-chaining-${Date.now()}-${Math.random().toString(36).slice(2)}`);
35
+ gitDir = join(testDir, '.git');
36
+ gitHooksDir = join(gitDir, 'hooks');
37
+ nodeModulesPath = join(testDir, 'node_modules/@pennyfarthing/core/pennyfarthing-dist');
38
+ hooksSourceDir = join(nodeModulesPath, 'scripts/hooks');
39
+ // Create directory structure
40
+ mkdirSync(gitHooksDir, { recursive: true });
41
+ mkdirSync(hooksSourceDir, { recursive: true });
42
+ // Create dispatcher template (shared source of truth)
43
+ writeFileSync(join(hooksSourceDir, 'dispatcher-template.sh'), '#!/bin/bash\n' +
44
+ '# pennyfarthing-dispatcher: Git hook dispatcher for __HOOK_NAME__\n' +
45
+ '# Runs all executable scripts in __HOOK_NAME__.d/ in sorted order.\n' +
46
+ '# Installed by pennyfarthing — do not edit manually.\n' +
47
+ '\n' +
48
+ 'set -uo pipefail\n' +
49
+ '\n' +
50
+ 'HOOK_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"\n' +
51
+ 'HOOK_NAME="__HOOK_NAME__"\n' +
52
+ 'D_DIR="${HOOK_DIR}/${HOOK_NAME}.d"\n' +
53
+ '\n' +
54
+ '# If .d/ directory doesn\'t exist or is empty, exit successfully\n' +
55
+ 'if [[ ! -d "${D_DIR}" ]]; then\n' +
56
+ ' exit 0\n' +
57
+ 'fi\n' +
58
+ '\n' +
59
+ '# Capture stdin for hooks that receive input (e.g., pre-push)\n' +
60
+ 'STDIN_DATA=""\n' +
61
+ 'if [[ ! -t 0 ]]; then\n' +
62
+ ' STDIN_DATA="$(cat /dev/stdin)"\n' +
63
+ 'fi\n' +
64
+ '\n' +
65
+ '# Run each executable script in sorted order\n' +
66
+ 'for hook_script in $(ls "${D_DIR}/" 2>/dev/null | sort); do\n' +
67
+ ' script_path="${D_DIR}/${hook_script}"\n' +
68
+ '\n' +
69
+ ' # Skip non-executable files\n' +
70
+ ' if [[ ! -x "${script_path}" ]]; then\n' +
71
+ ' continue\n' +
72
+ ' fi\n' +
73
+ '\n' +
74
+ ' # Run the hook, forwarding arguments and stdin\n' +
75
+ ' if [[ -n "${STDIN_DATA}" ]]; then\n' +
76
+ ' echo "${STDIN_DATA}" | "${script_path}" "$@"\n' +
77
+ ' else\n' +
78
+ ' "${script_path}" "$@"\n' +
79
+ ' fi\n' +
80
+ '\n' +
81
+ ' exit_code=$?\n' +
82
+ ' if [[ ${exit_code} -ne 0 ]]; then\n' +
83
+ ' exit ${exit_code}\n' +
84
+ ' fi\n' +
85
+ 'done\n' +
86
+ '\n' +
87
+ 'exit 0\n');
88
+ // Create source hook files (mimicking pennyfarthing-dist/scripts/hooks/)
89
+ writeFileSync(join(hooksSourceDir, 'pre-commit.sh'), '#!/bin/bash\n# pennyfarthing pre-commit hook\necho "pre-commit check"\nexit 0\n');
90
+ writeFileSync(join(hooksSourceDir, 'pre-push.sh'), '#!/bin/bash\n# pennyfarthing pre-push hook\necho "pre-push check"\nexit 0\n');
91
+ writeFileSync(join(hooksSourceDir, 'post-merge.sh'), '#!/bin/bash\n# pennyfarthing post-merge hook\necho "post-merge check"\nexit 0\n');
92
+ });
93
+ afterEach(() => {
94
+ if (existsSync(testDir)) {
95
+ rmSync(testDir, { recursive: true, force: true });
96
+ }
97
+ });
98
+ // ─── AC1: .d/ dispatcher architecture ──────────────────────────────
99
+ describe('AC1: Dispatcher architecture with .d/ directories', () => {
100
+ it('should create .d/ directory for each hook type', async () => {
101
+ await installGitHooks(testDir, nodeModulesPath, { dryRun: false });
102
+ assert.ok(existsSync(join(gitHooksDir, 'pre-commit.d')), 'pre-commit.d/ directory should be created');
103
+ assert.ok(existsSync(join(gitHooksDir, 'pre-push.d')), 'pre-push.d/ directory should be created');
104
+ assert.ok(existsSync(join(gitHooksDir, 'post-merge.d')), 'post-merge.d/ directory should be created');
105
+ });
106
+ it('should install dispatcher script at .git/hooks/{hook}', async () => {
107
+ await installGitHooks(testDir, nodeModulesPath, { dryRun: false });
108
+ const dispatcherPath = join(gitHooksDir, 'pre-commit');
109
+ assert.ok(existsSync(dispatcherPath), 'Dispatcher should exist at .git/hooks/pre-commit');
110
+ const content = readFileSync(dispatcherPath, 'utf8');
111
+ assert.ok(content.includes(DISPATCHER_MARKER), `Dispatcher should contain '${DISPATCHER_MARKER}' marker, got:\n${content.slice(0, 200)}`);
112
+ });
113
+ it('should install pennyfarthing hook into .d/ directory', async () => {
114
+ await installGitHooks(testDir, nodeModulesPath, { dryRun: false });
115
+ const dDir = join(gitHooksDir, 'pre-commit.d');
116
+ assert.ok(existsSync(dDir), 'pre-commit.d/ should exist');
117
+ const files = readdirSync(dDir);
118
+ const pfHook = files.find((f) => f.includes('pennyfarthing'));
119
+ assert.ok(pfHook, `Should have a pennyfarthing hook in pre-commit.d/, found: [${files.join(', ')}]`);
120
+ const hookContent = readFileSync(join(dDir, pfHook), 'utf8');
121
+ assert.ok(hookContent.includes(PF_MARKER), 'Hook in .d/ should contain pennyfarthing marker');
122
+ });
123
+ it('dispatcher should iterate and run all scripts in .d/ directory', async () => {
124
+ await installGitHooks(testDir, nodeModulesPath, { dryRun: false });
125
+ const dispatcherContent = readFileSync(join(gitHooksDir, 'pre-commit'), 'utf8');
126
+ // Dispatcher must reference the .d/ directory
127
+ assert.ok(dispatcherContent.includes('pre-commit.d') || dispatcherContent.includes('.d/') || dispatcherContent.includes('${HOOK_NAME}.d'), 'Dispatcher should reference the .d/ directory for script iteration');
128
+ // Dispatcher should have loop logic to run scripts
129
+ assert.ok(dispatcherContent.includes('for ') || dispatcherContent.includes('find '), 'Dispatcher should iterate over scripts in .d/ directory');
130
+ });
131
+ it('dispatcher should be executable', async () => {
132
+ await installGitHooks(testDir, nodeModulesPath, { dryRun: false });
133
+ const dispatcherPath = join(gitHooksDir, 'pre-commit');
134
+ const stat = statSync(dispatcherPath);
135
+ const isExecutable = (stat.mode & 0o111) !== 0;
136
+ assert.ok(isExecutable, 'Dispatcher script should be executable');
137
+ });
138
+ });
139
+ // ─── AC2: Hooks can be extended and composed without conflicts ─────
140
+ describe('AC2: Extension and composition without conflicts', () => {
141
+ it('should preserve user hooks already in .d/ directory', async () => {
142
+ // Pre-create a user hook in .d/
143
+ const dDir = join(gitHooksDir, 'pre-commit.d');
144
+ mkdirSync(dDir, { recursive: true });
145
+ const userHookContent = '#!/bin/bash\necho "user hook"\nexit 0\n';
146
+ writeFileSync(join(dDir, '99-user-lint.sh'), userHookContent);
147
+ await installGitHooks(testDir, nodeModulesPath, { dryRun: false });
148
+ // User hook should still be there
149
+ assert.ok(existsSync(join(dDir, '99-user-lint.sh')), 'User hook in .d/ should be preserved');
150
+ assert.strictEqual(readFileSync(join(dDir, '99-user-lint.sh'), 'utf8'), userHookContent, 'User hook content should be unchanged');
151
+ });
152
+ it('should not duplicate pennyfarthing hook on re-install', async () => {
153
+ // Install twice
154
+ await installGitHooks(testDir, nodeModulesPath, { dryRun: false });
155
+ await installGitHooks(testDir, nodeModulesPath, { dryRun: false });
156
+ const dDir = join(gitHooksDir, 'pre-commit.d');
157
+ const files = readdirSync(dDir);
158
+ const pfHooks = files.filter((f) => f.includes('pennyfarthing'));
159
+ assert.strictEqual(pfHooks.length, 1, `Should have exactly 1 pennyfarthing hook after re-install, found ${pfHooks.length}: [${pfHooks.join(', ')}]`);
160
+ });
161
+ it('should update stale pennyfarthing hook in .d/ directory', async () => {
162
+ // First install
163
+ await installGitHooks(testDir, nodeModulesPath, { dryRun: false });
164
+ // Update the source hook
165
+ const updatedContent = '#!/bin/bash\n# pennyfarthing pre-commit hook v2\necho "updated"\nexit 0\n';
166
+ writeFileSync(join(hooksSourceDir, 'pre-commit.sh'), updatedContent);
167
+ // Re-install
168
+ await installGitHooks(testDir, nodeModulesPath, { dryRun: false });
169
+ const dDir = join(gitHooksDir, 'pre-commit.d');
170
+ const files = readdirSync(dDir);
171
+ const pfHook = files.find((f) => f.includes('pennyfarthing'));
172
+ const installedContent = readFileSync(join(dDir, pfHook), 'utf8');
173
+ assert.strictEqual(installedContent, updatedContent, 'Pennyfarthing hook in .d/ should be updated with new content');
174
+ });
175
+ it('should allow multiple tools to add hooks to .d/', async () => {
176
+ await installGitHooks(testDir, nodeModulesPath, { dryRun: false });
177
+ // Simulate another tool adding a hook
178
+ const dDir = join(gitHooksDir, 'pre-commit.d');
179
+ writeFileSync(join(dDir, '50-husky-lint-staged.sh'), '#!/bin/bash\nnpx lint-staged\n');
180
+ // Re-install pennyfarthing — should not disturb other tool's hook
181
+ await installGitHooks(testDir, nodeModulesPath, { dryRun: false });
182
+ assert.ok(existsSync(join(dDir, '50-husky-lint-staged.sh')), 'Third-party tool hook should survive pennyfarthing re-install');
183
+ const files = readdirSync(dDir);
184
+ assert.ok(files.length >= 2, `Should have at least 2 hooks (pennyfarthing + husky), found: [${files.join(', ')}]`);
185
+ });
186
+ });
187
+ // ─── AC3: Support all three hook types ─────────────────────────────
188
+ describe('AC3: Support pre-commit, pre-push, and post-merge hooks', () => {
189
+ it('should install dispatcher + .d/ for pre-commit', async () => {
190
+ await installGitHooks(testDir, nodeModulesPath, { dryRun: false });
191
+ assert.ok(existsSync(join(gitHooksDir, 'pre-commit')), 'pre-commit dispatcher exists');
192
+ assert.ok(existsSync(join(gitHooksDir, 'pre-commit.d')), 'pre-commit.d/ exists');
193
+ const files = readdirSync(join(gitHooksDir, 'pre-commit.d'));
194
+ assert.ok(files.length > 0, 'pre-commit.d/ should have at least one hook');
195
+ });
196
+ it('should install dispatcher + .d/ for pre-push', async () => {
197
+ await installGitHooks(testDir, nodeModulesPath, { dryRun: false });
198
+ assert.ok(existsSync(join(gitHooksDir, 'pre-push')), 'pre-push dispatcher exists');
199
+ assert.ok(existsSync(join(gitHooksDir, 'pre-push.d')), 'pre-push.d/ exists');
200
+ const files = readdirSync(join(gitHooksDir, 'pre-push.d'));
201
+ assert.ok(files.length > 0, 'pre-push.d/ should have at least one hook');
202
+ });
203
+ it('should install dispatcher + .d/ for post-merge', async () => {
204
+ await installGitHooks(testDir, nodeModulesPath, { dryRun: false });
205
+ assert.ok(existsSync(join(gitHooksDir, 'post-merge')), 'post-merge dispatcher exists');
206
+ assert.ok(existsSync(join(gitHooksDir, 'post-merge.d')), 'post-merge.d/ exists');
207
+ const files = readdirSync(join(gitHooksDir, 'post-merge.d'));
208
+ assert.ok(files.length > 0, 'post-merge.d/ should have at least one hook');
209
+ });
210
+ it('should pass arguments through to hooks in .d/', async () => {
211
+ await installGitHooks(testDir, nodeModulesPath, { dryRun: false });
212
+ const dispatcherContent = readFileSync(join(gitHooksDir, 'pre-push'), 'utf8');
213
+ // pre-push receives remote name and URL as args — dispatcher must forward them
214
+ assert.ok(dispatcherContent.includes('"$@"') || dispatcherContent.includes('$@'), 'Dispatcher should forward arguments to hook scripts');
215
+ });
216
+ it('should pass stdin through to hooks in .d/ (for pre-push)', async () => {
217
+ await installGitHooks(testDir, nodeModulesPath, { dryRun: false });
218
+ const dispatcherContent = readFileSync(join(gitHooksDir, 'pre-push'), 'utf8');
219
+ // pre-push receives ref info on stdin — dispatcher should handle stdin
220
+ assert.ok(dispatcherContent.includes('stdin') ||
221
+ dispatcherContent.includes('tee') ||
222
+ dispatcherContent.includes('/dev/stdin') ||
223
+ dispatcherContent.includes('STDIN'), 'Dispatcher should handle stdin forwarding for hooks like pre-push');
224
+ });
225
+ });
226
+ // ─── AC4: Integration with install/upgrade flow ────────────────────
227
+ describe('AC4: Integration with install/upgrade flow', () => {
228
+ it('should create dispatcher from a template or generate it', async () => {
229
+ await installGitHooks(testDir, nodeModulesPath, { dryRun: false });
230
+ // Dispatcher should exist and be a proper bash script
231
+ const dispatcherPath = join(gitHooksDir, 'pre-commit');
232
+ const content = readFileSync(dispatcherPath, 'utf8');
233
+ assert.ok(content.startsWith('#!/bin/bash') || content.startsWith('#!/usr/bin/env bash'), 'Dispatcher should start with bash shebang');
234
+ });
235
+ it('dry-run should not create .d/ directories or dispatcher', async () => {
236
+ await installGitHooks(testDir, nodeModulesPath, { dryRun: true });
237
+ assert.ok(!existsSync(join(gitHooksDir, 'pre-commit.d')), '.d/ directory should not be created in dry-run mode');
238
+ // If a dispatcher was placed, it should not exist in dry-run
239
+ // (unless it was pre-existing — but our test starts clean)
240
+ if (existsSync(join(gitHooksDir, 'pre-commit'))) {
241
+ const content = readFileSync(join(gitHooksDir, 'pre-commit'), 'utf8');
242
+ assert.ok(!content.includes(DISPATCHER_MARKER), 'Dispatcher should not be installed in dry-run mode');
243
+ }
244
+ });
245
+ it('should handle missing source hooks gracefully', async () => {
246
+ // Remove one source hook
247
+ rmSync(join(hooksSourceDir, 'pre-push.sh'));
248
+ // Should not throw
249
+ await assert.doesNotReject(async () => installGitHooks(testDir, nodeModulesPath, { dryRun: false }), 'Should handle missing source hooks without throwing');
250
+ // The other hooks should still be installed
251
+ assert.ok(existsSync(join(gitHooksDir, 'pre-commit.d')), 'pre-commit.d/ should still be created for available hooks');
252
+ });
253
+ it('should handle non-git repos gracefully', async () => {
254
+ // Remove .git directory
255
+ rmSync(gitDir, { recursive: true, force: true });
256
+ // Should not throw
257
+ await assert.doesNotReject(async () => installGitHooks(testDir, nodeModulesPath, { dryRun: false }), 'Should handle non-git repos without throwing');
258
+ });
259
+ });
260
+ // ─── AC5: Backward compatibility ───────────────────────────────────
261
+ describe('AC5: Backward compatibility with existing hooks', () => {
262
+ it('should migrate existing single-file pennyfarthing hook into .d/', async () => {
263
+ // Pre-existing pennyfarthing hook at old location (single file)
264
+ const oldHookContent = '#!/bin/bash\n# pennyfarthing pre-commit hook\necho "old hook"\nexit 0\n';
265
+ writeFileSync(join(gitHooksDir, 'pre-commit'), oldHookContent, { mode: 0o755 });
266
+ await installGitHooks(testDir, nodeModulesPath, { dryRun: false });
267
+ // Old single-file hook should be replaced by dispatcher
268
+ const dispatcherContent = readFileSync(join(gitHooksDir, 'pre-commit'), 'utf8');
269
+ assert.ok(dispatcherContent.includes(DISPATCHER_MARKER), 'Old pennyfarthing hook should be replaced by dispatcher');
270
+ // Pennyfarthing hook content should be in .d/ directory (updated from source)
271
+ const dDir = join(gitHooksDir, 'pre-commit.d');
272
+ assert.ok(existsSync(dDir), 'pre-commit.d/ should be created during migration');
273
+ const files = readdirSync(dDir);
274
+ const pfHook = files.find((f) => f.includes('pennyfarthing'));
275
+ assert.ok(pfHook, 'Pennyfarthing hook should be in .d/ directory');
276
+ });
277
+ it('should migrate existing non-pennyfarthing hook into .d/', async () => {
278
+ // Pre-existing user/third-party hook
279
+ const userHookContent = '#!/bin/bash\n# my custom linter\neslint .\nexit 0\n';
280
+ writeFileSync(join(gitHooksDir, 'pre-commit'), userHookContent, { mode: 0o755 });
281
+ await installGitHooks(testDir, nodeModulesPath, { dryRun: false });
282
+ // Dispatcher should be installed
283
+ const dispatcherContent = readFileSync(join(gitHooksDir, 'pre-commit'), 'utf8');
284
+ assert.ok(dispatcherContent.includes(DISPATCHER_MARKER), 'Dispatcher should replace the user hook');
285
+ // User hook should be moved into .d/ directory
286
+ const dDir = join(gitHooksDir, 'pre-commit.d');
287
+ const files = readdirSync(dDir);
288
+ // Should have both: user's original hook + pennyfarthing hook
289
+ assert.ok(files.length >= 2, `Should have at least 2 hooks in .d/ (user + pennyfarthing), found: [${files.join(', ')}]`);
290
+ // Find the migrated user hook
291
+ const userHook = files.find((f) => !f.includes('pennyfarthing') && !f.startsWith('.'));
292
+ assert.ok(userHook, `User hook should be migrated to .d/, found: [${files.join(', ')}]`);
293
+ const migratedContent = readFileSync(join(dDir, userHook), 'utf8');
294
+ assert.strictEqual(migratedContent, userHookContent, 'Migrated user hook content should be preserved');
295
+ });
296
+ it('should not migrate an existing dispatcher (idempotent upgrade)', async () => {
297
+ // First install — creates dispatcher
298
+ await installGitHooks(testDir, nodeModulesPath, { dryRun: false });
299
+ const firstDispatcher = readFileSync(join(gitHooksDir, 'pre-commit'), 'utf8');
300
+ // Second install — should recognize dispatcher and not re-migrate
301
+ await installGitHooks(testDir, nodeModulesPath, { dryRun: false });
302
+ const secondDispatcher = readFileSync(join(gitHooksDir, 'pre-commit'), 'utf8');
303
+ assert.strictEqual(firstDispatcher, secondDispatcher, 'Dispatcher should be stable across re-installs');
304
+ // No duplicates in .d/
305
+ const files = readdirSync(join(gitHooksDir, 'pre-commit.d'));
306
+ const pfHooks = files.filter((f) => f.includes('pennyfarthing'));
307
+ assert.strictEqual(pfHooks.length, 1, 'Should not duplicate pennyfarthing hook on upgrade');
308
+ });
309
+ it('should handle existing .backup files gracefully', async () => {
310
+ // Simulate a previous installation that left .backup files
311
+ writeFileSync(join(gitHooksDir, 'pre-commit.backup'), '#!/bin/bash\necho "old backup"\n');
312
+ // Current non-pennyfarthing hook
313
+ writeFileSync(join(gitHooksDir, 'pre-commit'), '#!/bin/bash\necho "current user hook"\nexit 0\n', { mode: 0o755 });
314
+ // Should not throw and should handle gracefully
315
+ await assert.doesNotReject(async () => installGitHooks(testDir, nodeModulesPath, { dryRun: false }), 'Should handle existing .backup files gracefully');
316
+ // Dispatcher should be installed
317
+ assert.ok(existsSync(join(gitHooksDir, 'pre-commit')), 'Dispatcher should be installed');
318
+ });
319
+ });
320
+ // ─── Dispatcher execution behavior ─────────────────────────────────
321
+ describe('Dispatcher execution behavior', () => {
322
+ it('dispatcher should propagate failure exit codes', async () => {
323
+ await installGitHooks(testDir, nodeModulesPath, { dryRun: false });
324
+ const dispatcherContent = readFileSync(join(gitHooksDir, 'pre-commit'), 'utf8');
325
+ // Dispatcher should check exit codes and fail fast
326
+ assert.ok(dispatcherContent.includes('exit') &&
327
+ (dispatcherContent.includes('$?') || dispatcherContent.includes('||')), 'Dispatcher should propagate non-zero exit codes from hook scripts');
328
+ });
329
+ it('dispatcher should skip non-executable files in .d/', async () => {
330
+ await installGitHooks(testDir, nodeModulesPath, { dryRun: false });
331
+ const dispatcherContent = readFileSync(join(gitHooksDir, 'pre-commit'), 'utf8');
332
+ // Dispatcher should check for executable permission
333
+ assert.ok(dispatcherContent.includes('-x') || dispatcherContent.includes('executable'), 'Dispatcher should only run executable files in .d/ directory');
334
+ });
335
+ it('dispatcher should run hooks in sorted order', async () => {
336
+ await installGitHooks(testDir, nodeModulesPath, { dryRun: false });
337
+ const dispatcherContent = readFileSync(join(gitHooksDir, 'pre-commit'), 'utf8');
338
+ // Dispatcher should sort scripts (ls or sort)
339
+ assert.ok(dispatcherContent.includes('sort') || dispatcherContent.includes('ls'), 'Dispatcher should run hooks in sorted order for deterministic execution');
340
+ });
341
+ it('dispatcher should handle empty .d/ directory gracefully', async () => {
342
+ await installGitHooks(testDir, nodeModulesPath, { dryRun: false });
343
+ // Remove all hooks from .d/ to simulate empty directory
344
+ const dDir = join(gitHooksDir, 'pre-commit.d');
345
+ for (const f of readdirSync(dDir)) {
346
+ rmSync(join(dDir, f));
347
+ }
348
+ // Running the dispatcher should not fail
349
+ const dispatcherPath = join(gitHooksDir, 'pre-commit');
350
+ try {
351
+ execSync(`bash "${dispatcherPath}"`, {
352
+ cwd: testDir,
353
+ timeout: 5000,
354
+ encoding: 'utf8',
355
+ });
356
+ }
357
+ catch (err) {
358
+ const execErr = err;
359
+ assert.fail(`Dispatcher should succeed with empty .d/ directory, but got exit code ${execErr.status}: ${execErr.stderr}`);
360
+ }
361
+ });
362
+ });
363
+ // ─── Pennyfarthing hook naming convention ──────────────────────────
364
+ describe('Hook naming conventions in .d/', () => {
365
+ it('pennyfarthing hook should use numeric prefix for ordering', async () => {
366
+ await installGitHooks(testDir, nodeModulesPath, { dryRun: false });
367
+ const dDir = join(gitHooksDir, 'pre-commit.d');
368
+ const files = readdirSync(dDir);
369
+ const pfHook = files.find((f) => f.includes('pennyfarthing'));
370
+ assert.ok(pfHook, 'Pennyfarthing hook should exist');
371
+ // Should have numeric prefix like "10-pennyfarthing-pre-commit.sh"
372
+ assert.match(pfHook, /^\d+-/, `Pennyfarthing hook should have numeric prefix for ordering, got: ${pfHook}`);
373
+ });
374
+ it('migrated user hooks should have numeric prefix', async () => {
375
+ // Pre-existing user hook
376
+ writeFileSync(join(gitHooksDir, 'pre-commit'), '#!/bin/bash\necho "user hook"\nexit 0\n', { mode: 0o755 });
377
+ await installGitHooks(testDir, nodeModulesPath, { dryRun: false });
378
+ const dDir = join(gitHooksDir, 'pre-commit.d');
379
+ const files = readdirSync(dDir);
380
+ const userHook = files.find((f) => !f.includes('pennyfarthing') && !f.startsWith('.'));
381
+ assert.ok(userHook, 'Migrated user hook should exist');
382
+ assert.match(userHook, /^\d+-/, `Migrated user hook should have numeric prefix, got: ${userHook}`);
383
+ });
384
+ });
385
+ });
386
+ //# sourceMappingURL=hook-chaining.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hook-chaining.test.js","sourceRoot":"","sources":["../../../src/cli/commands/hook-chaining.test.ts"],"names":[],"mappings":"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"}
@@ -5,7 +5,12 @@ interface InitOptions {
5
5
  }
6
6
  export declare function initCommand(projectName: string | undefined, options: InitOptions): Promise<void>;
7
7
  /**
8
- * Install git hooks from pennyfarthing-dist to .git/hooks
8
+ * Install git hooks using .d/ dispatcher pattern.
9
+ * Creates a dispatcher script at .git/hooks/{hook} that runs all
10
+ * executable scripts in .git/hooks/{hook}.d/ in sorted order.
11
+ * Pennyfarthing hooks are placed in the .d/ directory with numeric prefixes.
12
+ * Existing hooks are migrated into .d/ to preserve user customizations.
13
+ *
9
14
  * Installs: pre-commit, pre-push, post-merge
10
15
  */
11
16
  export declare function installGitHooks(projectRoot: string, nodeModulesPath: string, options: {
@@ -1 +1 @@
1
- {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/init.ts"],"names":[],"mappings":"AAgCA,UAAU,WAAW;IACnB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,wBAAsB,WAAW,CAC/B,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,IAAI,CAAC,CA8Nf;AAED;;;GAGG;AACH,wBAAsB,eAAe,CACnC,WAAW,EAAE,MAAM,EACnB,eAAe,EAAE,MAAM,EACvB,OAAO,EAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,GAC5B,OAAO,CAAC,IAAI,CAAC,CAmEf"}
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/init.ts"],"names":[],"mappings":"AAgCA,UAAU,WAAW;IACnB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,wBAAsB,WAAW,CAC/B,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,IAAI,CAAC,CA8Nf;AAUD;;;;;;;;GAQG;AACH,wBAAsB,eAAe,CACnC,WAAW,EAAE,MAAM,EACnB,eAAe,EAAE,MAAM,EACvB,OAAO,EAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,GAC5B,OAAO,CAAC,IAAI,CAAC,CAyGf"}
@@ -1,4 +1,4 @@
1
- import { readFileSync, writeFileSync } from 'fs';
1
+ import { readFileSync, writeFileSync, mkdirSync } from 'fs';
2
2
  import { join, relative, basename } from 'path';
3
3
  import fsExtra from 'fs-extra';
4
4
  const { ensureDirSync, removeSync } = fsExtra;
@@ -7,7 +7,7 @@ import { prompts } from '../utils/prompts.js';
7
7
  import { manifestExists, readManifest, writeManifest, createManifest } from '../utils/manifest.js';
8
8
  import { pathExists, isDirectory, ensureDir } from '../utils/files.js';
9
9
  import { getPackageVersion, getAssetsPath } from '../utils/version.js';
10
- import { createDirectorySymlink, copyCommandsDirectory, copySkillsDirectory, removeSymlinkOrDirectory } from '../utils/symlinks.js';
10
+ import { createDirectorySymlink, createCommandsDirectory, createSkillsDirectory, removeSymlinkOrDirectory } from '../utils/symlinks.js';
11
11
  import { findNodeModulesPath } from '../utils/node-modules.js';
12
12
  import { CORE_AGENTS, DIRECTORY_SYMLINKS } from '../utils/constants.js';
13
13
  import { mergeSettingsLocalJson, ensureSettingsSymlink } from '../utils/settings.js';
@@ -125,14 +125,14 @@ export async function initCommand(projectName, options) {
125
125
  logger.warning(`Could not symlink ${name} to ${link}`);
126
126
  }
127
127
  }
128
- // Copy commands directory (allows user commands alongside built-in)
128
+ // Link commands directory (allows user commands alongside built-in)
129
129
  const builtInCommandsPath = join(nodeModulesPath, 'commands');
130
130
  const projectCommandsPath = join(projectRoot, '.pennyfarthing/project/commands');
131
- copyCommandsDirectory(projectRoot, builtInCommandsPath, projectCommandsPath, dryRun || false);
132
- // Copy skills directory (allows user skills alongside built-in)
131
+ createCommandsDirectory(projectRoot, builtInCommandsPath, projectCommandsPath, dryRun || false);
132
+ // Link skills directory (allows user skills alongside built-in)
133
133
  const builtInSkillsPath = join(nodeModulesPath, 'skills');
134
134
  const projectSkillsPath = join(projectRoot, '.pennyfarthing/project/skills');
135
- copySkillsDirectory(projectRoot, builtInSkillsPath, projectSkillsPath, dryRun || false);
135
+ createSkillsDirectory(projectRoot, builtInSkillsPath, projectSkillsPath, dryRun || false);
136
136
  // 8. Create agent sidecars if not exist
137
137
  logger.newline();
138
138
  logger.info('Creating agent sidecars...');
@@ -202,7 +202,19 @@ export async function initCommand(projectName, options) {
202
202
  logger.info(' - Run `pennyfarthing doctor` to verify installation');
203
203
  }
204
204
  /**
205
- * Install git hooks from pennyfarthing-dist to .git/hooks
205
+ * Generate a dispatcher script for a given hook name by substituting
206
+ * __HOOK_NAME__ in the shared template.
207
+ */
208
+ function generateDispatcher(hookName, template) {
209
+ return template.replace(/__HOOK_NAME__/g, hookName);
210
+ }
211
+ /**
212
+ * Install git hooks using .d/ dispatcher pattern.
213
+ * Creates a dispatcher script at .git/hooks/{hook} that runs all
214
+ * executable scripts in .git/hooks/{hook}.d/ in sorted order.
215
+ * Pennyfarthing hooks are placed in the .d/ directory with numeric prefixes.
216
+ * Existing hooks are migrated into .d/ to preserve user customizations.
217
+ *
206
218
  * Installs: pre-commit, pre-push, post-merge
207
219
  */
208
220
  export async function installGitHooks(projectRoot, nodeModulesPath, options) {
@@ -218,48 +230,89 @@ export async function installGitHooks(projectRoot, nodeModulesPath, options) {
218
230
  if (!pathExists(gitHooksDir) && !options.dryRun) {
219
231
  ensureDir(gitHooksDir, { dryRun: options.dryRun });
220
232
  }
233
+ const DISPATCHER_MARKER = 'pennyfarthing-dispatcher';
234
+ const PF_MARKER = 'pennyfarthing';
235
+ const PF_PREFIX = '10';
236
+ const MIGRATED_PREFIX = '50';
237
+ // Load shared dispatcher template
238
+ const dispatcherTemplatePath = join(nodeModulesPath, 'scripts/hooks/dispatcher-template.sh');
239
+ const dispatcherTemplate = readFileSync(dispatcherTemplatePath, 'utf8');
221
240
  // Define hooks to install
222
241
  const hooks = [
223
- { source: 'pre-commit.sh', dest: 'pre-commit', marker: 'pennyfarthing' },
224
- { source: 'pre-push.sh', dest: 'pre-push', marker: 'pennyfarthing' },
225
- { source: 'post-merge.sh', dest: 'post-merge', marker: 'pennyfarthing' },
242
+ { source: 'pre-commit.sh', dest: 'pre-commit' },
243
+ { source: 'pre-push.sh', dest: 'pre-push' },
244
+ { source: 'post-merge.sh', dest: 'post-merge' },
226
245
  ];
227
246
  for (const hook of hooks) {
228
247
  const sourcePath = join(nodeModulesPath, 'scripts/hooks', hook.source);
229
248
  const destPath = join(gitHooksDir, hook.dest);
249
+ const dDir = join(gitHooksDir, `${hook.dest}.d`);
250
+ const pfHookName = `${PF_PREFIX}-pennyfarthing-${hook.dest}.sh`;
251
+ const pfHookPath = join(dDir, pfHookName);
230
252
  if (!pathExists(sourcePath)) {
231
253
  logger.warning(`${hook.source} not found, skipping`);
232
254
  continue;
233
255
  }
234
256
  const sourceContent = readFileSync(sourcePath, 'utf8');
235
- // Check if hook already exists
257
+ if (options.dryRun) {
258
+ logger.info(`Would install .git/hooks/${hook.dest} dispatcher + .d/`);
259
+ continue;
260
+ }
261
+ // Create .d/ directory
262
+ if (!pathExists(dDir)) {
263
+ mkdirSync(dDir, { recursive: true });
264
+ }
265
+ // Migrate existing hook if present
236
266
  if (pathExists(destPath)) {
237
267
  const existingContent = readFileSync(destPath, 'utf8');
238
- if (existingContent.includes(hook.marker)) {
239
- // Our hookcheck if content matches (refresh stale copies)
240
- if (existingContent === sourceContent) {
241
- logger.skipped(`.git/hooks/${hook.dest}`, 'already installed');
242
- continue;
268
+ if (existingContent.includes(DISPATCHER_MARKER)) {
269
+ // Already a dispatcher update it if content changed
270
+ const newDispatcher = generateDispatcher(hook.dest, dispatcherTemplate);
271
+ if (existingContent !== newDispatcher) {
272
+ writeFileSync(destPath, newDispatcher, { mode: 0o755 });
273
+ logger.updated(`.git/hooks/${hook.dest} dispatcher`);
243
274
  }
244
- // Content differs — update in place
245
- if (!options.dryRun) {
246
- writeFileSync(destPath, sourceContent, { mode: 0o755 });
275
+ else {
276
+ logger.skipped(`.git/hooks/${hook.dest} dispatcher`, 'already installed');
247
277
  }
248
- logger.updated(`.git/hooks/${hook.dest}`);
249
- continue;
250
278
  }
251
- // Existing non-pennyfarthing hook - backup and replace
252
- if (!options.dryRun) {
253
- const backupPath = `${destPath}.backup`;
254
- writeFileSync(backupPath, existingContent, 'utf8');
255
- logger.info(`Backed up existing hook to ${backupPath}`);
279
+ else if (existingContent.includes(PF_MARKER)) {
280
+ // Old-style single-file pennyfarthing hook — replace with dispatcher
281
+ writeFileSync(destPath, generateDispatcher(hook.dest, dispatcherTemplate), { mode: 0o755 });
282
+ logger.updated(`.git/hooks/${hook.dest} dispatcher`);
256
283
  }
284
+ else {
285
+ // Non-pennyfarthing hook — migrate into .d/ then install dispatcher
286
+ const migratedName = `${MIGRATED_PREFIX}-migrated-${hook.dest}.sh`;
287
+ const migratedPath = join(dDir, migratedName);
288
+ if (!pathExists(migratedPath)) {
289
+ writeFileSync(migratedPath, existingContent, { mode: 0o755 });
290
+ logger.info(`Migrated existing ${hook.dest} hook to ${hook.dest}.d/${migratedName}`);
291
+ }
292
+ writeFileSync(destPath, generateDispatcher(hook.dest, dispatcherTemplate), { mode: 0o755 });
293
+ logger.created(`.git/hooks/${hook.dest} dispatcher`);
294
+ }
295
+ }
296
+ else {
297
+ // No existing hook — install fresh dispatcher
298
+ writeFileSync(destPath, generateDispatcher(hook.dest, dispatcherTemplate), { mode: 0o755 });
299
+ logger.created(`.git/hooks/${hook.dest} dispatcher`);
257
300
  }
258
- // Copy the hook
259
- if (!options.dryRun) {
260
- writeFileSync(destPath, sourceContent, { mode: 0o755 });
301
+ // Install/update pennyfarthing hook in .d/
302
+ if (pathExists(pfHookPath)) {
303
+ const existingPf = readFileSync(pfHookPath, 'utf8');
304
+ if (existingPf === sourceContent) {
305
+ logger.skipped(`.git/hooks/${hook.dest}.d/${pfHookName}`, 'already installed');
306
+ }
307
+ else {
308
+ writeFileSync(pfHookPath, sourceContent, { mode: 0o755 });
309
+ logger.updated(`.git/hooks/${hook.dest}.d/${pfHookName}`);
310
+ }
311
+ }
312
+ else {
313
+ writeFileSync(pfHookPath, sourceContent, { mode: 0o755 });
314
+ logger.created(`.git/hooks/${hook.dest}.d/${pfHookName}`);
261
315
  }
262
- logger.created(`.git/hooks/${hook.dest}`);
263
316
  }
264
317
  }
265
318
  /**