@ngxtm/devkit 2.1.0 → 3.0.1

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 (539) hide show
  1. package/README.md +57 -197
  2. package/cli/detect.js +292 -0
  3. package/cli/index.js +204 -92
  4. package/cli/init.js +245 -0
  5. package/cli/update.js +243 -0
  6. package/cli/utils.js +195 -0
  7. package/hooks/lib/__tests__/ck-config-utils.test.cjs +10 -0
  8. package/hooks/lib/__tests__/statusline-integration.test.cjs +46 -75
  9. package/hooks/scout-block/tests/test-monorepo-scenarios.cjs +2 -2
  10. package/hooks/tests/test-ckignore.cjs +7 -2
  11. package/package.json +16 -20
  12. package/rules-index.json +76 -0
  13. package/scripts/generate-index.js +223 -0
  14. package/scripts/merge-commands.js +290 -0
  15. package/scripts/organize-rules.js +226 -0
  16. package/templates/base/hooks/lib/ck-config-utils.cjs +769 -0
  17. package/templates/base/hooks/lib/ck-paths.cjs +110 -0
  18. package/templates/base/hooks/lib/colors.cjs +95 -0
  19. package/templates/base/hooks/lib/config-counter.cjs +103 -0
  20. package/templates/base/hooks/lib/context-builder.cjs +600 -0
  21. package/templates/base/hooks/lib/context-tracker.cjs +335 -0
  22. package/templates/base/hooks/lib/privacy-checker.cjs +297 -0
  23. package/templates/base/hooks/lib/project-detector.cjs +430 -0
  24. package/templates/base/hooks/lib/scout-checker.cjs +172 -0
  25. package/templates/base/hooks/lib/transcript-parser.cjs +164 -0
  26. package/templates/base/hooks/privacy-block.cjs +145 -0
  27. package/agents/backend-engineer.md +0 -154
  28. package/agents/brainstormer.md +0 -169
  29. package/agents/business-analyst.md +0 -166
  30. package/agents/database-architect.md +0 -159
  31. package/agents/debugger.md +0 -155
  32. package/agents/designer.md +0 -150
  33. package/agents/devops-engineer.md +0 -155
  34. package/agents/docs-manager.md +0 -171
  35. package/agents/frontend-engineer.md +0 -159
  36. package/agents/game-engineer.md +0 -148
  37. package/agents/mobile-engineer.md +0 -149
  38. package/agents/performance-engineer.md +0 -152
  39. package/agents/planner.md +0 -161
  40. package/agents/project-manager.md +0 -160
  41. package/agents/researcher.md +0 -146
  42. package/agents/reviewer.md +0 -155
  43. package/agents/scouter.md +0 -157
  44. package/agents/security-engineer.md +0 -154
  45. package/agents/tech-lead.md +0 -159
  46. package/agents/tester.md +0 -157
  47. package/agents-claudekit/brainstormer.md +0 -113
  48. package/agents-claudekit/code-reviewer.md +0 -157
  49. package/agents-claudekit/code-simplifier.md +0 -42
  50. package/agents-claudekit/copywriter.md +0 -110
  51. package/agents-claudekit/database-admin.md +0 -92
  52. package/agents-claudekit/debugger.md +0 -137
  53. package/agents-claudekit/docs-manager.md +0 -208
  54. package/agents-claudekit/fullstack-developer.md +0 -95
  55. package/agents-claudekit/git-manager.md +0 -394
  56. package/agents-claudekit/journal-writer.md +0 -113
  57. package/agents-claudekit/mcp-manager.md +0 -93
  58. package/agents-claudekit/planner.md +0 -108
  59. package/agents-claudekit/project-manager.md +0 -125
  60. package/agents-claudekit/researcher.md +0 -38
  61. package/agents-claudekit/scout-external.md +0 -141
  62. package/agents-claudekit/scout.md +0 -107
  63. package/agents-claudekit/tester.md +0 -105
  64. package/agents-claudekit/ui-ux-designer.md +0 -236
  65. package/commands/ask.md +0 -64
  66. package/commands/brainstorm.md +0 -64
  67. package/commands/code.md +0 -64
  68. package/commands/cook.md +0 -64
  69. package/commands/debug.md +0 -64
  70. package/commands/design/fast.md +0 -134
  71. package/commands/fix/fast.md +0 -84
  72. package/commands/fix/hard.md +0 -116
  73. package/commands/fix.md +0 -64
  74. package/commands/plan/fast.md +0 -78
  75. package/commands/plan/hard.md +0 -131
  76. package/commands/plan.md +0 -64
  77. package/commands/test.md +0 -64
  78. package/hooks/tests/test-modularization-hook.cjs +0 -126
  79. package/matrix-skills/_index.yaml +0 -275
  80. package/matrix-skills/ai-ml.yaml +0 -353
  81. package/matrix-skills/architecture.yaml +0 -93
  82. package/matrix-skills/backend.yaml +0 -280
  83. package/matrix-skills/cloud.yaml +0 -112
  84. package/matrix-skills/data.yaml +0 -74
  85. package/matrix-skills/design.yaml +0 -98
  86. package/matrix-skills/devops.yaml +0 -200
  87. package/matrix-skills/frontend.yaml +0 -200
  88. package/matrix-skills/gaming.yaml +0 -39
  89. package/matrix-skills/languages.yaml +0 -160
  90. package/matrix-skills/management.yaml +0 -50
  91. package/matrix-skills/mcp.yaml +0 -82
  92. package/matrix-skills/mobile.yaml +0 -85
  93. package/matrix-skills/performance.yaml +0 -23
  94. package/matrix-skills/planning.yaml +0 -117
  95. package/matrix-skills/quality.yaml +0 -195
  96. package/matrix-skills/research.yaml +0 -106
  97. package/matrix-skills/security.yaml +0 -293
  98. package/matrix-skills/tools.yaml +0 -352
  99. package/output-styles/coding-level-0-eli5.md +0 -103
  100. package/output-styles/coding-level-1-junior.md +0 -124
  101. package/output-styles/coding-level-2-mid.md +0 -146
  102. package/output-styles/coding-level-3-senior.md +0 -148
  103. package/output-styles/coding-level-4-lead.md +0 -159
  104. package/output-styles/coding-level-5-god.md +0 -91
  105. package/rules/README.md +0 -141
  106. package/rules/metadata.json +0 -54
  107. package/settings.json +0 -3
  108. package/statusline.cjs +0 -500
  109. package/statusline.ps1 +0 -307
  110. package/statusline.sh +0 -237
  111. package/workflows/development-rules.md +0 -42
  112. package/workflows/documentation-management.md +0 -121
  113. package/workflows/orchestration-protocol.md +0 -16
  114. package/workflows/primary-workflow.md +0 -45
  115. /package/{commands → merged-commands}/ask/fast.md +0 -0
  116. /package/{commands → merged-commands}/ask/hard.md +0 -0
  117. /package/{commands-claudekit → merged-commands}/ask.md +0 -0
  118. /package/{commands → merged-commands}/auto.md +0 -0
  119. /package/{commands-claudekit → merged-commands}/bootstrap/auto/fast.md +0 -0
  120. /package/{commands-claudekit → merged-commands}/bootstrap/auto/parallel.md +0 -0
  121. /package/{commands-claudekit → merged-commands}/bootstrap/auto.md +0 -0
  122. /package/{commands-claudekit → merged-commands}/bootstrap.md +0 -0
  123. /package/{commands → merged-commands}/brainstorm/fast.md +0 -0
  124. /package/{commands → merged-commands}/brainstorm/hard.md +0 -0
  125. /package/{commands-claudekit → merged-commands}/brainstorm.md +0 -0
  126. /package/{commands-claudekit → merged-commands}/ck-help.md +0 -0
  127. /package/{commands-claudekit → merged-commands}/code/auto.md +0 -0
  128. /package/{commands → merged-commands}/code/fast.md +0 -0
  129. /package/{commands → merged-commands}/code/hard.md +0 -0
  130. /package/{commands-claudekit → merged-commands}/code/no-test.md +0 -0
  131. /package/{commands-claudekit → merged-commands}/code/parallel.md +0 -0
  132. /package/{commands-claudekit → merged-commands}/code.md +0 -0
  133. /package/{commands-claudekit → merged-commands}/coding-level.md +0 -0
  134. /package/{commands-claudekit → merged-commands}/content/cro.md +0 -0
  135. /package/{commands-claudekit → merged-commands}/content/enhance.md +0 -0
  136. /package/{commands-claudekit → merged-commands}/content/fast.md +0 -0
  137. /package/{commands-claudekit → merged-commands}/content/good.md +0 -0
  138. /package/{commands-claudekit → merged-commands}/cook/auto/fast.md +0 -0
  139. /package/{commands-claudekit → merged-commands}/cook/auto/parallel.md +0 -0
  140. /package/{commands-claudekit → merged-commands}/cook/auto.md +0 -0
  141. /package/{commands → merged-commands}/cook/fast.md +0 -0
  142. /package/{commands → merged-commands}/cook/hard.md +0 -0
  143. /package/{commands-claudekit → merged-commands}/cook.md +0 -0
  144. /package/{commands → merged-commands}/debug/fast.md +0 -0
  145. /package/{commands → merged-commands}/debug/hard.md +0 -0
  146. /package/{commands-claudekit → merged-commands}/debug.md +0 -0
  147. /package/{commands → merged-commands}/deploy/check.md +0 -0
  148. /package/{commands → merged-commands}/deploy/preview.md +0 -0
  149. /package/{commands → merged-commands}/deploy/production.md +0 -0
  150. /package/{commands → merged-commands}/deploy/rollback.md +0 -0
  151. /package/{commands → merged-commands}/deploy.md +0 -0
  152. /package/{commands-claudekit → merged-commands}/design/3d.md +0 -0
  153. /package/{commands-claudekit → merged-commands}/design/describe.md +0 -0
  154. /package/{commands-claudekit → merged-commands}/design/fast.md +0 -0
  155. /package/{commands-claudekit → merged-commands}/design/good.md +0 -0
  156. /package/{commands → merged-commands}/design/hard.md +0 -0
  157. /package/{commands-claudekit → merged-commands}/design/screenshot.md +0 -0
  158. /package/{commands-claudekit → merged-commands}/design/video.md +0 -0
  159. /package/{commands → merged-commands}/design.md +0 -0
  160. /package/{commands → merged-commands}/docs/audit.md +0 -0
  161. /package/{commands → merged-commands}/docs/business.md +0 -0
  162. /package/{commands → merged-commands}/docs/core.md +0 -0
  163. /package/{commands-claudekit → merged-commands}/docs/init.md +0 -0
  164. /package/{commands-claudekit → merged-commands}/docs/summarize.md +0 -0
  165. /package/{commands-claudekit → merged-commands}/docs/update.md +0 -0
  166. /package/{commands → merged-commands}/docs.md +0 -0
  167. /package/{commands-claudekit → merged-commands}/fix/ci.md +0 -0
  168. /package/{commands-claudekit → merged-commands}/fix/fast.md +0 -0
  169. /package/{commands-claudekit → merged-commands}/fix/hard.md +0 -0
  170. /package/{commands-claudekit → merged-commands}/fix/logs.md +0 -0
  171. /package/{commands-claudekit → merged-commands}/fix/parallel.md +0 -0
  172. /package/{commands-claudekit → merged-commands}/fix/test.md +0 -0
  173. /package/{commands-claudekit → merged-commands}/fix/types.md +0 -0
  174. /package/{commands-claudekit → merged-commands}/fix/ui.md +0 -0
  175. /package/{commands-claudekit → merged-commands}/fix.md +0 -0
  176. /package/{commands-claudekit → merged-commands}/git/cm.md +0 -0
  177. /package/{commands-claudekit → merged-commands}/git/cp.md +0 -0
  178. /package/{commands-claudekit → merged-commands}/git/merge.md +0 -0
  179. /package/{commands-claudekit → merged-commands}/git/pr.md +0 -0
  180. /package/{commands-claudekit → merged-commands}/integrate/polar.md +0 -0
  181. /package/{commands-claudekit → merged-commands}/integrate/sepay.md +0 -0
  182. /package/{commands-claudekit → merged-commands}/journal.md +0 -0
  183. /package/{commands-claudekit → merged-commands}/kanban.md +0 -0
  184. /package/{commands-claudekit → merged-commands}/plan/archive.md +0 -0
  185. /package/{commands-claudekit → merged-commands}/plan/ci.md +0 -0
  186. /package/{commands-claudekit → merged-commands}/plan/cro.md +0 -0
  187. /package/{commands-claudekit → merged-commands}/plan/fast.md +0 -0
  188. /package/{commands-claudekit → merged-commands}/plan/hard.md +0 -0
  189. /package/{commands-claudekit → merged-commands}/plan/parallel.md +0 -0
  190. /package/{commands-claudekit → merged-commands}/plan/two.md +0 -0
  191. /package/{commands-claudekit → merged-commands}/plan/validate.md +0 -0
  192. /package/{commands-claudekit → merged-commands}/plan.md +0 -0
  193. /package/{commands-claudekit → merged-commands}/preview.md +0 -0
  194. /package/{commands-claudekit → merged-commands}/review/codebase/parallel.md +0 -0
  195. /package/{commands-claudekit → merged-commands}/review/codebase.md +0 -0
  196. /package/{commands → merged-commands}/review/fast.md +0 -0
  197. /package/{commands → merged-commands}/review/hard.md +0 -0
  198. /package/{commands → merged-commands}/review.md +0 -0
  199. /package/{commands-claudekit → merged-commands}/scout/ext.md +0 -0
  200. /package/{commands-claudekit → merged-commands}/scout.md +0 -0
  201. /package/{commands-claudekit → merged-commands}/skill/add.md +0 -0
  202. /package/{commands-claudekit → merged-commands}/skill/create.md +0 -0
  203. /package/{commands-claudekit → merged-commands}/skill/fix-logs.md +0 -0
  204. /package/{commands-claudekit → merged-commands}/skill/optimize/auto.md +0 -0
  205. /package/{commands-claudekit → merged-commands}/skill/optimize.md +0 -0
  206. /package/{commands-claudekit → merged-commands}/skill/plan.md +0 -0
  207. /package/{commands-claudekit → merged-commands}/skill/update.md +0 -0
  208. /package/{commands → merged-commands}/test/fast.md +0 -0
  209. /package/{commands → merged-commands}/test/hard.md +0 -0
  210. /package/{commands-claudekit → merged-commands}/test/ui.md +0 -0
  211. /package/{commands-claudekit → merged-commands}/test.md +0 -0
  212. /package/{commands-claudekit → merged-commands}/use-mcp.md +0 -0
  213. /package/{commands-claudekit → merged-commands}/watzup.md +0 -0
  214. /package/{commands-claudekit → merged-commands}/worktree.md +0 -0
  215. /package/{rules → templates/dart/rules}/dart/best-practices/SKILL.md +0 -0
  216. /package/{rules → templates/dart/rules}/dart/language/SKILL.md +0 -0
  217. /package/{rules → templates/dart/rules}/dart/tooling/SKILL.md +0 -0
  218. /package/{rules → templates/flutter/rules}/flutter/auto-route-navigation/SKILL.md +0 -0
  219. /package/{rules → templates/flutter/rules}/flutter/auto-route-navigation/references/REFERENCE.md +0 -0
  220. /package/{rules → templates/flutter/rules}/flutter/auto-route-navigation/references/router-config.md +0 -0
  221. /package/{rules → templates/flutter/rules}/flutter/bloc-state-management/SKILL.md +0 -0
  222. /package/{rules → templates/flutter/rules}/flutter/bloc-state-management/references/REFERENCE.md +0 -0
  223. /package/{rules → templates/flutter/rules}/flutter/bloc-state-management/references/auth-bloc-example.md +0 -0
  224. /package/{rules → templates/flutter/rules}/flutter/bloc-state-management/references/equatable-usage.md +0 -0
  225. /package/{rules → templates/flutter/rules}/flutter/bloc-state-management/references/property-based-state.md +0 -0
  226. /package/{rules → templates/flutter/rules}/flutter/bloc.rule.md +0 -0
  227. /package/{rules → templates/flutter/rules}/flutter/cicd/SKILL.md +0 -0
  228. /package/{rules → templates/flutter/rules}/flutter/cicd/references/advanced-workflow.md +0 -0
  229. /package/{rules → templates/flutter/rules}/flutter/cicd/references/fastlane.md +0 -0
  230. /package/{rules → templates/flutter/rules}/flutter/cicd/references/github-actions.md +0 -0
  231. /package/{rules → templates/flutter/rules}/flutter/dependency-injection/SKILL.md +0 -0
  232. /package/{rules → templates/flutter/rules}/flutter/dependency-injection/references/REFERENCE.md +0 -0
  233. /package/{rules → templates/flutter/rules}/flutter/dependency-injection/references/modules.md +0 -0
  234. /package/{rules → templates/flutter/rules}/flutter/error-handling/SKILL.md +0 -0
  235. /package/{rules → templates/flutter/rules}/flutter/error-handling/references/REFERENCE.md +0 -0
  236. /package/{rules → templates/flutter/rules}/flutter/error-handling/references/error-mapping.md +0 -0
  237. /package/{rules → templates/flutter/rules}/flutter/feature-based-clean-architecture/SKILL.md +0 -0
  238. /package/{rules → templates/flutter/rules}/flutter/feature-based-clean-architecture/references/REFERENCE.md +0 -0
  239. /package/{rules → templates/flutter/rules}/flutter/feature-based-clean-architecture/references/folder-structure.md +0 -0
  240. /package/{rules → templates/flutter/rules}/flutter/getx-navigation/SKILL.md +0 -0
  241. /package/{rules → templates/flutter/rules}/flutter/getx-navigation/references/app-pages.md +0 -0
  242. /package/{rules → templates/flutter/rules}/flutter/getx-navigation/references/middleware-example.md +0 -0
  243. /package/{rules → templates/flutter/rules}/flutter/getx-state-management/SKILL.md +0 -0
  244. /package/{rules → templates/flutter/rules}/flutter/getx-state-management/references/binding-example.md +0 -0
  245. /package/{rules → templates/flutter/rules}/flutter/getx-state-management/references/reactive-vs-simple.md +0 -0
  246. /package/{rules → templates/flutter/rules}/flutter/go-router-navigation/SKILL.md +0 -0
  247. /package/{rules → templates/flutter/rules}/flutter/idiomatic-flutter/SKILL.md +0 -0
  248. /package/{rules → templates/flutter/rules}/flutter/layer-based-clean-architecture/SKILL.md +0 -0
  249. /package/{rules → templates/flutter/rules}/flutter/layer-based-clean-architecture/references/REFERENCE.md +0 -0
  250. /package/{rules → templates/flutter/rules}/flutter/layer-based-clean-architecture/references/repository-mapping.md +0 -0
  251. /package/{rules → templates/flutter/rules}/flutter/localization/SKILL.md +0 -0
  252. /package/{rules → templates/flutter/rules}/flutter/localization/references/REFERENCE.md +0 -0
  253. /package/{rules → templates/flutter/rules}/flutter/localization/references/sheet-loader.md +0 -0
  254. /package/{rules → templates/flutter/rules}/flutter/navigator-v1-navigation/SKILL.md +0 -0
  255. /package/{rules → templates/flutter/rules}/flutter/navigator-v1-navigation/references/on-generate-route.md +0 -0
  256. /package/{rules → templates/flutter/rules}/flutter/performance/SKILL.md +0 -0
  257. /package/{rules → templates/flutter/rules}/flutter/retrofit-networking/SKILL.md +0 -0
  258. /package/{rules → templates/flutter/rules}/flutter/retrofit-networking/references/REFERENCE.md +0 -0
  259. /package/{rules → templates/flutter/rules}/flutter/retrofit-networking/references/token-refresh.md +0 -0
  260. /package/{rules → templates/flutter/rules}/flutter/riverpod-state-management/SKILL.md +0 -0
  261. /package/{rules → templates/flutter/rules}/flutter/riverpod-state-management/references/architecture.md +0 -0
  262. /package/{rules → templates/flutter/rules}/flutter/riverpod-state-management/references/best-practices.md +0 -0
  263. /package/{rules → templates/flutter/rules}/flutter/riverpod-state-management/references/testing.md +0 -0
  264. /package/{rules → templates/flutter/rules}/flutter/riverpod.rule.md +0 -0
  265. /package/{rules → templates/flutter/rules}/flutter/security/SKILL.md +0 -0
  266. /package/{rules → templates/flutter/rules}/flutter/security/references/REFERENCE.md +0 -0
  267. /package/{rules → templates/flutter/rules}/flutter/security/references/network-security.md +0 -0
  268. /package/{rules → templates/flutter/rules}/flutter/testing/SKILL.md +0 -0
  269. /package/{rules → templates/flutter/rules}/flutter/testing/references/REFERENCE.md +0 -0
  270. /package/{rules → templates/flutter/rules}/flutter/testing/references/bloc-testing.md +0 -0
  271. /package/{rules → templates/flutter/rules}/flutter/testing/references/integration-testing.md +0 -0
  272. /package/{rules → templates/flutter/rules}/flutter/testing/references/robot-pattern.md +0 -0
  273. /package/{rules → templates/flutter/rules}/flutter/testing/references/unit-testing.md +0 -0
  274. /package/{rules → templates/flutter/rules}/flutter/testing/references/widget-testing.md +0 -0
  275. /package/{rules → templates/flutter/rules}/flutter/widgets/SKILL.md +0 -0
  276. /package/{rules → templates/golang/rules}/golang/chi-router/SKILL.md +0 -0
  277. /package/{rules → templates/golang/rules}/golang/chi-router/references/REFERENCE.md +0 -0
  278. /package/{rules → templates/golang/rules}/golang/chi-router/references/routing-patterns.md +0 -0
  279. /package/{rules → templates/golang/rules}/golang/cobra-cli/SKILL.md +0 -0
  280. /package/{rules → templates/golang/rules}/golang/cobra-cli/references/REFERENCE.md +0 -0
  281. /package/{rules → templates/golang/rules}/golang/cobra-cli/references/command-patterns.md +0 -0
  282. /package/{rules → templates/golang/rules}/golang/core/SKILL.md +0 -0
  283. /package/{rules → templates/golang/rules}/golang/core/references/REFERENCE.md +0 -0
  284. /package/{rules → templates/golang/rules}/golang/core/references/concurrency-patterns.md +0 -0
  285. /package/{rules → templates/golang/rules}/golang/core/references/error-handling.md +0 -0
  286. /package/{rules → templates/golang/rules}/golang/echo-framework/SKILL.md +0 -0
  287. /package/{rules → templates/golang/rules}/golang/echo-framework/references/REFERENCE.md +0 -0
  288. /package/{rules → templates/golang/rules}/golang/echo-framework/references/middleware-patterns.md +0 -0
  289. /package/{rules → templates/golang/rules}/golang/echo-framework/references/routing-patterns.md +0 -0
  290. /package/{rules → templates/golang/rules}/golang/ent-orm/SKILL.md +0 -0
  291. /package/{rules → templates/golang/rules}/golang/ent-orm/references/REFERENCE.md +0 -0
  292. /package/{rules → templates/golang/rules}/golang/ent-orm/references/schema-patterns.md +0 -0
  293. /package/{rules → templates/golang/rules}/golang/fiber-framework/SKILL.md +0 -0
  294. /package/{rules → templates/golang/rules}/golang/fiber-framework/references/REFERENCE.md +0 -0
  295. /package/{rules → templates/golang/rules}/golang/fiber-framework/references/routing-patterns.md +0 -0
  296. /package/{rules → templates/golang/rules}/golang/gin-framework/SKILL.md +0 -0
  297. /package/{rules → templates/golang/rules}/golang/gin-framework/references/REFERENCE.md +0 -0
  298. /package/{rules → templates/golang/rules}/golang/gin-framework/references/middleware-patterns.md +0 -0
  299. /package/{rules → templates/golang/rules}/golang/gorm-orm/SKILL.md +0 -0
  300. /package/{rules → templates/golang/rules}/golang/gorm-orm/references/REFERENCE.md +0 -0
  301. /package/{rules → templates/golang/rules}/golang/gorm-orm/references/model-definitions.md +0 -0
  302. /package/{rules → templates/golang/rules}/golang/gorm-orm/references/query-patterns.md +0 -0
  303. /package/{rules → templates/golang/rules}/golang/grpc/SKILL.md +0 -0
  304. /package/{rules → templates/golang/rules}/golang/grpc/references/REFERENCE.md +0 -0
  305. /package/{rules → templates/golang/rules}/golang/grpc/references/service-patterns.md +0 -0
  306. /package/{rules → templates/golang/rules}/golang/testify/SKILL.md +0 -0
  307. /package/{rules → templates/golang/rules}/golang/testify/references/REFERENCE.md +0 -0
  308. /package/{rules → templates/golang/rules}/golang/testify/references/assert-patterns.md +0 -0
  309. /package/{rules → templates/golang/rules}/golang/validator/SKILL.md +0 -0
  310. /package/{rules → templates/golang/rules}/golang/validator/references/REFERENCE.md +0 -0
  311. /package/{rules → templates/golang/rules}/golang/validator/references/validation-tags.md +0 -0
  312. /package/{rules → templates/golang/rules}/golang/viper-config/SKILL.md +0 -0
  313. /package/{rules → templates/golang/rules}/golang/viper-config/references/REFERENCE.md +0 -0
  314. /package/{rules → templates/golang/rules}/golang/viper-config/references/config-loading.md +0 -0
  315. /package/{rules → templates/golang/rules}/golang/wire-di/SKILL.md +0 -0
  316. /package/{rules → templates/golang/rules}/golang/wire-di/references/REFERENCE.md +0 -0
  317. /package/{rules → templates/golang/rules}/golang/wire-di/references/provider-patterns.md +0 -0
  318. /package/{rules → templates/golang/rules}/golang/zap-logging/SKILL.md +0 -0
  319. /package/{rules → templates/golang/rules}/golang/zap-logging/references/REFERENCE.md +0 -0
  320. /package/{rules → templates/golang/rules}/golang/zap-logging/references/logger-config.md +0 -0
  321. /package/{rules → templates/java/rules}/java/build-gradle/SKILL.md +0 -0
  322. /package/{rules → templates/java/rules}/java/build-gradle/references/REFERENCE.md +0 -0
  323. /package/{rules → templates/java/rules}/java/build-gradle/references/kotlin-dsl.md +0 -0
  324. /package/{rules → templates/java/rules}/java/build-gradle/references/task-configuration.md +0 -0
  325. /package/{rules → templates/java/rules}/java/build-maven/SKILL.md +0 -0
  326. /package/{rules → templates/java/rules}/java/build-maven/references/REFERENCE.md +0 -0
  327. /package/{rules → templates/java/rules}/java/build-maven/references/dependency-management.md +0 -0
  328. /package/{rules → templates/java/rules}/java/build-maven/references/lifecycle-phases.md +0 -0
  329. /package/{rules → templates/java/rules}/java/graalvm-native/SKILL.md +0 -0
  330. /package/{rules → templates/java/rules}/java/graalvm-native/references/REFERENCE.md +0 -0
  331. /package/{rules → templates/java/rules}/java/java-collections-streams/SKILL.md +0 -0
  332. /package/{rules → templates/java/rules}/java/java-collections-streams/references/REFERENCE.md +0 -0
  333. /package/{rules → templates/java/rules}/java/java-collections-streams/references/collectors-patterns.md +0 -0
  334. /package/{rules → templates/java/rules}/java/java-collections-streams/references/stream-pipelines.md +0 -0
  335. /package/{rules → templates/java/rules}/java/java-concurrency/SKILL.md +0 -0
  336. /package/{rules → templates/java/rules}/java/java-concurrency/references/REFERENCE.md +0 -0
  337. /package/{rules → templates/java/rules}/java/java-concurrency/references/completable-future.md +0 -0
  338. /package/{rules → templates/java/rules}/java/java-concurrency/references/executor-patterns.md +0 -0
  339. /package/{rules → templates/java/rules}/java/java-concurrency/references/virtual-threads.md +0 -0
  340. /package/{rules → templates/java/rules}/java/java-core-language/SKILL.md +0 -0
  341. /package/{rules → templates/java/rules}/java/java-core-language/references/REFERENCE.md +0 -0
  342. /package/{rules → templates/java/rules}/java/java-core-language/references/jvm-memory-model.md +0 -0
  343. /package/{rules → templates/java/rules}/java/java-core-language/references/modern-java-features.md +0 -0
  344. /package/{rules → templates/java/rules}/java/java-project-structure/SKILL.md +0 -0
  345. /package/{rules → templates/java/rules}/java/java-project-structure/references/REFERENCE.md +0 -0
  346. /package/{rules → templates/java/rules}/java/java-project-structure/references/maven-project-layout.md +0 -0
  347. /package/{rules → templates/java/rules}/java/java-project-structure/references/module-system.md +0 -0
  348. /package/{rules → templates/java/rules}/java/micronaut-core/SKILL.md +0 -0
  349. /package/{rules → templates/java/rules}/java/micronaut-core/references/REFERENCE.md +0 -0
  350. /package/{rules → templates/java/rules}/java/micronaut-reactive/SKILL.md +0 -0
  351. /package/{rules → templates/java/rules}/java/micronaut-reactive/references/REFERENCE.md +0 -0
  352. /package/{rules → templates/java/rules}/java/quarkus-core/SKILL.md +0 -0
  353. /package/{rules → templates/java/rules}/java/quarkus-core/references/REFERENCE.md +0 -0
  354. /package/{rules → templates/java/rules}/java/quarkus-reactive/SKILL.md +0 -0
  355. /package/{rules → templates/java/rules}/java/quarkus-reactive/references/REFERENCE.md +0 -0
  356. /package/{rules → templates/java/rules}/java/spring-batch/SKILL.md +0 -0
  357. /package/{rules → templates/java/rules}/java/spring-batch/references/REFERENCE.md +0 -0
  358. /package/{rules → templates/java/rules}/java/spring-boot-architecture/SKILL.md +0 -0
  359. /package/{rules → templates/java/rules}/java/spring-boot-architecture/references/REFERENCE.md +0 -0
  360. /package/{rules → templates/java/rules}/java/spring-boot-architecture/references/auto-configuration.md +0 -0
  361. /package/{rules → templates/java/rules}/java/spring-boot-architecture/references/configuration-properties.md +0 -0
  362. /package/{rules → templates/java/rules}/java/spring-boot-web/SKILL.md +0 -0
  363. /package/{rules → templates/java/rules}/java/spring-boot-web/references/REFERENCE.md +0 -0
  364. /package/{rules → templates/java/rules}/java/spring-cloud/SKILL.md +0 -0
  365. /package/{rules → templates/java/rules}/java/spring-cloud/references/REFERENCE.md +0 -0
  366. /package/{rules → templates/java/rules}/java/spring-data-jpa/SKILL.md +0 -0
  367. /package/{rules → templates/java/rules}/java/spring-data-jpa/references/REFERENCE.md +0 -0
  368. /package/{rules → templates/java/rules}/java/spring-security/SKILL.md +0 -0
  369. /package/{rules → templates/java/rules}/java/spring-security/references/REFERENCE.md +0 -0
  370. /package/{rules → templates/java/rules}/java/spring-security/references/jwt-auth-flow.md +0 -0
  371. /package/{rules → templates/java/rules}/java/testing-junit-mockito/SKILL.md +0 -0
  372. /package/{rules → templates/java/rules}/java/testing-junit-mockito/references/REFERENCE.md +0 -0
  373. /package/{rules → templates/java/rules}/java/testing-junit-mockito/references/junit5-patterns.md +0 -0
  374. /package/{rules → templates/java/rules}/java/testing-junit-mockito/references/mockito-patterns.md +0 -0
  375. /package/{rules → templates/java/rules}/java/testing-junit-mockito/references/spring-boot-testing.md +0 -0
  376. /package/{rules → templates/javascript/rules}/javascript/best-practices/SKILL.md +0 -0
  377. /package/{rules → templates/javascript/rules}/javascript/best-practices/references/REFERENCE.md +0 -0
  378. /package/{rules → templates/javascript/rules}/javascript/language/SKILL.md +0 -0
  379. /package/{rules → templates/javascript/rules}/javascript/language/references/REFERENCE.md +0 -0
  380. /package/{rules → templates/javascript/rules}/javascript/tooling/SKILL.md +0 -0
  381. /package/{rules → templates/javascript/rules}/javascript/tooling/references/REFERENCE.md +0 -0
  382. /package/{rules → templates/nestjs/rules}/nestjs/api-standards/SKILL.md +0 -0
  383. /package/{rules → templates/nestjs/rules}/nestjs/api-standards/references/pagination-wrapper.md +0 -0
  384. /package/{rules → templates/nestjs/rules}/nestjs/architecture/SKILL.md +0 -0
  385. /package/{rules → templates/nestjs/rules}/nestjs/architecture/references/dynamic-module.md +0 -0
  386. /package/{rules → templates/nestjs/rules}/nestjs/caching/SKILL.md +0 -0
  387. /package/{rules → templates/nestjs/rules}/nestjs/caching/references/REFERENCE.md +0 -0
  388. /package/{rules → templates/nestjs/rules}/nestjs/caching/references/cache-patterns.md +0 -0
  389. /package/{rules → templates/nestjs/rules}/nestjs/configuration/SKILL.md +0 -0
  390. /package/{rules → templates/nestjs/rules}/nestjs/configuration/references/REFERENCE.md +0 -0
  391. /package/{rules → templates/nestjs/rules}/nestjs/configuration/references/config-patterns.md +0 -0
  392. /package/{rules → templates/nestjs/rules}/nestjs/controllers-services/SKILL.md +0 -0
  393. /package/{rules → templates/nestjs/rules}/nestjs/controllers-services/references/REFERENCE.md +0 -0
  394. /package/{rules → templates/nestjs/rules}/nestjs/controllers-services/references/controller-patterns.md +0 -0
  395. /package/{rules → templates/nestjs/rules}/nestjs/controllers-services/references/service-patterns.md +0 -0
  396. /package/{rules → templates/nestjs/rules}/nestjs/database/SKILL.md +0 -0
  397. /package/{rules → templates/nestjs/rules}/nestjs/database/references/REFERENCE.md +0 -0
  398. /package/{rules → templates/nestjs/rules}/nestjs/database/references/typeorm-patterns.md +0 -0
  399. /package/{rules → templates/nestjs/rules}/nestjs/deployment/SKILL.md +0 -0
  400. /package/{rules → templates/nestjs/rules}/nestjs/deployment/references/REFERENCE.md +0 -0
  401. /package/{rules → templates/nestjs/rules}/nestjs/deployment/references/deployment-patterns.md +0 -0
  402. /package/{rules → templates/nestjs/rules}/nestjs/documentation/SKILL.md +0 -0
  403. /package/{rules → templates/nestjs/rules}/nestjs/documentation/references/REFERENCE.md +0 -0
  404. /package/{rules → templates/nestjs/rules}/nestjs/documentation/references/swagger-patterns.md +0 -0
  405. /package/{rules → templates/nestjs/rules}/nestjs/error-handling/SKILL.md +0 -0
  406. /package/{rules → templates/nestjs/rules}/nestjs/error-handling/references/REFERENCE.md +0 -0
  407. /package/{rules → templates/nestjs/rules}/nestjs/error-handling/references/exception-filters.md +0 -0
  408. /package/{rules → templates/nestjs/rules}/nestjs/file-uploads/SKILL.md +0 -0
  409. /package/{rules → templates/nestjs/rules}/nestjs/file-uploads/references/REFERENCE.md +0 -0
  410. /package/{rules → templates/nestjs/rules}/nestjs/file-uploads/references/upload-patterns.md +0 -0
  411. /package/{rules → templates/nestjs/rules}/nestjs/observability/SKILL.md +0 -0
  412. /package/{rules → templates/nestjs/rules}/nestjs/observability/references/REFERENCE.md +0 -0
  413. /package/{rules → templates/nestjs/rules}/nestjs/observability/references/logging-metrics.md +0 -0
  414. /package/{rules → templates/nestjs/rules}/nestjs/performance/SKILL.md +0 -0
  415. /package/{rules → templates/nestjs/rules}/nestjs/performance/references/REFERENCE.md +0 -0
  416. /package/{rules → templates/nestjs/rules}/nestjs/performance/references/performance-patterns.md +0 -0
  417. /package/{rules → templates/nestjs/rules}/nestjs/real-time/SKILL.md +0 -0
  418. /package/{rules → templates/nestjs/rules}/nestjs/real-time/references/REFERENCE.md +0 -0
  419. /package/{rules → templates/nestjs/rules}/nestjs/real-time/references/websocket-patterns.md +0 -0
  420. /package/{rules → templates/nestjs/rules}/nestjs/scheduling/SKILL.md +0 -0
  421. /package/{rules → templates/nestjs/rules}/nestjs/scheduling/references/REFERENCE.md +0 -0
  422. /package/{rules → templates/nestjs/rules}/nestjs/scheduling/references/scheduling-patterns.md +0 -0
  423. /package/{rules → templates/nestjs/rules}/nestjs/search/SKILL.md +0 -0
  424. /package/{rules → templates/nestjs/rules}/nestjs/search/references/REFERENCE.md +0 -0
  425. /package/{rules → templates/nestjs/rules}/nestjs/search/references/search-patterns.md +0 -0
  426. /package/{rules → templates/nestjs/rules}/nestjs/security/SKILL.md +0 -0
  427. /package/{rules → templates/nestjs/rules}/nestjs/security/references/REFERENCE.md +0 -0
  428. /package/{rules → templates/nestjs/rules}/nestjs/security/references/authentication.md +0 -0
  429. /package/{rules → templates/nestjs/rules}/nestjs/testing/SKILL.md +0 -0
  430. /package/{rules → templates/nestjs/rules}/nestjs/testing/references/REFERENCE.md +0 -0
  431. /package/{rules → templates/nestjs/rules}/nestjs/testing/references/unit-testing.md +0 -0
  432. /package/{rules → templates/nestjs/rules}/nestjs/transport/SKILL.md +0 -0
  433. /package/{rules → templates/nestjs/rules}/nestjs/transport/references/REFERENCE.md +0 -0
  434. /package/{rules → templates/nestjs/rules}/nestjs/transport/references/microservices-patterns.md +0 -0
  435. /package/{rules → templates/nextjs/rules}/nextjs/app-router/SKILL.md +0 -0
  436. /package/{rules → templates/nextjs/rules}/nextjs/app-router/references/REFERENCE.md +0 -0
  437. /package/{rules → templates/nextjs/rules}/nextjs/app-router/references/routing-patterns.md +0 -0
  438. /package/{rules → templates/nextjs/rules}/nextjs/architecture/SKILL.md +0 -0
  439. /package/{rules → templates/nextjs/rules}/nextjs/architecture/references/fsd-structure.md +0 -0
  440. /package/{rules → templates/nextjs/rules}/nextjs/authentication/SKILL.md +0 -0
  441. /package/{rules → templates/nextjs/rules}/nextjs/authentication/references/auth-implementation.md +0 -0
  442. /package/{rules → templates/nextjs/rules}/nextjs/caching/SKILL.md +0 -0
  443. /package/{rules → templates/nextjs/rules}/nextjs/caching/references/REFERENCE.md +0 -0
  444. /package/{rules → templates/nextjs/rules}/nextjs/caching/references/cache-strategies.md +0 -0
  445. /package/{rules → templates/nextjs/rules}/nextjs/data-access-layer/SKILL.md +0 -0
  446. /package/{rules → templates/nextjs/rules}/nextjs/data-access-layer/references/patterns.md +0 -0
  447. /package/{rules → templates/nextjs/rules}/nextjs/data-fetching/SKILL.md +0 -0
  448. /package/{rules → templates/nextjs/rules}/nextjs/data-fetching/references/REFERENCE.md +0 -0
  449. /package/{rules → templates/nextjs/rules}/nextjs/data-fetching/references/fetch-patterns.md +0 -0
  450. /package/{rules → templates/nextjs/rules}/nextjs/internationalization/SKILL.md +0 -0
  451. /package/{rules → templates/nextjs/rules}/nextjs/internationalization/references/REFERENCE.md +0 -0
  452. /package/{rules → templates/nextjs/rules}/nextjs/internationalization/references/i18n-patterns.md +0 -0
  453. /package/{rules → templates/nextjs/rules}/nextjs/optimization/SKILL.md +0 -0
  454. /package/{rules → templates/nextjs/rules}/nextjs/optimization/references/REFERENCE.md +0 -0
  455. /package/{rules → templates/nextjs/rules}/nextjs/optimization/references/optimization-patterns.md +0 -0
  456. /package/{rules → templates/nextjs/rules}/nextjs/rendering/SKILL.md +0 -0
  457. /package/{rules → templates/nextjs/rules}/nextjs/rendering/references/REFERENCE.md +0 -0
  458. /package/{rules → templates/nextjs/rules}/nextjs/rendering/references/rendering-modes.md +0 -0
  459. /package/{rules → templates/nextjs/rules}/nextjs/server-actions/SKILL.md +0 -0
  460. /package/{rules → templates/nextjs/rules}/nextjs/server-actions/references/REFERENCE.md +0 -0
  461. /package/{rules → templates/nextjs/rules}/nextjs/server-actions/references/action-patterns.md +0 -0
  462. /package/{rules → templates/nextjs/rules}/nextjs/server-components/SKILL.md +0 -0
  463. /package/{rules → templates/nextjs/rules}/nextjs/server-components/references/REFERENCE.md +0 -0
  464. /package/{rules → templates/nextjs/rules}/nextjs/server-components/references/component-patterns.md +0 -0
  465. /package/{rules → templates/nextjs/rules}/nextjs/state-management/SKILL.md +0 -0
  466. /package/{rules → templates/nextjs/rules}/nextjs/state-management/references/REFERENCE.md +0 -0
  467. /package/{rules → templates/nextjs/rules}/nextjs/state-management/references/state-patterns.md +0 -0
  468. /package/{rules → templates/nextjs/rules}/nextjs/styling/SKILL.md +0 -0
  469. /package/{rules → templates/nextjs/rules}/nextjs/styling/references/implementation.md +0 -0
  470. /package/{rules → templates/react/rules}/react/component-patterns/SKILL.md +0 -0
  471. /package/{rules → templates/react/rules}/react/component-patterns/references/REFERENCE.md +0 -0
  472. /package/{rules → templates/react/rules}/react/hooks/SKILL.md +0 -0
  473. /package/{rules → templates/react/rules}/react/hooks/references/REFERENCE.md +0 -0
  474. /package/{rules → templates/react/rules}/react/hooks.rule.md +0 -0
  475. /package/{rules → templates/react/rules}/react/performance/SKILL.md +0 -0
  476. /package/{rules → templates/react/rules}/react/performance/references/REFERENCE.md +0 -0
  477. /package/{rules → templates/react/rules}/react/security/SKILL.md +0 -0
  478. /package/{rules → templates/react/rules}/react/security/references/REFERENCE.md +0 -0
  479. /package/{rules → templates/react/rules}/react/state-management/SKILL.md +0 -0
  480. /package/{rules → templates/react/rules}/react/state-management/references/REFERENCE.md +0 -0
  481. /package/{rules → templates/react/rules}/react/testing/SKILL.md +0 -0
  482. /package/{rules → templates/react/rules}/react/testing/references/REFERENCE.md +0 -0
  483. /package/{rules → templates/react/rules}/react/tooling/SKILL.md +0 -0
  484. /package/{rules → templates/react/rules}/react/typescript/SKILL.md +0 -0
  485. /package/{rules → templates/rust/rules}/rust/actix-web/SKILL.md +0 -0
  486. /package/{rules → templates/rust/rules}/rust/actix-web/references/REFERENCE.md +0 -0
  487. /package/{rules → templates/rust/rules}/rust/actix-web/references/handler-patterns.md +0 -0
  488. /package/{rules → templates/rust/rules}/rust/async-graphql/SKILL.md +0 -0
  489. /package/{rules → templates/rust/rules}/rust/async-graphql/references/REFERENCE.md +0 -0
  490. /package/{rules → templates/rust/rules}/rust/async-graphql/references/schema-patterns.md +0 -0
  491. /package/{rules → templates/rust/rules}/rust/axum/SKILL.md +0 -0
  492. /package/{rules → templates/rust/rules}/rust/axum/references/REFERENCE.md +0 -0
  493. /package/{rules → templates/rust/rules}/rust/axum/references/handler-patterns.md +0 -0
  494. /package/{rules → templates/rust/rules}/rust/bevy/SKILL.md +0 -0
  495. /package/{rules → templates/rust/rules}/rust/bevy/references/REFERENCE.md +0 -0
  496. /package/{rules → templates/rust/rules}/rust/bevy/references/ecs-patterns.md +0 -0
  497. /package/{rules → templates/rust/rules}/rust/clap/SKILL.md +0 -0
  498. /package/{rules → templates/rust/rules}/rust/clap/references/REFERENCE.md +0 -0
  499. /package/{rules → templates/rust/rules}/rust/clap/references/derive-patterns.md +0 -0
  500. /package/{rules → templates/rust/rules}/rust/core/SKILL.md +0 -0
  501. /package/{rules → templates/rust/rules}/rust/core/references/REFERENCE.md +0 -0
  502. /package/{rules → templates/rust/rules}/rust/core/references/error-handling.md +0 -0
  503. /package/{rules → templates/rust/rules}/rust/diesel-orm/SKILL.md +0 -0
  504. /package/{rules → templates/rust/rules}/rust/diesel-orm/references/REFERENCE.md +0 -0
  505. /package/{rules → templates/rust/rules}/rust/diesel-orm/references/schema-patterns.md +0 -0
  506. /package/{rules → templates/rust/rules}/rust/rocket/SKILL.md +0 -0
  507. /package/{rules → templates/rust/rules}/rust/rocket/references/REFERENCE.md +0 -0
  508. /package/{rules → templates/rust/rules}/rust/rocket/references/handler-patterns.md +0 -0
  509. /package/{rules → templates/rust/rules}/rust/sea-orm/SKILL.md +0 -0
  510. /package/{rules → templates/rust/rules}/rust/sea-orm/references/REFERENCE.md +0 -0
  511. /package/{rules → templates/rust/rules}/rust/sea-orm/references/entity-patterns.md +0 -0
  512. /package/{rules → templates/rust/rules}/rust/serde-serialization/SKILL.md +0 -0
  513. /package/{rules → templates/rust/rules}/rust/serde-serialization/references/REFERENCE.md +0 -0
  514. /package/{rules → templates/rust/rules}/rust/serde-serialization/references/serialization-patterns.md +0 -0
  515. /package/{rules → templates/rust/rules}/rust/sqlx-database/SKILL.md +0 -0
  516. /package/{rules → templates/rust/rules}/rust/sqlx-database/references/REFERENCE.md +0 -0
  517. /package/{rules → templates/rust/rules}/rust/sqlx-database/references/query-patterns.md +0 -0
  518. /package/{rules → templates/rust/rules}/rust/tauri/SKILL.md +0 -0
  519. /package/{rules → templates/rust/rules}/rust/tauri/references/REFERENCE.md +0 -0
  520. /package/{rules → templates/rust/rules}/rust/tauri/references/command-patterns.md +0 -0
  521. /package/{rules → templates/rust/rules}/rust/tokio-runtime/SKILL.md +0 -0
  522. /package/{rules → templates/rust/rules}/rust/tokio-runtime/references/REFERENCE.md +0 -0
  523. /package/{rules → templates/rust/rules}/rust/tokio-runtime/references/async-patterns.md +0 -0
  524. /package/{rules → templates/rust/rules}/rust/tokio-runtime/references/synchronization.md +0 -0
  525. /package/{rules → templates/rust/rules}/rust/tonic/SKILL.md +0 -0
  526. /package/{rules → templates/rust/rules}/rust/tonic/references/REFERENCE.md +0 -0
  527. /package/{rules → templates/rust/rules}/rust/tonic/references/service-patterns.md +0 -0
  528. /package/{rules → templates/rust/rules}/rust/tracing/SKILL.md +0 -0
  529. /package/{rules → templates/rust/rules}/rust/tracing/references/REFERENCE.md +0 -0
  530. /package/{rules → templates/rust/rules}/rust/tracing/references/instrumentation.md +0 -0
  531. /package/{rules → templates/typescript/rules}/typescript/best-practices/SKILL.md +0 -0
  532. /package/{rules → templates/typescript/rules}/typescript/best-practices/references/REFERENCE.md +0 -0
  533. /package/{rules → templates/typescript/rules}/typescript/language/SKILL.md +0 -0
  534. /package/{rules → templates/typescript/rules}/typescript/language/references/REFERENCE.md +0 -0
  535. /package/{rules → templates/typescript/rules}/typescript/patterns.rule.md +0 -0
  536. /package/{rules → templates/typescript/rules}/typescript/security/SKILL.md +0 -0
  537. /package/{rules → templates/typescript/rules}/typescript/security/references/REFERENCE.md +0 -0
  538. /package/{rules → templates/typescript/rules}/typescript/tooling/SKILL.md +0 -0
  539. /package/{rules → templates/typescript/rules}/typescript/tooling/references/REFERENCE.md +0 -0
@@ -0,0 +1,769 @@
1
+ /**
2
+ * Shared utilities for ClaudeKit hooks
3
+ *
4
+ * Contains config loading, path sanitization, and common constants
5
+ * used by session-init.cjs and dev-rules-reminder.cjs
6
+ */
7
+
8
+ const fs = require('fs');
9
+ const path = require('path');
10
+ const os = require('os');
11
+
12
+ const LOCAL_CONFIG_PATH = '.claude/.ck.json';
13
+ const GLOBAL_CONFIG_PATH = path.join(os.homedir(), '.claude', '.ck.json');
14
+
15
+ // Legacy export for backward compatibility
16
+ const CONFIG_PATH = LOCAL_CONFIG_PATH;
17
+
18
+ const DEFAULT_CONFIG = {
19
+ plan: {
20
+ namingFormat: '{date}-{issue}-{slug}',
21
+ dateFormat: 'YYMMDD-HHmm',
22
+ issuePrefix: null,
23
+ reportsDir: 'reports',
24
+ resolution: {
25
+ // CHANGED: Removed 'mostRecent' - only explicit session state activates plans
26
+ // Branch matching now returns 'suggested' not 'active'
27
+ order: ['session', 'branch'],
28
+ branchPattern: '(?:feat|fix|chore|refactor|docs)/(?:[^/]+/)?(.+)'
29
+ },
30
+ validation: {
31
+ mode: 'prompt', // 'auto' | 'prompt' | 'off'
32
+ minQuestions: 3,
33
+ maxQuestions: 8,
34
+ focusAreas: ['assumptions', 'risks', 'tradeoffs', 'architecture']
35
+ }
36
+ },
37
+ paths: {
38
+ docs: 'docs',
39
+ plans: 'plans'
40
+ },
41
+ docs: {
42
+ maxLoc: 800 // Maximum lines of code per doc file before warning
43
+ },
44
+ locale: {
45
+ thinkingLanguage: null, // Language for reasoning (e.g., "en" for precision)
46
+ responseLanguage: null // Language for user-facing output (e.g., "vi")
47
+ },
48
+ trust: {
49
+ passphrase: null,
50
+ enabled: false
51
+ },
52
+ project: {
53
+ type: 'auto',
54
+ packageManager: 'auto',
55
+ framework: 'auto'
56
+ },
57
+ skills: {
58
+ research: {
59
+ useGemini: true // Toggle Gemini CLI usage in research skill
60
+ }
61
+ },
62
+ assertions: []
63
+ };
64
+
65
+ /**
66
+ * Deep merge objects (source values override target, nested objects merged recursively)
67
+ * Arrays are replaced entirely (not concatenated) to avoid duplicate entries
68
+ * @param {Object} target - Base object
69
+ * @param {Object} source - Object to merge (takes precedence)
70
+ * @returns {Object} Merged object
71
+ */
72
+ function deepMerge(target, source) {
73
+ if (!source || typeof source !== 'object') return target;
74
+ if (!target || typeof target !== 'object') return source;
75
+
76
+ const result = { ...target };
77
+ for (const key of Object.keys(source)) {
78
+ const sourceVal = source[key];
79
+ const targetVal = target[key];
80
+
81
+ // Arrays: replace entirely (don't concatenate)
82
+ if (Array.isArray(sourceVal)) {
83
+ result[key] = [...sourceVal];
84
+ }
85
+ // Objects: recurse (but not null)
86
+ else if (sourceVal !== null && typeof sourceVal === 'object' && !Array.isArray(sourceVal)) {
87
+ result[key] = deepMerge(targetVal || {}, sourceVal);
88
+ }
89
+ // Primitives: source wins
90
+ else {
91
+ result[key] = sourceVal;
92
+ }
93
+ }
94
+ return result;
95
+ }
96
+
97
+ /**
98
+ * Load config from a specific file path
99
+ * @param {string} configPath - Path to config file
100
+ * @returns {Object|null} Parsed config or null if not found/invalid
101
+ */
102
+ function loadConfigFromPath(configPath) {
103
+ try {
104
+ if (!fs.existsSync(configPath)) return null;
105
+ return JSON.parse(fs.readFileSync(configPath, 'utf8'));
106
+ } catch (e) {
107
+ return null;
108
+ }
109
+ }
110
+
111
+ /**
112
+ * Get session temp file path
113
+ * @param {string} sessionId - Session identifier
114
+ * @returns {string} Path to session temp file
115
+ */
116
+ function getSessionTempPath(sessionId) {
117
+ return path.join(os.tmpdir(), `ck-session-${sessionId}.json`);
118
+ }
119
+
120
+ /**
121
+ * Read session state from temp file
122
+ * @param {string} sessionId - Session identifier
123
+ * @returns {Object|null} Session state or null
124
+ */
125
+ function readSessionState(sessionId) {
126
+ if (!sessionId) return null;
127
+ const tempPath = getSessionTempPath(sessionId);
128
+ try {
129
+ if (!fs.existsSync(tempPath)) return null;
130
+ return JSON.parse(fs.readFileSync(tempPath, 'utf8'));
131
+ } catch (e) {
132
+ return null;
133
+ }
134
+ }
135
+
136
+ /**
137
+ * Write session state atomically to temp file
138
+ * @param {string} sessionId - Session identifier
139
+ * @param {Object} state - State object to persist
140
+ * @returns {boolean} Success status
141
+ */
142
+ function writeSessionState(sessionId, state) {
143
+ if (!sessionId) return false;
144
+ const tempPath = getSessionTempPath(sessionId);
145
+ const tmpFile = tempPath + '.' + Math.random().toString(36).slice(2);
146
+ try {
147
+ fs.writeFileSync(tmpFile, JSON.stringify(state, null, 2));
148
+ fs.renameSync(tmpFile, tempPath);
149
+ return true;
150
+ } catch (e) {
151
+ try { fs.unlinkSync(tmpFile); } catch (_) { /* ignore */ }
152
+ return false;
153
+ }
154
+ }
155
+
156
+ /**
157
+ * Characters invalid in filenames across Windows, macOS, Linux
158
+ * Windows: < > : " / \ | ? *
159
+ * macOS/Linux: / and null byte
160
+ * Also includes control characters and other problematic chars
161
+ */
162
+ const INVALID_FILENAME_CHARS = /[<>:"/\\|?*\x00-\x1f\x7f]/g;
163
+
164
+ /**
165
+ * Sanitize slug for safe filesystem usage
166
+ * - Removes invalid filename characters
167
+ * - Replaces non-alphanumeric (except hyphen) with hyphen
168
+ * - Collapses multiple hyphens
169
+ * - Removes leading/trailing hyphens
170
+ * - Limits length to prevent filesystem issues
171
+ *
172
+ * @param {string} slug - Slug to sanitize
173
+ * @returns {string} Sanitized slug (empty string if nothing valid remains)
174
+ */
175
+ function sanitizeSlug(slug) {
176
+ if (!slug || typeof slug !== 'string') return '';
177
+
178
+ let sanitized = slug
179
+ // Remove invalid filename chars first
180
+ .replace(INVALID_FILENAME_CHARS, '')
181
+ // Replace any non-alphanumeric (except hyphen) with hyphen
182
+ .replace(/[^a-z0-9-]/gi, '-')
183
+ // Collapse multiple consecutive hyphens
184
+ .replace(/-+/g, '-')
185
+ // Remove leading/trailing hyphens
186
+ .replace(/^-+|-+$/g, '')
187
+ // Limit length (most filesystems support 255, but keep reasonable)
188
+ .slice(0, 100);
189
+
190
+ return sanitized;
191
+ }
192
+
193
+ /**
194
+ * Extract feature slug from git branch name
195
+ * Pattern: (?:feat|fix|chore|refactor|docs)/(?:[^/]+/)?(.+)
196
+ * @param {string} branch - Git branch name
197
+ * @param {string} pattern - Regex pattern (optional)
198
+ * @returns {string|null} Extracted slug or null
199
+ */
200
+ function extractSlugFromBranch(branch, pattern) {
201
+ if (!branch) return null;
202
+ const defaultPattern = /(?:feat|fix|chore|refactor|docs)\/(?:[^\/]+\/)?(.+)/;
203
+ const regex = pattern ? new RegExp(pattern) : defaultPattern;
204
+ const match = branch.match(regex);
205
+ return match ? sanitizeSlug(match[1]) : null;
206
+ }
207
+
208
+ /**
209
+ * Find most recent plan folder by timestamp prefix
210
+ * @param {string} plansDir - Plans directory path
211
+ * @returns {string|null} Most recent plan path or null
212
+ */
213
+ function findMostRecentPlan(plansDir) {
214
+ try {
215
+ if (!fs.existsSync(plansDir)) return null;
216
+ const entries = fs.readdirSync(plansDir, { withFileTypes: true });
217
+ const planDirs = entries
218
+ .filter(e => e.isDirectory() && /^\d{6}/.test(e.name))
219
+ .map(e => e.name)
220
+ .sort()
221
+ .reverse();
222
+ return planDirs.length > 0 ? path.join(plansDir, planDirs[0]) : null;
223
+ } catch (e) {
224
+ return null;
225
+ }
226
+ }
227
+
228
+ /**
229
+ * Default timeout for git commands (5 seconds)
230
+ * Prevents indefinite hangs on network mounts or corrupted repos
231
+ */
232
+ const DEFAULT_EXEC_TIMEOUT_MS = 5000;
233
+
234
+ /**
235
+ * Safely execute shell command (internal helper)
236
+ * SECURITY: Only accepts whitelisted git read commands
237
+ * @param {string} cmd - Command to execute
238
+ * @param {Object} options - Execution options
239
+ * @param {string} options.cwd - Working directory (optional)
240
+ * @param {number} options.timeout - Timeout in ms (default: 5000)
241
+ * @returns {string|null} Command output or null
242
+ */
243
+ function execSafe(cmd, options = {}) {
244
+ // Whitelist of safe read-only commands
245
+ const allowedCommands = [
246
+ 'git branch --show-current',
247
+ 'git rev-parse --abbrev-ref HEAD',
248
+ 'git rev-parse --show-toplevel'
249
+ ];
250
+ if (!allowedCommands.includes(cmd)) {
251
+ return null;
252
+ }
253
+
254
+ const { cwd = undefined, timeout = DEFAULT_EXEC_TIMEOUT_MS } = options;
255
+
256
+ try {
257
+ return require('child_process')
258
+ .execSync(cmd, {
259
+ encoding: 'utf8',
260
+ timeout,
261
+ cwd,
262
+ stdio: ['pipe', 'pipe', 'pipe']
263
+ })
264
+ .trim();
265
+ } catch (e) {
266
+ return null;
267
+ }
268
+ }
269
+
270
+ /**
271
+ * Resolve active plan path using cascading resolution with tracking
272
+ *
273
+ * Resolution semantics:
274
+ * - 'session': Explicitly set via set-active-plan.cjs → ACTIVE (directive)
275
+ * - 'branch': Matched from git branch name → SUGGESTED (hint only)
276
+ * - 'mostRecent': REMOVED - was causing stale plan pollution
277
+ *
278
+ * @param {string} sessionId - Session identifier (optional)
279
+ * @param {Object} config - ClaudeKit config
280
+ * @returns {{ path: string|null, resolvedBy: 'session'|'branch'|null }} Resolution result with tracking
281
+ */
282
+ function resolvePlanPath(sessionId, config) {
283
+ const plansDir = config?.paths?.plans || 'plans';
284
+ const resolution = config?.plan?.resolution || {};
285
+ const order = resolution.order || ['session', 'branch'];
286
+ const branchPattern = resolution.branchPattern;
287
+
288
+ for (const method of order) {
289
+ switch (method) {
290
+ case 'session': {
291
+ const state = readSessionState(sessionId);
292
+ if (state?.activePlan) {
293
+ // Issue #335: Handle both absolute and relative paths
294
+ // - Absolute paths (from updated set-active-plan.cjs): use as-is
295
+ // - Relative paths (legacy): resolve using sessionOrigin if available
296
+ let resolvedPath = state.activePlan;
297
+ if (!path.isAbsolute(resolvedPath) && state.sessionOrigin) {
298
+ // Resolve relative path using session origin directory
299
+ resolvedPath = path.join(state.sessionOrigin, resolvedPath);
300
+ }
301
+ return { path: resolvedPath, resolvedBy: 'session' };
302
+ }
303
+ break;
304
+ }
305
+ case 'branch': {
306
+ try {
307
+ const branch = execSafe('git branch --show-current');
308
+ const slug = extractSlugFromBranch(branch, branchPattern);
309
+ if (slug && fs.existsSync(plansDir)) {
310
+ const entries = fs.readdirSync(plansDir, { withFileTypes: true })
311
+ .filter(e => e.isDirectory() && e.name.includes(slug));
312
+ if (entries.length > 0) {
313
+ return {
314
+ path: path.join(plansDir, entries[entries.length - 1].name),
315
+ resolvedBy: 'branch'
316
+ };
317
+ }
318
+ }
319
+ } catch (e) {
320
+ // Ignore errors reading plans dir
321
+ }
322
+ break;
323
+ }
324
+ // NOTE: 'mostRecent' case intentionally removed - was causing stale plan pollution
325
+ }
326
+ }
327
+ return { path: null, resolvedBy: null };
328
+ }
329
+
330
+ /**
331
+ * Normalize path value (trim, remove trailing slashes, handle empty)
332
+ * @param {string} pathValue - Path to normalize
333
+ * @returns {string|null} Normalized path or null if invalid
334
+ */
335
+ function normalizePath(pathValue) {
336
+ if (!pathValue || typeof pathValue !== 'string') return null;
337
+
338
+ // Trim whitespace
339
+ let normalized = pathValue.trim();
340
+
341
+ // Empty after trim = invalid
342
+ if (!normalized) return null;
343
+
344
+ // Remove trailing slashes (but keep root "/" or "C:\")
345
+ normalized = normalized.replace(/[/\\]+$/, '');
346
+
347
+ // If it became empty (was just slashes), return null
348
+ if (!normalized) return null;
349
+
350
+ return normalized;
351
+ }
352
+
353
+ /**
354
+ * Check if path is absolute
355
+ * @param {string} pathValue - Path to check
356
+ * @returns {boolean} True if absolute path
357
+ */
358
+ function isAbsolutePath(pathValue) {
359
+ if (!pathValue) return false;
360
+ // Unix absolute: starts with /
361
+ // Windows absolute: starts with drive letter (C:\) or UNC (\\)
362
+ return path.isAbsolute(pathValue);
363
+ }
364
+
365
+ /**
366
+ * Sanitize path values
367
+ * - Normalizes path (trim, remove trailing slashes)
368
+ * - Allows absolute paths (for consolidated plans use case)
369
+ * - Prevents obvious security issues (null bytes, etc.)
370
+ *
371
+ * @param {string} pathValue - Path to sanitize
372
+ * @param {string} projectRoot - Project root for relative path resolution
373
+ * @returns {string|null} Sanitized path or null if invalid
374
+ */
375
+ function sanitizePath(pathValue, projectRoot) {
376
+ // Normalize first
377
+ const normalized = normalizePath(pathValue);
378
+ if (!normalized) return null;
379
+
380
+ // Block null bytes and other dangerous chars
381
+ if (/[\x00]/.test(normalized)) return null;
382
+
383
+ // Allow absolute paths (user explicitly wants consolidated plans elsewhere)
384
+ if (isAbsolutePath(normalized)) {
385
+ return normalized;
386
+ }
387
+
388
+ // For relative paths, resolve and validate
389
+ const resolved = path.resolve(projectRoot, normalized);
390
+
391
+ // Prevent path traversal outside project (../ attacks)
392
+ // But allow if user explicitly set absolute path
393
+ if (!resolved.startsWith(projectRoot + path.sep) && resolved !== projectRoot) {
394
+ // This is a relative path trying to escape - block it
395
+ return null;
396
+ }
397
+
398
+ return normalized;
399
+ }
400
+
401
+ /**
402
+ * Validate and sanitize config paths
403
+ */
404
+ function sanitizeConfig(config, projectRoot) {
405
+ const result = { ...config };
406
+
407
+ if (result.plan) {
408
+ result.plan = { ...result.plan };
409
+ if (!sanitizePath(result.plan.reportsDir, projectRoot)) {
410
+ result.plan.reportsDir = DEFAULT_CONFIG.plan.reportsDir;
411
+ }
412
+ // Merge resolution defaults
413
+ result.plan.resolution = {
414
+ ...DEFAULT_CONFIG.plan.resolution,
415
+ ...result.plan.resolution
416
+ };
417
+ // Merge validation defaults
418
+ result.plan.validation = {
419
+ ...DEFAULT_CONFIG.plan.validation,
420
+ ...result.plan.validation
421
+ };
422
+ }
423
+
424
+ if (result.paths) {
425
+ result.paths = { ...result.paths };
426
+ if (!sanitizePath(result.paths.docs, projectRoot)) {
427
+ result.paths.docs = DEFAULT_CONFIG.paths.docs;
428
+ }
429
+ if (!sanitizePath(result.paths.plans, projectRoot)) {
430
+ result.paths.plans = DEFAULT_CONFIG.paths.plans;
431
+ }
432
+ }
433
+
434
+ if (result.locale) {
435
+ result.locale = { ...result.locale };
436
+ }
437
+
438
+ return result;
439
+ }
440
+
441
+ /**
442
+ * Load config with cascading resolution: DEFAULT → global → local
443
+ *
444
+ * Resolution order (each layer overrides the previous):
445
+ * 1. DEFAULT_CONFIG (hardcoded defaults)
446
+ * 2. Global config (~/.claude/.ck.json) - user preferences
447
+ * 3. Local config (./.claude/.ck.json) - project-specific overrides
448
+ *
449
+ * @param {Object} options - Options for config loading
450
+ * @param {boolean} options.includeProject - Include project section (default: true)
451
+ * @param {boolean} options.includeAssertions - Include assertions (default: true)
452
+ * @param {boolean} options.includeLocale - Include locale section (default: true)
453
+ */
454
+ function loadConfig(options = {}) {
455
+ const { includeProject = true, includeAssertions = true, includeLocale = true } = options;
456
+ const projectRoot = process.cwd();
457
+
458
+ // Load configs from both locations
459
+ const globalConfig = loadConfigFromPath(GLOBAL_CONFIG_PATH);
460
+ const localConfig = loadConfigFromPath(LOCAL_CONFIG_PATH);
461
+
462
+ // No config files found - use defaults
463
+ if (!globalConfig && !localConfig) {
464
+ return getDefaultConfig(includeProject, includeAssertions, includeLocale);
465
+ }
466
+
467
+ try {
468
+ // Deep merge: DEFAULT → global → local (local wins)
469
+ let merged = deepMerge({}, DEFAULT_CONFIG);
470
+ if (globalConfig) merged = deepMerge(merged, globalConfig);
471
+ if (localConfig) merged = deepMerge(merged, localConfig);
472
+
473
+ // Build result with optional sections
474
+ const result = {
475
+ plan: merged.plan || DEFAULT_CONFIG.plan,
476
+ paths: merged.paths || DEFAULT_CONFIG.paths,
477
+ docs: merged.docs || DEFAULT_CONFIG.docs
478
+ };
479
+
480
+ if (includeLocale) {
481
+ result.locale = merged.locale || DEFAULT_CONFIG.locale;
482
+ }
483
+ // Always include trust config for verification
484
+ result.trust = merged.trust || DEFAULT_CONFIG.trust;
485
+ if (includeProject) {
486
+ result.project = merged.project || DEFAULT_CONFIG.project;
487
+ }
488
+ if (includeAssertions) {
489
+ result.assertions = merged.assertions || [];
490
+ }
491
+ // Coding level for output style selection (-1 to 5, default: -1 = disabled)
492
+ // -1 = disabled (no injection, saves tokens)
493
+ // 0-5 = inject corresponding level guidelines
494
+ result.codingLevel = merged.codingLevel ?? -1;
495
+ // Skills configuration
496
+ result.skills = merged.skills || DEFAULT_CONFIG.skills;
497
+
498
+ return sanitizeConfig(result, projectRoot);
499
+ } catch (e) {
500
+ return getDefaultConfig(includeProject, includeAssertions, includeLocale);
501
+ }
502
+ }
503
+
504
+ /**
505
+ * Get default config with optional sections
506
+ */
507
+ function getDefaultConfig(includeProject = true, includeAssertions = true, includeLocale = true) {
508
+ const result = {
509
+ plan: { ...DEFAULT_CONFIG.plan },
510
+ paths: { ...DEFAULT_CONFIG.paths },
511
+ docs: { ...DEFAULT_CONFIG.docs },
512
+ codingLevel: -1, // Default: disabled (no injection, saves tokens)
513
+ skills: { ...DEFAULT_CONFIG.skills }
514
+ };
515
+ if (includeLocale) {
516
+ result.locale = { ...DEFAULT_CONFIG.locale };
517
+ }
518
+ if (includeProject) {
519
+ result.project = { ...DEFAULT_CONFIG.project };
520
+ }
521
+ if (includeAssertions) {
522
+ result.assertions = [];
523
+ }
524
+ return result;
525
+ }
526
+
527
+ /**
528
+ * Escape shell special characters for env file values
529
+ * Handles: backslash, double quote, dollar sign, backtick
530
+ */
531
+ function escapeShellValue(str) {
532
+ if (typeof str !== 'string') return str;
533
+ return str
534
+ .replace(/\\/g, '\\\\') // Backslash first
535
+ .replace(/"/g, '\\"') // Double quotes
536
+ .replace(/\$/g, '\\$') // Dollar sign
537
+ .replace(/`/g, '\\`'); // Backticks (command substitution)
538
+ }
539
+
540
+ /**
541
+ * Write environment variable to CLAUDE_ENV_FILE (with escaping)
542
+ */
543
+ function writeEnv(envFile, key, value) {
544
+ if (envFile && value !== null && value !== undefined) {
545
+ const escaped = escapeShellValue(String(value));
546
+ fs.appendFileSync(envFile, `export ${key}="${escaped}"\n`);
547
+ }
548
+ }
549
+
550
+ /**
551
+ * Get reports path based on plan resolution
552
+ * Only uses plan-specific path for 'session' resolved plans (explicitly active)
553
+ * Branch-matched (suggested) plans use default path to avoid pollution
554
+ *
555
+ * @param {string|null} planPath - The plan path
556
+ * @param {string|null} resolvedBy - How plan was resolved ('session'|'branch'|null)
557
+ * @param {Object} planConfig - Plan configuration
558
+ * @param {Object} pathsConfig - Paths configuration
559
+ * @param {string|null} baseDir - Optional base directory for absolute path resolution
560
+ * @returns {string} Reports path (absolute if baseDir provided, relative otherwise)
561
+ */
562
+ function getReportsPath(planPath, resolvedBy, planConfig, pathsConfig, baseDir = null) {
563
+ const reportsDir = normalizePath(planConfig?.reportsDir) || 'reports';
564
+ const plansDir = normalizePath(pathsConfig?.plans) || 'plans';
565
+
566
+ let reportPath;
567
+ // Only use plan-specific reports path if explicitly active (session state)
568
+ // Issue #327: Validate normalized path to prevent whitespace-only paths creating invalid directories
569
+ const normalizedPlanPath = planPath && resolvedBy === 'session' ? normalizePath(planPath) : null;
570
+ if (normalizedPlanPath) {
571
+ reportPath = `${normalizedPlanPath}/${reportsDir}`;
572
+ } else {
573
+ // Default path for no plan or suggested (branch-matched) plans
574
+ reportPath = `${plansDir}/${reportsDir}`;
575
+ }
576
+
577
+ // Return absolute path if baseDir provided
578
+ if (baseDir) {
579
+ return path.join(baseDir, reportPath);
580
+ }
581
+ return reportPath + '/';
582
+ }
583
+
584
+ /**
585
+ * Format issue ID with prefix
586
+ */
587
+ function formatIssueId(issueId, planConfig) {
588
+ if (!issueId) return null;
589
+ return planConfig.issuePrefix ? `${planConfig.issuePrefix}${issueId}` : `#${issueId}`;
590
+ }
591
+
592
+ /**
593
+ * Extract issue ID from branch name
594
+ */
595
+ function extractIssueFromBranch(branch) {
596
+ if (!branch) return null;
597
+ const patterns = [
598
+ /(?:issue|gh|fix|feat|bug)[/-]?(\d+)/i,
599
+ /[/-](\d+)[/-]/,
600
+ /#(\d+)/
601
+ ];
602
+ for (const pattern of patterns) {
603
+ const match = branch.match(pattern);
604
+ if (match) return match[1];
605
+ }
606
+ return null;
607
+ }
608
+
609
+ /**
610
+ * Format date according to dateFormat config
611
+ * Supports: YYMMDD, YYMMDD-HHmm, YYYYMMDD, etc.
612
+ * @param {string} format - Date format string
613
+ * @returns {string} Formatted date
614
+ */
615
+ function formatDate(format) {
616
+ const now = new Date();
617
+ const pad = (n, len = 2) => String(n).padStart(len, '0');
618
+
619
+ const tokens = {
620
+ 'YYYY': now.getFullYear(),
621
+ 'YY': String(now.getFullYear()).slice(-2),
622
+ 'MM': pad(now.getMonth() + 1),
623
+ 'DD': pad(now.getDate()),
624
+ 'HH': pad(now.getHours()),
625
+ 'mm': pad(now.getMinutes()),
626
+ 'ss': pad(now.getSeconds())
627
+ };
628
+
629
+ let result = format;
630
+ for (const [token, value] of Object.entries(tokens)) {
631
+ result = result.replace(token, value);
632
+ }
633
+ return result;
634
+ }
635
+
636
+ /**
637
+ * Validate naming pattern result
638
+ * Ensures pattern resolves to a usable directory name
639
+ *
640
+ * @param {string} pattern - Resolved naming pattern
641
+ * @returns {{ valid: boolean, error?: string }} Validation result
642
+ */
643
+ function validateNamingPattern(pattern) {
644
+ if (!pattern || typeof pattern !== 'string') {
645
+ return { valid: false, error: 'Pattern is empty or not a string' };
646
+ }
647
+
648
+ // After removing {slug} placeholder, should still have content
649
+ const withoutSlug = pattern.replace(/\{slug\}/g, '').replace(/-+/g, '-').replace(/^-|-$/g, '');
650
+ if (!withoutSlug) {
651
+ return { valid: false, error: 'Pattern resolves to empty after removing {slug}' };
652
+ }
653
+
654
+ // Check for remaining unresolved placeholders (besides {slug})
655
+ const unresolvedMatch = withoutSlug.match(/\{[^}]+\}/);
656
+ if (unresolvedMatch) {
657
+ return { valid: false, error: `Unresolved placeholder: ${unresolvedMatch[0]}` };
658
+ }
659
+
660
+ // Pattern must contain {slug} for agents to substitute
661
+ if (!pattern.includes('{slug}')) {
662
+ return { valid: false, error: 'Pattern must contain {slug} placeholder' };
663
+ }
664
+
665
+ return { valid: true };
666
+ }
667
+
668
+ /**
669
+ * Resolve naming pattern with date and optional issue prefix
670
+ * Keeps {slug} as placeholder for agents to substitute
671
+ *
672
+ * Example: namingFormat="{date}-{issue}-{slug}", dateFormat="YYMMDD-HHmm", issue="GH-88"
673
+ * Returns: "251212-1830-GH-88-{slug}" (if issue exists)
674
+ * Returns: "251212-1830-{slug}" (if no issue)
675
+ *
676
+ * @param {Object} planConfig - Plan configuration
677
+ * @param {string|null} gitBranch - Current git branch (for issue extraction)
678
+ * @returns {string} Resolved naming pattern with {slug} placeholder
679
+ */
680
+ function resolveNamingPattern(planConfig, gitBranch) {
681
+ const { namingFormat, dateFormat, issuePrefix } = planConfig;
682
+ const formattedDate = formatDate(dateFormat);
683
+
684
+ // Try to extract issue ID from branch name
685
+ const issueId = extractIssueFromBranch(gitBranch);
686
+ const fullIssue = issueId && issuePrefix ? `${issuePrefix}${issueId}` : null;
687
+
688
+ // Build pattern by substituting {date} and {issue}, keep {slug}
689
+ let pattern = namingFormat;
690
+ pattern = pattern.replace('{date}', formattedDate);
691
+
692
+ if (fullIssue) {
693
+ pattern = pattern.replace('{issue}', fullIssue);
694
+ } else {
695
+ // Remove {issue} and any trailing/leading dash
696
+ pattern = pattern.replace(/-?\{issue\}-?/, '-').replace(/--+/g, '-');
697
+ }
698
+
699
+ // Clean up the result:
700
+ // - Remove leading/trailing hyphens
701
+ // - Collapse multiple hyphens (except around {slug})
702
+ pattern = pattern
703
+ .replace(/^-+/, '') // Remove leading hyphens
704
+ .replace(/-+$/, '') // Remove trailing hyphens
705
+ .replace(/-+(\{slug\})/g, '-$1') // Single hyphen before {slug}
706
+ .replace(/(\{slug\})-+/g, '$1-') // Single hyphen after {slug}
707
+ .replace(/--+/g, '-'); // Collapse other multiple hyphens
708
+
709
+ // Validate the resulting pattern
710
+ const validation = validateNamingPattern(pattern);
711
+ if (!validation.valid) {
712
+ // Log warning but return pattern anyway (fail-safe)
713
+ if (process.env.CK_DEBUG) {
714
+ console.error(`[ck-config] Warning: ${validation.error}`);
715
+ }
716
+ }
717
+
718
+ return pattern;
719
+ }
720
+
721
+ /**
722
+ * Get current git branch (safe execution)
723
+ * @param {string|null} cwd - Working directory to run git command from (optional)
724
+ * @returns {string|null} Current branch name or null
725
+ */
726
+ function getGitBranch(cwd = null) {
727
+ return execSafe('git branch --show-current', { cwd: cwd || undefined });
728
+ }
729
+
730
+ /**
731
+ * Get git repository root directory
732
+ * @param {string|null} cwd - Working directory to run git command from (optional)
733
+ * @returns {string|null} Git root absolute path or null if not in git repo
734
+ */
735
+ function getGitRoot(cwd = null) {
736
+ return execSafe('git rev-parse --show-toplevel', { cwd: cwd || undefined });
737
+ }
738
+
739
+ module.exports = {
740
+ CONFIG_PATH,
741
+ LOCAL_CONFIG_PATH,
742
+ GLOBAL_CONFIG_PATH,
743
+ DEFAULT_CONFIG,
744
+ INVALID_FILENAME_CHARS,
745
+ deepMerge,
746
+ loadConfigFromPath,
747
+ loadConfig,
748
+ normalizePath,
749
+ isAbsolutePath,
750
+ sanitizePath,
751
+ sanitizeSlug,
752
+ sanitizeConfig,
753
+ escapeShellValue,
754
+ writeEnv,
755
+ getSessionTempPath,
756
+ readSessionState,
757
+ writeSessionState,
758
+ resolvePlanPath,
759
+ extractSlugFromBranch,
760
+ findMostRecentPlan,
761
+ getReportsPath,
762
+ formatIssueId,
763
+ extractIssueFromBranch,
764
+ formatDate,
765
+ validateNamingPattern,
766
+ resolveNamingPattern,
767
+ getGitBranch,
768
+ getGitRoot
769
+ };