specweave 1.0.98 → 1.0.99

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.
package/CLAUDE.md CHANGED
@@ -1,10 +1,10 @@
1
- <!-- SW:META template="claude" version="1.0.97" sections="header,start,autodetect,metarule,rules,workflow,reflect,context,lsp,structure,taskformat,secrets,syncing,mapping,testing,api,limits,troubleshooting,principles,linking,mcp,autoexecute,auto,docs" -->
1
+ <!-- SW:META template="claude" version="1.0.98" sections="header,start,autodetect,metarule,rules,workflow,reflect,context,lsp,structure,taskformat,secrets,syncing,mapping,testing,api,limits,troubleshooting,principles,linking,mcp,autoexecute,auto,docs" -->
2
2
 
3
- <!-- SW:SECTION:header version="1.0.97" -->
3
+ <!-- SW:SECTION:header version="1.0.98" -->
4
4
  **Framework**: SpecWeave | **Truth**: `spec.md` + `tasks.md`
5
5
  <!-- SW:END:header -->
6
6
 
7
- <!-- SW:SECTION:start version="1.0.97" -->
7
+ <!-- SW:SECTION:start version="1.0.98" -->
8
8
  ## Getting Started
9
9
 
10
10
  **Initial increment**: `0001-project-setup` (auto-created by `specweave init`)
@@ -14,7 +14,7 @@
14
14
  2. **Customize**: Edit spec.md and use for setup tasks
15
15
  <!-- SW:END:start -->
16
16
 
17
- <!-- SW:SECTION:autodetect version="1.0.97" -->
17
+ <!-- SW:SECTION:autodetect version="1.0.98" -->
18
18
  ## Auto-Detection
19
19
 
20
20
  SpecWeave auto-detects product descriptions and routes to `/sw:increment`:
@@ -24,7 +24,7 @@ SpecWeave auto-detects product descriptions and routes to `/sw:increment`:
24
24
  **Opt-out phrases**: "Just brainstorm first" | "Don't plan yet" | "Quick discussion" | "Let's explore ideas"
25
25
  <!-- SW:END:autodetect -->
26
26
 
27
- <!-- SW:SECTION:metarule version="1.0.97" -->
27
+ <!-- SW:SECTION:metarule version="1.0.98" -->
28
28
  ## Meta-Rule: Think-Before-Act
29
29
 
30
30
  **Satisfy dependencies BEFORE dependent operations.**
@@ -35,7 +35,7 @@ SpecWeave auto-detects product descriptions and routes to `/sw:increment`:
35
35
  ```
36
36
  <!-- SW:END:metarule -->
37
37
 
38
- <!-- SW:SECTION:rules version="1.0.97" -->
38
+ <!-- SW:SECTION:rules version="1.0.98" -->
39
39
  ## Rules
40
40
 
41
41
  1. **Files** → `.specweave/increments/####-name/` (spec.md, plan.md, tasks.md at root; reports/, scripts/, logs/ subfolders)
@@ -47,7 +47,7 @@ SpecWeave auto-detects product descriptions and routes to `/sw:increment`:
47
47
  7. **⛔ Initialization guard**: `.specweave/` folders MUST ONLY exist where `specweave init` was run. NEVER create `.specweave/` in parent, nested, or unrelated directories. Check `config.json` exists before creating ANY `.specweave/` subfolders.
48
48
  <!-- SW:END:rules -->
49
49
 
50
- <!-- SW:SECTION:workflow version="1.0.97" -->
50
+ <!-- SW:SECTION:workflow version="1.0.98" -->
51
51
  ## Workflow
52
52
 
53
53
  `/sw:increment "X"` → `/sw:do` → `/sw:progress` → `/sw:done 0001`
@@ -67,7 +67,7 @@ SpecWeave auto-detects product descriptions and routes to `/sw:increment`:
67
67
  **Natural language**: "Let's build X" → `/sw:increment` | "What's status?" → `/sw:progress` | "We're done" → `/sw:done` | "Ship while sleeping" → `/sw:auto`
68
68
  <!-- SW:END:workflow -->
69
69
 
70
- <!-- SW:SECTION:reflect version="1.0.97" -->
70
+ <!-- SW:SECTION:reflect version="1.0.98" -->
71
71
  ## Self-Improving Skills (Reflect)
72
72
 
73
73
  **Learn once, never repeat.** Claude learns from corrections and patterns across sessions.
@@ -111,7 +111,7 @@ ls ~/.specweave/memory/*.md 2>/dev/null
111
111
  **Enable auto-learning**: `/sw:reflect-on` → Stop hook analyzes sessions automatically
112
112
  <!-- SW:END:reflect -->
113
113
 
114
- <!-- SW:SECTION:context version="1.0.97" -->
114
+ <!-- SW:SECTION:context version="1.0.98" -->
115
115
  ## Living Docs Context
116
116
 
117
117
  **Before implementing features**: Check existing docs for patterns and decisions.
@@ -131,7 +131,7 @@ grep -ril "keyword" .specweave/docs/internal/
131
131
  **Use `/sw:context <topic>`** to load relevant living docs into conversation.
132
132
  <!-- SW:END:context -->
133
133
 
134
- <!-- SW:SECTION:lsp version="1.0.97" -->
134
+ <!-- SW:SECTION:lsp version="1.0.98" -->
135
135
  ## LSP-Enhanced Exploration
136
136
 
137
137
  **USE LSP ACTIVELY** for semantic code understanding (100x faster than grep).
@@ -148,7 +148,7 @@ go install golang.org/x/tools/gopls@latest # Go
148
148
  **Best Practices**: ALWAYS use `findReferences` before refactoring | Use `goToDefinition` instead of grep | Combine with Explore agent
149
149
  <!-- SW:END:lsp -->
150
150
 
151
- <!-- SW:SECTION:structure version="1.0.97" -->
151
+ <!-- SW:SECTION:structure version="1.0.98" -->
152
152
  ## Structure
153
153
 
154
154
  ```
@@ -218,7 +218,7 @@ my-project/
218
218
  ```
219
219
  <!-- SW:END:structure -->
220
220
 
221
- <!-- SW:SECTION:taskformat version="1.0.97" -->
221
+ <!-- SW:SECTION:taskformat version="1.0.98" -->
222
222
  ## Task Format
223
223
 
224
224
  ```markdown
@@ -228,7 +228,7 @@ my-project/
228
228
  ```
229
229
  <!-- SW:END:taskformat -->
230
230
 
231
- <!-- SW:SECTION:secrets version="1.0.97" -->
231
+ <!-- SW:SECTION:secrets version="1.0.98" -->
232
232
  ## Secrets Check
233
233
 
234
234
  **BEFORE CLI tools**: Check existing config first!
@@ -239,7 +239,7 @@ gh auth status
239
239
  ```
240
240
  <!-- SW:END:secrets -->
241
241
 
242
- <!-- SW:SECTION:syncing version="1.0.97" -->
242
+ <!-- SW:SECTION:syncing version="1.0.98" -->
243
243
  ## External Sync (GitHub/JIRA/ADO)
244
244
 
245
245
  **After increment creation**: Run `/sw-github:sync {id}` to create issues!
@@ -267,7 +267,7 @@ Living docs sync ≠ External sync. They are separate:
267
267
  **Verify tokens**: `grep GITHUB_TOKEN .env` | `gh auth status`
268
268
  <!-- SW:END:syncing -->
269
269
 
270
- <!-- SW:SECTION:mapping version="1.0.97" -->
270
+ <!-- SW:SECTION:mapping version="1.0.98" -->
271
271
  ## GitHub Mapping
272
272
 
273
273
  | SpecWeave | GitHub |
@@ -277,7 +277,7 @@ Living docs sync ≠ External sync. They are separate:
277
277
  | Task T-XXX | Checkbox |
278
278
  <!-- SW:END:mapping -->
279
279
 
280
- <!-- SW:SECTION:testing version="1.0.97" -->
280
+ <!-- SW:SECTION:testing version="1.0.98" -->
281
281
  ## Testing
282
282
 
283
283
  BDD in tasks.md | Unit >80% | `.test.ts` (Vitest)
@@ -289,7 +289,7 @@ vi.mock('fs', () => ({ readFile: vi.fn() }));
289
289
  ```
290
290
  <!-- SW:END:testing -->
291
291
 
292
- <!-- SW:SECTION:api version="1.0.97" -->
292
+ <!-- SW:SECTION:api version="1.0.98" -->
293
293
  ## API Development (OpenAPI-First)
294
294
 
295
295
  **For API projects only.** OpenAPI = source of truth → Postman derived from it.
@@ -308,13 +308,13 @@ vi.mock('fs', () => ({ readFile: vi.fn() }));
308
308
  **Import**: Postman → Import collection + env → Fill secrets → Select env
309
309
  <!-- SW:END:api -->
310
310
 
311
- <!-- SW:SECTION:limits version="1.0.97" -->
311
+ <!-- SW:SECTION:limits version="1.0.98" -->
312
312
  ## Limits
313
313
 
314
314
  **Max 1500 lines/file** — extract before adding
315
315
  <!-- SW:END:limits -->
316
316
 
317
- <!-- SW:SECTION:troubleshooting version="1.0.97" -->
317
+ <!-- SW:SECTION:troubleshooting version="1.0.98" -->
318
318
  ## Troubleshooting
319
319
 
320
320
  | Issue | Fix |
@@ -333,7 +333,7 @@ vi.mock('fs', () => ({ readFile: vi.fn() }));
333
333
  | Path patterns not working | `//path` = absolute, `/path` = relative to settings file, `additionalDirectories` for explicit working dirs |
334
334
  <!-- SW:END:troubleshooting -->
335
335
 
336
- <!-- SW:SECTION:principles version="1.0.97" -->
336
+ <!-- SW:SECTION:principles version="1.0.98" -->
337
337
  ## Principles
338
338
 
339
339
  1. **Spec-first**: `/sw:increment` before coding
@@ -343,7 +343,7 @@ vi.mock('fs', () => ({ readFile: vi.fn() }));
343
343
  5. **Clean**: All files in increment folders
344
344
  <!-- SW:END:principles -->
345
345
 
346
- <!-- SW:SECTION:linking version="1.0.97" -->
346
+ <!-- SW:SECTION:linking version="1.0.98" -->
347
347
  ## Bidirectional Linking
348
348
 
349
349
  Tasks ↔ User Stories auto-linked via AC-IDs: `AC-US1-01` → `US-001`
@@ -351,7 +351,7 @@ Tasks ↔ User Stories auto-linked via AC-IDs: `AC-US1-01` → `US-001`
351
351
  Task format: `**AC**: AC-US1-01, AC-US1-02` (CRITICAL for linking)
352
352
  <!-- SW:END:linking -->
353
353
 
354
- <!-- SW:SECTION:mcp version="1.0.97" -->
354
+ <!-- SW:SECTION:mcp version="1.0.98" -->
355
355
  ## External Service Connection
356
356
 
357
357
  **Priority**: MCP Server → REST API → CLI → Direct Connection
@@ -375,7 +375,7 @@ wrangler whoami 2>/dev/null
375
375
  ```
376
376
  <!-- SW:END:mcp -->
377
377
 
378
- <!-- SW:SECTION:autoexecute version="1.0.97" -->
378
+ <!-- SW:SECTION:autoexecute version="1.0.98" -->
379
379
  ## Auto-Execute Rule
380
380
 
381
381
  **NEVER** output "Manual Step Required" when credentials exist. **EXECUTE DIRECTLY.**
@@ -389,7 +389,7 @@ wrangler whoami 2>/dev/null && gh auth status 2>/dev/null
389
389
  ```
390
390
  <!-- SW:END:autoexecute -->
391
391
 
392
- <!-- SW:SECTION:auto version="1.0.97" -->
392
+ <!-- SW:SECTION:auto version="1.0.98" -->
393
393
  ## Auto Mode (Autonomous Execution)
394
394
 
395
395
  **Continuous execution until all tasks complete.**
@@ -466,7 +466,7 @@ wrangler whoami 2>/dev/null && gh auth status 2>/dev/null
466
466
  **Circuit Breaker**: External API fails 3x? Queue & continue
467
467
  <!-- SW:END:auto -->
468
468
 
469
- <!-- SW:SECTION:docs version="1.0.97" -->
469
+ <!-- SW:SECTION:docs version="1.0.98" -->
470
470
  ## Docs
471
471
 
472
472
  [spec-weave.com](https://spec-weave.com) | `.specweave/docs/internal/`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "specweave",
3
- "version": "1.0.98",
3
+ "version": "1.0.99",
4
4
  "description": "Spec-driven development framework for Claude Code. AI-native workflow with living documentation, intelligent agents, and multilingual support (9 languages). Enterprise-grade traceability with permanent specs and temporary increments.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -123,30 +123,84 @@ is_actionable() {
123
123
  # Must contain some actionable verb
124
124
  echo "$text" | grep -qiE "(use|don.t|never|always|should|must|avoid|prefer)" || return 1
125
125
 
126
+ # QUALITY GATE: Skip documentation examples (markdown formatting, line numbers, JSON)
127
+ # These patterns indicate the text came from docs/examples, not actual user corrections
128
+ if echo "$text" | grep -qE '^\s*[0-9]+→|^\s*[0-9]+\s*\||```|\*\*|\\n|\\"|":[[:space:]]|"sessionId"|"userType"|"cwd"'; then
129
+ return 1
130
+ fi
131
+
132
+ # QUALITY GATE: Skip content that looks like code examples with backticks
133
+ if echo "$text" | grep -qE '`[^`]+`.*`[^`]+`'; then
134
+ return 1
135
+ fi
136
+
137
+ # QUALITY GATE: Skip content with escaped quotes (JSON/code artifacts)
138
+ if echo "$text" | grep -qE '\\"|\\".*\\"'; then
139
+ return 1
140
+ fi
141
+
126
142
  return 0
127
143
  }
128
144
 
129
145
  # Extract the actual rule from a correction context
146
+ # PRIORITY: Extract the "do Y instead" part, not the "don't X" part
130
147
  extract_rule() {
131
148
  local context="$1"
132
-
133
- # Try to find the "do Y instead" part
134
149
  local rule=""
135
150
 
136
- # Pattern: "No, don't X. Use Y instead" -> "Use Y"
137
- rule=$(echo "$context" | grep -oiE "(use|prefer|always)[^.!?]*" | head -1)
138
- [ -n "$rule" ] && echo "$rule" && return
151
+ # PRIORITY 1: Look for "Use X instead" pattern
152
+ # Captures "Use logger.info() instead" - allows alphanumeric, dots, parens before "instead"
153
+ rule=$(echo "$context" | grep -oiE "[Uu]se [a-zA-Z0-9_.()/]+(\s+[a-zA-Z0-9_.()/]+)*\s+instead" | head -1)
154
+ if [ -n "$rule" ] && [ ${#rule} -gt 15 ]; then
155
+ echo "$rule" | sed 's/^[[:space:]]*//' | cut -c1-100
156
+ return
157
+ fi
158
+
159
+ # PRIORITY 2: Look for "should use X" (correction pattern)
160
+ rule=$(echo "$context" | grep -oiE "should (use|be) [^.!?]+" | head -1)
161
+ if [ -n "$rule" ] && [ ${#rule} -gt 15 ]; then
162
+ echo "$rule" | sed 's/^[[:space:]]*//' | cut -c1-100
163
+ return
164
+ fi
165
+
166
+ # PRIORITY 3: Look for sentence after "Wrong!" or "No,"
167
+ # Extract the sentence that comes after the negation marker
168
+ rule=$(echo "$context" | grep -oiE "(Wrong!?|No,) [^.!?]+" | sed 's/^[Ww]rong!*[[:space:]]*//;s/^[Nn]o,[[:space:]]*//' | head -1)
169
+ if [ -n "$rule" ] && [ ${#rule} -gt 15 ]; then
170
+ echo "$rule" | sed 's/^[[:space:]]*//' | cut -c1-100
171
+ return
172
+ fi
173
+
174
+ # PRIORITY 4: "Always X" (explicit rule)
175
+ rule=$(echo "$context" | grep -oiE "[Aa]lways [^.!?]+" | head -1)
176
+ if [ -n "$rule" ] && [ ${#rule} -gt 15 ]; then
177
+ echo "$rule" | sed 's/^[[:space:]]*//' | cut -c1-100
178
+ return
179
+ fi
180
+
181
+ # PRIORITY 5: "Never X" (prohibition rule)
182
+ rule=$(echo "$context" | grep -oiE "[Nn]ever [^.!?]+" | head -1)
183
+ if [ -n "$rule" ] && [ ${#rule} -gt 15 ]; then
184
+ echo "$rule" | sed 's/^[[:space:]]*//' | cut -c1-100
185
+ return
186
+ fi
139
187
 
140
- # Pattern: "Should be X" -> "Should be X"
141
- rule=$(echo "$context" | grep -oiE "should (be|use)[^.!?]*" | head -1)
142
- [ -n "$rule" ] && echo "$rule" && return
188
+ # PRIORITY 6: "In this project/codebase, use X"
189
+ rule=$(echo "$context" | grep -oiE "[Ii]n this (project|codebase)[^.!?]+" | head -1)
190
+ if [ -n "$rule" ] && [ ${#rule} -gt 15 ]; then
191
+ echo "$rule" | sed 's/^[[:space:]]*//' | cut -c1-100
192
+ return
193
+ fi
143
194
 
144
- # Pattern: "Never X, always Y" -> "Always Y"
145
- rule=$(echo "$context" | grep -oiE "always[^.!?]*" | head -1)
146
- [ -n "$rule" ] && echo "$rule" && return
195
+ # PRIORITY 7: "prefer X" or "use X not Y"
196
+ rule=$(echo "$context" | grep -oiE "(prefer|use) [^.!?]+ (not|instead|over) [^.!?]+" | head -1)
197
+ if [ -n "$rule" ] && [ ${#rule} -gt 15 ]; then
198
+ echo "$rule" | sed 's/^[[:space:]]*//' | cut -c1-100
199
+ return
200
+ fi
147
201
 
148
- # Fallback: return cleaned context
149
- echo "$context" | cut -c1-100
202
+ # Fallback: return cleaned context (last resort, but apply quality filter in add_rule)
203
+ echo "$context" | sed 's/^[[:space:]]*//;s/^[Uu]ser:[[:space:]]*//' | cut -c1-100
150
204
  }
151
205
 
152
206
  # Detect high-value signals in transcript
@@ -205,25 +259,51 @@ detect_signals() {
205
259
  # DEDUPLICATION
206
260
  # ============================================================================
207
261
 
208
- # Check if a similar rule already exists (by key words)
262
+ # Check if a similar rule already exists
263
+ # STRICT DEDUPLICATION: Exact substring match OR high keyword overlap
209
264
  rule_exists() {
210
265
  local file="$1"
211
266
  local new_rule="$2"
212
267
 
213
268
  [ ! -f "$file" ] && return 1
214
269
 
215
- # Extract key words from new rule (nouns, verbs)
216
- local keywords=$(echo "$new_rule" | tr '[:upper:]' '[:lower:]' | grep -oE '\b[a-z]{4,}\b' | sort -u | head -5 | tr '\n' ' ')
270
+ # Normalize rule for comparison (lowercase, trim, collapse spaces)
271
+ local normalized=$(echo "$new_rule" | tr '[:upper:]' '[:lower:]' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//;s/[[:space:]]\+/ /g')
217
272
 
218
- # Check if 3+ keywords match any existing rule
219
- local match_count=0
220
- for kw in $keywords; do
221
- if grep -qi "\b$kw\b" "$file" 2>/dev/null; then
222
- match_count=$((match_count + 1))
223
- fi
224
- done
273
+ # STRICT CHECK 1: Exact substring match (catches same rule with different prefix)
274
+ if grep -qi "$normalized" "$file" 2>/dev/null; then
275
+ return 0
276
+ fi
277
+
278
+ # STRICT CHECK 2: Rule already contains the key phrase
279
+ # Extract core action phrase (verb + object)
280
+ local core_phrase=$(echo "$normalized" | grep -oE "(use|prefer|always|never) [a-z]+( [a-z]+)?" | head -1)
281
+ if [ -n "$core_phrase" ] && grep -qi "$core_phrase" "$file" 2>/dev/null; then
282
+ return 0
283
+ fi
284
+
285
+ # STRICT CHECK 3: High keyword overlap (at least 50% of words must match a single existing rule)
286
+ local keywords=$(echo "$normalized" | grep -oE '\b[a-z]{4,}\b' | sort -u | tr '\n' ' ')
287
+ local keyword_count=$(echo "$keywords" | wc -w | tr -d ' ')
288
+
289
+ # For each existing rule, check overlap
290
+ while IFS= read -r existing_rule; do
291
+ [ -z "$existing_rule" ] && continue
292
+ local existing_lower=$(echo "$existing_rule" | tr '[:upper:]' '[:lower:]')
293
+ local match_count=0
294
+
295
+ for kw in $keywords; do
296
+ if echo "$existing_lower" | grep -q "\b$kw\b" 2>/dev/null; then
297
+ match_count=$((match_count + 1))
298
+ fi
299
+ done
300
+
301
+ # If more than 50% of keywords match ONE existing rule, it's a duplicate
302
+ local threshold=$((keyword_count / 2))
303
+ [ "$threshold" -lt 2 ] && threshold=2
304
+ [ "$match_count" -ge "$threshold" ] && return 0
305
+ done < <(grep "^- " "$file" 2>/dev/null)
225
306
 
226
- [ "$match_count" -ge 3 ] && return 0
227
307
  return 1
228
308
  }
229
309
 
@@ -236,13 +316,16 @@ categorize() {
236
316
  local lower=$(echo "$text" | tr '[:upper:]' '[:lower:]')
237
317
 
238
318
  case "$lower" in
239
- *component*|*button*|*ui*|*style*|*css*|*react*|*vue*|*html*) echo "frontend" ;;
240
- *api*|*endpoint*|*route*|*rest*|*graphql*|*server*) echo "backend" ;;
241
- *test*|*spec*|*mock*|*assert*|*expect*) echo "testing" ;;
242
- *deploy*|*docker*|*k8s*|*ci*|*terraform*|*aws*) echo "devops" ;;
243
- *auth*|*security*|*token*|*password*|*secret*) echo "security" ;;
244
- *query*|*database*|*sql*|*schema*|*table*) echo "database" ;;
245
- *file*|*path*|*import*|*export*|*module*) echo "structure" ;;
319
+ *component*|*button*|*ui*|*style*|*css*|*react*|*vue*|*html*|*tailwind*) echo "frontend" ;;
320
+ *api*|*endpoint*|*route*|*rest*|*graphql*|*server*|*backend*) echo "backend" ;;
321
+ *test*|*spec*|*mock*|*assert*|*expect*|*vitest*|*jest*|*playwright*) echo "testing" ;;
322
+ *deploy*|*docker*|*k8s*|*ci*|*terraform*|*aws*|*gcp*|*azure*) echo "devops" ;;
323
+ *auth*|*security*|*token*|*password*|*secret*|*encryption*) echo "security" ;;
324
+ *query*|*database*|*sql*|*schema*|*table*|*prisma*|*drizzle*) echo "database" ;;
325
+ *file*|*path*|*import*|*export*|*module*|*require*) echo "structure" ;;
326
+ *logger*|*log*|*console*|*debug*|*error*|*warn*) echo "logging" ;;
327
+ *type*|*interface*|*typescript*|*generic*|*enum*) echo "types" ;;
328
+ *git*|*commit*|*branch*|*merge*|*rebase*) echo "git" ;;
246
329
  *) echo "general" ;;
247
330
  esac
248
331
  }
@@ -273,14 +356,42 @@ add_rule() {
273
356
 
274
357
  local file=$(ensure_memory_file "$category")
275
358
 
359
+ # Clean and format FIRST
360
+ # Remove "User:" prefix, extra whitespace, and limit length
361
+ local clean=$(echo "$rule" | tr -d '\n\r' | sed 's/ */ /g' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//' | sed 's/^[Uu]ser:[[:space:]]*//' | cut -c1-100)
362
+
363
+ # QUALITY GATE 1: Minimum length (too short = useless)
364
+ if [ ${#clean} -lt 15 ]; then
365
+ log "info" "Skipped too short: $clean"
366
+ return 1
367
+ fi
368
+
369
+ # QUALITY GATE 2: Must contain actionable verb
370
+ if ! echo "$clean" | grep -qiE "(use|prefer|always|never|should|avoid|don't|must|do not)"; then
371
+ log "info" "Skipped non-actionable: $clean"
372
+ return 1
373
+ fi
374
+
375
+ # QUALITY GATE 3: Must have specific subject (not just a verb phrase)
376
+ local word_count=$(echo "$clean" | wc -w | tr -d ' ')
377
+ if [ "$word_count" -lt 3 ]; then
378
+ log "info" "Skipped too vague: $clean"
379
+ return 1
380
+ fi
381
+
382
+ # QUALITY GATE 4: Skip documentation/JSON artifacts
383
+ # These patterns indicate corrupted data from parsing docs or code
384
+ if echo "$clean" | grep -qE '\\n|\\"|":|→|```|\*\*|sessionId|userType|"cwd"'; then
385
+ log "info" "Skipped doc/JSON artifact: $clean"
386
+ return 1
387
+ fi
388
+
276
389
  # Skip if similar rule exists
277
- if rule_exists "$file" "$rule"; then
278
- log "info" "Skipped duplicate: $rule"
390
+ if rule_exists "$file" "$clean"; then
391
+ log "info" "Skipped duplicate: $clean"
279
392
  return 1
280
393
  fi
281
394
 
282
- # Clean and format
283
- local clean=$(echo "$rule" | tr -d '\n\r' | sed 's/ */ /g' | cut -c1-100)
284
395
  local marker="→"
285
396
  [ "$type" = "CORRECTION" ] && marker="✗→✓"
286
397
 
@@ -17,8 +17,8 @@ Smart merge for CLAUDE.md and AGENTS.md instruction files.
17
17
 
18
18
  ## When to Use
19
19
 
20
- - After running `bash scripts/refresh-marketplace.sh`
21
- - After upgrading SpecWeave version (`npm update specweave`)
20
+ - After running `specweave refresh-marketplace`
21
+ - After upgrading SpecWeave version (`npm update -g specweave`)
22
22
  - When CLAUDE.md or AGENTS.md seem outdated
23
23
  - To sync instruction files with latest framework features
24
24