claude-flow-novice 2.15.10 → 2.16.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 (652) hide show
  1. package/.claude/cfn-extras/agents/cfn-v3-coordinator.md +517 -0
  2. package/.claude/commands/cfn-loop-cli.md +158 -464
  3. package/.claude/commands/cfn-loop-trigger.md +114 -0
  4. package/.claude/hooks/cfn-invoke-post-edit-ts.sh +100 -0
  5. package/.claude/hooks/cfn-invoke-post-edit-ts.sh.backup +78 -0
  6. package/.claude/hooks/cfn-invoke-post-edit.sh +22 -0
  7. package/.claude/hooks/cfn-invoke-post-edit.sh.backup +87 -0
  8. package/.claude/hooks/cfn-invoke-pre-edit-ts.sh +116 -0
  9. package/.claude/hooks/cfn-invoke-pre-edit-ts.sh.backup +94 -0
  10. package/.claude/hooks/cfn-invoke-pre-edit.sh +22 -0
  11. package/.claude/hooks/cfn-invoke-pre-edit.sh.backup +88 -0
  12. package/.claude/skills/cfn-agent-spawning/SKILL.md +48 -1
  13. package/.claude/skills/cfn-agent-spawning/SKILL.md.backup +135 -0
  14. package/.claude/skills/cfn-agent-spawning/TYPESCRIPT_MIGRATION.md +567 -0
  15. package/.claude/skills/cfn-agent-spawning/check-dependencies.sh +22 -0
  16. package/.claude/skills/{cfn-redis-coordination/check-dependencies.sh → cfn-agent-spawning/check-dependencies.sh.backup} +3 -5
  17. package/.claude/skills/cfn-agent-spawning/get-agent-provider-env.sh +22 -0
  18. package/.claude/skills/cfn-agent-spawning/get-agent-provider-env.sh.backup +127 -0
  19. package/.claude/skills/cfn-agent-spawning/parse-agent-provider.sh +22 -0
  20. package/.claude/skills/cfn-agent-spawning/parse-agent-provider.sh.backup +59 -0
  21. package/.claude/skills/cfn-agent-spawning/spawn-agent-wrapper.sh +63 -0
  22. package/.claude/skills/cfn-agent-spawning/spawn-agent-wrapper.sh.backup +41 -0
  23. package/.claude/skills/cfn-agent-spawning/spawn-agent.sh +26 -1
  24. package/.claude/skills/cfn-agent-spawning/spawn-templates.sh +22 -0
  25. package/.claude/skills/cfn-agent-spawning/spawn-templates.sh.backup +613 -0
  26. package/.claude/skills/cfn-agent-spawning/spawn-worker.sh +22 -0
  27. package/.claude/skills/cfn-agent-spawning/spawn-worker.sh.backup +176 -0
  28. package/.claude/skills/cfn-loop-orchestration/.backups/unknown/1763619700_33aff4a69b99159e4e849107ebc4d09f/metadata.json +8 -0
  29. package/.claude/skills/cfn-loop-orchestration/.backups/unknown/1763619700_33aff4a69b99159e4e849107ebc4d09f/original +271 -0
  30. package/.claude/skills/cfn-loop-orchestration/.backups/unknown/1763619700_33aff4a69b99159e4e849107ebc4d09f/revert.sh +7 -0
  31. package/.claude/skills/cfn-loop-orchestration/.backups/unknown/1763671642_06496e8c399a79db08167cc00ed4b31e/metadata.json +8 -0
  32. package/.claude/skills/cfn-loop-orchestration/.backups/unknown/1763671642_06496e8c399a79db08167cc00ed4b31e/original +325 -0
  33. package/.claude/skills/cfn-loop-orchestration/.backups/unknown/1763671642_06496e8c399a79db08167cc00ed4b31e/revert.sh +7 -0
  34. package/.claude/skills/cfn-loop-orchestration/CLI_IMPLEMENTATION_SUMMARY.md +330 -0
  35. package/.claude/skills/cfn-loop-orchestration/CONFIGURATION_IMPROVEMENTS.md +318 -0
  36. package/.claude/skills/cfn-loop-orchestration/CONTEXT_LOOKUP_MIGRATION.md +308 -0
  37. package/.claude/skills/cfn-loop-orchestration/CONTEXT_LOOKUP_QUICK_START.md +378 -0
  38. package/.claude/skills/cfn-loop-orchestration/E2E_VALIDATION_REPORT.md +262 -0
  39. package/.claude/skills/cfn-loop-orchestration/IMPLEMENTATION_SUMMARY.md +319 -519
  40. package/.claude/skills/cfn-loop-orchestration/NORTH_STAR_E2E_REPORT.md +299 -0
  41. package/.claude/skills/cfn-loop-orchestration/NORTH_STAR_EXECUTION_SUMMARY.md +403 -0
  42. package/.claude/skills/cfn-loop-orchestration/NORTH_STAR_INDEX.md +323 -0
  43. package/.claude/skills/cfn-loop-orchestration/SKILL.md +159 -48
  44. package/.claude/skills/cfn-loop-orchestration/SPAWN_AGENTS_IMPLEMENTATION.md +188 -0
  45. package/.claude/skills/cfn-loop-orchestration/TEST_COVERAGE_REPORT.md +335 -0
  46. package/.claude/skills/cfn-loop-orchestration/TEST_COVERAGE_SUMMARY.md +456 -0
  47. package/.claude/skills/cfn-loop-orchestration/TYPESCRIPT_INTEGRATION_REPORT.md +709 -0
  48. package/.claude/skills/cfn-loop-orchestration/TYPESCRIPT_INTEGRATION_SUMMARY.md +257 -0
  49. package/.claude/skills/cfn-loop-orchestration/VALIDATION_REPORT.md +572 -0
  50. package/.claude/skills/cfn-loop-orchestration/VALIDATION_SUMMARY.txt +196 -0
  51. package/.claude/skills/cfn-loop-orchestration/VALIDATOR_MODULE_GUIDE.md +526 -0
  52. package/.claude/skills/cfn-loop-orchestration/archive/legacy-bash/README.md +167 -0
  53. package/.claude/skills/cfn-loop-orchestration/archive/legacy-bash/orchestrate-enhanced.sh +548 -0
  54. package/{claude-assets/skills/cfn-loop-orchestration → .claude/skills/cfn-loop-orchestration/archive/legacy-bash}/orchestrate-wrapper.sh +11 -1
  55. package/.claude/skills/cfn-loop-orchestration/archive/legacy-bash/orchestrate.sh +182 -0
  56. package/.claude/skills/cfn-loop-orchestration/e2e-validation-fixed.js +240 -0
  57. package/.claude/skills/cfn-loop-orchestration/e2e-validation.js +213 -0
  58. package/.claude/skills/cfn-loop-orchestration/package-lock.json +3 -0
  59. package/.claude/skills/cfn-loop-orchestration/package.json +4 -0
  60. package/.claude/skills/cfn-loop-orchestration/run-north-star-e2e.ts +210 -0
  61. package/.claude/skills/cfn-loop-orchestration/src/cli/orchestrator-cli.ts +396 -0
  62. package/.claude/skills/cfn-loop-orchestration/src/helpers/CONFIDENCE_AGGREGATOR.md +564 -0
  63. package/.claude/skills/cfn-loop-orchestration/src/helpers/CONFIDENCE_AGGREGATOR_QUICK_REF.md +241 -0
  64. package/.claude/skills/cfn-loop-orchestration/src/helpers/CONTEXT_INJECTOR_IMPLEMENTATION.md +375 -0
  65. package/.claude/skills/cfn-loop-orchestration/src/helpers/CONTEXT_INJECTOR_QUICK_REFERENCE.md +362 -0
  66. package/.claude/skills/cfn-loop-orchestration/src/helpers/CONTEXT_INJECTOR_README.md +307 -0
  67. package/.claude/skills/cfn-loop-orchestration/src/helpers/CONTEXT_INJECTOR_USAGE_GUIDE.md +508 -0
  68. package/.claude/skills/cfn-loop-orchestration/src/helpers/confidence-aggregator.ts +473 -0
  69. package/.claude/skills/cfn-loop-orchestration/src/helpers/consensus.ts +1 -1
  70. package/.claude/skills/cfn-loop-orchestration/src/helpers/context-injector.ts +349 -0
  71. package/.claude/skills/cfn-loop-orchestration/src/helpers/context-lookup.ts +486 -0
  72. package/.claude/skills/cfn-loop-orchestration/src/helpers/deliverable-verifier.ts +6 -2
  73. package/.claude/skills/cfn-loop-orchestration/src/helpers/gate-check.ts +1 -1
  74. package/.claude/skills/cfn-loop-orchestration/src/helpers/product-owner-decision.ts +316 -0
  75. package/.claude/skills/cfn-loop-orchestration/src/helpers/spawn-agents.ts +357 -0
  76. package/.claude/skills/cfn-loop-orchestration/src/helpers/validator.ts +276 -0
  77. package/.claude/skills/cfn-loop-orchestration/src/index.ts +2 -0
  78. package/.claude/skills/cfn-loop-orchestration/src/orchestrate.ts +743 -2
  79. package/.claude/skills/cfn-loop-orchestration/src/types.ts +56 -0
  80. package/.claude/skills/cfn-loop-orchestration/test-cli.sh +92 -0
  81. package/.claude/skills/cfn-loop-orchestration/test-typescript-integration.sh +442 -0
  82. package/.claude/skills/cfn-loop-orchestration/tests/agent-spawner.test.ts +124 -0
  83. package/.claude/skills/cfn-loop-orchestration/tests/confidence-aggregator.test.ts +604 -0
  84. package/.claude/skills/cfn-loop-orchestration/tests/context-injector.test.ts +561 -0
  85. package/.claude/skills/cfn-loop-orchestration/tests/context-lookup.test.ts +661 -0
  86. package/.claude/skills/cfn-loop-orchestration/tests/deliverable-verifier.test.ts +2 -2
  87. package/.claude/skills/cfn-loop-orchestration/tests/gate-check-edge-cases.test.ts +422 -0
  88. package/.claude/skills/cfn-loop-orchestration/tests/gate-checker.test.ts +276 -0
  89. package/.claude/skills/cfn-loop-orchestration/tests/logger.test.ts +291 -0
  90. package/.claude/skills/cfn-loop-orchestration/tests/north-star-e2e.test.ts +334 -0
  91. package/.claude/skills/cfn-loop-orchestration/tests/redis-coordinator.test.ts +321 -0
  92. package/.claude/skills/cfn-loop-orchestration/tests/spawn-agents.test.ts +284 -0
  93. package/.claude/skills/cfn-loop-orchestration/tests/validator.test.ts +643 -0
  94. package/.claude/skills/cfn-loop-validation/IMPLEMENTATION_SUMMARY.md +672 -0
  95. package/.claude/skills/cfn-loop-validation/INDEX.md +531 -0
  96. package/.claude/skills/cfn-loop-validation/README_TYPESCRIPT.md +454 -0
  97. package/.claude/skills/cfn-loop-validation/SKILL.md +48 -1
  98. package/.claude/skills/cfn-loop-validation/SKILL.md.backup +353 -0
  99. package/.claude/skills/cfn-loop-validation/SKILL_TYPESCRIPT.md +782 -0
  100. package/.claude/skills/cfn-loop-validation/VAPOR_DETECTION_EXAMPLES.md +598 -0
  101. package/.claude/skills/cfn-loop-validation/check-dependencies.sh +22 -0
  102. package/{claude-assets/skills/cfn-redis-coordination/check-dependencies.sh → .claude/skills/cfn-loop-validation/check-dependencies.sh.backup} +4 -5
  103. package/.claude/skills/cfn-loop-validation/detect-vapor.sh +59 -0
  104. package/.claude/skills/cfn-loop-validation/detect-vapor.sh.backup +37 -0
  105. package/.claude/skills/cfn-loop-validation/dist/.tsbuildinfo +1 -0
  106. package/.claude/skills/cfn-loop-validation/dist/cli/detect-vapor.d.ts +14 -0
  107. package/.claude/skills/cfn-loop-validation/dist/cli/detect-vapor.d.ts.map +1 -0
  108. package/.claude/skills/cfn-loop-validation/dist/cli/detect-vapor.js +185 -0
  109. package/.claude/skills/cfn-loop-validation/dist/cli/detect-vapor.js.map +1 -0
  110. package/.claude/skills/cfn-loop-validation/dist/cli/validate-deliverables.d.ts +14 -0
  111. package/.claude/skills/cfn-loop-validation/dist/cli/validate-deliverables.d.ts.map +1 -0
  112. package/.claude/skills/cfn-loop-validation/dist/cli/validate-deliverables.js +176 -0
  113. package/.claude/skills/cfn-loop-validation/dist/cli/validate-deliverables.js.map +1 -0
  114. package/.claude/skills/cfn-loop-validation/dist/cli/validate-gate.d.ts +19 -0
  115. package/.claude/skills/cfn-loop-validation/dist/cli/validate-gate.d.ts.map +1 -0
  116. package/.claude/skills/cfn-loop-validation/dist/cli/validate-gate.js +123 -0
  117. package/.claude/skills/cfn-loop-validation/dist/cli/validate-gate.js.map +1 -0
  118. package/.claude/skills/cfn-loop-validation/dist/types.d.ts +156 -0
  119. package/.claude/skills/cfn-loop-validation/dist/types.d.ts.map +1 -0
  120. package/.claude/skills/cfn-loop-validation/dist/types.js +66 -0
  121. package/.claude/skills/cfn-loop-validation/dist/types.js.map +1 -0
  122. package/.claude/skills/cfn-loop-validation/dist/validator.d.ts +85 -0
  123. package/.claude/skills/cfn-loop-validation/dist/validator.d.ts.map +1 -0
  124. package/.claude/skills/cfn-loop-validation/dist/validator.js +411 -0
  125. package/.claude/skills/cfn-loop-validation/dist/validator.js.map +1 -0
  126. package/.claude/skills/cfn-loop-validation/orchestrate-cfn-loop.sh +22 -0
  127. package/.claude/skills/cfn-loop-validation/orchestrate-cfn-loop.sh.backup +252 -0
  128. package/.claude/skills/cfn-loop-validation/package.json +93 -0
  129. package/.claude/skills/cfn-loop-validation/src/cli/detect-vapor.ts +177 -0
  130. package/.claude/skills/cfn-loop-validation/src/cli/validate-deliverables.ts +161 -0
  131. package/.claude/skills/cfn-loop-validation/src/cli/validate-gate.ts +139 -0
  132. package/.claude/skills/cfn-loop-validation/src/types.ts +215 -0
  133. package/.claude/skills/cfn-loop-validation/src/validator.ts +503 -0
  134. package/.claude/skills/cfn-loop-validation/tests/validator.test.ts +537 -0
  135. package/.claude/skills/{cfn-redis-coordination → cfn-loop-validation}/tsconfig.json +34 -31
  136. package/.claude/skills/cfn-loop-validation/validate-deliverables.sh +59 -0
  137. package/.claude/skills/cfn-loop-validation/validate-deliverables.sh.backup +37 -0
  138. package/.claude/skills/cfn-loop-validation/validate-gate.sh +63 -0
  139. package/.claude/skills/cfn-loop-validation/validate-gate.sh.backup +41 -0
  140. package/.claude/skills/cfn-loop-validation/validate-iteration.sh +22 -0
  141. package/.claude/skills/cfn-loop-validation/validate-iteration.sh.backup +134 -0
  142. package/.claude/skills/cfn-product-owner-decision/SKILL.md +479 -147
  143. package/.claude/skills/cfn-product-owner-decision/TYPESCRIPT_IMPLEMENTATION.md +653 -0
  144. package/{claude-assets/skills/cfn-product-owner-decision → .claude/skills/cfn-product-owner-decision/archive/legacy-bash}/execute-decision.sh +24 -2
  145. package/.claude/skills/pre-edit-backup/SKILL.md +324 -0
  146. package/.claude/skills/pre-edit-backup/SKILL.md.backup +277 -0
  147. package/.claude/skills/pre-edit-backup/backup.sh +22 -0
  148. package/.claude/skills/pre-edit-backup/backup.sh.backup +107 -0
  149. package/claude-assets/agents/cfn-dev-team/CLAUDE.md +9 -81
  150. package/claude-assets/agents/cfn-dev-team/architecture/base-template-generator.md +4 -4
  151. package/claude-assets/agents/cfn-dev-team/architecture/planner.md +4 -4
  152. package/claude-assets/agents/cfn-dev-team/architecture/system-architect.md +5 -5
  153. package/claude-assets/agents/cfn-dev-team/coordinators/cfn-frontend-coordinator.md +1 -0
  154. package/claude-assets/agents/cfn-dev-team/coordinators/consensus-builder.md +1 -0
  155. package/claude-assets/agents/cfn-dev-team/coordinators/handoff-coordinator.md +1 -0
  156. package/claude-assets/agents/cfn-dev-team/coordinators/multi-sprint-coordinator.md +1 -0
  157. package/claude-assets/agents/cfn-dev-team/dev-ops/devops-engineer.md +14 -4
  158. package/claude-assets/agents/cfn-dev-team/dev-ops/docker-specialist.md +63 -70
  159. package/claude-assets/agents/cfn-dev-team/dev-ops/kubernetes-specialist.md +50 -70
  160. package/claude-assets/agents/cfn-dev-team/dev-ops/monitoring-specialist.md +13 -4
  161. package/claude-assets/agents/cfn-dev-team/developers/api-gateway-specialist.md +27 -58
  162. package/claude-assets/agents/cfn-dev-team/developers/backend-developer.md +40 -58
  163. package/claude-assets/agents/cfn-dev-team/developers/data/data-engineer.md +18 -20
  164. package/claude-assets/agents/cfn-dev-team/developers/database/database-architect.md +19 -28
  165. package/claude-assets/agents/cfn-dev-team/developers/frontend/mobile-dev.md +15 -19
  166. package/claude-assets/agents/cfn-dev-team/developers/frontend/react-frontend-engineer.md +15 -10
  167. package/claude-assets/agents/cfn-dev-team/developers/frontend/typescript-specialist.md +15 -10
  168. package/claude-assets/agents/cfn-dev-team/developers/frontend/ui-designer.md +15 -25
  169. package/claude-assets/agents/cfn-dev-team/developers/graphql-specialist.md +17 -21
  170. package/claude-assets/agents/cfn-dev-team/developers/rust-developer.md +17 -21
  171. package/claude-assets/agents/cfn-dev-team/product-owners/accessibility-advocate-persona.md +4 -4
  172. package/claude-assets/agents/cfn-dev-team/product-owners/cto-agent.md +4 -4
  173. package/claude-assets/agents/cfn-dev-team/product-owners/power-user-persona.md +4 -4
  174. package/claude-assets/agents/cfn-dev-team/product-owners/product-owner.md +19 -27
  175. package/claude-assets/agents/cfn-dev-team/reviewers/code-reviewer.md +20 -51
  176. package/claude-assets/agents/cfn-dev-team/reviewers/quality/code-quality-validator.md +22 -71
  177. package/claude-assets/agents/cfn-dev-team/reviewers/quality/perf-analyzer.md +21 -64
  178. package/claude-assets/agents/cfn-dev-team/reviewers/quality/performance-benchmarker.md +22 -67
  179. package/claude-assets/agents/cfn-dev-team/reviewers/quality/security-specialist.md +23 -67
  180. package/claude-assets/agents/cfn-dev-team/testers/api-testing-specialist.md +14 -70
  181. package/claude-assets/agents/cfn-dev-team/testers/chaos-engineering-specialist.md +25 -73
  182. package/claude-assets/agents/cfn-dev-team/testers/contract-tester.md +26 -65
  183. package/claude-assets/agents/cfn-dev-team/testers/e2e/playwright-tester.md +5 -5
  184. package/claude-assets/agents/cfn-dev-team/testers/integration-tester.md +27 -67
  185. package/claude-assets/agents/cfn-dev-team/testers/interaction-tester.md +16 -73
  186. package/claude-assets/agents/cfn-dev-team/testers/load-testing-specialist.md +27 -67
  187. package/claude-assets/agents/cfn-dev-team/testers/mutation-testing-specialist.md +27 -60
  188. package/claude-assets/agents/cfn-dev-team/testers/playwright-tester.md +15 -74
  189. package/claude-assets/agents/cfn-dev-team/testers/tester.md +14 -60
  190. package/claude-assets/agents/cfn-dev-team/testers/unit/tdd-london-unit-swarm.md +5 -5
  191. package/claude-assets/agents/cfn-dev-team/testers/validation/validation-production-validator.md +4 -4
  192. package/claude-assets/agents/cfn-dev-team/testing/test-validation-agent.md +4 -4
  193. package/claude-assets/agents/cfn-dev-team/utility/agent-builder.md +16 -16
  194. package/claude-assets/agents/cfn-dev-team/utility/analyst.md +16 -32
  195. package/claude-assets/agents/cfn-dev-team/utility/code-booster.md +17 -17
  196. package/claude-assets/agents/cfn-dev-team/utility/context-curator.md +10 -5
  197. package/claude-assets/agents/cfn-dev-team/utility/epic-creator.md +12 -102
  198. package/claude-assets/agents/cfn-dev-team/utility/memory-leak-specialist.md +127 -814
  199. package/claude-assets/agents/cfn-dev-team/utility/researcher.md +16 -25
  200. package/claude-assets/agents/cfn-dev-team/utility/z-ai-specialist.md +157 -667
  201. package/claude-assets/agents/custom/cfn-docker-expert.md +102 -0
  202. package/claude-assets/agents/custom/cfn-loops-cli-expert.md +129 -0
  203. package/claude-assets/cfn-extras/agents/cfn-v3-coordinator.md +517 -0
  204. package/claude-assets/commands/cfn-loop-cli.md +158 -464
  205. package/claude-assets/commands/cfn-loop-trigger.md +114 -0
  206. package/claude-assets/hooks/SKILL.md +518 -0
  207. package/claude-assets/hooks/SKILL.md.backup +471 -0
  208. package/claude-assets/hooks/cfn-invoke-post-edit-ts.sh +100 -0
  209. package/claude-assets/hooks/cfn-invoke-post-edit-ts.sh.backup +78 -0
  210. package/claude-assets/hooks/cfn-invoke-post-edit.sh +22 -0
  211. package/claude-assets/hooks/cfn-invoke-post-edit.sh.backup +87 -0
  212. package/claude-assets/hooks/cfn-invoke-pre-edit-ts.sh +116 -0
  213. package/claude-assets/hooks/cfn-invoke-pre-edit-ts.sh.backup +94 -0
  214. package/claude-assets/hooks/cfn-invoke-pre-edit.sh +22 -0
  215. package/claude-assets/hooks/cfn-invoke-pre-edit.sh.backup +88 -0
  216. package/claude-assets/skills/cfn-agent-selection-with-fallback/DELIVERABLES.md +409 -0
  217. package/claude-assets/skills/cfn-agent-selection-with-fallback/IMPLEMENTATION_SUMMARY.md +396 -0
  218. package/claude-assets/skills/cfn-agent-selection-with-fallback/INTEGRATION_GUIDE.md +308 -0
  219. package/claude-assets/skills/cfn-agent-selection-with-fallback/QUICK_REFERENCE.md +239 -0
  220. package/claude-assets/skills/cfn-agent-selection-with-fallback/SKILL.md +107 -1
  221. package/claude-assets/skills/cfn-agent-selection-with-fallback/SKILL.md.backup +302 -0
  222. package/claude-assets/skills/cfn-agent-selection-with-fallback/TYPESCRIPT_MIGRATION.md +295 -0
  223. package/claude-assets/skills/cfn-agent-selection-with-fallback/dist/agent-selector.cjs +297 -0
  224. package/claude-assets/skills/cfn-agent-selection-with-fallback/dist/agent-selector.js +297 -0
  225. package/claude-assets/skills/cfn-agent-selection-with-fallback/dist/cli.cjs +96 -0
  226. package/claude-assets/skills/cfn-agent-selection-with-fallback/dist/cli.js +96 -0
  227. package/claude-assets/skills/cfn-agent-selection-with-fallback/select-agents-ts.sh +45 -0
  228. package/claude-assets/skills/cfn-agent-selection-with-fallback/select-agents-ts.sh.backup +23 -0
  229. package/claude-assets/skills/cfn-agent-selection-with-fallback/select-agents.sh +22 -0
  230. package/claude-assets/skills/cfn-agent-selection-with-fallback/select-agents.sh.backup +173 -0
  231. package/claude-assets/skills/cfn-agent-selection-with-fallback/src/agent-selector.test.ts +357 -0
  232. package/claude-assets/skills/cfn-agent-selection-with-fallback/src/agent-selector.ts +350 -0
  233. package/claude-assets/skills/cfn-agent-selection-with-fallback/src/cli.ts +74 -0
  234. package/claude-assets/skills/cfn-agent-selection-with-fallback/task-classifier.sh +22 -0
  235. package/claude-assets/skills/cfn-agent-selection-with-fallback/task-classifier.sh.backup +71 -0
  236. package/claude-assets/skills/cfn-agent-selection-with-fallback/tsconfig.json +18 -0
  237. package/claude-assets/skills/cfn-agent-spawning/SKILL.md +48 -1
  238. package/claude-assets/skills/cfn-agent-spawning/SKILL.md.backup +135 -0
  239. package/claude-assets/skills/cfn-agent-spawning/TYPESCRIPT_MIGRATION.md +567 -0
  240. package/claude-assets/skills/cfn-agent-spawning/check-dependencies.sh +22 -0
  241. package/claude-assets/skills/cfn-agent-spawning/check-dependencies.sh.backup +30 -0
  242. package/claude-assets/skills/cfn-agent-spawning/get-agent-provider-env.sh +22 -0
  243. package/claude-assets/skills/cfn-agent-spawning/get-agent-provider-env.sh.backup +127 -0
  244. package/claude-assets/skills/cfn-agent-spawning/parse-agent-provider.sh +22 -0
  245. package/claude-assets/skills/cfn-agent-spawning/parse-agent-provider.sh.backup +59 -0
  246. package/claude-assets/skills/cfn-agent-spawning/spawn-agent-wrapper.sh +63 -0
  247. package/claude-assets/skills/cfn-agent-spawning/spawn-agent-wrapper.sh.backup +41 -0
  248. package/claude-assets/skills/cfn-agent-spawning/spawn-agent.sh +26 -1
  249. package/claude-assets/skills/cfn-agent-spawning/spawn-templates.sh +22 -0
  250. package/claude-assets/skills/cfn-agent-spawning/spawn-templates.sh.backup +613 -0
  251. package/claude-assets/skills/cfn-agent-spawning/spawn-worker.sh +22 -0
  252. package/claude-assets/skills/cfn-agent-spawning/spawn-worker.sh.backup +176 -0
  253. package/claude-assets/skills/cfn-coordination/agent-completion.sh.backup +36 -0
  254. package/claude-assets/skills/cfn-coordination/coordination-signal.sh.backup +36 -0
  255. package/claude-assets/skills/cfn-coordination/coordination-wait.sh.backup +36 -0
  256. package/claude-assets/skills/cfn-dependency-ingestion/README.md +101 -0
  257. package/claude-assets/skills/cfn-dependency-ingestion/SKILL.md +369 -0
  258. package/claude-assets/skills/cfn-dependency-ingestion/build.sh +23 -0
  259. package/claude-assets/skills/cfn-dependency-ingestion/dist/ingest-dependencies.js +478 -0
  260. package/claude-assets/skills/cfn-dependency-ingestion/ingest-dependencies.sh +295 -0
  261. package/claude-assets/skills/cfn-dependency-ingestion/src/ingest-dependencies.ts +563 -0
  262. package/claude-assets/skills/cfn-loop-orchestration/.backups/unknown/1763619700_33aff4a69b99159e4e849107ebc4d09f/metadata.json +8 -0
  263. package/claude-assets/skills/cfn-loop-orchestration/.backups/unknown/1763619700_33aff4a69b99159e4e849107ebc4d09f/original +271 -0
  264. package/claude-assets/skills/cfn-loop-orchestration/.backups/unknown/1763619700_33aff4a69b99159e4e849107ebc4d09f/revert.sh +7 -0
  265. package/claude-assets/skills/cfn-loop-orchestration/.backups/unknown/1763671642_06496e8c399a79db08167cc00ed4b31e/metadata.json +8 -0
  266. package/claude-assets/skills/cfn-loop-orchestration/.backups/unknown/1763671642_06496e8c399a79db08167cc00ed4b31e/original +325 -0
  267. package/claude-assets/skills/cfn-loop-orchestration/.backups/unknown/1763671642_06496e8c399a79db08167cc00ed4b31e/revert.sh +7 -0
  268. package/claude-assets/skills/cfn-loop-orchestration/CLI_IMPLEMENTATION_SUMMARY.md +330 -0
  269. package/claude-assets/skills/cfn-loop-orchestration/CONFIGURATION_IMPROVEMENTS.md +318 -0
  270. package/claude-assets/skills/cfn-loop-orchestration/CONTEXT_LOOKUP_MIGRATION.md +308 -0
  271. package/claude-assets/skills/cfn-loop-orchestration/CONTEXT_LOOKUP_QUICK_START.md +378 -0
  272. package/claude-assets/skills/cfn-loop-orchestration/E2E_VALIDATION_REPORT.md +262 -0
  273. package/claude-assets/skills/cfn-loop-orchestration/IMPLEMENTATION_SUMMARY.md +319 -519
  274. package/claude-assets/skills/cfn-loop-orchestration/NORTH_STAR_E2E_REPORT.md +299 -0
  275. package/claude-assets/skills/cfn-loop-orchestration/NORTH_STAR_EXECUTION_SUMMARY.md +403 -0
  276. package/claude-assets/skills/cfn-loop-orchestration/NORTH_STAR_INDEX.md +323 -0
  277. package/claude-assets/skills/cfn-loop-orchestration/SKILL.md +159 -48
  278. package/claude-assets/skills/cfn-loop-orchestration/SPAWN_AGENTS_IMPLEMENTATION.md +188 -0
  279. package/claude-assets/skills/cfn-loop-orchestration/TEST_COVERAGE_REPORT.md +335 -0
  280. package/claude-assets/skills/cfn-loop-orchestration/TEST_COVERAGE_SUMMARY.md +456 -0
  281. package/claude-assets/skills/cfn-loop-orchestration/TYPESCRIPT_INTEGRATION_REPORT.md +709 -0
  282. package/claude-assets/skills/cfn-loop-orchestration/TYPESCRIPT_INTEGRATION_SUMMARY.md +257 -0
  283. package/claude-assets/skills/cfn-loop-orchestration/VALIDATION_REPORT.md +572 -0
  284. package/claude-assets/skills/cfn-loop-orchestration/VALIDATION_SUMMARY.txt +196 -0
  285. package/claude-assets/skills/cfn-loop-orchestration/VALIDATOR_MODULE_GUIDE.md +526 -0
  286. package/claude-assets/skills/cfn-loop-orchestration/archive/legacy-bash/README.md +167 -0
  287. package/claude-assets/skills/cfn-loop-orchestration/archive/legacy-bash/orchestrate-enhanced.sh +548 -0
  288. package/{.claude/skills/cfn-loop-orchestration → claude-assets/skills/cfn-loop-orchestration/archive/legacy-bash}/orchestrate-wrapper.sh +11 -1
  289. package/claude-assets/skills/cfn-loop-orchestration/archive/legacy-bash/orchestrate.sh +182 -0
  290. package/claude-assets/skills/cfn-loop-orchestration/e2e-validation-fixed.js +240 -0
  291. package/claude-assets/skills/cfn-loop-orchestration/e2e-validation.js +213 -0
  292. package/claude-assets/skills/cfn-loop-orchestration/package-lock.json +3 -0
  293. package/claude-assets/skills/cfn-loop-orchestration/package.json +4 -0
  294. package/claude-assets/skills/cfn-loop-orchestration/run-north-star-e2e.ts +210 -0
  295. package/claude-assets/skills/cfn-loop-orchestration/src/cli/orchestrator-cli.ts +396 -0
  296. package/claude-assets/skills/cfn-loop-orchestration/src/helpers/CONFIDENCE_AGGREGATOR.md +564 -0
  297. package/claude-assets/skills/cfn-loop-orchestration/src/helpers/CONFIDENCE_AGGREGATOR_QUICK_REF.md +241 -0
  298. package/claude-assets/skills/cfn-loop-orchestration/src/helpers/CONTEXT_INJECTOR_IMPLEMENTATION.md +375 -0
  299. package/claude-assets/skills/cfn-loop-orchestration/src/helpers/CONTEXT_INJECTOR_QUICK_REFERENCE.md +362 -0
  300. package/claude-assets/skills/cfn-loop-orchestration/src/helpers/CONTEXT_INJECTOR_README.md +307 -0
  301. package/claude-assets/skills/cfn-loop-orchestration/src/helpers/CONTEXT_INJECTOR_USAGE_GUIDE.md +508 -0
  302. package/claude-assets/skills/cfn-loop-orchestration/src/helpers/confidence-aggregator.ts +473 -0
  303. package/claude-assets/skills/cfn-loop-orchestration/src/helpers/consensus.ts +1 -1
  304. package/claude-assets/skills/cfn-loop-orchestration/src/helpers/context-injector.ts +349 -0
  305. package/claude-assets/skills/cfn-loop-orchestration/src/helpers/context-lookup.ts +486 -0
  306. package/claude-assets/skills/cfn-loop-orchestration/src/helpers/deliverable-verifier.ts +6 -2
  307. package/claude-assets/skills/cfn-loop-orchestration/src/helpers/gate-check.ts +1 -1
  308. package/claude-assets/skills/cfn-loop-orchestration/src/helpers/product-owner-decision.ts +316 -0
  309. package/claude-assets/skills/cfn-loop-orchestration/src/helpers/spawn-agents.ts +357 -0
  310. package/claude-assets/skills/cfn-loop-orchestration/src/helpers/validator.ts +276 -0
  311. package/claude-assets/skills/cfn-loop-orchestration/src/index.ts +2 -0
  312. package/claude-assets/skills/cfn-loop-orchestration/src/orchestrate.ts +743 -2
  313. package/claude-assets/skills/cfn-loop-orchestration/src/types.ts +56 -0
  314. package/claude-assets/skills/cfn-loop-orchestration/test-cli.sh +92 -0
  315. package/claude-assets/skills/cfn-loop-orchestration/test-typescript-integration.sh +442 -0
  316. package/claude-assets/skills/cfn-loop-orchestration/tests/agent-spawner.test.ts +124 -0
  317. package/claude-assets/skills/cfn-loop-orchestration/tests/confidence-aggregator.test.ts +604 -0
  318. package/claude-assets/skills/cfn-loop-orchestration/tests/context-injector.test.ts +561 -0
  319. package/claude-assets/skills/cfn-loop-orchestration/tests/context-lookup.test.ts +661 -0
  320. package/claude-assets/skills/cfn-loop-orchestration/tests/deliverable-verifier.test.ts +2 -2
  321. package/claude-assets/skills/cfn-loop-orchestration/tests/gate-check-edge-cases.test.ts +422 -0
  322. package/claude-assets/skills/cfn-loop-orchestration/tests/gate-checker.test.ts +276 -0
  323. package/claude-assets/skills/cfn-loop-orchestration/tests/logger.test.ts +291 -0
  324. package/claude-assets/skills/cfn-loop-orchestration/tests/north-star-e2e.test.ts +334 -0
  325. package/claude-assets/skills/cfn-loop-orchestration/tests/redis-coordinator.test.ts +321 -0
  326. package/claude-assets/skills/cfn-loop-orchestration/tests/spawn-agents.test.ts +284 -0
  327. package/claude-assets/skills/cfn-loop-orchestration/tests/validator.test.ts +643 -0
  328. package/claude-assets/skills/cfn-loop-output-processing/.eslintrc.json +33 -0
  329. package/claude-assets/skills/cfn-loop-output-processing/DELIVERY_SUMMARY.txt +462 -0
  330. package/claude-assets/skills/cfn-loop-output-processing/DEPRECATION_NOTICE.md +183 -0
  331. package/claude-assets/skills/cfn-loop-output-processing/EXAMPLES.md +609 -0
  332. package/claude-assets/skills/cfn-loop-output-processing/IMPLEMENTATION_SUMMARY.md +418 -0
  333. package/claude-assets/skills/cfn-loop-output-processing/INDEX.md +531 -0
  334. package/claude-assets/skills/cfn-loop-output-processing/MIGRATION.md +362 -0
  335. package/claude-assets/skills/cfn-loop-output-processing/README.md +114 -0
  336. package/claude-assets/skills/cfn-loop-output-processing/SKILL.md +633 -0
  337. package/{.claude/skills/cfn-docker-redis-coordination → claude-assets/skills/cfn-loop-output-processing}/jest.config.js +7 -15
  338. package/claude-assets/skills/cfn-loop-output-processing/package.json +50 -0
  339. package/claude-assets/skills/cfn-loop-output-processing/src/cli/process-loop2.ts +195 -0
  340. package/claude-assets/skills/cfn-loop-output-processing/src/cli/process-loop3.ts +157 -0
  341. package/claude-assets/skills/cfn-loop-output-processing/src/output-processor.ts +632 -0
  342. package/claude-assets/skills/cfn-loop-output-processing/tests/output-processor.test.ts +617 -0
  343. package/claude-assets/skills/{cfn-docker-redis-coordination → cfn-loop-output-processing}/tsconfig.json +16 -7
  344. package/claude-assets/skills/cfn-loop-validation/IMPLEMENTATION_SUMMARY.md +672 -0
  345. package/claude-assets/skills/cfn-loop-validation/INDEX.md +531 -0
  346. package/claude-assets/skills/cfn-loop-validation/README_TYPESCRIPT.md +454 -0
  347. package/claude-assets/skills/cfn-loop-validation/SKILL.md +48 -1
  348. package/claude-assets/skills/cfn-loop-validation/SKILL.md.backup +353 -0
  349. package/claude-assets/skills/cfn-loop-validation/SKILL_TYPESCRIPT.md +782 -0
  350. package/claude-assets/skills/cfn-loop-validation/VAPOR_DETECTION_EXAMPLES.md +598 -0
  351. package/claude-assets/skills/cfn-loop-validation/check-dependencies.sh +22 -0
  352. package/claude-assets/skills/cfn-loop-validation/check-dependencies.sh.backup +31 -0
  353. package/claude-assets/skills/cfn-loop-validation/detect-vapor.sh +59 -0
  354. package/claude-assets/skills/cfn-loop-validation/detect-vapor.sh.backup +37 -0
  355. package/claude-assets/skills/cfn-loop-validation/dist/.tsbuildinfo +1 -0
  356. package/claude-assets/skills/cfn-loop-validation/dist/cli/detect-vapor.d.ts +14 -0
  357. package/claude-assets/skills/cfn-loop-validation/dist/cli/detect-vapor.d.ts.map +1 -0
  358. package/claude-assets/skills/cfn-loop-validation/dist/cli/detect-vapor.js +185 -0
  359. package/claude-assets/skills/cfn-loop-validation/dist/cli/detect-vapor.js.map +1 -0
  360. package/claude-assets/skills/cfn-loop-validation/dist/cli/validate-deliverables.d.ts +14 -0
  361. package/claude-assets/skills/cfn-loop-validation/dist/cli/validate-deliverables.d.ts.map +1 -0
  362. package/claude-assets/skills/cfn-loop-validation/dist/cli/validate-deliverables.js +176 -0
  363. package/claude-assets/skills/cfn-loop-validation/dist/cli/validate-deliverables.js.map +1 -0
  364. package/claude-assets/skills/cfn-loop-validation/dist/cli/validate-gate.d.ts +19 -0
  365. package/claude-assets/skills/cfn-loop-validation/dist/cli/validate-gate.d.ts.map +1 -0
  366. package/claude-assets/skills/cfn-loop-validation/dist/cli/validate-gate.js +123 -0
  367. package/claude-assets/skills/cfn-loop-validation/dist/cli/validate-gate.js.map +1 -0
  368. package/claude-assets/skills/cfn-loop-validation/dist/types.d.ts +156 -0
  369. package/claude-assets/skills/cfn-loop-validation/dist/types.d.ts.map +1 -0
  370. package/claude-assets/skills/cfn-loop-validation/dist/types.js +66 -0
  371. package/claude-assets/skills/cfn-loop-validation/dist/types.js.map +1 -0
  372. package/claude-assets/skills/cfn-loop-validation/dist/validator.d.ts +85 -0
  373. package/claude-assets/skills/cfn-loop-validation/dist/validator.d.ts.map +1 -0
  374. package/claude-assets/skills/cfn-loop-validation/dist/validator.js +411 -0
  375. package/claude-assets/skills/cfn-loop-validation/dist/validator.js.map +1 -0
  376. package/claude-assets/skills/cfn-loop-validation/orchestrate-cfn-loop.sh +22 -0
  377. package/claude-assets/skills/cfn-loop-validation/orchestrate-cfn-loop.sh.backup +252 -0
  378. package/claude-assets/skills/cfn-loop-validation/package.json +93 -0
  379. package/claude-assets/skills/cfn-loop-validation/src/cli/detect-vapor.ts +177 -0
  380. package/claude-assets/skills/cfn-loop-validation/src/cli/validate-deliverables.ts +161 -0
  381. package/claude-assets/skills/cfn-loop-validation/src/cli/validate-gate.ts +139 -0
  382. package/claude-assets/skills/cfn-loop-validation/src/types.ts +215 -0
  383. package/claude-assets/skills/cfn-loop-validation/src/validator.ts +503 -0
  384. package/claude-assets/skills/cfn-loop-validation/tests/validator.test.ts +537 -0
  385. package/claude-assets/skills/{cfn-redis-coordination → cfn-loop-validation}/tsconfig.json +34 -31
  386. package/claude-assets/skills/cfn-loop-validation/validate-deliverables.sh +59 -0
  387. package/claude-assets/skills/cfn-loop-validation/validate-deliverables.sh.backup +37 -0
  388. package/claude-assets/skills/cfn-loop-validation/validate-gate.sh +63 -0
  389. package/claude-assets/skills/cfn-loop-validation/validate-gate.sh.backup +41 -0
  390. package/claude-assets/skills/cfn-loop-validation/validate-iteration.sh +22 -0
  391. package/claude-assets/skills/cfn-loop-validation/validate-iteration.sh.backup +134 -0
  392. package/claude-assets/skills/cfn-product-owner-decision/SKILL.md +479 -147
  393. package/claude-assets/skills/cfn-product-owner-decision/TYPESCRIPT_IMPLEMENTATION.md +653 -0
  394. package/{.claude/skills/cfn-product-owner-decision → claude-assets/skills/cfn-product-owner-decision/archive/legacy-bash}/execute-decision.sh +24 -2
  395. package/claude-assets/skills/cfn-provider-routing/README.md +129 -0
  396. package/claude-assets/skills/cfn-provider-routing/SKILL.md +192 -0
  397. package/claude-assets/skills/cfn-provider-routing/resolve-provider-model.ts +223 -0
  398. package/claude-assets/skills/pre-edit-backup/SKILL.md +324 -0
  399. package/claude-assets/skills/pre-edit-backup/SKILL.md.backup +277 -0
  400. package/claude-assets/skills/pre-edit-backup/backup.sh +22 -0
  401. package/claude-assets/skills/pre-edit-backup/backup.sh.backup +107 -0
  402. package/dist/agents/agent-loader.js +146 -165
  403. package/dist/agents/agent-loader.js.map +1 -1
  404. package/dist/api/auth-endpoints.js +415 -0
  405. package/dist/api/auth-endpoints.js.map +1 -0
  406. package/dist/api/task-endpoints.js +562 -0
  407. package/dist/api/task-endpoints.js.map +1 -0
  408. package/dist/backend/server.js +418 -0
  409. package/dist/backend/server.js.map +1 -0
  410. package/dist/cfn-loop/product-owner/decision-parser.js +356 -0
  411. package/dist/cfn-loop/product-owner/decision-parser.js.map +1 -0
  412. package/dist/cfn-loop/product-owner/index.js +1 -0
  413. package/dist/cfn-loop/product-owner/index.js.map +1 -1
  414. package/dist/cli/agent-command.js +1 -1
  415. package/dist/cli/agent-command.js.map +1 -1
  416. package/dist/cli/agent-completion.js +273 -0
  417. package/dist/cli/agent-completion.js.map +1 -0
  418. package/dist/cli/agent-definition-parser.js +37 -4
  419. package/dist/cli/agent-definition-parser.js.map +1 -1
  420. package/dist/cli/agent-executor.js +32 -2
  421. package/dist/cli/agent-executor.js.map +1 -1
  422. package/dist/cli/agent-prompt-builder.js +83 -48
  423. package/dist/cli/agent-prompt-builder.js.map +1 -1
  424. package/dist/cli/agent-spawner.js +499 -0
  425. package/dist/cli/agent-spawner.js.map +1 -0
  426. package/dist/cli/anthropic-client.js +10 -3
  427. package/dist/cli/anthropic-client.js.map +1 -1
  428. package/dist/cli/config-manager.js.map +1 -1
  429. package/dist/cli/index.js +11 -0
  430. package/dist/cli/index.js.map +1 -1
  431. package/dist/cli/parse-decision-cli.js +268 -0
  432. package/dist/cli/parse-decision-cli.js.map +1 -0
  433. package/dist/cli/post-edit-hook.js +83 -0
  434. package/dist/cli/post-edit-hook.js.map +1 -0
  435. package/dist/cli/pre-edit-hook.js +77 -0
  436. package/dist/cli/pre-edit-hook.js.map +1 -0
  437. package/dist/cli/spawn-agent-cli.js +209 -0
  438. package/dist/cli/spawn-agent-cli.js.map +1 -0
  439. package/dist/coordination/coordination-wrapper.js +383 -0
  440. package/dist/coordination/coordination-wrapper.js.map +1 -0
  441. package/dist/coordination/store-success-criteria.js +68 -0
  442. package/dist/coordination/store-success-criteria.js.map +1 -0
  443. package/dist/coordination/store-task-context.js +65 -0
  444. package/dist/coordination/store-task-context.js.map +1 -0
  445. package/dist/hooks/backup-manager.js +273 -0
  446. package/dist/hooks/backup-manager.js.map +1 -0
  447. package/dist/hooks/post-edit-validator.js +388 -0
  448. package/dist/hooks/post-edit-validator.js.map +1 -0
  449. package/dist/integration/index.js +19 -0
  450. package/dist/integration/index.js.map +1 -0
  451. package/dist/integration/task-mode-adapter.js +297 -0
  452. package/dist/integration/task-mode-adapter.js.map +1 -0
  453. package/dist/integration/trigger-dev-client.js +253 -0
  454. package/dist/integration/trigger-dev-client.js.map +1 -0
  455. package/dist/integration/trigger-dev-webhooks.js +362 -0
  456. package/dist/integration/trigger-dev-webhooks.js.map +1 -0
  457. package/dist/lib/path-validator.js +14 -5
  458. package/dist/lib/path-validator.js.map +1 -1
  459. package/dist/lib/redis-queue-manager.js +5 -1
  460. package/dist/lib/redis-queue-manager.js.map +1 -1
  461. package/dist/middleware/authentication.js +317 -0
  462. package/dist/middleware/authentication.js.map +1 -0
  463. package/dist/services/authentication.js +669 -0
  464. package/dist/services/authentication.js.map +1 -0
  465. package/dist/services/session-management.js +436 -0
  466. package/dist/services/session-management.js.map +1 -0
  467. package/dist/services/skill-deployment.js +8 -6
  468. package/dist/services/skill-deployment.js.map +1 -1
  469. package/dist/services/user-service.js +710 -0
  470. package/dist/services/user-service.js.map +1 -0
  471. package/dist/types/trigger-dev-events.d.js +10 -0
  472. package/dist/types/trigger-dev-events.d.js.map +1 -0
  473. package/docs/README.md +240 -0
  474. package/package.json +13 -4
  475. package/scripts/compare-workflow-performance.sh +556 -0
  476. package/scripts/migrate-to-optimized-workflows.sh +438 -0
  477. package/scripts/organize-docs.sh +338 -0
  478. package/scripts/trigger-dev-setup.sh +267 -0
  479. package/.claude/skills/cfn-docker-redis-coordination/MIGRATION_SUMMARY.md +0 -348
  480. package/.claude/skills/cfn-docker-redis-coordination/README.md +0 -294
  481. package/.claude/skills/cfn-docker-redis-coordination/SKILL.md +0 -435
  482. package/.claude/skills/cfn-docker-redis-coordination/coordinate.sh +0 -650
  483. package/.claude/skills/cfn-docker-redis-coordination/coordinate.sh.backup-1763145142 +0 -641
  484. package/.claude/skills/cfn-docker-redis-coordination/package-lock.json +0 -5259
  485. package/.claude/skills/cfn-docker-redis-coordination/package.json +0 -40
  486. package/.claude/skills/cfn-docker-redis-coordination/src/coordinator.ts +0 -801
  487. package/.claude/skills/cfn-docker-redis-coordination/src/index.ts +0 -42
  488. package/.claude/skills/cfn-docker-redis-coordination/src/types.ts +0 -351
  489. package/.claude/skills/cfn-docker-redis-coordination/tests/coordinator.test.ts +0 -1464
  490. package/.claude/skills/cfn-docker-redis-coordination/tsconfig.json +0 -30
  491. package/.claude/skills/cfn-loop-orchestration/helpers/auto-tune-timeouts.sh +0 -228
  492. package/.claude/skills/cfn-loop-orchestration/helpers/consensus-ts.sh +0 -104
  493. package/.claude/skills/cfn-loop-orchestration/helpers/consensus.sh +0 -94
  494. package/.claude/skills/cfn-loop-orchestration/helpers/context-injection.sh +0 -142
  495. package/.claude/skills/cfn-loop-orchestration/helpers/context-lookup.sh +0 -359
  496. package/.claude/skills/cfn-loop-orchestration/helpers/deliverable-verifier-ts.sh +0 -123
  497. package/.claude/skills/cfn-loop-orchestration/helpers/deliverable-verifier.sh +0 -71
  498. package/.claude/skills/cfn-loop-orchestration/helpers/gate-check.sh +0 -56
  499. package/.claude/skills/cfn-loop-orchestration/helpers/iteration-manager-ts.sh +0 -89
  500. package/.claude/skills/cfn-loop-orchestration/helpers/iteration-manager.sh +0 -87
  501. package/.claude/skills/cfn-loop-orchestration/helpers/orchestrate-ts.sh +0 -104
  502. package/.claude/skills/cfn-loop-orchestration/helpers/parse-test-results.sh +0 -56
  503. package/.claude/skills/cfn-loop-orchestration/helpers/spawn-agents.sh +0 -290
  504. package/.claude/skills/cfn-loop-orchestration/helpers/timeout-calculator-ts.sh +0 -47
  505. package/.claude/skills/cfn-loop-orchestration/helpers/timeout-calculator.sh +0 -51
  506. package/.claude/skills/cfn-loop-orchestration/orchestrate.sh +0 -1345
  507. package/.claude/skills/cfn-redis-coordination/AGENT_LOGGING.md +0 -280
  508. package/.claude/skills/cfn-redis-coordination/BZPOPMIN_FIX_SUMMARY.md +0 -209
  509. package/.claude/skills/cfn-redis-coordination/CENTRALIZED_REDIS_WRAPPER.md +0 -319
  510. package/.claude/skills/cfn-redis-coordination/agent-log.sh.bak +0 -124
  511. package/.claude/skills/cfn-redis-coordination/config.json +0 -61
  512. package/.claude/skills/cfn-redis-coordination/demos/phase4-wake-queue-test-report.md +0 -82
  513. package/.claude/skills/cfn-redis-coordination/demos/test-bzpopmin-fix.sh +0 -274
  514. package/.claude/skills/cfn-redis-coordination/demos/test-cancel-swarm.sh +0 -0
  515. package/.claude/skills/cfn-redis-coordination/docs/migration/PHASE_3_REDIS_COORDINATION_COMPLETION_REPORT.md +0 -553
  516. package/.claude/skills/cfn-redis-coordination/jest.config.js +0 -23
  517. package/.claude/skills/cfn-redis-coordination/package-lock.json +0 -5272
  518. package/.claude/skills/cfn-redis-coordination/package.json +0 -45
  519. package/.claude/skills/cfn-redis-coordination/src/agent-logger.ts +0 -446
  520. package/.claude/skills/cfn-redis-coordination/src/agent-recovery.ts +0 -454
  521. package/.claude/skills/cfn-redis-coordination/src/completion-reporter.ts +0 -396
  522. package/.claude/skills/cfn-redis-coordination/src/context-manager.ts +0 -327
  523. package/.claude/skills/cfn-redis-coordination/src/index.ts +0 -82
  524. package/.claude/skills/cfn-redis-coordination/src/mode-detector.ts +0 -155
  525. package/.claude/skills/cfn-redis-coordination/src/redis/redis-client.ts +0 -305
  526. package/.claude/skills/cfn-redis-coordination/src/redis/redis-functions.ts +0 -283
  527. package/.claude/skills/cfn-redis-coordination/src/redis-client.ts +0 -654
  528. package/.claude/skills/cfn-redis-coordination/src/result-collector.ts +0 -437
  529. package/.claude/skills/cfn-redis-coordination/src/swarm-manager.ts +0 -494
  530. package/.claude/skills/cfn-redis-coordination/src/task-analyzer.ts +0 -404
  531. package/.claude/skills/cfn-redis-coordination/src/task-executor.ts +0 -423
  532. package/.claude/skills/cfn-redis-coordination/src/types.ts +0 -235
  533. package/.claude/skills/cfn-redis-coordination/src/waiting-coordinator.ts +0 -587
  534. package/.claude/skills/cfn-redis-coordination/store-success-criteria.sh +0 -85
  535. package/.claude/skills/cfn-redis-coordination/test-connection-attempts.js +0 -70
  536. package/.claude/skills/cfn-redis-coordination/test-mode-simple.js +0 -121
  537. package/.claude/skills/cfn-redis-coordination/test-redis-check.js +0 -84
  538. package/.claude/skills/cfn-redis-coordination/test-task-mode-redis.cjs +0 -391
  539. package/.claude/skills/cfn-redis-coordination/tests/coordination.test.ts +0 -788
  540. package/.claude/skills/cfn-redis-coordination/update-all-scripts.sh +0 -67
  541. package/claude-assets/agents/cfn-dev-team/coordinators/cfn-v3-coordinator.md +0 -976
  542. package/claude-assets/agents/typescript-specialist.md +0 -280
  543. package/claude-assets/skills/cfn-docker-redis-coordination/MIGRATION_SUMMARY.md +0 -348
  544. package/claude-assets/skills/cfn-docker-redis-coordination/README.md +0 -294
  545. package/claude-assets/skills/cfn-docker-redis-coordination/SKILL.md +0 -435
  546. package/claude-assets/skills/cfn-docker-redis-coordination/coordinate.sh +0 -650
  547. package/claude-assets/skills/cfn-docker-redis-coordination/coordinate.sh.backup-1763145142 +0 -641
  548. package/claude-assets/skills/cfn-docker-redis-coordination/jest.config.js +0 -37
  549. package/claude-assets/skills/cfn-docker-redis-coordination/package-lock.json +0 -5259
  550. package/claude-assets/skills/cfn-docker-redis-coordination/package.json +0 -40
  551. package/claude-assets/skills/cfn-docker-redis-coordination/src/coordinator.ts +0 -801
  552. package/claude-assets/skills/cfn-docker-redis-coordination/src/index.ts +0 -42
  553. package/claude-assets/skills/cfn-docker-redis-coordination/src/types.ts +0 -351
  554. package/claude-assets/skills/cfn-docker-redis-coordination/tests/coordinator.test.ts +0 -1464
  555. package/claude-assets/skills/cfn-loop-orchestration/helpers/auto-tune-timeouts.sh +0 -228
  556. package/claude-assets/skills/cfn-loop-orchestration/helpers/consensus-ts.sh +0 -104
  557. package/claude-assets/skills/cfn-loop-orchestration/helpers/consensus.sh +0 -94
  558. package/claude-assets/skills/cfn-loop-orchestration/helpers/context-injection.sh +0 -142
  559. package/claude-assets/skills/cfn-loop-orchestration/helpers/context-lookup.sh +0 -359
  560. package/claude-assets/skills/cfn-loop-orchestration/helpers/deliverable-verifier-ts.sh +0 -123
  561. package/claude-assets/skills/cfn-loop-orchestration/helpers/deliverable-verifier.sh +0 -71
  562. package/claude-assets/skills/cfn-loop-orchestration/helpers/gate-check.sh +0 -56
  563. package/claude-assets/skills/cfn-loop-orchestration/helpers/iteration-manager-ts.sh +0 -89
  564. package/claude-assets/skills/cfn-loop-orchestration/helpers/iteration-manager.sh +0 -87
  565. package/claude-assets/skills/cfn-loop-orchestration/helpers/orchestrate-ts.sh +0 -104
  566. package/claude-assets/skills/cfn-loop-orchestration/helpers/parse-test-results.sh +0 -56
  567. package/claude-assets/skills/cfn-loop-orchestration/helpers/spawn-agents.sh +0 -290
  568. package/claude-assets/skills/cfn-loop-orchestration/helpers/timeout-calculator-ts.sh +0 -47
  569. package/claude-assets/skills/cfn-loop-orchestration/helpers/timeout-calculator.sh +0 -51
  570. package/claude-assets/skills/cfn-loop-orchestration/orchestrate.sh +0 -1345
  571. package/claude-assets/skills/cfn-redis-cleanup/cleanup-redis.sh +0 -130
  572. package/claude-assets/skills/cfn-redis-coordination/AGENT_LOGGING.md +0 -280
  573. package/claude-assets/skills/cfn-redis-coordination/BZPOPMIN_FIX_SUMMARY.md +0 -209
  574. package/claude-assets/skills/cfn-redis-coordination/CENTRALIZED_REDIS_WRAPPER.md +0 -319
  575. package/claude-assets/skills/cfn-redis-coordination/agent-log.sh.bak +0 -124
  576. package/claude-assets/skills/cfn-redis-coordination/config.json +0 -61
  577. package/claude-assets/skills/cfn-redis-coordination/demos/phase4-wake-queue-test-report.md +0 -82
  578. package/claude-assets/skills/cfn-redis-coordination/demos/test-bzpopmin-fix.sh +0 -274
  579. package/claude-assets/skills/cfn-redis-coordination/demos/test-cancel-swarm.sh +0 -0
  580. package/claude-assets/skills/cfn-redis-coordination/docs/migration/PHASE_3_REDIS_COORDINATION_COMPLETION_REPORT.md +0 -553
  581. package/claude-assets/skills/cfn-redis-coordination/jest.config.js +0 -23
  582. package/claude-assets/skills/cfn-redis-coordination/package-lock.json +0 -5272
  583. package/claude-assets/skills/cfn-redis-coordination/package.json +0 -45
  584. package/claude-assets/skills/cfn-redis-coordination/src/agent-logger.ts +0 -446
  585. package/claude-assets/skills/cfn-redis-coordination/src/agent-recovery.ts +0 -454
  586. package/claude-assets/skills/cfn-redis-coordination/src/completion-reporter.ts +0 -396
  587. package/claude-assets/skills/cfn-redis-coordination/src/context-manager.ts +0 -327
  588. package/claude-assets/skills/cfn-redis-coordination/src/index.ts +0 -82
  589. package/claude-assets/skills/cfn-redis-coordination/src/mode-detector.ts +0 -155
  590. package/claude-assets/skills/cfn-redis-coordination/src/redis/redis-client.ts +0 -305
  591. package/claude-assets/skills/cfn-redis-coordination/src/redis/redis-functions.ts +0 -283
  592. package/claude-assets/skills/cfn-redis-coordination/src/redis-client.ts +0 -654
  593. package/claude-assets/skills/cfn-redis-coordination/src/result-collector.ts +0 -437
  594. package/claude-assets/skills/cfn-redis-coordination/src/swarm-manager.ts +0 -494
  595. package/claude-assets/skills/cfn-redis-coordination/src/task-analyzer.ts +0 -404
  596. package/claude-assets/skills/cfn-redis-coordination/src/task-executor.ts +0 -423
  597. package/claude-assets/skills/cfn-redis-coordination/src/types.ts +0 -235
  598. package/claude-assets/skills/cfn-redis-coordination/src/waiting-coordinator.ts +0 -587
  599. package/claude-assets/skills/cfn-redis-coordination/store-success-criteria.sh +0 -85
  600. package/claude-assets/skills/cfn-redis-coordination/test-connection-attempts.js +0 -70
  601. package/claude-assets/skills/cfn-redis-coordination/test-mode-simple.js +0 -121
  602. package/claude-assets/skills/cfn-redis-coordination/test-redis-check.js +0 -84
  603. package/claude-assets/skills/cfn-redis-coordination/test-task-mode-redis.cjs +0 -391
  604. package/claude-assets/skills/cfn-redis-coordination/tests/coordination.test.ts +0 -788
  605. package/claude-assets/skills/cfn-redis-coordination/update-all-scripts.sh +0 -67
  606. package/claude-assets/skills/cfn-redis-data-extraction/SKILL.md +0 -442
  607. package/claude-assets/skills/cfn-redis-data-extraction/extract.sh +0 -306
  608. package/dist/coordination/index.js +0 -25
  609. package/dist/coordination/index.js.map +0 -1
  610. package/docs/BUG_19_MEMORY_LEAK_TASK_MODE.md +0 -405
  611. package/docs/MEMORY_CLEANUP_GUIDE.md +0 -358
  612. package/docs/MEMORY_LEAK_FIX_SUMMARY.md +0 -322
  613. package/docs/REDIS_CLEANUP_EXECUTIVE_SUMMARY.md +0 -319
  614. package/docs/REDIS_CLEANUP_VERIFICATION_REPORT.md +0 -574
  615. /package/.claude/skills/cfn-loop-orchestration/{inject-loop-context.sh → archive/legacy-bash/inject-loop-context.sh} +0 -0
  616. /package/.claude/skills/cfn-loop-orchestration/{monitor-execution.sh → archive/legacy-bash/monitor-execution.sh} +0 -0
  617. /package/.claude/skills/cfn-redis-coordination/{agent-log.sh → agent-log.sh.backup} +0 -0
  618. /package/.claude/skills/cfn-redis-coordination/{agent-recovery.sh → agent-recovery.sh.backup} +0 -0
  619. /package/.claude/skills/cfn-redis-coordination/{analyze-task-complexity.sh → analyze-task-complexity.sh.backup} +0 -0
  620. /package/.claude/skills/cfn-redis-coordination/bash-wrappers/{store-context.sh → store-context.sh.backup} +0 -0
  621. /package/.claude/skills/cfn-redis-coordination/{cancel-swarm.sh → cancel-swarm.sh.backup} +0 -0
  622. /package/.claude/skills/cfn-redis-coordination/{cfn-loop-exec.sh → cfn-loop-exec.sh.backup} +0 -0
  623. /package/.claude/skills/cfn-redis-coordination/{cfn-loop-relaunch.sh → cfn-loop-relaunch.sh.backup} +0 -0
  624. /package/.claude/skills/cfn-redis-coordination/{collect-confidence-scores.sh → collect-confidence-scores.sh.backup} +0 -0
  625. /package/.claude/skills/cfn-redis-coordination/{collect-results.sh → collect-results.sh.backup} +0 -0
  626. /package/.claude/skills/cfn-redis-coordination/{complete-swarm.sh → complete-swarm.sh.backup} +0 -0
  627. /package/.claude/skills/cfn-redis-coordination/{get-context.sh → get-context.sh.backup} +0 -0
  628. /package/.claude/skills/cfn-redis-coordination/{get-success-criteria.sh → get-success-criteria.sh.backup} +0 -0
  629. /package/.claude/skills/cfn-redis-coordination/{invoke-waiting-mode.sh → invoke-waiting-mode.sh.backup} +0 -0
  630. /package/.claude/skills/cfn-redis-coordination/{redis-cli-wrapper.sh → redis-cli-wrapper.sh.backup} +0 -0
  631. /package/.claude/skills/cfn-redis-coordination/{redis-functions.sh → redis-functions.sh.backup} +0 -0
  632. /package/.claude/skills/cfn-redis-coordination/{report-completion.sh → report-completion.sh.backup} +0 -0
  633. /package/.claude/skills/cfn-redis-coordination/{store-context.sh → store-context.sh.backup} +0 -0
  634. /package/claude-assets/skills/cfn-loop-orchestration/{inject-loop-context.sh → archive/legacy-bash/inject-loop-context.sh} +0 -0
  635. /package/claude-assets/skills/cfn-loop-orchestration/{monitor-execution.sh → archive/legacy-bash/monitor-execution.sh} +0 -0
  636. /package/claude-assets/skills/cfn-redis-coordination/{agent-log.sh → agent-log.sh.backup} +0 -0
  637. /package/claude-assets/skills/cfn-redis-coordination/{agent-recovery.sh → agent-recovery.sh.backup} +0 -0
  638. /package/claude-assets/skills/cfn-redis-coordination/{analyze-task-complexity.sh → analyze-task-complexity.sh.backup} +0 -0
  639. /package/claude-assets/skills/cfn-redis-coordination/bash-wrappers/{store-context.sh → store-context.sh.backup} +0 -0
  640. /package/claude-assets/skills/cfn-redis-coordination/{cancel-swarm.sh → cancel-swarm.sh.backup} +0 -0
  641. /package/claude-assets/skills/cfn-redis-coordination/{cfn-loop-exec.sh → cfn-loop-exec.sh.backup} +0 -0
  642. /package/claude-assets/skills/cfn-redis-coordination/{cfn-loop-relaunch.sh → cfn-loop-relaunch.sh.backup} +0 -0
  643. /package/claude-assets/skills/cfn-redis-coordination/{collect-confidence-scores.sh → collect-confidence-scores.sh.backup} +0 -0
  644. /package/claude-assets/skills/cfn-redis-coordination/{collect-results.sh → collect-results.sh.backup} +0 -0
  645. /package/claude-assets/skills/cfn-redis-coordination/{complete-swarm.sh → complete-swarm.sh.backup} +0 -0
  646. /package/claude-assets/skills/cfn-redis-coordination/{get-context.sh → get-context.sh.backup} +0 -0
  647. /package/claude-assets/skills/cfn-redis-coordination/{get-success-criteria.sh → get-success-criteria.sh.backup} +0 -0
  648. /package/claude-assets/skills/cfn-redis-coordination/{invoke-waiting-mode.sh → invoke-waiting-mode.sh.backup} +0 -0
  649. /package/claude-assets/skills/cfn-redis-coordination/{redis-cli-wrapper.sh → redis-cli-wrapper.sh.backup} +0 -0
  650. /package/claude-assets/skills/cfn-redis-coordination/{redis-functions.sh → redis-functions.sh.backup} +0 -0
  651. /package/claude-assets/skills/cfn-redis-coordination/{report-completion.sh → report-completion.sh.backup} +0 -0
  652. /package/claude-assets/skills/cfn-redis-coordination/{store-context.sh → store-context.sh.backup} +0 -0
@@ -0,0 +1,362 @@
1
+ /**
2
+ * Trigger.dev Webhook Handlers for CFN Loop Integration
3
+ *
4
+ * Express router providing secure webhook endpoints for:
5
+ * - Agent completion events
6
+ * - Gate check results
7
+ * - Consensus collection results
8
+ * - Product Owner decisions
9
+ *
10
+ * Configuration:
11
+ * - WEBHOOK_SECRET: Shared secret for HMAC signature verification
12
+ * - WEBHOOK_ALGORITHM: Hash algorithm (default: sha256)
13
+ */ import { Router } from 'express';
14
+ import { createHmac, timingSafeEqual } from 'node:crypto';
15
+ /**
16
+ * Webhook validation error class
17
+ */ export class WebhookValidationError extends Error {
18
+ reason;
19
+ statusCode;
20
+ constructor(message, reason, statusCode = 401){
21
+ super(message), this.reason = reason, this.statusCode = statusCode;
22
+ this.name = 'WebhookValidationError';
23
+ }
24
+ }
25
+ /**
26
+ * TriggerDevWebhooks - Express router for secure webhook handling
27
+ *
28
+ * Provides endpoints:
29
+ * - POST /webhooks/agent-complete
30
+ * - POST /webhooks/gate-result
31
+ * - POST /webhooks/consensus-result
32
+ * - POST /webhooks/po-decision
33
+ */ export class TriggerDevWebhooks {
34
+ router;
35
+ config;
36
+ verificationOptions;
37
+ // Handler maps for dependency injection
38
+ agentCompleteHandler;
39
+ gateResultHandler;
40
+ consensusResultHandler;
41
+ poDecisionHandler;
42
+ constructor(config){
43
+ this.router = Router();
44
+ this.config = {
45
+ secret: config?.secret || process.env.WEBHOOK_SECRET || 'default-secret',
46
+ algorithm: config?.algorithm || process.env.WEBHOOK_ALGORITHM || 'sha256',
47
+ verifySignature: config?.verifySignature !== false
48
+ };
49
+ this.verificationOptions = {
50
+ algorithmType: this.config.algorithm || 'sha256',
51
+ headerName: 'x-trigger-signature'
52
+ };
53
+ this.setupRoutes();
54
+ }
55
+ /**
56
+ * Register handler for agent completion events
57
+ */ onAgentComplete(handler) {
58
+ this.agentCompleteHandler = handler;
59
+ return this;
60
+ }
61
+ /**
62
+ * Register handler for gate result events
63
+ */ onGateResult(handler) {
64
+ this.gateResultHandler = handler;
65
+ return this;
66
+ }
67
+ /**
68
+ * Register handler for consensus result events
69
+ */ onConsensusResult(handler) {
70
+ this.consensusResultHandler = handler;
71
+ return this;
72
+ }
73
+ /**
74
+ * Register handler for Product Owner decision events
75
+ */ onPODecision(handler) {
76
+ this.poDecisionHandler = handler;
77
+ return this;
78
+ }
79
+ /**
80
+ * Get Express router for mounting
81
+ */ getRouter() {
82
+ return this.router;
83
+ }
84
+ /**
85
+ * Setup all webhook routes
86
+ *
87
+ * @private
88
+ */ setupRoutes() {
89
+ // Middleware for signature verification
90
+ this.router.use(this.signatureVerificationMiddleware.bind(this));
91
+ // Agent completion endpoint
92
+ this.router.post('/agent-complete', this.handleAgentComplete.bind(this));
93
+ // Gate result endpoint
94
+ this.router.post('/gate-result', this.handleGateResult.bind(this));
95
+ // Consensus result endpoint
96
+ this.router.post('/consensus-result', this.handleConsensusResult.bind(this));
97
+ // PO decision endpoint
98
+ this.router.post('/po-decision', this.handlePODecision.bind(this));
99
+ // Error handling middleware
100
+ this.router.use(this.errorHandler.bind(this));
101
+ }
102
+ /**
103
+ * Signature verification middleware
104
+ *
105
+ * TODO: RUNTIME_TEST: Verify HMAC signature validation with valid/invalid keys
106
+ *
107
+ * @private
108
+ */ async signatureVerificationMiddleware(req, res, next) {
109
+ try {
110
+ if (!this.config.verifySignature) {
111
+ return next();
112
+ }
113
+ const signature = req.headers[this.verificationOptions.headerName];
114
+ if (!signature) {
115
+ throw new WebhookValidationError('Missing webhook signature', 'MISSING_SIGNATURE');
116
+ }
117
+ const body = JSON.stringify(req.body);
118
+ const isValid = await this.verifySignature(body, signature);
119
+ if (!isValid) {
120
+ throw new WebhookValidationError('Invalid webhook signature', 'INVALID_SIGNATURE');
121
+ }
122
+ next();
123
+ } catch (error) {
124
+ if (error instanceof WebhookValidationError) {
125
+ return res.status(error.statusCode).json({
126
+ success: false,
127
+ message: error.message,
128
+ reason: error.reason
129
+ });
130
+ }
131
+ return res.status(500).json({
132
+ success: false,
133
+ message: 'Signature verification failed',
134
+ reason: 'INTERNAL_ERROR'
135
+ });
136
+ }
137
+ }
138
+ /**
139
+ * Handle agent completion webhook
140
+ *
141
+ * @private
142
+ */ async handleAgentComplete(req, res) {
143
+ try {
144
+ const payload = req.body;
145
+ // Validate required fields
146
+ if (!payload.agentId || !payload.taskId) {
147
+ return res.status(400).json({
148
+ success: false,
149
+ message: 'Missing required fields',
150
+ data: null
151
+ });
152
+ }
153
+ const context = {
154
+ payload,
155
+ isVerified: this.config.verifySignature,
156
+ timestamp: Date.now(),
157
+ signature: req.headers[this.verificationOptions.headerName]
158
+ };
159
+ if (this.agentCompleteHandler) {
160
+ const result = await this.agentCompleteHandler(context);
161
+ return res.status(200).json(result);
162
+ }
163
+ res.status(200).json({
164
+ success: true,
165
+ message: 'Agent completion event received',
166
+ data: {
167
+ agentId: payload.agentId
168
+ }
169
+ });
170
+ } catch (error) {
171
+ return res.status(500).json({
172
+ success: false,
173
+ message: 'Failed to handle agent completion',
174
+ data: null
175
+ });
176
+ }
177
+ }
178
+ /**
179
+ * Handle gate result webhook
180
+ *
181
+ * @private
182
+ */ async handleGateResult(req, res) {
183
+ try {
184
+ const payload = req.body;
185
+ if (!payload.taskId || payload.gateType === undefined) {
186
+ return res.status(400).json({
187
+ success: false,
188
+ message: 'Missing required fields',
189
+ data: null
190
+ });
191
+ }
192
+ const context = {
193
+ payload,
194
+ isVerified: this.config.verifySignature,
195
+ timestamp: Date.now(),
196
+ signature: req.headers[this.verificationOptions.headerName]
197
+ };
198
+ if (this.gateResultHandler) {
199
+ const result = await this.gateResultHandler(context);
200
+ return res.status(200).json(result);
201
+ }
202
+ res.status(200).json({
203
+ success: true,
204
+ message: 'Gate result received',
205
+ data: {
206
+ taskId: payload.taskId,
207
+ passed: payload.passed
208
+ }
209
+ });
210
+ } catch (error) {
211
+ return res.status(500).json({
212
+ success: false,
213
+ message: 'Failed to handle gate result',
214
+ data: null
215
+ });
216
+ }
217
+ }
218
+ /**
219
+ * Handle consensus result webhook
220
+ *
221
+ * @private
222
+ */ async handleConsensusResult(req, res) {
223
+ try {
224
+ const payload = req.body;
225
+ if (!payload.taskId || payload.validatorCount === undefined) {
226
+ return res.status(400).json({
227
+ success: false,
228
+ message: 'Missing required fields',
229
+ data: null
230
+ });
231
+ }
232
+ const context = {
233
+ payload,
234
+ isVerified: this.config.verifySignature,
235
+ timestamp: Date.now(),
236
+ signature: req.headers[this.verificationOptions.headerName]
237
+ };
238
+ if (this.consensusResultHandler) {
239
+ const result = await this.consensusResultHandler(context);
240
+ return res.status(200).json(result);
241
+ }
242
+ res.status(200).json({
243
+ success: true,
244
+ message: 'Consensus result received',
245
+ data: {
246
+ taskId: payload.taskId,
247
+ consensusScore: payload.consensusScore
248
+ }
249
+ });
250
+ } catch (error) {
251
+ return res.status(500).json({
252
+ success: false,
253
+ message: 'Failed to handle consensus result',
254
+ data: null
255
+ });
256
+ }
257
+ }
258
+ /**
259
+ * Handle Product Owner decision webhook
260
+ *
261
+ * @private
262
+ */ async handlePODecision(req, res) {
263
+ try {
264
+ const payload = req.body;
265
+ if (!payload.taskId || !payload.decision) {
266
+ return res.status(400).json({
267
+ success: false,
268
+ message: 'Missing required fields',
269
+ data: null
270
+ });
271
+ }
272
+ // Validate decision enum
273
+ if (![
274
+ 'PROCEED',
275
+ 'ITERATE',
276
+ 'ABORT'
277
+ ].includes(payload.decision)) {
278
+ return res.status(400).json({
279
+ success: false,
280
+ message: 'Invalid decision value',
281
+ data: null
282
+ });
283
+ }
284
+ const context = {
285
+ payload,
286
+ isVerified: this.config.verifySignature,
287
+ timestamp: Date.now(),
288
+ signature: req.headers[this.verificationOptions.headerName]
289
+ };
290
+ if (this.poDecisionHandler) {
291
+ const result = await this.poDecisionHandler(context);
292
+ return res.status(200).json(result);
293
+ }
294
+ res.status(200).json({
295
+ success: true,
296
+ message: 'PO decision received',
297
+ data: {
298
+ taskId: payload.taskId,
299
+ decision: payload.decision
300
+ }
301
+ });
302
+ } catch (error) {
303
+ return res.status(500).json({
304
+ success: false,
305
+ message: 'Failed to handle PO decision',
306
+ data: null
307
+ });
308
+ }
309
+ }
310
+ /**
311
+ * Verify HMAC signature of webhook payload
312
+ *
313
+ * @private
314
+ * @param payload Raw request body as string
315
+ * @param signature Signature header value
316
+ * @returns Promise resolving to true if signature is valid
317
+ */ async verifySignature(payload, signature) {
318
+ try {
319
+ const hmac = createHmac(this.verificationOptions.algorithmType, this.config.secret);
320
+ hmac.update(payload, 'utf8');
321
+ const expectedSignature = hmac.digest('hex');
322
+ // Use timing-safe comparison
323
+ return timingSafeEqual(Buffer.from(signature), Buffer.from(expectedSignature));
324
+ } catch (error) {
325
+ console.error('Signature verification error:', error);
326
+ return false;
327
+ }
328
+ }
329
+ /**
330
+ * Error handling middleware
331
+ *
332
+ * @private
333
+ */ errorHandler(error, req, res, next) {
334
+ console.error('Webhook error:', error);
335
+ res.status(500).json({
336
+ success: false,
337
+ message: 'Internal server error',
338
+ data: null
339
+ });
340
+ }
341
+ }
342
+ /**
343
+ * Create webhook router with optional handlers
344
+ */ export const createWebhookRouter = (config, handlers)=>{
345
+ const webhooks = new TriggerDevWebhooks(config);
346
+ if (handlers?.onAgentComplete) {
347
+ webhooks.onAgentComplete(handlers.onAgentComplete);
348
+ }
349
+ if (handlers?.onGateResult) {
350
+ webhooks.onGateResult(handlers.onGateResult);
351
+ }
352
+ if (handlers?.onConsensusResult) {
353
+ webhooks.onConsensusResult(handlers.onConsensusResult);
354
+ }
355
+ if (handlers?.onPODecision) {
356
+ webhooks.onPODecision(handlers.onPODecision);
357
+ }
358
+ return webhooks.getRouter();
359
+ };
360
+ export default TriggerDevWebhooks;
361
+
362
+ //# sourceMappingURL=trigger-dev-webhooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/integration/trigger-dev-webhooks.ts"],"sourcesContent":["/**\r\n * Trigger.dev Webhook Handlers for CFN Loop Integration\r\n *\r\n * Express router providing secure webhook endpoints for:\r\n * - Agent completion events\r\n * - Gate check results\r\n * - Consensus collection results\r\n * - Product Owner decisions\r\n *\r\n * Configuration:\r\n * - WEBHOOK_SECRET: Shared secret for HMAC signature verification\r\n * - WEBHOOK_ALGORITHM: Hash algorithm (default: sha256)\r\n */\r\n\r\nimport { Router } from 'express';\r\nimport type { Request, Response, NextFunction } from 'express';\r\nimport { createHmac, timingSafeEqual } from 'node:crypto';\r\n\r\nimport type {\r\n AgentCompletePayload,\r\n GateResultPayload,\r\n ConsensusResultPayload,\r\n PODecisionPayload,\r\n WebhookContext,\r\n WebhookHandlerResult,\r\n WebhookVerificationOptions,\r\n} from '../types/trigger-dev-events.d.js';\r\n\r\n/**\r\n * Webhook validation error class\r\n */\r\nexport class WebhookValidationError extends Error {\r\n constructor(\r\n message: string,\r\n public readonly reason: string,\r\n public readonly statusCode: number = 401\r\n ) {\r\n super(message);\r\n this.name = 'WebhookValidationError';\r\n }\r\n}\r\n\r\n/**\r\n * Webhook handler type\r\n */\r\nexport type WebhookHandler<T> = (\r\n context: WebhookContext<T>\r\n) => Promise<WebhookHandlerResult>;\r\n\r\n/**\r\n * Configuration for webhook handlers\r\n */\r\nexport interface WebhookConfig {\r\n secret?: string;\r\n algorithm?: string;\r\n verifySignature?: boolean;\r\n}\r\n\r\n/**\r\n * TriggerDevWebhooks - Express router for secure webhook handling\r\n *\r\n * Provides endpoints:\r\n * - POST /webhooks/agent-complete\r\n * - POST /webhooks/gate-result\r\n * - POST /webhooks/consensus-result\r\n * - POST /webhooks/po-decision\r\n */\r\nexport class TriggerDevWebhooks {\r\n private readonly router: Router;\r\n private readonly config: Required<WebhookConfig>;\r\n private readonly verificationOptions: WebhookVerificationOptions;\r\n\r\n // Handler maps for dependency injection\r\n private agentCompleteHandler?: WebhookHandler<AgentCompletePayload>;\r\n private gateResultHandler?: WebhookHandler<GateResultPayload>;\r\n private consensusResultHandler?: WebhookHandler<ConsensusResultPayload>;\r\n private poDecisionHandler?: WebhookHandler<PODecisionPayload>;\r\n\r\n constructor(config?: WebhookConfig) {\r\n this.router = Router();\r\n this.config = {\r\n secret: config?.secret || process.env.WEBHOOK_SECRET || 'default-secret',\r\n algorithm: config?.algorithm || process.env.WEBHOOK_ALGORITHM || 'sha256',\r\n verifySignature: config?.verifySignature !== false,\r\n };\r\n\r\n this.verificationOptions = {\r\n algorithmType: (this.config.algorithm as 'sha256' | 'sha512') || 'sha256',\r\n headerName: 'x-trigger-signature',\r\n };\r\n\r\n this.setupRoutes();\r\n }\r\n\r\n /**\r\n * Register handler for agent completion events\r\n */\r\n onAgentComplete(handler: WebhookHandler<AgentCompletePayload>): this {\r\n this.agentCompleteHandler = handler;\r\n return this;\r\n }\r\n\r\n /**\r\n * Register handler for gate result events\r\n */\r\n onGateResult(handler: WebhookHandler<GateResultPayload>): this {\r\n this.gateResultHandler = handler;\r\n return this;\r\n }\r\n\r\n /**\r\n * Register handler for consensus result events\r\n */\r\n onConsensusResult(handler: WebhookHandler<ConsensusResultPayload>): this {\r\n this.consensusResultHandler = handler;\r\n return this;\r\n }\r\n\r\n /**\r\n * Register handler for Product Owner decision events\r\n */\r\n onPODecision(handler: WebhookHandler<PODecisionPayload>): this {\r\n this.poDecisionHandler = handler;\r\n return this;\r\n }\r\n\r\n /**\r\n * Get Express router for mounting\r\n */\r\n getRouter(): Router {\r\n return this.router;\r\n }\r\n\r\n /**\r\n * Setup all webhook routes\r\n *\r\n * @private\r\n */\r\n private setupRoutes(): void {\r\n // Middleware for signature verification\r\n this.router.use(this.signatureVerificationMiddleware.bind(this));\r\n\r\n // Agent completion endpoint\r\n this.router.post('/agent-complete', this.handleAgentComplete.bind(this));\r\n\r\n // Gate result endpoint\r\n this.router.post('/gate-result', this.handleGateResult.bind(this));\r\n\r\n // Consensus result endpoint\r\n this.router.post('/consensus-result', this.handleConsensusResult.bind(this));\r\n\r\n // PO decision endpoint\r\n this.router.post('/po-decision', this.handlePODecision.bind(this));\r\n\r\n // Error handling middleware\r\n this.router.use(this.errorHandler.bind(this));\r\n }\r\n\r\n /**\r\n * Signature verification middleware\r\n *\r\n * TODO: RUNTIME_TEST: Verify HMAC signature validation with valid/invalid keys\r\n *\r\n * @private\r\n */\r\n private async signatureVerificationMiddleware(\r\n req: Request,\r\n res: Response,\r\n next: NextFunction\r\n ): Promise<void> {\r\n try {\r\n if (!this.config.verifySignature) {\r\n return next();\r\n }\r\n\r\n const signature = req.headers[this.verificationOptions.headerName];\r\n\r\n if (!signature) {\r\n throw new WebhookValidationError(\r\n 'Missing webhook signature',\r\n 'MISSING_SIGNATURE'\r\n );\r\n }\r\n\r\n const body = JSON.stringify(req.body);\r\n const isValid = await this.verifySignature(body, signature as string);\r\n\r\n if (!isValid) {\r\n throw new WebhookValidationError(\r\n 'Invalid webhook signature',\r\n 'INVALID_SIGNATURE'\r\n );\r\n }\r\n\r\n next();\r\n } catch (error) {\r\n if (error instanceof WebhookValidationError) {\r\n return res.status(error.statusCode).json({\r\n success: false,\r\n message: error.message,\r\n reason: error.reason,\r\n });\r\n }\r\n\r\n return res.status(500).json({\r\n success: false,\r\n message: 'Signature verification failed',\r\n reason: 'INTERNAL_ERROR',\r\n });\r\n }\r\n }\r\n\r\n /**\r\n * Handle agent completion webhook\r\n *\r\n * @private\r\n */\r\n private async handleAgentComplete(\r\n req: Request,\r\n res: Response\r\n ): Promise<void> {\r\n try {\r\n const payload = req.body as AgentCompletePayload;\r\n\r\n // Validate required fields\r\n if (!payload.agentId || !payload.taskId) {\r\n return res.status(400).json({\r\n success: false,\r\n message: 'Missing required fields',\r\n data: null,\r\n });\r\n }\r\n\r\n const context: WebhookContext<AgentCompletePayload> = {\r\n payload,\r\n isVerified: this.config.verifySignature,\r\n timestamp: Date.now(),\r\n signature: req.headers[this.verificationOptions.headerName] as string,\r\n };\r\n\r\n if (this.agentCompleteHandler) {\r\n const result = await this.agentCompleteHandler(context);\r\n return res.status(200).json(result);\r\n }\r\n\r\n res.status(200).json({\r\n success: true,\r\n message: 'Agent completion event received',\r\n data: { agentId: payload.agentId },\r\n });\r\n } catch (error) {\r\n return res.status(500).json({\r\n success: false,\r\n message: 'Failed to handle agent completion',\r\n data: null,\r\n });\r\n }\r\n }\r\n\r\n /**\r\n * Handle gate result webhook\r\n *\r\n * @private\r\n */\r\n private async handleGateResult(req: Request, res: Response): Promise<void> {\r\n try {\r\n const payload = req.body as GateResultPayload;\r\n\r\n if (!payload.taskId || payload.gateType === undefined) {\r\n return res.status(400).json({\r\n success: false,\r\n message: 'Missing required fields',\r\n data: null,\r\n });\r\n }\r\n\r\n const context: WebhookContext<GateResultPayload> = {\r\n payload,\r\n isVerified: this.config.verifySignature,\r\n timestamp: Date.now(),\r\n signature: req.headers[this.verificationOptions.headerName] as string,\r\n };\r\n\r\n if (this.gateResultHandler) {\r\n const result = await this.gateResultHandler(context);\r\n return res.status(200).json(result);\r\n }\r\n\r\n res.status(200).json({\r\n success: true,\r\n message: 'Gate result received',\r\n data: { taskId: payload.taskId, passed: payload.passed },\r\n });\r\n } catch (error) {\r\n return res.status(500).json({\r\n success: false,\r\n message: 'Failed to handle gate result',\r\n data: null,\r\n });\r\n }\r\n }\r\n\r\n /**\r\n * Handle consensus result webhook\r\n *\r\n * @private\r\n */\r\n private async handleConsensusResult(\r\n req: Request,\r\n res: Response\r\n ): Promise<void> {\r\n try {\r\n const payload = req.body as ConsensusResultPayload;\r\n\r\n if (!payload.taskId || payload.validatorCount === undefined) {\r\n return res.status(400).json({\r\n success: false,\r\n message: 'Missing required fields',\r\n data: null,\r\n });\r\n }\r\n\r\n const context: WebhookContext<ConsensusResultPayload> = {\r\n payload,\r\n isVerified: this.config.verifySignature,\r\n timestamp: Date.now(),\r\n signature: req.headers[this.verificationOptions.headerName] as string,\r\n };\r\n\r\n if (this.consensusResultHandler) {\r\n const result = await this.consensusResultHandler(context);\r\n return res.status(200).json(result);\r\n }\r\n\r\n res.status(200).json({\r\n success: true,\r\n message: 'Consensus result received',\r\n data: {\r\n taskId: payload.taskId,\r\n consensusScore: payload.consensusScore,\r\n },\r\n });\r\n } catch (error) {\r\n return res.status(500).json({\r\n success: false,\r\n message: 'Failed to handle consensus result',\r\n data: null,\r\n });\r\n }\r\n }\r\n\r\n /**\r\n * Handle Product Owner decision webhook\r\n *\r\n * @private\r\n */\r\n private async handlePODecision(req: Request, res: Response): Promise<void> {\r\n try {\r\n const payload = req.body as PODecisionPayload;\r\n\r\n if (!payload.taskId || !payload.decision) {\r\n return res.status(400).json({\r\n success: false,\r\n message: 'Missing required fields',\r\n data: null,\r\n });\r\n }\r\n\r\n // Validate decision enum\r\n if (!['PROCEED', 'ITERATE', 'ABORT'].includes(payload.decision)) {\r\n return res.status(400).json({\r\n success: false,\r\n message: 'Invalid decision value',\r\n data: null,\r\n });\r\n }\r\n\r\n const context: WebhookContext<PODecisionPayload> = {\r\n payload,\r\n isVerified: this.config.verifySignature,\r\n timestamp: Date.now(),\r\n signature: req.headers[this.verificationOptions.headerName] as string,\r\n };\r\n\r\n if (this.poDecisionHandler) {\r\n const result = await this.poDecisionHandler(context);\r\n return res.status(200).json(result);\r\n }\r\n\r\n res.status(200).json({\r\n success: true,\r\n message: 'PO decision received',\r\n data: { taskId: payload.taskId, decision: payload.decision },\r\n });\r\n } catch (error) {\r\n return res.status(500).json({\r\n success: false,\r\n message: 'Failed to handle PO decision',\r\n data: null,\r\n });\r\n }\r\n }\r\n\r\n /**\r\n * Verify HMAC signature of webhook payload\r\n *\r\n * @private\r\n * @param payload Raw request body as string\r\n * @param signature Signature header value\r\n * @returns Promise resolving to true if signature is valid\r\n */\r\n private async verifySignature(\r\n payload: string,\r\n signature: string\r\n ): Promise<boolean> {\r\n try {\r\n const hmac = createHmac(\r\n this.verificationOptions.algorithmType,\r\n this.config.secret\r\n );\r\n hmac.update(payload, 'utf8');\r\n const expectedSignature = hmac.digest('hex');\r\n\r\n // Use timing-safe comparison\r\n return timingSafeEqual(\r\n Buffer.from(signature),\r\n Buffer.from(expectedSignature)\r\n );\r\n } catch (error) {\r\n console.error('Signature verification error:', error);\r\n return false;\r\n }\r\n }\r\n\r\n /**\r\n * Error handling middleware\r\n *\r\n * @private\r\n */\r\n private errorHandler(\r\n error: Error,\r\n req: Request,\r\n res: Response,\r\n next: NextFunction\r\n ): void {\r\n console.error('Webhook error:', error);\r\n\r\n res.status(500).json({\r\n success: false,\r\n message: 'Internal server error',\r\n data: null,\r\n });\r\n }\r\n}\r\n\r\n/**\r\n * Create webhook router with optional handlers\r\n */\r\nexport const createWebhookRouter = (\r\n config?: WebhookConfig,\r\n handlers?: {\r\n onAgentComplete?: WebhookHandler<AgentCompletePayload>;\r\n onGateResult?: WebhookHandler<GateResultPayload>;\r\n onConsensusResult?: WebhookHandler<ConsensusResultPayload>;\r\n onPODecision?: WebhookHandler<PODecisionPayload>;\r\n }\r\n): Router => {\r\n const webhooks = new TriggerDevWebhooks(config);\r\n\r\n if (handlers?.onAgentComplete) {\r\n webhooks.onAgentComplete(handlers.onAgentComplete);\r\n }\r\n if (handlers?.onGateResult) {\r\n webhooks.onGateResult(handlers.onGateResult);\r\n }\r\n if (handlers?.onConsensusResult) {\r\n webhooks.onConsensusResult(handlers.onConsensusResult);\r\n }\r\n if (handlers?.onPODecision) {\r\n webhooks.onPODecision(handlers.onPODecision);\r\n }\r\n\r\n return webhooks.getRouter();\r\n};\r\n\r\nexport default TriggerDevWebhooks;\r\n"],"names":["Router","createHmac","timingSafeEqual","WebhookValidationError","Error","message","reason","statusCode","name","TriggerDevWebhooks","router","config","verificationOptions","agentCompleteHandler","gateResultHandler","consensusResultHandler","poDecisionHandler","secret","process","env","WEBHOOK_SECRET","algorithm","WEBHOOK_ALGORITHM","verifySignature","algorithmType","headerName","setupRoutes","onAgentComplete","handler","onGateResult","onConsensusResult","onPODecision","getRouter","use","signatureVerificationMiddleware","bind","post","handleAgentComplete","handleGateResult","handleConsensusResult","handlePODecision","errorHandler","req","res","next","signature","headers","body","JSON","stringify","isValid","error","status","json","success","payload","agentId","taskId","data","context","isVerified","timestamp","Date","now","result","gateType","undefined","passed","validatorCount","consensusScore","decision","includes","hmac","update","expectedSignature","digest","Buffer","from","console","createWebhookRouter","handlers","webhooks"],"mappings":"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"}
@@ -60,9 +60,15 @@ import { StandardError } from './errors';
60
60
  try {
61
61
  decoded = decodeURIComponent(decoded);
62
62
  } catch (error) {
63
- // Invalid URL encoding (including malformed UTF-8) indicates attack
64
- // e.g., %c0%ae is malformed UTF-8 for overlong encoding
65
- invalidEncodingDetected = true;
63
+ // Invalid URL encoding can be legitimate (e.g., file100%, %PATH%)
64
+ // These are literal percent signs in filenames, not attacks
65
+ // Only flag as attack if it's malformed UTF-8 (overlong encoding)
66
+ const errorMessage = error.message || '';
67
+ // Malformed UTF-8 sequences like %c0%ae are attacks
68
+ if (decoded.match(/%[cC][0-9a-fA-F]/) || decoded.match(/%[eE][0-9a-fA-F]/)) {
69
+ invalidEncodingDetected = true;
70
+ break;
71
+ }
66
72
  break;
67
73
  }
68
74
  iterations++;
@@ -85,8 +91,11 @@ import { StandardError } from './errors';
85
91
  reason: 'INVALID_ENCODING_DETECTED'
86
92
  });
87
93
  }
88
- // Detect if decoding required multiple iterations (possible double-encoding attack)
89
- const encodingAttackDetected = iterations > 1;
94
+ // Detect double+ encoding attacks (3+ iterations = double-encoded or more)
95
+ // Single-level URL encoding requires 2 iterations: decode + stability check
96
+ // Example: subdir%2ffile.txt → subdir/file.txt → stable (2 iterations, legitimate)
97
+ // Example: %252e%252e%252f → %2e%2e%2f → ../ (3 iterations, attack)
98
+ const encodingAttackDetected = iterations > 2;
90
99
  // Unicode normalization to handle overlong UTF-8 sequences
91
100
  // e.g., %c0%ae (%c0%ae = UTF-8 overlong encoding for ".")
92
101
  let normalized = decoded;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/lib/path-validator.ts"],"sourcesContent":["/**\r\n * Path Validator - Security Utility for Safe File Operations\r\n *\r\n * Provides robust path sanitization and validation to prevent path traversal attacks (CVSS 7.0+).\r\n * Enforces strict rules on file access with protection against encoding attacks:\r\n *\r\n * CRITICAL FIXES (CVSS 7.0+):\r\n * - Iterative URL decoding prevents double-encoding bypasses (%252e%252e%252f → ../)\r\n * - Unicode normalization (NFC) prevents overlong UTF-8 bypasses (%c0%ae → .)\r\n * - Null byte detection prevents null injection attacks\r\n * - All decoding performed BEFORE path normalization to prevent layered attacks\r\n * - Encoding attack detection with security logging\r\n *\r\n * Base Security Controls:\r\n * - Path normalization to resolve \"..\" and \".\" sequences\r\n * - Validation that resolved paths stay within allowed directories\r\n * - Detection and rejection of symlinks\r\n * - Rejection of absolute paths outside allowed directories\r\n * - Prevention of home directory access (\"~\")\r\n *\r\n * @module path-validator\r\n * @version 2.0.0 (SECURITY CRITICAL)\r\n */\r\n\r\nimport * as path from 'path';\r\nimport * as fs from 'fs';\r\nimport { StandardError } from './errors';\r\n\r\n/**\r\n * Path validation error - thrown when a path violates security constraints\r\n */\r\nexport class PathValidationError extends StandardError {\r\n constructor(message: string, context?: Record<string, unknown>) {\r\n super('PATH_VALIDATION_ERROR', message, context);\r\n this.name = 'PathValidationError';\r\n }\r\n}\r\n\r\n/**\r\n * Security encoding attack detection\r\n */\r\nexport interface EncodingAttackDetection {\r\n detected: boolean;\r\n type?: 'double_encoding' | 'unicode_encoding' | 'mixed_encoding';\r\n originalInput: string;\r\n decodedOutput: string;\r\n iterationsRequired: number;\r\n}\r\n\r\n/**\r\n * Path validation result with detailed information\r\n */\r\nexport interface PathValidationResult {\r\n valid: boolean;\r\n resolvedPath: string;\r\n normalizedPath: string;\r\n isWithinBase: boolean;\r\n isSymlink: boolean;\r\n reason?: string;\r\n}\r\n\r\n/**\r\n * Safely decode a path with protection against encoding attacks\r\n *\r\n * Performs iterative URL decoding and Unicode normalization to prevent:\r\n * - Double encoding bypass (e.g., %252e%252e%252f → %2e%2e%2f → ../)\r\n * - Overlong UTF-8 encoding (e.g., %c0%ae%c0%ae/ → ../)\r\n * - Mixed encoding attacks\r\n *\r\n * @param inputPath - The potentially encoded path\r\n * @returns Decoded path and attack detection info\r\n * @throws PathValidationError if encoding attack detected\r\n *\r\n * @example\r\n * const { decoded, encoding } = decodePathSafely('%252e%252e%252f');\r\n * // Detects double-encoding attempt\r\n */\r\nfunction decodePathSafely(inputPath: string): {\r\n decoded: string;\r\n encoding: EncodingAttackDetection;\r\n} {\r\n let decoded = inputPath;\r\n let previous = '';\r\n let iterations = 0;\r\n const MAX_ITERATIONS = 5;\r\n const originalInput = inputPath;\r\n let invalidEncodingDetected = false;\r\n\r\n // Iteratively decode URL-encoded characters until stable\r\n // This prevents bypass attacks using multiple encoding layers\r\n while (decoded !== previous && iterations < MAX_ITERATIONS) {\r\n previous = decoded;\r\n try {\r\n decoded = decodeURIComponent(decoded);\r\n } catch (error) {\r\n // Invalid URL encoding (including malformed UTF-8) indicates attack\r\n // e.g., %c0%ae is malformed UTF-8 for overlong encoding\r\n invalidEncodingDetected = true;\r\n // Treat invalid encoding as a suspicious attack indicator\r\n // but continue with the path as-is for analysis\r\n break;\r\n }\r\n iterations++;\r\n }\r\n\r\n // Check if we hit max iterations (indicates potential encoding attack)\r\n if (iterations >= MAX_ITERATIONS && decoded !== previous) {\r\n throw new PathValidationError(\r\n 'Path validation failed: excessive encoding layers detected',\r\n {\r\n originalInput,\r\n decodedOutput: decoded,\r\n iterations,\r\n reason: 'ENCODING_ATTACK_DETECTED',\r\n }\r\n );\r\n }\r\n\r\n // Invalid encoding like malformed UTF-8 is itself an attack indicator\r\n if (invalidEncodingDetected) {\r\n throw new PathValidationError(\r\n 'Path validation failed: invalid encoding detected (possible encoding attack)',\r\n {\r\n originalInput,\r\n decodedOutput: decoded,\r\n iterations,\r\n reason: 'INVALID_ENCODING_DETECTED',\r\n }\r\n );\r\n }\r\n\r\n // Detect if decoding required multiple iterations (possible double-encoding attack)\r\n const encodingAttackDetected = iterations > 1;\r\n\r\n // Unicode normalization to handle overlong UTF-8 sequences\r\n // e.g., %c0%ae (%c0%ae = UTF-8 overlong encoding for \".\")\r\n let normalized = decoded;\r\n try {\r\n normalized = decoded.normalize('NFC');\r\n } catch (error) {\r\n // Some paths may not be valid Unicode, continue with non-normalized version\r\n }\r\n\r\n // Check for null bytes (another common encoding attack vector)\r\n if (normalized.includes('\\0')) {\r\n throw new PathValidationError(\r\n 'Path validation failed: null byte injection detected',\r\n {\r\n originalInput,\r\n decodedOutput: normalized,\r\n reason: 'NULL_BYTE_INJECTION',\r\n }\r\n );\r\n }\r\n\r\n return {\r\n decoded: normalized,\r\n encoding: {\r\n detected: encodingAttackDetected,\r\n type: encodingAttackDetected ? 'double_encoding' : undefined,\r\n originalInput,\r\n decodedOutput: normalized,\r\n iterationsRequired: iterations,\r\n },\r\n };\r\n}\r\n\r\n/**\r\n * Validate a file path to prevent directory traversal attacks\r\n *\r\n * Security checks performed (in order):\r\n * 1. CRITICAL: Iteratively decode URL encoding to handle %252e%252e%252f and similar bypasses\r\n * 2. CRITICAL: Normalize Unicode (NFC) to handle overlong UTF-8 like %c0%ae%c0%ae/\r\n * 3. CRITICAL: Detect null bytes and excessive encoding layers\r\n * 4. Check for home directory expansion (\"~\") on DECODED path\r\n * 5. Normalize path to resolve \"..\" and \".\"\r\n * 6. Verify all suspicious patterns are eliminated\r\n * 7. Check if path is within base directory\r\n * 8. Reject symlinks to prevent symlink attacks\r\n * 9. Log any encoding attacks detected for security monitoring\r\n *\r\n * @param filePath - The file path to validate (may be encoded)\r\n * @param baseDirectory - The base directory that file must reside within\r\n * @returns PathValidationResult with validation details\r\n * @throws PathValidationError if path validation fails or encoding attack detected\r\n *\r\n * @example\r\n * const result = validatePath('docs/FEATURE.md', './.claude/skills');\r\n * if (!result.valid) {\r\n * throw result; // Safe to throw, contains all context\r\n * }\r\n * // Use result.resolvedPath\r\n *\r\n * @security\r\n * Designed to prevent:\r\n * - Double-encoding bypasses: %252e%252e%252f\r\n * - Overlong UTF-8: %c0%ae%c0%ae/\r\n * - Mixed encoding: URL + Unicode combinations\r\n * - Null byte injection: file.txt%00.jpg\r\n * - Traditional path traversal: ../../../etc/passwd\r\n */\r\nexport function validatePath(filePath: string, baseDirectory: string): PathValidationResult {\r\n // SECURITY FIX: Decode all encoding layers first before any path normalization\r\n // This prevents double-encoding bypasses like %252e%252e%252f\r\n const { decoded: decodedPath, encoding: pathEncoding } = decodePathSafely(filePath);\r\n const { decoded: decodedBase } = decodePathSafely(baseDirectory);\r\n\r\n // Log encoding attacks for security monitoring\r\n if (pathEncoding.detected) {\r\n // In production, this should trigger security alerts\r\n console.warn('Security: Encoding attack detected in path input', {\r\n originalInput: pathEncoding.originalInput,\r\n decodedOutput: pathEncoding.decodedOutput,\r\n iterationsRequired: pathEncoding.iterationsRequired,\r\n });\r\n }\r\n\r\n // Check for home directory expansion attempts on DECODED path\r\n if (decodedPath.startsWith('~') || decodedPath.includes('/~') || decodedPath.includes('\\\\~')) {\r\n throw new PathValidationError(\r\n 'Path validation failed: home directory access denied',\r\n {\r\n filePath,\r\n decodedPath,\r\n baseDirectory,\r\n reason: 'HOME_DIRECTORY_ACCESS',\r\n }\r\n );\r\n }\r\n\r\n // Check for home directory expansion attempts in baseDirectory\r\n if (decodedBase.startsWith('~')) {\r\n throw new PathValidationError(\r\n 'Base directory validation failed: home directory access denied',\r\n {\r\n baseDirectory,\r\n decodedBase,\r\n reason: 'BASE_HOME_DIRECTORY_ACCESS',\r\n }\r\n );\r\n }\r\n\r\n // Normalize the base directory first\r\n const normalizedBase = path.normalize(decodedBase);\r\n const resolvedBase = path.resolve(normalizedBase);\r\n\r\n // Normalize and resolve the file path relative to base\r\n // NOW normalized on already-decoded path to prevent encoding bypasses\r\n const normalizedPath = path.normalize(decodedPath);\r\n\r\n // Check if path contains suspicious patterns after normalization\r\n if (normalizedPath.includes('..') || normalizedPath === '.' || normalizedPath.includes('/./')) {\r\n throw new PathValidationError(\r\n 'Path validation failed: path contains directory traversal patterns',\r\n {\r\n filePath,\r\n decodedPath,\r\n normalizedPath,\r\n baseDirectory,\r\n reason: 'TRAVERSAL_PATTERN_DETECTED',\r\n }\r\n );\r\n }\r\n\r\n // Resolve the path relative to base directory\r\n const resolvedPath = path.resolve(resolvedBase, normalizedPath);\r\n\r\n // Check if resolved path is within base directory\r\n const isWithinBase = isPathWithinBase(resolvedPath, resolvedBase);\r\n\r\n if (!isWithinBase) {\r\n throw new PathValidationError(\r\n 'Path validation failed: resolved path is outside allowed directory',\r\n {\r\n filePath,\r\n resolvedPath,\r\n baseDirectory: resolvedBase,\r\n reason: 'PATH_OUTSIDE_BASE',\r\n }\r\n );\r\n }\r\n\r\n // Check for symlinks (prevents symlink attacks)\r\n let isSymlink = false;\r\n try {\r\n const stats = fs.lstatSync(resolvedPath);\r\n isSymlink = stats.isSymbolicLink();\r\n\r\n if (isSymlink) {\r\n throw new PathValidationError(\r\n 'Path validation failed: symbolic links are not allowed',\r\n {\r\n filePath,\r\n resolvedPath,\r\n reason: 'SYMLINK_NOT_ALLOWED',\r\n }\r\n );\r\n }\r\n } catch (error) {\r\n // File doesn't exist yet (expected for validation before creation)\r\n // or it's a symlink that was rejected above\r\n if (error instanceof PathValidationError) {\r\n throw error;\r\n }\r\n // Other errors (permission denied, etc.) are not path validation failures\r\n // The file validation happens later\r\n }\r\n\r\n return {\r\n valid: true,\r\n resolvedPath,\r\n normalizedPath,\r\n isWithinBase: true,\r\n isSymlink: false,\r\n };\r\n}\r\n\r\n/**\r\n * Check if a path is within a base directory\r\n *\r\n * Uses path resolution and string comparison to ensure the resolved path\r\n * is actually within the base directory (not just sharing a prefix).\r\n *\r\n * @param filePath - The path to check\r\n * @param baseDirectory - The base directory\r\n * @returns True if filePath is within baseDirectory\r\n *\r\n * @example\r\n * isPathWithinBase('/home/user/project/src/file.ts', '/home/user/project') // true\r\n * isPathWithinBase('/home/user/project-evil/file.ts', '/home/user/project') // false\r\n */\r\nexport function isPathWithinBase(filePath: string, baseDirectory: string): boolean {\r\n // Ensure both paths are normalized and absolute\r\n const normalizedFile = path.normalize(path.resolve(filePath));\r\n const normalizedBase = path.normalize(path.resolve(baseDirectory));\r\n\r\n // Exact match\r\n if (normalizedFile === normalizedBase) {\r\n return true;\r\n }\r\n\r\n // Check if file is within base (use path.relative to ensure it's not going up)\r\n const relative = path.relative(normalizedBase, normalizedFile);\r\n\r\n // If relative path starts with \"..\", it's outside the base directory\r\n if (relative.startsWith('..')) {\r\n return false;\r\n }\r\n\r\n // If relative path is absolute, it's outside the base directory\r\n if (path.isAbsolute(relative)) {\r\n return false;\r\n }\r\n\r\n return true;\r\n}\r\n\r\n/**\r\n * Validate multiple paths within the same base directory\r\n *\r\n * Efficiently validates multiple paths, returning results for each.\r\n *\r\n * @param filePaths - Array of file paths to validate\r\n * @param baseDirectory - The base directory that all files must reside within\r\n * @returns Map of file path to validation result\r\n *\r\n * @example\r\n * const results = validatePaths(['docs/SKILL.md', 'src/index.ts'], './.claude/skills');\r\n * results.forEach((result, filePath) => {\r\n * if (!result.valid) {\r\n * console.error(`Invalid path: ${filePath}`, result.reason);\r\n * }\r\n * });\r\n */\r\nexport function validatePaths(\r\n filePaths: string[],\r\n baseDirectory: string\r\n): Map<string, PathValidationResult> {\r\n const results = new Map<string, PathValidationResult>();\r\n\r\n for (const filePath of filePaths) {\r\n try {\r\n const result = validatePath(filePath, baseDirectory);\r\n results.set(filePath, result);\r\n } catch (error) {\r\n if (error instanceof PathValidationError) {\r\n results.set(filePath, {\r\n valid: false,\r\n resolvedPath: '',\r\n normalizedPath: '',\r\n isWithinBase: false,\r\n isSymlink: false,\r\n reason: error.context?.reason as string | undefined,\r\n });\r\n } else {\r\n throw error;\r\n }\r\n }\r\n }\r\n\r\n return results;\r\n}\r\n\r\n/**\r\n * Get the safe path for a file (or throw if validation fails)\r\n *\r\n * Convenience function that validates and returns the resolved path,\r\n * or throws an error if validation fails.\r\n *\r\n * @param filePath - The file path to validate\r\n * @param baseDirectory - The base directory that file must reside within\r\n * @returns The resolved, validated absolute path\r\n * @throws PathValidationError if path validation fails\r\n *\r\n * @example\r\n * const safePath = getSafePath('docs/SKILL.md', './.claude/skills');\r\n * fs.readFileSync(safePath); // Safe to use\r\n */\r\nexport function getSafePath(filePath: string, baseDirectory: string): string {\r\n const result = validatePath(filePath, baseDirectory);\r\n return result.resolvedPath;\r\n}\r\n\r\n/**\r\n * Check if a path is considered safe for operations\r\n *\r\n * This is a non-throwing version of validatePath for conditional logic.\r\n *\r\n * @param filePath - The file path to check\r\n * @param baseDirectory - The base directory\r\n * @returns True if path is safe, false otherwise\r\n *\r\n * @example\r\n * if (isPathSafe(userInput, './.claude/skills')) {\r\n * // Process the file\r\n * } else {\r\n * // Reject the request\r\n * }\r\n */\r\nexport function isPathSafe(filePath: string, baseDirectory: string): boolean {\r\n try {\r\n validatePath(filePath, baseDirectory);\r\n return true;\r\n } catch {\r\n return false;\r\n }\r\n}\r\n\r\n/**\r\n * Get validation error details for a path (if invalid)\r\n *\r\n * Useful for logging and diagnostics.\r\n *\r\n * @param filePath - The file path to validate\r\n * @param baseDirectory - The base directory\r\n * @returns Error with details, or undefined if path is valid\r\n *\r\n * @example\r\n * const error = getPathValidationError('../../etc/passwd', './.claude/skills');\r\n * if (error) {\r\n * logger.error(error.message, error.context);\r\n * }\r\n */\r\nexport function getPathValidationError(\r\n filePath: string,\r\n baseDirectory: string\r\n): PathValidationError | undefined {\r\n try {\r\n validatePath(filePath, baseDirectory);\r\n return undefined;\r\n } catch (error) {\r\n if (error instanceof PathValidationError) {\r\n return error;\r\n }\r\n throw error;\r\n }\r\n}\r\n\r\n/**\r\n * List allowed files within a base directory (safely)\r\n *\r\n * Recursively lists all files within base directory, validating\r\n * each path to ensure it's within bounds.\r\n *\r\n * @param baseDirectory - The base directory to scan\r\n * @param options - Options for listing (maxDepth, filter)\r\n * @returns Array of validated, safe paths relative to baseDirectory\r\n *\r\n * @example\r\n * const files = safeListDirectory('./.claude/skills');\r\n * files.forEach(file => {\r\n * // All paths are guaranteed safe\r\n * console.log(file);\r\n * });\r\n */\r\nexport function safeListDirectory(\r\n baseDirectory: string,\r\n options?: {\r\n maxDepth?: number;\r\n filter?: (path: string) => boolean;\r\n }\r\n): string[] {\r\n const safeFiles: string[] = [];\r\n const maxDepth = options?.maxDepth ?? Infinity;\r\n const filter = options?.filter ?? (() => true);\r\n\r\n function walkDirectory(dir: string, currentDepth: number = 0): void {\r\n if (currentDepth > maxDepth) {\r\n return;\r\n }\r\n\r\n try {\r\n const entries = fs.readdirSync(dir, { withFileTypes: true });\r\n\r\n for (const entry of entries) {\r\n const fullPath = path.join(dir, entry.name);\r\n const relativePath = path.relative(baseDirectory, fullPath);\r\n\r\n // Validate the path is still within base\r\n if (!isPathWithinBase(fullPath, baseDirectory)) {\r\n continue;\r\n }\r\n\r\n // Apply filter\r\n if (!filter(relativePath)) {\r\n continue;\r\n }\r\n\r\n safeFiles.push(relativePath);\r\n\r\n // Recursively walk directories\r\n if (entry.isDirectory() && !entry.isSymbolicLink()) {\r\n walkDirectory(fullPath, currentDepth + 1);\r\n }\r\n }\r\n } catch (error) {\r\n // Silently skip directories we can't read (permission denied, etc.)\r\n }\r\n }\r\n\r\n walkDirectory(baseDirectory);\r\n return safeFiles;\r\n}\r\n"],"names":["path","fs","StandardError","PathValidationError","message","context","name","decodePathSafely","inputPath","decoded","previous","iterations","MAX_ITERATIONS","originalInput","invalidEncodingDetected","decodeURIComponent","error","decodedOutput","reason","encodingAttackDetected","normalized","normalize","includes","encoding","detected","type","undefined","iterationsRequired","validatePath","filePath","baseDirectory","decodedPath","pathEncoding","decodedBase","console","warn","startsWith","normalizedBase","resolvedBase","resolve","normalizedPath","resolvedPath","isWithinBase","isPathWithinBase","isSymlink","stats","lstatSync","isSymbolicLink","valid","normalizedFile","relative","isAbsolute","validatePaths","filePaths","results","Map","result","set","getSafePath","isPathSafe","getPathValidationError","safeListDirectory","options","safeFiles","maxDepth","Infinity","filter","walkDirectory","dir","currentDepth","entries","readdirSync","withFileTypes","entry","fullPath","join","relativePath","push","isDirectory"],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;CAsBC,GAED,YAAYA,UAAU,OAAO;AAC7B,YAAYC,QAAQ,KAAK;AACzB,SAASC,aAAa,QAAQ,WAAW;AAEzC;;CAEC,GACD,OAAO,MAAMC,4BAA4BD;IACvC,YAAYE,OAAe,EAAEC,OAAiC,CAAE;QAC9D,KAAK,CAAC,yBAAyBD,SAASC;QACxC,IAAI,CAACC,IAAI,GAAG;IACd;AACF;AAyBA;;;;;;;;;;;;;;;CAeC,GACD,SAASC,iBAAiBC,SAAiB;IAIzC,IAAIC,UAAUD;IACd,IAAIE,WAAW;IACf,IAAIC,aAAa;IACjB,MAAMC,iBAAiB;IACvB,MAAMC,gBAAgBL;IACtB,IAAIM,0BAA0B;IAE9B,yDAAyD;IACzD,8DAA8D;IAC9D,MAAOL,YAAYC,YAAYC,aAAaC,eAAgB;QAC1DF,WAAWD;QACX,IAAI;YACFA,UAAUM,mBAAmBN;QAC/B,EAAE,OAAOO,OAAO;YACd,oEAAoE;YACpE,wDAAwD;YACxDF,0BAA0B;YAG1B;QACF;QACAH;IACF;IAEA,uEAAuE;IACvE,IAAIA,cAAcC,kBAAkBH,YAAYC,UAAU;QACxD,MAAM,IAAIP,oBACR,8DACA;YACEU;YACAI,eAAeR;YACfE;YACAO,QAAQ;QACV;IAEJ;IAEA,sEAAsE;IACtE,IAAIJ,yBAAyB;QAC3B,MAAM,IAAIX,oBACR,gFACA;YACEU;YACAI,eAAeR;YACfE;YACAO,QAAQ;QACV;IAEJ;IAEA,oFAAoF;IACpF,MAAMC,yBAAyBR,aAAa;IAE5C,2DAA2D;IAC3D,0DAA0D;IAC1D,IAAIS,aAAaX;IACjB,IAAI;QACFW,aAAaX,QAAQY,SAAS,CAAC;IACjC,EAAE,OAAOL,OAAO;IACd,4EAA4E;IAC9E;IAEA,+DAA+D;IAC/D,IAAII,WAAWE,QAAQ,CAAC,OAAO;QAC7B,MAAM,IAAInB,oBACR,wDACA;YACEU;YACAI,eAAeG;YACfF,QAAQ;QACV;IAEJ;IAEA,OAAO;QACLT,SAASW;QACTG,UAAU;YACRC,UAAUL;YACVM,MAAMN,yBAAyB,oBAAoBO;YACnDb;YACAI,eAAeG;YACfO,oBAAoBhB;QACtB;IACF;AACF;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiCC,GACD,OAAO,SAASiB,aAAaC,QAAgB,EAAEC,aAAqB;IAClE,+EAA+E;IAC/E,8DAA8D;IAC9D,MAAM,EAAErB,SAASsB,WAAW,EAAER,UAAUS,YAAY,EAAE,GAAGzB,iBAAiBsB;IAC1E,MAAM,EAAEpB,SAASwB,WAAW,EAAE,GAAG1B,iBAAiBuB;IAElD,+CAA+C;IAC/C,IAAIE,aAAaR,QAAQ,EAAE;QACzB,qDAAqD;QACrDU,QAAQC,IAAI,CAAC,oDAAoD;YAC/DtB,eAAemB,aAAanB,aAAa;YACzCI,eAAee,aAAaf,aAAa;YACzCU,oBAAoBK,aAAaL,kBAAkB;QACrD;IACF;IAEA,8DAA8D;IAC9D,IAAII,YAAYK,UAAU,CAAC,QAAQL,YAAYT,QAAQ,CAAC,SAASS,YAAYT,QAAQ,CAAC,QAAQ;QAC5F,MAAM,IAAInB,oBACR,wDACA;YACE0B;YACAE;YACAD;YACAZ,QAAQ;QACV;IAEJ;IAEA,+DAA+D;IAC/D,IAAIe,YAAYG,UAAU,CAAC,MAAM;QAC/B,MAAM,IAAIjC,oBACR,kEACA;YACE2B;YACAG;YACAf,QAAQ;QACV;IAEJ;IAEA,qCAAqC;IACrC,MAAMmB,iBAAiBrC,KAAKqB,SAAS,CAACY;IACtC,MAAMK,eAAetC,KAAKuC,OAAO,CAACF;IAElC,uDAAuD;IACvD,sEAAsE;IACtE,MAAMG,iBAAiBxC,KAAKqB,SAAS,CAACU;IAEtC,iEAAiE;IACjE,IAAIS,eAAelB,QAAQ,CAAC,SAASkB,mBAAmB,OAAOA,eAAelB,QAAQ,CAAC,QAAQ;QAC7F,MAAM,IAAInB,oBACR,sEACA;YACE0B;YACAE;YACAS;YACAV;YACAZ,QAAQ;QACV;IAEJ;IAEA,8CAA8C;IAC9C,MAAMuB,eAAezC,KAAKuC,OAAO,CAACD,cAAcE;IAEhD,kDAAkD;IAClD,MAAME,eAAeC,iBAAiBF,cAAcH;IAEpD,IAAI,CAACI,cAAc;QACjB,MAAM,IAAIvC,oBACR,sEACA;YACE0B;YACAY;YACAX,eAAeQ;YACfpB,QAAQ;QACV;IAEJ;IAEA,gDAAgD;IAChD,IAAI0B,YAAY;IAChB,IAAI;QACF,MAAMC,QAAQ5C,GAAG6C,SAAS,CAACL;QAC3BG,YAAYC,MAAME,cAAc;QAEhC,IAAIH,WAAW;YACb,MAAM,IAAIzC,oBACR,0DACA;gBACE0B;gBACAY;gBACAvB,QAAQ;YACV;QAEJ;IACF,EAAE,OAAOF,OAAO;QACd,mEAAmE;QACnE,4CAA4C;QAC5C,IAAIA,iBAAiBb,qBAAqB;YACxC,MAAMa;QACR;IACA,0EAA0E;IAC1E,oCAAoC;IACtC;IAEA,OAAO;QACLgC,OAAO;QACPP;QACAD;QACAE,cAAc;QACdE,WAAW;IACb;AACF;AAEA;;;;;;;;;;;;;CAaC,GACD,OAAO,SAASD,iBAAiBd,QAAgB,EAAEC,aAAqB;IACtE,gDAAgD;IAChD,MAAMmB,iBAAiBjD,KAAKqB,SAAS,CAACrB,KAAKuC,OAAO,CAACV;IACnD,MAAMQ,iBAAiBrC,KAAKqB,SAAS,CAACrB,KAAKuC,OAAO,CAACT;IAEnD,cAAc;IACd,IAAImB,mBAAmBZ,gBAAgB;QACrC,OAAO;IACT;IAEA,+EAA+E;IAC/E,MAAMa,WAAWlD,KAAKkD,QAAQ,CAACb,gBAAgBY;IAE/C,qEAAqE;IACrE,IAAIC,SAASd,UAAU,CAAC,OAAO;QAC7B,OAAO;IACT;IAEA,gEAAgE;IAChE,IAAIpC,KAAKmD,UAAU,CAACD,WAAW;QAC7B,OAAO;IACT;IAEA,OAAO;AACT;AAEA;;;;;;;;;;;;;;;;CAgBC,GACD,OAAO,SAASE,cACdC,SAAmB,EACnBvB,aAAqB;IAErB,MAAMwB,UAAU,IAAIC;IAEpB,KAAK,MAAM1B,YAAYwB,UAAW;QAChC,IAAI;YACF,MAAMG,SAAS5B,aAAaC,UAAUC;YACtCwB,QAAQG,GAAG,CAAC5B,UAAU2B;QACxB,EAAE,OAAOxC,OAAO;YACd,IAAIA,iBAAiBb,qBAAqB;gBACxCmD,QAAQG,GAAG,CAAC5B,UAAU;oBACpBmB,OAAO;oBACPP,cAAc;oBACdD,gBAAgB;oBAChBE,cAAc;oBACdE,WAAW;oBACX1B,QAAQF,MAAMX,OAAO,EAAEa;gBACzB;YACF,OAAO;gBACL,MAAMF;YACR;QACF;IACF;IAEA,OAAOsC;AACT;AAEA;;;;;;;;;;;;;;CAcC,GACD,OAAO,SAASI,YAAY7B,QAAgB,EAAEC,aAAqB;IACjE,MAAM0B,SAAS5B,aAAaC,UAAUC;IACtC,OAAO0B,OAAOf,YAAY;AAC5B;AAEA;;;;;;;;;;;;;;;CAeC,GACD,OAAO,SAASkB,WAAW9B,QAAgB,EAAEC,aAAqB;IAChE,IAAI;QACFF,aAAaC,UAAUC;QACvB,OAAO;IACT,EAAE,OAAM;QACN,OAAO;IACT;AACF;AAEA;;;;;;;;;;;;;;CAcC,GACD,OAAO,SAAS8B,uBACd/B,QAAgB,EAChBC,aAAqB;IAErB,IAAI;QACFF,aAAaC,UAAUC;QACvB,OAAOJ;IACT,EAAE,OAAOV,OAAO;QACd,IAAIA,iBAAiBb,qBAAqB;YACxC,OAAOa;QACT;QACA,MAAMA;IACR;AACF;AAEA;;;;;;;;;;;;;;;;CAgBC,GACD,OAAO,SAAS6C,kBACd/B,aAAqB,EACrBgC,OAGC;IAED,MAAMC,YAAsB,EAAE;IAC9B,MAAMC,WAAWF,SAASE,YAAYC;IACtC,MAAMC,SAASJ,SAASI,UAAW,CAAA,IAAM,IAAG;IAE5C,SAASC,cAAcC,GAAW,EAAEC,eAAuB,CAAC;QAC1D,IAAIA,eAAeL,UAAU;YAC3B;QACF;QAEA,IAAI;YACF,MAAMM,UAAUrE,GAAGsE,WAAW,CAACH,KAAK;gBAAEI,eAAe;YAAK;YAE1D,KAAK,MAAMC,SAASH,QAAS;gBAC3B,MAAMI,WAAW1E,KAAK2E,IAAI,CAACP,KAAKK,MAAMnE,IAAI;gBAC1C,MAAMsE,eAAe5E,KAAKkD,QAAQ,CAACpB,eAAe4C;gBAElD,yCAAyC;gBACzC,IAAI,CAAC/B,iBAAiB+B,UAAU5C,gBAAgB;oBAC9C;gBACF;gBAEA,eAAe;gBACf,IAAI,CAACoC,OAAOU,eAAe;oBACzB;gBACF;gBAEAb,UAAUc,IAAI,CAACD;gBAEf,+BAA+B;gBAC/B,IAAIH,MAAMK,WAAW,MAAM,CAACL,MAAM1B,cAAc,IAAI;oBAClDoB,cAAcO,UAAUL,eAAe;gBACzC;YACF;QACF,EAAE,OAAOrD,OAAO;QACd,oEAAoE;QACtE;IACF;IAEAmD,cAAcrC;IACd,OAAOiC;AACT"}
1
+ {"version":3,"sources":["../../src/lib/path-validator.ts"],"sourcesContent":["/**\r\n * Path Validator - Security Utility for Safe File Operations\r\n *\r\n * Provides robust path sanitization and validation to prevent path traversal attacks (CVSS 7.0+).\r\n * Enforces strict rules on file access with protection against encoding attacks:\r\n *\r\n * CRITICAL FIXES (CVSS 7.0+):\r\n * - Iterative URL decoding prevents double-encoding bypasses (%252e%252e%252f → ../)\r\n * - Unicode normalization (NFC) prevents overlong UTF-8 bypasses (%c0%ae → .)\r\n * - Null byte detection prevents null injection attacks\r\n * - All decoding performed BEFORE path normalization to prevent layered attacks\r\n * - Encoding attack detection with security logging\r\n *\r\n * Base Security Controls:\r\n * - Path normalization to resolve \"..\" and \".\" sequences\r\n * - Validation that resolved paths stay within allowed directories\r\n * - Detection and rejection of symlinks\r\n * - Rejection of absolute paths outside allowed directories\r\n * - Prevention of home directory access (\"~\")\r\n *\r\n * @module path-validator\r\n * @version 2.0.0 (SECURITY CRITICAL)\r\n */\r\n\r\nimport * as path from 'path';\r\nimport * as fs from 'fs';\r\nimport { StandardError } from './errors';\r\n\r\n/**\r\n * Path validation error - thrown when a path violates security constraints\r\n */\r\nexport class PathValidationError extends StandardError {\r\n constructor(message: string, context?: Record<string, unknown>) {\r\n super('PATH_VALIDATION_ERROR', message, context);\r\n this.name = 'PathValidationError';\r\n }\r\n}\r\n\r\n/**\r\n * Security encoding attack detection\r\n */\r\nexport interface EncodingAttackDetection {\r\n detected: boolean;\r\n type?: 'double_encoding' | 'unicode_encoding' | 'mixed_encoding';\r\n originalInput: string;\r\n decodedOutput: string;\r\n iterationsRequired: number;\r\n}\r\n\r\n/**\r\n * Path validation result with detailed information\r\n */\r\nexport interface PathValidationResult {\r\n valid: boolean;\r\n resolvedPath: string;\r\n normalizedPath: string;\r\n isWithinBase: boolean;\r\n isSymlink: boolean;\r\n reason?: string;\r\n}\r\n\r\n/**\r\n * Safely decode a path with protection against encoding attacks\r\n *\r\n * Performs iterative URL decoding and Unicode normalization to prevent:\r\n * - Double encoding bypass (e.g., %252e%252e%252f → %2e%2e%2f → ../)\r\n * - Overlong UTF-8 encoding (e.g., %c0%ae%c0%ae/ → ../)\r\n * - Mixed encoding attacks\r\n *\r\n * @param inputPath - The potentially encoded path\r\n * @returns Decoded path and attack detection info\r\n * @throws PathValidationError if encoding attack detected\r\n *\r\n * @example\r\n * const { decoded, encoding } = decodePathSafely('%252e%252e%252f');\r\n * // Detects double-encoding attempt\r\n */\r\nfunction decodePathSafely(inputPath: string): {\r\n decoded: string;\r\n encoding: EncodingAttackDetection;\r\n} {\r\n let decoded = inputPath;\r\n let previous = '';\r\n let iterations = 0;\r\n const MAX_ITERATIONS = 5;\r\n const originalInput = inputPath;\r\n let invalidEncodingDetected = false;\r\n\r\n // Iteratively decode URL-encoded characters until stable\r\n // This prevents bypass attacks using multiple encoding layers\r\n while (decoded !== previous && iterations < MAX_ITERATIONS) {\r\n previous = decoded;\r\n try {\r\n decoded = decodeURIComponent(decoded);\r\n } catch (error) {\r\n // Invalid URL encoding can be legitimate (e.g., file100%, %PATH%)\r\n // These are literal percent signs in filenames, not attacks\r\n // Only flag as attack if it's malformed UTF-8 (overlong encoding)\r\n const errorMessage = (error as Error).message || '';\r\n\r\n // Malformed UTF-8 sequences like %c0%ae are attacks\r\n if (decoded.match(/%[cC][0-9a-fA-F]/) || decoded.match(/%[eE][0-9a-fA-F]/)) {\r\n invalidEncodingDetected = true;\r\n break;\r\n }\r\n\r\n // Otherwise, it's just a literal % in the filename - OK\r\n // Keep the path as-is and stop decoding\r\n break;\r\n }\r\n iterations++;\r\n }\r\n\r\n // Check if we hit max iterations (indicates potential encoding attack)\r\n if (iterations >= MAX_ITERATIONS && decoded !== previous) {\r\n throw new PathValidationError(\r\n 'Path validation failed: excessive encoding layers detected',\r\n {\r\n originalInput,\r\n decodedOutput: decoded,\r\n iterations,\r\n reason: 'ENCODING_ATTACK_DETECTED',\r\n }\r\n );\r\n }\r\n\r\n // Invalid encoding like malformed UTF-8 is itself an attack indicator\r\n if (invalidEncodingDetected) {\r\n throw new PathValidationError(\r\n 'Path validation failed: invalid encoding detected (possible encoding attack)',\r\n {\r\n originalInput,\r\n decodedOutput: decoded,\r\n iterations,\r\n reason: 'INVALID_ENCODING_DETECTED',\r\n }\r\n );\r\n }\r\n\r\n // Detect double+ encoding attacks (3+ iterations = double-encoded or more)\r\n // Single-level URL encoding requires 2 iterations: decode + stability check\r\n // Example: subdir%2ffile.txt → subdir/file.txt → stable (2 iterations, legitimate)\r\n // Example: %252e%252e%252f → %2e%2e%2f → ../ (3 iterations, attack)\r\n const encodingAttackDetected = iterations > 2;\r\n\r\n // Unicode normalization to handle overlong UTF-8 sequences\r\n // e.g., %c0%ae (%c0%ae = UTF-8 overlong encoding for \".\")\r\n let normalized = decoded;\r\n try {\r\n normalized = decoded.normalize('NFC');\r\n } catch (error) {\r\n // Some paths may not be valid Unicode, continue with non-normalized version\r\n }\r\n\r\n // Check for null bytes (another common encoding attack vector)\r\n if (normalized.includes('\\0')) {\r\n throw new PathValidationError(\r\n 'Path validation failed: null byte injection detected',\r\n {\r\n originalInput,\r\n decodedOutput: normalized,\r\n reason: 'NULL_BYTE_INJECTION',\r\n }\r\n );\r\n }\r\n\r\n return {\r\n decoded: normalized,\r\n encoding: {\r\n detected: encodingAttackDetected,\r\n type: encodingAttackDetected ? 'double_encoding' : undefined,\r\n originalInput,\r\n decodedOutput: normalized,\r\n iterationsRequired: iterations,\r\n },\r\n };\r\n}\r\n\r\n/**\r\n * Validate a file path to prevent directory traversal attacks\r\n *\r\n * Security checks performed (in order):\r\n * 1. CRITICAL: Iteratively decode URL encoding to handle %252e%252e%252f and similar bypasses\r\n * 2. CRITICAL: Normalize Unicode (NFC) to handle overlong UTF-8 like %c0%ae%c0%ae/\r\n * 3. CRITICAL: Detect null bytes and excessive encoding layers\r\n * 4. Check for home directory expansion (\"~\") on DECODED path\r\n * 5. Normalize path to resolve \"..\" and \".\"\r\n * 6. Verify all suspicious patterns are eliminated\r\n * 7. Check if path is within base directory\r\n * 8. Reject symlinks to prevent symlink attacks\r\n * 9. Log any encoding attacks detected for security monitoring\r\n *\r\n * @param filePath - The file path to validate (may be encoded)\r\n * @param baseDirectory - The base directory that file must reside within\r\n * @returns PathValidationResult with validation details\r\n * @throws PathValidationError if path validation fails or encoding attack detected\r\n *\r\n * @example\r\n * const result = validatePath('docs/FEATURE.md', './.claude/skills');\r\n * if (!result.valid) {\r\n * throw result; // Safe to throw, contains all context\r\n * }\r\n * // Use result.resolvedPath\r\n *\r\n * @security\r\n * Designed to prevent:\r\n * - Double-encoding bypasses: %252e%252e%252f\r\n * - Overlong UTF-8: %c0%ae%c0%ae/\r\n * - Mixed encoding: URL + Unicode combinations\r\n * - Null byte injection: file.txt%00.jpg\r\n * - Traditional path traversal: ../../../etc/passwd\r\n */\r\nexport function validatePath(filePath: string, baseDirectory: string): PathValidationResult {\r\n // SECURITY FIX: Decode all encoding layers first before any path normalization\r\n // This prevents double-encoding bypasses like %252e%252e%252f\r\n const { decoded: decodedPath, encoding: pathEncoding } = decodePathSafely(filePath);\r\n const { decoded: decodedBase } = decodePathSafely(baseDirectory);\r\n\r\n // Log encoding attacks for security monitoring\r\n if (pathEncoding.detected) {\r\n // In production, this should trigger security alerts\r\n console.warn('Security: Encoding attack detected in path input', {\r\n originalInput: pathEncoding.originalInput,\r\n decodedOutput: pathEncoding.decodedOutput,\r\n iterationsRequired: pathEncoding.iterationsRequired,\r\n });\r\n }\r\n\r\n // Check for home directory expansion attempts on DECODED path\r\n if (decodedPath.startsWith('~') || decodedPath.includes('/~') || decodedPath.includes('\\\\~')) {\r\n throw new PathValidationError(\r\n 'Path validation failed: home directory access denied',\r\n {\r\n filePath,\r\n decodedPath,\r\n baseDirectory,\r\n reason: 'HOME_DIRECTORY_ACCESS',\r\n }\r\n );\r\n }\r\n\r\n // Check for home directory expansion attempts in baseDirectory\r\n if (decodedBase.startsWith('~')) {\r\n throw new PathValidationError(\r\n 'Base directory validation failed: home directory access denied',\r\n {\r\n baseDirectory,\r\n decodedBase,\r\n reason: 'BASE_HOME_DIRECTORY_ACCESS',\r\n }\r\n );\r\n }\r\n\r\n // Normalize the base directory first\r\n const normalizedBase = path.normalize(decodedBase);\r\n const resolvedBase = path.resolve(normalizedBase);\r\n\r\n // Normalize and resolve the file path relative to base\r\n // NOW normalized on already-decoded path to prevent encoding bypasses\r\n const normalizedPath = path.normalize(decodedPath);\r\n\r\n // Check if path contains suspicious patterns after normalization\r\n if (normalizedPath.includes('..') || normalizedPath === '.' || normalizedPath.includes('/./')) {\r\n throw new PathValidationError(\r\n 'Path validation failed: path contains directory traversal patterns',\r\n {\r\n filePath,\r\n decodedPath,\r\n normalizedPath,\r\n baseDirectory,\r\n reason: 'TRAVERSAL_PATTERN_DETECTED',\r\n }\r\n );\r\n }\r\n\r\n // Resolve the path relative to base directory\r\n const resolvedPath = path.resolve(resolvedBase, normalizedPath);\r\n\r\n // Check if resolved path is within base directory\r\n const isWithinBase = isPathWithinBase(resolvedPath, resolvedBase);\r\n\r\n if (!isWithinBase) {\r\n throw new PathValidationError(\r\n 'Path validation failed: resolved path is outside allowed directory',\r\n {\r\n filePath,\r\n resolvedPath,\r\n baseDirectory: resolvedBase,\r\n reason: 'PATH_OUTSIDE_BASE',\r\n }\r\n );\r\n }\r\n\r\n // Check for symlinks (prevents symlink attacks)\r\n let isSymlink = false;\r\n try {\r\n const stats = fs.lstatSync(resolvedPath);\r\n isSymlink = stats.isSymbolicLink();\r\n\r\n if (isSymlink) {\r\n throw new PathValidationError(\r\n 'Path validation failed: symbolic links are not allowed',\r\n {\r\n filePath,\r\n resolvedPath,\r\n reason: 'SYMLINK_NOT_ALLOWED',\r\n }\r\n );\r\n }\r\n } catch (error) {\r\n // File doesn't exist yet (expected for validation before creation)\r\n // or it's a symlink that was rejected above\r\n if (error instanceof PathValidationError) {\r\n throw error;\r\n }\r\n // Other errors (permission denied, etc.) are not path validation failures\r\n // The file validation happens later\r\n }\r\n\r\n return {\r\n valid: true,\r\n resolvedPath,\r\n normalizedPath,\r\n isWithinBase: true,\r\n isSymlink: false,\r\n };\r\n}\r\n\r\n/**\r\n * Check if a path is within a base directory\r\n *\r\n * Uses path resolution and string comparison to ensure the resolved path\r\n * is actually within the base directory (not just sharing a prefix).\r\n *\r\n * @param filePath - The path to check\r\n * @param baseDirectory - The base directory\r\n * @returns True if filePath is within baseDirectory\r\n *\r\n * @example\r\n * isPathWithinBase('/home/user/project/src/file.ts', '/home/user/project') // true\r\n * isPathWithinBase('/home/user/project-evil/file.ts', '/home/user/project') // false\r\n */\r\nexport function isPathWithinBase(filePath: string, baseDirectory: string): boolean {\r\n // Ensure both paths are normalized and absolute\r\n const normalizedFile = path.normalize(path.resolve(filePath));\r\n const normalizedBase = path.normalize(path.resolve(baseDirectory));\r\n\r\n // Exact match\r\n if (normalizedFile === normalizedBase) {\r\n return true;\r\n }\r\n\r\n // Check if file is within base (use path.relative to ensure it's not going up)\r\n const relative = path.relative(normalizedBase, normalizedFile);\r\n\r\n // If relative path starts with \"..\", it's outside the base directory\r\n if (relative.startsWith('..')) {\r\n return false;\r\n }\r\n\r\n // If relative path is absolute, it's outside the base directory\r\n if (path.isAbsolute(relative)) {\r\n return false;\r\n }\r\n\r\n return true;\r\n}\r\n\r\n/**\r\n * Validate multiple paths within the same base directory\r\n *\r\n * Efficiently validates multiple paths, returning results for each.\r\n *\r\n * @param filePaths - Array of file paths to validate\r\n * @param baseDirectory - The base directory that all files must reside within\r\n * @returns Map of file path to validation result\r\n *\r\n * @example\r\n * const results = validatePaths(['docs/SKILL.md', 'src/index.ts'], './.claude/skills');\r\n * results.forEach((result, filePath) => {\r\n * if (!result.valid) {\r\n * console.error(`Invalid path: ${filePath}`, result.reason);\r\n * }\r\n * });\r\n */\r\nexport function validatePaths(\r\n filePaths: string[],\r\n baseDirectory: string\r\n): Map<string, PathValidationResult> {\r\n const results = new Map<string, PathValidationResult>();\r\n\r\n for (const filePath of filePaths) {\r\n try {\r\n const result = validatePath(filePath, baseDirectory);\r\n results.set(filePath, result);\r\n } catch (error) {\r\n if (error instanceof PathValidationError) {\r\n results.set(filePath, {\r\n valid: false,\r\n resolvedPath: '',\r\n normalizedPath: '',\r\n isWithinBase: false,\r\n isSymlink: false,\r\n reason: error.context?.reason as string | undefined,\r\n });\r\n } else {\r\n throw error;\r\n }\r\n }\r\n }\r\n\r\n return results;\r\n}\r\n\r\n/**\r\n * Get the safe path for a file (or throw if validation fails)\r\n *\r\n * Convenience function that validates and returns the resolved path,\r\n * or throws an error if validation fails.\r\n *\r\n * @param filePath - The file path to validate\r\n * @param baseDirectory - The base directory that file must reside within\r\n * @returns The resolved, validated absolute path\r\n * @throws PathValidationError if path validation fails\r\n *\r\n * @example\r\n * const safePath = getSafePath('docs/SKILL.md', './.claude/skills');\r\n * fs.readFileSync(safePath); // Safe to use\r\n */\r\nexport function getSafePath(filePath: string, baseDirectory: string): string {\r\n const result = validatePath(filePath, baseDirectory);\r\n return result.resolvedPath;\r\n}\r\n\r\n/**\r\n * Check if a path is considered safe for operations\r\n *\r\n * This is a non-throwing version of validatePath for conditional logic.\r\n *\r\n * @param filePath - The file path to check\r\n * @param baseDirectory - The base directory\r\n * @returns True if path is safe, false otherwise\r\n *\r\n * @example\r\n * if (isPathSafe(userInput, './.claude/skills')) {\r\n * // Process the file\r\n * } else {\r\n * // Reject the request\r\n * }\r\n */\r\nexport function isPathSafe(filePath: string, baseDirectory: string): boolean {\r\n try {\r\n validatePath(filePath, baseDirectory);\r\n return true;\r\n } catch {\r\n return false;\r\n }\r\n}\r\n\r\n/**\r\n * Get validation error details for a path (if invalid)\r\n *\r\n * Useful for logging and diagnostics.\r\n *\r\n * @param filePath - The file path to validate\r\n * @param baseDirectory - The base directory\r\n * @returns Error with details, or undefined if path is valid\r\n *\r\n * @example\r\n * const error = getPathValidationError('../../etc/passwd', './.claude/skills');\r\n * if (error) {\r\n * logger.error(error.message, error.context);\r\n * }\r\n */\r\nexport function getPathValidationError(\r\n filePath: string,\r\n baseDirectory: string\r\n): PathValidationError | undefined {\r\n try {\r\n validatePath(filePath, baseDirectory);\r\n return undefined;\r\n } catch (error) {\r\n if (error instanceof PathValidationError) {\r\n return error;\r\n }\r\n throw error;\r\n }\r\n}\r\n\r\n/**\r\n * List allowed files within a base directory (safely)\r\n *\r\n * Recursively lists all files within base directory, validating\r\n * each path to ensure it's within bounds.\r\n *\r\n * @param baseDirectory - The base directory to scan\r\n * @param options - Options for listing (maxDepth, filter)\r\n * @returns Array of validated, safe paths relative to baseDirectory\r\n *\r\n * @example\r\n * const files = safeListDirectory('./.claude/skills');\r\n * files.forEach(file => {\r\n * // All paths are guaranteed safe\r\n * console.log(file);\r\n * });\r\n */\r\nexport function safeListDirectory(\r\n baseDirectory: string,\r\n options?: {\r\n maxDepth?: number;\r\n filter?: (path: string) => boolean;\r\n }\r\n): string[] {\r\n const safeFiles: string[] = [];\r\n const maxDepth = options?.maxDepth ?? Infinity;\r\n const filter = options?.filter ?? (() => true);\r\n\r\n function walkDirectory(dir: string, currentDepth: number = 0): void {\r\n if (currentDepth > maxDepth) {\r\n return;\r\n }\r\n\r\n try {\r\n const entries = fs.readdirSync(dir, { withFileTypes: true });\r\n\r\n for (const entry of entries) {\r\n const fullPath = path.join(dir, entry.name);\r\n const relativePath = path.relative(baseDirectory, fullPath);\r\n\r\n // Validate the path is still within base\r\n if (!isPathWithinBase(fullPath, baseDirectory)) {\r\n continue;\r\n }\r\n\r\n // Apply filter\r\n if (!filter(relativePath)) {\r\n continue;\r\n }\r\n\r\n safeFiles.push(relativePath);\r\n\r\n // Recursively walk directories\r\n if (entry.isDirectory() && !entry.isSymbolicLink()) {\r\n walkDirectory(fullPath, currentDepth + 1);\r\n }\r\n }\r\n } catch (error) {\r\n // Silently skip directories we can't read (permission denied, etc.)\r\n }\r\n }\r\n\r\n walkDirectory(baseDirectory);\r\n return safeFiles;\r\n}\r\n"],"names":["path","fs","StandardError","PathValidationError","message","context","name","decodePathSafely","inputPath","decoded","previous","iterations","MAX_ITERATIONS","originalInput","invalidEncodingDetected","decodeURIComponent","error","errorMessage","match","decodedOutput","reason","encodingAttackDetected","normalized","normalize","includes","encoding","detected","type","undefined","iterationsRequired","validatePath","filePath","baseDirectory","decodedPath","pathEncoding","decodedBase","console","warn","startsWith","normalizedBase","resolvedBase","resolve","normalizedPath","resolvedPath","isWithinBase","isPathWithinBase","isSymlink","stats","lstatSync","isSymbolicLink","valid","normalizedFile","relative","isAbsolute","validatePaths","filePaths","results","Map","result","set","getSafePath","isPathSafe","getPathValidationError","safeListDirectory","options","safeFiles","maxDepth","Infinity","filter","walkDirectory","dir","currentDepth","entries","readdirSync","withFileTypes","entry","fullPath","join","relativePath","push","isDirectory"],"mappings":"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"}
@@ -33,7 +33,7 @@
33
33
  * }
34
34
  */ import { v4 as uuidv4 } from 'uuid';
35
35
  import { createLogger } from './logging.js';
36
- import { createError, ErrorCode, isRetryableError } from './errors.js';
36
+ import { createError, ErrorCode, isRetryableError, StandardError } from './errors.js';
37
37
  import { withRetry } from './retry.js';
38
38
  import { MessageDeduplicator } from './message-deduplicator.js';
39
39
  const logger = createLogger('redis-queue-manager');
@@ -137,6 +137,10 @@ const DEFAULT_DEQUEUE_OPTIONS = {
137
137
  }
138
138
  return message.id;
139
139
  } catch (error) {
140
+ // Re-throw duplicate errors without wrapping
141
+ if (error instanceof StandardError && error.code === ErrorCode.DB_DUPLICATE_KEY) {
142
+ throw error;
143
+ }
140
144
  logger.error('Failed to enqueue message', error instanceof Error ? error : new Error(String(error)), {
141
145
  queue
142
146
  });