specweave 1.0.31 → 1.0.33

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 (123) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/CLAUDE.md +205 -148
  3. package/README.md +0 -2
  4. package/bin/specweave.js +11 -0
  5. package/dist/src/cli/commands/init.js +1 -1
  6. package/dist/src/cli/commands/init.js.map +1 -1
  7. package/dist/src/cli/commands/update-instructions.d.ts +16 -0
  8. package/dist/src/cli/commands/update-instructions.d.ts.map +1 -0
  9. package/dist/src/cli/commands/update-instructions.js +134 -0
  10. package/dist/src/cli/commands/update-instructions.js.map +1 -0
  11. package/dist/src/cli/helpers/init/directory-structure.d.ts +28 -1
  12. package/dist/src/cli/helpers/init/directory-structure.d.ts.map +1 -1
  13. package/dist/src/cli/helpers/init/directory-structure.js +163 -33
  14. package/dist/src/cli/helpers/init/directory-structure.js.map +1 -1
  15. package/dist/src/cli/helpers/init/index.d.ts +2 -1
  16. package/dist/src/cli/helpers/init/index.d.ts.map +1 -1
  17. package/dist/src/cli/helpers/init/index.js +3 -1
  18. package/dist/src/cli/helpers/init/index.js.map +1 -1
  19. package/dist/src/cli/helpers/init/instruction-file-merger.d.ts +23 -0
  20. package/dist/src/cli/helpers/init/instruction-file-merger.d.ts.map +1 -0
  21. package/dist/src/cli/helpers/init/instruction-file-merger.js +243 -0
  22. package/dist/src/cli/helpers/init/instruction-file-merger.js.map +1 -0
  23. package/dist/src/cli/helpers/init/plugin-installer.js +49 -0
  24. package/dist/src/cli/helpers/init/plugin-installer.js.map +1 -1
  25. package/dist/src/config/types.d.ts +2 -2
  26. package/dist/src/core/living-docs/external-sync-orchestrator.d.ts +26 -0
  27. package/dist/src/core/living-docs/external-sync-orchestrator.d.ts.map +1 -1
  28. package/dist/src/core/living-docs/external-sync-orchestrator.js +61 -0
  29. package/dist/src/core/living-docs/external-sync-orchestrator.js.map +1 -1
  30. package/dist/src/core/living-docs/scaffolding/index.d.ts +12 -0
  31. package/dist/src/core/living-docs/scaffolding/index.d.ts.map +1 -0
  32. package/dist/src/core/living-docs/scaffolding/index.js +15 -0
  33. package/dist/src/core/living-docs/scaffolding/index.js.map +1 -0
  34. package/dist/src/core/living-docs/scaffolding/merger.d.ts +183 -0
  35. package/dist/src/core/living-docs/scaffolding/merger.d.ts.map +1 -0
  36. package/dist/src/core/living-docs/scaffolding/merger.js +523 -0
  37. package/dist/src/core/living-docs/scaffolding/merger.js.map +1 -0
  38. package/dist/src/core/living-docs/scaffolding/scaffold.d.ts +102 -0
  39. package/dist/src/core/living-docs/scaffolding/scaffold.d.ts.map +1 -0
  40. package/dist/src/core/living-docs/scaffolding/scaffold.js +346 -0
  41. package/dist/src/core/living-docs/scaffolding/scaffold.js.map +1 -0
  42. package/dist/src/core/living-docs/scaffolding/template-engine.d.ts +108 -0
  43. package/dist/src/core/living-docs/scaffolding/template-engine.d.ts.map +1 -0
  44. package/dist/src/core/living-docs/scaffolding/template-engine.js +204 -0
  45. package/dist/src/core/living-docs/scaffolding/template-engine.js.map +1 -0
  46. package/dist/src/core/living-docs/sync-helpers/generators.d.ts +38 -2
  47. package/dist/src/core/living-docs/sync-helpers/generators.d.ts.map +1 -1
  48. package/dist/src/core/living-docs/sync-helpers/generators.js +65 -10
  49. package/dist/src/core/living-docs/sync-helpers/generators.js.map +1 -1
  50. package/dist/src/core/living-docs/sync-helpers/index.d.ts +1 -1
  51. package/dist/src/core/living-docs/sync-helpers/index.d.ts.map +1 -1
  52. package/dist/src/core/living-docs/sync-helpers/index.js.map +1 -1
  53. package/dist/src/core/tools/index.d.ts +11 -0
  54. package/dist/src/core/tools/index.d.ts.map +1 -0
  55. package/dist/src/core/tools/index.js +10 -0
  56. package/dist/src/core/tools/index.js.map +1 -0
  57. package/dist/src/core/tools/tool-event-bus.d.ts +33 -0
  58. package/dist/src/core/tools/tool-event-bus.d.ts.map +1 -0
  59. package/dist/src/core/tools/tool-event-bus.js +84 -0
  60. package/dist/src/core/tools/tool-event-bus.js.map +1 -0
  61. package/dist/src/core/tools/tool-index-builder.d.ts +27 -0
  62. package/dist/src/core/tools/tool-index-builder.d.ts.map +1 -0
  63. package/dist/src/core/tools/tool-index-builder.js +289 -0
  64. package/dist/src/core/tools/tool-index-builder.js.map +1 -0
  65. package/dist/src/core/tools/tool-registry.d.ts +51 -0
  66. package/dist/src/core/tools/tool-registry.d.ts.map +1 -0
  67. package/dist/src/core/tools/tool-registry.js +224 -0
  68. package/dist/src/core/tools/tool-registry.js.map +1 -0
  69. package/dist/src/core/tools/tool-search-engine.d.ts +22 -0
  70. package/dist/src/core/tools/tool-search-engine.d.ts.map +1 -0
  71. package/dist/src/core/tools/tool-search-engine.js +174 -0
  72. package/dist/src/core/tools/tool-search-engine.js.map +1 -0
  73. package/dist/src/core/tools/types/tool-registry-types.d.ts +112 -0
  74. package/dist/src/core/tools/types/tool-registry-types.d.ts.map +1 -0
  75. package/dist/src/core/tools/types/tool-registry-types.js +7 -0
  76. package/dist/src/core/tools/types/tool-registry-types.js.map +1 -0
  77. package/dist/src/init/compliance/types.d.ts +1 -1
  78. package/package.json +1 -1
  79. package/plugins/specweave/hooks/hooks.json +3 -13
  80. package/plugins/specweave/hooks/lib/common-setup.sh +47 -321
  81. package/plugins/specweave/hooks/lib/migrate-increment-work.sh +5 -5
  82. package/plugins/specweave/hooks/lib/sync-spec-content.sh +5 -5
  83. package/plugins/specweave/hooks/universal/dispatcher.mjs +4 -5
  84. package/plugins/specweave/hooks/universal/fail-fast-wrapper.sh +43 -296
  85. package/plugins/specweave/hooks/universal/hook-wrapper.sh +3 -1
  86. package/plugins/specweave/hooks/user-prompt-submit.sh +1 -1
  87. package/plugins/specweave/hooks/v2/dispatchers/post-tool-use.sh +2 -2
  88. package/plugins/specweave/hooks/v2/dispatchers/session-start.sh +1 -10
  89. package/plugins/specweave/hooks/v2/guards/completion-guard.sh +12 -29
  90. package/plugins/specweave/hooks/v2/guards/increment-duplicate-guard.sh +27 -29
  91. package/plugins/specweave/hooks/v2/guards/metadata-json-guard.sh +10 -4
  92. package/plugins/specweave/hooks/v2/guards/spec-validation-guard.sh +139 -0
  93. package/plugins/specweave/hooks/v2/guards/task-ac-sync-guard.sh +4 -2
  94. package/plugins/specweave/hooks/v2/session-end.sh +3 -1
  95. package/plugins/specweave/hooks/v2/session-start.sh +3 -1
  96. package/plugins/specweave/skills/increment-planner/templates/plan.md +14 -0
  97. package/plugins/specweave/skills/update-instructions/SKILL.md +80 -0
  98. package/plugins/specweave-ado/hooks/post-living-docs-update.sh +1 -1
  99. package/plugins/specweave-mobile/README.md +55 -35
  100. package/plugins/specweave-mobile/agents/mobile-architect/AGENT.md +805 -329
  101. package/plugins/specweave-mobile/skills/expo-workflow/SKILL.md +226 -9
  102. package/plugins/specweave-mobile/skills/native-modules/SKILL.md +221 -20
  103. package/plugins/specweave-mobile/skills/performance-optimization/SKILL.md +186 -14
  104. package/plugins/specweave-mobile/skills/react-native-setup/SKILL.md +151 -54
  105. package/plugins/specweave-release/commands/npm.md +61 -17
  106. package/plugins/specweave-release/hooks/post-task-completion.sh +2 -3
  107. package/src/templates/AGENTS.md.template +34 -0
  108. package/src/templates/CLAUDE.md.template +121 -155
  109. package/plugins/specweave/hooks/config-env-separator.sh +0 -99
  110. package/plugins/specweave/hooks/github-metadata-guard.sh +0 -73
  111. package/plugins/specweave/hooks/lib/circuit-breaker.sh +0 -381
  112. package/plugins/specweave/hooks/lib/crash-prevention.sh +0 -336
  113. package/plugins/specweave/hooks/lib/logging.sh +0 -231
  114. package/plugins/specweave/hooks/lib/metrics.sh +0 -347
  115. package/plugins/specweave/hooks/lib/semaphore.sh +0 -216
  116. package/plugins/specweave/hooks/project-folder-guard.sh +0 -274
  117. package/plugins/specweave/hooks/spec-project-validator.sh +0 -210
  118. package/plugins/specweave/hooks/v2/guards/bash-file-guard.sh +0 -212
  119. package/plugins/specweave/hooks/v2/guards/bash-file-guard.test.sh +0 -163
  120. package/plugins/specweave/hooks/v2/guards/features-folder-guard.sh +0 -51
  121. package/plugins/specweave/hooks/v2/guards/increment-root-guard.sh +0 -63
  122. package/plugins/specweave/hooks/v2/guards/per-us-project-validator.sh +0 -335
  123. package/plugins/specweave/hooks/v2/guards/per-us-project-validator.test.sh +0 -406
@@ -1,406 +0,0 @@
1
- #!/bin/bash
2
- # Comprehensive test suite for per-us-project-validator.sh
3
- # Tests that each User Story in spec.md has **Project**: (and **Board**: for 2-level)
4
- #
5
- # Usage: bash per-us-project-validator.test.sh
6
- #
7
- # v0.34.0 - Initial test suite with all US formats
8
-
9
- set -e
10
-
11
- GUARD="$(dirname "$0")/per-us-project-validator.sh"
12
- TEST_DIR=$(mktemp -d)
13
- PASS=0
14
- FAIL=0
15
- TOTAL=0
16
-
17
- # Colors for output
18
- RED='\033[0;31m'
19
- GREEN='\033[0;32m'
20
- YELLOW='\033[1;33m'
21
- NC='\033[0m' # No Color
22
-
23
- cleanup() {
24
- rm -rf "$TEST_DIR"
25
- }
26
- trap cleanup EXIT
27
-
28
- # Test helper: should block
29
- test_should_block() {
30
- local name="$1"
31
- local spec_content="$2"
32
- TOTAL=$((TOTAL + 1))
33
-
34
- local file_path="$TEST_DIR/.specweave/increments/0001-test/spec.md"
35
- mkdir -p "$(dirname "$file_path")"
36
-
37
- # Build JSON input for the guard
38
- # Use printf + jq -Rs to properly escape newlines in the content
39
- local json_input
40
- json_input=$(printf '%s' "$spec_content" | jq -Rs \
41
- --arg tool_name "Write" \
42
- --arg file_path "$file_path" \
43
- '{tool_name: $tool_name, tool_input: {file_path: $file_path, content: .}}')
44
-
45
- result=$(echo "$json_input" | bash "$GUARD" 2>&1; echo "EXIT:$?")
46
- exit_code=$(echo "$result" | grep -o 'EXIT:[0-9]*' | cut -d: -f2)
47
-
48
- if [[ "$exit_code" == "0" ]] && echo "$result" | grep -q '"decision".*"block"'; then
49
- echo -e "${GREEN}✓ BLOCKED${NC}: $name"
50
- PASS=$((PASS + 1))
51
- else
52
- echo -e "${RED}✗ NOT BLOCKED${NC}: $name"
53
- echo " Exit code: $exit_code"
54
- echo " Result: $(echo "$result" | head -3)"
55
- FAIL=$((FAIL + 1))
56
- fi
57
- }
58
-
59
- # Test helper: should allow
60
- test_should_allow() {
61
- local name="$1"
62
- local spec_content="$2"
63
- TOTAL=$((TOTAL + 1))
64
-
65
- local file_path="$TEST_DIR/.specweave/increments/0001-test/spec.md"
66
- mkdir -p "$(dirname "$file_path")"
67
-
68
- # Build JSON input for the guard
69
- # Use printf + jq -Rs to properly escape newlines in the content
70
- local json_input
71
- json_input=$(printf '%s' "$spec_content" | jq -Rs \
72
- --arg tool_name "Write" \
73
- --arg file_path "$file_path" \
74
- '{tool_name: $tool_name, tool_input: {file_path: $file_path, content: .}}')
75
-
76
- result=$(echo "$json_input" | bash "$GUARD" 2>&1; echo "EXIT:$?")
77
- exit_code=$(echo "$result" | grep -o 'EXIT:[0-9]*' | cut -d: -f2)
78
-
79
- if [[ "$exit_code" == "0" ]] && echo "$result" | grep -q '"decision".*"allow"'; then
80
- echo -e "${GREEN}✓ ALLOWED${NC}: $name"
81
- PASS=$((PASS + 1))
82
- else
83
- echo -e "${RED}✗ WRONGLY BLOCKED${NC}: $name"
84
- echo " Exit code: $exit_code"
85
- echo " Result: $(echo "$result" | head -5)"
86
- FAIL=$((FAIL + 1))
87
- fi
88
- }
89
-
90
- echo "========================================"
91
- echo " PER-US PROJECT VALIDATOR - COMPREHENSIVE TESTS"
92
- echo "========================================"
93
- echo ""
94
- echo "Test directory: $TEST_DIR"
95
- echo ""
96
-
97
- echo -e "${YELLOW}=== SIMPLE US FORMAT (### US-001:) ===${NC}"
98
-
99
- test_should_allow "Simple US with Project field" '---
100
- increment: 0001-test
101
- project: my-app
102
- ---
103
- # Feature
104
-
105
- ### US-001: Login Form
106
- **Project**: frontend-app
107
-
108
- **As a** user
109
- **I want** to log in
110
- **So that** I can access the app
111
-
112
- **Acceptance Criteria**:
113
- - [ ] **AC-US1-01**: Form exists
114
- '
115
-
116
- test_should_block "Simple US WITHOUT Project field" '---
117
- increment: 0001-test
118
- project: my-app
119
- ---
120
- # Feature
121
-
122
- ### US-001: Login Form
123
-
124
- **As a** user
125
- **I want** to log in
126
- **So that** I can access the app
127
- '
128
-
129
- echo ""
130
- echo -e "${YELLOW}=== MULTI-PROJECT US FORMAT (#### US-FE-001:) ===${NC}"
131
-
132
- test_should_allow "Multi-project US-FE-001 with Project field" '---
133
- increment: 0001-test
134
- project: my-app
135
- multi_project: true
136
- ---
137
- # Feature
138
-
139
- #### US-FE-001: Login Form UI
140
- **Project**: frontend-app
141
-
142
- **As a** user
143
- **I want** to see a login form
144
-
145
- #### US-BE-001: Auth API
146
- **Project**: backend-api
147
-
148
- **As a** frontend
149
- **I want** auth endpoints
150
- '
151
-
152
- test_should_block "Multi-project US-FE-001 WITHOUT Project field" '---
153
- increment: 0001-test
154
- project: my-app
155
- multi_project: true
156
- ---
157
- # Feature
158
-
159
- #### US-FE-001: Login Form UI
160
-
161
- **As a** user
162
- **I want** to see a login form
163
- '
164
-
165
- echo ""
166
- echo -e "${YELLOW}=== MIXED FORMAT (### with prefixes) ===${NC}"
167
-
168
- test_should_allow "Mixed: ### US-FE-001 with Project" '---
169
- increment: 0001-test
170
- project: my-app
171
- ---
172
- # Feature
173
-
174
- ### US-FE-001: Frontend Story
175
- **Project**: frontend
176
-
177
- ### US-BE-001: Backend Story
178
- **Project**: backend
179
- '
180
-
181
- test_should_block "Mixed: ### US-FE-001 without Project" '---
182
- increment: 0001-test
183
- project: my-app
184
- ---
185
- # Feature
186
-
187
- ### US-FE-001: Frontend Story
188
-
189
- ### US-BE-001: Backend Story
190
- **Project**: backend
191
- '
192
-
193
- echo ""
194
- echo -e "${YELLOW}=== SHARED PROJECT PREFIX ===${NC}"
195
-
196
- test_should_allow "US-SHARED-001 with Project" '---
197
- increment: 0001-test
198
- project: my-app
199
- ---
200
- # Feature
201
-
202
- #### US-SHARED-001: Shared Types
203
- **Project**: shared-lib
204
-
205
- **As a** developer
206
- **I want** shared types
207
- '
208
-
209
- test_should_block "US-SHARED-001 without Project" '---
210
- increment: 0001-test
211
- project: my-app
212
- ---
213
- # Feature
214
-
215
- #### US-SHARED-001: Shared Types
216
-
217
- **As a** developer
218
- **I want** shared types
219
- '
220
-
221
- echo ""
222
- echo -e "${YELLOW}=== MULTIPLE PROJECTS (1:1 VIOLATION - COMMA SEPARATED) ===${NC}"
223
-
224
- test_should_block "Multiple projects (comma separated) - FORBIDDEN" '---
225
- increment: 0001-test
226
- project: my-app
227
- ---
228
- # Feature
229
-
230
- ### US-001: Cross-cutting Story
231
- **Project**: frontend-app, backend-api
232
-
233
- **As a** user
234
- **I want** everything
235
- '
236
-
237
- test_should_block "Multiple projects with spaces" '---
238
- increment: 0001-test
239
- project: my-app
240
- ---
241
- # Feature
242
-
243
- ### US-001: Cross-cutting Story
244
- **Project**: frontend-app,backend-api,shared
245
-
246
- **As a** user
247
- **I want** everything
248
- '
249
-
250
- echo ""
251
- echo -e "${YELLOW}=== NO USER STORIES (should allow) ===${NC}"
252
-
253
- test_should_allow "Spec without any User Stories" '---
254
- increment: 0001-test
255
- project: my-app
256
- ---
257
- # Feature Overview
258
-
259
- This is just an overview document.
260
-
261
- ## Goals
262
-
263
- - Goal 1
264
- - Goal 2
265
- '
266
-
267
- test_should_allow "Tasks-only spec (no US headings)" '---
268
- increment: 0001-test
269
- project: my-app
270
- ---
271
- # Implementation Tasks
272
-
273
- ## T-001: Setup
274
-
275
- Do setup stuff.
276
- '
277
-
278
- echo ""
279
- echo -e "${YELLOW}=== ALL USER STORIES HAVE PROJECT ===${NC}"
280
-
281
- test_should_allow "Multiple US, all with Project" '---
282
- increment: 0001-test
283
- project: my-app
284
- ---
285
- # Feature
286
-
287
- ### US-001: First Story
288
- **Project**: app-a
289
-
290
- ### US-002: Second Story
291
- **Project**: app-b
292
-
293
- ### US-003: Third Story
294
- **Project**: app-c
295
- '
296
-
297
- test_should_block "Multiple US, one missing Project" '---
298
- increment: 0001-test
299
- project: my-app
300
- ---
301
- # Feature
302
-
303
- ### US-001: First Story
304
- **Project**: app-a
305
-
306
- ### US-002: Second Story
307
-
308
- ### US-003: Third Story
309
- **Project**: app-c
310
- '
311
-
312
- echo ""
313
- echo -e "${YELLOW}=== NON-WRITE TOOLS ===${NC}"
314
-
315
- # Test with Edit tool (should allow - no full content)
316
- TOTAL=$((TOTAL + 1))
317
- json_input=$(jq -n \
318
- --arg tool_name "Edit" \
319
- --arg file_path "$TEST_DIR/.specweave/increments/0001-test/spec.md" \
320
- '{tool_name: $tool_name, tool_input: {file_path: $file_path}}')
321
-
322
- result=$(echo "$json_input" | bash "$GUARD" 2>&1; echo "EXIT:$?")
323
- exit_code=$(echo "$result" | grep -o 'EXIT:[0-9]*' | cut -d: -f2)
324
- if [[ "$exit_code" == "0" ]]; then
325
- echo -e "${GREEN}✓ ALLOWED${NC}: Edit tool (no full content validation)"
326
- PASS=$((PASS + 1))
327
- else
328
- echo -e "${RED}✗ FAILED${NC}: Edit tool should be allowed"
329
- FAIL=$((FAIL + 1))
330
- fi
331
-
332
- echo ""
333
- echo -e "${YELLOW}=== BYPASS MODES ===${NC}"
334
-
335
- # Test SPECWEAVE_FORCE_PROJECT bypass
336
- TOTAL=$((TOTAL + 1))
337
- spec_content='---
338
- increment: 0001-test
339
- ---
340
- ### US-001: No Project
341
- **As a** user
342
- **I want** something
343
- '
344
- json_input=$(jq -n \
345
- --arg tool_name "Write" \
346
- --arg file_path "$TEST_DIR/.specweave/increments/0001-test/spec.md" \
347
- --arg content "$spec_content" \
348
- '{tool_name: $tool_name, tool_input: {file_path: $file_path, content: $content}}')
349
-
350
- result=$(SPECWEAVE_FORCE_PROJECT=1 bash -c "echo '$json_input' | bash '$GUARD'" 2>&1; echo "EXIT:$?")
351
- exit_code=$(echo "$result" | grep -o 'EXIT:[0-9]*' | cut -d: -f2)
352
- if [[ "$exit_code" == "0" ]] && echo "$result" | grep -q "bypassed"; then
353
- echo -e "${GREEN}✓ ALLOWED (bypass)${NC}: SPECWEAVE_FORCE_PROJECT=1"
354
- PASS=$((PASS + 1))
355
- else
356
- echo -e "${RED}✗ BYPASS FAILED${NC}: SPECWEAVE_FORCE_PROJECT=1"
357
- FAIL=$((FAIL + 1))
358
- fi
359
-
360
- # Test SPECWEAVE_LEGACY_SPEC bypass
361
- TOTAL=$((TOTAL + 1))
362
- result=$(SPECWEAVE_LEGACY_SPEC=1 bash -c "echo '$json_input' | bash '$GUARD'" 2>&1; echo "EXIT:$?")
363
- exit_code=$(echo "$result" | grep -o 'EXIT:[0-9]*' | cut -d: -f2)
364
- if [[ "$exit_code" == "0" ]] && echo "$result" | grep -q "legacy"; then
365
- echo -e "${GREEN}✓ ALLOWED (bypass)${NC}: SPECWEAVE_LEGACY_SPEC=1"
366
- PASS=$((PASS + 1))
367
- else
368
- echo -e "${RED}✗ BYPASS FAILED${NC}: SPECWEAVE_LEGACY_SPEC=1"
369
- FAIL=$((FAIL + 1))
370
- fi
371
-
372
- echo ""
373
- echo -e "${YELLOW}=== FILES OUTSIDE INCREMENTS ===${NC}"
374
-
375
- # Test file outside increments folder
376
- TOTAL=$((TOTAL + 1))
377
- json_input=$(jq -n \
378
- --arg tool_name "Write" \
379
- --arg file_path "$TEST_DIR/other/spec.md" \
380
- --arg content "no project" \
381
- '{tool_name: $tool_name, tool_input: {file_path: $file_path, content: $content}}')
382
-
383
- result=$(echo "$json_input" | bash "$GUARD" 2>&1; echo "EXIT:$?")
384
- exit_code=$(echo "$result" | grep -o 'EXIT:[0-9]*' | cut -d: -f2)
385
- if [[ "$exit_code" == "0" ]] && echo "$result" | grep -q '"decision".*"allow"'; then
386
- echo -e "${GREEN}✓ ALLOWED${NC}: spec.md outside increments folder"
387
- PASS=$((PASS + 1))
388
- else
389
- echo -e "${RED}✗ FAILED${NC}: Files outside increments should be allowed"
390
- FAIL=$((FAIL + 1))
391
- fi
392
-
393
- echo ""
394
- echo "========================================"
395
- echo " RESULTS"
396
- echo "========================================"
397
- echo -e "Total: $TOTAL"
398
- echo -e "${GREEN}Passed: $PASS${NC}"
399
- if [[ $FAIL -gt 0 ]]; then
400
- echo -e "${RED}Failed: $FAIL${NC}"
401
- exit 1
402
- else
403
- echo -e "Failed: 0"
404
- echo ""
405
- echo -e "${GREEN}ALL TESTS PASSED!${NC}"
406
- fi