@ngxtm/devkit 2.1.0 → 3.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 (533) hide show
  1. package/cli/detect.js +292 -0
  2. package/cli/index.js +204 -92
  3. package/cli/init.js +245 -0
  4. package/cli/update.js +243 -0
  5. package/cli/utils.js +195 -0
  6. package/package.json +16 -20
  7. package/rules-index.json +76 -0
  8. package/scripts/generate-index.js +223 -0
  9. package/scripts/merge-commands.js +290 -0
  10. package/scripts/organize-rules.js +226 -0
  11. package/templates/base/hooks/lib/ck-config-utils.cjs +769 -0
  12. package/templates/base/hooks/lib/ck-paths.cjs +110 -0
  13. package/templates/base/hooks/lib/colors.cjs +95 -0
  14. package/templates/base/hooks/lib/config-counter.cjs +103 -0
  15. package/templates/base/hooks/lib/context-builder.cjs +600 -0
  16. package/templates/base/hooks/lib/context-tracker.cjs +335 -0
  17. package/templates/base/hooks/lib/privacy-checker.cjs +297 -0
  18. package/templates/base/hooks/lib/project-detector.cjs +430 -0
  19. package/templates/base/hooks/lib/scout-checker.cjs +172 -0
  20. package/templates/base/hooks/lib/transcript-parser.cjs +164 -0
  21. package/templates/base/hooks/privacy-block.cjs +145 -0
  22. package/agents/backend-engineer.md +0 -154
  23. package/agents/brainstormer.md +0 -169
  24. package/agents/business-analyst.md +0 -166
  25. package/agents/database-architect.md +0 -159
  26. package/agents/debugger.md +0 -155
  27. package/agents/designer.md +0 -150
  28. package/agents/devops-engineer.md +0 -155
  29. package/agents/docs-manager.md +0 -171
  30. package/agents/frontend-engineer.md +0 -159
  31. package/agents/game-engineer.md +0 -148
  32. package/agents/mobile-engineer.md +0 -149
  33. package/agents/performance-engineer.md +0 -152
  34. package/agents/planner.md +0 -161
  35. package/agents/project-manager.md +0 -160
  36. package/agents/researcher.md +0 -146
  37. package/agents/reviewer.md +0 -155
  38. package/agents/scouter.md +0 -157
  39. package/agents/security-engineer.md +0 -154
  40. package/agents/tech-lead.md +0 -159
  41. package/agents/tester.md +0 -157
  42. package/agents-claudekit/brainstormer.md +0 -113
  43. package/agents-claudekit/code-reviewer.md +0 -157
  44. package/agents-claudekit/code-simplifier.md +0 -42
  45. package/agents-claudekit/copywriter.md +0 -110
  46. package/agents-claudekit/database-admin.md +0 -92
  47. package/agents-claudekit/debugger.md +0 -137
  48. package/agents-claudekit/docs-manager.md +0 -208
  49. package/agents-claudekit/fullstack-developer.md +0 -95
  50. package/agents-claudekit/git-manager.md +0 -394
  51. package/agents-claudekit/journal-writer.md +0 -113
  52. package/agents-claudekit/mcp-manager.md +0 -93
  53. package/agents-claudekit/planner.md +0 -108
  54. package/agents-claudekit/project-manager.md +0 -125
  55. package/agents-claudekit/researcher.md +0 -38
  56. package/agents-claudekit/scout-external.md +0 -141
  57. package/agents-claudekit/scout.md +0 -107
  58. package/agents-claudekit/tester.md +0 -105
  59. package/agents-claudekit/ui-ux-designer.md +0 -236
  60. package/commands/ask.md +0 -64
  61. package/commands/brainstorm.md +0 -64
  62. package/commands/code.md +0 -64
  63. package/commands/cook.md +0 -64
  64. package/commands/debug.md +0 -64
  65. package/commands/design/fast.md +0 -134
  66. package/commands/fix/fast.md +0 -84
  67. package/commands/fix/hard.md +0 -116
  68. package/commands/fix.md +0 -64
  69. package/commands/plan/fast.md +0 -78
  70. package/commands/plan/hard.md +0 -131
  71. package/commands/plan.md +0 -64
  72. package/commands/test.md +0 -64
  73. package/matrix-skills/_index.yaml +0 -275
  74. package/matrix-skills/ai-ml.yaml +0 -353
  75. package/matrix-skills/architecture.yaml +0 -93
  76. package/matrix-skills/backend.yaml +0 -280
  77. package/matrix-skills/cloud.yaml +0 -112
  78. package/matrix-skills/data.yaml +0 -74
  79. package/matrix-skills/design.yaml +0 -98
  80. package/matrix-skills/devops.yaml +0 -200
  81. package/matrix-skills/frontend.yaml +0 -200
  82. package/matrix-skills/gaming.yaml +0 -39
  83. package/matrix-skills/languages.yaml +0 -160
  84. package/matrix-skills/management.yaml +0 -50
  85. package/matrix-skills/mcp.yaml +0 -82
  86. package/matrix-skills/mobile.yaml +0 -85
  87. package/matrix-skills/performance.yaml +0 -23
  88. package/matrix-skills/planning.yaml +0 -117
  89. package/matrix-skills/quality.yaml +0 -195
  90. package/matrix-skills/research.yaml +0 -106
  91. package/matrix-skills/security.yaml +0 -293
  92. package/matrix-skills/tools.yaml +0 -352
  93. package/output-styles/coding-level-0-eli5.md +0 -103
  94. package/output-styles/coding-level-1-junior.md +0 -124
  95. package/output-styles/coding-level-2-mid.md +0 -146
  96. package/output-styles/coding-level-3-senior.md +0 -148
  97. package/output-styles/coding-level-4-lead.md +0 -159
  98. package/output-styles/coding-level-5-god.md +0 -91
  99. package/rules/README.md +0 -141
  100. package/rules/metadata.json +0 -54
  101. package/settings.json +0 -3
  102. package/statusline.cjs +0 -500
  103. package/statusline.ps1 +0 -307
  104. package/statusline.sh +0 -237
  105. package/workflows/development-rules.md +0 -42
  106. package/workflows/documentation-management.md +0 -121
  107. package/workflows/orchestration-protocol.md +0 -16
  108. package/workflows/primary-workflow.md +0 -45
  109. /package/{commands → merged-commands}/ask/fast.md +0 -0
  110. /package/{commands → merged-commands}/ask/hard.md +0 -0
  111. /package/{commands-claudekit → merged-commands}/ask.md +0 -0
  112. /package/{commands → merged-commands}/auto.md +0 -0
  113. /package/{commands-claudekit → merged-commands}/bootstrap/auto/fast.md +0 -0
  114. /package/{commands-claudekit → merged-commands}/bootstrap/auto/parallel.md +0 -0
  115. /package/{commands-claudekit → merged-commands}/bootstrap/auto.md +0 -0
  116. /package/{commands-claudekit → merged-commands}/bootstrap.md +0 -0
  117. /package/{commands → merged-commands}/brainstorm/fast.md +0 -0
  118. /package/{commands → merged-commands}/brainstorm/hard.md +0 -0
  119. /package/{commands-claudekit → merged-commands}/brainstorm.md +0 -0
  120. /package/{commands-claudekit → merged-commands}/ck-help.md +0 -0
  121. /package/{commands-claudekit → merged-commands}/code/auto.md +0 -0
  122. /package/{commands → merged-commands}/code/fast.md +0 -0
  123. /package/{commands → merged-commands}/code/hard.md +0 -0
  124. /package/{commands-claudekit → merged-commands}/code/no-test.md +0 -0
  125. /package/{commands-claudekit → merged-commands}/code/parallel.md +0 -0
  126. /package/{commands-claudekit → merged-commands}/code.md +0 -0
  127. /package/{commands-claudekit → merged-commands}/coding-level.md +0 -0
  128. /package/{commands-claudekit → merged-commands}/content/cro.md +0 -0
  129. /package/{commands-claudekit → merged-commands}/content/enhance.md +0 -0
  130. /package/{commands-claudekit → merged-commands}/content/fast.md +0 -0
  131. /package/{commands-claudekit → merged-commands}/content/good.md +0 -0
  132. /package/{commands-claudekit → merged-commands}/cook/auto/fast.md +0 -0
  133. /package/{commands-claudekit → merged-commands}/cook/auto/parallel.md +0 -0
  134. /package/{commands-claudekit → merged-commands}/cook/auto.md +0 -0
  135. /package/{commands → merged-commands}/cook/fast.md +0 -0
  136. /package/{commands → merged-commands}/cook/hard.md +0 -0
  137. /package/{commands-claudekit → merged-commands}/cook.md +0 -0
  138. /package/{commands → merged-commands}/debug/fast.md +0 -0
  139. /package/{commands → merged-commands}/debug/hard.md +0 -0
  140. /package/{commands-claudekit → merged-commands}/debug.md +0 -0
  141. /package/{commands → merged-commands}/deploy/check.md +0 -0
  142. /package/{commands → merged-commands}/deploy/preview.md +0 -0
  143. /package/{commands → merged-commands}/deploy/production.md +0 -0
  144. /package/{commands → merged-commands}/deploy/rollback.md +0 -0
  145. /package/{commands → merged-commands}/deploy.md +0 -0
  146. /package/{commands-claudekit → merged-commands}/design/3d.md +0 -0
  147. /package/{commands-claudekit → merged-commands}/design/describe.md +0 -0
  148. /package/{commands-claudekit → merged-commands}/design/fast.md +0 -0
  149. /package/{commands-claudekit → merged-commands}/design/good.md +0 -0
  150. /package/{commands → merged-commands}/design/hard.md +0 -0
  151. /package/{commands-claudekit → merged-commands}/design/screenshot.md +0 -0
  152. /package/{commands-claudekit → merged-commands}/design/video.md +0 -0
  153. /package/{commands → merged-commands}/design.md +0 -0
  154. /package/{commands → merged-commands}/docs/audit.md +0 -0
  155. /package/{commands → merged-commands}/docs/business.md +0 -0
  156. /package/{commands → merged-commands}/docs/core.md +0 -0
  157. /package/{commands-claudekit → merged-commands}/docs/init.md +0 -0
  158. /package/{commands-claudekit → merged-commands}/docs/summarize.md +0 -0
  159. /package/{commands-claudekit → merged-commands}/docs/update.md +0 -0
  160. /package/{commands → merged-commands}/docs.md +0 -0
  161. /package/{commands-claudekit → merged-commands}/fix/ci.md +0 -0
  162. /package/{commands-claudekit → merged-commands}/fix/fast.md +0 -0
  163. /package/{commands-claudekit → merged-commands}/fix/hard.md +0 -0
  164. /package/{commands-claudekit → merged-commands}/fix/logs.md +0 -0
  165. /package/{commands-claudekit → merged-commands}/fix/parallel.md +0 -0
  166. /package/{commands-claudekit → merged-commands}/fix/test.md +0 -0
  167. /package/{commands-claudekit → merged-commands}/fix/types.md +0 -0
  168. /package/{commands-claudekit → merged-commands}/fix/ui.md +0 -0
  169. /package/{commands-claudekit → merged-commands}/fix.md +0 -0
  170. /package/{commands-claudekit → merged-commands}/git/cm.md +0 -0
  171. /package/{commands-claudekit → merged-commands}/git/cp.md +0 -0
  172. /package/{commands-claudekit → merged-commands}/git/merge.md +0 -0
  173. /package/{commands-claudekit → merged-commands}/git/pr.md +0 -0
  174. /package/{commands-claudekit → merged-commands}/integrate/polar.md +0 -0
  175. /package/{commands-claudekit → merged-commands}/integrate/sepay.md +0 -0
  176. /package/{commands-claudekit → merged-commands}/journal.md +0 -0
  177. /package/{commands-claudekit → merged-commands}/kanban.md +0 -0
  178. /package/{commands-claudekit → merged-commands}/plan/archive.md +0 -0
  179. /package/{commands-claudekit → merged-commands}/plan/ci.md +0 -0
  180. /package/{commands-claudekit → merged-commands}/plan/cro.md +0 -0
  181. /package/{commands-claudekit → merged-commands}/plan/fast.md +0 -0
  182. /package/{commands-claudekit → merged-commands}/plan/hard.md +0 -0
  183. /package/{commands-claudekit → merged-commands}/plan/parallel.md +0 -0
  184. /package/{commands-claudekit → merged-commands}/plan/two.md +0 -0
  185. /package/{commands-claudekit → merged-commands}/plan/validate.md +0 -0
  186. /package/{commands-claudekit → merged-commands}/plan.md +0 -0
  187. /package/{commands-claudekit → merged-commands}/preview.md +0 -0
  188. /package/{commands-claudekit → merged-commands}/review/codebase/parallel.md +0 -0
  189. /package/{commands-claudekit → merged-commands}/review/codebase.md +0 -0
  190. /package/{commands → merged-commands}/review/fast.md +0 -0
  191. /package/{commands → merged-commands}/review/hard.md +0 -0
  192. /package/{commands → merged-commands}/review.md +0 -0
  193. /package/{commands-claudekit → merged-commands}/scout/ext.md +0 -0
  194. /package/{commands-claudekit → merged-commands}/scout.md +0 -0
  195. /package/{commands-claudekit → merged-commands}/skill/add.md +0 -0
  196. /package/{commands-claudekit → merged-commands}/skill/create.md +0 -0
  197. /package/{commands-claudekit → merged-commands}/skill/fix-logs.md +0 -0
  198. /package/{commands-claudekit → merged-commands}/skill/optimize/auto.md +0 -0
  199. /package/{commands-claudekit → merged-commands}/skill/optimize.md +0 -0
  200. /package/{commands-claudekit → merged-commands}/skill/plan.md +0 -0
  201. /package/{commands-claudekit → merged-commands}/skill/update.md +0 -0
  202. /package/{commands → merged-commands}/test/fast.md +0 -0
  203. /package/{commands → merged-commands}/test/hard.md +0 -0
  204. /package/{commands-claudekit → merged-commands}/test/ui.md +0 -0
  205. /package/{commands-claudekit → merged-commands}/test.md +0 -0
  206. /package/{commands-claudekit → merged-commands}/use-mcp.md +0 -0
  207. /package/{commands-claudekit → merged-commands}/watzup.md +0 -0
  208. /package/{commands-claudekit → merged-commands}/worktree.md +0 -0
  209. /package/{rules → templates/dart/rules}/dart/best-practices/SKILL.md +0 -0
  210. /package/{rules → templates/dart/rules}/dart/language/SKILL.md +0 -0
  211. /package/{rules → templates/dart/rules}/dart/tooling/SKILL.md +0 -0
  212. /package/{rules → templates/flutter/rules}/flutter/auto-route-navigation/SKILL.md +0 -0
  213. /package/{rules → templates/flutter/rules}/flutter/auto-route-navigation/references/REFERENCE.md +0 -0
  214. /package/{rules → templates/flutter/rules}/flutter/auto-route-navigation/references/router-config.md +0 -0
  215. /package/{rules → templates/flutter/rules}/flutter/bloc-state-management/SKILL.md +0 -0
  216. /package/{rules → templates/flutter/rules}/flutter/bloc-state-management/references/REFERENCE.md +0 -0
  217. /package/{rules → templates/flutter/rules}/flutter/bloc-state-management/references/auth-bloc-example.md +0 -0
  218. /package/{rules → templates/flutter/rules}/flutter/bloc-state-management/references/equatable-usage.md +0 -0
  219. /package/{rules → templates/flutter/rules}/flutter/bloc-state-management/references/property-based-state.md +0 -0
  220. /package/{rules → templates/flutter/rules}/flutter/bloc.rule.md +0 -0
  221. /package/{rules → templates/flutter/rules}/flutter/cicd/SKILL.md +0 -0
  222. /package/{rules → templates/flutter/rules}/flutter/cicd/references/advanced-workflow.md +0 -0
  223. /package/{rules → templates/flutter/rules}/flutter/cicd/references/fastlane.md +0 -0
  224. /package/{rules → templates/flutter/rules}/flutter/cicd/references/github-actions.md +0 -0
  225. /package/{rules → templates/flutter/rules}/flutter/dependency-injection/SKILL.md +0 -0
  226. /package/{rules → templates/flutter/rules}/flutter/dependency-injection/references/REFERENCE.md +0 -0
  227. /package/{rules → templates/flutter/rules}/flutter/dependency-injection/references/modules.md +0 -0
  228. /package/{rules → templates/flutter/rules}/flutter/error-handling/SKILL.md +0 -0
  229. /package/{rules → templates/flutter/rules}/flutter/error-handling/references/REFERENCE.md +0 -0
  230. /package/{rules → templates/flutter/rules}/flutter/error-handling/references/error-mapping.md +0 -0
  231. /package/{rules → templates/flutter/rules}/flutter/feature-based-clean-architecture/SKILL.md +0 -0
  232. /package/{rules → templates/flutter/rules}/flutter/feature-based-clean-architecture/references/REFERENCE.md +0 -0
  233. /package/{rules → templates/flutter/rules}/flutter/feature-based-clean-architecture/references/folder-structure.md +0 -0
  234. /package/{rules → templates/flutter/rules}/flutter/getx-navigation/SKILL.md +0 -0
  235. /package/{rules → templates/flutter/rules}/flutter/getx-navigation/references/app-pages.md +0 -0
  236. /package/{rules → templates/flutter/rules}/flutter/getx-navigation/references/middleware-example.md +0 -0
  237. /package/{rules → templates/flutter/rules}/flutter/getx-state-management/SKILL.md +0 -0
  238. /package/{rules → templates/flutter/rules}/flutter/getx-state-management/references/binding-example.md +0 -0
  239. /package/{rules → templates/flutter/rules}/flutter/getx-state-management/references/reactive-vs-simple.md +0 -0
  240. /package/{rules → templates/flutter/rules}/flutter/go-router-navigation/SKILL.md +0 -0
  241. /package/{rules → templates/flutter/rules}/flutter/idiomatic-flutter/SKILL.md +0 -0
  242. /package/{rules → templates/flutter/rules}/flutter/layer-based-clean-architecture/SKILL.md +0 -0
  243. /package/{rules → templates/flutter/rules}/flutter/layer-based-clean-architecture/references/REFERENCE.md +0 -0
  244. /package/{rules → templates/flutter/rules}/flutter/layer-based-clean-architecture/references/repository-mapping.md +0 -0
  245. /package/{rules → templates/flutter/rules}/flutter/localization/SKILL.md +0 -0
  246. /package/{rules → templates/flutter/rules}/flutter/localization/references/REFERENCE.md +0 -0
  247. /package/{rules → templates/flutter/rules}/flutter/localization/references/sheet-loader.md +0 -0
  248. /package/{rules → templates/flutter/rules}/flutter/navigator-v1-navigation/SKILL.md +0 -0
  249. /package/{rules → templates/flutter/rules}/flutter/navigator-v1-navigation/references/on-generate-route.md +0 -0
  250. /package/{rules → templates/flutter/rules}/flutter/performance/SKILL.md +0 -0
  251. /package/{rules → templates/flutter/rules}/flutter/retrofit-networking/SKILL.md +0 -0
  252. /package/{rules → templates/flutter/rules}/flutter/retrofit-networking/references/REFERENCE.md +0 -0
  253. /package/{rules → templates/flutter/rules}/flutter/retrofit-networking/references/token-refresh.md +0 -0
  254. /package/{rules → templates/flutter/rules}/flutter/riverpod-state-management/SKILL.md +0 -0
  255. /package/{rules → templates/flutter/rules}/flutter/riverpod-state-management/references/architecture.md +0 -0
  256. /package/{rules → templates/flutter/rules}/flutter/riverpod-state-management/references/best-practices.md +0 -0
  257. /package/{rules → templates/flutter/rules}/flutter/riverpod-state-management/references/testing.md +0 -0
  258. /package/{rules → templates/flutter/rules}/flutter/riverpod.rule.md +0 -0
  259. /package/{rules → templates/flutter/rules}/flutter/security/SKILL.md +0 -0
  260. /package/{rules → templates/flutter/rules}/flutter/security/references/REFERENCE.md +0 -0
  261. /package/{rules → templates/flutter/rules}/flutter/security/references/network-security.md +0 -0
  262. /package/{rules → templates/flutter/rules}/flutter/testing/SKILL.md +0 -0
  263. /package/{rules → templates/flutter/rules}/flutter/testing/references/REFERENCE.md +0 -0
  264. /package/{rules → templates/flutter/rules}/flutter/testing/references/bloc-testing.md +0 -0
  265. /package/{rules → templates/flutter/rules}/flutter/testing/references/integration-testing.md +0 -0
  266. /package/{rules → templates/flutter/rules}/flutter/testing/references/robot-pattern.md +0 -0
  267. /package/{rules → templates/flutter/rules}/flutter/testing/references/unit-testing.md +0 -0
  268. /package/{rules → templates/flutter/rules}/flutter/testing/references/widget-testing.md +0 -0
  269. /package/{rules → templates/flutter/rules}/flutter/widgets/SKILL.md +0 -0
  270. /package/{rules → templates/golang/rules}/golang/chi-router/SKILL.md +0 -0
  271. /package/{rules → templates/golang/rules}/golang/chi-router/references/REFERENCE.md +0 -0
  272. /package/{rules → templates/golang/rules}/golang/chi-router/references/routing-patterns.md +0 -0
  273. /package/{rules → templates/golang/rules}/golang/cobra-cli/SKILL.md +0 -0
  274. /package/{rules → templates/golang/rules}/golang/cobra-cli/references/REFERENCE.md +0 -0
  275. /package/{rules → templates/golang/rules}/golang/cobra-cli/references/command-patterns.md +0 -0
  276. /package/{rules → templates/golang/rules}/golang/core/SKILL.md +0 -0
  277. /package/{rules → templates/golang/rules}/golang/core/references/REFERENCE.md +0 -0
  278. /package/{rules → templates/golang/rules}/golang/core/references/concurrency-patterns.md +0 -0
  279. /package/{rules → templates/golang/rules}/golang/core/references/error-handling.md +0 -0
  280. /package/{rules → templates/golang/rules}/golang/echo-framework/SKILL.md +0 -0
  281. /package/{rules → templates/golang/rules}/golang/echo-framework/references/REFERENCE.md +0 -0
  282. /package/{rules → templates/golang/rules}/golang/echo-framework/references/middleware-patterns.md +0 -0
  283. /package/{rules → templates/golang/rules}/golang/echo-framework/references/routing-patterns.md +0 -0
  284. /package/{rules → templates/golang/rules}/golang/ent-orm/SKILL.md +0 -0
  285. /package/{rules → templates/golang/rules}/golang/ent-orm/references/REFERENCE.md +0 -0
  286. /package/{rules → templates/golang/rules}/golang/ent-orm/references/schema-patterns.md +0 -0
  287. /package/{rules → templates/golang/rules}/golang/fiber-framework/SKILL.md +0 -0
  288. /package/{rules → templates/golang/rules}/golang/fiber-framework/references/REFERENCE.md +0 -0
  289. /package/{rules → templates/golang/rules}/golang/fiber-framework/references/routing-patterns.md +0 -0
  290. /package/{rules → templates/golang/rules}/golang/gin-framework/SKILL.md +0 -0
  291. /package/{rules → templates/golang/rules}/golang/gin-framework/references/REFERENCE.md +0 -0
  292. /package/{rules → templates/golang/rules}/golang/gin-framework/references/middleware-patterns.md +0 -0
  293. /package/{rules → templates/golang/rules}/golang/gorm-orm/SKILL.md +0 -0
  294. /package/{rules → templates/golang/rules}/golang/gorm-orm/references/REFERENCE.md +0 -0
  295. /package/{rules → templates/golang/rules}/golang/gorm-orm/references/model-definitions.md +0 -0
  296. /package/{rules → templates/golang/rules}/golang/gorm-orm/references/query-patterns.md +0 -0
  297. /package/{rules → templates/golang/rules}/golang/grpc/SKILL.md +0 -0
  298. /package/{rules → templates/golang/rules}/golang/grpc/references/REFERENCE.md +0 -0
  299. /package/{rules → templates/golang/rules}/golang/grpc/references/service-patterns.md +0 -0
  300. /package/{rules → templates/golang/rules}/golang/testify/SKILL.md +0 -0
  301. /package/{rules → templates/golang/rules}/golang/testify/references/REFERENCE.md +0 -0
  302. /package/{rules → templates/golang/rules}/golang/testify/references/assert-patterns.md +0 -0
  303. /package/{rules → templates/golang/rules}/golang/validator/SKILL.md +0 -0
  304. /package/{rules → templates/golang/rules}/golang/validator/references/REFERENCE.md +0 -0
  305. /package/{rules → templates/golang/rules}/golang/validator/references/validation-tags.md +0 -0
  306. /package/{rules → templates/golang/rules}/golang/viper-config/SKILL.md +0 -0
  307. /package/{rules → templates/golang/rules}/golang/viper-config/references/REFERENCE.md +0 -0
  308. /package/{rules → templates/golang/rules}/golang/viper-config/references/config-loading.md +0 -0
  309. /package/{rules → templates/golang/rules}/golang/wire-di/SKILL.md +0 -0
  310. /package/{rules → templates/golang/rules}/golang/wire-di/references/REFERENCE.md +0 -0
  311. /package/{rules → templates/golang/rules}/golang/wire-di/references/provider-patterns.md +0 -0
  312. /package/{rules → templates/golang/rules}/golang/zap-logging/SKILL.md +0 -0
  313. /package/{rules → templates/golang/rules}/golang/zap-logging/references/REFERENCE.md +0 -0
  314. /package/{rules → templates/golang/rules}/golang/zap-logging/references/logger-config.md +0 -0
  315. /package/{rules → templates/java/rules}/java/build-gradle/SKILL.md +0 -0
  316. /package/{rules → templates/java/rules}/java/build-gradle/references/REFERENCE.md +0 -0
  317. /package/{rules → templates/java/rules}/java/build-gradle/references/kotlin-dsl.md +0 -0
  318. /package/{rules → templates/java/rules}/java/build-gradle/references/task-configuration.md +0 -0
  319. /package/{rules → templates/java/rules}/java/build-maven/SKILL.md +0 -0
  320. /package/{rules → templates/java/rules}/java/build-maven/references/REFERENCE.md +0 -0
  321. /package/{rules → templates/java/rules}/java/build-maven/references/dependency-management.md +0 -0
  322. /package/{rules → templates/java/rules}/java/build-maven/references/lifecycle-phases.md +0 -0
  323. /package/{rules → templates/java/rules}/java/graalvm-native/SKILL.md +0 -0
  324. /package/{rules → templates/java/rules}/java/graalvm-native/references/REFERENCE.md +0 -0
  325. /package/{rules → templates/java/rules}/java/java-collections-streams/SKILL.md +0 -0
  326. /package/{rules → templates/java/rules}/java/java-collections-streams/references/REFERENCE.md +0 -0
  327. /package/{rules → templates/java/rules}/java/java-collections-streams/references/collectors-patterns.md +0 -0
  328. /package/{rules → templates/java/rules}/java/java-collections-streams/references/stream-pipelines.md +0 -0
  329. /package/{rules → templates/java/rules}/java/java-concurrency/SKILL.md +0 -0
  330. /package/{rules → templates/java/rules}/java/java-concurrency/references/REFERENCE.md +0 -0
  331. /package/{rules → templates/java/rules}/java/java-concurrency/references/completable-future.md +0 -0
  332. /package/{rules → templates/java/rules}/java/java-concurrency/references/executor-patterns.md +0 -0
  333. /package/{rules → templates/java/rules}/java/java-concurrency/references/virtual-threads.md +0 -0
  334. /package/{rules → templates/java/rules}/java/java-core-language/SKILL.md +0 -0
  335. /package/{rules → templates/java/rules}/java/java-core-language/references/REFERENCE.md +0 -0
  336. /package/{rules → templates/java/rules}/java/java-core-language/references/jvm-memory-model.md +0 -0
  337. /package/{rules → templates/java/rules}/java/java-core-language/references/modern-java-features.md +0 -0
  338. /package/{rules → templates/java/rules}/java/java-project-structure/SKILL.md +0 -0
  339. /package/{rules → templates/java/rules}/java/java-project-structure/references/REFERENCE.md +0 -0
  340. /package/{rules → templates/java/rules}/java/java-project-structure/references/maven-project-layout.md +0 -0
  341. /package/{rules → templates/java/rules}/java/java-project-structure/references/module-system.md +0 -0
  342. /package/{rules → templates/java/rules}/java/micronaut-core/SKILL.md +0 -0
  343. /package/{rules → templates/java/rules}/java/micronaut-core/references/REFERENCE.md +0 -0
  344. /package/{rules → templates/java/rules}/java/micronaut-reactive/SKILL.md +0 -0
  345. /package/{rules → templates/java/rules}/java/micronaut-reactive/references/REFERENCE.md +0 -0
  346. /package/{rules → templates/java/rules}/java/quarkus-core/SKILL.md +0 -0
  347. /package/{rules → templates/java/rules}/java/quarkus-core/references/REFERENCE.md +0 -0
  348. /package/{rules → templates/java/rules}/java/quarkus-reactive/SKILL.md +0 -0
  349. /package/{rules → templates/java/rules}/java/quarkus-reactive/references/REFERENCE.md +0 -0
  350. /package/{rules → templates/java/rules}/java/spring-batch/SKILL.md +0 -0
  351. /package/{rules → templates/java/rules}/java/spring-batch/references/REFERENCE.md +0 -0
  352. /package/{rules → templates/java/rules}/java/spring-boot-architecture/SKILL.md +0 -0
  353. /package/{rules → templates/java/rules}/java/spring-boot-architecture/references/REFERENCE.md +0 -0
  354. /package/{rules → templates/java/rules}/java/spring-boot-architecture/references/auto-configuration.md +0 -0
  355. /package/{rules → templates/java/rules}/java/spring-boot-architecture/references/configuration-properties.md +0 -0
  356. /package/{rules → templates/java/rules}/java/spring-boot-web/SKILL.md +0 -0
  357. /package/{rules → templates/java/rules}/java/spring-boot-web/references/REFERENCE.md +0 -0
  358. /package/{rules → templates/java/rules}/java/spring-cloud/SKILL.md +0 -0
  359. /package/{rules → templates/java/rules}/java/spring-cloud/references/REFERENCE.md +0 -0
  360. /package/{rules → templates/java/rules}/java/spring-data-jpa/SKILL.md +0 -0
  361. /package/{rules → templates/java/rules}/java/spring-data-jpa/references/REFERENCE.md +0 -0
  362. /package/{rules → templates/java/rules}/java/spring-security/SKILL.md +0 -0
  363. /package/{rules → templates/java/rules}/java/spring-security/references/REFERENCE.md +0 -0
  364. /package/{rules → templates/java/rules}/java/spring-security/references/jwt-auth-flow.md +0 -0
  365. /package/{rules → templates/java/rules}/java/testing-junit-mockito/SKILL.md +0 -0
  366. /package/{rules → templates/java/rules}/java/testing-junit-mockito/references/REFERENCE.md +0 -0
  367. /package/{rules → templates/java/rules}/java/testing-junit-mockito/references/junit5-patterns.md +0 -0
  368. /package/{rules → templates/java/rules}/java/testing-junit-mockito/references/mockito-patterns.md +0 -0
  369. /package/{rules → templates/java/rules}/java/testing-junit-mockito/references/spring-boot-testing.md +0 -0
  370. /package/{rules → templates/javascript/rules}/javascript/best-practices/SKILL.md +0 -0
  371. /package/{rules → templates/javascript/rules}/javascript/best-practices/references/REFERENCE.md +0 -0
  372. /package/{rules → templates/javascript/rules}/javascript/language/SKILL.md +0 -0
  373. /package/{rules → templates/javascript/rules}/javascript/language/references/REFERENCE.md +0 -0
  374. /package/{rules → templates/javascript/rules}/javascript/tooling/SKILL.md +0 -0
  375. /package/{rules → templates/javascript/rules}/javascript/tooling/references/REFERENCE.md +0 -0
  376. /package/{rules → templates/nestjs/rules}/nestjs/api-standards/SKILL.md +0 -0
  377. /package/{rules → templates/nestjs/rules}/nestjs/api-standards/references/pagination-wrapper.md +0 -0
  378. /package/{rules → templates/nestjs/rules}/nestjs/architecture/SKILL.md +0 -0
  379. /package/{rules → templates/nestjs/rules}/nestjs/architecture/references/dynamic-module.md +0 -0
  380. /package/{rules → templates/nestjs/rules}/nestjs/caching/SKILL.md +0 -0
  381. /package/{rules → templates/nestjs/rules}/nestjs/caching/references/REFERENCE.md +0 -0
  382. /package/{rules → templates/nestjs/rules}/nestjs/caching/references/cache-patterns.md +0 -0
  383. /package/{rules → templates/nestjs/rules}/nestjs/configuration/SKILL.md +0 -0
  384. /package/{rules → templates/nestjs/rules}/nestjs/configuration/references/REFERENCE.md +0 -0
  385. /package/{rules → templates/nestjs/rules}/nestjs/configuration/references/config-patterns.md +0 -0
  386. /package/{rules → templates/nestjs/rules}/nestjs/controllers-services/SKILL.md +0 -0
  387. /package/{rules → templates/nestjs/rules}/nestjs/controllers-services/references/REFERENCE.md +0 -0
  388. /package/{rules → templates/nestjs/rules}/nestjs/controllers-services/references/controller-patterns.md +0 -0
  389. /package/{rules → templates/nestjs/rules}/nestjs/controllers-services/references/service-patterns.md +0 -0
  390. /package/{rules → templates/nestjs/rules}/nestjs/database/SKILL.md +0 -0
  391. /package/{rules → templates/nestjs/rules}/nestjs/database/references/REFERENCE.md +0 -0
  392. /package/{rules → templates/nestjs/rules}/nestjs/database/references/typeorm-patterns.md +0 -0
  393. /package/{rules → templates/nestjs/rules}/nestjs/deployment/SKILL.md +0 -0
  394. /package/{rules → templates/nestjs/rules}/nestjs/deployment/references/REFERENCE.md +0 -0
  395. /package/{rules → templates/nestjs/rules}/nestjs/deployment/references/deployment-patterns.md +0 -0
  396. /package/{rules → templates/nestjs/rules}/nestjs/documentation/SKILL.md +0 -0
  397. /package/{rules → templates/nestjs/rules}/nestjs/documentation/references/REFERENCE.md +0 -0
  398. /package/{rules → templates/nestjs/rules}/nestjs/documentation/references/swagger-patterns.md +0 -0
  399. /package/{rules → templates/nestjs/rules}/nestjs/error-handling/SKILL.md +0 -0
  400. /package/{rules → templates/nestjs/rules}/nestjs/error-handling/references/REFERENCE.md +0 -0
  401. /package/{rules → templates/nestjs/rules}/nestjs/error-handling/references/exception-filters.md +0 -0
  402. /package/{rules → templates/nestjs/rules}/nestjs/file-uploads/SKILL.md +0 -0
  403. /package/{rules → templates/nestjs/rules}/nestjs/file-uploads/references/REFERENCE.md +0 -0
  404. /package/{rules → templates/nestjs/rules}/nestjs/file-uploads/references/upload-patterns.md +0 -0
  405. /package/{rules → templates/nestjs/rules}/nestjs/observability/SKILL.md +0 -0
  406. /package/{rules → templates/nestjs/rules}/nestjs/observability/references/REFERENCE.md +0 -0
  407. /package/{rules → templates/nestjs/rules}/nestjs/observability/references/logging-metrics.md +0 -0
  408. /package/{rules → templates/nestjs/rules}/nestjs/performance/SKILL.md +0 -0
  409. /package/{rules → templates/nestjs/rules}/nestjs/performance/references/REFERENCE.md +0 -0
  410. /package/{rules → templates/nestjs/rules}/nestjs/performance/references/performance-patterns.md +0 -0
  411. /package/{rules → templates/nestjs/rules}/nestjs/real-time/SKILL.md +0 -0
  412. /package/{rules → templates/nestjs/rules}/nestjs/real-time/references/REFERENCE.md +0 -0
  413. /package/{rules → templates/nestjs/rules}/nestjs/real-time/references/websocket-patterns.md +0 -0
  414. /package/{rules → templates/nestjs/rules}/nestjs/scheduling/SKILL.md +0 -0
  415. /package/{rules → templates/nestjs/rules}/nestjs/scheduling/references/REFERENCE.md +0 -0
  416. /package/{rules → templates/nestjs/rules}/nestjs/scheduling/references/scheduling-patterns.md +0 -0
  417. /package/{rules → templates/nestjs/rules}/nestjs/search/SKILL.md +0 -0
  418. /package/{rules → templates/nestjs/rules}/nestjs/search/references/REFERENCE.md +0 -0
  419. /package/{rules → templates/nestjs/rules}/nestjs/search/references/search-patterns.md +0 -0
  420. /package/{rules → templates/nestjs/rules}/nestjs/security/SKILL.md +0 -0
  421. /package/{rules → templates/nestjs/rules}/nestjs/security/references/REFERENCE.md +0 -0
  422. /package/{rules → templates/nestjs/rules}/nestjs/security/references/authentication.md +0 -0
  423. /package/{rules → templates/nestjs/rules}/nestjs/testing/SKILL.md +0 -0
  424. /package/{rules → templates/nestjs/rules}/nestjs/testing/references/REFERENCE.md +0 -0
  425. /package/{rules → templates/nestjs/rules}/nestjs/testing/references/unit-testing.md +0 -0
  426. /package/{rules → templates/nestjs/rules}/nestjs/transport/SKILL.md +0 -0
  427. /package/{rules → templates/nestjs/rules}/nestjs/transport/references/REFERENCE.md +0 -0
  428. /package/{rules → templates/nestjs/rules}/nestjs/transport/references/microservices-patterns.md +0 -0
  429. /package/{rules → templates/nextjs/rules}/nextjs/app-router/SKILL.md +0 -0
  430. /package/{rules → templates/nextjs/rules}/nextjs/app-router/references/REFERENCE.md +0 -0
  431. /package/{rules → templates/nextjs/rules}/nextjs/app-router/references/routing-patterns.md +0 -0
  432. /package/{rules → templates/nextjs/rules}/nextjs/architecture/SKILL.md +0 -0
  433. /package/{rules → templates/nextjs/rules}/nextjs/architecture/references/fsd-structure.md +0 -0
  434. /package/{rules → templates/nextjs/rules}/nextjs/authentication/SKILL.md +0 -0
  435. /package/{rules → templates/nextjs/rules}/nextjs/authentication/references/auth-implementation.md +0 -0
  436. /package/{rules → templates/nextjs/rules}/nextjs/caching/SKILL.md +0 -0
  437. /package/{rules → templates/nextjs/rules}/nextjs/caching/references/REFERENCE.md +0 -0
  438. /package/{rules → templates/nextjs/rules}/nextjs/caching/references/cache-strategies.md +0 -0
  439. /package/{rules → templates/nextjs/rules}/nextjs/data-access-layer/SKILL.md +0 -0
  440. /package/{rules → templates/nextjs/rules}/nextjs/data-access-layer/references/patterns.md +0 -0
  441. /package/{rules → templates/nextjs/rules}/nextjs/data-fetching/SKILL.md +0 -0
  442. /package/{rules → templates/nextjs/rules}/nextjs/data-fetching/references/REFERENCE.md +0 -0
  443. /package/{rules → templates/nextjs/rules}/nextjs/data-fetching/references/fetch-patterns.md +0 -0
  444. /package/{rules → templates/nextjs/rules}/nextjs/internationalization/SKILL.md +0 -0
  445. /package/{rules → templates/nextjs/rules}/nextjs/internationalization/references/REFERENCE.md +0 -0
  446. /package/{rules → templates/nextjs/rules}/nextjs/internationalization/references/i18n-patterns.md +0 -0
  447. /package/{rules → templates/nextjs/rules}/nextjs/optimization/SKILL.md +0 -0
  448. /package/{rules → templates/nextjs/rules}/nextjs/optimization/references/REFERENCE.md +0 -0
  449. /package/{rules → templates/nextjs/rules}/nextjs/optimization/references/optimization-patterns.md +0 -0
  450. /package/{rules → templates/nextjs/rules}/nextjs/rendering/SKILL.md +0 -0
  451. /package/{rules → templates/nextjs/rules}/nextjs/rendering/references/REFERENCE.md +0 -0
  452. /package/{rules → templates/nextjs/rules}/nextjs/rendering/references/rendering-modes.md +0 -0
  453. /package/{rules → templates/nextjs/rules}/nextjs/server-actions/SKILL.md +0 -0
  454. /package/{rules → templates/nextjs/rules}/nextjs/server-actions/references/REFERENCE.md +0 -0
  455. /package/{rules → templates/nextjs/rules}/nextjs/server-actions/references/action-patterns.md +0 -0
  456. /package/{rules → templates/nextjs/rules}/nextjs/server-components/SKILL.md +0 -0
  457. /package/{rules → templates/nextjs/rules}/nextjs/server-components/references/REFERENCE.md +0 -0
  458. /package/{rules → templates/nextjs/rules}/nextjs/server-components/references/component-patterns.md +0 -0
  459. /package/{rules → templates/nextjs/rules}/nextjs/state-management/SKILL.md +0 -0
  460. /package/{rules → templates/nextjs/rules}/nextjs/state-management/references/REFERENCE.md +0 -0
  461. /package/{rules → templates/nextjs/rules}/nextjs/state-management/references/state-patterns.md +0 -0
  462. /package/{rules → templates/nextjs/rules}/nextjs/styling/SKILL.md +0 -0
  463. /package/{rules → templates/nextjs/rules}/nextjs/styling/references/implementation.md +0 -0
  464. /package/{rules → templates/react/rules}/react/component-patterns/SKILL.md +0 -0
  465. /package/{rules → templates/react/rules}/react/component-patterns/references/REFERENCE.md +0 -0
  466. /package/{rules → templates/react/rules}/react/hooks/SKILL.md +0 -0
  467. /package/{rules → templates/react/rules}/react/hooks/references/REFERENCE.md +0 -0
  468. /package/{rules → templates/react/rules}/react/hooks.rule.md +0 -0
  469. /package/{rules → templates/react/rules}/react/performance/SKILL.md +0 -0
  470. /package/{rules → templates/react/rules}/react/performance/references/REFERENCE.md +0 -0
  471. /package/{rules → templates/react/rules}/react/security/SKILL.md +0 -0
  472. /package/{rules → templates/react/rules}/react/security/references/REFERENCE.md +0 -0
  473. /package/{rules → templates/react/rules}/react/state-management/SKILL.md +0 -0
  474. /package/{rules → templates/react/rules}/react/state-management/references/REFERENCE.md +0 -0
  475. /package/{rules → templates/react/rules}/react/testing/SKILL.md +0 -0
  476. /package/{rules → templates/react/rules}/react/testing/references/REFERENCE.md +0 -0
  477. /package/{rules → templates/react/rules}/react/tooling/SKILL.md +0 -0
  478. /package/{rules → templates/react/rules}/react/typescript/SKILL.md +0 -0
  479. /package/{rules → templates/rust/rules}/rust/actix-web/SKILL.md +0 -0
  480. /package/{rules → templates/rust/rules}/rust/actix-web/references/REFERENCE.md +0 -0
  481. /package/{rules → templates/rust/rules}/rust/actix-web/references/handler-patterns.md +0 -0
  482. /package/{rules → templates/rust/rules}/rust/async-graphql/SKILL.md +0 -0
  483. /package/{rules → templates/rust/rules}/rust/async-graphql/references/REFERENCE.md +0 -0
  484. /package/{rules → templates/rust/rules}/rust/async-graphql/references/schema-patterns.md +0 -0
  485. /package/{rules → templates/rust/rules}/rust/axum/SKILL.md +0 -0
  486. /package/{rules → templates/rust/rules}/rust/axum/references/REFERENCE.md +0 -0
  487. /package/{rules → templates/rust/rules}/rust/axum/references/handler-patterns.md +0 -0
  488. /package/{rules → templates/rust/rules}/rust/bevy/SKILL.md +0 -0
  489. /package/{rules → templates/rust/rules}/rust/bevy/references/REFERENCE.md +0 -0
  490. /package/{rules → templates/rust/rules}/rust/bevy/references/ecs-patterns.md +0 -0
  491. /package/{rules → templates/rust/rules}/rust/clap/SKILL.md +0 -0
  492. /package/{rules → templates/rust/rules}/rust/clap/references/REFERENCE.md +0 -0
  493. /package/{rules → templates/rust/rules}/rust/clap/references/derive-patterns.md +0 -0
  494. /package/{rules → templates/rust/rules}/rust/core/SKILL.md +0 -0
  495. /package/{rules → templates/rust/rules}/rust/core/references/REFERENCE.md +0 -0
  496. /package/{rules → templates/rust/rules}/rust/core/references/error-handling.md +0 -0
  497. /package/{rules → templates/rust/rules}/rust/diesel-orm/SKILL.md +0 -0
  498. /package/{rules → templates/rust/rules}/rust/diesel-orm/references/REFERENCE.md +0 -0
  499. /package/{rules → templates/rust/rules}/rust/diesel-orm/references/schema-patterns.md +0 -0
  500. /package/{rules → templates/rust/rules}/rust/rocket/SKILL.md +0 -0
  501. /package/{rules → templates/rust/rules}/rust/rocket/references/REFERENCE.md +0 -0
  502. /package/{rules → templates/rust/rules}/rust/rocket/references/handler-patterns.md +0 -0
  503. /package/{rules → templates/rust/rules}/rust/sea-orm/SKILL.md +0 -0
  504. /package/{rules → templates/rust/rules}/rust/sea-orm/references/REFERENCE.md +0 -0
  505. /package/{rules → templates/rust/rules}/rust/sea-orm/references/entity-patterns.md +0 -0
  506. /package/{rules → templates/rust/rules}/rust/serde-serialization/SKILL.md +0 -0
  507. /package/{rules → templates/rust/rules}/rust/serde-serialization/references/REFERENCE.md +0 -0
  508. /package/{rules → templates/rust/rules}/rust/serde-serialization/references/serialization-patterns.md +0 -0
  509. /package/{rules → templates/rust/rules}/rust/sqlx-database/SKILL.md +0 -0
  510. /package/{rules → templates/rust/rules}/rust/sqlx-database/references/REFERENCE.md +0 -0
  511. /package/{rules → templates/rust/rules}/rust/sqlx-database/references/query-patterns.md +0 -0
  512. /package/{rules → templates/rust/rules}/rust/tauri/SKILL.md +0 -0
  513. /package/{rules → templates/rust/rules}/rust/tauri/references/REFERENCE.md +0 -0
  514. /package/{rules → templates/rust/rules}/rust/tauri/references/command-patterns.md +0 -0
  515. /package/{rules → templates/rust/rules}/rust/tokio-runtime/SKILL.md +0 -0
  516. /package/{rules → templates/rust/rules}/rust/tokio-runtime/references/REFERENCE.md +0 -0
  517. /package/{rules → templates/rust/rules}/rust/tokio-runtime/references/async-patterns.md +0 -0
  518. /package/{rules → templates/rust/rules}/rust/tokio-runtime/references/synchronization.md +0 -0
  519. /package/{rules → templates/rust/rules}/rust/tonic/SKILL.md +0 -0
  520. /package/{rules → templates/rust/rules}/rust/tonic/references/REFERENCE.md +0 -0
  521. /package/{rules → templates/rust/rules}/rust/tonic/references/service-patterns.md +0 -0
  522. /package/{rules → templates/rust/rules}/rust/tracing/SKILL.md +0 -0
  523. /package/{rules → templates/rust/rules}/rust/tracing/references/REFERENCE.md +0 -0
  524. /package/{rules → templates/rust/rules}/rust/tracing/references/instrumentation.md +0 -0
  525. /package/{rules → templates/typescript/rules}/typescript/best-practices/SKILL.md +0 -0
  526. /package/{rules → templates/typescript/rules}/typescript/best-practices/references/REFERENCE.md +0 -0
  527. /package/{rules → templates/typescript/rules}/typescript/language/SKILL.md +0 -0
  528. /package/{rules → templates/typescript/rules}/typescript/language/references/REFERENCE.md +0 -0
  529. /package/{rules → templates/typescript/rules}/typescript/patterns.rule.md +0 -0
  530. /package/{rules → templates/typescript/rules}/typescript/security/SKILL.md +0 -0
  531. /package/{rules → templates/typescript/rules}/typescript/security/references/REFERENCE.md +0 -0
  532. /package/{rules → templates/typescript/rules}/typescript/tooling/SKILL.md +0 -0
  533. /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
+ };