@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,781 @@
1
+ /**
2
+ * Tests for Story 98-11: Settings.local.json shared merge model for multi-framework coexistence
3
+ *
4
+ * Acceptance Criteria:
5
+ * AC1: Namespace/section approach — each framework contributes settings under its own key
6
+ * AC2: Merge conflict resolution — hooks concatenated, permissions unioned, scalars priority-based
7
+ * AC3: Migration from legacy v1 format to v2 shared format
8
+ * AC4: Rollback — removing a framework cleanly extracts its contributions
9
+ * AC5: Backward compatibility — v2 exports flat format compatible with Claude Code
10
+ * AC6: Validation and error handling
11
+ *
12
+ * Run with: cd packages/core && npm run build && npm test
13
+ */
14
+ import { describe, it, beforeEach } from 'node:test';
15
+ import assert from 'node:assert';
16
+ import { createEmptySharedSettings, contributeFrameworkSettings, removeFrameworkSettings, mergeHooks, mergePermissions, resolveScalar, detectConflicts, migrateToSharedFormat, isSharedFormat, toFlatFormat, validateSharedSettings, SHARED_SETTINGS_VERSION, } from './settings-merge.js';
17
+ // =============================================================================
18
+ // Test Fixtures
19
+ // =============================================================================
20
+ function makePennyfarthingContribution() {
21
+ return {
22
+ version: '10.4.0',
23
+ hooks: {
24
+ SessionStart: [
25
+ {
26
+ hooks: [
27
+ { type: 'command', command: '.pennyfarthing/scripts/hooks/session-start.sh' },
28
+ ],
29
+ },
30
+ ],
31
+ PostToolUse: [
32
+ {
33
+ matcher: '',
34
+ hooks: [
35
+ { type: 'command', command: '.pennyfarthing/scripts/hooks/bell-mode-hook.sh' },
36
+ ],
37
+ },
38
+ ],
39
+ },
40
+ permissions: {
41
+ allow: ['Read', 'Grep', 'Glob', 'Bash', 'Skill(sm)', 'Skill(tea)'],
42
+ },
43
+ statusLine: {
44
+ type: 'command',
45
+ command: '.pennyfarthing/scripts/misc/statusline.sh',
46
+ },
47
+ context_budget: {
48
+ warning_threshold: 70,
49
+ critical_threshold: 85,
50
+ max_tokens: 200000,
51
+ },
52
+ };
53
+ }
54
+ function makeOtherFrameworkContribution() {
55
+ return {
56
+ version: '2.1.0',
57
+ hooks: {
58
+ SessionStart: [
59
+ {
60
+ hooks: [
61
+ { type: 'command', command: '.other-framework/hooks/init.sh' },
62
+ ],
63
+ },
64
+ ],
65
+ PreToolUse: [
66
+ {
67
+ matcher: 'Bash',
68
+ hooks: [
69
+ { type: 'command', command: '.other-framework/hooks/pre-bash.sh' },
70
+ ],
71
+ },
72
+ ],
73
+ },
74
+ permissions: {
75
+ allow: ['Read', 'Bash', 'Skill(custom-skill)'],
76
+ },
77
+ statusLine: {
78
+ type: 'command',
79
+ command: '.other-framework/statusline.sh',
80
+ },
81
+ };
82
+ }
83
+ function makeMinimalContribution() {
84
+ return {
85
+ version: '1.0.0',
86
+ hooks: {
87
+ Stop: [
88
+ {
89
+ matcher: '',
90
+ hooks: [{ type: 'command', command: '.minimal/hooks/stop.sh' }],
91
+ },
92
+ ],
93
+ },
94
+ };
95
+ }
96
+ // =============================================================================
97
+ // AC1: Namespace/Section Approach
98
+ // =============================================================================
99
+ describe('AC1: Namespace/section approach', () => {
100
+ let settings;
101
+ beforeEach(() => {
102
+ settings = createEmptySharedSettings();
103
+ });
104
+ it('should create empty shared settings with correct version', () => {
105
+ assert.strictEqual(settings._version, SHARED_SETTINGS_VERSION);
106
+ assert.deepStrictEqual(settings._frameworks, {});
107
+ assert.deepStrictEqual(settings._contributions, {});
108
+ assert.deepStrictEqual(settings.hooks, {});
109
+ assert.deepStrictEqual(settings.permissions, { allow: [] });
110
+ });
111
+ it('should store framework contribution under its namespace key', () => {
112
+ const contribution = makePennyfarthingContribution();
113
+ const result = contributeFrameworkSettings(settings, 'pennyfarthing', contribution);
114
+ assert.ok(result.settings._contributions['pennyfarthing'], 'Contribution should be stored under pennyfarthing key');
115
+ assert.strictEqual(result.settings._contributions['pennyfarthing'].version, '10.4.0', 'Should store the framework version');
116
+ });
117
+ it('should register framework metadata on contribution', () => {
118
+ const contribution = makePennyfarthingContribution();
119
+ const result = contributeFrameworkSettings(settings, 'pennyfarthing', contribution);
120
+ assert.ok(result.settings._frameworks['pennyfarthing'], 'Framework metadata should exist');
121
+ assert.strictEqual(result.settings._frameworks['pennyfarthing'].version, '10.4.0');
122
+ assert.ok(result.settings._frameworks['pennyfarthing'].installed_at, 'Should have installed_at timestamp');
123
+ });
124
+ it('should store multiple frameworks with separate namespace keys', () => {
125
+ const pf = makePennyfarthingContribution();
126
+ const other = makeOtherFrameworkContribution();
127
+ let result = contributeFrameworkSettings(settings, 'pennyfarthing', pf);
128
+ result = contributeFrameworkSettings(result.settings, 'other-framework', other);
129
+ assert.ok(result.settings._contributions['pennyfarthing']);
130
+ assert.ok(result.settings._contributions['other-framework']);
131
+ assert.strictEqual(Object.keys(result.settings._contributions).length, 2, 'Should have exactly 2 framework contributions');
132
+ });
133
+ it('should update existing framework contribution without creating duplicate', () => {
134
+ const v1 = makePennyfarthingContribution();
135
+ const v2 = { ...makePennyfarthingContribution(), version: '10.5.0' };
136
+ let result = contributeFrameworkSettings(settings, 'pennyfarthing', v1);
137
+ result = contributeFrameworkSettings(result.settings, 'pennyfarthing', v2);
138
+ assert.strictEqual(Object.keys(result.settings._contributions).length, 1, 'Should still have exactly 1 contribution');
139
+ assert.strictEqual(result.settings._contributions['pennyfarthing'].version, '10.5.0', 'Should have updated version');
140
+ });
141
+ it('should preserve framework contribution data integrity', () => {
142
+ const contribution = makePennyfarthingContribution();
143
+ const result = contributeFrameworkSettings(settings, 'pennyfarthing', contribution);
144
+ const stored = result.settings._contributions['pennyfarthing'];
145
+ assert.deepStrictEqual(stored.hooks, contribution.hooks, 'Stored hooks should match contribution');
146
+ assert.deepStrictEqual(stored.permissions, contribution.permissions, 'Stored permissions should match contribution');
147
+ });
148
+ });
149
+ // =============================================================================
150
+ // AC2: Merge Conflict Resolution — Hooks
151
+ // =============================================================================
152
+ describe('AC2a: Hook merging', () => {
153
+ it('should merge hooks from single framework', () => {
154
+ const contributions = {
155
+ pennyfarthing: makePennyfarthingContribution(),
156
+ };
157
+ const merged = mergeHooks(contributions);
158
+ assert.ok(merged.SessionStart, 'Should have SessionStart hooks');
159
+ assert.ok(merged.PostToolUse, 'Should have PostToolUse hooks');
160
+ assert.strictEqual(merged.SessionStart.length, 1, 'Should have 1 SessionStart entry');
161
+ });
162
+ it('should concatenate hooks from multiple frameworks per hook type', () => {
163
+ const contributions = {
164
+ pennyfarthing: makePennyfarthingContribution(),
165
+ 'other-framework': makeOtherFrameworkContribution(),
166
+ };
167
+ const merged = mergeHooks(contributions);
168
+ // SessionStart: both frameworks contribute
169
+ assert.ok(merged.SessionStart, 'Should have SessionStart');
170
+ assert.strictEqual(merged.SessionStart.length, 2, 'SessionStart should have entries from both frameworks');
171
+ // PostToolUse: only pennyfarthing
172
+ assert.ok(merged.PostToolUse, 'Should have PostToolUse');
173
+ assert.strictEqual(merged.PostToolUse.length, 1, 'PostToolUse from pennyfarthing only');
174
+ // PreToolUse: only other-framework
175
+ assert.ok(merged.PreToolUse, 'Should have PreToolUse');
176
+ assert.strictEqual(merged.PreToolUse.length, 1, 'PreToolUse from other-framework only');
177
+ });
178
+ it('should deduplicate identical hooks within same framework', () => {
179
+ const contribution = {
180
+ version: '1.0.0',
181
+ hooks: {
182
+ SessionStart: [
183
+ { hooks: [{ type: 'command', command: 'same-hook.sh' }] },
184
+ { hooks: [{ type: 'command', command: 'same-hook.sh' }] },
185
+ ],
186
+ },
187
+ };
188
+ const merged = mergeHooks({ test: contribution });
189
+ assert.strictEqual(merged.SessionStart.length, 1, 'Duplicate hooks within same framework should be deduplicated');
190
+ });
191
+ it('should preserve hook ordering — framework order then hook order', () => {
192
+ const contributions = {
193
+ alpha: {
194
+ version: '1.0.0',
195
+ hooks: {
196
+ SessionStart: [
197
+ { hooks: [{ type: 'command', command: 'alpha-first.sh' }] },
198
+ { hooks: [{ type: 'command', command: 'alpha-second.sh' }] },
199
+ ],
200
+ },
201
+ },
202
+ beta: {
203
+ version: '1.0.0',
204
+ hooks: {
205
+ SessionStart: [
206
+ { hooks: [{ type: 'command', command: 'beta-first.sh' }] },
207
+ ],
208
+ },
209
+ },
210
+ };
211
+ const merged = mergeHooks(contributions);
212
+ const commands = merged.SessionStart.map(e => e.hooks[0].command);
213
+ assert.strictEqual(commands[0], 'alpha-first.sh');
214
+ assert.strictEqual(commands[1], 'alpha-second.sh');
215
+ assert.strictEqual(commands[2], 'beta-first.sh');
216
+ });
217
+ it('should handle framework with no hooks', () => {
218
+ const contributions = {
219
+ empty: { version: '1.0.0' },
220
+ withHooks: makePennyfarthingContribution(),
221
+ };
222
+ const merged = mergeHooks(contributions);
223
+ assert.ok(merged.SessionStart, 'Should still have hooks from the other framework');
224
+ });
225
+ it('should return empty object when no contributions have hooks', () => {
226
+ const contributions = {
227
+ a: { version: '1.0.0' },
228
+ b: { version: '2.0.0' },
229
+ };
230
+ const merged = mergeHooks(contributions);
231
+ assert.deepStrictEqual(merged, {}, 'Should return empty hooks');
232
+ });
233
+ });
234
+ // =============================================================================
235
+ // AC2: Merge Conflict Resolution — Permissions
236
+ // =============================================================================
237
+ describe('AC2b: Permission merging', () => {
238
+ it('should union permissions from multiple frameworks', () => {
239
+ const contributions = {
240
+ pennyfarthing: makePennyfarthingContribution(),
241
+ 'other-framework': makeOtherFrameworkContribution(),
242
+ };
243
+ const merged = mergePermissions(contributions);
244
+ // Pennyfarthing: Read, Grep, Glob, Bash, Skill(sm), Skill(tea)
245
+ // Other: Read, Bash, Skill(custom-skill)
246
+ // Union: Read, Grep, Glob, Bash, Skill(sm), Skill(tea), Skill(custom-skill)
247
+ assert.ok(merged.allow.includes('Read'), 'Should include Read');
248
+ assert.ok(merged.allow.includes('Grep'), 'Should include Grep (pennyfarthing only)');
249
+ assert.ok(merged.allow.includes('Glob'), 'Should include Glob (pennyfarthing only)');
250
+ assert.ok(merged.allow.includes('Bash'), 'Should include Bash');
251
+ assert.ok(merged.allow.includes('Skill(sm)'), 'Should include Skill(sm)');
252
+ assert.ok(merged.allow.includes('Skill(custom-skill)'), 'Should include Skill(custom-skill)');
253
+ });
254
+ it('should deduplicate permissions present in multiple frameworks', () => {
255
+ const contributions = {
256
+ pennyfarthing: makePennyfarthingContribution(),
257
+ 'other-framework': makeOtherFrameworkContribution(),
258
+ };
259
+ const merged = mergePermissions(contributions);
260
+ // Read and Bash appear in both — should only appear once
261
+ const readCount = merged.allow.filter(p => p === 'Read').length;
262
+ const bashCount = merged.allow.filter(p => p === 'Bash').length;
263
+ assert.strictEqual(readCount, 1, 'Read should appear exactly once');
264
+ assert.strictEqual(bashCount, 1, 'Bash should appear exactly once');
265
+ });
266
+ it('should handle framework with no permissions', () => {
267
+ const contributions = {
268
+ minimal: makeMinimalContribution(),
269
+ pennyfarthing: makePennyfarthingContribution(),
270
+ };
271
+ const merged = mergePermissions(contributions);
272
+ assert.ok(merged.allow.length > 0, 'Should have permissions from pennyfarthing');
273
+ assert.ok(merged.allow.includes('Read'));
274
+ });
275
+ it('should return empty allow list when no frameworks contribute permissions', () => {
276
+ const contributions = {
277
+ a: { version: '1.0.0' },
278
+ };
279
+ const merged = mergePermissions(contributions);
280
+ assert.deepStrictEqual(merged, { allow: [] });
281
+ });
282
+ it('should maintain stable ordering — alphabetical by permission string', () => {
283
+ const contributions = {
284
+ z: {
285
+ version: '1.0.0',
286
+ permissions: { allow: ['Zebra', 'Apple'] },
287
+ },
288
+ a: {
289
+ version: '1.0.0',
290
+ permissions: { allow: ['Mango', 'Banana'] },
291
+ },
292
+ };
293
+ const merged = mergePermissions(contributions);
294
+ const sorted = [...merged.allow].sort();
295
+ assert.deepStrictEqual(merged.allow, sorted, 'Permissions should be alphabetically sorted');
296
+ });
297
+ });
298
+ // =============================================================================
299
+ // AC2: Merge Conflict Resolution — Scalar Values
300
+ // =============================================================================
301
+ describe('AC2c: Scalar resolution', () => {
302
+ it('should use statusLine from highest-priority framework', () => {
303
+ const contributions = {
304
+ pennyfarthing: makePennyfarthingContribution(),
305
+ 'other-framework': makeOtherFrameworkContribution(),
306
+ };
307
+ const frameworks = {
308
+ pennyfarthing: { version: '10.4.0', installed_at: '2026-01-01', priority: 1 },
309
+ 'other-framework': { version: '2.1.0', installed_at: '2026-02-01', priority: 2 },
310
+ };
311
+ const result = resolveScalar(contributions, frameworks, 'statusLine');
312
+ assert.ok(result, 'Should resolve a statusLine');
313
+ assert.ok(result.command.includes('.pennyfarthing'), 'Should use pennyfarthing statusLine (priority 1)');
314
+ });
315
+ it('should use context_budget from highest-priority framework', () => {
316
+ const contributions = {
317
+ pennyfarthing: makePennyfarthingContribution(),
318
+ 'other-framework': {
319
+ ...makeOtherFrameworkContribution(),
320
+ context_budget: { warning_threshold: 60, critical_threshold: 80, max_tokens: 150000 },
321
+ },
322
+ };
323
+ const frameworks = {
324
+ pennyfarthing: { version: '10.4.0', installed_at: '2026-01-01', priority: 1 },
325
+ 'other-framework': { version: '2.1.0', installed_at: '2026-02-01', priority: 2 },
326
+ };
327
+ const result = resolveScalar(contributions, frameworks, 'context_budget');
328
+ assert.ok(result, 'Should resolve context_budget');
329
+ assert.strictEqual(result.warning_threshold, 70, 'Should use pennyfarthing value (priority 1)');
330
+ });
331
+ it('should fall back to alphabetical order when priorities are equal', () => {
332
+ const contributions = {
333
+ beta: {
334
+ version: '1.0.0',
335
+ statusLine: { type: 'command', command: 'beta-status.sh' },
336
+ },
337
+ alpha: {
338
+ version: '1.0.0',
339
+ statusLine: { type: 'command', command: 'alpha-status.sh' },
340
+ },
341
+ };
342
+ const frameworks = {
343
+ alpha: { version: '1.0.0', installed_at: '2026-01-01' },
344
+ beta: { version: '1.0.0', installed_at: '2026-01-01' },
345
+ };
346
+ const result = resolveScalar(contributions, frameworks, 'statusLine');
347
+ assert.ok(result);
348
+ assert.ok(result.command.includes('alpha'), 'Should use alpha (alphabetically first) when priorities equal');
349
+ });
350
+ it('should return undefined when no framework contributes the scalar', () => {
351
+ const contributions = {
352
+ minimal: makeMinimalContribution(),
353
+ };
354
+ const frameworks = {
355
+ minimal: { version: '1.0.0', installed_at: '2026-01-01' },
356
+ };
357
+ const result = resolveScalar(contributions, frameworks, 'statusLine');
358
+ assert.strictEqual(result, undefined, 'Should return undefined when nobody contributes');
359
+ });
360
+ });
361
+ // =============================================================================
362
+ // AC2: Conflict Detection
363
+ // =============================================================================
364
+ describe('AC2d: Conflict detection', () => {
365
+ it('should detect duplicate hook commands across frameworks', () => {
366
+ const contributions = {
367
+ alpha: {
368
+ version: '1.0.0',
369
+ hooks: {
370
+ SessionStart: [
371
+ { hooks: [{ type: 'command', command: 'shared-hook.sh' }] },
372
+ ],
373
+ },
374
+ },
375
+ beta: {
376
+ version: '1.0.0',
377
+ hooks: {
378
+ SessionStart: [
379
+ { hooks: [{ type: 'command', command: 'shared-hook.sh' }] },
380
+ ],
381
+ },
382
+ },
383
+ };
384
+ const conflicts = detectConflicts(contributions);
385
+ assert.ok(conflicts.length > 0, 'Should detect at least one conflict');
386
+ const hookConflict = conflicts.find(c => c.type === 'hook_duplicate');
387
+ assert.ok(hookConflict, 'Should have a hook_duplicate conflict');
388
+ assert.ok(hookConflict.frameworks.includes('alpha') && hookConflict.frameworks.includes('beta'), 'Conflict should reference both frameworks');
389
+ });
390
+ it('should detect statusLine collisions between frameworks', () => {
391
+ const contributions = {
392
+ alpha: {
393
+ version: '1.0.0',
394
+ statusLine: { type: 'command', command: 'alpha-status.sh' },
395
+ },
396
+ beta: {
397
+ version: '1.0.0',
398
+ statusLine: { type: 'command', command: 'beta-status.sh' },
399
+ },
400
+ };
401
+ const conflicts = detectConflicts(contributions);
402
+ const scalarConflict = conflicts.find(c => c.type === 'scalar_collision');
403
+ assert.ok(scalarConflict, 'Should detect scalar collision for statusLine');
404
+ assert.strictEqual(scalarConflict.key, 'statusLine');
405
+ });
406
+ it('should NOT report conflict when only one framework contributes a scalar', () => {
407
+ const contributions = {
408
+ alpha: {
409
+ version: '1.0.0',
410
+ statusLine: { type: 'command', command: 'alpha-status.sh' },
411
+ },
412
+ beta: { version: '1.0.0' },
413
+ };
414
+ const conflicts = detectConflicts(contributions);
415
+ const scalarConflicts = conflicts.filter(c => c.type === 'scalar_collision');
416
+ assert.strictEqual(scalarConflicts.length, 0, 'No collision when only one contributes');
417
+ });
418
+ it('should NOT report conflict for different hook commands in same hook type', () => {
419
+ const contributions = {
420
+ alpha: {
421
+ version: '1.0.0',
422
+ hooks: {
423
+ SessionStart: [
424
+ { hooks: [{ type: 'command', command: 'alpha-hook.sh' }] },
425
+ ],
426
+ },
427
+ },
428
+ beta: {
429
+ version: '1.0.0',
430
+ hooks: {
431
+ SessionStart: [
432
+ { hooks: [{ type: 'command', command: 'beta-hook.sh' }] },
433
+ ],
434
+ },
435
+ },
436
+ };
437
+ const conflicts = detectConflicts(contributions);
438
+ const hookConflicts = conflicts.filter(c => c.type === 'hook_duplicate');
439
+ assert.strictEqual(hookConflicts.length, 0, 'Different commands in same hook type is normal, not a conflict');
440
+ });
441
+ it('should return empty array when there are no conflicts', () => {
442
+ const contributions = {
443
+ alpha: makePennyfarthingContribution(),
444
+ };
445
+ const conflicts = detectConflicts(contributions);
446
+ assert.deepStrictEqual(conflicts, [], 'Single framework should have no conflicts');
447
+ });
448
+ });
449
+ // =============================================================================
450
+ // AC3: Migration from Legacy v1 to Shared v2 Format
451
+ // =============================================================================
452
+ describe('AC3: Migration from legacy format', () => {
453
+ it('should migrate legacy settings into v2 format under framework namespace', () => {
454
+ const legacy = {
455
+ hooks: {
456
+ SessionStart: [
457
+ { hooks: [{ type: 'command', command: 'session-start.sh' }] },
458
+ ],
459
+ },
460
+ permissions: { allow: ['Read', 'Bash'] },
461
+ statusLine: { type: 'command', command: 'statusline.sh' },
462
+ context_budget: { warning_threshold: 70, critical_threshold: 85, max_tokens: 200000 },
463
+ };
464
+ const migrated = migrateToSharedFormat(legacy, 'pennyfarthing', '10.4.0');
465
+ assert.strictEqual(migrated._version, SHARED_SETTINGS_VERSION, 'Should be v2');
466
+ assert.ok(migrated._frameworks['pennyfarthing'], 'Should have framework metadata');
467
+ assert.ok(migrated._contributions['pennyfarthing'], 'Should have contribution');
468
+ const contribution = migrated._contributions['pennyfarthing'];
469
+ assert.deepStrictEqual(contribution.hooks, legacy.hooks, 'Hooks should be preserved in contribution');
470
+ assert.deepStrictEqual(contribution.permissions, legacy.permissions, 'Permissions should be preserved');
471
+ });
472
+ it('should populate merged output fields after migration', () => {
473
+ const legacy = {
474
+ hooks: {
475
+ SessionStart: [
476
+ { hooks: [{ type: 'command', command: 'session-start.sh' }] },
477
+ ],
478
+ },
479
+ permissions: { allow: ['Read', 'Bash'] },
480
+ };
481
+ const migrated = migrateToSharedFormat(legacy, 'pennyfarthing', '10.4.0');
482
+ // Merged output should reflect the single framework's contributions
483
+ assert.ok(migrated.hooks.SessionStart, 'Merged hooks should exist');
484
+ assert.ok(migrated.permissions.allow.includes('Read'), 'Merged permissions should include Read');
485
+ });
486
+ it('should handle legacy settings with no hooks', () => {
487
+ const legacy = {
488
+ permissions: { allow: ['Read'] },
489
+ };
490
+ const migrated = migrateToSharedFormat(legacy, 'test-framework', '1.0.0');
491
+ assert.ok(migrated._contributions['test-framework']);
492
+ assert.deepStrictEqual(migrated.hooks, {}, 'Hooks should be empty when legacy has none');
493
+ });
494
+ it('should handle legacy settings with no permissions', () => {
495
+ const legacy = {
496
+ hooks: {
497
+ Stop: [{ hooks: [{ type: 'command', command: 'stop.sh' }] }],
498
+ },
499
+ };
500
+ const migrated = migrateToSharedFormat(legacy, 'test-framework', '1.0.0');
501
+ assert.deepStrictEqual(migrated.permissions, { allow: [] }, 'Permissions should default to empty');
502
+ });
503
+ it('should handle completely empty legacy settings', () => {
504
+ const migrated = migrateToSharedFormat({}, 'test-framework', '1.0.0');
505
+ assert.strictEqual(migrated._version, SHARED_SETTINGS_VERSION);
506
+ assert.ok(migrated._frameworks['test-framework']);
507
+ assert.deepStrictEqual(migrated.hooks, {});
508
+ assert.deepStrictEqual(migrated.permissions, { allow: [] });
509
+ });
510
+ it('should handle unknown top-level keys gracefully without crashing', () => {
511
+ const legacy = {
512
+ hooks: {},
513
+ permissions: { allow: [] },
514
+ customExtension: { someData: true },
515
+ };
516
+ // Unknown keys are intentionally dropped — only known fields are migrated
517
+ const migrated = migrateToSharedFormat(legacy, 'test-framework', '1.0.0');
518
+ assert.ok(migrated._frameworks['test-framework'], 'Framework should be registered');
519
+ assert.strictEqual(migrated._version, SHARED_SETTINGS_VERSION);
520
+ });
521
+ });
522
+ // =============================================================================
523
+ // AC4: Framework Removal (Rollback)
524
+ // =============================================================================
525
+ describe('AC4: Framework removal (rollback)', () => {
526
+ it('should remove a framework contribution cleanly', () => {
527
+ const settings = createEmptySharedSettings();
528
+ const pf = makePennyfarthingContribution();
529
+ const other = makeOtherFrameworkContribution();
530
+ let result = contributeFrameworkSettings(settings, 'pennyfarthing', pf);
531
+ result = contributeFrameworkSettings(result.settings, 'other-framework', other);
532
+ const afterRemoval = removeFrameworkSettings(result.settings, 'other-framework');
533
+ assert.ok(!afterRemoval._contributions['other-framework'], 'Removed framework contribution should not exist');
534
+ assert.ok(!afterRemoval._frameworks['other-framework'], 'Removed framework metadata should not exist');
535
+ assert.ok(afterRemoval._contributions['pennyfarthing'], 'Remaining framework should still exist');
536
+ });
537
+ it('should re-merge hooks after framework removal', () => {
538
+ const settings = createEmptySharedSettings();
539
+ const pf = makePennyfarthingContribution();
540
+ const other = makeOtherFrameworkContribution();
541
+ let result = contributeFrameworkSettings(settings, 'pennyfarthing', pf);
542
+ result = contributeFrameworkSettings(result.settings, 'other-framework', other);
543
+ const afterRemoval = removeFrameworkSettings(result.settings, 'other-framework');
544
+ // PreToolUse was only contributed by other-framework — should be gone
545
+ assert.ok(!afterRemoval.hooks.PreToolUse || afterRemoval.hooks.PreToolUse.length === 0, 'PreToolUse hooks should be removed with other-framework');
546
+ // SessionStart should only have pennyfarthing's entry now
547
+ assert.strictEqual(afterRemoval.hooks.SessionStart?.length, 1, 'SessionStart should only have pennyfarthing entry');
548
+ });
549
+ it('should re-merge permissions after framework removal', () => {
550
+ const settings = createEmptySharedSettings();
551
+ const pf = makePennyfarthingContribution();
552
+ const other = makeOtherFrameworkContribution();
553
+ let result = contributeFrameworkSettings(settings, 'pennyfarthing', pf);
554
+ result = contributeFrameworkSettings(result.settings, 'other-framework', other);
555
+ const afterRemoval = removeFrameworkSettings(result.settings, 'other-framework');
556
+ assert.ok(!afterRemoval.permissions.allow.includes('Skill(custom-skill)'), 'Permission from removed framework should be gone');
557
+ assert.ok(afterRemoval.permissions.allow.includes('Skill(sm)'), 'Permission from remaining framework should stay');
558
+ });
559
+ it('should handle removing the only framework', () => {
560
+ const settings = createEmptySharedSettings();
561
+ const pf = makePennyfarthingContribution();
562
+ const result = contributeFrameworkSettings(settings, 'pennyfarthing', pf);
563
+ const afterRemoval = removeFrameworkSettings(result.settings, 'pennyfarthing');
564
+ assert.deepStrictEqual(afterRemoval._contributions, {});
565
+ assert.deepStrictEqual(afterRemoval._frameworks, {});
566
+ assert.deepStrictEqual(afterRemoval.hooks, {});
567
+ assert.deepStrictEqual(afterRemoval.permissions, { allow: [] });
568
+ });
569
+ it('should be no-op when removing non-existent framework', () => {
570
+ const settings = createEmptySharedSettings();
571
+ const pf = makePennyfarthingContribution();
572
+ const result = contributeFrameworkSettings(settings, 'pennyfarthing', pf);
573
+ const afterRemoval = removeFrameworkSettings(result.settings, 'nonexistent');
574
+ assert.deepStrictEqual(afterRemoval._contributions, result.settings._contributions, 'Should not change anything');
575
+ });
576
+ });
577
+ // =============================================================================
578
+ // AC5: Backward Compatibility — Flat Format Export
579
+ // =============================================================================
580
+ describe('AC5: Backward compatibility (flat format export)', () => {
581
+ it('should export merged settings in Claude Code-compatible flat format', () => {
582
+ const settings = createEmptySharedSettings();
583
+ const pf = makePennyfarthingContribution();
584
+ const result = contributeFrameworkSettings(settings, 'pennyfarthing', pf);
585
+ const flat = toFlatFormat(result.settings);
586
+ // Should NOT contain internal fields
587
+ assert.strictEqual(flat._version, undefined, 'Should not contain _version');
588
+ assert.strictEqual(flat._frameworks, undefined, 'Should not contain _frameworks');
589
+ assert.strictEqual(flat._contributions, undefined, 'Should not contain _contributions');
590
+ // Should contain merged results
591
+ assert.ok(flat.hooks, 'Should contain hooks');
592
+ assert.ok(flat.permissions, 'Should contain permissions');
593
+ });
594
+ it('should produce valid hooks structure in flat format', () => {
595
+ const settings = createEmptySharedSettings();
596
+ const pf = makePennyfarthingContribution();
597
+ const result = contributeFrameworkSettings(settings, 'pennyfarthing', pf);
598
+ const flat = toFlatFormat(result.settings);
599
+ const hooks = flat.hooks;
600
+ assert.ok(Array.isArray(hooks.SessionStart), 'SessionStart should be an array');
601
+ assert.ok(Array.isArray(hooks.PostToolUse), 'PostToolUse should be an array');
602
+ });
603
+ it('should produce valid permissions structure in flat format', () => {
604
+ const settings = createEmptySharedSettings();
605
+ const pf = makePennyfarthingContribution();
606
+ const result = contributeFrameworkSettings(settings, 'pennyfarthing', pf);
607
+ const flat = toFlatFormat(result.settings);
608
+ const permissions = flat.permissions;
609
+ assert.ok(Array.isArray(permissions.allow), 'allow should be an array');
610
+ assert.ok(permissions.allow.includes('Read'));
611
+ });
612
+ it('should include statusLine in flat format when present', () => {
613
+ const settings = createEmptySharedSettings();
614
+ const pf = makePennyfarthingContribution();
615
+ const result = contributeFrameworkSettings(settings, 'pennyfarthing', pf);
616
+ const flat = toFlatFormat(result.settings);
617
+ assert.ok(flat.statusLine, 'Should include statusLine');
618
+ const sl = flat.statusLine;
619
+ assert.strictEqual(sl.type, 'command');
620
+ assert.ok(sl.command.includes('statusline.sh'));
621
+ });
622
+ it('should include context_budget in flat format when present', () => {
623
+ const settings = createEmptySharedSettings();
624
+ const pf = makePennyfarthingContribution();
625
+ const result = contributeFrameworkSettings(settings, 'pennyfarthing', pf);
626
+ const flat = toFlatFormat(result.settings);
627
+ assert.ok(flat.context_budget, 'Should include context_budget');
628
+ const cb = flat.context_budget;
629
+ assert.strictEqual(cb.warning_threshold, 70);
630
+ });
631
+ it('should produce identical JSON to what Claude Code expects', () => {
632
+ const settings = createEmptySharedSettings();
633
+ const pf = makePennyfarthingContribution();
634
+ const result = contributeFrameworkSettings(settings, 'pennyfarthing', pf);
635
+ const flat = toFlatFormat(result.settings);
636
+ // Verify it round-trips through JSON without issues
637
+ const json = JSON.stringify(flat, null, 2);
638
+ const parsed = JSON.parse(json);
639
+ assert.deepStrictEqual(parsed, flat, 'Should round-trip through JSON');
640
+ });
641
+ });
642
+ // =============================================================================
643
+ // AC6: Validation
644
+ // =============================================================================
645
+ describe('AC6: Validation', () => {
646
+ it('should validate a correct SharedSettings structure', () => {
647
+ const settings = createEmptySharedSettings();
648
+ const pf = makePennyfarthingContribution();
649
+ const result = contributeFrameworkSettings(settings, 'pennyfarthing', pf);
650
+ const validation = validateSharedSettings(result.settings);
651
+ assert.ok(validation.valid, `Should be valid, errors: ${validation.errors.join(', ')}`);
652
+ assert.deepStrictEqual(validation.errors, []);
653
+ });
654
+ it('should reject null input', () => {
655
+ const validation = validateSharedSettings(null);
656
+ assert.strictEqual(validation.valid, false);
657
+ assert.ok(validation.errors.length > 0);
658
+ });
659
+ it('should reject non-object input', () => {
660
+ const validation = validateSharedSettings('not an object');
661
+ assert.strictEqual(validation.valid, false);
662
+ });
663
+ it('should reject missing _version field', () => {
664
+ const validation = validateSharedSettings({
665
+ _frameworks: {},
666
+ _contributions: {},
667
+ hooks: {},
668
+ permissions: { allow: [] },
669
+ });
670
+ assert.strictEqual(validation.valid, false);
671
+ assert.ok(validation.errors.some(e => e.includes('version')), 'Should mention version in error');
672
+ });
673
+ it('should reject wrong _version value', () => {
674
+ const validation = validateSharedSettings({
675
+ _version: 999,
676
+ _frameworks: {},
677
+ _contributions: {},
678
+ hooks: {},
679
+ permissions: { allow: [] },
680
+ });
681
+ assert.strictEqual(validation.valid, false);
682
+ });
683
+ it('should reject missing _frameworks field', () => {
684
+ const validation = validateSharedSettings({
685
+ _version: SHARED_SETTINGS_VERSION,
686
+ _contributions: {},
687
+ hooks: {},
688
+ permissions: { allow: [] },
689
+ });
690
+ assert.strictEqual(validation.valid, false);
691
+ });
692
+ it('should validate empty shared settings as valid', () => {
693
+ const settings = createEmptySharedSettings();
694
+ const validation = validateSharedSettings(settings);
695
+ assert.ok(validation.valid, 'Empty shared settings should be valid');
696
+ });
697
+ });
698
+ // =============================================================================
699
+ // Format Detection
700
+ // =============================================================================
701
+ describe('Format detection', () => {
702
+ it('should identify v2 shared format', () => {
703
+ const settings = createEmptySharedSettings();
704
+ assert.ok(isSharedFormat(settings), 'Should detect shared format');
705
+ });
706
+ it('should reject legacy v1 format', () => {
707
+ const legacy = {
708
+ hooks: {},
709
+ permissions: { allow: [] },
710
+ };
711
+ assert.ok(!isSharedFormat(legacy), 'Should reject legacy format');
712
+ });
713
+ it('should reject null', () => {
714
+ assert.ok(!isSharedFormat(null));
715
+ });
716
+ it('should reject wrong version number', () => {
717
+ assert.ok(!isSharedFormat({ _version: 1 }));
718
+ assert.ok(!isSharedFormat({ _version: 3 }));
719
+ });
720
+ });
721
+ // =============================================================================
722
+ // Integration: Full Multi-Framework Lifecycle
723
+ // =============================================================================
724
+ describe('Integration: multi-framework lifecycle', () => {
725
+ it('should handle full lifecycle: create → contribute → contribute → remove → export', () => {
726
+ // 1. Create empty
727
+ const empty = createEmptySharedSettings();
728
+ assert.strictEqual(Object.keys(empty._contributions).length, 0);
729
+ // 2. First framework contributes
730
+ const r1 = contributeFrameworkSettings(empty, 'pennyfarthing', makePennyfarthingContribution());
731
+ assert.strictEqual(Object.keys(r1.settings._contributions).length, 1);
732
+ assert.ok(r1.settings.hooks.SessionStart);
733
+ // 3. Second framework contributes
734
+ const r2 = contributeFrameworkSettings(r1.settings, 'other', makeOtherFrameworkContribution());
735
+ assert.strictEqual(Object.keys(r2.settings._contributions).length, 2);
736
+ // Both frameworks' SessionStart hooks should be present
737
+ assert.strictEqual(r2.settings.hooks.SessionStart.length, 2);
738
+ // Permissions should be unioned
739
+ assert.ok(r2.settings.permissions.allow.includes('Skill(sm)'));
740
+ assert.ok(r2.settings.permissions.allow.includes('Skill(custom-skill)'));
741
+ // 4. Remove second framework
742
+ const afterRemove = removeFrameworkSettings(r2.settings, 'other');
743
+ assert.strictEqual(Object.keys(afterRemove._contributions).length, 1);
744
+ assert.strictEqual(afterRemove.hooks.SessionStart.length, 1);
745
+ assert.ok(!afterRemove.permissions.allow.includes('Skill(custom-skill)'));
746
+ // 5. Export flat format
747
+ const flat = toFlatFormat(afterRemove);
748
+ assert.strictEqual(flat._version, undefined);
749
+ assert.ok(flat.hooks);
750
+ assert.ok(flat.permissions);
751
+ });
752
+ it('should handle migrate-then-contribute workflow', () => {
753
+ // Legacy settings exist, then another framework installs
754
+ const legacy = {
755
+ hooks: {
756
+ SessionStart: [
757
+ { hooks: [{ type: 'command', command: '.pennyfarthing/scripts/hooks/session-start.sh' }] },
758
+ ],
759
+ },
760
+ permissions: { allow: ['Read', 'Bash'] },
761
+ statusLine: { type: 'command', command: '.pennyfarthing/scripts/misc/statusline.sh' },
762
+ };
763
+ // Migrate legacy to v2
764
+ const migrated = migrateToSharedFormat(legacy, 'pennyfarthing', '10.4.0');
765
+ assert.strictEqual(migrated._version, SHARED_SETTINGS_VERSION);
766
+ // New framework contributes
767
+ const result = contributeFrameworkSettings(migrated, 'new-tool', makeOtherFrameworkContribution());
768
+ assert.strictEqual(Object.keys(result.settings._contributions).length, 2);
769
+ assert.ok(result.settings.hooks.SessionStart.length >= 2, 'Should have hooks from both');
770
+ });
771
+ it('should be idempotent — contributing same framework twice produces same result', () => {
772
+ const settings = createEmptySharedSettings();
773
+ const contribution = makePennyfarthingContribution();
774
+ const r1 = contributeFrameworkSettings(settings, 'pennyfarthing', contribution);
775
+ const r2 = contributeFrameworkSettings(r1.settings, 'pennyfarthing', contribution);
776
+ // Merged output should be identical
777
+ assert.deepStrictEqual(r1.settings.hooks, r2.settings.hooks, 'Hooks should be identical');
778
+ assert.deepStrictEqual(r1.settings.permissions, r2.settings.permissions, 'Permissions should be identical');
779
+ });
780
+ });
781
+ //# sourceMappingURL=settings-merge.test.js.map