gsd-code-first 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 (238) hide show
  1. package/LICENSE +21 -0
  2. package/README.ja-JP.md +834 -0
  3. package/README.ko-KR.md +823 -0
  4. package/README.md +937 -0
  5. package/README.pt-BR.md +452 -0
  6. package/README.zh-CN.md +800 -0
  7. package/agents/gsd-advisor-researcher.md +104 -0
  8. package/agents/gsd-annotator.md +148 -0
  9. package/agents/gsd-arc-executor.md +537 -0
  10. package/agents/gsd-arc-planner.md +374 -0
  11. package/agents/gsd-assumptions-analyzer.md +105 -0
  12. package/agents/gsd-code-planner.md +155 -0
  13. package/agents/gsd-codebase-mapper.md +770 -0
  14. package/agents/gsd-debugger.md +1373 -0
  15. package/agents/gsd-executor.md +509 -0
  16. package/agents/gsd-integration-checker.md +443 -0
  17. package/agents/gsd-nyquist-auditor.md +176 -0
  18. package/agents/gsd-phase-researcher.md +698 -0
  19. package/agents/gsd-plan-checker.md +773 -0
  20. package/agents/gsd-planner.md +1354 -0
  21. package/agents/gsd-project-researcher.md +654 -0
  22. package/agents/gsd-prototyper.md +161 -0
  23. package/agents/gsd-research-synthesizer.md +247 -0
  24. package/agents/gsd-roadmapper.md +679 -0
  25. package/agents/gsd-ui-auditor.md +439 -0
  26. package/agents/gsd-ui-checker.md +300 -0
  27. package/agents/gsd-ui-researcher.md +357 -0
  28. package/agents/gsd-user-profiler.md +171 -0
  29. package/agents/gsd-verifier.md +700 -0
  30. package/bin/install.js +5009 -0
  31. package/commands/gsd/add-backlog.md +76 -0
  32. package/commands/gsd/add-phase.md +43 -0
  33. package/commands/gsd/add-tests.md +41 -0
  34. package/commands/gsd/add-todo.md +47 -0
  35. package/commands/gsd/annotate.md +54 -0
  36. package/commands/gsd/audit-milestone.md +36 -0
  37. package/commands/gsd/audit-uat.md +24 -0
  38. package/commands/gsd/autonomous.md +41 -0
  39. package/commands/gsd/check-todos.md +45 -0
  40. package/commands/gsd/cleanup.md +18 -0
  41. package/commands/gsd/complete-milestone.md +136 -0
  42. package/commands/gsd/debug.md +173 -0
  43. package/commands/gsd/deep-plan.md +52 -0
  44. package/commands/gsd/discuss-phase.md +64 -0
  45. package/commands/gsd/do.md +30 -0
  46. package/commands/gsd/execute-phase.md +59 -0
  47. package/commands/gsd/extract-plan.md +35 -0
  48. package/commands/gsd/fast.md +30 -0
  49. package/commands/gsd/forensics.md +56 -0
  50. package/commands/gsd/health.md +22 -0
  51. package/commands/gsd/help.md +22 -0
  52. package/commands/gsd/insert-phase.md +32 -0
  53. package/commands/gsd/iterate.md +124 -0
  54. package/commands/gsd/join-discord.md +18 -0
  55. package/commands/gsd/list-phase-assumptions.md +46 -0
  56. package/commands/gsd/list-workspaces.md +19 -0
  57. package/commands/gsd/manager.md +39 -0
  58. package/commands/gsd/map-codebase.md +71 -0
  59. package/commands/gsd/milestone-summary.md +51 -0
  60. package/commands/gsd/new-milestone.md +44 -0
  61. package/commands/gsd/new-project.md +42 -0
  62. package/commands/gsd/new-workspace.md +44 -0
  63. package/commands/gsd/next.md +24 -0
  64. package/commands/gsd/note.md +34 -0
  65. package/commands/gsd/pause-work.md +38 -0
  66. package/commands/gsd/plan-milestone-gaps.md +34 -0
  67. package/commands/gsd/plan-phase.md +47 -0
  68. package/commands/gsd/plant-seed.md +28 -0
  69. package/commands/gsd/pr-branch.md +25 -0
  70. package/commands/gsd/profile-user.md +46 -0
  71. package/commands/gsd/progress.md +24 -0
  72. package/commands/gsd/prototype.md +56 -0
  73. package/commands/gsd/quick.md +47 -0
  74. package/commands/gsd/reapply-patches.md +123 -0
  75. package/commands/gsd/remove-phase.md +31 -0
  76. package/commands/gsd/remove-workspace.md +26 -0
  77. package/commands/gsd/research-phase.md +195 -0
  78. package/commands/gsd/resume-work.md +40 -0
  79. package/commands/gsd/review-backlog.md +61 -0
  80. package/commands/gsd/review.md +37 -0
  81. package/commands/gsd/session-report.md +19 -0
  82. package/commands/gsd/set-mode.md +41 -0
  83. package/commands/gsd/set-profile.md +12 -0
  84. package/commands/gsd/settings.md +36 -0
  85. package/commands/gsd/ship.md +23 -0
  86. package/commands/gsd/stats.md +18 -0
  87. package/commands/gsd/thread.md +127 -0
  88. package/commands/gsd/ui-phase.md +34 -0
  89. package/commands/gsd/ui-review.md +32 -0
  90. package/commands/gsd/update.md +37 -0
  91. package/commands/gsd/validate-phase.md +35 -0
  92. package/commands/gsd/verify-work.md +38 -0
  93. package/commands/gsd/workstreams.md +63 -0
  94. package/get-shit-done/bin/gsd-tools.cjs +946 -0
  95. package/get-shit-done/bin/lib/arc-scanner.cjs +341 -0
  96. package/get-shit-done/bin/lib/commands.cjs +959 -0
  97. package/get-shit-done/bin/lib/config.cjs +466 -0
  98. package/get-shit-done/bin/lib/core.cjs +1230 -0
  99. package/get-shit-done/bin/lib/frontmatter.cjs +336 -0
  100. package/get-shit-done/bin/lib/init.cjs +1442 -0
  101. package/get-shit-done/bin/lib/milestone.cjs +252 -0
  102. package/get-shit-done/bin/lib/model-profiles.cjs +68 -0
  103. package/get-shit-done/bin/lib/phase.cjs +888 -0
  104. package/get-shit-done/bin/lib/profile-output.cjs +952 -0
  105. package/get-shit-done/bin/lib/profile-pipeline.cjs +539 -0
  106. package/get-shit-done/bin/lib/roadmap.cjs +329 -0
  107. package/get-shit-done/bin/lib/security.cjs +382 -0
  108. package/get-shit-done/bin/lib/state.cjs +1031 -0
  109. package/get-shit-done/bin/lib/template.cjs +222 -0
  110. package/get-shit-done/bin/lib/uat.cjs +282 -0
  111. package/get-shit-done/bin/lib/verify.cjs +888 -0
  112. package/get-shit-done/bin/lib/workstream.cjs +491 -0
  113. package/get-shit-done/commands/gsd/workstreams.md +63 -0
  114. package/get-shit-done/references/arc-standard.md +315 -0
  115. package/get-shit-done/references/checkpoints.md +778 -0
  116. package/get-shit-done/references/continuation-format.md +249 -0
  117. package/get-shit-done/references/decimal-phase-calculation.md +64 -0
  118. package/get-shit-done/references/git-integration.md +295 -0
  119. package/get-shit-done/references/git-planning-commit.md +38 -0
  120. package/get-shit-done/references/model-profile-resolution.md +36 -0
  121. package/get-shit-done/references/model-profiles.md +139 -0
  122. package/get-shit-done/references/phase-argument-parsing.md +61 -0
  123. package/get-shit-done/references/planning-config.md +202 -0
  124. package/get-shit-done/references/questioning.md +162 -0
  125. package/get-shit-done/references/tdd.md +263 -0
  126. package/get-shit-done/references/ui-brand.md +160 -0
  127. package/get-shit-done/references/user-profiling.md +681 -0
  128. package/get-shit-done/references/verification-patterns.md +612 -0
  129. package/get-shit-done/references/workstream-flag.md +58 -0
  130. package/get-shit-done/templates/DEBUG.md +164 -0
  131. package/get-shit-done/templates/UAT.md +265 -0
  132. package/get-shit-done/templates/UI-SPEC.md +100 -0
  133. package/get-shit-done/templates/VALIDATION.md +76 -0
  134. package/get-shit-done/templates/claude-md.md +122 -0
  135. package/get-shit-done/templates/codebase/architecture.md +255 -0
  136. package/get-shit-done/templates/codebase/concerns.md +310 -0
  137. package/get-shit-done/templates/codebase/conventions.md +307 -0
  138. package/get-shit-done/templates/codebase/integrations.md +280 -0
  139. package/get-shit-done/templates/codebase/stack.md +186 -0
  140. package/get-shit-done/templates/codebase/structure.md +285 -0
  141. package/get-shit-done/templates/codebase/testing.md +480 -0
  142. package/get-shit-done/templates/config.json +44 -0
  143. package/get-shit-done/templates/context.md +352 -0
  144. package/get-shit-done/templates/continue-here.md +78 -0
  145. package/get-shit-done/templates/copilot-instructions.md +7 -0
  146. package/get-shit-done/templates/debug-subagent-prompt.md +91 -0
  147. package/get-shit-done/templates/dev-preferences.md +21 -0
  148. package/get-shit-done/templates/discovery.md +146 -0
  149. package/get-shit-done/templates/discussion-log.md +63 -0
  150. package/get-shit-done/templates/milestone-archive.md +123 -0
  151. package/get-shit-done/templates/milestone.md +115 -0
  152. package/get-shit-done/templates/phase-prompt.md +610 -0
  153. package/get-shit-done/templates/planner-subagent-prompt.md +117 -0
  154. package/get-shit-done/templates/project.md +186 -0
  155. package/get-shit-done/templates/requirements.md +231 -0
  156. package/get-shit-done/templates/research-project/ARCHITECTURE.md +204 -0
  157. package/get-shit-done/templates/research-project/FEATURES.md +147 -0
  158. package/get-shit-done/templates/research-project/PITFALLS.md +200 -0
  159. package/get-shit-done/templates/research-project/STACK.md +120 -0
  160. package/get-shit-done/templates/research-project/SUMMARY.md +170 -0
  161. package/get-shit-done/templates/research.md +552 -0
  162. package/get-shit-done/templates/retrospective.md +54 -0
  163. package/get-shit-done/templates/roadmap.md +202 -0
  164. package/get-shit-done/templates/state.md +176 -0
  165. package/get-shit-done/templates/summary-complex.md +59 -0
  166. package/get-shit-done/templates/summary-minimal.md +41 -0
  167. package/get-shit-done/templates/summary-standard.md +48 -0
  168. package/get-shit-done/templates/summary.md +248 -0
  169. package/get-shit-done/templates/user-profile.md +146 -0
  170. package/get-shit-done/templates/user-setup.md +311 -0
  171. package/get-shit-done/templates/verification-report.md +322 -0
  172. package/get-shit-done/workflows/add-phase.md +112 -0
  173. package/get-shit-done/workflows/add-tests.md +351 -0
  174. package/get-shit-done/workflows/add-todo.md +158 -0
  175. package/get-shit-done/workflows/audit-milestone.md +340 -0
  176. package/get-shit-done/workflows/audit-uat.md +109 -0
  177. package/get-shit-done/workflows/autonomous.md +891 -0
  178. package/get-shit-done/workflows/check-todos.md +177 -0
  179. package/get-shit-done/workflows/cleanup.md +152 -0
  180. package/get-shit-done/workflows/complete-milestone.md +767 -0
  181. package/get-shit-done/workflows/diagnose-issues.md +231 -0
  182. package/get-shit-done/workflows/discovery-phase.md +289 -0
  183. package/get-shit-done/workflows/discuss-phase-assumptions.md +653 -0
  184. package/get-shit-done/workflows/discuss-phase.md +1049 -0
  185. package/get-shit-done/workflows/do.md +104 -0
  186. package/get-shit-done/workflows/execute-phase.md +846 -0
  187. package/get-shit-done/workflows/execute-plan.md +514 -0
  188. package/get-shit-done/workflows/fast.md +105 -0
  189. package/get-shit-done/workflows/forensics.md +265 -0
  190. package/get-shit-done/workflows/health.md +181 -0
  191. package/get-shit-done/workflows/help.md +634 -0
  192. package/get-shit-done/workflows/insert-phase.md +130 -0
  193. package/get-shit-done/workflows/list-phase-assumptions.md +178 -0
  194. package/get-shit-done/workflows/list-workspaces.md +56 -0
  195. package/get-shit-done/workflows/manager.md +362 -0
  196. package/get-shit-done/workflows/map-codebase.md +377 -0
  197. package/get-shit-done/workflows/milestone-summary.md +223 -0
  198. package/get-shit-done/workflows/new-milestone.md +486 -0
  199. package/get-shit-done/workflows/new-project.md +1250 -0
  200. package/get-shit-done/workflows/new-workspace.md +237 -0
  201. package/get-shit-done/workflows/next.md +97 -0
  202. package/get-shit-done/workflows/node-repair.md +92 -0
  203. package/get-shit-done/workflows/note.md +156 -0
  204. package/get-shit-done/workflows/pause-work.md +176 -0
  205. package/get-shit-done/workflows/plan-milestone-gaps.md +273 -0
  206. package/get-shit-done/workflows/plan-phase.md +859 -0
  207. package/get-shit-done/workflows/plant-seed.md +169 -0
  208. package/get-shit-done/workflows/pr-branch.md +129 -0
  209. package/get-shit-done/workflows/profile-user.md +450 -0
  210. package/get-shit-done/workflows/progress.md +507 -0
  211. package/get-shit-done/workflows/quick.md +757 -0
  212. package/get-shit-done/workflows/remove-phase.md +155 -0
  213. package/get-shit-done/workflows/remove-workspace.md +90 -0
  214. package/get-shit-done/workflows/research-phase.md +82 -0
  215. package/get-shit-done/workflows/resume-project.md +326 -0
  216. package/get-shit-done/workflows/review.md +228 -0
  217. package/get-shit-done/workflows/session-report.md +146 -0
  218. package/get-shit-done/workflows/settings.md +283 -0
  219. package/get-shit-done/workflows/ship.md +228 -0
  220. package/get-shit-done/workflows/stats.md +60 -0
  221. package/get-shit-done/workflows/transition.md +671 -0
  222. package/get-shit-done/workflows/ui-phase.md +302 -0
  223. package/get-shit-done/workflows/ui-review.md +165 -0
  224. package/get-shit-done/workflows/update.md +323 -0
  225. package/get-shit-done/workflows/validate-phase.md +174 -0
  226. package/get-shit-done/workflows/verify-phase.md +254 -0
  227. package/get-shit-done/workflows/verify-work.md +637 -0
  228. package/hooks/dist/gsd-check-update.js +114 -0
  229. package/hooks/dist/gsd-context-monitor.js +156 -0
  230. package/hooks/dist/gsd-prompt-guard.js +96 -0
  231. package/hooks/dist/gsd-statusline.js +119 -0
  232. package/hooks/dist/gsd-workflow-guard.js +94 -0
  233. package/package.json +52 -0
  234. package/scripts/base64-scan.sh +262 -0
  235. package/scripts/build-hooks.js +82 -0
  236. package/scripts/prompt-injection-scan.sh +198 -0
  237. package/scripts/run-tests.cjs +29 -0
  238. package/scripts/secret-scan.sh +227 -0
@@ -0,0 +1,262 @@
1
+ #!/usr/bin/env bash
2
+ # base64-scan.sh — Detect base64-obfuscated prompt injection in source files
3
+ #
4
+ # Extracts base64 blobs >= 40 chars, decodes them, and checks decoded content
5
+ # against the same injection patterns used by prompt-injection-scan.sh.
6
+ #
7
+ # Usage:
8
+ # scripts/base64-scan.sh --diff origin/main # CI mode: scan changed files
9
+ # scripts/base64-scan.sh --file path/to/file # Scan a single file
10
+ # scripts/base64-scan.sh --dir agents/ # Scan all files in a directory
11
+ #
12
+ # Exit codes:
13
+ # 0 = clean
14
+ # 1 = findings detected
15
+ # 2 = usage error
16
+ set -euo pipefail
17
+
18
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
19
+ MIN_BLOB_LENGTH=40
20
+
21
+ # ─── Injection Patterns (decoded content) ────────────────────────────────────
22
+ # Subset of patterns — if someone base64-encoded something, check for the
23
+ # most common injection indicators.
24
+ DECODED_PATTERNS=(
25
+ 'ignore[[:space:]]+(all[[:space:]]+)?previous[[:space:]]+instructions'
26
+ 'you[[:space:]]+are[[:space:]]+now[[:space:]]+'
27
+ 'system[[:space:]]+prompt'
28
+ '</?system>'
29
+ '</?assistant>'
30
+ '\[SYSTEM\]'
31
+ '\[INST\]'
32
+ '<<SYS>>'
33
+ 'override[[:space:]]+(system|safety|security)'
34
+ 'pretend[[:space:]]+(you|to)[[:space:]]'
35
+ 'act[[:space:]]+as[[:space:]]+(a|an|if)'
36
+ 'jailbreak'
37
+ 'bypass[[:space:]]+(safety|content|security)'
38
+ 'eval[[:space:]]*\('
39
+ 'exec[[:space:]]*\('
40
+ 'rm[[:space:]]+-rf'
41
+ 'curl[[:space:]].*\|[[:space:]]*sh'
42
+ 'wget[[:space:]].*\|[[:space:]]*sh'
43
+ )
44
+
45
+ # ─── Ignorelist ──────────────────────────────────────────────────────────────
46
+
47
+ IGNOREFILE=".base64scanignore"
48
+ IGNORED_PATTERNS=()
49
+
50
+ load_ignorelist() {
51
+ if [[ -f "$IGNOREFILE" ]]; then
52
+ while IFS= read -r line; do
53
+ # Skip comments and empty lines
54
+ [[ "$line" =~ ^[[:space:]]*# ]] && continue
55
+ [[ -z "${line// }" ]] && continue
56
+ IGNORED_PATTERNS+=("$line")
57
+ done < "$IGNOREFILE"
58
+ fi
59
+ }
60
+
61
+ is_ignored() {
62
+ local blob="$1"
63
+ if [[ ${#IGNORED_PATTERNS[@]} -eq 0 ]]; then
64
+ return 1
65
+ fi
66
+ for pattern in "${IGNORED_PATTERNS[@]}"; do
67
+ if [[ "$blob" == "$pattern" ]]; then
68
+ return 0
69
+ fi
70
+ done
71
+ return 1
72
+ }
73
+
74
+ # ─── Skip Rules ──────────────────────────────────────────────────────────────
75
+
76
+ should_skip_file() {
77
+ local file="$1"
78
+ # Skip binary files
79
+ case "$file" in
80
+ *.png|*.jpg|*.jpeg|*.gif|*.ico|*.woff|*.woff2|*.ttf|*.eot|*.otf) return 0 ;;
81
+ *.zip|*.tar|*.gz|*.bz2|*.xz|*.7z) return 0 ;;
82
+ *.pdf|*.doc|*.docx|*.xls|*.xlsx) return 0 ;;
83
+ esac
84
+ # Skip lockfiles and node_modules
85
+ case "$file" in
86
+ */node_modules/*) return 0 ;;
87
+ */package-lock.json) return 0 ;;
88
+ */yarn.lock) return 0 ;;
89
+ */pnpm-lock.yaml) return 0 ;;
90
+ esac
91
+ # Skip the scan scripts themselves and test files
92
+ case "$file" in
93
+ */base64-scan.sh) return 0 ;;
94
+ */security-scan.test.cjs) return 0 ;;
95
+ esac
96
+ return 1
97
+ }
98
+
99
+ is_data_uri() {
100
+ local context="$1"
101
+ # data:image/png;base64,... or data:application/font-woff;base64,...
102
+ echo "$context" | grep -qE 'data:[a-zA-Z]+/[a-zA-Z0-9.+-]+;base64,' 2>/dev/null
103
+ }
104
+
105
+ # ─── File Collection ─────────────────────────────────────────────────────────
106
+
107
+ collect_files() {
108
+ local mode="$1"
109
+ shift
110
+
111
+ case "$mode" in
112
+ --diff)
113
+ local base="${1:-origin/main}"
114
+ git diff --name-only --diff-filter=ACMR "$base"...HEAD 2>/dev/null \
115
+ | grep -vE '\.(png|jpg|jpeg|gif|ico|woff|woff2|ttf|eot|otf|zip|tar|gz|pdf)$' || true
116
+ ;;
117
+ --file)
118
+ if [[ -f "$1" ]]; then
119
+ echo "$1"
120
+ else
121
+ echo "Error: file not found: $1" >&2
122
+ exit 2
123
+ fi
124
+ ;;
125
+ --dir)
126
+ local dir="$1"
127
+ if [[ ! -d "$dir" ]]; then
128
+ echo "Error: directory not found: $dir" >&2
129
+ exit 2
130
+ fi
131
+ find "$dir" -type f ! -path '*/node_modules/*' ! -path '*/.git/*' ! -path '*/dist/*' \
132
+ ! -name '*.png' ! -name '*.jpg' ! -name '*.gif' ! -name '*.woff*' 2>/dev/null || true
133
+ ;;
134
+ --stdin)
135
+ cat
136
+ ;;
137
+ *)
138
+ echo "Usage: $0 --diff [base] | --file <path> | --dir <path> | --stdin" >&2
139
+ exit 2
140
+ ;;
141
+ esac
142
+ }
143
+
144
+ # ─── Scanner ─────────────────────────────────────────────────────────────────
145
+
146
+ extract_and_check_blobs() {
147
+ local file="$1"
148
+ local found=0
149
+ local line_num=0
150
+
151
+ while IFS= read -r line; do
152
+ line_num=$((line_num + 1))
153
+
154
+ # Skip data URIs — legitimate base64 usage
155
+ if is_data_uri "$line"; then
156
+ continue
157
+ fi
158
+
159
+ # Extract base64-like blobs (alphanumeric + / + = padding, >= MIN_BLOB_LENGTH)
160
+ local blobs
161
+ blobs=$(echo "$line" | grep -oE '[A-Za-z0-9+/]{'"$MIN_BLOB_LENGTH"',}={0,3}' 2>/dev/null || true)
162
+
163
+ if [[ -z "$blobs" ]]; then
164
+ continue
165
+ fi
166
+
167
+ while IFS= read -r blob; do
168
+ [[ -z "$blob" ]] && continue
169
+
170
+ # Check ignorelist
171
+ if [[ ${#IGNORED_PATTERNS[@]} -gt 0 ]] && is_ignored "$blob"; then
172
+ continue
173
+ fi
174
+
175
+ # Try to decode — if it fails, not valid base64
176
+ local decoded
177
+ decoded=$(echo "$blob" | base64 -d 2>/dev/null || echo "")
178
+
179
+ if [[ -z "$decoded" ]]; then
180
+ continue
181
+ fi
182
+
183
+ # Check if decoded content is mostly printable text (not random binary)
184
+ local printable_ratio
185
+ local total_chars=${#decoded}
186
+ if [[ $total_chars -eq 0 ]]; then
187
+ continue
188
+ fi
189
+
190
+ # Count printable ASCII characters
191
+ local printable_count
192
+ printable_count=$(echo -n "$decoded" | tr -cd '[:print:]' | wc -c | tr -d ' ')
193
+ # Skip if less than 70% printable (likely binary data, not obfuscated text)
194
+ if [[ $((printable_count * 100 / total_chars)) -lt 70 ]]; then
195
+ continue
196
+ fi
197
+
198
+ # Scan decoded content against injection patterns
199
+ for pattern in "${DECODED_PATTERNS[@]}"; do
200
+ if echo "$decoded" | grep -iqE "$pattern" 2>/dev/null; then
201
+ if [[ $found -eq 0 ]]; then
202
+ echo "FAIL: $file"
203
+ found=1
204
+ fi
205
+ echo " line $line_num: base64 blob decodes to suspicious content"
206
+ echo " blob: ${blob:0:60}..."
207
+ echo " decoded: ${decoded:0:120}"
208
+ echo " matched: $pattern"
209
+ break
210
+ fi
211
+ done
212
+ done <<< "$blobs"
213
+ done < "$file"
214
+
215
+ return $found
216
+ }
217
+
218
+ # ─── Main ────────────────────────────────────────────────────────────────────
219
+
220
+ main() {
221
+ if [[ $# -eq 0 ]]; then
222
+ echo "Usage: $0 --diff [base] | --file <path> | --dir <path>" >&2
223
+ exit 2
224
+ fi
225
+
226
+ load_ignorelist
227
+
228
+ local mode="$1"
229
+ shift
230
+
231
+ local files
232
+ files=$(collect_files "$mode" "$@")
233
+
234
+ if [[ -z "$files" ]]; then
235
+ echo "base64-scan: no files to scan"
236
+ exit 0
237
+ fi
238
+
239
+ local total=0
240
+ local failed=0
241
+
242
+ while IFS= read -r file; do
243
+ [[ -z "$file" ]] && continue
244
+ if should_skip_file "$file"; then
245
+ continue
246
+ fi
247
+ total=$((total + 1))
248
+ if ! extract_and_check_blobs "$file"; then
249
+ failed=$((failed + 1))
250
+ fi
251
+ done <<< "$files"
252
+
253
+ echo ""
254
+ echo "base64-scan: scanned $total files, $failed with findings"
255
+
256
+ if [[ $failed -gt 0 ]]; then
257
+ exit 1
258
+ fi
259
+ exit 0
260
+ }
261
+
262
+ main "$@"
@@ -0,0 +1,82 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Copy GSD hooks to dist for installation.
4
+ * Validates JavaScript syntax before copying to prevent shipping broken hooks.
5
+ * See #1107, #1109, #1125, #1161 — a duplicate const declaration shipped
6
+ * in dist and caused PostToolUse hook errors for all users.
7
+ */
8
+
9
+ const fs = require('fs');
10
+ const path = require('path');
11
+ const vm = require('vm');
12
+
13
+ const HOOKS_DIR = path.join(__dirname, '..', 'hooks');
14
+ const DIST_DIR = path.join(HOOKS_DIR, 'dist');
15
+
16
+ // Hooks to copy (pure Node.js, no bundling needed)
17
+ const HOOKS_TO_COPY = [
18
+ 'gsd-check-update.js',
19
+ 'gsd-context-monitor.js',
20
+ 'gsd-prompt-guard.js',
21
+ 'gsd-statusline.js',
22
+ 'gsd-workflow-guard.js'
23
+ ];
24
+
25
+ /**
26
+ * Validate JavaScript syntax without executing the file.
27
+ * Catches SyntaxError (duplicate const, missing brackets, etc.)
28
+ * before the hook gets shipped to users.
29
+ */
30
+ function validateSyntax(filePath) {
31
+ const content = fs.readFileSync(filePath, 'utf8');
32
+ try {
33
+ // Use vm.compileFunction to check syntax without executing
34
+ new vm.Script(content, { filename: path.basename(filePath) });
35
+ return null; // No error
36
+ } catch (e) {
37
+ if (e instanceof SyntaxError) {
38
+ return e.message;
39
+ }
40
+ throw e;
41
+ }
42
+ }
43
+
44
+ function build() {
45
+ // Ensure dist directory exists
46
+ if (!fs.existsSync(DIST_DIR)) {
47
+ fs.mkdirSync(DIST_DIR, { recursive: true });
48
+ }
49
+
50
+ let hasErrors = false;
51
+
52
+ // Copy hooks to dist with syntax validation
53
+ for (const hook of HOOKS_TO_COPY) {
54
+ const src = path.join(HOOKS_DIR, hook);
55
+ const dest = path.join(DIST_DIR, hook);
56
+
57
+ if (!fs.existsSync(src)) {
58
+ console.warn(`Warning: ${hook} not found, skipping`);
59
+ continue;
60
+ }
61
+
62
+ // Validate syntax before copying
63
+ const syntaxError = validateSyntax(src);
64
+ if (syntaxError) {
65
+ console.error(`\x1b[31m✗ ${hook}: SyntaxError — ${syntaxError}\x1b[0m`);
66
+ hasErrors = true;
67
+ continue;
68
+ }
69
+
70
+ console.log(`\x1b[32m✓\x1b[0m Copying ${hook}...`);
71
+ fs.copyFileSync(src, dest);
72
+ }
73
+
74
+ if (hasErrors) {
75
+ console.error('\n\x1b[31mBuild failed: fix syntax errors above before publishing.\x1b[0m');
76
+ process.exit(1);
77
+ }
78
+
79
+ console.log('\nBuild complete.');
80
+ }
81
+
82
+ build();
@@ -0,0 +1,198 @@
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
+ 'get-shit-done/bin/lib/security.cjs'
76
+ 'hooks/gsd-prompt-guard.js'
77
+ 'SECURITY.md'
78
+ )
79
+
80
+ is_allowlisted() {
81
+ local file="$1"
82
+ for allowed in "${ALLOWLIST[@]}"; do
83
+ if [[ "$file" == *"$allowed" ]]; then
84
+ return 0
85
+ fi
86
+ done
87
+ return 1
88
+ }
89
+
90
+ # ─── File Collection ─────────────────────────────────────────────────────────
91
+
92
+ collect_files() {
93
+ local mode="$1"
94
+ shift
95
+
96
+ case "$mode" in
97
+ --diff)
98
+ local base="${1:-origin/main}"
99
+ # Get changed files in the diff, filter to scannable extensions
100
+ git diff --name-only --diff-filter=ACMR "$base"...HEAD 2>/dev/null \
101
+ | grep -E '\.(md|cjs|js|json|yml|yaml|sh)$' || true
102
+ ;;
103
+ --file)
104
+ if [[ -f "$1" ]]; then
105
+ echo "$1"
106
+ else
107
+ echo "Error: file not found: $1" >&2
108
+ exit 2
109
+ fi
110
+ ;;
111
+ --dir)
112
+ local dir="$1"
113
+ if [[ ! -d "$dir" ]]; then
114
+ echo "Error: directory not found: $dir" >&2
115
+ exit 2
116
+ fi
117
+ find "$dir" -type f \( -name '*.md' -o -name '*.cjs' -o -name '*.js' -o -name '*.json' -o -name '*.yml' -o -name '*.yaml' -o -name '*.sh' \) \
118
+ ! -path '*/node_modules/*' ! -path '*/.git/*' ! -path '*/dist/*' 2>/dev/null || true
119
+ ;;
120
+ --stdin)
121
+ cat
122
+ ;;
123
+ *)
124
+ echo "Usage: $0 --diff [base] | --file <path> | --dir <path> | --stdin" >&2
125
+ exit 2
126
+ ;;
127
+ esac
128
+ }
129
+
130
+ # ─── Scanner ─────────────────────────────────────────────────────────────────
131
+
132
+ scan_file() {
133
+ local file="$1"
134
+ local found=0
135
+
136
+ if is_allowlisted "$file"; then
137
+ return 0
138
+ fi
139
+
140
+ for pattern in "${PATTERNS[@]}"; do
141
+ # Use grep -iE for case-insensitive extended regex
142
+ # -n for line numbers, -c for count mode first to check
143
+ local matches
144
+ matches=$(grep -inE -e "$pattern" "$file" 2>/dev/null || true)
145
+ if [[ -n "$matches" ]]; then
146
+ if [[ $found -eq 0 ]]; then
147
+ echo "FAIL: $file"
148
+ found=1
149
+ fi
150
+ echo "$matches" | while IFS= read -r line; do
151
+ echo " $line"
152
+ done
153
+ fi
154
+ done
155
+
156
+ return $found
157
+ }
158
+
159
+ # ─── Main ────────────────────────────────────────────────────────────────────
160
+
161
+ main() {
162
+ if [[ $# -eq 0 ]]; then
163
+ echo "Usage: $0 --diff [base] | --file <path> | --dir <path>" >&2
164
+ exit 2
165
+ fi
166
+
167
+ local mode="$1"
168
+ shift
169
+
170
+ local files
171
+ files=$(collect_files "$mode" "$@")
172
+
173
+ if [[ -z "$files" ]]; then
174
+ echo "prompt-injection-scan: no files to scan"
175
+ exit 0
176
+ fi
177
+
178
+ local total=0
179
+ local failed=0
180
+
181
+ while IFS= read -r file; do
182
+ [[ -z "$file" ]] && continue
183
+ total=$((total + 1))
184
+ if ! scan_file "$file"; then
185
+ failed=$((failed + 1))
186
+ fi
187
+ done <<< "$files"
188
+
189
+ echo ""
190
+ echo "prompt-injection-scan: scanned $total files, $failed with findings"
191
+
192
+ if [[ $failed -gt 0 ]]; then
193
+ exit 1
194
+ fi
195
+ exit 0
196
+ }
197
+
198
+ main "$@"
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env node
2
+ // Cross-platform test runner — resolves test file globs via Node
3
+ // instead of relying on shell expansion (which fails on Windows PowerShell/cmd).
4
+ // Propagates NODE_V8_COVERAGE so c8 collects coverage from the child process.
5
+ 'use strict';
6
+
7
+ const { readdirSync } = require('fs');
8
+ const { join } = require('path');
9
+ const { execFileSync } = require('child_process');
10
+
11
+ const testDir = join(__dirname, '..', 'tests');
12
+ const files = readdirSync(testDir)
13
+ .filter(f => f.endsWith('.test.cjs'))
14
+ .sort()
15
+ .map(f => join('tests', f));
16
+
17
+ if (files.length === 0) {
18
+ console.error('No test files found in tests/');
19
+ process.exit(1);
20
+ }
21
+
22
+ try {
23
+ execFileSync(process.execPath, ['--test', ...files], {
24
+ stdio: 'inherit',
25
+ env: { ...process.env },
26
+ });
27
+ } catch (err) {
28
+ process.exit(err.status || 1);
29
+ }