@trieungoctam/vibekit 1.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 (352) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +85 -0
  3. package/agents/debugger.md +158 -0
  4. package/agents/docs-manager.md +220 -0
  5. package/agents/planner.md +129 -0
  6. package/agents/researcher.md +58 -0
  7. package/agents/reviewer.md +152 -0
  8. package/agents/tester.md +126 -0
  9. package/bin/vibekit.js +18 -0
  10. package/hooks/lib/ck-config-utils.cjs +831 -0
  11. package/hooks/lib/colors.cjs +95 -0
  12. package/hooks/lib/config-counter.cjs +103 -0
  13. package/hooks/lib/context-builder.cjs +616 -0
  14. package/hooks/lib/git-info-cache.cjs +143 -0
  15. package/hooks/lib/hook-logger.cjs +92 -0
  16. package/hooks/lib/privacy-checker.cjs +297 -0
  17. package/hooks/lib/project-detector.cjs +474 -0
  18. package/hooks/lib/scout-checker.cjs +263 -0
  19. package/hooks/lib/transcript-parser.cjs +181 -0
  20. package/hooks/post-edit-simplify-reminder.cjs +156 -0
  21. package/hooks/privacy-block.cjs +166 -0
  22. package/hooks/scout-block.cjs +147 -0
  23. package/hooks/session-init.cjs +360 -0
  24. package/package.json +41 -0
  25. package/rules/development-rules.md +52 -0
  26. package/rules/documentation-management.md +121 -0
  27. package/rules/orchestration-protocol.md +43 -0
  28. package/rules/primary-workflow.md +57 -0
  29. package/rules/team-coordination-rules.md +90 -0
  30. package/skills/ai/agent-browser/SKILL.md +294 -0
  31. package/skills/ai/agent-browser/references/.gitkeep +0 -0
  32. package/skills/ai/agent-browser/references/agent-browser-vs-chrome-devtools.md +112 -0
  33. package/skills/ai/agent-browser/references/browserbase-cloud-setup.md +161 -0
  34. package/skills/ai/ai-artist/SKILL.md +122 -0
  35. package/skills/ai/ai-artist/data/awesome-prompts.csv +3592 -0
  36. package/skills/ai/ai-artist/data/lighting.csv +19 -0
  37. package/skills/ai/ai-artist/data/nano-banana-templates.csv +17 -0
  38. package/skills/ai/ai-artist/data/platforms.csv +11 -0
  39. package/skills/ai/ai-artist/data/styles.csv +26 -0
  40. package/skills/ai/ai-artist/data/techniques.csv +19 -0
  41. package/skills/ai/ai-artist/data/use-cases.csv +16 -0
  42. package/skills/ai/ai-artist/references/advanced-techniques.md +184 -0
  43. package/skills/ai/ai-artist/references/awesome-nano-banana-pro-prompts.md +8575 -0
  44. package/skills/ai/ai-artist/references/domain-code.md +66 -0
  45. package/skills/ai/ai-artist/references/domain-data.md +72 -0
  46. package/skills/ai/ai-artist/references/domain-marketing.md +66 -0
  47. package/skills/ai/ai-artist/references/domain-patterns.md +33 -0
  48. package/skills/ai/ai-artist/references/domain-writing.md +68 -0
  49. package/skills/ai/ai-artist/references/image-prompting.md +141 -0
  50. package/skills/ai/ai-artist/references/llm-prompting.md +165 -0
  51. package/skills/ai/ai-artist/references/nano-banana.md +136 -0
  52. package/skills/ai/ai-artist/references/reasoning-techniques.md +201 -0
  53. package/skills/ai/ai-artist/references/validation-workflow.md +117 -0
  54. package/skills/ai/ai-artist/scripts/core.py +197 -0
  55. package/skills/ai/ai-artist/scripts/extract_prompts.py +102 -0
  56. package/skills/ai/ai-artist/scripts/generate.py +370 -0
  57. package/skills/ai/ai-artist/scripts/search.py +147 -0
  58. package/skills/ai/ai-multimodal/.env.example +204 -0
  59. package/skills/ai/ai-multimodal/SKILL.md +110 -0
  60. package/skills/ai/ai-multimodal/references/audio-processing.md +387 -0
  61. package/skills/ai/ai-multimodal/references/image-generation.md +939 -0
  62. package/skills/ai/ai-multimodal/references/music-generation.md +311 -0
  63. package/skills/ai/ai-multimodal/references/video-analysis.md +515 -0
  64. package/skills/ai/ai-multimodal/references/video-generation.md +457 -0
  65. package/skills/ai/ai-multimodal/references/vision-understanding.md +492 -0
  66. package/skills/ai/ai-multimodal/scripts/.coverage +0 -0
  67. package/skills/ai/ai-multimodal/scripts/check_setup.py +315 -0
  68. package/skills/ai/ai-multimodal/scripts/document_converter.py +395 -0
  69. package/skills/ai/ai-multimodal/scripts/gemini_batch_process.py +1185 -0
  70. package/skills/ai/ai-multimodal/scripts/media_optimizer.py +506 -0
  71. package/skills/ai/ai-multimodal/scripts/requirements.txt +26 -0
  72. package/skills/ai/ai-multimodal/scripts/tests/.coverage +0 -0
  73. package/skills/ai/ai-multimodal/scripts/tests/requirements.txt +20 -0
  74. package/skills/ai/ai-multimodal/scripts/tests/test_document_converter.py +74 -0
  75. package/skills/ai/ai-multimodal/scripts/tests/test_gemini_batch_process.py +362 -0
  76. package/skills/ai/ai-multimodal/scripts/tests/test_media_optimizer.py +373 -0
  77. package/skills/ai/mcp-management/README.md +219 -0
  78. package/skills/ai/mcp-management/SKILL.md +210 -0
  79. package/skills/ai/mcp-management/assets/tools.json +3146 -0
  80. package/skills/ai/mcp-management/references/configuration.md +114 -0
  81. package/skills/ai/mcp-management/references/gemini-cli-integration.md +221 -0
  82. package/skills/ai/mcp-management/references/mcp-protocol.md +116 -0
  83. package/skills/ai/mcp-management/scripts/.env.example +10 -0
  84. package/skills/ai/mcp-management/scripts/cli.ts +195 -0
  85. package/skills/ai/mcp-management/scripts/dist/analyze-tools.js +70 -0
  86. package/skills/ai/mcp-management/scripts/dist/cli.js +160 -0
  87. package/skills/ai/mcp-management/scripts/dist/mcp-client.js +183 -0
  88. package/skills/ai/mcp-management/scripts/mcp-client.ts +230 -0
  89. package/skills/ai/mcp-management/scripts/package.json +20 -0
  90. package/skills/ai/mcp-management/scripts/tsconfig.json +15 -0
  91. package/skills/core/brainstorm/SKILL.md +164 -0
  92. package/skills/core/brainstorm/scripts/frame-template.html +214 -0
  93. package/skills/core/brainstorm/scripts/helper.js +88 -0
  94. package/skills/core/brainstorm/scripts/server.cjs +338 -0
  95. package/skills/core/brainstorm/scripts/start-server.sh +153 -0
  96. package/skills/core/brainstorm/scripts/stop-server.sh +55 -0
  97. package/skills/core/brainstorm/spec-document-reviewer-prompt.md +49 -0
  98. package/skills/core/brainstorm/visual-companion.md +286 -0
  99. package/skills/core/code-review/SKILL.md +147 -0
  100. package/skills/core/code-review/references/code-review-reception.md +113 -0
  101. package/skills/core/code-review/references/codebase-scan-workflow.md +29 -0
  102. package/skills/core/code-review/references/edge-case-scouting.md +119 -0
  103. package/skills/core/code-review/references/parallel-review-workflow.md +69 -0
  104. package/skills/core/code-review/references/requesting-code-review.md +116 -0
  105. package/skills/core/code-review/references/task-management-reviews.md +140 -0
  106. package/skills/core/code-review/references/verification-before-completion.md +139 -0
  107. package/skills/core/cook/README.md +86 -0
  108. package/skills/core/cook/SKILL.md +113 -0
  109. package/skills/core/cook/references/intent-detection.md +101 -0
  110. package/skills/core/cook/references/review-cycle.md +75 -0
  111. package/skills/core/cook/references/subagent-patterns.md +75 -0
  112. package/skills/core/cook/references/workflow-steps.md +172 -0
  113. package/skills/core/debug/SKILL.md +121 -0
  114. package/skills/core/debug/references/defense-in-depth.md +124 -0
  115. package/skills/core/debug/references/frontend-verification.md +103 -0
  116. package/skills/core/debug/references/investigation-methodology.md +101 -0
  117. package/skills/core/debug/references/log-and-ci-analysis.md +97 -0
  118. package/skills/core/debug/references/performance-diagnostics.md +113 -0
  119. package/skills/core/debug/references/reporting-standards.md +122 -0
  120. package/skills/core/debug/references/root-cause-tracing.md +122 -0
  121. package/skills/core/debug/references/systematic-debugging.md +102 -0
  122. package/skills/core/debug/references/task-management-debugging.md +155 -0
  123. package/skills/core/debug/references/verification.md +123 -0
  124. package/skills/core/debug/scripts/find-polluter.sh +63 -0
  125. package/skills/core/debug/scripts/find-polluter.test.md +102 -0
  126. package/skills/core/execute/SKILL.md +70 -0
  127. package/skills/core/fix/SKILL.md +111 -0
  128. package/skills/core/fix/references/complexity-assessment.md +72 -0
  129. package/skills/core/fix/references/mode-selection.md +46 -0
  130. package/skills/core/fix/references/parallel-exploration.md +100 -0
  131. package/skills/core/fix/references/review-cycle.md +77 -0
  132. package/skills/core/fix/references/skill-activation-matrix.md +78 -0
  133. package/skills/core/fix/references/task-orchestration.md +103 -0
  134. package/skills/core/fix/references/workflow-ci.md +28 -0
  135. package/skills/core/fix/references/workflow-deep.md +122 -0
  136. package/skills/core/fix/references/workflow-logs.md +72 -0
  137. package/skills/core/fix/references/workflow-quick.md +59 -0
  138. package/skills/core/fix/references/workflow-standard.md +111 -0
  139. package/skills/core/fix/references/workflow-test.md +75 -0
  140. package/skills/core/fix/references/workflow-types.md +33 -0
  141. package/skills/core/fix/references/workflow-ui.md +75 -0
  142. package/skills/core/plan/SKILL.md +145 -0
  143. package/skills/core/plan/plan-document-reviewer-prompt.md +49 -0
  144. package/skills/core/subagent-dev/SKILL.md +277 -0
  145. package/skills/core/subagent-dev/code-quality-reviewer-prompt.md +26 -0
  146. package/skills/core/subagent-dev/implementer-prompt.md +113 -0
  147. package/skills/core/subagent-dev/spec-reviewer-prompt.md +61 -0
  148. package/skills/core/tdd/SKILL.md +371 -0
  149. package/skills/core/tdd/testing-anti-patterns.md +299 -0
  150. package/skills/core/test/SKILL.md +109 -0
  151. package/skills/core/test/references/report-format.md +58 -0
  152. package/skills/core/test/references/test-execution-workflow.md +103 -0
  153. package/skills/core/test/references/ui-testing-workflow.md +65 -0
  154. package/skills/core/verify/SKILL.md +139 -0
  155. package/skills/dev/backend-dev/SKILL.md +96 -0
  156. package/skills/dev/backend-dev/references/backend-api-design.md +495 -0
  157. package/skills/dev/backend-dev/references/backend-architecture.md +454 -0
  158. package/skills/dev/backend-dev/references/backend-authentication.md +338 -0
  159. package/skills/dev/backend-dev/references/backend-code-quality.md +659 -0
  160. package/skills/dev/backend-dev/references/backend-debugging.md +904 -0
  161. package/skills/dev/backend-dev/references/backend-devops.md +494 -0
  162. package/skills/dev/backend-dev/references/backend-mindset.md +387 -0
  163. package/skills/dev/backend-dev/references/backend-performance.md +397 -0
  164. package/skills/dev/backend-dev/references/backend-security.md +290 -0
  165. package/skills/dev/backend-dev/references/backend-technologies.md +256 -0
  166. package/skills/dev/backend-dev/references/backend-testing.md +429 -0
  167. package/skills/dev/context-engineering/SKILL.md +108 -0
  168. package/skills/dev/context-engineering/references/context-compression.md +84 -0
  169. package/skills/dev/context-engineering/references/context-degradation.md +93 -0
  170. package/skills/dev/context-engineering/references/context-fundamentals.md +75 -0
  171. package/skills/dev/context-engineering/references/context-optimization.md +82 -0
  172. package/skills/dev/context-engineering/references/evaluation.md +89 -0
  173. package/skills/dev/context-engineering/references/memory-systems.md +88 -0
  174. package/skills/dev/context-engineering/references/multi-agent-patterns.md +90 -0
  175. package/skills/dev/context-engineering/references/project-development.md +97 -0
  176. package/skills/dev/context-engineering/references/runtime-awareness.md +202 -0
  177. package/skills/dev/context-engineering/references/tool-design.md +86 -0
  178. package/skills/dev/context-engineering/scripts/compression_evaluator.py +349 -0
  179. package/skills/dev/context-engineering/scripts/context_analyzer.py +317 -0
  180. package/skills/dev/context-engineering/scripts/tests/test_edge_cases.py +246 -0
  181. package/skills/dev/databases/SKILL.md +84 -0
  182. package/skills/dev/databases/analytics.md +198 -0
  183. package/skills/dev/databases/db-design.md +188 -0
  184. package/skills/dev/databases/incremental-etl.md +213 -0
  185. package/skills/dev/databases/references/mongodb-aggregation.md +447 -0
  186. package/skills/dev/databases/references/mongodb-atlas.md +465 -0
  187. package/skills/dev/databases/references/mongodb-crud.md +408 -0
  188. package/skills/dev/databases/references/mongodb-indexing.md +442 -0
  189. package/skills/dev/databases/references/postgresql-administration.md +594 -0
  190. package/skills/dev/databases/references/postgresql-performance.md +527 -0
  191. package/skills/dev/databases/references/postgresql-psql-cli.md +467 -0
  192. package/skills/dev/databases/references/postgresql-queries.md +475 -0
  193. package/skills/dev/databases/scripts/.coverage +0 -0
  194. package/skills/dev/databases/scripts/db_backup.py +502 -0
  195. package/skills/dev/databases/scripts/db_migrate.py +426 -0
  196. package/skills/dev/databases/scripts/db_performance_check.py +457 -0
  197. package/skills/dev/databases/scripts/requirements.txt +20 -0
  198. package/skills/dev/databases/scripts/tests/coverage-db.json +1 -0
  199. package/skills/dev/databases/scripts/tests/requirements.txt +4 -0
  200. package/skills/dev/databases/scripts/tests/test_db_backup.py +340 -0
  201. package/skills/dev/databases/scripts/tests/test_db_migrate.py +277 -0
  202. package/skills/dev/databases/scripts/tests/test_db_performance_check.py +370 -0
  203. package/skills/dev/databases/stacks/bigquery.md +231 -0
  204. package/skills/dev/databases/stacks/d1_cloudflare.md +137 -0
  205. package/skills/dev/databases/stacks/mysql.md +216 -0
  206. package/skills/dev/databases/stacks/postgres.md +235 -0
  207. package/skills/dev/databases/stacks/sqlite.md +244 -0
  208. package/skills/dev/databases/transactional.md +176 -0
  209. package/skills/dev/devops/.env.example +76 -0
  210. package/skills/dev/devops/SKILL.md +97 -0
  211. package/skills/dev/devops/references/browser-rendering.md +305 -0
  212. package/skills/dev/devops/references/cloudflare-d1-kv.md +123 -0
  213. package/skills/dev/devops/references/cloudflare-platform.md +271 -0
  214. package/skills/dev/devops/references/cloudflare-r2-storage.md +280 -0
  215. package/skills/dev/devops/references/cloudflare-workers-advanced.md +312 -0
  216. package/skills/dev/devops/references/cloudflare-workers-apis.md +309 -0
  217. package/skills/dev/devops/references/cloudflare-workers-basics.md +418 -0
  218. package/skills/dev/devops/references/docker-basics.md +297 -0
  219. package/skills/dev/devops/references/docker-compose.md +292 -0
  220. package/skills/dev/devops/references/gcloud-platform.md +297 -0
  221. package/skills/dev/devops/references/gcloud-services.md +304 -0
  222. package/skills/dev/devops/references/kubernetes-basics.md +99 -0
  223. package/skills/dev/devops/references/kubernetes-helm-advanced.md +75 -0
  224. package/skills/dev/devops/references/kubernetes-helm.md +81 -0
  225. package/skills/dev/devops/references/kubernetes-kubectl.md +74 -0
  226. package/skills/dev/devops/references/kubernetes-security-advanced.md +98 -0
  227. package/skills/dev/devops/references/kubernetes-security.md +95 -0
  228. package/skills/dev/devops/references/kubernetes-troubleshooting-advanced.md +74 -0
  229. package/skills/dev/devops/references/kubernetes-troubleshooting.md +49 -0
  230. package/skills/dev/devops/references/kubernetes-workflows-advanced.md +75 -0
  231. package/skills/dev/devops/references/kubernetes-workflows.md +78 -0
  232. package/skills/dev/devops/scripts/cloudflare_deploy.py +269 -0
  233. package/skills/dev/devops/scripts/docker_optimize.py +332 -0
  234. package/skills/dev/devops/scripts/requirements.txt +20 -0
  235. package/skills/dev/devops/scripts/tests/requirements.txt +3 -0
  236. package/skills/dev/devops/scripts/tests/test_cloudflare_deploy.py +285 -0
  237. package/skills/dev/devops/scripts/tests/test_docker_optimize.py +436 -0
  238. package/skills/dev/frontend-design/SKILL.md +78 -0
  239. package/skills/dev/frontend-design/references/ai-multimodal-overview.md +165 -0
  240. package/skills/dev/frontend-design/references/analysis-best-practices.md +80 -0
  241. package/skills/dev/frontend-design/references/analysis-prompts.md +141 -0
  242. package/skills/dev/frontend-design/references/analysis-techniques.md +118 -0
  243. package/skills/dev/frontend-design/references/animejs.md +396 -0
  244. package/skills/dev/frontend-design/references/asset-generation.md +337 -0
  245. package/skills/dev/frontend-design/references/design-extraction-overview.md +71 -0
  246. package/skills/dev/frontend-design/references/extraction-best-practices.md +141 -0
  247. package/skills/dev/frontend-design/references/extraction-output-templates.md +162 -0
  248. package/skills/dev/frontend-design/references/extraction-prompts.md +127 -0
  249. package/skills/dev/frontend-design/references/technical-accessibility.md +119 -0
  250. package/skills/dev/frontend-design/references/technical-best-practices.md +97 -0
  251. package/skills/dev/frontend-design/references/technical-optimization.md +44 -0
  252. package/skills/dev/frontend-design/references/technical-overview.md +90 -0
  253. package/skills/dev/frontend-design/references/technical-workflows.md +150 -0
  254. package/skills/dev/frontend-design/references/visual-analysis-overview.md +95 -0
  255. package/skills/dev/frontend-design/references/workflow-3d.md +102 -0
  256. package/skills/dev/frontend-design/references/workflow-describe.md +87 -0
  257. package/skills/dev/frontend-design/references/workflow-immersive.md +87 -0
  258. package/skills/dev/frontend-design/references/workflow-quick.md +57 -0
  259. package/skills/dev/frontend-design/references/workflow-screenshot.md +63 -0
  260. package/skills/dev/frontend-design/references/workflow-video.md +74 -0
  261. package/skills/dev/frontend-dev/SKILL.md +400 -0
  262. package/skills/dev/frontend-dev/resources/common-patterns.md +331 -0
  263. package/skills/dev/frontend-dev/resources/complete-examples.md +872 -0
  264. package/skills/dev/frontend-dev/resources/component-patterns.md +502 -0
  265. package/skills/dev/frontend-dev/resources/data-fetching.md +767 -0
  266. package/skills/dev/frontend-dev/resources/file-organization.md +502 -0
  267. package/skills/dev/frontend-dev/resources/loading-and-error-states.md +501 -0
  268. package/skills/dev/frontend-dev/resources/performance.md +406 -0
  269. package/skills/dev/frontend-dev/resources/routing-guide.md +364 -0
  270. package/skills/dev/frontend-dev/resources/styling-guide.md +428 -0
  271. package/skills/dev/frontend-dev/resources/typescript-standards.md +418 -0
  272. package/skills/dev/git/SKILL.md +114 -0
  273. package/skills/dev/git/references/branch-management.md +88 -0
  274. package/skills/dev/git/references/commit-standards.md +46 -0
  275. package/skills/dev/git/references/gh-cli-guide.md +109 -0
  276. package/skills/dev/git/references/safety-protocols.md +69 -0
  277. package/skills/dev/git/references/workflow-commit.md +58 -0
  278. package/skills/dev/git/references/workflow-merge.md +48 -0
  279. package/skills/dev/git/references/workflow-pr.md +58 -0
  280. package/skills/dev/git/references/workflow-push.md +52 -0
  281. package/skills/dev/git-worktree/SKILL.md +218 -0
  282. package/skills/utils/ask/SKILL.md +58 -0
  283. package/skills/utils/bootstrap/SKILL.md +101 -0
  284. package/skills/utils/bootstrap/references/shared-phases.md +59 -0
  285. package/skills/utils/bootstrap/references/workflow-auto.md +52 -0
  286. package/skills/utils/bootstrap/references/workflow-fast.md +50 -0
  287. package/skills/utils/bootstrap/references/workflow-full.md +60 -0
  288. package/skills/utils/bootstrap/references/workflow-parallel.md +59 -0
  289. package/skills/utils/ck-help/SKILL.md +102 -0
  290. package/skills/utils/ck-help/scripts/ck-help.py +1321 -0
  291. package/skills/utils/ck-help/scripts/commands_data.yaml +3 -0
  292. package/skills/utils/ck-help/scripts/skills_data.yaml +593 -0
  293. package/skills/utils/copywriting/SKILL.md +94 -0
  294. package/skills/utils/copywriting/references/copy-formulas.md +150 -0
  295. package/skills/utils/copywriting/references/cta-patterns.md +168 -0
  296. package/skills/utils/copywriting/references/email-copy.md +193 -0
  297. package/skills/utils/copywriting/references/headline-templates.md +140 -0
  298. package/skills/utils/copywriting/references/landing-page-copy.md +175 -0
  299. package/skills/utils/copywriting/references/power-words.md +189 -0
  300. package/skills/utils/copywriting/references/social-media-copy.md +222 -0
  301. package/skills/utils/copywriting/references/workflow-cro.md +83 -0
  302. package/skills/utils/copywriting/references/workflow-enhance.md +32 -0
  303. package/skills/utils/copywriting/references/workflow-fast.md +29 -0
  304. package/skills/utils/copywriting/references/workflow-good.md +39 -0
  305. package/skills/utils/copywriting/references/writing-styles.md +247 -0
  306. package/skills/utils/copywriting/scripts/extract-writing-styles.py +308 -0
  307. package/skills/utils/copywriting/templates/copy-brief.md +49 -0
  308. package/skills/utils/docs/SKILL.md +55 -0
  309. package/skills/utils/docs/references/init-workflow.md +32 -0
  310. package/skills/utils/docs/references/summarize-workflow.md +18 -0
  311. package/skills/utils/docs/references/update-workflow.md +59 -0
  312. package/skills/utils/journal/SKILL.md +11 -0
  313. package/skills/utils/kanban/SKILL.md +99 -0
  314. package/skills/utils/preview/SKILL.md +75 -0
  315. package/skills/utils/preview/references/generation-modes.md +95 -0
  316. package/skills/utils/preview/references/view-mode.md +42 -0
  317. package/skills/utils/repomix/SKILL.md +248 -0
  318. package/skills/utils/repomix/references/configuration.md +211 -0
  319. package/skills/utils/repomix/references/usage-patterns.md +232 -0
  320. package/skills/utils/repomix/scripts/.coverage +0 -0
  321. package/skills/utils/repomix/scripts/README.md +179 -0
  322. package/skills/utils/repomix/scripts/repomix_batch.py +455 -0
  323. package/skills/utils/repomix/scripts/repos.example.json +15 -0
  324. package/skills/utils/repomix/scripts/requirements.txt +15 -0
  325. package/skills/utils/repomix/scripts/tests/test_repomix_batch.py +531 -0
  326. package/skills/utils/research/SKILL.md +171 -0
  327. package/skills/utils/scout/SKILL.md +89 -0
  328. package/skills/utils/scout/references/external-scouting.md +140 -0
  329. package/skills/utils/scout/references/internal-scouting.md +119 -0
  330. package/skills/utils/scout/references/task-management-scouting.md +125 -0
  331. package/skills/utils/sequential-thinking/.env.example +8 -0
  332. package/skills/utils/sequential-thinking/README.md +183 -0
  333. package/skills/utils/sequential-thinking/SKILL.md +95 -0
  334. package/skills/utils/sequential-thinking/package.json +31 -0
  335. package/skills/utils/sequential-thinking/references/advanced-strategies.md +79 -0
  336. package/skills/utils/sequential-thinking/references/advanced-techniques.md +76 -0
  337. package/skills/utils/sequential-thinking/references/core-patterns.md +95 -0
  338. package/skills/utils/sequential-thinking/references/examples-api.md +88 -0
  339. package/skills/utils/sequential-thinking/references/examples-architecture.md +94 -0
  340. package/skills/utils/sequential-thinking/references/examples-debug.md +90 -0
  341. package/skills/utils/sequential-thinking/scripts/format-thought.js +159 -0
  342. package/skills/utils/sequential-thinking/scripts/process-thought.js +236 -0
  343. package/skills/utils/sequential-thinking/tests/format-thought.test.js +133 -0
  344. package/skills/utils/sequential-thinking/tests/process-thought.test.js +215 -0
  345. package/skills/utils/write-skill/SKILL.md +655 -0
  346. package/skills/utils/write-skill/anthropic-best-practices.md +1150 -0
  347. package/skills/utils/write-skill/examples/CLAUDE_MD_TESTING.md +189 -0
  348. package/skills/utils/write-skill/graphviz-conventions.dot +172 -0
  349. package/skills/utils/write-skill/persuasion-principles.md +187 -0
  350. package/skills/utils/write-skill/render-graphs.js +168 -0
  351. package/skills/utils/write-skill/testing-skills-with-subagents.md +384 -0
  352. package/src/commands/init.js +238 -0
@@ -0,0 +1,831 @@
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
+ statusline: 'full',
64
+ hooks: {
65
+ 'session-init': true,
66
+ 'subagent-init': true,
67
+ 'dev-rules-reminder': true,
68
+ 'usage-context-awareness': true,
69
+ 'context-tracking': true,
70
+ 'scout-block': true,
71
+ 'privacy-block': true,
72
+ 'post-edit-simplify-reminder': true,
73
+ 'task-completed-handler': true,
74
+ 'teammate-idle-handler': true
75
+ }
76
+ };
77
+
78
+ /**
79
+ * Deep merge objects (source values override target, nested objects merged recursively)
80
+ * Arrays are replaced entirely (not concatenated) to avoid duplicate entries
81
+ *
82
+ * IMPORTANT: Empty objects {} are treated as "inherit from parent", not "replace with empty".
83
+ * This allows global config to set hooks.foo: false and have it persist even when
84
+ * local config has hooks: {} (empty = inherit, not reset to defaults).
85
+ *
86
+ * @param {Object} target - Base object
87
+ * @param {Object} source - Object to merge (takes precedence)
88
+ * @returns {Object} Merged object
89
+ */
90
+ function deepMerge(target, source) {
91
+ if (!source || typeof source !== 'object') return target;
92
+ if (!target || typeof target !== 'object') return source;
93
+
94
+ const result = { ...target };
95
+ for (const key of Object.keys(source)) {
96
+ const sourceVal = source[key];
97
+ const targetVal = target[key];
98
+
99
+ // Arrays: replace entirely (don't concatenate)
100
+ if (Array.isArray(sourceVal)) {
101
+ result[key] = [...sourceVal];
102
+ }
103
+ // Objects: recurse (but not null)
104
+ // SKIP empty objects - treat {} as "inherit from parent"
105
+ else if (sourceVal !== null && typeof sourceVal === 'object' && !Array.isArray(sourceVal)) {
106
+ // Empty object = inherit (don't override parent values)
107
+ if (Object.keys(sourceVal).length === 0) {
108
+ // Keep target value unchanged - empty source means "no override"
109
+ continue;
110
+ }
111
+ result[key] = deepMerge(targetVal || {}, sourceVal);
112
+ }
113
+ // Primitives: source wins
114
+ else {
115
+ result[key] = sourceVal;
116
+ }
117
+ }
118
+ return result;
119
+ }
120
+
121
+ /**
122
+ * Load config from a specific file path
123
+ * @param {string} configPath - Path to config file
124
+ * @returns {Object|null} Parsed config or null if not found/invalid
125
+ */
126
+ function loadConfigFromPath(configPath) {
127
+ try {
128
+ if (!fs.existsSync(configPath)) return null;
129
+ return JSON.parse(fs.readFileSync(configPath, 'utf8'));
130
+ } catch (e) {
131
+ return null;
132
+ }
133
+ }
134
+
135
+ /**
136
+ * Get session temp file path
137
+ * @param {string} sessionId - Session identifier
138
+ * @returns {string} Path to session temp file
139
+ */
140
+ function getSessionTempPath(sessionId) {
141
+ return path.join(os.tmpdir(), `ck-session-${sessionId}.json`);
142
+ }
143
+
144
+ /**
145
+ * Read session state from temp file
146
+ * @param {string} sessionId - Session identifier
147
+ * @returns {Object|null} Session state or null
148
+ */
149
+ function readSessionState(sessionId) {
150
+ if (!sessionId) return null;
151
+ const tempPath = getSessionTempPath(sessionId);
152
+ try {
153
+ if (!fs.existsSync(tempPath)) return null;
154
+ return JSON.parse(fs.readFileSync(tempPath, 'utf8'));
155
+ } catch (e) {
156
+ return null;
157
+ }
158
+ }
159
+
160
+ /**
161
+ * Write session state atomically to temp file
162
+ * @param {string} sessionId - Session identifier
163
+ * @param {Object} state - State object to persist
164
+ * @returns {boolean} Success status
165
+ */
166
+ function writeSessionState(sessionId, state) {
167
+ if (!sessionId) return false;
168
+ const tempPath = getSessionTempPath(sessionId);
169
+ const tmpFile = tempPath + '.' + Math.random().toString(36).slice(2);
170
+ try {
171
+ fs.writeFileSync(tmpFile, JSON.stringify(state, null, 2));
172
+ fs.renameSync(tmpFile, tempPath);
173
+ return true;
174
+ } catch (e) {
175
+ try { fs.unlinkSync(tmpFile); } catch (_) { /* ignore */ }
176
+ return false;
177
+ }
178
+ }
179
+
180
+ /**
181
+ * Characters invalid in filenames across Windows, macOS, Linux
182
+ * Windows: < > : " / \ | ? *
183
+ * macOS/Linux: / and null byte
184
+ * Also includes control characters and other problematic chars
185
+ */
186
+ const INVALID_FILENAME_CHARS = /[<>:"/\\|?*\x00-\x1f\x7f]/g;
187
+
188
+ /**
189
+ * Sanitize slug for safe filesystem usage
190
+ * - Removes invalid filename characters
191
+ * - Replaces non-alphanumeric (except hyphen) with hyphen
192
+ * - Collapses multiple hyphens
193
+ * - Removes leading/trailing hyphens
194
+ * - Limits length to prevent filesystem issues
195
+ *
196
+ * @param {string} slug - Slug to sanitize
197
+ * @returns {string} Sanitized slug (empty string if nothing valid remains)
198
+ */
199
+ function sanitizeSlug(slug) {
200
+ if (!slug || typeof slug !== 'string') return '';
201
+
202
+ let sanitized = slug
203
+ // Remove invalid filename chars first
204
+ .replace(INVALID_FILENAME_CHARS, '')
205
+ // Replace any non-alphanumeric (except hyphen) with hyphen
206
+ .replace(/[^a-z0-9-]/gi, '-')
207
+ // Collapse multiple consecutive hyphens
208
+ .replace(/-+/g, '-')
209
+ // Remove leading/trailing hyphens
210
+ .replace(/^-+|-+$/g, '')
211
+ // Limit length (most filesystems support 255, but keep reasonable)
212
+ .slice(0, 100);
213
+
214
+ return sanitized;
215
+ }
216
+
217
+ /**
218
+ * Extract feature slug from git branch name
219
+ * Pattern: (?:feat|fix|chore|refactor|docs)/(?:[^/]+/)?(.+)
220
+ * @param {string} branch - Git branch name
221
+ * @param {string} pattern - Regex pattern (optional)
222
+ * @returns {string|null} Extracted slug or null
223
+ */
224
+ function extractSlugFromBranch(branch, pattern) {
225
+ if (!branch) return null;
226
+ const defaultPattern = /(?:feat|fix|chore|refactor|docs)\/(?:[^\/]+\/)?(.+)/;
227
+ const regex = pattern ? new RegExp(pattern) : defaultPattern;
228
+ const match = branch.match(regex);
229
+ return match ? sanitizeSlug(match[1]) : null;
230
+ }
231
+
232
+ /**
233
+ * Find most recent plan folder by timestamp prefix
234
+ * @param {string} plansDir - Plans directory path
235
+ * @returns {string|null} Most recent plan path or null
236
+ */
237
+ function findMostRecentPlan(plansDir) {
238
+ try {
239
+ if (!fs.existsSync(plansDir)) return null;
240
+ const entries = fs.readdirSync(plansDir, { withFileTypes: true });
241
+ const planDirs = entries
242
+ .filter(e => e.isDirectory() && /^\d{6}/.test(e.name))
243
+ .map(e => e.name)
244
+ .sort()
245
+ .reverse();
246
+ return planDirs.length > 0 ? path.join(plansDir, planDirs[0]) : null;
247
+ } catch (e) {
248
+ return null;
249
+ }
250
+ }
251
+
252
+ /**
253
+ * Default timeout for git commands (5 seconds)
254
+ * Prevents indefinite hangs on network mounts or corrupted repos
255
+ */
256
+ const DEFAULT_EXEC_TIMEOUT_MS = 5000;
257
+
258
+ /**
259
+ * Safely execute shell command (internal helper)
260
+ * SECURITY: Only accepts whitelisted git read commands
261
+ * @param {string} cmd - Command to execute
262
+ * @param {Object} options - Execution options
263
+ * @param {string} options.cwd - Working directory (optional)
264
+ * @param {number} options.timeout - Timeout in ms (default: 5000)
265
+ * @returns {string|null} Command output or null
266
+ */
267
+ function execSafe(cmd, options = {}) {
268
+ // Whitelist of safe read-only commands
269
+ const allowedCommands = [
270
+ 'git branch --show-current',
271
+ 'git rev-parse --abbrev-ref HEAD',
272
+ 'git rev-parse --show-toplevel'
273
+ ];
274
+ if (!allowedCommands.includes(cmd)) {
275
+ return null;
276
+ }
277
+
278
+ const { cwd = undefined, timeout = DEFAULT_EXEC_TIMEOUT_MS } = options;
279
+
280
+ try {
281
+ return require('child_process')
282
+ .execSync(cmd, {
283
+ encoding: 'utf8',
284
+ timeout,
285
+ cwd,
286
+ stdio: ['pipe', 'pipe', 'pipe']
287
+ })
288
+ .trim();
289
+ } catch (e) {
290
+ return null;
291
+ }
292
+ }
293
+
294
+ /**
295
+ * Resolve active plan path using cascading resolution with tracking
296
+ *
297
+ * Resolution semantics:
298
+ * - 'session': Explicitly set via set-active-plan.cjs → ACTIVE (directive)
299
+ * - 'branch': Matched from git branch name → SUGGESTED (hint only)
300
+ * - 'mostRecent': REMOVED - was causing stale plan pollution
301
+ *
302
+ * @param {string} sessionId - Session identifier (optional)
303
+ * @param {Object} config - ClaudeKit config
304
+ * @returns {{ path: string|null, resolvedBy: 'session'|'branch'|null }} Resolution result with tracking
305
+ */
306
+ function resolvePlanPath(sessionId, config) {
307
+ const plansDir = config?.paths?.plans || 'plans';
308
+ const resolution = config?.plan?.resolution || {};
309
+ const order = resolution.order || ['session', 'branch'];
310
+ const branchPattern = resolution.branchPattern;
311
+
312
+ for (const method of order) {
313
+ switch (method) {
314
+ case 'session': {
315
+ const state = readSessionState(sessionId);
316
+ if (state?.activePlan) {
317
+ // Issue #335: Handle both absolute and relative paths
318
+ // - Absolute paths (from updated set-active-plan.cjs): use as-is
319
+ // - Relative paths (legacy): resolve using sessionOrigin if available
320
+ let resolvedPath = state.activePlan;
321
+ if (!path.isAbsolute(resolvedPath) && state.sessionOrigin) {
322
+ // Resolve relative path using session origin directory
323
+ resolvedPath = path.join(state.sessionOrigin, resolvedPath);
324
+ }
325
+ return { path: resolvedPath, resolvedBy: 'session' };
326
+ }
327
+ break;
328
+ }
329
+ case 'branch': {
330
+ try {
331
+ const branch = execSafe('git branch --show-current');
332
+ const slug = extractSlugFromBranch(branch, branchPattern);
333
+ if (slug && fs.existsSync(plansDir)) {
334
+ const entries = fs.readdirSync(plansDir, { withFileTypes: true })
335
+ .filter(e => e.isDirectory() && e.name.includes(slug));
336
+ if (entries.length > 0) {
337
+ return {
338
+ path: path.join(plansDir, entries[entries.length - 1].name),
339
+ resolvedBy: 'branch'
340
+ };
341
+ }
342
+ }
343
+ } catch (e) {
344
+ // Ignore errors reading plans dir
345
+ }
346
+ break;
347
+ }
348
+ // NOTE: 'mostRecent' case intentionally removed - was causing stale plan pollution
349
+ }
350
+ }
351
+ return { path: null, resolvedBy: null };
352
+ }
353
+
354
+ /**
355
+ * Normalize path value (trim, remove trailing slashes, handle empty)
356
+ * @param {string} pathValue - Path to normalize
357
+ * @returns {string|null} Normalized path or null if invalid
358
+ */
359
+ function normalizePath(pathValue) {
360
+ if (!pathValue || typeof pathValue !== 'string') return null;
361
+
362
+ // Trim whitespace
363
+ let normalized = pathValue.trim();
364
+
365
+ // Empty after trim = invalid
366
+ if (!normalized) return null;
367
+
368
+ // Remove trailing slashes (but keep root "/" or "C:\")
369
+ normalized = normalized.replace(/[/\\]+$/, '');
370
+
371
+ // If it became empty (was just slashes), return null
372
+ if (!normalized) return null;
373
+
374
+ return normalized;
375
+ }
376
+
377
+ /**
378
+ * Check if path is absolute
379
+ * @param {string} pathValue - Path to check
380
+ * @returns {boolean} True if absolute path
381
+ */
382
+ function isAbsolutePath(pathValue) {
383
+ if (!pathValue) return false;
384
+ // Unix absolute: starts with /
385
+ // Windows absolute: starts with drive letter (C:\) or UNC (\\)
386
+ return path.isAbsolute(pathValue);
387
+ }
388
+
389
+ /**
390
+ * Sanitize path values
391
+ * - Normalizes path (trim, remove trailing slashes)
392
+ * - Allows absolute paths (for consolidated plans use case)
393
+ * - Prevents obvious security issues (null bytes, etc.)
394
+ *
395
+ * @param {string} pathValue - Path to sanitize
396
+ * @param {string} projectRoot - Project root for relative path resolution
397
+ * @returns {string|null} Sanitized path or null if invalid
398
+ */
399
+ function sanitizePath(pathValue, projectRoot) {
400
+ // Normalize first
401
+ const normalized = normalizePath(pathValue);
402
+ if (!normalized) return null;
403
+
404
+ // Block null bytes and other dangerous chars
405
+ if (/[\x00]/.test(normalized)) return null;
406
+
407
+ // Allow absolute paths (user explicitly wants consolidated plans elsewhere)
408
+ if (isAbsolutePath(normalized)) {
409
+ return normalized;
410
+ }
411
+
412
+ // For relative paths, resolve and validate
413
+ const resolved = path.resolve(projectRoot, normalized);
414
+
415
+ // Prevent path traversal outside project (../ attacks)
416
+ // But allow if user explicitly set absolute path
417
+ if (!resolved.startsWith(projectRoot + path.sep) && resolved !== projectRoot) {
418
+ // This is a relative path trying to escape - block it
419
+ return null;
420
+ }
421
+
422
+ return normalized;
423
+ }
424
+
425
+ /**
426
+ * Validate and sanitize config paths
427
+ */
428
+ function sanitizeConfig(config, projectRoot) {
429
+ const result = { ...config };
430
+
431
+ if (result.plan) {
432
+ result.plan = { ...result.plan };
433
+ if (!sanitizePath(result.plan.reportsDir, projectRoot)) {
434
+ result.plan.reportsDir = DEFAULT_CONFIG.plan.reportsDir;
435
+ }
436
+ // Merge resolution defaults
437
+ result.plan.resolution = {
438
+ ...DEFAULT_CONFIG.plan.resolution,
439
+ ...result.plan.resolution
440
+ };
441
+ // Merge validation defaults
442
+ result.plan.validation = {
443
+ ...DEFAULT_CONFIG.plan.validation,
444
+ ...result.plan.validation
445
+ };
446
+ }
447
+
448
+ if (result.paths) {
449
+ result.paths = { ...result.paths };
450
+ if (!sanitizePath(result.paths.docs, projectRoot)) {
451
+ result.paths.docs = DEFAULT_CONFIG.paths.docs;
452
+ }
453
+ if (!sanitizePath(result.paths.plans, projectRoot)) {
454
+ result.paths.plans = DEFAULT_CONFIG.paths.plans;
455
+ }
456
+ }
457
+
458
+ if (result.locale) {
459
+ result.locale = { ...result.locale };
460
+ }
461
+
462
+ return result;
463
+ }
464
+
465
+ /**
466
+ * Load config with cascading resolution: DEFAULT → global → local
467
+ *
468
+ * Resolution order (each layer overrides the previous):
469
+ * 1. DEFAULT_CONFIG (hardcoded defaults)
470
+ * 2. Global config (~/.claude/.ck.json) - user preferences
471
+ * 3. Local config (./.claude/.ck.json) - project-specific overrides
472
+ *
473
+ * @param {Object} options - Options for config loading
474
+ * @param {boolean} options.includeProject - Include project section (default: true)
475
+ * @param {boolean} options.includeAssertions - Include assertions (default: true)
476
+ * @param {boolean} options.includeLocale - Include locale section (default: true)
477
+ */
478
+ function loadConfig(options = {}) {
479
+ const { includeProject = true, includeAssertions = true, includeLocale = true } = options;
480
+ const projectRoot = process.cwd();
481
+
482
+ // Load configs from both locations
483
+ const globalConfig = loadConfigFromPath(GLOBAL_CONFIG_PATH);
484
+ const localConfig = loadConfigFromPath(LOCAL_CONFIG_PATH);
485
+
486
+ // No config files found - use defaults
487
+ if (!globalConfig && !localConfig) {
488
+ return getDefaultConfig(includeProject, includeAssertions, includeLocale);
489
+ }
490
+
491
+ try {
492
+ // Deep merge: DEFAULT → global → local (local wins)
493
+ let merged = deepMerge({}, DEFAULT_CONFIG);
494
+ if (globalConfig) merged = deepMerge(merged, globalConfig);
495
+ if (localConfig) merged = deepMerge(merged, localConfig);
496
+
497
+ // Build result with optional sections
498
+ const result = {
499
+ plan: merged.plan || DEFAULT_CONFIG.plan,
500
+ paths: merged.paths || DEFAULT_CONFIG.paths,
501
+ docs: merged.docs || DEFAULT_CONFIG.docs
502
+ };
503
+
504
+ if (includeLocale) {
505
+ result.locale = merged.locale || DEFAULT_CONFIG.locale;
506
+ }
507
+ // Always include trust config for verification
508
+ result.trust = merged.trust || DEFAULT_CONFIG.trust;
509
+ if (includeProject) {
510
+ result.project = merged.project || DEFAULT_CONFIG.project;
511
+ }
512
+ if (includeAssertions) {
513
+ result.assertions = merged.assertions || [];
514
+ }
515
+ // Coding level for output style selection (-1 to 5, default: -1 = disabled)
516
+ // -1 = disabled (no injection, saves tokens)
517
+ // 0-5 = inject corresponding level guidelines
518
+ result.codingLevel = merged.codingLevel ?? -1;
519
+ // Skills configuration
520
+ result.skills = merged.skills || DEFAULT_CONFIG.skills;
521
+ // Hooks configuration
522
+ result.hooks = merged.hooks || DEFAULT_CONFIG.hooks;
523
+ // Statusline mode
524
+ result.statusline = merged.statusline || 'full';
525
+
526
+ return sanitizeConfig(result, projectRoot);
527
+ } catch (e) {
528
+ return getDefaultConfig(includeProject, includeAssertions, includeLocale);
529
+ }
530
+ }
531
+
532
+ /**
533
+ * Get default config with optional sections
534
+ */
535
+ function getDefaultConfig(includeProject = true, includeAssertions = true, includeLocale = true) {
536
+ const result = {
537
+ plan: { ...DEFAULT_CONFIG.plan },
538
+ paths: { ...DEFAULT_CONFIG.paths },
539
+ docs: { ...DEFAULT_CONFIG.docs },
540
+ codingLevel: -1, // Default: disabled (no injection, saves tokens)
541
+ skills: { ...DEFAULT_CONFIG.skills },
542
+ hooks: { ...DEFAULT_CONFIG.hooks },
543
+ statusline: 'full'
544
+ };
545
+ if (includeLocale) {
546
+ result.locale = { ...DEFAULT_CONFIG.locale };
547
+ }
548
+ if (includeProject) {
549
+ result.project = { ...DEFAULT_CONFIG.project };
550
+ }
551
+ if (includeAssertions) {
552
+ result.assertions = [];
553
+ }
554
+ return result;
555
+ }
556
+
557
+ /**
558
+ * Escape shell special characters for env file values
559
+ * Handles: backslash, double quote, dollar sign, backtick
560
+ */
561
+ function escapeShellValue(str) {
562
+ if (typeof str !== 'string') return str;
563
+ return str
564
+ .replace(/\\/g, '\\\\') // Backslash first
565
+ .replace(/"/g, '\\"') // Double quotes
566
+ .replace(/\$/g, '\\$') // Dollar sign
567
+ .replace(/`/g, '\\`'); // Backticks (command substitution)
568
+ }
569
+
570
+ /**
571
+ * Write environment variable to CLAUDE_ENV_FILE (with escaping)
572
+ */
573
+ function writeEnv(envFile, key, value) {
574
+ if (envFile && value !== null && value !== undefined) {
575
+ const escaped = escapeShellValue(String(value));
576
+ fs.appendFileSync(envFile, `export ${key}="${escaped}"\n`);
577
+ }
578
+ }
579
+
580
+ /**
581
+ * Get reports path based on plan resolution
582
+ * Only uses plan-specific path for 'session' resolved plans (explicitly active)
583
+ * Branch-matched (suggested) plans use default path to avoid pollution
584
+ *
585
+ * @param {string|null} planPath - The plan path
586
+ * @param {string|null} resolvedBy - How plan was resolved ('session'|'branch'|null)
587
+ * @param {Object} planConfig - Plan configuration
588
+ * @param {Object} pathsConfig - Paths configuration
589
+ * @param {string|null} baseDir - Optional base directory for absolute path resolution
590
+ * @returns {string} Reports path (absolute if baseDir provided, relative otherwise)
591
+ */
592
+ function getReportsPath(planPath, resolvedBy, planConfig, pathsConfig, baseDir = null) {
593
+ const reportsDir = normalizePath(planConfig?.reportsDir) || 'reports';
594
+ const plansDir = normalizePath(pathsConfig?.plans) || 'plans';
595
+
596
+ let reportPath;
597
+ // Only use plan-specific reports path if explicitly active (session state)
598
+ // Issue #327: Validate normalized path to prevent whitespace-only paths creating invalid directories
599
+ const normalizedPlanPath = planPath && resolvedBy === 'session' ? normalizePath(planPath) : null;
600
+ if (normalizedPlanPath) {
601
+ reportPath = `${normalizedPlanPath}/${reportsDir}`;
602
+ } else {
603
+ // Default path for no plan or suggested (branch-matched) plans
604
+ reportPath = `${plansDir}/${reportsDir}`;
605
+ }
606
+
607
+ // Return absolute path if baseDir provided
608
+ if (baseDir) {
609
+ return path.join(baseDir, reportPath);
610
+ }
611
+ return reportPath + '/';
612
+ }
613
+
614
+ /**
615
+ * Format issue ID with prefix
616
+ */
617
+ function formatIssueId(issueId, planConfig) {
618
+ if (!issueId) return null;
619
+ return planConfig.issuePrefix ? `${planConfig.issuePrefix}${issueId}` : `#${issueId}`;
620
+ }
621
+
622
+ /**
623
+ * Extract issue ID from branch name
624
+ */
625
+ function extractIssueFromBranch(branch) {
626
+ if (!branch) return null;
627
+ const patterns = [
628
+ /(?:issue|gh|fix|feat|bug)[/-]?(\d+)/i,
629
+ /[/-](\d+)[/-]/,
630
+ /#(\d+)/
631
+ ];
632
+ for (const pattern of patterns) {
633
+ const match = branch.match(pattern);
634
+ if (match) return match[1];
635
+ }
636
+ return null;
637
+ }
638
+
639
+ /**
640
+ * Format date according to dateFormat config
641
+ * Supports: YYMMDD, YYMMDD-HHmm, YYYYMMDD, etc.
642
+ * @param {string} format - Date format string
643
+ * @returns {string} Formatted date
644
+ */
645
+ function formatDate(format) {
646
+ const now = new Date();
647
+ const pad = (n, len = 2) => String(n).padStart(len, '0');
648
+
649
+ const tokens = {
650
+ 'YYYY': now.getFullYear(),
651
+ 'YY': String(now.getFullYear()).slice(-2),
652
+ 'MM': pad(now.getMonth() + 1),
653
+ 'DD': pad(now.getDate()),
654
+ 'HH': pad(now.getHours()),
655
+ 'mm': pad(now.getMinutes()),
656
+ 'ss': pad(now.getSeconds())
657
+ };
658
+
659
+ let result = format;
660
+ for (const [token, value] of Object.entries(tokens)) {
661
+ result = result.replace(token, value);
662
+ }
663
+ return result;
664
+ }
665
+
666
+ /**
667
+ * Validate naming pattern result
668
+ * Ensures pattern resolves to a usable directory name
669
+ *
670
+ * @param {string} pattern - Resolved naming pattern
671
+ * @returns {{ valid: boolean, error?: string }} Validation result
672
+ */
673
+ function validateNamingPattern(pattern) {
674
+ if (!pattern || typeof pattern !== 'string') {
675
+ return { valid: false, error: 'Pattern is empty or not a string' };
676
+ }
677
+
678
+ // After removing {slug} placeholder, should still have content
679
+ const withoutSlug = pattern.replace(/\{slug\}/g, '').replace(/-+/g, '-').replace(/^-|-$/g, '');
680
+ if (!withoutSlug) {
681
+ return { valid: false, error: 'Pattern resolves to empty after removing {slug}' };
682
+ }
683
+
684
+ // Check for remaining unresolved placeholders (besides {slug})
685
+ const unresolvedMatch = withoutSlug.match(/\{[^}]+\}/);
686
+ if (unresolvedMatch) {
687
+ return { valid: false, error: `Unresolved placeholder: ${unresolvedMatch[0]}` };
688
+ }
689
+
690
+ // Pattern must contain {slug} for agents to substitute
691
+ if (!pattern.includes('{slug}')) {
692
+ return { valid: false, error: 'Pattern must contain {slug} placeholder' };
693
+ }
694
+
695
+ return { valid: true };
696
+ }
697
+
698
+ /**
699
+ * Resolve naming pattern with date and optional issue prefix
700
+ * Keeps {slug} as placeholder for agents to substitute
701
+ *
702
+ * Example: namingFormat="{date}-{issue}-{slug}", dateFormat="YYMMDD-HHmm", issue="GH-88"
703
+ * Returns: "251212-1830-GH-88-{slug}" (if issue exists)
704
+ * Returns: "251212-1830-{slug}" (if no issue)
705
+ *
706
+ * @param {Object} planConfig - Plan configuration
707
+ * @param {string|null} gitBranch - Current git branch (for issue extraction)
708
+ * @returns {string} Resolved naming pattern with {slug} placeholder
709
+ */
710
+ function resolveNamingPattern(planConfig, gitBranch) {
711
+ const { namingFormat, dateFormat, issuePrefix } = planConfig;
712
+ const formattedDate = formatDate(dateFormat);
713
+
714
+ // Try to extract issue ID from branch name
715
+ const issueId = extractIssueFromBranch(gitBranch);
716
+ const fullIssue = issueId && issuePrefix ? `${issuePrefix}${issueId}` : null;
717
+
718
+ // Build pattern by substituting {date} and {issue}, keep {slug}
719
+ let pattern = namingFormat;
720
+ pattern = pattern.replace('{date}', formattedDate);
721
+
722
+ if (fullIssue) {
723
+ pattern = pattern.replace('{issue}', fullIssue);
724
+ } else {
725
+ // Remove {issue} and any trailing/leading dash
726
+ pattern = pattern.replace(/-?\{issue\}-?/, '-').replace(/--+/g, '-');
727
+ }
728
+
729
+ // Clean up the result:
730
+ // - Remove leading/trailing hyphens
731
+ // - Collapse multiple hyphens (except around {slug})
732
+ pattern = pattern
733
+ .replace(/^-+/, '') // Remove leading hyphens
734
+ .replace(/-+$/, '') // Remove trailing hyphens
735
+ .replace(/-+(\{slug\})/g, '-$1') // Single hyphen before {slug}
736
+ .replace(/(\{slug\})-+/g, '$1-') // Single hyphen after {slug}
737
+ .replace(/--+/g, '-'); // Collapse other multiple hyphens
738
+
739
+ // Validate the resulting pattern
740
+ const validation = validateNamingPattern(pattern);
741
+ if (!validation.valid) {
742
+ // Log warning but return pattern anyway (fail-safe)
743
+ if (process.env.CK_DEBUG) {
744
+ console.error(`[ck-config] Warning: ${validation.error}`);
745
+ }
746
+ }
747
+
748
+ return pattern;
749
+ }
750
+
751
+ /**
752
+ * Get current git branch (safe execution)
753
+ * @param {string|null} cwd - Working directory to run git command from (optional)
754
+ * @returns {string|null} Current branch name or null
755
+ */
756
+ function getGitBranch(cwd = null) {
757
+ return execSafe('git branch --show-current', { cwd: cwd || undefined });
758
+ }
759
+
760
+ /**
761
+ * Get git repository root directory
762
+ * @param {string|null} cwd - Working directory to run git command from (optional)
763
+ * @returns {string|null} Git root absolute path or null if not in git repo
764
+ */
765
+ function getGitRoot(cwd = null) {
766
+ return execSafe('git rev-parse --show-toplevel', { cwd: cwd || undefined });
767
+ }
768
+
769
+ /**
770
+ * Extract task list ID from plan resolution for Claude Code Tasks coordination
771
+ * Only returns ID for session-resolved plans (explicitly active, not branch-suggested)
772
+ *
773
+ * Cross-platform: path.basename() handles both Unix/Windows separators
774
+ *
775
+ * @param {{ path: string|null, resolvedBy: 'session'|'branch'|null }} resolved - Plan resolution result
776
+ * @returns {string|null} Task list ID (plan directory name) or null
777
+ */
778
+ function extractTaskListId(resolved) {
779
+ if (!resolved || resolved.resolvedBy !== 'session' || !resolved.path) {
780
+ return null;
781
+ }
782
+ return path.basename(resolved.path);
783
+ }
784
+
785
+ /**
786
+ * Check if a hook is enabled in config
787
+ * Returns true if hook is not defined (default enabled)
788
+ *
789
+ * @param {string} hookName - Hook name (script basename without .cjs)
790
+ * @returns {boolean} Whether hook is enabled
791
+ */
792
+ function isHookEnabled(hookName) {
793
+ const config = loadConfig({ includeProject: false, includeAssertions: false, includeLocale: false });
794
+ const hooks = config.hooks || {};
795
+ // Return true if undefined (default enabled), otherwise return the boolean value
796
+ return hooks[hookName] !== false;
797
+ }
798
+
799
+ module.exports = {
800
+ CONFIG_PATH,
801
+ LOCAL_CONFIG_PATH,
802
+ GLOBAL_CONFIG_PATH,
803
+ DEFAULT_CONFIG,
804
+ INVALID_FILENAME_CHARS,
805
+ deepMerge,
806
+ loadConfigFromPath,
807
+ loadConfig,
808
+ normalizePath,
809
+ isAbsolutePath,
810
+ sanitizePath,
811
+ sanitizeSlug,
812
+ sanitizeConfig,
813
+ escapeShellValue,
814
+ writeEnv,
815
+ getSessionTempPath,
816
+ readSessionState,
817
+ writeSessionState,
818
+ resolvePlanPath,
819
+ extractSlugFromBranch,
820
+ findMostRecentPlan,
821
+ getReportsPath,
822
+ formatIssueId,
823
+ extractIssueFromBranch,
824
+ formatDate,
825
+ validateNamingPattern,
826
+ resolveNamingPattern,
827
+ getGitBranch,
828
+ getGitRoot,
829
+ extractTaskListId,
830
+ isHookEnabled
831
+ };