@curdx/flow 1.1.4 → 1.1.6
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-plugin/marketplace.json +25 -0
- package/.claude-plugin/plugin.json +43 -0
- package/CHANGELOG.md +279 -0
- package/agent-preamble/preamble.md +214 -0
- package/agents/flow-adversary.md +216 -0
- package/agents/flow-architect.md +190 -0
- package/agents/flow-debugger.md +325 -0
- package/agents/flow-edge-hunter.md +273 -0
- package/agents/flow-executor.md +246 -0
- package/agents/flow-planner.md +204 -0
- package/agents/flow-product-designer.md +146 -0
- package/agents/flow-qa-engineer.md +276 -0
- package/agents/flow-researcher.md +155 -0
- package/agents/flow-reviewer.md +280 -0
- package/agents/flow-security-auditor.md +398 -0
- package/agents/flow-triage-analyst.md +290 -0
- package/agents/flow-ui-researcher.md +227 -0
- package/agents/flow-ux-designer.md +247 -0
- package/agents/flow-verifier.md +283 -0
- package/agents/persona-amelia.md +128 -0
- package/agents/persona-david.md +141 -0
- package/agents/persona-emma.md +179 -0
- package/agents/persona-john.md +105 -0
- package/agents/persona-mary.md +95 -0
- package/agents/persona-oliver.md +136 -0
- package/agents/persona-rachel.md +126 -0
- package/agents/persona-serena.md +175 -0
- package/agents/persona-winston.md +117 -0
- package/bin/curdx-flow.js +5 -2
- package/cli/install.js +44 -5
- package/commands/audit.md +170 -0
- package/commands/autoplan.md +184 -0
- package/commands/debug.md +199 -0
- package/commands/design.md +155 -0
- package/commands/discuss.md +162 -0
- package/commands/doctor.md +124 -0
- package/commands/fast.md +128 -0
- package/commands/help.md +119 -0
- package/commands/implement.md +381 -0
- package/commands/index.md +261 -0
- package/commands/init.md +105 -0
- package/commands/install-deps.md +128 -0
- package/commands/party.md +241 -0
- package/commands/plan-ceo.md +117 -0
- package/commands/plan-design.md +107 -0
- package/commands/plan-dx.md +104 -0
- package/commands/plan-eng.md +108 -0
- package/commands/qa.md +118 -0
- package/commands/requirements.md +146 -0
- package/commands/research.md +141 -0
- package/commands/review.md +168 -0
- package/commands/security.md +109 -0
- package/commands/sketch.md +118 -0
- package/commands/spec.md +135 -0
- package/commands/spike.md +181 -0
- package/commands/start.md +189 -0
- package/commands/status.md +139 -0
- package/commands/switch.md +95 -0
- package/commands/tasks.md +189 -0
- package/commands/triage.md +160 -0
- package/commands/verify.md +124 -0
- package/gates/adversarial-review-gate.md +219 -0
- package/gates/coverage-audit-gate.md +184 -0
- package/gates/devex-gate.md +255 -0
- package/gates/edge-case-gate.md +194 -0
- package/gates/karpathy-gate.md +130 -0
- package/gates/security-gate.md +218 -0
- package/gates/tdd-gate.md +188 -0
- package/gates/verification-gate.md +183 -0
- package/hooks/hooks.json +56 -0
- package/hooks/scripts/fail-tracker.sh +31 -0
- package/hooks/scripts/inject-karpathy.sh +52 -0
- package/hooks/scripts/quick-mode-guard.sh +64 -0
- package/hooks/scripts/session-start.sh +76 -0
- package/hooks/scripts/stop-watcher.sh +166 -0
- package/knowledge/atomic-commits.md +262 -0
- package/knowledge/epic-decomposition.md +307 -0
- package/knowledge/execution-strategies.md +278 -0
- package/knowledge/karpathy-guidelines.md +219 -0
- package/knowledge/planning-reviews.md +211 -0
- package/knowledge/poc-first-workflow.md +227 -0
- package/knowledge/spec-driven-development.md +183 -0
- package/knowledge/systematic-debugging.md +384 -0
- package/knowledge/two-stage-review.md +233 -0
- package/knowledge/wave-execution.md +387 -0
- package/package.json +14 -3
- package/schemas/config.schema.json +100 -0
- package/schemas/spec-frontmatter.schema.json +42 -0
- package/schemas/spec-state.schema.json +117 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# CurDX-Flow InstructionsLoaded Hook
|
|
3
|
+
# Injects the L1 baseline (Karpathy 4 principles + mandatory tool rules + 3 red lines)
|
|
4
|
+
# into every session after CLAUDE.md is loaded.
|
|
5
|
+
#
|
|
6
|
+
# This is what makes the baseline "always on" — it doesn't rely on CLAUDE.md being
|
|
7
|
+
# present in every project, and it survives context compaction.
|
|
8
|
+
|
|
9
|
+
set -u
|
|
10
|
+
|
|
11
|
+
CONTEXT='## CurDX-Flow Mind Baseline (L1 — always on)
|
|
12
|
+
|
|
13
|
+
### 1. Think Before Coding
|
|
14
|
+
- State assumptions before any non-trivial task
|
|
15
|
+
- When uncertain, use AskUserQuestion — do not silently assume
|
|
16
|
+
- When confused, stop — do not push forward
|
|
17
|
+
|
|
18
|
+
### 2. Simplicity First
|
|
19
|
+
- YAGNI: do not write features beyond what was requested
|
|
20
|
+
- No single-use abstractions
|
|
21
|
+
- If 200 lines suffice, do not write 1000
|
|
22
|
+
|
|
23
|
+
### 3. Surgical Changes
|
|
24
|
+
- Modify only the lines that must change
|
|
25
|
+
- Match existing style
|
|
26
|
+
- Do not delete pre-existing dead code
|
|
27
|
+
|
|
28
|
+
### 4. Goal-Driven
|
|
29
|
+
- Define a verifiable success criterion first
|
|
30
|
+
- Forbidden to say done/fixed/working without evidence
|
|
31
|
+
|
|
32
|
+
## Mandatory Tools (L2 — enforced)
|
|
33
|
+
|
|
34
|
+
- library/framework questions → **context7 first** (`mcp__context7__*`); forbidden to rely on training memory
|
|
35
|
+
- planning / design / architecture / review → **sequential-thinking first** (≥5 thoughts)
|
|
36
|
+
- before any task → **claude-mem search** (if installed)
|
|
37
|
+
- UI code → **frontend-design skill** (if installed)
|
|
38
|
+
- browser QA → **chrome-devtools MCP**
|
|
39
|
+
|
|
40
|
+
## Three Red Lines (L3 — inherited from pua, universal)
|
|
41
|
+
|
|
42
|
+
1. **Closed loop**: claiming "done"? Provide evidence (build output / passing tests / curl result)
|
|
43
|
+
2. **Fact-driven**: verify before saying "probably". An unverified attribution is blame-shifting
|
|
44
|
+
3. **Exhaust everything**: before saying "I cannot", complete the systematic 4-stage debugging'
|
|
45
|
+
|
|
46
|
+
# Emit JSON with safe encoding
|
|
47
|
+
if command -v python3 >/dev/null 2>&1; then
|
|
48
|
+
ESCAPED="$(printf '%s' "$CONTEXT" | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read()))')"
|
|
49
|
+
printf '{"hookSpecificOutput":{"hookEventName":"InstructionsLoaded","additionalContext":%s}}\n' "$ESCAPED"
|
|
50
|
+
fi
|
|
51
|
+
|
|
52
|
+
exit 0
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# CurDX-Flow PreToolUse Hook for AskUserQuestion
|
|
3
|
+
# Blocks AskUserQuestion when the active spec has quickMode=true or mode=autonomous.
|
|
4
|
+
# This prevents the autonomous loop from stalling waiting for user input.
|
|
5
|
+
#
|
|
6
|
+
# The hook reads Claude's PreToolUse input JSON from stdin. We only act when
|
|
7
|
+
# the tool being invoked is AskUserQuestion.
|
|
8
|
+
|
|
9
|
+
set -u
|
|
10
|
+
|
|
11
|
+
# Read input (Claude sends JSON on stdin for PreToolUse)
|
|
12
|
+
INPUT=$(cat 2>/dev/null || echo "{}")
|
|
13
|
+
|
|
14
|
+
if ! command -v python3 >/dev/null 2>&1; then
|
|
15
|
+
# Without JSON parsing, allow everything (fail open)
|
|
16
|
+
exit 0
|
|
17
|
+
fi
|
|
18
|
+
|
|
19
|
+
# Parse the tool name being invoked
|
|
20
|
+
TOOL_NAME=$(echo "$INPUT" | python3 -c '
|
|
21
|
+
import json, sys
|
|
22
|
+
try:
|
|
23
|
+
d = json.load(sys.stdin)
|
|
24
|
+
print(d.get("tool_name", ""))
|
|
25
|
+
except Exception:
|
|
26
|
+
print("")
|
|
27
|
+
' 2>/dev/null)
|
|
28
|
+
|
|
29
|
+
# We only guard AskUserQuestion
|
|
30
|
+
if [ "$TOOL_NAME" != "AskUserQuestion" ]; then
|
|
31
|
+
exit 0
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
# Check if we're in a flow project with quick mode enabled
|
|
35
|
+
[ ! -d ".flow" ] && exit 0
|
|
36
|
+
|
|
37
|
+
ACTIVE=$(cat .flow/.active-spec 2>/dev/null)
|
|
38
|
+
[ -z "$ACTIVE" ] && exit 0
|
|
39
|
+
|
|
40
|
+
STATE_FILE=".flow/specs/$ACTIVE/.state.json"
|
|
41
|
+
[ ! -f "$STATE_FILE" ] && exit 0
|
|
42
|
+
|
|
43
|
+
# Read quickMode + mode
|
|
44
|
+
QUICK_MODE=$(python3 -c "
|
|
45
|
+
import json
|
|
46
|
+
try:
|
|
47
|
+
s = json.load(open('$STATE_FILE'))
|
|
48
|
+
qm = s.get('quickMode', False)
|
|
49
|
+
mode = s.get('mode', '')
|
|
50
|
+
print('true' if (qm or mode == 'autonomous') else 'false')
|
|
51
|
+
except Exception:
|
|
52
|
+
print('false')
|
|
53
|
+
" 2>/dev/null)
|
|
54
|
+
|
|
55
|
+
if [ "$QUICK_MODE" = "true" ]; then
|
|
56
|
+
# Block and inject guidance
|
|
57
|
+
MSG="[CurDX-Flow quick-mode-guard] Active spec '$ACTIVE' is in quick mode or autonomous mode — AskUserQuestion is forbidden. Decide autonomously based on user preferences in .flow/CONTEXT.md plus the most reasonable assumption, and record your assumption in .progress.md."
|
|
58
|
+
ESCAPED=$(printf '%s' "$MSG" | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read()))')
|
|
59
|
+
printf '{"decision":"block","reason":%s}\n' "$ESCAPED"
|
|
60
|
+
exit 0
|
|
61
|
+
fi
|
|
62
|
+
|
|
63
|
+
# Allow
|
|
64
|
+
exit 0
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# CurDX-Flow SessionStart Hook
|
|
3
|
+
# Duties:
|
|
4
|
+
# 1. Daily dependency check — nudge user to /flow-install-deps if recommended plugins missing
|
|
5
|
+
# 2. Load active spec progress into session context
|
|
6
|
+
#
|
|
7
|
+
# Design notes:
|
|
8
|
+
# - Idempotent: marker file tracks last check date
|
|
9
|
+
# - Silent when everything is healthy (no noise)
|
|
10
|
+
# - Graceful degradation: missing `claude` CLI doesn't break hook
|
|
11
|
+
|
|
12
|
+
set -u
|
|
13
|
+
|
|
14
|
+
DATA_DIR="${CLAUDE_PLUGIN_DATA:-$HOME/.claude/plugins/data/curdx-flow}"
|
|
15
|
+
MARKER="$DATA_DIR/.deps-checked"
|
|
16
|
+
TODAY="$(date +%Y-%m-%d)"
|
|
17
|
+
ADDITIONAL_CONTEXT=""
|
|
18
|
+
|
|
19
|
+
mkdir -p "$DATA_DIR" 2>/dev/null || true
|
|
20
|
+
|
|
21
|
+
# ---------- 1. Dependency check (once per day) ----------
|
|
22
|
+
LAST_CHECK=""
|
|
23
|
+
[ -f "$MARKER" ] && LAST_CHECK="$(cat "$MARKER" 2>/dev/null)"
|
|
24
|
+
|
|
25
|
+
if [ "$LAST_CHECK" != "$TODAY" ]; then
|
|
26
|
+
MISSING=()
|
|
27
|
+
|
|
28
|
+
# Skip if `claude` CLI is not on PATH
|
|
29
|
+
if command -v claude >/dev/null 2>&1; then
|
|
30
|
+
INSTALLED="$(claude plugin list 2>/dev/null || true)"
|
|
31
|
+
|
|
32
|
+
echo "$INSTALLED" | grep -q 'pua' || MISSING+=("pua")
|
|
33
|
+
echo "$INSTALLED" | grep -q 'claude-mem' || MISSING+=("claude-mem")
|
|
34
|
+
echo "$INSTALLED" | grep -q 'frontend-design' || MISSING+=("frontend-design")
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
if [ "${#MISSING[@]}" -gt 0 ]; then
|
|
38
|
+
JOINED="$(IFS=,; echo "${MISSING[*]}")"
|
|
39
|
+
ADDITIONAL_CONTEXT+="## CurDX-Flow Recommended Plugins Check\n\nThe following recommended plugins were not detected: **${JOINED}**\n\nRun \`/curdx-flow:install-deps\` for interactive one-shot install. Run \`/curdx-flow:doctor\` for the full health report.\n\n"
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
echo "$TODAY" > "$MARKER" 2>/dev/null || true
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
# ---------- 2. Load .flow/ state (if project is a flow project) ----------
|
|
46
|
+
if [ -d ".flow" ]; then
|
|
47
|
+
ADDITIONAL_CONTEXT+="## CurDX-Flow Project Active\n\n"
|
|
48
|
+
|
|
49
|
+
if [ -f ".flow/PROJECT.md" ]; then
|
|
50
|
+
ADDITIONAL_CONTEXT+="### Project Vision\n$(head -80 .flow/PROJECT.md)\n\n"
|
|
51
|
+
fi
|
|
52
|
+
|
|
53
|
+
if [ -f ".flow/.active-spec" ]; then
|
|
54
|
+
ACTIVE="$(cat .flow/.active-spec 2>/dev/null)"
|
|
55
|
+
if [ -n "$ACTIVE" ] && [ -d ".flow/specs/$ACTIVE" ]; then
|
|
56
|
+
ADDITIONAL_CONTEXT+="### Active Spec: \`$ACTIVE\`\n\n"
|
|
57
|
+
if [ -f ".flow/specs/$ACTIVE/.progress.md" ]; then
|
|
58
|
+
ADDITIONAL_CONTEXT+="$(head -40 ".flow/specs/$ACTIVE/.progress.md")\n\n"
|
|
59
|
+
fi
|
|
60
|
+
fi
|
|
61
|
+
fi
|
|
62
|
+
fi
|
|
63
|
+
|
|
64
|
+
# ---------- 3. Emit hook output ----------
|
|
65
|
+
if [ -n "$ADDITIONAL_CONTEXT" ]; then
|
|
66
|
+
# Use python3 for safe JSON encoding (handles newlines, quotes)
|
|
67
|
+
if command -v python3 >/dev/null 2>&1; then
|
|
68
|
+
ESCAPED="$(printf '%s' "$ADDITIONAL_CONTEXT" | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read()))')"
|
|
69
|
+
else
|
|
70
|
+
# Fallback: naive escape (only newlines and quotes)
|
|
71
|
+
ESCAPED="$(printf '%s' "$ADDITIONAL_CONTEXT" | sed 's/\\/\\\\/g; s/"/\\"/g' | awk 'BEGIN{printf "\""} {printf "%s\\n", $0} END{printf "\""}')"
|
|
72
|
+
fi
|
|
73
|
+
printf '{"hookSpecificOutput":{"hookEventName":"SessionStart","additionalContext":%s}}\n' "$ESCAPED"
|
|
74
|
+
fi
|
|
75
|
+
|
|
76
|
+
exit 0
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# CurDX-Flow Stop Hook — StopHookLoop strategy implementation
|
|
3
|
+
#
|
|
4
|
+
# Fires when Claude's turn ends. Decides whether to block (force continue)
|
|
5
|
+
# or allow the session to stop.
|
|
6
|
+
#
|
|
7
|
+
# Decision logic:
|
|
8
|
+
# 1. Not a flow project? → allow stop
|
|
9
|
+
# 2. No active spec? → allow stop
|
|
10
|
+
# 3. Strategy is not "stop-hook"? → allow stop
|
|
11
|
+
# 4. Phase != "execute"? → allow stop
|
|
12
|
+
# 5. All tasks done OR "ALL_TASKS_COMPLETE" in transcript? → cleanup + allow stop
|
|
13
|
+
# 6. Too many rounds (>100) or too many failures (>3)? → stop + warn
|
|
14
|
+
# 7. Otherwise → block and force continue next task
|
|
15
|
+
|
|
16
|
+
set -u
|
|
17
|
+
|
|
18
|
+
DATA_DIR="${CLAUDE_PLUGIN_DATA:-$HOME/.claude/plugins/data/curdx-flow}"
|
|
19
|
+
|
|
20
|
+
# ---------- helper: exit with "allow stop" ----------
|
|
21
|
+
allow_stop() {
|
|
22
|
+
# No output = allow stop normally
|
|
23
|
+
exit 0
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
# ---------- helper: block and inject continuation ----------
|
|
27
|
+
block_continue() {
|
|
28
|
+
local reason="$1"
|
|
29
|
+
# Safely JSON-encode via python3 if available
|
|
30
|
+
if command -v python3 >/dev/null 2>&1; then
|
|
31
|
+
printf '{"decision":"block","reason":%s}\n' \
|
|
32
|
+
"$(printf '%s' "$reason" | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read()))')"
|
|
33
|
+
else
|
|
34
|
+
# Fallback: escape quotes and newlines naively
|
|
35
|
+
local escaped=$(printf '%s' "$reason" | sed 's/\\/\\\\/g; s/"/\\"/g' | tr '\n' ' ')
|
|
36
|
+
printf '{"decision":"block","reason":"%s"}\n' "$escaped"
|
|
37
|
+
fi
|
|
38
|
+
exit 0
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
# ---------- 1. Must be a flow project ----------
|
|
42
|
+
[ ! -d ".flow" ] && allow_stop
|
|
43
|
+
|
|
44
|
+
# ---------- 2. Must have active spec ----------
|
|
45
|
+
ACTIVE=$(cat .flow/.active-spec 2>/dev/null)
|
|
46
|
+
[ -z "$ACTIVE" ] && allow_stop
|
|
47
|
+
SPEC_DIR=".flow/specs/$ACTIVE"
|
|
48
|
+
[ ! -d "$SPEC_DIR" ] && allow_stop
|
|
49
|
+
|
|
50
|
+
STATE_FILE="$SPEC_DIR/.state.json"
|
|
51
|
+
[ ! -f "$STATE_FILE" ] && allow_stop
|
|
52
|
+
|
|
53
|
+
# ---------- 3-4. Strategy + phase check (use python3 for JSON parsing) ----------
|
|
54
|
+
if ! command -v python3 >/dev/null 2>&1; then
|
|
55
|
+
# Without python3 we can't safely parse JSON. Allow stop.
|
|
56
|
+
allow_stop
|
|
57
|
+
fi
|
|
58
|
+
|
|
59
|
+
read STRATEGY PHASE TASK_INDEX TOTAL_TASKS FAILED ROUNDS <<EOF
|
|
60
|
+
$(python3 <<'PY'
|
|
61
|
+
import json, os, sys
|
|
62
|
+
p = os.environ.get("STATE_FILE")
|
|
63
|
+
try:
|
|
64
|
+
s = json.load(open(p))
|
|
65
|
+
except Exception:
|
|
66
|
+
sys.exit(0)
|
|
67
|
+
strategy = s.get("strategy", "auto")
|
|
68
|
+
phase = s.get("phase", "")
|
|
69
|
+
ex = s.get("execute_state", {}) or {}
|
|
70
|
+
ti = ex.get("task_index", 0)
|
|
71
|
+
tt = ex.get("total_tasks", 0)
|
|
72
|
+
failed = ex.get("failed_attempts", 0)
|
|
73
|
+
rounds = ex.get("global_iteration", 0)
|
|
74
|
+
print(strategy, phase, ti, tt, failed, rounds)
|
|
75
|
+
PY
|
|
76
|
+
)
|
|
77
|
+
EOF
|
|
78
|
+
export STATE_FILE
|
|
79
|
+
|
|
80
|
+
# Only activate for stop-hook strategy + execute phase
|
|
81
|
+
[ "$STRATEGY" != "stop-hook" ] && allow_stop
|
|
82
|
+
[ "$PHASE" != "execute" ] && allow_stop
|
|
83
|
+
|
|
84
|
+
# ---------- 5. Check for completion signal in transcript ----------
|
|
85
|
+
# Claude Code passes transcript path via stdin as JSON: {"transcript_path": "/path/..."}
|
|
86
|
+
# We read stdin to detect ALL_TASKS_COMPLETE or TASK_FAILED
|
|
87
|
+
INPUT=$(cat 2>/dev/null || echo "{}")
|
|
88
|
+
TRANSCRIPT_PATH=$(echo "$INPUT" | python3 -c 'import json,sys;
|
|
89
|
+
try: print(json.load(sys.stdin).get("transcript_path",""))
|
|
90
|
+
except: print("")' 2>/dev/null)
|
|
91
|
+
|
|
92
|
+
TRANSCRIPT_TAIL=""
|
|
93
|
+
if [ -n "$TRANSCRIPT_PATH" ] && [ -f "$TRANSCRIPT_PATH" ]; then
|
|
94
|
+
# Read last 50KB only (efficiency)
|
|
95
|
+
TRANSCRIPT_TAIL=$(tail -c 51200 "$TRANSCRIPT_PATH" 2>/dev/null || echo "")
|
|
96
|
+
fi
|
|
97
|
+
|
|
98
|
+
# Check for explicit completion signals
|
|
99
|
+
if echo "$TRANSCRIPT_TAIL" | grep -q "ALL_TASKS_COMPLETE"; then
|
|
100
|
+
# Cleanup: mark phase completed
|
|
101
|
+
python3 <<PY 2>/dev/null
|
|
102
|
+
import json
|
|
103
|
+
p = "$STATE_FILE"
|
|
104
|
+
s = json.load(open(p))
|
|
105
|
+
s.setdefault("phase_status", {})["execute"] = "completed"
|
|
106
|
+
s["phase"] = "verify" # move to verify phase
|
|
107
|
+
json.dump(s, open(p, "w"), indent=2, ensure_ascii=False)
|
|
108
|
+
PY
|
|
109
|
+
allow_stop
|
|
110
|
+
fi
|
|
111
|
+
|
|
112
|
+
# Check for fail signal (accumulate; actual stop decision below)
|
|
113
|
+
if echo "$TRANSCRIPT_TAIL" | grep -q "TASK_FAILED"; then
|
|
114
|
+
# Increment failed_attempts
|
|
115
|
+
python3 <<PY 2>/dev/null
|
|
116
|
+
import json
|
|
117
|
+
p = "$STATE_FILE"
|
|
118
|
+
s = json.load(open(p))
|
|
119
|
+
s.setdefault("execute_state", {})
|
|
120
|
+
s["execute_state"]["failed_attempts"] = s["execute_state"].get("failed_attempts", 0) + 1
|
|
121
|
+
json.dump(s, open(p, "w"), indent=2, ensure_ascii=False)
|
|
122
|
+
PY
|
|
123
|
+
# Re-read
|
|
124
|
+
FAILED=$(python3 -c "import json; print(json.load(open('$STATE_FILE'))['execute_state']['failed_attempts'])" 2>/dev/null || echo 0)
|
|
125
|
+
fi
|
|
126
|
+
|
|
127
|
+
# ---------- 6. Safety brakes ----------
|
|
128
|
+
if [ "$FAILED" -ge 3 ]; then
|
|
129
|
+
# Too many failures — stop and surface
|
|
130
|
+
allow_stop
|
|
131
|
+
fi
|
|
132
|
+
|
|
133
|
+
if [ "$ROUNDS" -ge 100 ]; then
|
|
134
|
+
# Runaway loop protection
|
|
135
|
+
allow_stop
|
|
136
|
+
fi
|
|
137
|
+
|
|
138
|
+
# Check if all tasks done
|
|
139
|
+
if [ "$TASK_INDEX" -ge "$TOTAL_TASKS" ] && [ "$TOTAL_TASKS" -gt 0 ]; then
|
|
140
|
+
# Mark complete
|
|
141
|
+
python3 <<PY 2>/dev/null
|
|
142
|
+
import json
|
|
143
|
+
p = "$STATE_FILE"
|
|
144
|
+
s = json.load(open(p))
|
|
145
|
+
s.setdefault("phase_status", {})["execute"] = "completed"
|
|
146
|
+
s["phase"] = "verify"
|
|
147
|
+
json.dump(s, open(p, "w"), indent=2, ensure_ascii=False)
|
|
148
|
+
PY
|
|
149
|
+
allow_stop
|
|
150
|
+
fi
|
|
151
|
+
|
|
152
|
+
# ---------- 7. Block and continue ----------
|
|
153
|
+
# Increment round counter
|
|
154
|
+
python3 <<PY 2>/dev/null
|
|
155
|
+
import json
|
|
156
|
+
p = "$STATE_FILE"
|
|
157
|
+
s = json.load(open(p))
|
|
158
|
+
s.setdefault("execute_state", {})
|
|
159
|
+
s["execute_state"]["global_iteration"] = s["execute_state"].get("global_iteration", 0) + 1
|
|
160
|
+
json.dump(s, open(p, "w"), indent=2, ensure_ascii=False)
|
|
161
|
+
PY
|
|
162
|
+
|
|
163
|
+
NEXT_INDEX=$((TASK_INDEX + 1))
|
|
164
|
+
REASON="[CurDX-Flow stop-hook] Spec '$ACTIVE' has tasks remaining ($TASK_INDEX/$TOTAL_TASKS). Continue to the next task. Dispatch flow-executor for task_id=next. On completion, emit TASK_COMPLETE or TASK_FAILED. When all tasks are done, emit ALL_TASKS_COMPLETE."
|
|
165
|
+
|
|
166
|
+
block_continue "$REASON"
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
# Atomic Commits — Atomic Commit Rules
|
|
2
|
+
|
|
3
|
+
> One task, one commit. This is the iron rule for all execution agents in CurDX-Flow.
|
|
4
|
+
>
|
|
5
|
+
> Agents reference this via `@${CLAUDE_PLUGIN_ROOT}/knowledge/atomic-commits.md`.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Core Principle
|
|
10
|
+
|
|
11
|
+
**One task = one commit = one rollback unit.**
|
|
12
|
+
|
|
13
|
+
Why:
|
|
14
|
+
- `git bisect` can pinpoint a problem to a specific task
|
|
15
|
+
- `git revert <hash>` can undo a single task in isolation
|
|
16
|
+
- PR review can walk through changes commit-by-commit
|
|
17
|
+
- Downstream agents can trace "which AD / FR this change came from" via commit history
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Commit Message Format (Conventional Commits + CurDX-Flow extensions)
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
<type>(<scope>): <summary>
|
|
25
|
+
|
|
26
|
+
[body - explain why, not what]
|
|
27
|
+
|
|
28
|
+
[footer - reference IDs]
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Type (required)
|
|
32
|
+
|
|
33
|
+
| Type | Purpose | Example |
|
|
34
|
+
|------|---------|---------|
|
|
35
|
+
| `feat` | New feature | `feat(auth): add JWT refresh endpoint` |
|
|
36
|
+
| `fix` | Bug fix | `fix(login): handle empty email case` |
|
|
37
|
+
| `refactor` | Refactor (behavior unchanged) | `refactor(db): extract connection pool` |
|
|
38
|
+
| `test` | Tests | `test(auth): red - add login validation tests` |
|
|
39
|
+
| `docs` | Documentation | `docs(readme): add install instructions` |
|
|
40
|
+
| `chore` | Misc (deps, config) | `chore(deps): upgrade bcrypt to 5.1.0` |
|
|
41
|
+
| `perf` | Performance | `perf(query): cache user lookups` |
|
|
42
|
+
| `style` | Formatting (no behavior change) | `style: fix indentation in auth module` |
|
|
43
|
+
| `build` | Build config | `build(tsconfig): enable strict mode` |
|
|
44
|
+
| `ci` | CI config | `ci(github): add test workflow` |
|
|
45
|
+
|
|
46
|
+
### TDD phase markers (used in Phase 3)
|
|
47
|
+
|
|
48
|
+
| Phase | Type + suffix | Example |
|
|
49
|
+
|-------|--------------|---------|
|
|
50
|
+
| RED | `test(scope): red - ...` | `test(auth): red - login validation` |
|
|
51
|
+
| GREEN | `feat(scope): green - ...` | `feat(auth): green - satisfy login test` |
|
|
52
|
+
| YELLOW | `refactor(scope): yellow - ...` | `refactor(auth): yellow - extract validator` |
|
|
53
|
+
|
|
54
|
+
### Scope
|
|
55
|
+
|
|
56
|
+
- Module name (`auth`, `db`, `ui`, `api`)
|
|
57
|
+
- Or file name (without extension)
|
|
58
|
+
- Or `(spec-name)` if spanning multiple modules
|
|
59
|
+
|
|
60
|
+
### Summary
|
|
61
|
+
|
|
62
|
+
- Imperative mood (`add`, `fix`, not `added`, `fixes`)
|
|
63
|
+
- Lowercase start (unless a proper noun)
|
|
64
|
+
- No trailing period
|
|
65
|
+
- ≤ 70 characters
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## Body (optional but recommended)
|
|
70
|
+
|
|
71
|
+
Explain **why**. Do not explain **what** (the diff shows what).
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
feat(auth): implement JWT refresh flow
|
|
75
|
+
|
|
76
|
+
Tokens expire after 15 minutes. Without refresh, users get
|
|
77
|
+
logged out mid-session. This adds a refresh endpoint that
|
|
78
|
+
validates the refresh_token and issues a new access_token.
|
|
79
|
+
|
|
80
|
+
Per AD-03: use rotating refresh tokens to mitigate theft.
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Do NOT write:
|
|
84
|
+
```
|
|
85
|
+
feat(auth): implement JWT refresh flow
|
|
86
|
+
|
|
87
|
+
Added refreshToken() function in auth.ts.
|
|
88
|
+
Added POST /auth/refresh endpoint.
|
|
89
|
+
Added tests for success and failure cases.
|
|
90
|
+
```
|
|
91
|
+
← the diff already shows this; it wastes commit message space.
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## Footer (reference IDs)
|
|
96
|
+
|
|
97
|
+
CurDX-Flow extension: if a task references FR / AC / AD / D-NN, list them in the footer:
|
|
98
|
+
|
|
99
|
+
```
|
|
100
|
+
feat(auth): implement JWT refresh flow
|
|
101
|
+
|
|
102
|
+
Refresh tokens rotate on each use, preventing replay attacks.
|
|
103
|
+
|
|
104
|
+
Requirements: FR-03, AC-2.1
|
|
105
|
+
Design: AD-03
|
|
106
|
+
Decisions: D-07 (session storage strategy)
|
|
107
|
+
Task: spec/auth-system/tasks.md#3.2
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Fields:
|
|
111
|
+
- `Requirements:` lists implemented FR / AC
|
|
112
|
+
- `Design:` lists related AD
|
|
113
|
+
- `Decisions:` lists referenced project-level decisions (D-NN)
|
|
114
|
+
- `Task:` optional, points at the task definition location
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## Concrete Rules for Atomicity
|
|
119
|
+
|
|
120
|
+
### 1. One commit does one thing
|
|
121
|
+
|
|
122
|
+
✗ **Bad**:
|
|
123
|
+
```
|
|
124
|
+
feat: add login + fix typo + refactor db connection
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
✓ **Good** (split into 3 commits):
|
|
128
|
+
```
|
|
129
|
+
feat(auth): add login endpoint
|
|
130
|
+
docs: fix typo in README
|
|
131
|
+
refactor(db): extract connection factory
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### 2. Do not mix "task code" and "cleanup"
|
|
135
|
+
|
|
136
|
+
✗ **Bad**:
|
|
137
|
+
```
|
|
138
|
+
feat(auth): add JWT + remove unused imports in user.ts
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
✓ **Good**:
|
|
142
|
+
```
|
|
143
|
+
feat(auth): add JWT endpoint
|
|
144
|
+
chore: remove unused imports in user.ts
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### 3. Every commit must pass tests independently
|
|
148
|
+
|
|
149
|
+
This is what makes `git bisect` work.
|
|
150
|
+
|
|
151
|
+
- Do not commit "broken intermediate states"
|
|
152
|
+
- Even WIP should build + test (even with few tests)
|
|
153
|
+
- For a large change that truly must be split, use a feature branch and squash at the end
|
|
154
|
+
|
|
155
|
+
### 4. The file change scope should match the commit message
|
|
156
|
+
|
|
157
|
+
If the commit is `feat(auth): ...`, it should not include changes under `src/ui/`. Otherwise you actually did two things.
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## The flow-executor Agent's Commit Flow
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
# Step 1: git add only the files involved in the task
|
|
165
|
+
git add src/auth/login.ts src/auth/login.test.ts
|
|
166
|
+
|
|
167
|
+
# Step 2: check that staged diff matches the commit message
|
|
168
|
+
git diff --cached --stat
|
|
169
|
+
|
|
170
|
+
# Step 3: ensure no unexpected changes sneaked in
|
|
171
|
+
# (if you see a file that shouldn't be there, git reset HEAD <file>)
|
|
172
|
+
|
|
173
|
+
# Step 4: commit
|
|
174
|
+
git commit -m "feat(auth): green - implement login endpoint
|
|
175
|
+
|
|
176
|
+
Per AD-03, uses bcrypt for password hashing.
|
|
177
|
+
|
|
178
|
+
Requirements: FR-01
|
|
179
|
+
Design: AD-03
|
|
180
|
+
"
|
|
181
|
+
|
|
182
|
+
# Step 5: record the hash
|
|
183
|
+
COMMIT_HASH=$(git rev-parse --short HEAD)
|
|
184
|
+
echo "✓ Committed: $COMMIT_HASH"
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## Forbidden Patterns
|
|
190
|
+
|
|
191
|
+
### ✗ Giant commit
|
|
192
|
+
```
|
|
193
|
+
feat(auth): implement entire authentication system
|
|
194
|
+
```
|
|
195
|
+
Too large, cannot review, cannot bisect. Split into N small commits.
|
|
196
|
+
|
|
197
|
+
### ✗ Hedging words
|
|
198
|
+
```
|
|
199
|
+
feat(auth): maybe fix login issue?
|
|
200
|
+
```
|
|
201
|
+
If unsure, do not commit yet. A commit is a definitive operation.
|
|
202
|
+
|
|
203
|
+
### ✗ Meaningless commits
|
|
204
|
+
```
|
|
205
|
+
wip
|
|
206
|
+
fix
|
|
207
|
+
update
|
|
208
|
+
```
|
|
209
|
+
Future maintainers will curse you. At minimum write `wip(auth): placeholder for token refresh`.
|
|
210
|
+
|
|
211
|
+
### ✗ `--amend` on a pushed commit
|
|
212
|
+
```
|
|
213
|
+
git commit --amend # after push
|
|
214
|
+
git push -f # overwrite remote
|
|
215
|
+
```
|
|
216
|
+
Destroys shared history. Only amend local, unpushed commits.
|
|
217
|
+
|
|
218
|
+
### ✗ Skipping hooks
|
|
219
|
+
```
|
|
220
|
+
git commit --no-verify
|
|
221
|
+
```
|
|
222
|
+
Unless the user explicitly requests it. Hooks exist for reasons (pre-commit lint, commit-msg check).
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
## Relationship to PR Review
|
|
227
|
+
|
|
228
|
+
PR review reads each commit's message.
|
|
229
|
+
|
|
230
|
+
- Good commit message → reviewer finishes in 5 minutes
|
|
231
|
+
- Bad commit message → reviewer either rubber-stamps or blocks without reading
|
|
232
|
+
|
|
233
|
+
CurDX-Flow's `/curdx-flow:ship` command (Phase 6) will turn atomic commits into a clean PR description. Poor commit quality yields poor PR descriptions.
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
## Real Example (commit history for a full spec execution)
|
|
238
|
+
|
|
239
|
+
```
|
|
240
|
+
$ git log --oneline auth-system spec
|
|
241
|
+
|
|
242
|
+
abc123f feat(auth): green - implement login endpoint (Requirements: FR-01)
|
|
243
|
+
def456g feat(auth): green - implement password hashing (Design: AD-03)
|
|
244
|
+
ghi789h test(auth): red - add login endpoint tests
|
|
245
|
+
jkl012i test(auth): red - add password hash tests
|
|
246
|
+
mno345j chore(deps): add bcrypt@5.1.0
|
|
247
|
+
pqr678k docs(auth): add design.md for JWT authentication
|
|
248
|
+
stu901l docs(auth): add requirements.md
|
|
249
|
+
vwx234m docs(auth): add research.md (initial spec)
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
Reading this history, anyone can understand:
|
|
253
|
+
- First came research → requirements → design (doc commits)
|
|
254
|
+
- Then a dependency was added
|
|
255
|
+
- Then TDD red (tests) → green (implementation)
|
|
256
|
+
- Every step is atomic
|
|
257
|
+
|
|
258
|
+
This is the quality CurDX-Flow demands.
|
|
259
|
+
|
|
260
|
+
---
|
|
261
|
+
|
|
262
|
+
_CurDX-Flow internal rule. Violation counts as a flow-executor task failure._
|