claude-flow-novice 2.15.11 → 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 (636) 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/coordinators/cfn-frontend-coordinator.md +1 -0
  150. package/claude-assets/agents/cfn-dev-team/coordinators/consensus-builder.md +1 -0
  151. package/claude-assets/agents/cfn-dev-team/coordinators/handoff-coordinator.md +1 -0
  152. package/claude-assets/agents/cfn-dev-team/coordinators/multi-sprint-coordinator.md +1 -0
  153. package/claude-assets/agents/cfn-dev-team/dev-ops/devops-engineer.md +10 -0
  154. package/claude-assets/agents/cfn-dev-team/dev-ops/docker-specialist.md +56 -33
  155. package/claude-assets/agents/cfn-dev-team/dev-ops/kubernetes-specialist.md +46 -36
  156. package/claude-assets/agents/cfn-dev-team/dev-ops/monitoring-specialist.md +9 -0
  157. package/claude-assets/agents/cfn-dev-team/developers/api-gateway-specialist.md +17 -17
  158. package/claude-assets/agents/cfn-dev-team/developers/backend-developer.md +40 -58
  159. package/claude-assets/agents/cfn-dev-team/developers/data/data-engineer.md +18 -20
  160. package/claude-assets/agents/cfn-dev-team/developers/database/database-architect.md +19 -28
  161. package/claude-assets/agents/cfn-dev-team/developers/frontend/mobile-dev.md +15 -19
  162. package/claude-assets/agents/cfn-dev-team/developers/frontend/react-frontend-engineer.md +15 -10
  163. package/claude-assets/agents/cfn-dev-team/developers/frontend/typescript-specialist.md +15 -10
  164. package/claude-assets/agents/cfn-dev-team/developers/frontend/ui-designer.md +15 -25
  165. package/claude-assets/agents/cfn-dev-team/developers/graphql-specialist.md +17 -21
  166. package/claude-assets/agents/cfn-dev-team/developers/rust-developer.md +17 -21
  167. package/claude-assets/agents/cfn-dev-team/product-owners/product-owner.md +1 -5
  168. package/claude-assets/agents/cfn-dev-team/reviewers/code-reviewer.md +20 -51
  169. package/claude-assets/agents/cfn-dev-team/reviewers/quality/code-quality-validator.md +22 -71
  170. package/claude-assets/agents/cfn-dev-team/reviewers/quality/perf-analyzer.md +21 -64
  171. package/claude-assets/agents/cfn-dev-team/reviewers/quality/performance-benchmarker.md +22 -67
  172. package/claude-assets/agents/cfn-dev-team/reviewers/quality/security-specialist.md +23 -67
  173. package/claude-assets/agents/cfn-dev-team/testers/api-testing-specialist.md +7 -35
  174. package/claude-assets/agents/cfn-dev-team/testers/chaos-engineering-specialist.md +8 -37
  175. package/claude-assets/agents/cfn-dev-team/testers/contract-tester.md +16 -54
  176. package/claude-assets/agents/cfn-dev-team/testers/integration-tester.md +17 -55
  177. package/claude-assets/agents/cfn-dev-team/testers/interaction-tester.md +9 -37
  178. package/claude-assets/agents/cfn-dev-team/testers/load-testing-specialist.md +17 -55
  179. package/claude-assets/agents/cfn-dev-team/testers/mutation-testing-specialist.md +17 -48
  180. package/claude-assets/agents/cfn-dev-team/testers/playwright-tester.md +8 -37
  181. package/claude-assets/agents/cfn-dev-team/testers/tester.md +7 -27
  182. package/claude-assets/agents/cfn-dev-team/utility/analyst.md +12 -28
  183. package/claude-assets/agents/cfn-dev-team/utility/code-booster.md +13 -13
  184. package/claude-assets/agents/cfn-dev-team/utility/context-curator.md +7 -2
  185. package/claude-assets/agents/cfn-dev-team/utility/epic-creator.md +5 -10
  186. package/claude-assets/agents/cfn-dev-team/utility/memory-leak-specialist.md +120 -714
  187. package/claude-assets/agents/cfn-dev-team/utility/researcher.md +12 -21
  188. package/claude-assets/agents/cfn-dev-team/utility/z-ai-specialist.md +146 -572
  189. package/claude-assets/agents/custom/cfn-docker-expert.md +102 -0
  190. package/claude-assets/agents/custom/cfn-loops-cli-expert.md +129 -0
  191. package/claude-assets/cfn-extras/agents/cfn-v3-coordinator.md +517 -0
  192. package/claude-assets/commands/cfn-loop-cli.md +158 -464
  193. package/claude-assets/commands/cfn-loop-trigger.md +114 -0
  194. package/claude-assets/hooks/SKILL.md +518 -0
  195. package/claude-assets/hooks/SKILL.md.backup +471 -0
  196. package/claude-assets/hooks/cfn-invoke-post-edit-ts.sh +100 -0
  197. package/claude-assets/hooks/cfn-invoke-post-edit-ts.sh.backup +78 -0
  198. package/claude-assets/hooks/cfn-invoke-post-edit.sh +22 -0
  199. package/claude-assets/hooks/cfn-invoke-post-edit.sh.backup +87 -0
  200. package/claude-assets/hooks/cfn-invoke-pre-edit-ts.sh +116 -0
  201. package/claude-assets/hooks/cfn-invoke-pre-edit-ts.sh.backup +94 -0
  202. package/claude-assets/hooks/cfn-invoke-pre-edit.sh +22 -0
  203. package/claude-assets/hooks/cfn-invoke-pre-edit.sh.backup +88 -0
  204. package/claude-assets/skills/cfn-agent-selection-with-fallback/DELIVERABLES.md +409 -0
  205. package/claude-assets/skills/cfn-agent-selection-with-fallback/IMPLEMENTATION_SUMMARY.md +396 -0
  206. package/claude-assets/skills/cfn-agent-selection-with-fallback/INTEGRATION_GUIDE.md +308 -0
  207. package/claude-assets/skills/cfn-agent-selection-with-fallback/QUICK_REFERENCE.md +239 -0
  208. package/claude-assets/skills/cfn-agent-selection-with-fallback/SKILL.md +107 -1
  209. package/claude-assets/skills/cfn-agent-selection-with-fallback/SKILL.md.backup +302 -0
  210. package/claude-assets/skills/cfn-agent-selection-with-fallback/TYPESCRIPT_MIGRATION.md +295 -0
  211. package/claude-assets/skills/cfn-agent-selection-with-fallback/dist/agent-selector.cjs +297 -0
  212. package/claude-assets/skills/cfn-agent-selection-with-fallback/dist/agent-selector.js +297 -0
  213. package/claude-assets/skills/cfn-agent-selection-with-fallback/dist/cli.cjs +96 -0
  214. package/claude-assets/skills/cfn-agent-selection-with-fallback/dist/cli.js +96 -0
  215. package/claude-assets/skills/cfn-agent-selection-with-fallback/select-agents-ts.sh +45 -0
  216. package/claude-assets/skills/cfn-agent-selection-with-fallback/select-agents-ts.sh.backup +23 -0
  217. package/claude-assets/skills/cfn-agent-selection-with-fallback/select-agents.sh +22 -0
  218. package/claude-assets/skills/cfn-agent-selection-with-fallback/select-agents.sh.backup +173 -0
  219. package/claude-assets/skills/cfn-agent-selection-with-fallback/src/agent-selector.test.ts +357 -0
  220. package/claude-assets/skills/cfn-agent-selection-with-fallback/src/agent-selector.ts +350 -0
  221. package/claude-assets/skills/cfn-agent-selection-with-fallback/src/cli.ts +74 -0
  222. package/claude-assets/skills/cfn-agent-selection-with-fallback/task-classifier.sh +22 -0
  223. package/claude-assets/skills/cfn-agent-selection-with-fallback/task-classifier.sh.backup +71 -0
  224. package/claude-assets/skills/cfn-agent-selection-with-fallback/tsconfig.json +18 -0
  225. package/claude-assets/skills/cfn-agent-spawning/SKILL.md +48 -1
  226. package/claude-assets/skills/cfn-agent-spawning/SKILL.md.backup +135 -0
  227. package/claude-assets/skills/cfn-agent-spawning/TYPESCRIPT_MIGRATION.md +567 -0
  228. package/claude-assets/skills/cfn-agent-spawning/check-dependencies.sh +22 -0
  229. package/claude-assets/skills/cfn-agent-spawning/check-dependencies.sh.backup +30 -0
  230. package/claude-assets/skills/cfn-agent-spawning/get-agent-provider-env.sh +22 -0
  231. package/claude-assets/skills/cfn-agent-spawning/get-agent-provider-env.sh.backup +127 -0
  232. package/claude-assets/skills/cfn-agent-spawning/parse-agent-provider.sh +22 -0
  233. package/claude-assets/skills/cfn-agent-spawning/parse-agent-provider.sh.backup +59 -0
  234. package/claude-assets/skills/cfn-agent-spawning/spawn-agent-wrapper.sh +63 -0
  235. package/claude-assets/skills/cfn-agent-spawning/spawn-agent-wrapper.sh.backup +41 -0
  236. package/claude-assets/skills/cfn-agent-spawning/spawn-agent.sh +26 -1
  237. package/claude-assets/skills/cfn-agent-spawning/spawn-templates.sh +22 -0
  238. package/claude-assets/skills/cfn-agent-spawning/spawn-templates.sh.backup +613 -0
  239. package/claude-assets/skills/cfn-agent-spawning/spawn-worker.sh +22 -0
  240. package/claude-assets/skills/cfn-agent-spawning/spawn-worker.sh.backup +176 -0
  241. package/claude-assets/skills/cfn-coordination/agent-completion.sh.backup +36 -0
  242. package/claude-assets/skills/cfn-coordination/coordination-signal.sh.backup +36 -0
  243. package/claude-assets/skills/cfn-coordination/coordination-wait.sh.backup +36 -0
  244. package/claude-assets/skills/cfn-dependency-ingestion/README.md +101 -0
  245. package/claude-assets/skills/cfn-dependency-ingestion/SKILL.md +369 -0
  246. package/claude-assets/skills/cfn-dependency-ingestion/build.sh +23 -0
  247. package/claude-assets/skills/cfn-dependency-ingestion/dist/ingest-dependencies.js +478 -0
  248. package/claude-assets/skills/cfn-dependency-ingestion/ingest-dependencies.sh +295 -0
  249. package/claude-assets/skills/cfn-dependency-ingestion/src/ingest-dependencies.ts +563 -0
  250. package/claude-assets/skills/cfn-loop-orchestration/.backups/unknown/1763619700_33aff4a69b99159e4e849107ebc4d09f/metadata.json +8 -0
  251. package/claude-assets/skills/cfn-loop-orchestration/.backups/unknown/1763619700_33aff4a69b99159e4e849107ebc4d09f/original +271 -0
  252. package/claude-assets/skills/cfn-loop-orchestration/.backups/unknown/1763619700_33aff4a69b99159e4e849107ebc4d09f/revert.sh +7 -0
  253. package/claude-assets/skills/cfn-loop-orchestration/.backups/unknown/1763671642_06496e8c399a79db08167cc00ed4b31e/metadata.json +8 -0
  254. package/claude-assets/skills/cfn-loop-orchestration/.backups/unknown/1763671642_06496e8c399a79db08167cc00ed4b31e/original +325 -0
  255. package/claude-assets/skills/cfn-loop-orchestration/.backups/unknown/1763671642_06496e8c399a79db08167cc00ed4b31e/revert.sh +7 -0
  256. package/claude-assets/skills/cfn-loop-orchestration/CLI_IMPLEMENTATION_SUMMARY.md +330 -0
  257. package/claude-assets/skills/cfn-loop-orchestration/CONFIGURATION_IMPROVEMENTS.md +318 -0
  258. package/claude-assets/skills/cfn-loop-orchestration/CONTEXT_LOOKUP_MIGRATION.md +308 -0
  259. package/claude-assets/skills/cfn-loop-orchestration/CONTEXT_LOOKUP_QUICK_START.md +378 -0
  260. package/claude-assets/skills/cfn-loop-orchestration/E2E_VALIDATION_REPORT.md +262 -0
  261. package/claude-assets/skills/cfn-loop-orchestration/IMPLEMENTATION_SUMMARY.md +319 -519
  262. package/claude-assets/skills/cfn-loop-orchestration/NORTH_STAR_E2E_REPORT.md +299 -0
  263. package/claude-assets/skills/cfn-loop-orchestration/NORTH_STAR_EXECUTION_SUMMARY.md +403 -0
  264. package/claude-assets/skills/cfn-loop-orchestration/NORTH_STAR_INDEX.md +323 -0
  265. package/claude-assets/skills/cfn-loop-orchestration/SKILL.md +159 -48
  266. package/claude-assets/skills/cfn-loop-orchestration/SPAWN_AGENTS_IMPLEMENTATION.md +188 -0
  267. package/claude-assets/skills/cfn-loop-orchestration/TEST_COVERAGE_REPORT.md +335 -0
  268. package/claude-assets/skills/cfn-loop-orchestration/TEST_COVERAGE_SUMMARY.md +456 -0
  269. package/claude-assets/skills/cfn-loop-orchestration/TYPESCRIPT_INTEGRATION_REPORT.md +709 -0
  270. package/claude-assets/skills/cfn-loop-orchestration/TYPESCRIPT_INTEGRATION_SUMMARY.md +257 -0
  271. package/claude-assets/skills/cfn-loop-orchestration/VALIDATION_REPORT.md +572 -0
  272. package/claude-assets/skills/cfn-loop-orchestration/VALIDATION_SUMMARY.txt +196 -0
  273. package/claude-assets/skills/cfn-loop-orchestration/VALIDATOR_MODULE_GUIDE.md +526 -0
  274. package/claude-assets/skills/cfn-loop-orchestration/archive/legacy-bash/README.md +167 -0
  275. package/claude-assets/skills/cfn-loop-orchestration/archive/legacy-bash/orchestrate-enhanced.sh +548 -0
  276. package/{.claude/skills/cfn-loop-orchestration → claude-assets/skills/cfn-loop-orchestration/archive/legacy-bash}/orchestrate-wrapper.sh +11 -1
  277. package/claude-assets/skills/cfn-loop-orchestration/archive/legacy-bash/orchestrate.sh +182 -0
  278. package/claude-assets/skills/cfn-loop-orchestration/e2e-validation-fixed.js +240 -0
  279. package/claude-assets/skills/cfn-loop-orchestration/e2e-validation.js +213 -0
  280. package/claude-assets/skills/cfn-loop-orchestration/package-lock.json +3 -0
  281. package/claude-assets/skills/cfn-loop-orchestration/package.json +4 -0
  282. package/claude-assets/skills/cfn-loop-orchestration/run-north-star-e2e.ts +210 -0
  283. package/claude-assets/skills/cfn-loop-orchestration/src/cli/orchestrator-cli.ts +396 -0
  284. package/claude-assets/skills/cfn-loop-orchestration/src/helpers/CONFIDENCE_AGGREGATOR.md +564 -0
  285. package/claude-assets/skills/cfn-loop-orchestration/src/helpers/CONFIDENCE_AGGREGATOR_QUICK_REF.md +241 -0
  286. package/claude-assets/skills/cfn-loop-orchestration/src/helpers/CONTEXT_INJECTOR_IMPLEMENTATION.md +375 -0
  287. package/claude-assets/skills/cfn-loop-orchestration/src/helpers/CONTEXT_INJECTOR_QUICK_REFERENCE.md +362 -0
  288. package/claude-assets/skills/cfn-loop-orchestration/src/helpers/CONTEXT_INJECTOR_README.md +307 -0
  289. package/claude-assets/skills/cfn-loop-orchestration/src/helpers/CONTEXT_INJECTOR_USAGE_GUIDE.md +508 -0
  290. package/claude-assets/skills/cfn-loop-orchestration/src/helpers/confidence-aggregator.ts +473 -0
  291. package/claude-assets/skills/cfn-loop-orchestration/src/helpers/consensus.ts +1 -1
  292. package/claude-assets/skills/cfn-loop-orchestration/src/helpers/context-injector.ts +349 -0
  293. package/claude-assets/skills/cfn-loop-orchestration/src/helpers/context-lookup.ts +486 -0
  294. package/claude-assets/skills/cfn-loop-orchestration/src/helpers/deliverable-verifier.ts +6 -2
  295. package/claude-assets/skills/cfn-loop-orchestration/src/helpers/gate-check.ts +1 -1
  296. package/claude-assets/skills/cfn-loop-orchestration/src/helpers/product-owner-decision.ts +316 -0
  297. package/claude-assets/skills/cfn-loop-orchestration/src/helpers/spawn-agents.ts +357 -0
  298. package/claude-assets/skills/cfn-loop-orchestration/src/helpers/validator.ts +276 -0
  299. package/claude-assets/skills/cfn-loop-orchestration/src/index.ts +2 -0
  300. package/claude-assets/skills/cfn-loop-orchestration/src/orchestrate.ts +743 -2
  301. package/claude-assets/skills/cfn-loop-orchestration/src/types.ts +56 -0
  302. package/claude-assets/skills/cfn-loop-orchestration/test-cli.sh +92 -0
  303. package/claude-assets/skills/cfn-loop-orchestration/test-typescript-integration.sh +442 -0
  304. package/claude-assets/skills/cfn-loop-orchestration/tests/agent-spawner.test.ts +124 -0
  305. package/claude-assets/skills/cfn-loop-orchestration/tests/confidence-aggregator.test.ts +604 -0
  306. package/claude-assets/skills/cfn-loop-orchestration/tests/context-injector.test.ts +561 -0
  307. package/claude-assets/skills/cfn-loop-orchestration/tests/context-lookup.test.ts +661 -0
  308. package/claude-assets/skills/cfn-loop-orchestration/tests/deliverable-verifier.test.ts +2 -2
  309. package/claude-assets/skills/cfn-loop-orchestration/tests/gate-check-edge-cases.test.ts +422 -0
  310. package/claude-assets/skills/cfn-loop-orchestration/tests/gate-checker.test.ts +276 -0
  311. package/claude-assets/skills/cfn-loop-orchestration/tests/logger.test.ts +291 -0
  312. package/claude-assets/skills/cfn-loop-orchestration/tests/north-star-e2e.test.ts +334 -0
  313. package/claude-assets/skills/cfn-loop-orchestration/tests/redis-coordinator.test.ts +321 -0
  314. package/claude-assets/skills/cfn-loop-orchestration/tests/spawn-agents.test.ts +284 -0
  315. package/claude-assets/skills/cfn-loop-orchestration/tests/validator.test.ts +643 -0
  316. package/claude-assets/skills/cfn-loop-output-processing/.eslintrc.json +33 -0
  317. package/claude-assets/skills/cfn-loop-output-processing/DELIVERY_SUMMARY.txt +462 -0
  318. package/claude-assets/skills/cfn-loop-output-processing/DEPRECATION_NOTICE.md +183 -0
  319. package/claude-assets/skills/cfn-loop-output-processing/EXAMPLES.md +609 -0
  320. package/claude-assets/skills/cfn-loop-output-processing/IMPLEMENTATION_SUMMARY.md +418 -0
  321. package/claude-assets/skills/cfn-loop-output-processing/INDEX.md +531 -0
  322. package/claude-assets/skills/cfn-loop-output-processing/MIGRATION.md +362 -0
  323. package/claude-assets/skills/cfn-loop-output-processing/README.md +114 -0
  324. package/claude-assets/skills/cfn-loop-output-processing/SKILL.md +633 -0
  325. package/{.claude/skills/cfn-docker-redis-coordination → claude-assets/skills/cfn-loop-output-processing}/jest.config.js +7 -15
  326. package/claude-assets/skills/cfn-loop-output-processing/package.json +50 -0
  327. package/claude-assets/skills/cfn-loop-output-processing/src/cli/process-loop2.ts +195 -0
  328. package/claude-assets/skills/cfn-loop-output-processing/src/cli/process-loop3.ts +157 -0
  329. package/claude-assets/skills/cfn-loop-output-processing/src/output-processor.ts +632 -0
  330. package/claude-assets/skills/cfn-loop-output-processing/tests/output-processor.test.ts +617 -0
  331. package/claude-assets/skills/{cfn-docker-redis-coordination → cfn-loop-output-processing}/tsconfig.json +16 -7
  332. package/claude-assets/skills/cfn-loop-validation/IMPLEMENTATION_SUMMARY.md +672 -0
  333. package/claude-assets/skills/cfn-loop-validation/INDEX.md +531 -0
  334. package/claude-assets/skills/cfn-loop-validation/README_TYPESCRIPT.md +454 -0
  335. package/claude-assets/skills/cfn-loop-validation/SKILL.md +48 -1
  336. package/claude-assets/skills/cfn-loop-validation/SKILL.md.backup +353 -0
  337. package/claude-assets/skills/cfn-loop-validation/SKILL_TYPESCRIPT.md +782 -0
  338. package/claude-assets/skills/cfn-loop-validation/VAPOR_DETECTION_EXAMPLES.md +598 -0
  339. package/claude-assets/skills/cfn-loop-validation/check-dependencies.sh +22 -0
  340. package/claude-assets/skills/cfn-loop-validation/check-dependencies.sh.backup +31 -0
  341. package/claude-assets/skills/cfn-loop-validation/detect-vapor.sh +59 -0
  342. package/claude-assets/skills/cfn-loop-validation/detect-vapor.sh.backup +37 -0
  343. package/claude-assets/skills/cfn-loop-validation/dist/.tsbuildinfo +1 -0
  344. package/claude-assets/skills/cfn-loop-validation/dist/cli/detect-vapor.d.ts +14 -0
  345. package/claude-assets/skills/cfn-loop-validation/dist/cli/detect-vapor.d.ts.map +1 -0
  346. package/claude-assets/skills/cfn-loop-validation/dist/cli/detect-vapor.js +185 -0
  347. package/claude-assets/skills/cfn-loop-validation/dist/cli/detect-vapor.js.map +1 -0
  348. package/claude-assets/skills/cfn-loop-validation/dist/cli/validate-deliverables.d.ts +14 -0
  349. package/claude-assets/skills/cfn-loop-validation/dist/cli/validate-deliverables.d.ts.map +1 -0
  350. package/claude-assets/skills/cfn-loop-validation/dist/cli/validate-deliverables.js +176 -0
  351. package/claude-assets/skills/cfn-loop-validation/dist/cli/validate-deliverables.js.map +1 -0
  352. package/claude-assets/skills/cfn-loop-validation/dist/cli/validate-gate.d.ts +19 -0
  353. package/claude-assets/skills/cfn-loop-validation/dist/cli/validate-gate.d.ts.map +1 -0
  354. package/claude-assets/skills/cfn-loop-validation/dist/cli/validate-gate.js +123 -0
  355. package/claude-assets/skills/cfn-loop-validation/dist/cli/validate-gate.js.map +1 -0
  356. package/claude-assets/skills/cfn-loop-validation/dist/types.d.ts +156 -0
  357. package/claude-assets/skills/cfn-loop-validation/dist/types.d.ts.map +1 -0
  358. package/claude-assets/skills/cfn-loop-validation/dist/types.js +66 -0
  359. package/claude-assets/skills/cfn-loop-validation/dist/types.js.map +1 -0
  360. package/claude-assets/skills/cfn-loop-validation/dist/validator.d.ts +85 -0
  361. package/claude-assets/skills/cfn-loop-validation/dist/validator.d.ts.map +1 -0
  362. package/claude-assets/skills/cfn-loop-validation/dist/validator.js +411 -0
  363. package/claude-assets/skills/cfn-loop-validation/dist/validator.js.map +1 -0
  364. package/claude-assets/skills/cfn-loop-validation/orchestrate-cfn-loop.sh +22 -0
  365. package/claude-assets/skills/cfn-loop-validation/orchestrate-cfn-loop.sh.backup +252 -0
  366. package/claude-assets/skills/cfn-loop-validation/package.json +93 -0
  367. package/claude-assets/skills/cfn-loop-validation/src/cli/detect-vapor.ts +177 -0
  368. package/claude-assets/skills/cfn-loop-validation/src/cli/validate-deliverables.ts +161 -0
  369. package/claude-assets/skills/cfn-loop-validation/src/cli/validate-gate.ts +139 -0
  370. package/claude-assets/skills/cfn-loop-validation/src/types.ts +215 -0
  371. package/claude-assets/skills/cfn-loop-validation/src/validator.ts +503 -0
  372. package/claude-assets/skills/cfn-loop-validation/tests/validator.test.ts +537 -0
  373. package/claude-assets/skills/{cfn-redis-coordination → cfn-loop-validation}/tsconfig.json +34 -31
  374. package/claude-assets/skills/cfn-loop-validation/validate-deliverables.sh +59 -0
  375. package/claude-assets/skills/cfn-loop-validation/validate-deliverables.sh.backup +37 -0
  376. package/claude-assets/skills/cfn-loop-validation/validate-gate.sh +63 -0
  377. package/claude-assets/skills/cfn-loop-validation/validate-gate.sh.backup +41 -0
  378. package/claude-assets/skills/cfn-loop-validation/validate-iteration.sh +22 -0
  379. package/claude-assets/skills/cfn-loop-validation/validate-iteration.sh.backup +134 -0
  380. package/claude-assets/skills/cfn-product-owner-decision/SKILL.md +479 -147
  381. package/claude-assets/skills/cfn-product-owner-decision/TYPESCRIPT_IMPLEMENTATION.md +653 -0
  382. package/{.claude/skills/cfn-product-owner-decision → claude-assets/skills/cfn-product-owner-decision/archive/legacy-bash}/execute-decision.sh +24 -2
  383. package/claude-assets/skills/cfn-provider-routing/README.md +129 -0
  384. package/claude-assets/skills/cfn-provider-routing/SKILL.md +192 -0
  385. package/claude-assets/skills/cfn-provider-routing/resolve-provider-model.ts +223 -0
  386. package/claude-assets/skills/pre-edit-backup/SKILL.md +324 -0
  387. package/claude-assets/skills/pre-edit-backup/SKILL.md.backup +277 -0
  388. package/claude-assets/skills/pre-edit-backup/backup.sh +22 -0
  389. package/claude-assets/skills/pre-edit-backup/backup.sh.backup +107 -0
  390. package/dist/agents/agent-loader.js +146 -165
  391. package/dist/agents/agent-loader.js.map +1 -1
  392. package/dist/api/auth-endpoints.js +415 -0
  393. package/dist/api/auth-endpoints.js.map +1 -0
  394. package/dist/api/task-endpoints.js +562 -0
  395. package/dist/api/task-endpoints.js.map +1 -0
  396. package/dist/backend/server.js +418 -0
  397. package/dist/backend/server.js.map +1 -0
  398. package/dist/cfn-loop/product-owner/decision-parser.js +356 -0
  399. package/dist/cfn-loop/product-owner/decision-parser.js.map +1 -0
  400. package/dist/cfn-loop/product-owner/index.js +1 -0
  401. package/dist/cfn-loop/product-owner/index.js.map +1 -1
  402. package/dist/cli/agent-command.js +1 -1
  403. package/dist/cli/agent-command.js.map +1 -1
  404. package/dist/cli/agent-completion.js +273 -0
  405. package/dist/cli/agent-completion.js.map +1 -0
  406. package/dist/cli/agent-prompt-builder.js +83 -48
  407. package/dist/cli/agent-prompt-builder.js.map +1 -1
  408. package/dist/cli/agent-spawner.js +499 -0
  409. package/dist/cli/agent-spawner.js.map +1 -0
  410. package/dist/cli/anthropic-client.js +10 -3
  411. package/dist/cli/anthropic-client.js.map +1 -1
  412. package/dist/cli/config-manager.js +91 -109
  413. package/dist/cli/index.js +11 -0
  414. package/dist/cli/index.js.map +1 -1
  415. package/dist/cli/parse-decision-cli.js +268 -0
  416. package/dist/cli/parse-decision-cli.js.map +1 -0
  417. package/dist/cli/post-edit-hook.js +83 -0
  418. package/dist/cli/post-edit-hook.js.map +1 -0
  419. package/dist/cli/pre-edit-hook.js +77 -0
  420. package/dist/cli/pre-edit-hook.js.map +1 -0
  421. package/dist/cli/spawn-agent-cli.js +209 -0
  422. package/dist/cli/spawn-agent-cli.js.map +1 -0
  423. package/dist/coordination/coordination-wrapper.js +383 -0
  424. package/dist/coordination/coordination-wrapper.js.map +1 -0
  425. package/dist/coordination/store-success-criteria.js +68 -0
  426. package/dist/coordination/store-success-criteria.js.map +1 -0
  427. package/dist/coordination/store-task-context.js +65 -0
  428. package/dist/coordination/store-task-context.js.map +1 -0
  429. package/dist/hooks/backup-manager.js +273 -0
  430. package/dist/hooks/backup-manager.js.map +1 -0
  431. package/dist/hooks/post-edit-validator.js +388 -0
  432. package/dist/hooks/post-edit-validator.js.map +1 -0
  433. package/dist/integration/index.js +19 -0
  434. package/dist/integration/index.js.map +1 -0
  435. package/dist/integration/task-mode-adapter.js +297 -0
  436. package/dist/integration/task-mode-adapter.js.map +1 -0
  437. package/dist/integration/trigger-dev-client.js +253 -0
  438. package/dist/integration/trigger-dev-client.js.map +1 -0
  439. package/dist/integration/trigger-dev-webhooks.js +362 -0
  440. package/dist/integration/trigger-dev-webhooks.js.map +1 -0
  441. package/dist/lib/path-validator.js +14 -5
  442. package/dist/lib/path-validator.js.map +1 -1
  443. package/dist/lib/redis-queue-manager.js +5 -1
  444. package/dist/lib/redis-queue-manager.js.map +1 -1
  445. package/dist/middleware/authentication.js +317 -0
  446. package/dist/middleware/authentication.js.map +1 -0
  447. package/dist/services/authentication.js +669 -0
  448. package/dist/services/authentication.js.map +1 -0
  449. package/dist/services/session-management.js +436 -0
  450. package/dist/services/session-management.js.map +1 -0
  451. package/dist/services/skill-deployment.js +8 -6
  452. package/dist/services/skill-deployment.js.map +1 -1
  453. package/dist/services/user-service.js +710 -0
  454. package/dist/services/user-service.js.map +1 -0
  455. package/dist/types/trigger-dev-events.d.js +10 -0
  456. package/dist/types/trigger-dev-events.d.js.map +1 -0
  457. package/docs/README.md +240 -0
  458. package/package.json +13 -4
  459. package/scripts/compare-workflow-performance.sh +556 -0
  460. package/scripts/migrate-to-optimized-workflows.sh +438 -0
  461. package/scripts/organize-docs.sh +338 -0
  462. package/scripts/trigger-dev-setup.sh +267 -0
  463. package/.claude/skills/cfn-docker-redis-coordination/MIGRATION_SUMMARY.md +0 -348
  464. package/.claude/skills/cfn-docker-redis-coordination/README.md +0 -294
  465. package/.claude/skills/cfn-docker-redis-coordination/SKILL.md +0 -435
  466. package/.claude/skills/cfn-docker-redis-coordination/coordinate.sh +0 -650
  467. package/.claude/skills/cfn-docker-redis-coordination/coordinate.sh.backup-1763145142 +0 -641
  468. package/.claude/skills/cfn-docker-redis-coordination/package-lock.json +0 -5259
  469. package/.claude/skills/cfn-docker-redis-coordination/package.json +0 -40
  470. package/.claude/skills/cfn-docker-redis-coordination/src/coordinator.ts +0 -801
  471. package/.claude/skills/cfn-docker-redis-coordination/src/index.ts +0 -42
  472. package/.claude/skills/cfn-docker-redis-coordination/src/types.ts +0 -351
  473. package/.claude/skills/cfn-docker-redis-coordination/tests/coordinator.test.ts +0 -1464
  474. package/.claude/skills/cfn-docker-redis-coordination/tsconfig.json +0 -30
  475. package/.claude/skills/cfn-loop-orchestration/helpers/auto-tune-timeouts.sh +0 -228
  476. package/.claude/skills/cfn-loop-orchestration/helpers/consensus-ts.sh +0 -104
  477. package/.claude/skills/cfn-loop-orchestration/helpers/consensus.sh +0 -94
  478. package/.claude/skills/cfn-loop-orchestration/helpers/context-injection.sh +0 -142
  479. package/.claude/skills/cfn-loop-orchestration/helpers/context-lookup.sh +0 -359
  480. package/.claude/skills/cfn-loop-orchestration/helpers/deliverable-verifier-ts.sh +0 -123
  481. package/.claude/skills/cfn-loop-orchestration/helpers/deliverable-verifier.sh +0 -71
  482. package/.claude/skills/cfn-loop-orchestration/helpers/gate-check.sh +0 -56
  483. package/.claude/skills/cfn-loop-orchestration/helpers/iteration-manager-ts.sh +0 -89
  484. package/.claude/skills/cfn-loop-orchestration/helpers/iteration-manager.sh +0 -87
  485. package/.claude/skills/cfn-loop-orchestration/helpers/orchestrate-ts.sh +0 -104
  486. package/.claude/skills/cfn-loop-orchestration/helpers/parse-test-results.sh +0 -56
  487. package/.claude/skills/cfn-loop-orchestration/helpers/spawn-agents.sh +0 -290
  488. package/.claude/skills/cfn-loop-orchestration/helpers/timeout-calculator-ts.sh +0 -47
  489. package/.claude/skills/cfn-loop-orchestration/helpers/timeout-calculator.sh +0 -51
  490. package/.claude/skills/cfn-loop-orchestration/orchestrate.sh +0 -1345
  491. package/.claude/skills/cfn-redis-coordination/AGENT_LOGGING.md +0 -280
  492. package/.claude/skills/cfn-redis-coordination/BZPOPMIN_FIX_SUMMARY.md +0 -209
  493. package/.claude/skills/cfn-redis-coordination/CENTRALIZED_REDIS_WRAPPER.md +0 -319
  494. package/.claude/skills/cfn-redis-coordination/agent-log.sh.bak +0 -124
  495. package/.claude/skills/cfn-redis-coordination/config.json +0 -61
  496. package/.claude/skills/cfn-redis-coordination/demos/phase4-wake-queue-test-report.md +0 -82
  497. package/.claude/skills/cfn-redis-coordination/demos/test-bzpopmin-fix.sh +0 -274
  498. package/.claude/skills/cfn-redis-coordination/demos/test-cancel-swarm.sh +0 -0
  499. package/.claude/skills/cfn-redis-coordination/docs/migration/PHASE_3_REDIS_COORDINATION_COMPLETION_REPORT.md +0 -553
  500. package/.claude/skills/cfn-redis-coordination/jest.config.js +0 -23
  501. package/.claude/skills/cfn-redis-coordination/package-lock.json +0 -5272
  502. package/.claude/skills/cfn-redis-coordination/package.json +0 -45
  503. package/.claude/skills/cfn-redis-coordination/src/agent-logger.ts +0 -446
  504. package/.claude/skills/cfn-redis-coordination/src/agent-recovery.ts +0 -454
  505. package/.claude/skills/cfn-redis-coordination/src/completion-reporter.ts +0 -396
  506. package/.claude/skills/cfn-redis-coordination/src/context-manager.ts +0 -327
  507. package/.claude/skills/cfn-redis-coordination/src/index.ts +0 -82
  508. package/.claude/skills/cfn-redis-coordination/src/mode-detector.ts +0 -155
  509. package/.claude/skills/cfn-redis-coordination/src/redis/redis-client.ts +0 -305
  510. package/.claude/skills/cfn-redis-coordination/src/redis/redis-functions.ts +0 -283
  511. package/.claude/skills/cfn-redis-coordination/src/redis-client.ts +0 -654
  512. package/.claude/skills/cfn-redis-coordination/src/result-collector.ts +0 -437
  513. package/.claude/skills/cfn-redis-coordination/src/swarm-manager.ts +0 -494
  514. package/.claude/skills/cfn-redis-coordination/src/task-analyzer.ts +0 -404
  515. package/.claude/skills/cfn-redis-coordination/src/task-executor.ts +0 -423
  516. package/.claude/skills/cfn-redis-coordination/src/types.ts +0 -235
  517. package/.claude/skills/cfn-redis-coordination/src/waiting-coordinator.ts +0 -587
  518. package/.claude/skills/cfn-redis-coordination/store-success-criteria.sh +0 -85
  519. package/.claude/skills/cfn-redis-coordination/test-connection-attempts.js +0 -70
  520. package/.claude/skills/cfn-redis-coordination/test-mode-simple.js +0 -121
  521. package/.claude/skills/cfn-redis-coordination/test-redis-check.js +0 -84
  522. package/.claude/skills/cfn-redis-coordination/test-task-mode-redis.cjs +0 -391
  523. package/.claude/skills/cfn-redis-coordination/tests/coordination.test.ts +0 -788
  524. package/.claude/skills/cfn-redis-coordination/update-all-scripts.sh +0 -67
  525. package/claude-assets/agents/cfn-dev-team/coordinators/cfn-v3-coordinator.md +0 -980
  526. package/claude-assets/agents/typescript-specialist.md +0 -280
  527. package/claude-assets/skills/cfn-docker-redis-coordination/MIGRATION_SUMMARY.md +0 -348
  528. package/claude-assets/skills/cfn-docker-redis-coordination/README.md +0 -294
  529. package/claude-assets/skills/cfn-docker-redis-coordination/SKILL.md +0 -435
  530. package/claude-assets/skills/cfn-docker-redis-coordination/coordinate.sh +0 -650
  531. package/claude-assets/skills/cfn-docker-redis-coordination/coordinate.sh.backup-1763145142 +0 -641
  532. package/claude-assets/skills/cfn-docker-redis-coordination/jest.config.js +0 -37
  533. package/claude-assets/skills/cfn-docker-redis-coordination/package-lock.json +0 -5259
  534. package/claude-assets/skills/cfn-docker-redis-coordination/package.json +0 -40
  535. package/claude-assets/skills/cfn-docker-redis-coordination/src/coordinator.ts +0 -801
  536. package/claude-assets/skills/cfn-docker-redis-coordination/src/index.ts +0 -42
  537. package/claude-assets/skills/cfn-docker-redis-coordination/src/types.ts +0 -351
  538. package/claude-assets/skills/cfn-docker-redis-coordination/tests/coordinator.test.ts +0 -1464
  539. package/claude-assets/skills/cfn-loop-orchestration/helpers/auto-tune-timeouts.sh +0 -228
  540. package/claude-assets/skills/cfn-loop-orchestration/helpers/consensus-ts.sh +0 -104
  541. package/claude-assets/skills/cfn-loop-orchestration/helpers/consensus.sh +0 -94
  542. package/claude-assets/skills/cfn-loop-orchestration/helpers/context-injection.sh +0 -142
  543. package/claude-assets/skills/cfn-loop-orchestration/helpers/context-lookup.sh +0 -359
  544. package/claude-assets/skills/cfn-loop-orchestration/helpers/deliverable-verifier-ts.sh +0 -123
  545. package/claude-assets/skills/cfn-loop-orchestration/helpers/deliverable-verifier.sh +0 -71
  546. package/claude-assets/skills/cfn-loop-orchestration/helpers/gate-check.sh +0 -56
  547. package/claude-assets/skills/cfn-loop-orchestration/helpers/iteration-manager-ts.sh +0 -89
  548. package/claude-assets/skills/cfn-loop-orchestration/helpers/iteration-manager.sh +0 -87
  549. package/claude-assets/skills/cfn-loop-orchestration/helpers/orchestrate-ts.sh +0 -104
  550. package/claude-assets/skills/cfn-loop-orchestration/helpers/parse-test-results.sh +0 -56
  551. package/claude-assets/skills/cfn-loop-orchestration/helpers/spawn-agents.sh +0 -290
  552. package/claude-assets/skills/cfn-loop-orchestration/helpers/timeout-calculator-ts.sh +0 -47
  553. package/claude-assets/skills/cfn-loop-orchestration/helpers/timeout-calculator.sh +0 -51
  554. package/claude-assets/skills/cfn-loop-orchestration/orchestrate.sh +0 -1345
  555. package/claude-assets/skills/cfn-redis-cleanup/cleanup-redis.sh +0 -130
  556. package/claude-assets/skills/cfn-redis-coordination/AGENT_LOGGING.md +0 -280
  557. package/claude-assets/skills/cfn-redis-coordination/BZPOPMIN_FIX_SUMMARY.md +0 -209
  558. package/claude-assets/skills/cfn-redis-coordination/CENTRALIZED_REDIS_WRAPPER.md +0 -319
  559. package/claude-assets/skills/cfn-redis-coordination/agent-log.sh.bak +0 -124
  560. package/claude-assets/skills/cfn-redis-coordination/config.json +0 -61
  561. package/claude-assets/skills/cfn-redis-coordination/demos/phase4-wake-queue-test-report.md +0 -82
  562. package/claude-assets/skills/cfn-redis-coordination/demos/test-bzpopmin-fix.sh +0 -274
  563. package/claude-assets/skills/cfn-redis-coordination/demos/test-cancel-swarm.sh +0 -0
  564. package/claude-assets/skills/cfn-redis-coordination/docs/migration/PHASE_3_REDIS_COORDINATION_COMPLETION_REPORT.md +0 -553
  565. package/claude-assets/skills/cfn-redis-coordination/jest.config.js +0 -23
  566. package/claude-assets/skills/cfn-redis-coordination/package-lock.json +0 -5272
  567. package/claude-assets/skills/cfn-redis-coordination/package.json +0 -45
  568. package/claude-assets/skills/cfn-redis-coordination/src/agent-logger.ts +0 -446
  569. package/claude-assets/skills/cfn-redis-coordination/src/agent-recovery.ts +0 -454
  570. package/claude-assets/skills/cfn-redis-coordination/src/completion-reporter.ts +0 -396
  571. package/claude-assets/skills/cfn-redis-coordination/src/context-manager.ts +0 -327
  572. package/claude-assets/skills/cfn-redis-coordination/src/index.ts +0 -82
  573. package/claude-assets/skills/cfn-redis-coordination/src/mode-detector.ts +0 -155
  574. package/claude-assets/skills/cfn-redis-coordination/src/redis/redis-client.ts +0 -305
  575. package/claude-assets/skills/cfn-redis-coordination/src/redis/redis-functions.ts +0 -283
  576. package/claude-assets/skills/cfn-redis-coordination/src/redis-client.ts +0 -654
  577. package/claude-assets/skills/cfn-redis-coordination/src/result-collector.ts +0 -437
  578. package/claude-assets/skills/cfn-redis-coordination/src/swarm-manager.ts +0 -494
  579. package/claude-assets/skills/cfn-redis-coordination/src/task-analyzer.ts +0 -404
  580. package/claude-assets/skills/cfn-redis-coordination/src/task-executor.ts +0 -423
  581. package/claude-assets/skills/cfn-redis-coordination/src/types.ts +0 -235
  582. package/claude-assets/skills/cfn-redis-coordination/src/waiting-coordinator.ts +0 -587
  583. package/claude-assets/skills/cfn-redis-coordination/store-success-criteria.sh +0 -85
  584. package/claude-assets/skills/cfn-redis-coordination/test-connection-attempts.js +0 -70
  585. package/claude-assets/skills/cfn-redis-coordination/test-mode-simple.js +0 -121
  586. package/claude-assets/skills/cfn-redis-coordination/test-redis-check.js +0 -84
  587. package/claude-assets/skills/cfn-redis-coordination/test-task-mode-redis.cjs +0 -391
  588. package/claude-assets/skills/cfn-redis-coordination/tests/coordination.test.ts +0 -788
  589. package/claude-assets/skills/cfn-redis-coordination/update-all-scripts.sh +0 -67
  590. package/claude-assets/skills/cfn-redis-data-extraction/SKILL.md +0 -442
  591. package/claude-assets/skills/cfn-redis-data-extraction/extract.sh +0 -306
  592. package/dist/coordination/index.js +0 -25
  593. package/dist/coordination/index.js.map +0 -1
  594. package/docs/BUG_19_MEMORY_LEAK_TASK_MODE.md +0 -405
  595. package/docs/MEMORY_CLEANUP_GUIDE.md +0 -358
  596. package/docs/MEMORY_LEAK_FIX_SUMMARY.md +0 -322
  597. package/docs/REDIS_CLEANUP_EXECUTIVE_SUMMARY.md +0 -319
  598. package/docs/REDIS_CLEANUP_VERIFICATION_REPORT.md +0 -574
  599. /package/.claude/skills/cfn-loop-orchestration/{inject-loop-context.sh → archive/legacy-bash/inject-loop-context.sh} +0 -0
  600. /package/.claude/skills/cfn-loop-orchestration/{monitor-execution.sh → archive/legacy-bash/monitor-execution.sh} +0 -0
  601. /package/.claude/skills/cfn-redis-coordination/{agent-log.sh → agent-log.sh.backup} +0 -0
  602. /package/.claude/skills/cfn-redis-coordination/{agent-recovery.sh → agent-recovery.sh.backup} +0 -0
  603. /package/.claude/skills/cfn-redis-coordination/{analyze-task-complexity.sh → analyze-task-complexity.sh.backup} +0 -0
  604. /package/.claude/skills/cfn-redis-coordination/bash-wrappers/{store-context.sh → store-context.sh.backup} +0 -0
  605. /package/.claude/skills/cfn-redis-coordination/{cancel-swarm.sh → cancel-swarm.sh.backup} +0 -0
  606. /package/.claude/skills/cfn-redis-coordination/{cfn-loop-exec.sh → cfn-loop-exec.sh.backup} +0 -0
  607. /package/.claude/skills/cfn-redis-coordination/{cfn-loop-relaunch.sh → cfn-loop-relaunch.sh.backup} +0 -0
  608. /package/.claude/skills/cfn-redis-coordination/{collect-confidence-scores.sh → collect-confidence-scores.sh.backup} +0 -0
  609. /package/.claude/skills/cfn-redis-coordination/{collect-results.sh → collect-results.sh.backup} +0 -0
  610. /package/.claude/skills/cfn-redis-coordination/{complete-swarm.sh → complete-swarm.sh.backup} +0 -0
  611. /package/.claude/skills/cfn-redis-coordination/{get-context.sh → get-context.sh.backup} +0 -0
  612. /package/.claude/skills/cfn-redis-coordination/{get-success-criteria.sh → get-success-criteria.sh.backup} +0 -0
  613. /package/.claude/skills/cfn-redis-coordination/{invoke-waiting-mode.sh → invoke-waiting-mode.sh.backup} +0 -0
  614. /package/.claude/skills/cfn-redis-coordination/{redis-cli-wrapper.sh → redis-cli-wrapper.sh.backup} +0 -0
  615. /package/.claude/skills/cfn-redis-coordination/{redis-functions.sh → redis-functions.sh.backup} +0 -0
  616. /package/.claude/skills/cfn-redis-coordination/{report-completion.sh → report-completion.sh.backup} +0 -0
  617. /package/.claude/skills/cfn-redis-coordination/{store-context.sh → store-context.sh.backup} +0 -0
  618. /package/claude-assets/skills/cfn-loop-orchestration/{inject-loop-context.sh → archive/legacy-bash/inject-loop-context.sh} +0 -0
  619. /package/claude-assets/skills/cfn-loop-orchestration/{monitor-execution.sh → archive/legacy-bash/monitor-execution.sh} +0 -0
  620. /package/claude-assets/skills/cfn-redis-coordination/{agent-log.sh → agent-log.sh.backup} +0 -0
  621. /package/claude-assets/skills/cfn-redis-coordination/{agent-recovery.sh → agent-recovery.sh.backup} +0 -0
  622. /package/claude-assets/skills/cfn-redis-coordination/{analyze-task-complexity.sh → analyze-task-complexity.sh.backup} +0 -0
  623. /package/claude-assets/skills/cfn-redis-coordination/bash-wrappers/{store-context.sh → store-context.sh.backup} +0 -0
  624. /package/claude-assets/skills/cfn-redis-coordination/{cancel-swarm.sh → cancel-swarm.sh.backup} +0 -0
  625. /package/claude-assets/skills/cfn-redis-coordination/{cfn-loop-exec.sh → cfn-loop-exec.sh.backup} +0 -0
  626. /package/claude-assets/skills/cfn-redis-coordination/{cfn-loop-relaunch.sh → cfn-loop-relaunch.sh.backup} +0 -0
  627. /package/claude-assets/skills/cfn-redis-coordination/{collect-confidence-scores.sh → collect-confidence-scores.sh.backup} +0 -0
  628. /package/claude-assets/skills/cfn-redis-coordination/{collect-results.sh → collect-results.sh.backup} +0 -0
  629. /package/claude-assets/skills/cfn-redis-coordination/{complete-swarm.sh → complete-swarm.sh.backup} +0 -0
  630. /package/claude-assets/skills/cfn-redis-coordination/{get-context.sh → get-context.sh.backup} +0 -0
  631. /package/claude-assets/skills/cfn-redis-coordination/{get-success-criteria.sh → get-success-criteria.sh.backup} +0 -0
  632. /package/claude-assets/skills/cfn-redis-coordination/{invoke-waiting-mode.sh → invoke-waiting-mode.sh.backup} +0 -0
  633. /package/claude-assets/skills/cfn-redis-coordination/{redis-cli-wrapper.sh → redis-cli-wrapper.sh.backup} +0 -0
  634. /package/claude-assets/skills/cfn-redis-coordination/{redis-functions.sh → redis-functions.sh.backup} +0 -0
  635. /package/claude-assets/skills/cfn-redis-coordination/{report-completion.sh → report-completion.sh.backup} +0 -0
  636. /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":"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"}
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
  });