cap-pro 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (275) hide show
  1. package/.claude-plugin/README.md +26 -0
  2. package/.claude-plugin/marketplace.json +24 -0
  3. package/.claude-plugin/plugin.json +24 -0
  4. package/LICENSE +21 -0
  5. package/README.ja-JP.md +834 -0
  6. package/README.ko-KR.md +823 -0
  7. package/README.md +806 -0
  8. package/README.pt-BR.md +452 -0
  9. package/README.zh-CN.md +800 -0
  10. package/agents/cap-architect.md +269 -0
  11. package/agents/cap-brainstormer.md +207 -0
  12. package/agents/cap-curator.md +276 -0
  13. package/agents/cap-debugger.md +365 -0
  14. package/agents/cap-designer.md +246 -0
  15. package/agents/cap-historian.md +464 -0
  16. package/agents/cap-migrator.md +291 -0
  17. package/agents/cap-prototyper.md +197 -0
  18. package/agents/cap-validator.md +308 -0
  19. package/bin/install.js +5433 -0
  20. package/cap/bin/cap-tools.cjs +853 -0
  21. package/cap/bin/lib/arc-scanner.cjs +344 -0
  22. package/cap/bin/lib/cap-affinity-engine.cjs +862 -0
  23. package/cap/bin/lib/cap-anchor.cjs +228 -0
  24. package/cap/bin/lib/cap-annotation-writer.cjs +340 -0
  25. package/cap/bin/lib/cap-checkpoint.cjs +434 -0
  26. package/cap/bin/lib/cap-cluster-detect.cjs +945 -0
  27. package/cap/bin/lib/cap-cluster-display.cjs +52 -0
  28. package/cap/bin/lib/cap-cluster-format.cjs +245 -0
  29. package/cap/bin/lib/cap-cluster-helpers.cjs +295 -0
  30. package/cap/bin/lib/cap-cluster-io.cjs +212 -0
  31. package/cap/bin/lib/cap-completeness.cjs +540 -0
  32. package/cap/bin/lib/cap-deps.cjs +583 -0
  33. package/cap/bin/lib/cap-design-families.cjs +332 -0
  34. package/cap/bin/lib/cap-design.cjs +966 -0
  35. package/cap/bin/lib/cap-divergence-detector.cjs +400 -0
  36. package/cap/bin/lib/cap-doctor.cjs +752 -0
  37. package/cap/bin/lib/cap-feature-map-internals.cjs +19 -0
  38. package/cap/bin/lib/cap-feature-map-migrate.cjs +335 -0
  39. package/cap/bin/lib/cap-feature-map-monorepo.cjs +885 -0
  40. package/cap/bin/lib/cap-feature-map-shard.cjs +315 -0
  41. package/cap/bin/lib/cap-feature-map.cjs +1943 -0
  42. package/cap/bin/lib/cap-fitness-score.cjs +1075 -0
  43. package/cap/bin/lib/cap-impact-analysis.cjs +652 -0
  44. package/cap/bin/lib/cap-learn-review.cjs +1072 -0
  45. package/cap/bin/lib/cap-learning-signals.cjs +627 -0
  46. package/cap/bin/lib/cap-loader.cjs +227 -0
  47. package/cap/bin/lib/cap-logger.cjs +57 -0
  48. package/cap/bin/lib/cap-memory-bridge.cjs +764 -0
  49. package/cap/bin/lib/cap-memory-confidence.cjs +452 -0
  50. package/cap/bin/lib/cap-memory-dir.cjs +987 -0
  51. package/cap/bin/lib/cap-memory-engine.cjs +698 -0
  52. package/cap/bin/lib/cap-memory-extends.cjs +398 -0
  53. package/cap/bin/lib/cap-memory-graph.cjs +790 -0
  54. package/cap/bin/lib/cap-memory-migrate.cjs +2015 -0
  55. package/cap/bin/lib/cap-memory-pin.cjs +183 -0
  56. package/cap/bin/lib/cap-memory-platform.cjs +490 -0
  57. package/cap/bin/lib/cap-memory-prune.cjs +707 -0
  58. package/cap/bin/lib/cap-memory-schema.cjs +812 -0
  59. package/cap/bin/lib/cap-migrate-tags.cjs +309 -0
  60. package/cap/bin/lib/cap-migrate.cjs +540 -0
  61. package/cap/bin/lib/cap-pattern-apply.cjs +1203 -0
  62. package/cap/bin/lib/cap-pattern-pipeline.cjs +1034 -0
  63. package/cap/bin/lib/cap-plugin-manifest.cjs +80 -0
  64. package/cap/bin/lib/cap-realtime-affinity.cjs +399 -0
  65. package/cap/bin/lib/cap-reconcile.cjs +570 -0
  66. package/cap/bin/lib/cap-research-gate.cjs +218 -0
  67. package/cap/bin/lib/cap-scope-filter.cjs +402 -0
  68. package/cap/bin/lib/cap-semantic-pipeline.cjs +1038 -0
  69. package/cap/bin/lib/cap-session-extract.cjs +987 -0
  70. package/cap/bin/lib/cap-session.cjs +445 -0
  71. package/cap/bin/lib/cap-snapshot-linkage.cjs +963 -0
  72. package/cap/bin/lib/cap-stack-docs.cjs +646 -0
  73. package/cap/bin/lib/cap-tag-observer.cjs +371 -0
  74. package/cap/bin/lib/cap-tag-scanner.cjs +1766 -0
  75. package/cap/bin/lib/cap-telemetry.cjs +466 -0
  76. package/cap/bin/lib/cap-test-audit.cjs +1438 -0
  77. package/cap/bin/lib/cap-thread-migrator.cjs +307 -0
  78. package/cap/bin/lib/cap-thread-synthesis.cjs +545 -0
  79. package/cap/bin/lib/cap-thread-tracker.cjs +519 -0
  80. package/cap/bin/lib/cap-trace.cjs +399 -0
  81. package/cap/bin/lib/cap-trust-mode.cjs +336 -0
  82. package/cap/bin/lib/cap-ui-design-editor.cjs +642 -0
  83. package/cap/bin/lib/cap-ui-mind-map.cjs +712 -0
  84. package/cap/bin/lib/cap-ui-thread-nav.cjs +693 -0
  85. package/cap/bin/lib/cap-ui.cjs +1245 -0
  86. package/cap/bin/lib/cap-upgrade.cjs +1028 -0
  87. package/cap/bin/lib/cli/arg-helpers.cjs +49 -0
  88. package/cap/bin/lib/cli/frontmatter-router.cjs +31 -0
  89. package/cap/bin/lib/cli/init-router.cjs +68 -0
  90. package/cap/bin/lib/cli/phase-router.cjs +102 -0
  91. package/cap/bin/lib/cli/state-router.cjs +61 -0
  92. package/cap/bin/lib/cli/template-router.cjs +37 -0
  93. package/cap/bin/lib/cli/uat-router.cjs +29 -0
  94. package/cap/bin/lib/cli/validation-router.cjs +26 -0
  95. package/cap/bin/lib/cli/verification-router.cjs +31 -0
  96. package/cap/bin/lib/cli/workstream-router.cjs +39 -0
  97. package/cap/bin/lib/commands.cjs +961 -0
  98. package/cap/bin/lib/config.cjs +467 -0
  99. package/cap/bin/lib/convention-reader.cjs +258 -0
  100. package/cap/bin/lib/core.cjs +1241 -0
  101. package/cap/bin/lib/feature-aggregator.cjs +423 -0
  102. package/cap/bin/lib/frontmatter.cjs +337 -0
  103. package/cap/bin/lib/init.cjs +1443 -0
  104. package/cap/bin/lib/manifest-generator.cjs +383 -0
  105. package/cap/bin/lib/milestone.cjs +253 -0
  106. package/cap/bin/lib/model-profiles.cjs +69 -0
  107. package/cap/bin/lib/monorepo-context.cjs +226 -0
  108. package/cap/bin/lib/monorepo-migrator.cjs +509 -0
  109. package/cap/bin/lib/phase.cjs +889 -0
  110. package/cap/bin/lib/profile-output.cjs +989 -0
  111. package/cap/bin/lib/profile-pipeline.cjs +540 -0
  112. package/cap/bin/lib/roadmap.cjs +330 -0
  113. package/cap/bin/lib/security.cjs +394 -0
  114. package/cap/bin/lib/session-manager.cjs +292 -0
  115. package/cap/bin/lib/skeleton-generator.cjs +179 -0
  116. package/cap/bin/lib/state.cjs +1032 -0
  117. package/cap/bin/lib/template.cjs +231 -0
  118. package/cap/bin/lib/test-detector.cjs +62 -0
  119. package/cap/bin/lib/uat.cjs +283 -0
  120. package/cap/bin/lib/verify.cjs +889 -0
  121. package/cap/bin/lib/workspace-detector.cjs +371 -0
  122. package/cap/bin/lib/workstream.cjs +492 -0
  123. package/cap/commands/gsd/workstreams.md +63 -0
  124. package/cap/references/arc-standard.md +315 -0
  125. package/cap/references/cap-agent-architecture.md +101 -0
  126. package/cap/references/cap-gitignore-template +9 -0
  127. package/cap/references/cap-zero-deps.md +158 -0
  128. package/cap/references/checkpoints.md +778 -0
  129. package/cap/references/continuation-format.md +249 -0
  130. package/cap/references/contract-test-templates.md +312 -0
  131. package/cap/references/feature-map-template.md +25 -0
  132. package/cap/references/git-integration.md +295 -0
  133. package/cap/references/git-planning-commit.md +38 -0
  134. package/cap/references/model-profiles.md +174 -0
  135. package/cap/references/phase-numbering.md +126 -0
  136. package/cap/references/planning-config.md +202 -0
  137. package/cap/references/property-test-templates.md +316 -0
  138. package/cap/references/security-test-templates.md +347 -0
  139. package/cap/references/session-template.json +8 -0
  140. package/cap/references/tdd.md +263 -0
  141. package/cap/references/user-profiling.md +681 -0
  142. package/cap/references/verification-patterns.md +612 -0
  143. package/cap/templates/UAT.md +265 -0
  144. package/cap/templates/claude-md.md +175 -0
  145. package/cap/templates/codebase/architecture.md +255 -0
  146. package/cap/templates/codebase/concerns.md +310 -0
  147. package/cap/templates/codebase/conventions.md +307 -0
  148. package/cap/templates/codebase/integrations.md +280 -0
  149. package/cap/templates/codebase/stack.md +186 -0
  150. package/cap/templates/codebase/structure.md +285 -0
  151. package/cap/templates/codebase/testing.md +480 -0
  152. package/cap/templates/config.json +44 -0
  153. package/cap/templates/context.md +352 -0
  154. package/cap/templates/continue-here.md +78 -0
  155. package/cap/templates/copilot-instructions.md +7 -0
  156. package/cap/templates/debug-subagent-prompt.md +91 -0
  157. package/cap/templates/discussion-log.md +63 -0
  158. package/cap/templates/milestone-archive.md +123 -0
  159. package/cap/templates/milestone.md +115 -0
  160. package/cap/templates/phase-prompt.md +610 -0
  161. package/cap/templates/planner-subagent-prompt.md +117 -0
  162. package/cap/templates/project.md +186 -0
  163. package/cap/templates/requirements.md +231 -0
  164. package/cap/templates/research-project/ARCHITECTURE.md +204 -0
  165. package/cap/templates/research-project/FEATURES.md +147 -0
  166. package/cap/templates/research-project/PITFALLS.md +200 -0
  167. package/cap/templates/research-project/STACK.md +120 -0
  168. package/cap/templates/research-project/SUMMARY.md +170 -0
  169. package/cap/templates/research.md +552 -0
  170. package/cap/templates/roadmap.md +202 -0
  171. package/cap/templates/state.md +176 -0
  172. package/cap/templates/summary.md +364 -0
  173. package/cap/templates/user-preferences.md +498 -0
  174. package/cap/templates/verification-report.md +322 -0
  175. package/cap/workflows/add-phase.md +112 -0
  176. package/cap/workflows/add-tests.md +351 -0
  177. package/cap/workflows/add-todo.md +158 -0
  178. package/cap/workflows/audit-milestone.md +340 -0
  179. package/cap/workflows/audit-uat.md +109 -0
  180. package/cap/workflows/autonomous.md +891 -0
  181. package/cap/workflows/check-todos.md +177 -0
  182. package/cap/workflows/cleanup.md +152 -0
  183. package/cap/workflows/complete-milestone.md +767 -0
  184. package/cap/workflows/diagnose-issues.md +231 -0
  185. package/cap/workflows/discovery-phase.md +289 -0
  186. package/cap/workflows/discuss-phase-assumptions.md +653 -0
  187. package/cap/workflows/discuss-phase.md +1049 -0
  188. package/cap/workflows/do.md +104 -0
  189. package/cap/workflows/execute-phase.md +846 -0
  190. package/cap/workflows/execute-plan.md +514 -0
  191. package/cap/workflows/fast.md +105 -0
  192. package/cap/workflows/forensics.md +265 -0
  193. package/cap/workflows/health.md +181 -0
  194. package/cap/workflows/help.md +660 -0
  195. package/cap/workflows/insert-phase.md +130 -0
  196. package/cap/workflows/list-phase-assumptions.md +178 -0
  197. package/cap/workflows/list-workspaces.md +56 -0
  198. package/cap/workflows/manager.md +362 -0
  199. package/cap/workflows/map-codebase.md +377 -0
  200. package/cap/workflows/milestone-summary.md +223 -0
  201. package/cap/workflows/new-milestone.md +486 -0
  202. package/cap/workflows/new-project.md +1250 -0
  203. package/cap/workflows/new-workspace.md +237 -0
  204. package/cap/workflows/next.md +97 -0
  205. package/cap/workflows/node-repair.md +92 -0
  206. package/cap/workflows/note.md +156 -0
  207. package/cap/workflows/pause-work.md +176 -0
  208. package/cap/workflows/plan-milestone-gaps.md +273 -0
  209. package/cap/workflows/plan-phase.md +857 -0
  210. package/cap/workflows/plant-seed.md +169 -0
  211. package/cap/workflows/pr-branch.md +129 -0
  212. package/cap/workflows/profile-user.md +449 -0
  213. package/cap/workflows/progress.md +507 -0
  214. package/cap/workflows/quick.md +757 -0
  215. package/cap/workflows/remove-phase.md +155 -0
  216. package/cap/workflows/remove-workspace.md +90 -0
  217. package/cap/workflows/research-phase.md +82 -0
  218. package/cap/workflows/resume-project.md +326 -0
  219. package/cap/workflows/review.md +228 -0
  220. package/cap/workflows/session-report.md +146 -0
  221. package/cap/workflows/settings.md +283 -0
  222. package/cap/workflows/ship.md +228 -0
  223. package/cap/workflows/stats.md +60 -0
  224. package/cap/workflows/transition.md +671 -0
  225. package/cap/workflows/ui-phase.md +298 -0
  226. package/cap/workflows/ui-review.md +161 -0
  227. package/cap/workflows/update.md +323 -0
  228. package/cap/workflows/validate-phase.md +170 -0
  229. package/cap/workflows/verify-phase.md +254 -0
  230. package/cap/workflows/verify-work.md +637 -0
  231. package/commands/cap/annotate.md +165 -0
  232. package/commands/cap/brainstorm.md +393 -0
  233. package/commands/cap/checkpoint.md +106 -0
  234. package/commands/cap/completeness.md +94 -0
  235. package/commands/cap/continue.md +72 -0
  236. package/commands/cap/debug.md +588 -0
  237. package/commands/cap/deps.md +169 -0
  238. package/commands/cap/design.md +479 -0
  239. package/commands/cap/init.md +354 -0
  240. package/commands/cap/iterate.md +249 -0
  241. package/commands/cap/learn.md +459 -0
  242. package/commands/cap/memory.md +275 -0
  243. package/commands/cap/migrate-feature-map.md +91 -0
  244. package/commands/cap/migrate-memory.md +108 -0
  245. package/commands/cap/migrate-tags.md +91 -0
  246. package/commands/cap/migrate.md +131 -0
  247. package/commands/cap/prototype.md +510 -0
  248. package/commands/cap/reconcile.md +121 -0
  249. package/commands/cap/review.md +360 -0
  250. package/commands/cap/save.md +72 -0
  251. package/commands/cap/scan.md +404 -0
  252. package/commands/cap/start.md +356 -0
  253. package/commands/cap/status.md +118 -0
  254. package/commands/cap/test-audit.md +262 -0
  255. package/commands/cap/test.md +394 -0
  256. package/commands/cap/trace.md +133 -0
  257. package/commands/cap/ui.md +167 -0
  258. package/hooks/dist/cap-check-update.js +115 -0
  259. package/hooks/dist/cap-context-monitor.js +185 -0
  260. package/hooks/dist/cap-learn-review-hook.js +114 -0
  261. package/hooks/dist/cap-learning-hook.js +192 -0
  262. package/hooks/dist/cap-memory.js +299 -0
  263. package/hooks/dist/cap-prompt-guard.js +97 -0
  264. package/hooks/dist/cap-statusline.js +157 -0
  265. package/hooks/dist/cap-tag-observer.js +115 -0
  266. package/hooks/dist/cap-version-check.js +112 -0
  267. package/hooks/dist/cap-workflow-guard.js +175 -0
  268. package/hooks/hooks.json +55 -0
  269. package/package.json +58 -0
  270. package/scripts/base64-scan.sh +262 -0
  271. package/scripts/build-hooks.js +93 -0
  272. package/scripts/cap-removal-checklist.md +202 -0
  273. package/scripts/prompt-injection-scan.sh +199 -0
  274. package/scripts/run-tests.cjs +181 -0
  275. package/scripts/secret-scan.sh +227 -0
@@ -0,0 +1,199 @@
1
+ #!/usr/bin/env bash
2
+ # prompt-injection-scan.sh — Scan files for prompt injection patterns
3
+ #
4
+ # Usage:
5
+ # scripts/prompt-injection-scan.sh --diff origin/main # CI mode: scan changed .md files
6
+ # scripts/prompt-injection-scan.sh --file path/to/file # Scan a single file
7
+ # scripts/prompt-injection-scan.sh --dir agents/ # Scan all files in a directory
8
+ #
9
+ # Exit codes:
10
+ # 0 = clean
11
+ # 1 = findings detected
12
+ # 2 = usage error
13
+ set -euo pipefail
14
+
15
+ # ─── Patterns ────────────────────────────────────────────────────────────────
16
+ # Each pattern is a POSIX extended regex. Keep alphabetized by category.
17
+
18
+ PATTERNS=(
19
+ # Instruction override
20
+ 'ignore[[:space:]]+(all[[:space:]]+)?(previous|prior|above|earlier|preceding)[[:space:]]+(instructions|prompts|rules|directives|context)'
21
+ 'disregard[[:space:]]+(all[[:space:]]+)?(previous|prior|above)[[:space:]]+(instructions|prompts|rules)'
22
+ 'forget[[:space:]]+(all[[:space:]]+)?(previous|prior|above)[[:space:]]+(instructions|prompts|rules|context)'
23
+ 'override[[:space:]]+(all[[:space:]]+)?(system|previous|safety)[[:space:]]+(instructions|prompts|rules|checks|filters|guards)'
24
+ 'override[[:space:]]+(system|safety|security)[[:space:]]'
25
+
26
+ # Role manipulation
27
+ 'you[[:space:]]+are[[:space:]]+now[[:space:]]+(a|an|my)[[:space:]]'
28
+ 'from[[:space:]]+now[[:space:]]+on[[:space:]]+(you|pretend|act|behave)'
29
+ 'pretend[[:space:]]+(you[[:space:]]+are|to[[:space:]]+be)[[:space:]]'
30
+ 'act[[:space:]]+as[[:space:]]+(a|an|if|my)[[:space:]]'
31
+ 'roleplay[[:space:]]+as[[:space:]]'
32
+ 'assume[[:space:]]+the[[:space:]]+role[[:space:]]+of[[:space:]]'
33
+
34
+ # System prompt extraction
35
+ 'output[[:space:]]+(your|the)[[:space:]]+(system[[:space:]]+)?(prompt|instructions)'
36
+ 'reveal[[:space:]]+(your|the)[[:space:]]+(system[[:space:]]+)?(prompt|instructions)'
37
+ 'show[[:space:]]+me[[:space:]]+(your|the)[[:space:]]+(system[[:space:]]+)?(prompt|instructions)'
38
+ 'print[[:space:]]+(your|the)[[:space:]]+(system[[:space:]]+)?(prompt|instructions)'
39
+ 'what[[:space:]]+(is|are)[[:space:]]+(your|the)[[:space:]]+(system[[:space:]]+)?(prompt|instructions)'
40
+ 'repeat[[:space:]]+(your|the|all)[[:space:]]+(system[[:space:]]+)?(prompt|instructions|rules)'
41
+
42
+ # Fake message boundaries
43
+ '</?system>'
44
+ '</?assistant>'
45
+ '</?human>'
46
+ '\[SYSTEM\]'
47
+ '\[/SYSTEM\]'
48
+ '\[INST\]'
49
+ '\[/INST\]'
50
+ '<<SYS>>'
51
+ '<</SYS>>'
52
+
53
+ # Tool call injection / code execution in markdown
54
+ 'eval[[:space:]]*\([[:space:]]*["\x27]'
55
+ 'exec[[:space:]]*\([[:space:]]*["\x27]'
56
+ 'Function[[:space:]]*\([[:space:]]*["\x27].*return'
57
+
58
+ # Jailbreak / DAN patterns
59
+ 'do[[:space:]]+anything[[:space:]]+now'
60
+ 'DAN[[:space:]]+mode'
61
+ 'developer[[:space:]]+mode[[:space:]]+(enabled|output|activated)'
62
+ 'jailbreak'
63
+ 'bypass[[:space:]]+(safety|content|security)[[:space:]]+(filter|check|rule|guard)'
64
+ )
65
+
66
+ # ─── Allowlist ───────────────────────────────────────────────────────────────
67
+ # Files that legitimately discuss injection patterns (security docs, tests, this script)
68
+ ALLOWLIST=(
69
+ 'scripts/prompt-injection-scan.sh'
70
+ 'scripts/base64-scan.sh'
71
+ 'scripts/secret-scan.sh'
72
+ 'tests/security-scan.test.cjs'
73
+ 'tests/security.test.cjs'
74
+ 'tests/prompt-injection-scan.test.cjs'
75
+ 'tests/cap-prompt-guard.test.cjs'
76
+ 'get-shit-done/bin/lib/security.cjs'
77
+ 'hooks/gsd-prompt-guard.js'
78
+ 'SECURITY.md'
79
+ )
80
+
81
+ is_allowlisted() {
82
+ local file="$1"
83
+ for allowed in "${ALLOWLIST[@]}"; do
84
+ if [[ "$file" == *"$allowed" ]]; then
85
+ return 0
86
+ fi
87
+ done
88
+ return 1
89
+ }
90
+
91
+ # ─── File Collection ─────────────────────────────────────────────────────────
92
+
93
+ collect_files() {
94
+ local mode="$1"
95
+ shift
96
+
97
+ case "$mode" in
98
+ --diff)
99
+ local base="${1:-origin/main}"
100
+ # Get changed files in the diff, filter to scannable extensions
101
+ git diff --name-only --diff-filter=ACMR "$base"...HEAD 2>/dev/null \
102
+ | grep -E '\.(md|cjs|js|json|yml|yaml|sh)$' || true
103
+ ;;
104
+ --file)
105
+ if [[ -f "$1" ]]; then
106
+ echo "$1"
107
+ else
108
+ echo "Error: file not found: $1" >&2
109
+ exit 2
110
+ fi
111
+ ;;
112
+ --dir)
113
+ local dir="$1"
114
+ if [[ ! -d "$dir" ]]; then
115
+ echo "Error: directory not found: $dir" >&2
116
+ exit 2
117
+ fi
118
+ find "$dir" -type f \( -name '*.md' -o -name '*.cjs' -o -name '*.js' -o -name '*.json' -o -name '*.yml' -o -name '*.yaml' -o -name '*.sh' \) \
119
+ ! -path '*/node_modules/*' ! -path '*/.git/*' ! -path '*/dist/*' 2>/dev/null || true
120
+ ;;
121
+ --stdin)
122
+ cat
123
+ ;;
124
+ *)
125
+ echo "Usage: $0 --diff [base] | --file <path> | --dir <path> | --stdin" >&2
126
+ exit 2
127
+ ;;
128
+ esac
129
+ }
130
+
131
+ # ─── Scanner ─────────────────────────────────────────────────────────────────
132
+
133
+ scan_file() {
134
+ local file="$1"
135
+ local found=0
136
+
137
+ if is_allowlisted "$file"; then
138
+ return 0
139
+ fi
140
+
141
+ for pattern in "${PATTERNS[@]}"; do
142
+ # Use grep -iE for case-insensitive extended regex
143
+ # -n for line numbers, -c for count mode first to check
144
+ local matches
145
+ matches=$(grep -inE -e "$pattern" "$file" 2>/dev/null || true)
146
+ if [[ -n "$matches" ]]; then
147
+ if [[ $found -eq 0 ]]; then
148
+ echo "FAIL: $file"
149
+ found=1
150
+ fi
151
+ echo "$matches" | while IFS= read -r line; do
152
+ echo " $line"
153
+ done
154
+ fi
155
+ done
156
+
157
+ return $found
158
+ }
159
+
160
+ # ─── Main ────────────────────────────────────────────────────────────────────
161
+
162
+ main() {
163
+ if [[ $# -eq 0 ]]; then
164
+ echo "Usage: $0 --diff [base] | --file <path> | --dir <path>" >&2
165
+ exit 2
166
+ fi
167
+
168
+ local mode="$1"
169
+ shift
170
+
171
+ local files
172
+ files=$(collect_files "$mode" "$@")
173
+
174
+ if [[ -z "$files" ]]; then
175
+ echo "prompt-injection-scan: no files to scan"
176
+ exit 0
177
+ fi
178
+
179
+ local total=0
180
+ local failed=0
181
+
182
+ while IFS= read -r file; do
183
+ [[ -z "$file" ]] && continue
184
+ total=$((total + 1))
185
+ if ! scan_file "$file"; then
186
+ failed=$((failed + 1))
187
+ fi
188
+ done <<< "$files"
189
+
190
+ echo ""
191
+ echo "prompt-injection-scan: scanned $total files, $failed with findings"
192
+
193
+ if [[ $failed -gt 0 ]]; then
194
+ exit 1
195
+ fi
196
+ exit 0
197
+ }
198
+
199
+ main "$@"
@@ -0,0 +1,181 @@
1
+ #!/usr/bin/env node
2
+ // @cap-history(sessions:3, edits:12, since:2026-04-20, learned:2026-05-06) Frequently modified — 3 sessions, 12 edits
3
+ // Cross-platform test runner — resolves test file globs via Node
4
+ // instead of relying on shell expansion (which fails on Windows PowerShell/cmd).
5
+ // Pass --coverage to enable Node's native experimental test coverage
6
+ // (c8 misses data from `node --test` isolation subprocesses on Node 22+).
7
+ 'use strict';
8
+
9
+ const { readdirSync } = require('fs');
10
+ const { join } = require('path');
11
+ const { execFileSync } = require('child_process');
12
+
13
+ const wantsCoverage = process.argv.includes('--coverage');
14
+ const testDir = join(__dirname, '..', 'tests');
15
+ const files = readdirSync(testDir)
16
+ .filter(f => f.endsWith('.test.cjs'))
17
+ .sort()
18
+ .map(f => join('tests', f));
19
+
20
+ if (files.length === 0) {
21
+ console.error('No test files found in tests/');
22
+ process.exit(1);
23
+ }
24
+
25
+ // The default TAP reporter swallows failure details under
26
+ // --experimental-test-isolation=none (only `# fail N` in the summary, no
27
+ // per-test `not ok` lines), which makes red CI unnecessarily opaque.
28
+ // The spec reporter emits explicit ✖ lines with the assertion error.
29
+ const nodeArgs = ['--test', '--test-reporter=spec'];
30
+ if (wantsCoverage) {
31
+ // Node >=22 defaults to process-per-file isolation. Coverage from those
32
+ // subprocesses is dropped by both c8 and the native reporter, so force
33
+ // single-process execution when measuring coverage. Plain `npm test` keeps
34
+ // the safer default isolation on purpose: it surfaces shared-state leaks
35
+ // (F-052 was found exactly this way), which isolation=none would hide.
36
+ // Flag-name history: `--experimental-test-isolation` landed in Node v22.8.0;
37
+ // the non-experimental `--test-isolation=...` form was stabilised in v23.x.
38
+ // CI runs on Node 22 reject the stabilised name with "bad option", so we use
39
+ // the experimental prefix — Node 23+ still accepts it for back-compat.
40
+ // Discovered during the 2026-04-21 F-054..F-059 batch: every feature PR
41
+ // merged with red CI because this mismatch failed the runner before a single
42
+ // test executed.
43
+ //
44
+ // @cap-decision(CI/issue-42) Path 1 rejected — DOUBLE-CONFIRMED.
45
+ //
46
+ // First measurement (2026-05-07, before Path 2): dropping
47
+ // --experimental-test-isolation=none cut wall-time 43x (1437s -> 33s) but
48
+ // collapsed line coverage 96.95% -> 56.31% (-40.64pp). Hypothesis: subprocess
49
+ // fixtures via `runGsdTools` (helpers.cjs:21) hide coverage from the native
50
+ // --experimental-test-coverage aggregator. Bridge fix PR #46 raised CI
51
+ // timeout 10->20 min. Path 2 plan: migrate fixtures to in-process module calls.
52
+ //
53
+ // Second measurement (2026-05-07, after Path 2 Phase 1-3 -- PRs #54-#61
54
+ // migrated 552 callsites across 14 files): re-ran the same Path 1 change.
55
+ // Wall-time 1298s -> 26s (49.9x faster); line coverage 97.40% -> 55.83%
56
+ // (-41.57pp). Per-hot-file deltas remain catastrophic (cap-feature-map.cjs
57
+ // -72.78, cap-feature-map-monorepo.cjs -71.19, cap-memory-migrate.cjs
58
+ // -70.26, cap-tag-scanner.cjs -61.23). The 552 migrations had near-zero
59
+ // effect on the coverage gap.
60
+ //
61
+ // Revised root-cause: NOT subprocess fixtures. Node 22's native
62
+ // --experimental-test-coverage aggregator does not merge coverage across
63
+ // test-file workers -- only the parent process is counted. Under per-file
64
+ // isolation each test file becomes its own worker; their coverage data is
65
+ // not surfaced. The gap is at the worker->parent boundary, NOT
66
+ // fixture->parent. Migrating runGsdTools to in-process moved work from
67
+ // grand-child fixtures into worker processes, which the aggregator still
68
+ // doesn't see.
69
+ //
70
+ // Phase 4 (migrate the remaining 248 callsites in 9 files: dispatcher,
71
+ // frontmatter-cli, init-manager, profile-pipeline, milestone, roadmap,
72
+ // template, verify, uat) WILL NOT close the gap -- those are CLI-dispatch
73
+ // tests, not the hot lib modules whose coverage collapses. Diagnosed
74
+ // empirically.
75
+ //
76
+ // Three real remediation paths considered:
77
+ // 1. Switch to c8/nyc (hypothesis: writes per-worker JSON, supports merge
78
+ // across workers). REJECTED — see @cap-decision below.
79
+ // 2. Upgrade CI to Node 23+ and switch to stable --test-isolation=process
80
+ // with v23's worker-coverage-merge fixes. ~0.5 day, needs verification.
81
+ // 3. Status quo: keep --experimental-test-isolation=none, ~21 min CI runs,
82
+ // 97.4% coverage. F-052-class race detection sacrificed by design here
83
+ // but covered by plain `npm test` worker isolation.
84
+ //
85
+ // @cap-decision(CI/issue-42 c8-also-rejected) Path 1 (c8 variant) REJECTED.
86
+ //
87
+ // Hypothesis from PR #62 follow-up: c8 instruments via a require()-hook at
88
+ // module-load time and writes per-worker JSON to coverage/tmp/, so it should
89
+ // fix the worker-aggregator gap that bit native --experimental-test-coverage.
90
+ // Empirically tested 2026-05-07 with c8 v10.1.3 + Node 24 + --test-isolation=
91
+ // process (the stabilised flag). Three configurations measured:
92
+ //
93
+ // A. native + --experimental-test-isolation=none .... 97.40% lines, 1240s
94
+ // (current production, unchanged since F-051)
95
+ // B. c8 + --test-isolation=process (default) .... 55.18% lines, 26.4s
96
+ // (the hypothesised win — REJECTED, same gap as native+Path 1's 55.83%)
97
+ // C. c8 + --experimental-test-isolation=none .... 97.39% lines, 1258s
98
+ // (parity check — confirms c8 matches native at the same isolation)
99
+ //
100
+ // Root cause of the c8 rejection: c8 v10 does NOT use a require()-hook. The
101
+ // hypothesis was wrong about how c8 works. c8 v10 just sets NODE_V8_COVERAGE
102
+ // before spawning the wrapped command and reads V8's per-process JSONs on
103
+ // exit — it is a thin wrapper around Node's *native* coverage mechanism.
104
+ // Forensic check on coverage/tmp/ after the c8+Path1 run: 308 worker JSONs
105
+ // were written but only 46 of 167 test files appeared in any of them. The
106
+ // missing 121 test files are alphabetically-early (agent-*, antigravity-*,
107
+ // arc-*, build-*, cap-affinity through cap-divergence, cap-feature-map.*,
108
+ // most cap-memory-*, cap-tag-*, etc). This is the SAME Node-level bug:
109
+ // --test-isolation=process pool workers spawn before NODE_V8_COVERAGE has
110
+ // had its on-exit handler armed, so early workers exit without writing.
111
+ // Switching tools cannot fix this — both tools sit downstream of the same
112
+ // V8/Node coverage mechanism.
113
+ //
114
+ // Phase 4 is still NOT necessary. The remaining remediation options are:
115
+ // - Node 23+ upgrade + stable --test-isolation=process with verified
116
+ // worker-coverage-merge (Remediation Path 2 above).
117
+ // - Persist current --experimental-test-isolation=none status quo.
118
+ // c8 was the original tool, removed in F-051 (commit 8cc51fd) under the same
119
+ // empirical evidence — that decision stands.
120
+ //
121
+ // Until Path 2 lands: the flag stays. The 552 in-process migrations from
122
+ // PRs #54-#61 remain net wins (faster `npm test` runs locally + cleaner test
123
+ // architecture) but neither they nor c8 close the coverage gap on Path 1.
124
+ //
125
+ // @cap-decision(CI/issue-42 quadruple-rejection) Path 1 also rejected on
126
+ // Node 22, 23, 25 — confirms the bug is structural, not version-specific.
127
+ //
128
+ // Empirical Node-version sweep (2026-05-07): tested Path 1 across all four
129
+ // current Node majors with both flag forms:
130
+ //
131
+ // Node 22.22.2 + Path 1 (--experimental-...=none removed) .. 55.22% lines, 23s
132
+ // Node 23.11.1 + Path 1 ........................................ 55.84% lines, 23s
133
+ // Node 24.15.0 + Path 1 ........................................ 55.11% lines, 26s
134
+ // Node 25.9.0 + Path 1 ........................................ 55.04% lines, 26s
135
+ // Node 23.11.1 + --test-isolation=process (stabilised flag) ... 55.43% lines, 26s
136
+ // Node 25.9.0 + --test-isolation=process (stabilised flag) ... 55.15% lines, 28s
137
+ //
138
+ // Hot-file numbers were BYTE-IDENTICAL across all 6 Path-1 runs. This is
139
+ // deterministic missing-aggregation, not flaky measurement. The bug is
140
+ // present in Node 22 through 25 and survives both flag forms.
141
+ //
142
+ // What this eliminates:
143
+ // - "Node 23+ has worker-coverage-merge fixes" — wrong, v23 has the bug.
144
+ // - "Upgrade fixes it" — wrong, v25 has the bug.
145
+ // - "Use the stabilised flag" — wrong, same bug under both forms.
146
+ //
147
+ // Final remediation set: Status quo only. The
148
+ // --experimental-test-isolation=none flag stays. ~21 min CI runs, ~97.4%
149
+ // coverage. F-052-class race detection sacrificed by design under coverage
150
+ // but covered by plain `npm test` worker-isolation. The 552 in-process
151
+ // migrations from PRs #54-#61 remain net wins for plain-test speed but did
152
+ // NOT enable Path 1.
153
+ //
154
+ // If a future investigator considers re-trying Path 1: don't. It has been
155
+ // rejected FOUR times with empirical data:
156
+ // 1. native pre-Path-2 (PR #62, ~40pp drop)
157
+ // 2. native post-Path-2 (PR #62, ~42pp drop, byte-identical hot files)
158
+ // 3. c8 v10 (PR #63, ~42pp drop, same hot files)
159
+ // 4. Node 22/23/24/25 sweep (this commit, ~42pp drop on every Node major)
160
+ //
161
+ // The actionable next step (NOT this commit, low urgency) is filing an
162
+ // upstream Node bug with a minimal repro showing alphabetically-early
163
+ // workers missing from the native coverage aggregator under per-file
164
+ // isolation. Until that lands and ships in some Node version: status quo.
165
+ nodeArgs.push(
166
+ '--experimental-test-isolation=none',
167
+ '--experimental-test-coverage',
168
+ '--test-coverage-lines=0.7',
169
+ '--test-coverage-include=cap/bin/lib/*.cjs'
170
+ );
171
+ }
172
+ nodeArgs.push(...files);
173
+
174
+ try {
175
+ execFileSync(process.execPath, nodeArgs, {
176
+ stdio: 'inherit',
177
+ env: { ...process.env },
178
+ });
179
+ } catch (err) {
180
+ process.exit(err.status || 1);
181
+ }
@@ -0,0 +1,227 @@
1
+ #!/usr/bin/env bash
2
+ # secret-scan.sh — Check files for accidentally committed secrets/credentials
3
+ #
4
+ # Usage:
5
+ # scripts/secret-scan.sh --diff origin/main # CI mode: scan changed files
6
+ # scripts/secret-scan.sh --file path/to/file # Scan a single file
7
+ # scripts/secret-scan.sh --dir agents/ # Scan all files in a directory
8
+ #
9
+ # Exit codes:
10
+ # 0 = clean
11
+ # 1 = findings detected
12
+ # 2 = usage error
13
+ set -euo pipefail
14
+
15
+ # ─── Secret Patterns ─────────────────────────────────────────────────────────
16
+ # Format: "LABEL:::REGEX"
17
+ # Each entry is a human label paired with a POSIX extended regex.
18
+
19
+ SECRET_PATTERNS=(
20
+ # AWS
21
+ "AWS Access Key:::AKIA[0-9A-Z]{16}"
22
+ "AWS Secret Key:::aws_secret_access_key[[:space:]]*=[[:space:]]*[A-Za-z0-9/+=]{40}"
23
+
24
+ # OpenAI / Anthropic / AI providers
25
+ "OpenAI API Key:::sk-[A-Za-z0-9]{20,}"
26
+ "Anthropic API Key:::sk-ant-[A-Za-z0-9_-]{20,}"
27
+
28
+ # GitHub
29
+ "GitHub PAT:::ghp_[A-Za-z0-9]{36}"
30
+ "GitHub OAuth:::gho_[A-Za-z0-9]{36}"
31
+ "GitHub App Token:::ghs_[A-Za-z0-9]{36}"
32
+ "GitHub Fine-grained PAT:::github_pat_[A-Za-z0-9_]{20,}"
33
+
34
+ # Stripe
35
+ "Stripe Secret Key:::sk_live_[A-Za-z0-9]{24,}"
36
+ "Stripe Publishable Key:::pk_live_[A-Za-z0-9]{24,}"
37
+
38
+ # Generic patterns
39
+ "Private Key Header:::-----BEGIN[[:space:]]+(RSA|EC|DSA|OPENSSH)?[[:space:]]*PRIVATE[[:space:]]+KEY-----"
40
+ "Generic API Key Assignment:::api[_-]?key[[:space:]]*[:=][[:space:]]*['\"][A-Za-z0-9_-]{20,}['\"]"
41
+ "Generic Secret Assignment:::secret[[:space:]]*[:=][[:space:]]*['\"][A-Za-z0-9_-]{20,}['\"]"
42
+ "Generic Token Assignment:::token[[:space:]]*[:=][[:space:]]*['\"][A-Za-z0-9_-]{20,}['\"]"
43
+ "Generic Password Assignment:::password[[:space:]]*[:=][[:space:]]*['\"][^'\"]{8,}['\"]"
44
+
45
+ # Slack
46
+ "Slack Bot Token:::xoxb-[0-9]{10,}-[A-Za-z0-9]{20,}"
47
+ "Slack Webhook:::hooks\.slack\.com/services/T[A-Z0-9]{8,}/B[A-Z0-9]{8,}/[A-Za-z0-9]{24}"
48
+
49
+ # Google
50
+ "Google API Key:::AIza[A-Za-z0-9_-]{35}"
51
+
52
+ # NPM
53
+ "NPM Token:::npm_[A-Za-z0-9]{36}"
54
+
55
+ # .env file content (key=value with sensitive-looking keys)
56
+ "Env Variable Leak:::(DATABASE_URL|DB_PASSWORD|REDIS_URL|MONGO_URI|JWT_SECRET|SESSION_SECRET|ENCRYPTION_KEY)[[:space:]]*=[[:space:]]*[^[:space:]]{8,}"
57
+ )
58
+
59
+ # ─── Ignorelist ──────────────────────────────────────────────────────────────
60
+
61
+ IGNOREFILE=".secretscanignore"
62
+ IGNORED_FILES=()
63
+
64
+ load_ignorelist() {
65
+ if [[ -f "$IGNOREFILE" ]]; then
66
+ while IFS= read -r line; do
67
+ [[ "$line" =~ ^[[:space:]]*# ]] && continue
68
+ [[ -z "${line// }" ]] && continue
69
+ IGNORED_FILES+=("$line")
70
+ done < "$IGNOREFILE"
71
+ fi
72
+ }
73
+
74
+ is_ignored() {
75
+ local file="$1"
76
+ if [[ ${#IGNORED_FILES[@]} -eq 0 ]]; then
77
+ return 1
78
+ fi
79
+ for pattern in "${IGNORED_FILES[@]}"; do
80
+ # Support glob-style matching
81
+ # shellcheck disable=SC2254
82
+ case "$file" in
83
+ $pattern) return 0 ;;
84
+ esac
85
+ done
86
+ return 1
87
+ }
88
+
89
+ # ─── Skip Rules ──────────────────────────────────────────────────────────────
90
+
91
+ should_skip_file() {
92
+ local file="$1"
93
+ # Skip binary files
94
+ case "$file" in
95
+ *.png|*.jpg|*.jpeg|*.gif|*.ico|*.woff|*.woff2|*.ttf|*.eot|*.otf) return 0 ;;
96
+ *.zip|*.tar|*.gz|*.bz2|*.xz|*.7z) return 0 ;;
97
+ *.pdf|*.doc|*.docx|*.xls|*.xlsx) return 0 ;;
98
+ esac
99
+ # Skip lockfiles and node_modules
100
+ case "$file" in
101
+ */node_modules/*) return 0 ;;
102
+ */package-lock.json) return 0 ;;
103
+ */yarn.lock) return 0 ;;
104
+ */pnpm-lock.yaml) return 0 ;;
105
+ esac
106
+ # Skip the scan scripts themselves and test files
107
+ case "$file" in
108
+ */secret-scan.sh) return 0 ;;
109
+ */security-scan.test.cjs) return 0 ;;
110
+ esac
111
+ return 1
112
+ }
113
+
114
+ # ─── File Collection ─────────────────────────────────────────────────────────
115
+
116
+ collect_files() {
117
+ local mode="$1"
118
+ shift
119
+
120
+ case "$mode" in
121
+ --diff)
122
+ local base="${1:-origin/main}"
123
+ git diff --name-only --diff-filter=ACMR "$base"...HEAD 2>/dev/null \
124
+ | grep -vE '\.(png|jpg|jpeg|gif|ico|woff|woff2|ttf|eot|otf|zip|tar|gz|pdf)$' || true
125
+ ;;
126
+ --file)
127
+ if [[ -f "$1" ]]; then
128
+ echo "$1"
129
+ else
130
+ echo "Error: file not found: $1" >&2
131
+ exit 2
132
+ fi
133
+ ;;
134
+ --dir)
135
+ local dir="$1"
136
+ if [[ ! -d "$dir" ]]; then
137
+ echo "Error: directory not found: $dir" >&2
138
+ exit 2
139
+ fi
140
+ find "$dir" -type f ! -path '*/node_modules/*' ! -path '*/.git/*' ! -path '*/dist/*' \
141
+ ! -name '*.png' ! -name '*.jpg' ! -name '*.gif' ! -name '*.woff*' 2>/dev/null || true
142
+ ;;
143
+ --stdin)
144
+ cat
145
+ ;;
146
+ *)
147
+ echo "Usage: $0 --diff [base] | --file <path> | --dir <path> | --stdin" >&2
148
+ exit 2
149
+ ;;
150
+ esac
151
+ }
152
+
153
+ # ─── Scanner ─────────────────────────────────────────────────────────────────
154
+
155
+ scan_file() {
156
+ local file="$1"
157
+ local found=0
158
+
159
+ if is_ignored "$file"; then
160
+ return 0
161
+ fi
162
+
163
+ for entry in "${SECRET_PATTERNS[@]}"; do
164
+ local label="${entry%%:::*}"
165
+ local pattern="${entry#*:::}"
166
+
167
+ local matches
168
+ matches=$(grep -nE -e "$pattern" "$file" 2>/dev/null || true)
169
+ if [[ -n "$matches" ]]; then
170
+ if [[ $found -eq 0 ]]; then
171
+ echo "FAIL: $file"
172
+ found=1
173
+ fi
174
+ echo "$matches" | while IFS= read -r line; do
175
+ echo " [$label] $line"
176
+ done
177
+ fi
178
+ done
179
+
180
+ return $found
181
+ }
182
+
183
+ # ─── Main ────────────────────────────────────────────────────────────────────
184
+
185
+ main() {
186
+ if [[ $# -eq 0 ]]; then
187
+ echo "Usage: $0 --diff [base] | --file <path> | --dir <path>" >&2
188
+ exit 2
189
+ fi
190
+
191
+ load_ignorelist
192
+
193
+ local mode="$1"
194
+ shift
195
+
196
+ local files
197
+ files=$(collect_files "$mode" "$@")
198
+
199
+ if [[ -z "$files" ]]; then
200
+ echo "secret-scan: no files to scan"
201
+ exit 0
202
+ fi
203
+
204
+ local total=0
205
+ local failed=0
206
+
207
+ while IFS= read -r file; do
208
+ [[ -z "$file" ]] && continue
209
+ if should_skip_file "$file"; then
210
+ continue
211
+ fi
212
+ total=$((total + 1))
213
+ if ! scan_file "$file"; then
214
+ failed=$((failed + 1))
215
+ fi
216
+ done <<< "$files"
217
+
218
+ echo ""
219
+ echo "secret-scan: scanned $total files, $failed with findings"
220
+
221
+ if [[ $failed -gt 0 ]]; then
222
+ exit 1
223
+ fi
224
+ exit 0
225
+ }
226
+
227
+ main "$@"