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 +36 -20
- package/package.json +1 -1
- package/plugins/specweave/hooks/log-decision.sh +162 -0
- package/plugins/specweave/hooks/stop-auto.sh +136 -11
- package/plugins/specweave/skills/image-generation/SKILL.md +492 -5
- package/src/templates/AGENTS.md.template +182 -1
- package/src/templates/CLAUDE.md.template +30 -8
package/CLAUDE.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<!-- SW:META template="claude" version="1.0.
|
|
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.
|
|
147
|
-
##
|
|
146
|
+
<!-- SW:SECTION:metarule version="1.0.203" -->
|
|
147
|
+
## Workflow Orchestration
|
|
148
148
|
|
|
149
|
-
|
|
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
|
|
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**:
|
|
236
|
-
- **2026-02-02**:
|
|
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.
|
|
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
|
-
|
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
|
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
|
|
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
|
-
##
|
|
154
|
+
## Workflow Orchestration
|
|
155
155
|
|
|
156
|
-
|
|
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
|
|
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
|
-
|
|
|
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 -->
|