@givolt/i3-composer-agent 0.0.0-stage → 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 (231) hide show
  1. package/.agents/plugins/marketplace.json +23 -0
  2. package/.claude/commands/build.md +44 -0
  3. package/.claude/commands/code-simplify.md +22 -0
  4. package/.claude/commands/constraints.md +32 -0
  5. package/.claude/commands/plan.md +18 -0
  6. package/.claude/commands/review.md +16 -0
  7. package/.claude/commands/ship.md +72 -0
  8. package/.claude/commands/spec.md +17 -0
  9. package/.claude/commands/test.md +19 -0
  10. package/.claude/commands/webperf.md +32 -0
  11. package/.claude-plugin/marketplace.json +27 -0
  12. package/.claude-plugin/plugin.json +19 -0
  13. package/.codex-plugin/plugin.json +28 -0
  14. package/.gemini/commands/build.toml +43 -0
  15. package/.gemini/commands/code-simplify.toml +22 -0
  16. package/.gemini/commands/constraints.toml +32 -0
  17. package/.gemini/commands/planning.toml +18 -0
  18. package/.gemini/commands/review.toml +16 -0
  19. package/.gemini/commands/ship.toml +72 -0
  20. package/.gemini/commands/spec.toml +17 -0
  21. package/.gemini/commands/test.toml +19 -0
  22. package/.gemini/commands/webperf.toml +32 -0
  23. package/CHANGELOG.md +23 -0
  24. package/LICENSE +21 -0
  25. package/NOTICE.md +9 -0
  26. package/README.md +117 -2
  27. package/agents/code-reviewer.md +102 -0
  28. package/agents/security-auditor.md +112 -0
  29. package/agents/test-engineer.md +95 -0
  30. package/agents/web-performance-auditor.md +184 -0
  31. package/bin/i3-composer-agent.cjs +122 -0
  32. package/commands/build.toml +43 -0
  33. package/commands/code-simplify.toml +22 -0
  34. package/commands/constraints.toml +32 -0
  35. package/commands/planning.toml +18 -0
  36. package/commands/review.toml +16 -0
  37. package/commands/ship.toml +72 -0
  38. package/commands/spec.toml +17 -0
  39. package/commands/test.toml +19 -0
  40. package/commands/webperf.toml +32 -0
  41. package/docs/adoption-guide.md +129 -0
  42. package/docs/advanced-per-agent-configuration.md +70 -0
  43. package/docs/agents.md +123 -0
  44. package/docs/antigravity-setup.md +128 -0
  45. package/docs/codebuddy-setup.md +41 -0
  46. package/docs/codex-setup.md +34 -0
  47. package/docs/commandcode-setup.md +69 -0
  48. package/docs/comparison.md +131 -0
  49. package/docs/copilot-cli-setup.md +64 -0
  50. package/docs/copilot-setup.md +162 -0
  51. package/docs/cursor-setup.md +225 -0
  52. package/docs/developer-onboarding.md +116 -0
  53. package/docs/gemini-cli-setup.md +133 -0
  54. package/docs/getting-started.md +208 -0
  55. package/docs/opencode-setup.md +259 -0
  56. package/docs/other-hosts.md +7 -0
  57. package/docs/skill-anatomy.md +203 -0
  58. package/docs/windsurf-setup.md +48 -0
  59. package/evals/README.md +101 -0
  60. package/evals/cases/api-and-interface-design.json +45 -0
  61. package/evals/cases/browser-testing-with-devtools.json +44 -0
  62. package/evals/cases/ci-cd-and-automation.json +45 -0
  63. package/evals/cases/code-review-and-quality.json +49 -0
  64. package/evals/cases/code-simplification.json +45 -0
  65. package/evals/cases/constraint-driven-development.json +79 -0
  66. package/evals/cases/context-engineering.json +44 -0
  67. package/evals/cases/debugging-and-error-recovery.json +58 -0
  68. package/evals/cases/deprecation-and-migration.json +52 -0
  69. package/evals/cases/documentation-and-adrs.json +44 -0
  70. package/evals/cases/doubt-driven-development.json +43 -0
  71. package/evals/cases/frontend-ui-engineering.json +52 -0
  72. package/evals/cases/git-workflow-and-versioning.json +47 -0
  73. package/evals/cases/idea-refine.json +43 -0
  74. package/evals/cases/incremental-implementation.json +57 -0
  75. package/evals/cases/interview-me.json +42 -0
  76. package/evals/cases/observability-and-instrumentation.json +44 -0
  77. package/evals/cases/performance-optimization.json +53 -0
  78. package/evals/cases/planning-and-task-breakdown.json +45 -0
  79. package/evals/cases/security-and-hardening.json +53 -0
  80. package/evals/cases/shipping-and-launch.json +56 -0
  81. package/evals/cases/source-driven-development.json +44 -0
  82. package/evals/cases/spec-driven-development.json +72 -0
  83. package/evals/cases/test-driven-development.json +73 -0
  84. package/evals/cases/using-agent-skills.json +44 -0
  85. package/evals/fixtures/api-and-interface-design/service-brief.md +19 -0
  86. package/evals/fixtures/browser-testing-with-devtools/README.md +5 -0
  87. package/evals/fixtures/browser-testing-with-devtools/index.html +24 -0
  88. package/evals/fixtures/browser-testing-with-devtools/server.js +15 -0
  89. package/evals/fixtures/ci-cd-and-automation/package.json +8 -0
  90. package/evals/fixtures/ci-cd-and-automation/src/slug.js +3 -0
  91. package/evals/fixtures/ci-cd-and-automation/test/slug.test.js +9 -0
  92. package/evals/fixtures/code-review-and-quality/user-search.diff +16 -0
  93. package/evals/fixtures/code-simplification/config-parser.js +46 -0
  94. package/evals/fixtures/code-simplification/config-parser.test.js +15 -0
  95. package/evals/fixtures/context-engineering/context-audit.md +15 -0
  96. package/evals/fixtures/debugging-and-error-recovery/pagination.js +8 -0
  97. package/evals/fixtures/debugging-and-error-recovery/pagination.test.js +9 -0
  98. package/evals/fixtures/debugging-and-error-recovery/time-pressure.md +6 -0
  99. package/evals/fixtures/deprecation-and-migration/api-inventory.md +9 -0
  100. package/evals/fixtures/documentation-and-adrs/decision-context.md +16 -0
  101. package/evals/fixtures/doubt-driven-development/migration-plan.md +19 -0
  102. package/evals/fixtures/frontend-ui-engineering/Button.tsx +7 -0
  103. package/evals/fixtures/frontend-ui-engineering/design-system.md +11 -0
  104. package/evals/fixtures/git-workflow-and-versioning/.eval/working-tree.patch +21 -0
  105. package/evals/fixtures/git-workflow-and-versioning/app.js +7 -0
  106. package/evals/fixtures/git-workflow-and-versioning/app.test.js +9 -0
  107. package/evals/fixtures/incremental-implementation/reports.js +7 -0
  108. package/evals/fixtures/incremental-implementation/reports.test.js +12 -0
  109. package/evals/fixtures/incremental-implementation/tasks/plan.md +8 -0
  110. package/evals/fixtures/incremental-implementation-pressure/draft-export.js +17 -0
  111. package/evals/fixtures/incremental-implementation-pressure/scenario.md +9 -0
  112. package/evals/fixtures/observability-and-instrumentation/operations.md +11 -0
  113. package/evals/fixtures/observability-and-instrumentation/payment-retry.js +14 -0
  114. package/evals/fixtures/performance-optimization/benchmark.js +15 -0
  115. package/evals/fixtures/performance-optimization/products.js +14 -0
  116. package/evals/fixtures/planning-and-task-breakdown/notifications-spec.md +18 -0
  117. package/evals/fixtures/security-and-hardening/webhook.js +11 -0
  118. package/evals/fixtures/security-and-hardening/webhook.test.js +13 -0
  119. package/evals/fixtures/shipping-and-launch/authority-pressure.md +6 -0
  120. package/evals/fixtures/shipping-and-launch/launch-status.md +11 -0
  121. package/evals/fixtures/source-driven-development/framework-task.md +10 -0
  122. package/evals/fixtures/spec-driven-development/billing-brief.md +16 -0
  123. package/evals/fixtures/spec-driven-development-decomposition/portal-brief.md +22 -0
  124. package/evals/fixtures/test-driven-development/BUG.md +10 -0
  125. package/evals/fixtures/test-driven-development/README.md +28 -0
  126. package/evals/fixtures/test-driven-development/package.json +9 -0
  127. package/evals/fixtures/test-driven-development/src/split.js +8 -0
  128. package/evals/fixtures/test-driven-development/test/split.test.js +13 -0
  129. package/evals/fixtures/test-driven-development-ecosystem/README.md +9 -0
  130. package/evals/fixtures/test-driven-development-ecosystem/ledger.py +15 -0
  131. package/evals/fixtures/test-driven-development-ecosystem/test_ledger.py +19 -0
  132. package/evals/fixtures/using-agent-skills/incident.md +6 -0
  133. package/evals/plugin/code-review-fires/graders/off-by-one.md +7 -0
  134. package/evals/plugin/code-review-fires/graders/severity-labels.md +4 -0
  135. package/evals/plugin/code-review-fires/graders/skill-fired.md +5 -0
  136. package/evals/plugin/code-review-fires/prompt.md +31 -0
  137. package/evals/plugin/code-review-stays-quiet/graders/not-fired.md +8 -0
  138. package/evals/plugin/code-review-stays-quiet/graders/tdd-fired.md +6 -0
  139. package/evals/plugin/code-review-stays-quiet/prompt.md +8 -0
  140. package/evals/plugin/code-review-stays-quiet-on-commit-message/graders/not-fired.md +8 -0
  141. package/evals/plugin/code-review-stays-quiet-on-commit-message/prompt.md +25 -0
  142. package/evals/skill-impact.md +6 -0
  143. package/hooks/SDD-CACHE.md +167 -0
  144. package/hooks/SIMPLIFY-IGNORE.md +91 -0
  145. package/hooks/sdd-cache-post.sh +135 -0
  146. package/hooks/sdd-cache-pre.sh +106 -0
  147. package/hooks/sdd-cache-test.sh +246 -0
  148. package/hooks/session-start-test.sh +47 -0
  149. package/hooks/session-start.sh +33 -0
  150. package/hooks/simplify-ignore-test.sh +476 -0
  151. package/hooks/simplify-ignore.sh +352 -0
  152. package/package.json +89 -4
  153. package/plugin.json +7 -0
  154. package/rebrand/MAINTENANCE.md +112 -0
  155. package/rebrand/NOTICE.md +9 -0
  156. package/rebrand/PR-WORKFLOW.md +34 -0
  157. package/rebrand/UNIFICATION-SPEC.md +33 -0
  158. package/rebrand/VALIDATION.md +28 -0
  159. package/rebrand/VERSIONING.md +32 -0
  160. package/rebrand/apply.cjs +194 -0
  161. package/rebrand/brand.json +13 -0
  162. package/rebrand/catalog.cjs +56 -0
  163. package/rebrand/fork-patches.json +12 -0
  164. package/rebrand/overlay/.github/workflows/test-plugin-install.yml +177 -0
  165. package/rebrand/overlay/CHANGELOG.md +23 -0
  166. package/rebrand/overlay/bin/i3-composer-agent.cjs +122 -0
  167. package/rebrand/overlay/scripts/validate-brand.cjs +44 -0
  168. package/rebrand/overlay/tests/bundle.test.cjs +85 -0
  169. package/rebrand/package.template.json +82 -0
  170. package/rebrand/rebrand.sh +4 -0
  171. package/rebrand/record-upstream.cjs +37 -0
  172. package/rebrand/templates/README.md +86 -0
  173. package/rebrand/templates/codebuddy-setup.md +41 -0
  174. package/rebrand/templates/codex-setup.md +34 -0
  175. package/rebrand/tests/pipeline.test.cjs +185 -0
  176. package/rebrand/upstream.json +171 -0
  177. package/references/accessibility-checklist.md +160 -0
  178. package/references/definition-of-done.md +67 -0
  179. package/references/observability-checklist.md +92 -0
  180. package/references/orchestration-patterns.md +370 -0
  181. package/references/performance-checklist.md +236 -0
  182. package/references/security-checklist.md +248 -0
  183. package/references/testing-patterns.md +235 -0
  184. package/scripts/floor-guard-reference-test.js +291 -0
  185. package/scripts/lib/skill-lint-test.js +650 -0
  186. package/scripts/lib/skill-lint.js +584 -0
  187. package/scripts/run-evals-test.js +623 -0
  188. package/scripts/run-evals.js +638 -0
  189. package/scripts/validate-artifact-paths-test.js +179 -0
  190. package/scripts/validate-artifact-paths.js +128 -0
  191. package/scripts/validate-brand.cjs +44 -0
  192. package/scripts/validate-commands-test.js +216 -0
  193. package/scripts/validate-commands.js +222 -0
  194. package/scripts/validate-reference-links-test.js +495 -0
  195. package/scripts/validate-reference-links.js +187 -0
  196. package/scripts/validate-skills.js +69 -0
  197. package/scripts/validate-versions-test.js +32 -0
  198. package/scripts/validate-versions.js +35 -0
  199. package/skills/api-and-interface-design/SKILL.md +367 -0
  200. package/skills/browser-testing-with-devtools/SKILL.md +317 -0
  201. package/skills/ci-cd-and-automation/SKILL.md +390 -0
  202. package/skills/code-review-and-quality/SKILL.md +398 -0
  203. package/skills/code-simplification/SKILL.md +331 -0
  204. package/skills/constraint-driven-development/SKILL.md +311 -0
  205. package/skills/constraint-driven-development/references/floor-guard.md +182 -0
  206. package/skills/context-engineering/SKILL.md +353 -0
  207. package/skills/debugging-and-error-recovery/SKILL.md +300 -0
  208. package/skills/deprecation-and-migration/SKILL.md +247 -0
  209. package/skills/documentation-and-adrs/SKILL.md +288 -0
  210. package/skills/doubt-driven-development/SKILL.md +243 -0
  211. package/skills/frontend-ui-engineering/SKILL.md +340 -0
  212. package/skills/git-workflow-and-versioning/SKILL.md +355 -0
  213. package/skills/idea-refine/SKILL.md +178 -0
  214. package/skills/idea-refine/examples.md +238 -0
  215. package/skills/idea-refine/frameworks.md +99 -0
  216. package/skills/idea-refine/refinement-criteria.md +113 -0
  217. package/skills/idea-refine/scripts/idea-refine.sh +15 -0
  218. package/skills/incremental-implementation/SKILL.md +249 -0
  219. package/skills/interview-me/SKILL.md +235 -0
  220. package/skills/observability-and-instrumentation/SKILL.md +238 -0
  221. package/skills/performance-optimization/SKILL.md +267 -0
  222. package/skills/performance-optimization/references/optimization-patterns.md +250 -0
  223. package/skills/planning-and-task-breakdown/SKILL.md +257 -0
  224. package/skills/security-and-hardening/SKILL.md +216 -0
  225. package/skills/security-and-hardening/references/hardening-patterns.md +324 -0
  226. package/skills/shipping-and-launch/SKILL.md +330 -0
  227. package/skills/source-driven-development/SKILL.md +216 -0
  228. package/skills/spec-driven-development/SKILL.md +259 -0
  229. package/skills/test-driven-development/SKILL.md +398 -0
  230. package/skills/using-agent-skills/SKILL.md +192 -0
  231. package/tests/bundle.test.cjs +85 -0
@@ -0,0 +1,476 @@
1
+ #!/bin/bash
2
+ # simplify-ignore-test.sh — Tests for the simplify-ignore hook
3
+ #
4
+ # Exercises filter_file by extracting function definitions from the hook.
5
+ # Run: bash hooks/simplify-ignore-test.sh
6
+
7
+ set -euo pipefail
8
+
9
+ PASS=0 FAIL=0
10
+ TMPDIR=$(mktemp -d)
11
+ trap 'rm -rf "$TMPDIR"' EXIT
12
+
13
+ export CACHE="$TMPDIR/cache"
14
+ mkdir -p "$CACHE"
15
+
16
+ # Extract function definitions we need
17
+ hash_cmd() {
18
+ if command -v shasum >/dev/null 2>&1; then shasum
19
+ elif command -v sha1sum >/dev/null 2>&1; then sha1sum
20
+ else printf '%s\n' "error: missing shasum or sha1sum" >&2; exit 1; fi
21
+ }
22
+ file_id() { printf '%s' "$1" | hash_cmd | cut -c1-16; }
23
+ block_hash() { printf '%s' "$1" | hash_cmd | cut -c1-8; }
24
+ escape_glob() {
25
+ local s="$1"
26
+ s="${s//\\/\\\\}"
27
+ s="${s//\*/\\*}"
28
+ s="${s//\?/\\?}"
29
+ s="${s//\[/\\[}"
30
+ printf '%s' "$s"
31
+ }
32
+
33
+ # Extract filter_file from the hook script (line 59 "filter_file()" to line 142 closing brace)
34
+ eval "$(sed -n '/^filter_file()/,/^}/p' hooks/simplify-ignore.sh)"
35
+
36
+ assert_eq() {
37
+ local label="$1" expected="$2" actual="$3"
38
+ if [ "$expected" = "$actual" ]; then
39
+ PASS=$((PASS + 1))
40
+ printf ' PASS: %s\n' "$label"
41
+ else
42
+ FAIL=$((FAIL + 1))
43
+ printf ' FAIL: %s\n' "$label" >&2
44
+ printf ' expected: %s\n' "$(printf '%s' "$expected" | cat -v)" >&2
45
+ printf ' actual: %s\n' "$(printf '%s' "$actual" | cat -v)" >&2
46
+ fi
47
+ }
48
+
49
+ # ── Test 1: Single-line block produces exactly one placeholder ────────────
50
+ printf 'Test 1: Single-line block (start+end on same line)\n'
51
+ rm -f "$CACHE"/*
52
+
53
+ SRC="$TMPDIR/single-line.js"
54
+ DEST="$TMPDIR/single-line-filtered.js"
55
+ cat > "$SRC" <<'EOF'
56
+ const a = 1;
57
+ /* simplify-ignore-start */ const secret = 42; /* simplify-ignore-end */
58
+ const b = 2;
59
+ EOF
60
+
61
+ FID="test_single"
62
+ filter_file "$SRC" "$DEST" "$FID"
63
+
64
+ placeholder_count=$(grep -c 'BLOCK_' "$DEST")
65
+ assert_eq "exactly one placeholder line" "1" "$placeholder_count"
66
+ assert_eq "line before block preserved" "1" "$(grep -c 'const a = 1' "$DEST")"
67
+ assert_eq "line after block preserved" "1" "$(grep -c 'const b = 2' "$DEST")"
68
+
69
+ block_files=$(ls "$CACHE/${FID}".block.* 2>/dev/null | wc -l | tr -d ' ')
70
+ assert_eq "one block file in cache" "1" "$block_files"
71
+
72
+ block_content=$(cat "$CACHE/${FID}".block.*)
73
+ assert_eq "block content matches" \
74
+ "/* simplify-ignore-start */ const secret = 42; /* simplify-ignore-end */" \
75
+ "$block_content"
76
+
77
+ # ── Test 2: Multi-line block ─────────────────────────────────────────────
78
+ printf '\nTest 2: Multi-line block\n'
79
+ rm -f "$CACHE"/*
80
+
81
+ SRC="$TMPDIR/multi-line.js"
82
+ DEST="$TMPDIR/multi-line-filtered.js"
83
+ cat > "$SRC" <<'EOF'
84
+ const a = 1;
85
+ // simplify-ignore-start
86
+ const secret1 = 42;
87
+ const secret2 = 99;
88
+ // simplify-ignore-end
89
+ const b = 2;
90
+ EOF
91
+
92
+ FID="test_multi"
93
+ filter_file "$SRC" "$DEST" "$FID"
94
+
95
+ placeholder_count=$(grep -c 'BLOCK_' "$DEST")
96
+ assert_eq "exactly one placeholder for multi-line block" "1" "$placeholder_count"
97
+
98
+ output_lines=$(wc -l < "$DEST" | tr -d ' ')
99
+ assert_eq "output has 3 lines (before + placeholder + after)" "3" "$output_lines"
100
+
101
+ # ── Test 3: Multiple blocks in one file ──────────────────────────────────
102
+ printf '\nTest 3: Multiple blocks in one file\n'
103
+ rm -f "$CACHE"/*
104
+
105
+ SRC="$TMPDIR/multi-block.js"
106
+ DEST="$TMPDIR/multi-block-filtered.js"
107
+ cat > "$SRC" <<'EOF'
108
+ line1
109
+ // simplify-ignore-start
110
+ blockA
111
+ // simplify-ignore-end
112
+ line2
113
+ // simplify-ignore-start
114
+ blockB
115
+ // simplify-ignore-end
116
+ line3
117
+ EOF
118
+
119
+ FID="test_multiblock"
120
+ filter_file "$SRC" "$DEST" "$FID"
121
+
122
+ placeholder_count=$(grep -c 'BLOCK_' "$DEST")
123
+ assert_eq "two placeholders for two blocks" "2" "$placeholder_count"
124
+
125
+ block_files=$(ls "$CACHE/${FID}".block.* 2>/dev/null | wc -l | tr -d ' ')
126
+ assert_eq "two block files in cache" "2" "$block_files"
127
+
128
+ # ── Test 4: Reason string preserved ──────────────────────────────────────
129
+ printf '\nTest 4: Reason string in placeholder\n'
130
+ rm -f "$CACHE"/*
131
+
132
+ SRC="$TMPDIR/reason.js"
133
+ DEST="$TMPDIR/reason-filtered.js"
134
+ cat > "$SRC" <<'EOF'
135
+ // simplify-ignore-start: perf-critical
136
+ hot_loop();
137
+ // simplify-ignore-end
138
+ EOF
139
+
140
+ FID="test_reason"
141
+ filter_file "$SRC" "$DEST" "$FID"
142
+
143
+ assert_eq "placeholder includes reason" "1" "$(grep -c 'perf-critical' "$DEST")"
144
+
145
+ reason_files=$(ls "$CACHE/${FID}".reason.* 2>/dev/null | wc -l | tr -d ' ')
146
+ assert_eq "reason file saved" "1" "$reason_files"
147
+ assert_eq "reason content" "perf-critical" "$(cat "$CACHE/${FID}".reason.*)"
148
+
149
+ # ── Test 5: Trailing newline preservation ────────────────────────────────
150
+ printf '\nTest 5: Trailing newline preservation\n'
151
+ rm -f "$CACHE"/*
152
+
153
+ SRC="$TMPDIR/no-trailing-nl.js"
154
+ DEST="$TMPDIR/no-trailing-nl-filtered.js"
155
+ printf 'line1\n// simplify-ignore-start\nsecret\n// simplify-ignore-end' > "$SRC"
156
+
157
+ FID="test_trail"
158
+ filter_file "$SRC" "$DEST" "$FID"
159
+
160
+ # Source has no trailing newline; dest should also have no trailing newline
161
+ src_has_nl=$(tail -c 1 "$SRC" | wc -l | tr -d ' ')
162
+ dest_has_nl=$(tail -c 1 "$DEST" | wc -l | tr -d ' ')
163
+ assert_eq "dest preserves no-trailing-newline from source" "$src_has_nl" "$dest_has_nl"
164
+
165
+ # ── Test 6: No blocks → return 1 ────────────────────────────────────────
166
+ printf '\nTest 6: No blocks returns 1\n'
167
+ rm -f "$CACHE"/*
168
+
169
+ SRC="$TMPDIR/no-blocks.js"
170
+ DEST="$TMPDIR/no-blocks-filtered.js"
171
+ cat > "$SRC" <<'EOF'
172
+ const a = 1;
173
+ const b = 2;
174
+ EOF
175
+
176
+ FID="test_noblocks"
177
+ rc=0
178
+ filter_file "$SRC" "$DEST" "$FID" || rc=$?
179
+ assert_eq "returns 1 when no blocks found" "1" "$rc"
180
+
181
+ # ── Test 7: Unclosed block emits warning and flushes ─────────────────────
182
+ printf '\nTest 7: Unclosed block\n'
183
+ rm -f "$CACHE"/*
184
+
185
+ SRC="$TMPDIR/unclosed.js"
186
+ DEST="$TMPDIR/unclosed-filtered.js"
187
+ cat > "$SRC" <<'EOF'
188
+ line1
189
+ // simplify-ignore-start
190
+ orphan code
191
+ EOF
192
+
193
+ FID="test_unclosed"
194
+ stderr_out=$(filter_file "$SRC" "$DEST" "$FID" 2>&1) || true
195
+ assert_eq "warning emitted for unclosed block" "1" "$(printf '%s' "$stderr_out" | grep -c 'unclosed')"
196
+ assert_eq "orphan code flushed to output" "1" "$(grep -c 'orphan code' "$DEST")"
197
+
198
+ # ── Test 8: Single-line block with reason ────────────────────────────────
199
+ printf '\nTest 8: Single-line block with reason\n'
200
+ rm -f "$CACHE"/*
201
+
202
+ SRC="$TMPDIR/single-reason.js"
203
+ DEST="$TMPDIR/single-reason-filtered.js"
204
+ cat > "$SRC" <<'EOF'
205
+ before
206
+ /* simplify-ignore-start: hot-path */ x = compute(); /* simplify-ignore-end */
207
+ after
208
+ EOF
209
+
210
+ FID="test_single_reason"
211
+ filter_file "$SRC" "$DEST" "$FID"
212
+
213
+ placeholder_count=$(grep -c 'BLOCK_' "$DEST")
214
+ assert_eq "exactly one placeholder for single-line+reason" "1" "$placeholder_count"
215
+ assert_eq "reason in placeholder" "1" "$(grep -c 'hot-path' "$DEST")"
216
+
217
+ # ── Test 9: HTML comment syntax ──────────────────────────────────────────
218
+ printf '\nTest 9: HTML comment syntax\n'
219
+ rm -f "$CACHE"/*
220
+
221
+ SRC="$TMPDIR/html.html"
222
+ DEST="$TMPDIR/html-filtered.html"
223
+ cat > "$SRC" <<'EOF'
224
+ <div>
225
+ <!-- simplify-ignore-start -->
226
+ <secret-component />
227
+ <!-- simplify-ignore-end -->
228
+ </div>
229
+ EOF
230
+
231
+ FID="test_html"
232
+ filter_file "$SRC" "$DEST" "$FID"
233
+
234
+ placeholder_count=$(grep -c 'BLOCK_' "$DEST")
235
+ assert_eq "HTML block replaced" "1" "$placeholder_count"
236
+ assert_eq "HTML suffix preserved" "1" "$(grep -c '\-\->' "$DEST")"
237
+
238
+ # ── Test 10: JSON parsing error warning ──────────────────────────────────
239
+ printf '\nTest 10: Malformed JSON input produces warning\n'
240
+
241
+ # Without jq the hook's guard exits before parsing input. Assert whichever
242
+ # branch this machine can exercise (same pattern as session-start-test.sh).
243
+ warning_out=$(echo 'NOT_JSON{{{' | bash hooks/simplify-ignore.sh 2>&1) || true
244
+ if command -v jq >/dev/null 2>&1; then
245
+ assert_eq "warning on bad JSON" "1" "$(printf '%s' "$warning_out" | grep -c 'Warning.*failed to parse')"
246
+ else
247
+ assert_eq "missing-jq guard surfaced" "1" "$(printf '%s' "$warning_out" | grep -c 'error: missing jq')"
248
+ fi
249
+
250
+ # ── Test 11: Stop keeps a change made outside Edit|Write ─────────────────
251
+ printf '\nTest 11: Stop keeps a Bash write made after the last Edit\n'
252
+
253
+ if ! command -v jq >/dev/null 2>&1; then
254
+ printf ' SKIP: full lifecycle needs jq (the hook exits at its jq guard)\n'
255
+ else
256
+ PROJ="$TMPDIR/proj"
257
+ rm -rf "$PROJ"
258
+ mkdir -p "$PROJ/.claude"
259
+ TARGET="$PROJ/app.js"
260
+ cat > "$TARGET" <<'EOF'
261
+ const editable = 1;
262
+ /* simplify-ignore-start: perf-critical */
263
+ const protectedValue = 42;
264
+ /* simplify-ignore-end */
265
+ EOF
266
+
267
+ hook_event() { printf '%s' "$1" | CLAUDE_PROJECT_DIR="$PROJ" bash hooks/simplify-ignore.sh; }
268
+ read_event=$(printf '{"tool_name":"Read","tool_input":{"file_path":"%s"}}' "$TARGET")
269
+ edit_event=$(printf '{"tool_name":"Edit","tool_input":{"file_path":"%s"}}' "$TARGET")
270
+
271
+ # PreToolUse Read → block hidden behind a placeholder, backup taken
272
+ hook_event "$read_event"
273
+ assert_eq "protected block hidden after Read" "1" "$(grep -c 'BLOCK_' "$TARGET")"
274
+ assert_eq "protected content off disk after Read" "0" "$(grep -c 'protectedValue' "$TARGET")"
275
+
276
+ # Model edits the filtered file → PostToolUse Edit refreshes the backup
277
+ printf 'const added = 3;\n' >> "$TARGET"
278
+ hook_event "$edit_event"
279
+
280
+ # A later write through Bash — no Edit or Write event follows it
281
+ sed 's/const editable = 1;/const editable = 2;/' "$TARGET" > "$TMPDIR/bash-write.js"
282
+ cat "$TMPDIR/bash-write.js" > "$TARGET"
283
+
284
+ # Stop
285
+ hook_event '{}'
286
+
287
+ assert_eq "Bash write survives Stop" "1" "$(grep -c 'const editable = 2;' "$TARGET")"
288
+ assert_eq "stale backup value not restored" "0" "$(grep -c 'const editable = 1;' "$TARGET")"
289
+ assert_eq "edit made through Edit survives Stop" "1" "$(grep -c 'const added = 3;' "$TARGET")"
290
+ assert_eq "protected block back on disk" "1" "$(grep -c 'const protectedValue = 42;' "$TARGET")"
291
+ assert_eq "no placeholder left after Stop" "0" "$(grep -c 'BLOCK_' "$TARGET")"
292
+ fi
293
+
294
+ # ── Test 12: Stop fallback keeps the rewrite in the cache ───────────────
295
+ printf '\nTest 12: Wholesale rewrite is kept in the cache before the backup restore\n'
296
+
297
+ if ! command -v jq >/dev/null 2>&1; then
298
+ printf ' SKIP: full lifecycle needs jq (the hook exits at its jq guard)\n'
299
+ else
300
+ PROJ="$TMPDIR/proj-rewrite"
301
+ rm -rf "$PROJ"
302
+ mkdir -p "$PROJ/.claude"
303
+ TARGET="$PROJ/app.js"
304
+ cat > "$TARGET" <<'EOF'
305
+ const editable = 1;
306
+ /* simplify-ignore-start: perf-critical */
307
+ const protectedValue = 42;
308
+ /* simplify-ignore-end */
309
+ EOF
310
+
311
+ hook_event() { printf '%s' "$1" | CLAUDE_PROJECT_DIR="$PROJ" bash hooks/simplify-ignore.sh; }
312
+ read_event=$(printf '{"tool_name":"Read","tool_input":{"file_path":"%s"}}' "$TARGET")
313
+
314
+ hook_event "$read_event"
315
+ assert_eq "protected block hidden after Read" "1" "$(grep -c 'BLOCK_' "$TARGET")"
316
+
317
+ # Wholesale rewrite — every placeholder is gone, so Stop has nothing to expand
318
+ printf 'const rewritten = 7;\n' > "$TARGET"
319
+
320
+ stop_out=$(hook_event '{}' 2>&1) || true
321
+
322
+ PROJ_CACHE="$PROJ/.claude/.simplify-ignore-cache"
323
+ RECOVERED="$PROJ_CACHE/$(file_id "$TARGET").recovered"
324
+
325
+ assert_eq "backup restored over the rewrite" "1" "$(grep -c 'const protectedValue = 42;' "$TARGET")"
326
+ assert_eq "rewrite kept in cache" "1" "$([ -f "$RECOVERED" ] && echo 1 || echo 0)"
327
+ assert_eq "cached rewrite has the rewritten content" "const rewritten = 7;" "$(cat "$RECOVERED")"
328
+ assert_eq "warning names the restored file" "1" \
329
+ "$(printf '%s' "$stop_out" | grep -c -F "$TARGET")"
330
+ assert_eq "warning names the cached rewrite" "1" \
331
+ "$(printf '%s' "$stop_out" | grep -c -F "$RECOVERED")"
332
+ fi
333
+
334
+ # ── Test 13: Read → direct write with no event → Stop ───────────────────
335
+ printf '\nTest 13: A write with no Edit or Write event survives Stop\n'
336
+
337
+ if ! command -v jq >/dev/null 2>&1; then
338
+ printf ' SKIP: full lifecycle needs jq (the hook exits at its jq guard)\n'
339
+ else
340
+ PROJ="$TMPDIR/proj-noevent"
341
+ rm -rf "$PROJ"
342
+ mkdir -p "$PROJ/.claude"
343
+ TARGET="$PROJ/app.js"
344
+ cat > "$TARGET" <<'EOF'
345
+ const editable = 1;
346
+ /* simplify-ignore-start: perf-critical */
347
+ const protectedValue = 42;
348
+ /* simplify-ignore-end */
349
+ EOF
350
+
351
+ hook_event() { printf '%s' "$1" | CLAUDE_PROJECT_DIR="$PROJ" bash hooks/simplify-ignore.sh; }
352
+ read_event=$(printf '{"tool_name":"Read","tool_input":{"file_path":"%s"}}' "$TARGET")
353
+
354
+ # Read → placeholder on disk, backup taken
355
+ hook_event "$read_event"
356
+
357
+ # Direct write, no Edit or Write event follows it
358
+ sed 's/const editable = 1;/const editable = 9;/' "$TARGET" > "$TMPDIR/noevent-write.js"
359
+ cat "$TMPDIR/noevent-write.js" > "$TARGET"
360
+
361
+ # Stop
362
+ hook_event '{}'
363
+
364
+ assert_eq "write with no event survives Stop" "1" "$(grep -c 'const editable = 9;' "$TARGET")"
365
+ assert_eq "stale backup value not restored" "0" "$(grep -c 'const editable = 1;' "$TARGET")"
366
+ assert_eq "protected block back on disk" "1" "$(grep -c 'const protectedValue = 42;' "$TARGET")"
367
+ assert_eq "no placeholder left after Stop" "0" "$(grep -c 'BLOCK_' "$TARGET")"
368
+ fi
369
+
370
+ # ── Tests 14-16: full-lifecycle round-trip fidelity ──────────────────────
371
+ # Run the hook end-to-end (Read → [Edit] → Stop) against an isolated
372
+ # CLAUDE_PROJECT_DIR and assert the original bytes are recovered exactly.
373
+ # These exercise the expand/restore half of the hook, which the
374
+ # function-extraction tests above cannot reach.
375
+
376
+ if command -v jq >/dev/null 2>&1; then
377
+
378
+ rt_hook_event() {
379
+ # rt_hook_event <project_dir> <tool_name|""> <file_path|"">
380
+ local proj="$1" tool="$2" fp="$3" input
381
+ if [ -n "$tool" ]; then
382
+ input=$(jq -n --arg t "$tool" --arg fp "$fp" \
383
+ '{tool_name:$t, tool_input:{file_path:$fp}}')
384
+ else
385
+ input='{}'
386
+ fi
387
+ printf '%s' "$input" | CLAUDE_PROJECT_DIR="$proj" bash hooks/simplify-ignore.sh
388
+ }
389
+
390
+ # ── Test 14: Read → Stop restores the file byte-identically ──────────────
391
+ printf '\nTest 14: Round-trip Read → Stop (byte fidelity)\n'
392
+ PROJ="$TMPDIR/rt14"; mkdir -p "$PROJ"
393
+ RT="$PROJ/roundtrip.js"
394
+ cat > "$RT" <<'EOF'
395
+ const a = `template ${literal}`;
396
+ // simplify-ignore-start: perf-critical
397
+ const secret = "glob*chars? [and] \\backslashes";
398
+ if (a && b) { mask = flags & 0xff; } // ampersands must survive bash 5.2 patsub_replacement
399
+ hot_loop($HOME);
400
+ // simplify-ignore-end
401
+ middle line
402
+ /* simplify-ignore-start */ const inline = 1; /* simplify-ignore-end */
403
+ const b = 2;
404
+ EOF
405
+ cp "$RT" "$PROJ/roundtrip.orig"
406
+
407
+ rt_hook_event "$PROJ" "Read" "$RT"
408
+ assert_eq "blocks hidden on disk after Read" "2" "$(grep -c 'BLOCK_' "$RT")"
409
+ assert_eq "secret absent from disk after Read" "0" "$(grep -c 'secret' "$RT")"
410
+
411
+ rt_hook_event "$PROJ" "" ""
412
+ if cmp -s "$RT" "$PROJ/roundtrip.orig"; then
413
+ assert_eq "Stop restores original bytes" "identical" "identical"
414
+ else
415
+ assert_eq "Stop restores original bytes" "identical" "$(cmp "$RT" "$PROJ/roundtrip.orig" 2>&1 | head -1)"
416
+ fi
417
+
418
+ # ── Test 15: Round-trip preserves missing trailing newline ────────────────
419
+ printf '\nTest 15: Round-trip with no trailing newline\n'
420
+ PROJ="$TMPDIR/rt15"; mkdir -p "$PROJ"
421
+ RT="$PROJ/nonewline.js"
422
+ printf 'top\n// simplify-ignore-start\nhidden\n// simplify-ignore-end\nbottom' > "$RT"
423
+ cp "$RT" "$PROJ/nonewline.orig"
424
+
425
+ rt_hook_event "$PROJ" "Read" "$RT"
426
+ assert_eq "block hidden" "1" "$(grep -c 'BLOCK_' "$RT")"
427
+ rt_hook_event "$PROJ" "" ""
428
+ if cmp -s "$RT" "$PROJ/nonewline.orig"; then
429
+ assert_eq "no-trailing-newline file restored byte-identically" "identical" "identical"
430
+ else
431
+ assert_eq "no-trailing-newline file restored byte-identically" "identical" "differs"
432
+ fi
433
+
434
+ # ── Test 16: Read → model edit → Edit event → Stop keeps the edit ────────
435
+ printf '\nTest 16: Round-trip with an edit during the session\n'
436
+ PROJ="$TMPDIR/rt16"; mkdir -p "$PROJ"
437
+ RT="$PROJ/edited.js"
438
+ cat > "$RT" <<'EOF'
439
+ const a = 1;
440
+ // simplify-ignore-start
441
+ const secret = 42;
442
+ // simplify-ignore-end
443
+ const b = 2;
444
+ EOF
445
+ cp "$RT" "$PROJ/edited.orig"
446
+
447
+ rt_hook_event "$PROJ" "Read" "$RT"
448
+ # Simulate the model editing the filtered file: append a new line.
449
+ printf '%s\n' "const added = true;" >> "$RT"
450
+ rt_hook_event "$PROJ" "Edit" "$RT"
451
+
452
+ assert_eq "disk still placeholdered after Edit" "1" "$(grep -c 'BLOCK_' "$RT")"
453
+ assert_eq "secret still absent from disk after Edit" "0" "$(grep -c 'secret' "$RT")"
454
+ assert_eq "added line survives re-filter" "1" "$(grep -c 'const added' "$RT")"
455
+ BAK=$(ls "$PROJ/.claude/.simplify-ignore-cache"/*.bak 2>/dev/null | head -1)
456
+ assert_eq "backup holds real content" "1" "$(grep -c 'const secret = 42' "${BAK:-/dev/null}")"
457
+ assert_eq "backup holds the edit" "1" "$(grep -c 'const added' "${BAK:-/dev/null}")"
458
+
459
+ rt_hook_event "$PROJ" "" ""
460
+ EXPECTED="$PROJ/edited.expected"
461
+ cp "$PROJ/edited.orig" "$EXPECTED"
462
+ printf '%s\n' "const added = true;" >> "$EXPECTED"
463
+ if cmp -s "$RT" "$EXPECTED"; then
464
+ assert_eq "Stop restores original + edit byte-identically" "identical" "identical"
465
+ else
466
+ assert_eq "Stop restores original + edit byte-identically" "identical" "differs"
467
+ fi
468
+
469
+ else
470
+ printf '\nTests 14-16 skipped: jq not available (hook no-ops without it)\n'
471
+ fi
472
+
473
+ # ── Summary ──────────────────────────────────────────────────────────────
474
+ printf '\n══════════════════════════════════════════\n'
475
+ printf 'Results: %d passed, %d failed\n' "$PASS" "$FAIL"
476
+ [ "$FAIL" -eq 0 ] && exit 0 || exit 1