specweave 1.0.203 → 1.0.205

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,4 +1,4 @@
1
- <!-- SW:META template="claude" version="1.0.202" sections="header,start,autodetect,metarule,rules,workflow,reflect,context,structure,taskformat,secrets,syncing,testing,tdd,api,limits,troubleshooting,lazyloading,principles,linking,mcp,auto,docs" -->
1
+ <!-- SW:META template="claude" version="1.0.203" sections="header,start,autodetect,metarule,rules,workflow,reflect,context,structure,taskformat,secrets,syncing,testing,tdd,api,limits,troubleshooting,lazyloading,principles,linking,mcp,auto,docs" -->
2
2
 
3
3
  <!-- SW:SECTION:hook-priority version="1.0.171" -->
4
4
  ## ⛔ Hook Instructions Override Everything
@@ -143,11 +143,27 @@ SpecWeave auto-detects product descriptions and routes to `/sw:increment`:
143
143
  **Opt-out phrases**: "Just brainstorm first" | "Don't plan yet" | "Quick discussion" | "Let's explore ideas"
144
144
  <!-- SW:END:autodetect -->
145
145
 
146
- <!-- SW:SECTION:metarule version="1.0.202" -->
147
- ## Meta-Rule: Think-Before-Act
146
+ <!-- SW:SECTION:metarule version="1.0.203" -->
147
+ ## Workflow Orchestration
148
148
 
149
- **Satisfy dependencies BEFORE dependent operations.**
149
+ ### 1. Plan Mode Default
150
+ - Enter plan mode for ANY non-trivial task (3+ steps or architectural decisions)
151
+ - If something goes sideways, **STOP and re-plan** - don't keep pushing
152
+ - Write detailed specs upfront to reduce ambiguity
153
+
154
+ ### 2. Subagent Strategy
155
+ - Use subagents liberally to keep main context clean
156
+ - Offload research, exploration, and parallel analysis to subagents
157
+ - One task per subagent for focused execution
158
+ - Append "use subagents" to requests for safe parallelization
150
159
 
160
+ ### 3. Verification Before Done
161
+ - Never mark a task complete without proving it works
162
+ - Ask yourself: **"Would a staff engineer approve this?"**
163
+ - Run tests, check logs, demonstrate correctness
164
+
165
+ ### 4. Think-Before-Act (Dependencies)
166
+ **Satisfy dependencies BEFORE dependent operations.**
151
167
  ```
152
168
  ❌ node script.js → Error → npm run build
153
169
  ✅ npm run build → node script.js → Success
@@ -159,7 +175,10 @@ SpecWeave auto-detects product descriptions and routes to `/sw:increment`:
159
175
 
160
176
  1. **Files** → `.specweave/increments/####-name/` (see Structure section for details)
161
177
  2. **Update immediately**: `Edit("tasks.md", "[ ] pending", "[x] completed")` + `Edit("spec.md", "[ ] AC-", "[x] AC-")`
162
- 3. **Unique IDs**: Check `ls .specweave/increments/ | grep "^[0-9]" | tail -5`
178
+ 3. **Unique IDs**: Check ALL folders (active, archive, abandoned):
179
+ ```bash
180
+ find .specweave/increments -maxdepth 2 -type d -name "[0-9]*" | grep -oE '[0-9]{4}E?' | sort -u | tail -5
181
+ ```
163
182
  4. **Emergency**: "emergency mode" → 1 edit, 50 lines max, no agents
164
183
  5. **⛔ Initialization guard**: `.specweave/` folders MUST ONLY exist where `specweave init` was run
165
184
  6. **⛔ Marketplace refresh**: Use `specweave refresh-marketplace` CLI (not `scripts/refresh-marketplace.sh`)
@@ -232,14 +251,8 @@ SpecWeave learns from corrections. Learnings saved here automatically. Edit or d
232
251
  - **2026-02-02**: Enable interview process during increment creation for SpecWeave projects
233
252
 
234
253
  ### General
235
- - **2026-02-02**: User wants comprehensive codebase analysis to identify unused/obsolete commands and skills using multiple parallel agents (up to 40 subagents)
236
- - **2026-02-02**: User prefers detailed investigation and leaderboard reporting on command/skill usage patterns and deletion candidates
237
- - **2026-02-02**: "Use subagents" phrase triggers safe parallelization - documented in CLAUDE.md with full guidance
238
- - **2026-02-02**: User prefers comprehensive codebase analysis with multiple parallel agents (up to 40 subagents) for identifying unused/obsolete skills and commands
239
- - **2026-02-02**: User wants leaderboard-style output showing least-used commands/skills as candidates for deletion or removal
240
- - **2026-02-02**: User wants comprehensive codebase analysis to identify least-used commands/skills as candidates for deletion/removal - prefers large-scale investigation work with multiple parallel agents
241
- - **2026-02-02**: User wants comprehensive codebase analysis using multiple parallel agents (up to 40) to identify obsolete or least-used commands and skills for removal
242
- - **2026-02-02**: User expects detailed investigation work and leaderboard-style ranking of command/skill usage patterns
254
+ - **2026-02-02**: Use subagents liberally for codebase analysis - up to 10+ concurrent for large-scale exploration
255
+ - **2026-02-02**: Prefer leaderboard-style reporting when analyzing usage patterns or identifying deletion candidates
243
256
 
244
257
  <!-- SW:SECTION:context version="1.0.202" -->
245
258
  ## Context
@@ -385,7 +398,7 @@ Enable in config: `{"apiDocs":{"enabled":true,"openApiPath":"openapi.yaml"}}`
385
398
  **Max 1500 lines/file** — extract before adding
386
399
  <!-- SW:END:limits -->
387
400
 
388
- <!-- SW:SECTION:troubleshooting version="1.0.202" -->
401
+ <!-- SW:SECTION:troubleshooting version="1.0.203" -->
389
402
  ## Troubleshooting
390
403
 
391
404
  | Issue | Fix |
@@ -393,13 +406,9 @@ Enable in config: `{"apiDocs":{"enabled":true,"openApiPath":"openapi.yaml"}}`
393
406
  | Skills/commands missing | Restart Claude Code |
394
407
  | Plugins outdated | `specweave refresh-marketplace` |
395
408
  | Out of sync | `/sw:sync-tasks` |
396
- | Find increment | `/sw:status` |
397
- | Root polluted | Move to `.specweave/increments/####/reports/` |
398
409
  | Duplicate IDs | `/sw:fix-duplicates` |
399
- | GitHub sync issues | Check config: `sync.github.enabled`, `canUpdateExternalItems` |
400
410
  | Edits blocked | Add `"additionalDirectories":["repositories"]` to `.claude/settings.json` |
401
- | Marketplace shows 0 | Normal with auto-load; `/plugin list` shows actual |
402
- | Docs folder collisions | Check: `ls docs/ \| grep -E '^[0-9]{2}-' \| cut -d'-' -f1 \| sort \| uniq -d` |
411
+ | Session stuck | Kill + `rm -f .specweave/state/*.lock` + restart |
403
412
  <!-- SW:END:troubleshooting -->
404
413
 
405
414
  <!-- SW:SECTION:lazyloading version="1.0.202" -->
@@ -416,13 +425,20 @@ export SPECWEAVE_DISABLE_AUTO_LOAD=1 # Disable auto-load
416
425
  **Token savings**: Core ~3-5K tokens vs all plugins ~60K+
417
426
  <!-- SW:END:lazyloading -->
418
427
 
419
- <!-- SW:SECTION:principles version="1.0.202" -->
428
+ <!-- SW:SECTION:principles version="1.0.203" -->
420
429
  ## Principles
421
430
 
431
+ ### SpecWeave Principles
422
432
  1. **Spec-first**: `/sw:increment` before coding
423
433
  2. **Docs = truth**: Specs guide implementation
424
434
  3. **Incremental**: Small, validated increments
425
435
  4. **Traceable**: All work → specs → ACs
436
+
437
+ ### Core Principles (Quality)
438
+ - **Simplicity First**: Make every change as simple as possible. Impact minimal code.
439
+ - **No Laziness**: Find root causes. No temporary fixes. Senior developer standards.
440
+ - **Minimal Impact**: Changes should only touch what's necessary. Avoid introducing bugs.
441
+ - **Demand Elegance**: For non-trivial changes, pause and ask "is there a more elegant way?" - but skip this for simple, obvious fixes (don't over-engineer).
426
442
  <!-- SW:END:principles -->
427
443
 
428
444
  <!-- SW:SECTION:linking version="1.0.202" -->
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "specweave",
3
- "version": "1.0.203",
3
+ "version": "1.0.205",
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",
@@ -0,0 +1,162 @@
1
+ #!/bin/bash
2
+ # log-decision.sh - Structured Decision Logging Utility
3
+ #
4
+ # Shared utility for all SpecWeave hooks to log decisions in structured JSON format.
5
+ # Inspired by Claude Code 2.1.27's "Added tool call failures and denials to debug logs"
6
+ #
7
+ # Usage:
8
+ # source log-decision.sh
9
+ # log_decision "hook_name" "decision" "reason_code" "reason" "context_json" duration_ms
10
+ #
11
+ # Output:
12
+ # - Appends JSON entry to .specweave/logs/decisions.jsonl
13
+ # - Debug output to stderr when SPECWEAVE_DEBUG_HOOKS=1
14
+ #
15
+ # Schema:
16
+ # {
17
+ # "timestamp": "ISO8601",
18
+ # "hook": "stop-auto|stop-reflect|user-prompt-submit",
19
+ # "decision": "approve|block",
20
+ # "reason": "Human-readable reason",
21
+ # "reasonCode": "machine_parseable_enum",
22
+ # "durationMs": 123,
23
+ # "context": { ... hook-specific context ... }
24
+ # }
25
+
26
+ # Max log size before rotation (10MB)
27
+ _LOG_MAX_SIZE=10485760
28
+
29
+ # Debug logging to stderr (only when SPECWEAVE_DEBUG_HOOKS=1)
30
+ _log_debug() {
31
+ if [ "${SPECWEAVE_DEBUG_HOOKS:-0}" = "1" ]; then
32
+ local color_reset="\033[0m"
33
+ local color_cyan="\033[36m"
34
+ local color_green="\033[32m"
35
+ local color_red="\033[31m"
36
+ local color_yellow="\033[33m"
37
+
38
+ local level="$1"
39
+ shift
40
+ local message="$*"
41
+
42
+ local color="$color_cyan"
43
+ case "$level" in
44
+ "DECISION") color="$color_green" ;;
45
+ "BLOCK") color="$color_red" ;;
46
+ "WARN") color="$color_yellow" ;;
47
+ esac
48
+
49
+ echo -e "${color}[DEBUG]${color_reset} $message" >&2
50
+ fi
51
+ }
52
+
53
+ # Rotate log file if it exceeds max size
54
+ _rotate_log_if_needed() {
55
+ local log_file="$1"
56
+
57
+ if [ ! -f "$log_file" ]; then
58
+ return 0
59
+ fi
60
+
61
+ local file_size
62
+ # Cross-platform file size (macOS vs Linux)
63
+ if [[ "$OSTYPE" == "darwin"* ]]; then
64
+ file_size=$(stat -f%z "$log_file" 2>/dev/null || echo "0")
65
+ else
66
+ file_size=$(stat -c%s "$log_file" 2>/dev/null || echo "0")
67
+ fi
68
+
69
+ if [ "$file_size" -gt "$_LOG_MAX_SIZE" ]; then
70
+ _log_debug "WARN" "Log rotation triggered (${file_size} > ${_LOG_MAX_SIZE})"
71
+
72
+ # Keep last ~5MB by taking last N lines
73
+ # Estimate: average 500 bytes per line, so 5MB = ~10000 lines
74
+ local temp_file="${log_file}.tmp"
75
+ tail -n 10000 "$log_file" > "$temp_file" 2>/dev/null
76
+ mv "$temp_file" "$log_file" 2>/dev/null
77
+
78
+ _log_debug "INFO" "Log rotated, kept last 10000 entries"
79
+ fi
80
+ }
81
+
82
+ # Main logging function
83
+ # Arguments:
84
+ # $1: hook_name - Name of the hook (stop-auto, stop-reflect, etc.)
85
+ # $2: decision - Decision made (approve, block)
86
+ # $3: reason_code - Machine-parseable reason code (session_inactive, work_remaining, etc.)
87
+ # $4: reason - Human-readable reason message
88
+ # $5: context_json - JSON string with hook-specific context (default: "{}")
89
+ # $6: duration_ms - Execution time in milliseconds (default: 0)
90
+ log_decision() {
91
+ local hook_name="$1"
92
+ local decision="$2"
93
+ local reason_code="$3"
94
+ local reason="$4"
95
+ local context_json="${5:-"{}"}"
96
+ local duration_ms="${6:-0}"
97
+
98
+ # Compute paths based on current PROJECT_ROOT
99
+ local project_root="${PROJECT_ROOT:-$(pwd)}"
100
+ local log_dir="$project_root/.specweave/logs"
101
+ local log_file="$log_dir/decisions.jsonl"
102
+
103
+ # Validate required arguments
104
+ if [ -z "$hook_name" ] || [ -z "$decision" ] || [ -z "$reason_code" ]; then
105
+ _log_debug "WARN" "log_decision called with missing arguments"
106
+ return 1
107
+ fi
108
+
109
+ # Ensure logs directory exists
110
+ if [ ! -d "$log_dir" ]; then
111
+ mkdir -p "$log_dir" 2>/dev/null
112
+ _log_debug "INFO" "Created logs directory: $log_dir"
113
+ fi
114
+
115
+ # Check for log rotation
116
+ _rotate_log_if_needed "$log_file"
117
+
118
+ # Generate ISO 8601 timestamp
119
+ local timestamp
120
+ timestamp=$(date -u +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date +%Y-%m-%dT%H:%M:%SZ)
121
+
122
+ # Escape special characters in reason for JSON
123
+ local escaped_reason
124
+ escaped_reason=$(printf '%s' "$reason" | sed 's/\\/\\\\/g; s/"/\\"/g; s/ /\\t/g' | tr '\n' ' ')
125
+
126
+ # Build JSON entry using jq
127
+ local json_entry
128
+ json_entry=$(jq -n -c \
129
+ --arg timestamp "$timestamp" \
130
+ --arg hook "$hook_name" \
131
+ --arg decision "$decision" \
132
+ --arg reason "$escaped_reason" \
133
+ --arg reasonCode "$reason_code" \
134
+ --argjson durationMs "$duration_ms" \
135
+ --argjson context "$context_json" \
136
+ '{
137
+ timestamp: $timestamp,
138
+ hook: $hook,
139
+ decision: $decision,
140
+ reason: $reason,
141
+ reasonCode: $reasonCode,
142
+ durationMs: $durationMs,
143
+ context: $context
144
+ }' 2>/dev/null)
145
+
146
+ # Fallback if jq fails
147
+ if [ -z "$json_entry" ]; then
148
+ json_entry="{\"timestamp\":\"$timestamp\",\"hook\":\"$hook_name\",\"decision\":\"$decision\",\"reason\":\"$escaped_reason\",\"reasonCode\":\"$reason_code\",\"durationMs\":$duration_ms,\"context\":$context_json}"
149
+ fi
150
+
151
+ # Append to log file
152
+ printf '%s\n' "$json_entry" >> "$log_file" 2>/dev/null
153
+
154
+ # Debug output
155
+ if [ "$decision" = "block" ]; then
156
+ _log_debug "BLOCK" "$hook_name: $reason_code - $reason"
157
+ else
158
+ _log_debug "DECISION" "$hook_name: $decision ($reason_code)"
159
+ fi
160
+
161
+ return 0
162
+ }
@@ -27,10 +27,36 @@
27
27
 
28
28
  set +e # Don't exit on errors
29
29
 
30
+ # Capture start time for duration tracking (macOS doesn't support %N, fallback to seconds only)
31
+ if [[ "$OSTYPE" == "darwin"* ]]; then
32
+ _START_TIME_MS=$(($(date +%s) * 1000))
33
+ else
34
+ _START_TIME_MS=$(($(date +%s) * 1000 + $(date +%N 2>/dev/null | cut -c1-3 || echo "0")))
35
+ fi
36
+
30
37
  # Read stdin (Claude Code passes context here)
31
38
  INPUT=$(cat)
32
39
  PROJECT_ROOT="${PROJECT_ROOT:-$(pwd)}"
33
40
 
41
+ # ============================================================================
42
+ # SOURCE STRUCTURED DECISION LOGGING
43
+ # ============================================================================
44
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
45
+ if [ -f "$SCRIPT_DIR/log-decision.sh" ]; then
46
+ source "$SCRIPT_DIR/log-decision.sh"
47
+ fi
48
+
49
+ # Helper to calculate duration in ms (macOS-compatible)
50
+ _get_duration_ms() {
51
+ local end_time_ms
52
+ if [[ "$OSTYPE" == "darwin"* ]]; then
53
+ end_time_ms=$(($(date +%s) * 1000))
54
+ else
55
+ end_time_ms=$(($(date +%s) * 1000 + $(date +%N 2>/dev/null | cut -c1-3 || echo "0")))
56
+ fi
57
+ echo $((end_time_ms - _START_TIME_MS))
58
+ }
59
+
34
60
  # ============================================================================
35
61
  # LOGGING
36
62
  # ============================================================================
@@ -46,7 +72,17 @@ log() {
46
72
  # SILENT APPROVE - Normal sessions get NO output
47
73
  # ============================================================================
48
74
  silent_approve() {
49
- log "APPROVE: $1"
75
+ local reason="$1"
76
+ local reason_code="${2:-session_inactive}"
77
+ local context_json="${3:-"{}"}"
78
+
79
+ log "APPROVE: $reason"
80
+
81
+ # Log structured decision if log_decision function is available
82
+ if type log_decision &>/dev/null; then
83
+ log_decision "stop-auto" "approve" "$reason_code" "$reason" "$context_json" "$(_get_duration_ms)"
84
+ fi
85
+
50
86
  echo '{"decision":"approve"}'
51
87
  exit 0
52
88
  }
@@ -60,8 +96,8 @@ INCREMENTS_DIR="$SPECWEAVE_DIR/increments"
60
96
  CONFIG_FILE="$SPECWEAVE_DIR/config.json"
61
97
 
62
98
  # Not a SpecWeave project - silent approve
63
- [ ! -d "$SPECWEAVE_DIR" ] && silent_approve "Not a SpecWeave project"
64
- [ ! -d "$INCREMENTS_DIR" ] && silent_approve "No increments directory"
99
+ [ ! -d "$SPECWEAVE_DIR" ] && silent_approve "Not a SpecWeave project" "not_specweave_project" "{}"
100
+ [ ! -d "$INCREMENTS_DIR" ] && silent_approve "No increments directory" "no_increments_dir" "{}"
65
101
 
66
102
  # ============================================================================
67
103
  # STATE DIRECTORY (MUST be defined BEFORE AUTO_SESSION_FILE check)
@@ -101,7 +137,7 @@ if [ -f "$CONFIG_FILE" ]; then
101
137
  fi
102
138
 
103
139
  # Auto mode disabled in config - silent approve
104
- [ "$AUTO_ENABLED" != "true" ] && silent_approve "Auto mode disabled in config"
140
+ [ "$AUTO_ENABLED" != "true" ] && silent_approve "Auto mode disabled in config" "auto_disabled" "{}"
105
141
 
106
142
  # ============================================================================
107
143
  # CHECK FOR AUTO MODE SESSION - Only block if explicitly activated
@@ -112,7 +148,7 @@ AUTO_SESSION_FILE="$STATE_DIR/auto-mode.json"
112
148
 
113
149
  # If no auto-mode.json exists, auto mode was never started this session
114
150
  if [ ! -f "$AUTO_SESSION_FILE" ]; then
115
- silent_approve "Auto mode not activated (no session file)"
151
+ silent_approve "Auto mode not activated (no session file)" "session_inactive" '{"sessionActive":false}'
116
152
  fi
117
153
 
118
154
  # STALENESS CHECK: If session file is older than 30 minutes, it's stale
@@ -129,13 +165,13 @@ if [ "$SESSION_AGE" -gt "$MAX_SESSION_AGE" ]; then
129
165
  rm -f "$STATE_DIR/.stop-auto-dedup" 2>/dev/null
130
166
  rm -f "$STATE_DIR/.stop-auto-retry" 2>/dev/null
131
167
  rm -f "$STATE_DIR/.stop-auto-turns" 2>/dev/null # Also clear turn counter
132
- silent_approve "Stale auto-mode session cleared (inactive for ${SESSION_AGE}s)"
168
+ silent_approve "Stale auto-mode session cleared (inactive for ${SESSION_AGE}s)" "session_stale" "$(jq -n --argjson age "$SESSION_AGE" --argjson maxAge "$MAX_SESSION_AGE" '{sessionAge:$age,maxSessionAge:$maxAge}')"
133
169
  fi
134
170
 
135
171
  # Check if session is actually active
136
172
  AUTO_SESSION_ACTIVE=$(jq -r '.active // false' "$AUTO_SESSION_FILE" 2>/dev/null || echo "false")
137
173
  if [ "$AUTO_SESSION_ACTIVE" != "true" ]; then
138
- silent_approve "Auto mode session not active"
174
+ silent_approve "Auto mode session not active" "session_inactive" '{"sessionActive":false}'
139
175
  fi
140
176
 
141
177
  # Update session file mtime to keep it fresh (touch it)
@@ -235,6 +271,20 @@ This is a safety mechanism to prevent runaway sessions.
235
271
  Current limit: $MAX_TURNS turns
236
272
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
237
273
 
274
+ # Log structured decision for turn limit
275
+ if type log_decision &>/dev/null; then
276
+ _turn_context=$(jq -n \
277
+ --argjson turnCurrent "$CURRENT_TURN" \
278
+ --argjson turnMax "$MAX_TURNS" \
279
+ --arg activeIncs "${ACTIVE_INCS:-none}" \
280
+ '{
281
+ sessionActive: true,
282
+ turn: {current: $turnCurrent, max: $turnMax},
283
+ increments: {active: ($activeIncs | split(", "))}
284
+ }')
285
+ log_decision "stop-auto" "approve" "turn_limit" "Turn limit reached: $CURRENT_TURN/$MAX_TURNS turns" "$_turn_context" "$(_get_duration_ms)"
286
+ fi
287
+
238
288
  jq -n \
239
289
  --arg decision "approve" \
240
290
  --arg reason "Turn limit reached: $CURRENT_TURN/$MAX_TURNS turns" \
@@ -255,7 +305,7 @@ if [ -f "$DEDUP_FILE" ]; then
255
305
  LAST_FIRE=$(cat "$DEDUP_FILE" 2>/dev/null || echo "0")
256
306
  ELAPSED=$((NOW - LAST_FIRE))
257
307
  [ "$ELAPSED" -gt 3600 ] && rm -f "$DEDUP_FILE" 2>/dev/null
258
- [ "$ELAPSED" -lt "$DEDUP_WINDOW" ] && silent_approve "Deduplicated (${ELAPSED}s < ${DEDUP_WINDOW}s)"
308
+ [ "$ELAPSED" -lt "$DEDUP_WINDOW" ] && silent_approve "Deduplicated (${ELAPSED}s < ${DEDUP_WINDOW}s)" "deduplicated" "$(jq -n --argjson elapsed "$ELAPSED" --argjson window "$DEDUP_WINDOW" '{elapsed:$elapsed,dedupWindow:$window}')"
259
309
  fi
260
310
 
261
311
  mkdir -p "$STATE_DIR" 2>/dev/null
@@ -390,7 +440,10 @@ count_open_acs() {
390
440
  fi
391
441
 
392
442
  # Count unchecked ACs: - [ ] **AC-
393
- grep -c '^- \[ \] \*\*AC-' "$spec_file" 2>/dev/null || echo "0"
443
+ # Note: grep -c returns exit code 1 when count is 0, so use a temp var
444
+ local count
445
+ count=$(grep -c '^- \[ \] \*\*AC-' "$spec_file" 2>/dev/null) || count=0
446
+ echo "$count"
394
447
  }
395
448
 
396
449
  # ============================================================================
@@ -738,11 +791,22 @@ if [ "$REMAINING_COUNT" -eq 0 ] && [ "$SKILL_VALIDATION_FAILED" != "true" ]; the
738
791
  clear_retry_counter
739
792
  clear_auto_session # Clear session marker - work is done!
740
793
 
794
+ # Build context for logging
795
+ _complete_context=$(jq -n \
796
+ --argjson turnCurrent "$CURRENT_TURN" \
797
+ --argjson turnMax "$MAX_TURNS" \
798
+ --argjson closedCount "$CLOSED_COUNT" \
799
+ '{
800
+ sessionActive: false,
801
+ turn: {current: $turnCurrent, max: $turnMax},
802
+ closedIncrements: $closedCount
803
+ }')
804
+
741
805
  if [ "$CLOSED_COUNT" -gt 0 ]; then
742
806
  log "APPROVE: Auto-closed $CLOSED_COUNT increment(s), all work complete"
743
- silent_approve "Auto-closed $CLOSED_COUNT increment(s)"
807
+ silent_approve "Auto-closed $CLOSED_COUNT increment(s)" "all_complete" "$_complete_context"
744
808
  else
745
- silent_approve "No active increments"
809
+ silent_approve "No active increments" "all_complete" "$_complete_context"
746
810
  fi
747
811
  fi
748
812
 
@@ -762,6 +826,16 @@ if [ "$INCOMPLETE_COUNT" -eq 0 ] && [ "$SKILL_VALIDATION_FAILED" != "true" ]; th
762
826
 
763
827
  log "APPROVE: All tasks complete in active increments (manual close pending)"
764
828
 
829
+ # Log structured decision
830
+ if type log_decision &>/dev/null; then
831
+ _work_complete_context=$(jq -n \
832
+ --argjson turnCurrent "$CURRENT_TURN" \
833
+ --argjson turnMax "$MAX_TURNS" \
834
+ --arg readyToClose "$READY_TO_CLOSE" \
835
+ '{sessionActive: false, turn: {current: $turnCurrent, max: $turnMax}, increments: {active: [], readyToClose: ($readyToClose | split(" ") | map(select(length > 0)))}}')
836
+ log_decision "stop-auto" "approve" "work_complete" "All tasks complete - increments ready for manual close" "$_work_complete_context" "$(_get_duration_ms)"
837
+ fi
838
+
765
839
  # Show message about manual close needed
766
840
  CLOSE_MSG="
767
841
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
@@ -1010,6 +1084,18 @@ if [ "$CURRENT_RETRY" -ge "$MAX_RETRIES_BEFORE_ESCALATE" ]; then
1010
1084
  clear_retry_counter
1011
1085
  clear_auto_session # End auto mode on circuit breaker
1012
1086
 
1087
+ # Log structured decision
1088
+ if type log_decision &>/dev/null; then
1089
+ _circuit_context=$(jq -n \
1090
+ --argjson turnCurrent "$CURRENT_TURN" \
1091
+ --argjson turnMax "$MAX_TURNS" \
1092
+ --argjson retryCurrent "$CURRENT_RETRY" \
1093
+ --argjson retryMax "$MAX_RETRIES_BEFORE_ESCALATE" \
1094
+ --arg activeIncs "$REMAINING_INCS" \
1095
+ '{sessionActive: false, turn: {current: $turnCurrent, max: $turnMax}, retry: {current: $retryCurrent, max: $retryMax, stuck: true}, increments: {active: ($activeIncs | split(", ") | map(select(length > 0)))}}')
1096
+ log_decision "stop-auto" "approve" "retry_limit" "Stuck session: $CURRENT_RETRY retries on same incomplete work" "$_circuit_context" "$(_get_duration_ms)"
1097
+ fi
1098
+
1013
1099
  jq -n \
1014
1100
  --arg decision "approve" \
1015
1101
  --arg reason "Stuck session: $CURRENT_RETRY retries on same incomplete work" \
@@ -1128,6 +1214,45 @@ Current status: $pending_count pending task(s), $open_acs open AC(s)
1128
1214
  Next command to run: $NEXT_COMMAND
1129
1215
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
1130
1216
 
1217
+ # Log structured decision before blocking
1218
+ if type log_decision &>/dev/null; then
1219
+ # Get previous reasons from retry file
1220
+ _prev_reasons_json="[]"
1221
+ if [ -f "$RETRY_FILE" ] && [ -s "$RETRY_FILE" ]; then
1222
+ _tmp_reasons=$(jq -c '.reasons // []' "$RETRY_FILE" 2>/dev/null)
1223
+ [ -n "$_tmp_reasons" ] && _prev_reasons_json="$_tmp_reasons"
1224
+ fi
1225
+
1226
+ # Build blocked increments array
1227
+ _blocked_json="[]"
1228
+ if [ -n "$FIRST_INC" ]; then
1229
+ _blocked_json=$(jq -n \
1230
+ --arg id "$FIRST_INC" \
1231
+ --argjson tasksPending "$pending_count" \
1232
+ --argjson acsOpen "$open_acs" \
1233
+ --arg reason "$SUCCESS_CRITERIA" \
1234
+ '[{id: $id, tasksPending: $tasksPending, acsOpen: $acsOpen, reason: $reason}]')
1235
+ fi
1236
+
1237
+ # Pre-compute stuck value (avoid subshell in jq args)
1238
+ _stuck_val="false"
1239
+ [ "$CURRENT_RETRY" -ge "$MAX_RETRIES_BEFORE_ESCALATE" ] && _stuck_val="true"
1240
+
1241
+ # Build full context JSON
1242
+ _block_context=$(jq -n \
1243
+ --argjson turnCurrent "$CURRENT_TURN" \
1244
+ --argjson turnMax "$MAX_TURNS" \
1245
+ --argjson retryCurrent "$CURRENT_RETRY" \
1246
+ --argjson retryMax "$MAX_RETRIES_BEFORE_ESCALATE" \
1247
+ --argjson stuck "$_stuck_val" \
1248
+ --arg activeIncs "$REMAINING_INCS" \
1249
+ --argjson blocked "$_blocked_json" \
1250
+ --argjson previousReasons "$_prev_reasons_json" \
1251
+ '{sessionActive: true, turn: {current: $turnCurrent, max: $turnMax}, retry: {current: $retryCurrent, max: $retryMax, stuck: $stuck}, increments: {active: ($activeIncs | split(", ") | map(select(length > 0))), blocked: $blocked}, previousReasons: $previousReasons}')
1252
+
1253
+ log_decision "stop-auto" "block" "work_remaining" "$SUCCESS_CRITERIA" "$_block_context" "$(_get_duration_ms)"
1254
+ fi
1255
+
1131
1256
  jq -n \
1132
1257
  --arg decision "block" \
1133
1258
  --arg reason "$ACTIONABLE_REASON" \
@@ -1,25 +1,426 @@
1
1
  ---
2
2
  name: image-generation
3
- description: AI image generation using Pollinations.ai - FREE with no API key required. Use when generating hero images, icons, logos, illustrations, mockups, or any visual assets for websites and apps. Covers product shots, avatars, placeholders, and social media images with professional quality.
3
+ description: AI image generation using Pollinations.ai with Stable Horde fallback - FREE with no API key required. Use when generating hero images, icons, logos, illustrations, mockups, or any visual assets for websites and apps. Covers product shots, avatars, placeholders, and social media images with professional quality.
4
4
  allowed-tools:
5
5
  - Read
6
6
  - Write
7
7
  - WebFetch
8
+ - Bash
8
9
  context: fork
9
10
  model: opus
10
11
  ---
11
12
 
12
13
  # AI Image Generation Skill
13
14
 
14
- Expert in generating professional-quality images using Pollinations.ai - a FREE, open-source AI image generation platform requiring NO API keys.
15
+ Expert in generating professional-quality images using multi-provider resilience: **Pollinations.ai** (primary) with **Stable Horde** (fallback) - both FREE, no API keys required.
16
+
17
+ ## ⚠️ CRITICAL: Health Check First (MANDATORY)
18
+
19
+ **BEFORE generating any image, ALWAYS run this CONTENT-BASED health check:**
20
+
21
+ ⚠️ **WARNING**: HTTP 200 status is NOT sufficient! Pollinations may return 200 but with error text instead of image data. **ALWAYS verify the response is actually an image.**
22
+
23
+ ```bash
24
+ # ROBUST health check - verifies actual image content (not just HTTP status)
25
+ TEMP_FILE=$(mktemp)
26
+ curl -s -L -o "$TEMP_FILE" --max-time 10 "https://image.pollinations.ai/prompt/blue%20square?width=64&height=64&nologo=true"
27
+ CONTENT_TYPE=$(file -b "$TEMP_FILE" | head -c 10)
28
+
29
+ if [[ "$CONTENT_TYPE" == "PNG image" ]] || [[ "$CONTENT_TYPE" == "JPEG image" ]]; then
30
+ echo "✅ HEALTHY - Use Pollinations"
31
+ else
32
+ echo "❌ BROKEN - Use Stable Horde (got: $CONTENT_TYPE)"
33
+ cat "$TEMP_FILE" # Show error message
34
+ fi
35
+ rm -f "$TEMP_FILE"
36
+ ```
37
+
38
+ ### Decision Tree Based on CONTENT (Not Just Status)
39
+
40
+ | Content Check | Meaning | Action |
41
+ |---------------|---------|--------|
42
+ | `PNG image data` | ✅ Healthy | Use Pollinations.ai |
43
+ | `JPEG image data` | ✅ Healthy | Use Pollinations.ai |
44
+ | `ASCII text` | ❌ Service error (502/503 in body) | **→ Use Stable Horde** |
45
+ | `HTML document` | ❌ Error page | **→ Use Stable Horde** |
46
+ | Empty/timeout | ❌ Network error | **→ Use Stable Horde** |
47
+
48
+ ### Automated Provider Selection Script (ROBUST VERSION)
49
+
50
+ ```bash
51
+ #!/bin/bash
52
+ # save as: check-image-api.sh
53
+ # IMPORTANT: This checks CONTENT, not just HTTP status!
54
+
55
+ TEMP_FILE=$(mktemp)
56
+ curl -s -L -o "$TEMP_FILE" --max-time 15 \
57
+ "https://image.pollinations.ai/prompt/test%20square?width=64&height=64&nologo=true" 2>/dev/null
58
+
59
+ CONTENT_TYPE=$(file -b "$TEMP_FILE" 2>/dev/null | cut -d',' -f1)
60
+ rm -f "$TEMP_FILE"
61
+
62
+ if [[ "$CONTENT_TYPE" == "PNG image data" ]] || [[ "$CONTENT_TYPE" == "JPEG image data" ]]; then
63
+ echo "PROVIDER=pollinations"
64
+ echo "STATUS=healthy"
65
+ else
66
+ echo "PROVIDER=stablehorde"
67
+ echo "REASON=Pollinations returned '$CONTENT_TYPE' instead of image"
68
+ fi
69
+ ```
70
+
71
+ ### Why HTTP 200 Is Misleading
72
+
73
+ Pollinations.ai uses Cloudflare CDN. When the origin server is down:
74
+ - Cloudflare returns **HTTP 200** (edge responds)
75
+ - But the BODY contains: `502 Bad Gateway - Unable to reach the origin service`
76
+ - This is why **content-based checking is mandatory**
77
+
78
+ ---
79
+
80
+ ## 🚨 NEVER GET STUCK Protocol (Claude Code Behavior)
81
+
82
+ **This section defines how Claude Code should behave to NEVER get stuck on image generation.**
83
+
84
+ ### Rule 1: Always Set Timeouts
85
+
86
+ ```bash
87
+ # ALWAYS use --max-time flag (15-30 seconds max)
88
+ curl --max-time 15 "https://image.pollinations.ai/prompt/..."
89
+
90
+ # NEVER use curl without timeout (can hang forever)
91
+ curl "https://image.pollinations.ai/prompt/..." # ❌ WRONG - can hang
92
+ ```
93
+
94
+ ### Rule 2: Fail Fast, Switch Immediately
95
+
96
+ If ANY of these occur, **immediately switch to Stable Horde**:
97
+ - Timeout (>15 seconds)
98
+ - Non-image response (ASCII text, HTML)
99
+ - HTTP 4xx/5xx errors
100
+ - API Error 400 "Could not process image"
101
+
102
+ **DO NOT:**
103
+ - ❌ Retry more than once
104
+ - ❌ Wait indefinitely
105
+ - ❌ Keep trying the same failing provider
106
+
107
+ ### Rule 3: Check Before Acting
108
+
109
+ **ALWAYS run health check BEFORE attempting image generation:**
110
+
111
+ ```bash
112
+ # Quick pre-flight check (5 second max)
113
+ TEMP=$(mktemp)
114
+ timeout 5 curl -s -o "$TEMP" "https://image.pollinations.ai/prompt/test?width=64&height=64" 2>/dev/null
115
+ if file "$TEMP" | grep -q "image"; then
116
+ echo "Provider ready"
117
+ else
118
+ echo "Provider down - use Stable Horde"
119
+ fi
120
+ rm -f "$TEMP"
121
+ ```
122
+
123
+ ### Rule 4: Structured Error Handling
124
+
125
+ When image generation fails, follow this exact flow:
126
+
127
+ ```
128
+ 1. Try Pollinations (timeout: 15s)
129
+ ├── Success (image data) → Done ✅
130
+ └── Failure (any error) → Step 2
131
+
132
+ 2. Try Stable Horde (timeout: 120s for full job)
133
+ ├── Success → Done ✅
134
+ └── Failure → Step 3
135
+
136
+ 3. Report failure and STOP
137
+ └── Tell user: "Image generation unavailable. Both Pollinations.ai and Stable Horde are down."
138
+ └── DO NOT keep retrying
139
+ ```
140
+
141
+ ### Rule 5: Exit Conditions (MANDATORY)
142
+
143
+ Claude Code MUST exit the image generation attempt when:
144
+
145
+ | Condition | Action | Max Attempts |
146
+ |-----------|--------|--------------|
147
+ | Timeout | Switch provider | 1 |
148
+ | HTTP 5xx | Switch provider | 1 |
149
+ | "Could not process image" | Switch provider | 1 |
150
+ | Non-image response | Switch provider | 1 |
151
+ | Both providers fail | **STOP and report** | 0 |
152
+
153
+ ### Example: Non-Blocking Image Generation
154
+
155
+ ```typescript
156
+ async function generateImageNonBlocking(prompt: string): Promise<string | null> {
157
+ const TIMEOUT_MS = 15000;
158
+
159
+ // Try Pollinations first
160
+ try {
161
+ const controller = new AbortController();
162
+ const timeoutId = setTimeout(() => controller.abort(), TIMEOUT_MS);
163
+
164
+ const response = await fetch(
165
+ `https://image.pollinations.ai/prompt/${encodeURIComponent(prompt)}?width=512&height=512`,
166
+ { signal: controller.signal }
167
+ );
168
+ clearTimeout(timeoutId);
169
+
170
+ const contentType = response.headers.get('content-type');
171
+ if (contentType?.startsWith('image/')) {
172
+ return await response.blob().then(b => URL.createObjectURL(b));
173
+ }
174
+ // Not an image - fall through to Stable Horde
175
+ } catch (e) {
176
+ console.log('Pollinations failed, trying Stable Horde');
177
+ }
178
+
179
+ // Try Stable Horde
180
+ try {
181
+ return await generateViaStableHorde(prompt);
182
+ } catch (e) {
183
+ console.error('Both providers failed');
184
+ return null; // STOP - don't keep retrying
185
+ }
186
+ }
187
+ ```
188
+
189
+ ### Claude API "Could Not Process Image" Error
190
+
191
+ When you see this error:
192
+ ```
193
+ API Error: 400 {"type":"error","error":{"type":"invalid_request_error","message":"Could not process image"}}
194
+ ```
195
+
196
+ **This means Claude's vision API cannot process the image.** Solutions:
197
+ 1. The image file may be corrupted or incomplete (502 error in disguise)
198
+ 2. The image format is unsupported
199
+ 3. The image URL returned an error page, not actual image data
200
+
201
+ **Action:** Do NOT retry with Claude's Read tool. Instead:
202
+ 1. Download the image with curl first
203
+ 2. Verify it's actually an image: `file downloaded.png`
204
+ 3. If not an image → use Stable Horde fallback
15
205
 
16
206
  ## Quick Reference
17
207
 
18
- **Generate any image instantly:**
208
+ ### Primary: Pollinations.ai (when healthy)
19
209
  ```
20
210
  https://image.pollinations.ai/prompt/YOUR_PROMPT_HERE
21
211
  ```
22
212
 
213
+ ### Fallback: Stable Horde (when Pollinations is down)
214
+ ```
215
+ POST https://stablehorde.net/api/v2/generate/async
216
+ Content-Type: application/json
217
+
218
+ {"prompt": "YOUR_PROMPT_HERE", "params": {"width": 512, "height": 512}}
219
+ ```
220
+
221
+ ---
222
+
223
+ ## Stable Horde Fallback API (FREE, No API Key)
224
+
225
+ When Pollinations.ai returns 5xx errors, use Stable Horde - a community-powered, 100% free alternative.
226
+
227
+ ### Why Stable Horde Works
228
+
229
+ - ✅ No API key required (anonymous mode)
230
+ - ✅ No rate limits for reasonable usage
231
+ - ✅ High-quality Stable Diffusion models
232
+ - ✅ Community-powered (crowdsourced GPUs)
233
+ - ⏱️ Generation time: 30-120 seconds (varies by queue)
234
+
235
+ ### Step 1: Submit Generation Request
236
+
237
+ ```bash
238
+ curl -X POST "https://stablehorde.net/api/v2/generate/async" \
239
+ -H "Content-Type: application/json" \
240
+ -H "apikey: 0000000000" \
241
+ -d '{
242
+ "prompt": "a majestic mountain landscape, professional photography, 8k, detailed",
243
+ "params": {
244
+ "width": 512,
245
+ "height": 512,
246
+ "steps": 30,
247
+ "cfg_scale": 7.5,
248
+ "sampler_name": "k_euler_a"
249
+ },
250
+ "nsfw": false,
251
+ "censor_nsfw": true,
252
+ "models": ["stable_diffusion"]
253
+ }'
254
+ ```
255
+
256
+ **Response:**
257
+ ```json
258
+ {
259
+ "id": "abc123-def456-ghi789",
260
+ "kudos": 10
261
+ }
262
+ ```
263
+
264
+ ### Step 2: Poll for Completion
265
+
266
+ ```bash
267
+ # Poll every 5 seconds until done
268
+ curl -s "https://stablehorde.net/api/v2/generate/check/abc123-def456-ghi789"
269
+ ```
270
+
271
+ **Response when processing:**
272
+ ```json
273
+ {
274
+ "done": false,
275
+ "wait_time": 45,
276
+ "queue_position": 3,
277
+ "processing": 1
278
+ }
279
+ ```
280
+
281
+ **Response when complete:**
282
+ ```json
283
+ {
284
+ "done": true,
285
+ "generations": [
286
+ {
287
+ "img": "base64_encoded_image_data...",
288
+ "seed": "12345",
289
+ "worker_id": "worker-abc",
290
+ "model": "stable_diffusion"
291
+ }
292
+ ]
293
+ }
294
+ ```
295
+
296
+ ### Step 3: Save the Image
297
+
298
+ ```bash
299
+ # Extract and save base64 image
300
+ curl -s "https://stablehorde.net/api/v2/generate/status/abc123-def456-ghi789" | \
301
+ jq -r '.generations[0].img' | base64 -d > output.png
302
+ ```
303
+
304
+ ### Complete Node.js Example with Fallback
305
+
306
+ ```typescript
307
+ import https from 'https';
308
+ import fs from 'fs';
309
+
310
+ interface ImageOptions {
311
+ prompt: string;
312
+ width?: number;
313
+ height?: number;
314
+ outputPath: string;
315
+ }
316
+
317
+ // Health check for Pollinations
318
+ async function checkPollinationsHealth(): Promise<boolean> {
319
+ return new Promise((resolve) => {
320
+ const req = https.get(
321
+ 'https://image.pollinations.ai/prompt/health?width=64&height=64',
322
+ { timeout: 3000 },
323
+ (res) => resolve(res.statusCode === 200)
324
+ );
325
+ req.on('error', () => resolve(false));
326
+ req.on('timeout', () => { req.destroy(); resolve(false); });
327
+ });
328
+ }
329
+
330
+ // Generate via Pollinations (primary)
331
+ async function generatePollinations(options: ImageOptions): Promise<string> {
332
+ const { prompt, width = 1024, height = 1024, outputPath } = options;
333
+ const url = `https://image.pollinations.ai/prompt/${encodeURIComponent(prompt)}?width=${width}&height=${height}&nologo=true`;
334
+
335
+ return new Promise((resolve, reject) => {
336
+ https.get(url, (res) => {
337
+ if (res.statusCode !== 200) {
338
+ reject(new Error(`Pollinations returned ${res.statusCode}`));
339
+ return;
340
+ }
341
+ const file = fs.createWriteStream(outputPath);
342
+ res.pipe(file);
343
+ file.on('finish', () => { file.close(); resolve(outputPath); });
344
+ }).on('error', reject);
345
+ });
346
+ }
347
+
348
+ // Generate via Stable Horde (fallback)
349
+ async function generateStableHorde(options: ImageOptions): Promise<string> {
350
+ const { prompt, width = 512, height = 512, outputPath } = options;
351
+
352
+ // Step 1: Submit job
353
+ const jobId = await submitHordeJob(prompt, width, height);
354
+ console.log(`Stable Horde job submitted: ${jobId}`);
355
+
356
+ // Step 2: Poll until done
357
+ let done = false;
358
+ let imageData: string | null = null;
359
+
360
+ while (!done) {
361
+ await new Promise(r => setTimeout(r, 5000)); // Wait 5s
362
+ const status = await checkHordeStatus(jobId);
363
+ console.log(`Queue position: ${status.queue_position}, Wait: ${status.wait_time}s`);
364
+
365
+ if (status.done) {
366
+ done = true;
367
+ imageData = status.generations[0]?.img;
368
+ }
369
+ }
370
+
371
+ // Step 3: Save image
372
+ if (imageData) {
373
+ fs.writeFileSync(outputPath, Buffer.from(imageData, 'base64'));
374
+ return outputPath;
375
+ }
376
+ throw new Error('No image generated');
377
+ }
378
+
379
+ // Main function with automatic fallback
380
+ async function generateImage(options: ImageOptions): Promise<string> {
381
+ console.log('Checking Pollinations.ai health...');
382
+ const pollinationsHealthy = await checkPollinationsHealth();
383
+
384
+ if (pollinationsHealthy) {
385
+ console.log('✅ Pollinations healthy, using primary provider');
386
+ try {
387
+ return await generatePollinations(options);
388
+ } catch (err) {
389
+ console.log('⚠️ Pollinations failed, falling back to Stable Horde');
390
+ return await generateStableHorde(options);
391
+ }
392
+ } else {
393
+ console.log('❌ Pollinations down (502/503), using Stable Horde fallback');
394
+ return await generateStableHorde(options);
395
+ }
396
+ }
397
+
398
+ // Usage
399
+ generateImage({
400
+ prompt: 'a futuristic city at sunset, cyberpunk, neon lights, 8k',
401
+ width: 1024,
402
+ height: 1024,
403
+ outputPath: './generated-image.png'
404
+ }).then(path => console.log(`Image saved to: ${path}`));
405
+ ```
406
+
407
+ ### Stable Horde Parameters Reference
408
+
409
+ | Parameter | Values | Default | Description |
410
+ |-----------|--------|---------|-------------|
411
+ | `width` | 64-1024 (multiples of 64) | 512 | Image width |
412
+ | `height` | 64-1024 (multiples of 64) | 512 | Image height |
413
+ | `steps` | 1-150 | 30 | Diffusion steps |
414
+ | `cfg_scale` | 1-30 | 7.5 | Prompt adherence |
415
+ | `sampler_name` | k_euler_a, k_dpm_2, etc. | k_euler_a | Sampling method |
416
+ | `models` | ["stable_diffusion", "SDXL 1.0"] | auto | Model selection |
417
+
418
+ ### Stable Horde Web UI Alternative
419
+
420
+ If you prefer a visual interface: **[ArtBot](https://tinybots.net/artbot)** - free web UI for Stable Horde.
421
+
422
+ ---
423
+
23
424
  ## When This Skill Activates
24
425
 
25
426
  This skill auto-activates when you need images for:
@@ -343,15 +744,101 @@ async function getOrGenerateImage(prompt: string, options: ImageOptions) {
343
744
 
344
745
  ## Troubleshooting
345
746
 
747
+ ### HTTP Error Codes & Solutions
748
+
749
+ | Error Code | Provider | Meaning | Solution |
750
+ |------------|----------|---------|----------|
751
+ | `200` | Both | ✅ Success | Image generated |
752
+ | `400` | Both | Bad Request | Fix prompt (invalid characters, too long) |
753
+ | `429` | Pollinations | Rate Limited | Wait 15s or switch to Stable Horde |
754
+ | `500` | Both | Internal Error | Retry once, then switch provider |
755
+ | `502` | Pollinations | **Bad Gateway** | **→ Use Stable Horde immediately** |
756
+ | `503` | Both | Service Unavailable | **→ Use Stable Horde immediately** |
757
+ | `504` | Pollinations | Gateway Timeout | **→ Use Stable Horde immediately** |
758
+ | `000` | Both | Network/DNS Error | Check internet, try Stable Horde |
759
+
760
+ ### Common Issues & Fixes
761
+
346
762
  | Issue | Solution |
347
763
  |-------|----------|
348
- | Slow generation | Use `turbo` model for faster results |
764
+ | **Claude Code stuck** | Run health check first, use fallback on 5xx |
765
+ | Slow generation | Use `turbo` model (Pollinations) or reduce steps (Horde) |
349
766
  | Poor quality | Add quality modifiers, use `flux` or `flux-realism` |
350
767
  | Wrong style | Specify style explicitly: "photograph", "illustration" |
351
768
  | Watermark appears | Add `nologo=true` parameter |
352
769
  | Inconsistent results | Use same `seed` parameter |
353
- | Rate limited | Wait 15s between requests or register for higher limits |
770
+ | Rate limited | Wait 15s or use Stable Horde (no rate limits) |
354
771
  | Image not loading | URL-encode the prompt properly |
772
+ | Stable Horde slow | Normal: 30-120s queue time, be patient |
773
+ | Base64 decode fails | Ensure you're getting the full `img` field |
774
+
775
+ ### Quick Diagnostic Script (Content-Based)
776
+
777
+ ```bash
778
+ #!/bin/bash
779
+ # diagnose-image-api.sh - Run this when image generation fails
780
+ # IMPORTANT: Uses CONTENT checking, not just HTTP status!
781
+
782
+ echo "=== Image API Diagnostics (Content-Based) ==="
783
+
784
+ # Test Pollinations with actual image download
785
+ echo -n "1. Pollinations.ai: "
786
+ TEMP_FILE=$(mktemp)
787
+ curl -s -L -o "$TEMP_FILE" --max-time 15 \
788
+ "https://image.pollinations.ai/prompt/diagnostic%20test?width=64&height=64&nologo=true" 2>/dev/null
789
+ P_CONTENT=$(file -b "$TEMP_FILE" 2>/dev/null | cut -d',' -f1)
790
+
791
+ if [[ "$P_CONTENT" == "PNG image data" ]] || [[ "$P_CONTENT" == "JPEG image data" ]]; then
792
+ echo "✅ HEALTHY (returns actual images)"
793
+ P_HEALTHY=true
794
+ elif [[ "$P_CONTENT" == "ASCII text" ]]; then
795
+ echo "❌ BROKEN (returns error text: $(head -c 50 "$TEMP_FILE"))"
796
+ P_HEALTHY=false
797
+ elif [[ -z "$P_CONTENT" ]]; then
798
+ echo "❌ TIMEOUT or empty response"
799
+ P_HEALTHY=false
800
+ else
801
+ echo "⚠️ UNKNOWN ($P_CONTENT)"
802
+ P_HEALTHY=false
803
+ fi
804
+ rm -f "$TEMP_FILE"
805
+
806
+ # Test Stable Horde API
807
+ echo -n "2. Stable Horde: "
808
+ H_STATUS=$(curl -s -o /dev/null -w "%{http_code}" --max-time 5 \
809
+ "https://stablehorde.net/api/v2/status/heartbeat" 2>/dev/null || echo "TIMEOUT")
810
+ if [[ "$H_STATUS" == "200" ]]; then
811
+ echo "✅ HEALTHY (API responding)"
812
+ H_HEALTHY=true
813
+ else
814
+ echo "❌ ISSUE ($H_STATUS)"
815
+ H_HEALTHY=false
816
+ fi
817
+
818
+ # Test internet
819
+ echo -n "3. Internet: "
820
+ I_STATUS=$(curl -s -o /dev/null -w "%{http_code}" --max-time 3 "https://google.com" 2>/dev/null || echo "TIMEOUT")
821
+ if [[ "$I_STATUS" == "200" || "$I_STATUS" == "301" ]]; then
822
+ echo "✅ OK"
823
+ else
824
+ echo "❌ NO INTERNET"
825
+ fi
826
+
827
+ echo ""
828
+ echo "=== Recommendation ==="
829
+ if [[ "$P_HEALTHY" == "true" ]]; then
830
+ echo "✅ Use: Pollinations.ai (primary) - working normally"
831
+ elif [[ "$H_HEALTHY" == "true" ]]; then
832
+ echo "🔄 Use: Stable Horde (fallback) - Pollinations is currently down"
833
+ echo ""
834
+ echo "Stable Horde usage:"
835
+ echo " 1. POST to https://stablehorde.net/api/v2/generate/async"
836
+ echo " 2. Poll https://stablehorde.net/api/v2/generate/check/{id}"
837
+ echo " 3. Get result from https://stablehorde.net/api/v2/generate/status/{id}"
838
+ else
839
+ echo "❌ Both services unavailable. Check internet connection."
840
+ fi
841
+ ```
355
842
 
356
843
  ## Integration with Frontend Design
357
844
 
@@ -23,6 +23,8 @@
23
23
  | Section | Search For | Purpose |
24
24
  |---------|------------|---------|
25
25
  | Rules | `#essential-rules` | Critical rules, file organization |
26
+ | **Orchestration** | `#workflow-orchestration` | **Plan Mode, Subagents, Verification** |
27
+ | **Principles** | `#core-principles` | **Quality: Simplicity, No Laziness** |
26
28
  | Commands | `#commands` | All SpecWeave commands |
27
29
  | **Hooks** | `#non-claude-tools` | **CRITICAL: Hook behavior to mimic** |
28
30
  | **User Story** | `#user-story-format` | **CRITICAL: Project/Board fields** |
@@ -100,6 +102,171 @@
100
102
 
101
103
  ---
102
104
 
105
+ <!-- SECTION:orchestration required -->
106
+ ## Workflow Orchestration {#workflow-orchestration}
107
+
108
+ **Claude Code has built-in orchestration features. Non-Claude tools must implement these manually.**
109
+
110
+ ### 1. Plan Mode Default
111
+
112
+ **Claude Code**: Has `EnterPlanMode` tool that automatically enters planning state for complex tasks.
113
+
114
+ **Non-Claude Tools - Manual Planning Protocol:**
115
+ ```
116
+ BEFORE implementing ANY non-trivial task (3+ steps):
117
+
118
+ 1. STOP - Don't start coding immediately
119
+ 2. Create `.specweave/increments/XXXX-feature/plan.md`
120
+ 3. List all steps required
121
+ 4. Identify dependencies between steps
122
+ 5. Note architectural decisions needed
123
+ 6. GET USER APPROVAL before implementing
124
+
125
+ If something goes sideways during implementation:
126
+ → STOP and re-plan (don't keep pushing)
127
+ → Update plan.md with revised approach
128
+ → Get approval again if scope changed
129
+ ```
130
+
131
+ **Planning Template (plan.md):**
132
+ ```markdown
133
+ # Plan: Feature Name
134
+
135
+ ## Approach
136
+ [High-level approach description]
137
+
138
+ ## Steps
139
+ 1. [ ] Step one
140
+ 2. [ ] Step two (depends on: #1)
141
+ 3. [ ] Step three
142
+
143
+ ## Risks/Decisions
144
+ - [ ] Decision: [question needing user input]
145
+ - Risk: [potential issue and mitigation]
146
+
147
+ ## Approval
148
+ - [ ] User approved this plan
149
+ ```
150
+
151
+ ### 2. Subagent Strategy (Parallel Execution)
152
+
153
+ **Claude Code**: Can spawn subagents with `Task` tool for parallel work.
154
+
155
+ **Non-Claude Tools - Manual Parallelization:**
156
+ ```
157
+ For large exploration/analysis tasks:
158
+
159
+ Option A: Sequential Breakdown
160
+ 1. Split work into independent chunks
161
+ 2. Process one chunk at a time
162
+ 3. Aggregate results
163
+
164
+ Option B: Parallel Prompts (Cursor/Copilot)
165
+ 1. Open multiple chat sessions
166
+ 2. Give each session one focused task
167
+ 3. Combine outputs manually
168
+
169
+ Best practices:
170
+ - One task per "subagent" (focused execution)
171
+ - Keep analysis/exploration separate from implementation
172
+ - Use checklists to track parallel workstreams
173
+ ```
174
+
175
+ **When to use parallel approach:**
176
+ - Codebase exploration (search multiple areas)
177
+ - Multi-file analysis (review patterns across modules)
178
+ - Batch validation (check multiple files for issues)
179
+ - Large-scale refactoring analysis
180
+
181
+ ### 3. Verification Before Done
182
+
183
+ **Claude Code**: PostToolUse hooks validate completion automatically.
184
+
185
+ **Non-Claude Tools - Manual Verification Checklist:**
186
+ ```
187
+ ⛔ NEVER mark a task complete without proving it works!
188
+
189
+ Before marking ANY task as [x] completed:
190
+
191
+ □ Code compiles/builds successfully
192
+ □ Tests pass (run: npm test, pytest, etc.)
193
+ □ Manual verification performed (if applicable)
194
+ □ Acceptance criteria actually satisfied (re-read AC)
195
+ □ No console errors in browser (for frontend)
196
+ □ API returns expected responses (for backend)
197
+
198
+ Ask yourself: "Would a staff engineer approve this?"
199
+
200
+ If answer is NO → task is NOT complete
201
+ ```
202
+
203
+ **Verification Commands by Stack:**
204
+ ```bash
205
+ # JavaScript/TypeScript
206
+ npm run build && npm test
207
+
208
+ # Python
209
+ pytest && mypy .
210
+
211
+ # .NET
212
+ dotnet build && dotnet test
213
+
214
+ # General
215
+ git diff # Review what actually changed
216
+ ```
217
+
218
+ ### 4. Think-Before-Act (Dependencies)
219
+
220
+ **Satisfy dependencies BEFORE dependent operations.**
221
+
222
+ ```
223
+ ❌ Wrong: node script.js → Error → npm run build
224
+ ✅ Correct: npm run build → node script.js → Success
225
+
226
+ ❌ Wrong: Import module → Error → Install package
227
+ ✅ Correct: npm install package → Import module → Success
228
+ ```
229
+
230
+ **Dependency Detection Questions:**
231
+ 1. Does this require a build step first?
232
+ 2. Are all imports/packages installed?
233
+ 3. Does this depend on another file being created?
234
+ 4. Is there a database migration needed?
235
+ 5. Are environment variables configured?
236
+ <!-- /SECTION -->
237
+
238
+ ---
239
+
240
+ <!-- SECTION:principles required -->
241
+ ## Core Principles (Quality) {#core-principles}
242
+
243
+ ### Simplicity First
244
+ - Write the simplest code that solves the problem
245
+ - Avoid over-engineering and premature optimization
246
+ - One function = one responsibility
247
+ - If you can delete code and tests still pass, delete it
248
+
249
+ ### No Laziness
250
+ - Don't leave TODO comments for "later"
251
+ - Don't skip error handling because "it probably won't fail"
252
+ - Don't copy-paste without understanding
253
+ - Test edge cases, not just happy paths
254
+
255
+ ### Minimal Impact
256
+ - Change only what's necessary for the task
257
+ - Don't refactor adjacent code unless asked
258
+ - Keep PRs focused and reviewable
259
+ - Preserve existing patterns unless improving them is the task
260
+
261
+ ### Demand Elegance (Balanced)
262
+ - Code should be readable by humans first
263
+ - Names should reveal intent
264
+ - BUT: Don't over-abstract for hypothetical futures
265
+ - Pragmatic > Perfect
266
+ <!-- /SECTION -->
267
+
268
+ ---
269
+
103
270
  <!-- SECTION:commands required -->
104
271
  ## Commands Reference {#commands}
105
272
 
@@ -129,7 +296,21 @@
129
296
  <!-- SECTION:nonclaudetools required -->
130
297
  ## Non-Claude Tools (Cursor, Copilot, etc.) {#non-claude-tools}
131
298
 
132
- **CRITICAL**: Claude Code has automatic hooks. Other tools DO NOT.
299
+ **CRITICAL**: Claude Code has automatic hooks and orchestration. Other tools DO NOT.
300
+
301
+ > **See also**: [Workflow Orchestration](#workflow-orchestration) for Plan Mode, Subagent Strategy, and Verification protocols.
302
+
303
+ ### Built-in vs Manual - Complete Comparison
304
+
305
+ | Capability | Claude Code | Non-Claude Tools |
306
+ |------------|-------------|------------------|
307
+ | **Plan Mode** | `EnterPlanMode` tool (automatic) | Manual: Create plan.md, get approval |
308
+ | **Subagents** | `Task` tool spawns parallel agents | Manual: Split work, parallel prompts |
309
+ | **Verification** | PostToolUse hooks validate | Manual: Run tests, check checklist |
310
+ | **Hooks** | Auto-run on events | YOU must mimic (see below) |
311
+ | **Task sync** | Automatic AC updates | Manual: Edit tasks.md + spec.md |
312
+ | **Commands** | Slash syntax works | Read command .md, follow manually |
313
+ | **Skills** | Auto-activate on keywords | Read SKILL.md, follow workflow |
133
314
 
134
315
  ### Latest Features
135
316
 
@@ -151,10 +151,26 @@ SpecWeave auto-detects product descriptions and routes to `/sw:increment`:
151
151
  <!-- /SECTION -->
152
152
 
153
153
  <!-- SECTION:metarule required -->
154
- ## Meta-Rule: Think-Before-Act
154
+ ## Workflow Orchestration
155
155
 
156
- **Satisfy dependencies BEFORE dependent operations.**
156
+ ### 1. Plan Mode Default
157
+ - Enter plan mode for ANY non-trivial task (3+ steps or architectural decisions)
158
+ - If something goes sideways, **STOP and re-plan** - don't keep pushing
159
+ - Write detailed specs upfront to reduce ambiguity
160
+
161
+ ### 2. Subagent Strategy
162
+ - Use subagents liberally to keep main context clean
163
+ - Offload research, exploration, and parallel analysis to subagents
164
+ - One task per subagent for focused execution
165
+ - Append "use subagents" to requests for safe parallelization
157
166
 
167
+ ### 3. Verification Before Done
168
+ - Never mark a task complete without proving it works
169
+ - Ask yourself: **"Would a staff engineer approve this?"**
170
+ - Run tests, check logs, demonstrate correctness
171
+
172
+ ### 4. Think-Before-Act (Dependencies)
173
+ **Satisfy dependencies BEFORE dependent operations.**
158
174
  ```
159
175
  ❌ node script.js → Error → npm run build
160
176
  ✅ npm run build → node script.js → Success
@@ -166,7 +182,10 @@ SpecWeave auto-detects product descriptions and routes to `/sw:increment`:
166
182
 
167
183
  1. **Files** → `.specweave/increments/####-name/` (see Structure section for details)
168
184
  2. **Update immediately**: `Edit("tasks.md", "[ ] pending", "[x] completed")` + `Edit("spec.md", "[ ] AC-", "[x] AC-")`
169
- 3. **Unique IDs**: Check `ls .specweave/increments/ | grep "^[0-9]" | tail -5`
185
+ 3. **Unique IDs**: Check ALL folders (active, archive, abandoned):
186
+ ```bash
187
+ find .specweave/increments -maxdepth 2 -type d -name "[0-9]*" | grep -oE '[0-9]{4}E?' | sort -u | tail -5
188
+ ```
170
189
  4. **Emergency**: "emergency mode" → 1 edit, 50 lines max, no agents
171
190
  5. **⛔ Initialization guard**: `.specweave/` folders MUST ONLY exist where `specweave init` was run
172
191
  6. **⛔ Marketplace refresh**: Use `specweave refresh-marketplace` CLI (not `scripts/refresh-marketplace.sh`)
@@ -384,13 +403,9 @@ Enable in config: `{"apiDocs":{"enabled":true,"openApiPath":"openapi.yaml"}}`
384
403
  | Skills/commands missing | Restart Claude Code |
385
404
  | Plugins outdated | `specweave refresh-marketplace` |
386
405
  | Out of sync | `/sw:sync-tasks` |
387
- | Find increment | `/sw:status` |
388
- | Root polluted | Move to `.specweave/increments/####/reports/` |
389
406
  | Duplicate IDs | `/sw:fix-duplicates` |
390
- | GitHub sync issues | Check config: `sync.github.enabled`, `canUpdateExternalItems` |
391
407
  | Edits blocked | Add `"additionalDirectories":["repositories"]` to `.claude/settings.json` |
392
- | Marketplace shows 0 | Normal with auto-load; `/plugin list` shows actual |
393
- | Docs folder collisions | Check: `ls docs/ \| grep -E '^[0-9]{2}-' \| cut -d'-' -f1 \| sort \| uniq -d` |
408
+ | Session stuck | Kill + `rm -f .specweave/state/*.lock` + restart |
394
409
  <!-- /SECTION -->
395
410
 
396
411
  <!-- SECTION:lazyloading -->
@@ -410,10 +425,17 @@ export SPECWEAVE_DISABLE_AUTO_LOAD=1 # Disable auto-load
410
425
  <!-- SECTION:principles -->
411
426
  ## Principles
412
427
 
428
+ ### SpecWeave Principles
413
429
  1. **Spec-first**: `/sw:increment` before coding
414
430
  2. **Docs = truth**: Specs guide implementation
415
431
  3. **Incremental**: Small, validated increments
416
432
  4. **Traceable**: All work → specs → ACs
433
+
434
+ ### Core Principles (Quality)
435
+ - **Simplicity First**: Make every change as simple as possible. Impact minimal code.
436
+ - **No Laziness**: Find root causes. No temporary fixes. Senior developer standards.
437
+ - **Minimal Impact**: Changes should only touch what's necessary. Avoid introducing bugs.
438
+ - **Demand Elegance**: For non-trivial changes, pause and ask "is there a more elegant way?" - but skip this for simple, obvious fixes (don't over-engineer).
417
439
  <!-- /SECTION -->
418
440
 
419
441
  <!-- SECTION:linking -->