@curdx/flow 3.0.0 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (219) hide show
  1. package/CHANGELOG.md +21 -87
  2. package/LICENSE +1 -1
  3. package/README.md +28 -129
  4. package/dist/index.mjs +995 -0
  5. package/package.json +33 -44
  6. package/.claude-plugin/marketplace.json +0 -48
  7. package/.claude-plugin/plugin.json +0 -52
  8. package/agent-preamble/preamble.md +0 -314
  9. package/agents/flow-adversary.md +0 -203
  10. package/agents/flow-architect.md +0 -198
  11. package/agents/flow-brownfield-analyst.md +0 -143
  12. package/agents/flow-debugger.md +0 -321
  13. package/agents/flow-edge-hunter.md +0 -289
  14. package/agents/flow-executor.md +0 -269
  15. package/agents/flow-orchestrator.md +0 -145
  16. package/agents/flow-planner.md +0 -247
  17. package/agents/flow-product-designer.md +0 -159
  18. package/agents/flow-qa-engineer.md +0 -282
  19. package/agents/flow-researcher.md +0 -166
  20. package/agents/flow-reviewer.md +0 -304
  21. package/agents/flow-security-auditor.md +0 -401
  22. package/agents/flow-triage-analyst.md +0 -272
  23. package/agents/flow-ui-researcher.md +0 -230
  24. package/agents/flow-ux-designer.md +0 -221
  25. package/agents/flow-verifier.md +0 -350
  26. package/bin/curdx-flow +0 -5
  27. package/bin/curdx-flow-state +0 -104
  28. package/bin/curdx-flow.js +0 -54
  29. package/cli/README.md +0 -104
  30. package/cli/doctor-workflow.js +0 -483
  31. package/cli/doctor.js +0 -73
  32. package/cli/help.js +0 -59
  33. package/cli/install-bundled-mcps.js +0 -37
  34. package/cli/install-companions.js +0 -19
  35. package/cli/install-context7-config.js +0 -80
  36. package/cli/install-curdx-plugin.js +0 -96
  37. package/cli/install-language.js +0 -35
  38. package/cli/install-next-steps.js +0 -29
  39. package/cli/install-options.js +0 -9
  40. package/cli/install-paths.js +0 -52
  41. package/cli/install-recommended-plugins.js +0 -104
  42. package/cli/install-required-plugins.js +0 -57
  43. package/cli/install-self-update.js +0 -62
  44. package/cli/install-workflow.js +0 -209
  45. package/cli/install.js +0 -101
  46. package/cli/lib/claude-commands.js +0 -41
  47. package/cli/lib/claude-ops.js +0 -47
  48. package/cli/lib/claude.js +0 -183
  49. package/cli/lib/config.js +0 -24
  50. package/cli/lib/doctor-claude-settings.js +0 -1186
  51. package/cli/lib/doctor-report.js +0 -978
  52. package/cli/lib/doctor-runtime-environment.js +0 -196
  53. package/cli/lib/frontmatter.js +0 -44
  54. package/cli/lib/json-schema.js +0 -57
  55. package/cli/lib/logging.js +0 -25
  56. package/cli/lib/process.js +0 -60
  57. package/cli/lib/prompts.js +0 -135
  58. package/cli/lib/runtime.js +0 -107
  59. package/cli/lib/semver.js +0 -109
  60. package/cli/lib/version.js +0 -12
  61. package/cli/protocols-body.md +0 -22
  62. package/cli/protocols.js +0 -162
  63. package/cli/registry.js +0 -123
  64. package/cli/router.js +0 -49
  65. package/cli/uninstall-actions.js +0 -360
  66. package/cli/uninstall-workflow.js +0 -146
  67. package/cli/uninstall.js +0 -42
  68. package/cli/upgrade-workflow.js +0 -80
  69. package/cli/upgrade.js +0 -91
  70. package/cli/utils.js +0 -40
  71. package/gates/adversarial-review-gate.md +0 -219
  72. package/gates/coverage-audit-gate.md +0 -182
  73. package/gates/devex-gate.md +0 -254
  74. package/gates/edge-case-gate.md +0 -194
  75. package/gates/karpathy-gate.md +0 -130
  76. package/gates/security-gate.md +0 -218
  77. package/gates/tdd-gate.md +0 -182
  78. package/gates/test-quality-gate.md +0 -59
  79. package/gates/verification-gate.md +0 -179
  80. package/hooks/hooks.json +0 -130
  81. package/hooks/scripts/common.sh +0 -237
  82. package/hooks/scripts/config-change-guard.sh +0 -94
  83. package/hooks/scripts/flow-context-watch.sh +0 -94
  84. package/hooks/scripts/inject-karpathy.sh +0 -53
  85. package/hooks/scripts/quick-mode-guard.sh +0 -69
  86. package/hooks/scripts/session-start.sh +0 -94
  87. package/hooks/scripts/session-title.sh +0 -87
  88. package/hooks/scripts/stop-watcher.sh +0 -231
  89. package/hooks/scripts/subagent-artifact-guard.sh +0 -92
  90. package/hooks/scripts/subagent-statusline.sh +0 -111
  91. package/hooks/scripts/task-lifecycle-guard.sh +0 -106
  92. package/hooks/scripts/teammate-idle-guard.sh +0 -83
  93. package/knowledge/artifact-output-discipline.md +0 -24
  94. package/knowledge/artifact-summary-contracts.md +0 -50
  95. package/knowledge/atomic-commits.md +0 -262
  96. package/knowledge/claude-code-runtime-contracts.md +0 -240
  97. package/knowledge/epic-decomposition.md +0 -307
  98. package/knowledge/execution-strategies.md +0 -303
  99. package/knowledge/karpathy-guidelines.md +0 -219
  100. package/knowledge/planning-reviews.md +0 -211
  101. package/knowledge/poc-first-workflow.md +0 -223
  102. package/knowledge/review-feedback-intake.md +0 -57
  103. package/knowledge/spec-driven-development.md +0 -180
  104. package/knowledge/systematic-debugging.md +0 -378
  105. package/knowledge/two-stage-review.md +0 -249
  106. package/knowledge/wave-execution.md +0 -403
  107. package/monitors/monitors.json +0 -8
  108. package/monitors/scripts/flow-state-monitor.sh +0 -102
  109. package/output-styles/curdx-evidence-first.md +0 -34
  110. package/output-styles/curdx-fast-mode.md +0 -42
  111. package/output-styles/curdx-spec-mode.md +0 -46
  112. package/schemas/agent-frontmatter.schema.json +0 -66
  113. package/schemas/config.schema.json +0 -134
  114. package/schemas/gate-frontmatter.schema.json +0 -30
  115. package/schemas/hooks.schema.json +0 -115
  116. package/schemas/output-style-frontmatter.schema.json +0 -22
  117. package/schemas/plugin-manifest.schema.json +0 -436
  118. package/schemas/plugin-settings.schema.json +0 -29
  119. package/schemas/skill-frontmatter.schema.json +0 -177
  120. package/schemas/spec-frontmatter.schema.json +0 -42
  121. package/schemas/spec-state.schema.json +0 -165
  122. package/settings.json +0 -8
  123. package/skills/brownfield-index/SKILL.md +0 -53
  124. package/skills/brownfield-index/references/applicability.md +0 -12
  125. package/skills/brownfield-index/references/handoff.md +0 -8
  126. package/skills/brownfield-index/references/index-contract.md +0 -10
  127. package/skills/browser-qa/SKILL.md +0 -39
  128. package/skills/browser-qa/references/handoff.md +0 -6
  129. package/skills/browser-qa/references/prerequisites.md +0 -10
  130. package/skills/browser-qa/references/qa-contract.md +0 -20
  131. package/skills/cancel/SKILL.md +0 -41
  132. package/skills/cancel/references/destructive-mode.md +0 -17
  133. package/skills/cancel/references/reporting.md +0 -18
  134. package/skills/cancel/references/state-recovery.md +0 -30
  135. package/skills/cancel/references/target-resolution.md +0 -7
  136. package/skills/debug/SKILL.md +0 -45
  137. package/skills/debug/references/context-gathering.md +0 -11
  138. package/skills/debug/references/failure-guard.md +0 -25
  139. package/skills/debug/references/intake.md +0 -12
  140. package/skills/debug/references/phase-workflow.md +0 -34
  141. package/skills/debug/references/reporting.md +0 -20
  142. package/skills/epic/SKILL.md +0 -39
  143. package/skills/epic/references/epic-artifacts.md +0 -20
  144. package/skills/epic/references/epic-intake.md +0 -9
  145. package/skills/epic/references/slice-handoff.md +0 -16
  146. package/skills/fast/SKILL.md +0 -62
  147. package/skills/fast/references/applicability.md +0 -25
  148. package/skills/fast/references/clarification.md +0 -20
  149. package/skills/fast/references/execution-contract.md +0 -56
  150. package/skills/help/SKILL.md +0 -55
  151. package/skills/help/references/dispatch.md +0 -20
  152. package/skills/help/references/overview.md +0 -39
  153. package/skills/help/references/troubleshoot.md +0 -47
  154. package/skills/help/references/workflow.md +0 -37
  155. package/skills/implement/SKILL.md +0 -104
  156. package/skills/implement/references/error-recovery.md +0 -36
  157. package/skills/implement/references/linear-execution.md +0 -43
  158. package/skills/implement/references/native-task-sync.md +0 -107
  159. package/skills/implement/references/preflight.md +0 -43
  160. package/skills/implement/references/progress-contract.md +0 -36
  161. package/skills/implement/references/state-init.md +0 -36
  162. package/skills/implement/references/stop-hook-execution.md +0 -50
  163. package/skills/implement/references/strategy-router.md +0 -38
  164. package/skills/implement/references/subagent-execution.md +0 -57
  165. package/skills/implement/references/wave-execution.md +0 -180
  166. package/skills/init/SKILL.md +0 -49
  167. package/skills/init/references/gitignore-and-health.md +0 -26
  168. package/skills/init/references/next-steps.md +0 -22
  169. package/skills/init/references/preflight.md +0 -15
  170. package/skills/init/references/scaffold-contract.md +0 -27
  171. package/skills/review/SKILL.md +0 -82
  172. package/skills/review/references/optional-passes.md +0 -48
  173. package/skills/review/references/preflight.md +0 -38
  174. package/skills/review/references/report-contract.md +0 -49
  175. package/skills/review/references/reporting.md +0 -20
  176. package/skills/review/references/stage-execution.md +0 -32
  177. package/skills/security-audit/SKILL.md +0 -47
  178. package/skills/security-audit/references/audit-contract.md +0 -21
  179. package/skills/security-audit/references/gate-handoff.md +0 -8
  180. package/skills/security-audit/references/scope-and-depth.md +0 -9
  181. package/skills/spec/SKILL.md +0 -100
  182. package/skills/spec/references/artifact-landing.md +0 -31
  183. package/skills/spec/references/phase-execution.md +0 -50
  184. package/skills/spec/references/planning-review.md +0 -31
  185. package/skills/spec/references/preflight-and-routing.md +0 -46
  186. package/skills/spec/references/reporting.md +0 -21
  187. package/skills/start/SKILL.md +0 -84
  188. package/skills/start/references/branch-routing.md +0 -51
  189. package/skills/start/references/mode-semantics.md +0 -12
  190. package/skills/start/references/preflight.md +0 -13
  191. package/skills/start/references/reporting.md +0 -20
  192. package/skills/start/references/state-seeding.md +0 -44
  193. package/skills/start/references/workflow-handoff.md +0 -26
  194. package/skills/status/SKILL.md +0 -41
  195. package/skills/status/references/gather-contract.md +0 -30
  196. package/skills/status/references/health-rules.md +0 -27
  197. package/skills/status/references/output-contract.md +0 -25
  198. package/skills/status/references/preflight.md +0 -10
  199. package/skills/status/references/recovery-hints.md +0 -18
  200. package/skills/ui-sketch/SKILL.md +0 -39
  201. package/skills/ui-sketch/references/brief-intake.md +0 -10
  202. package/skills/ui-sketch/references/iteration-handoff.md +0 -5
  203. package/skills/ui-sketch/references/variant-contract.md +0 -15
  204. package/skills/verify/SKILL.md +0 -56
  205. package/skills/verify/references/evidence-workflow.md +0 -39
  206. package/skills/verify/references/output-contract.md +0 -23
  207. package/skills/verify/references/preflight.md +0 -11
  208. package/skills/verify/references/report-handoff.md +0 -35
  209. package/skills/verify/references/strict-mode.md +0 -12
  210. package/templates/CONTEXT.md.tmpl +0 -53
  211. package/templates/PROJECT.md.tmpl +0 -59
  212. package/templates/ROADMAP.md.tmpl +0 -50
  213. package/templates/STATE.md.tmpl +0 -49
  214. package/templates/config.json.tmpl +0 -51
  215. package/templates/design.md.tmpl +0 -83
  216. package/templates/progress.md.tmpl +0 -77
  217. package/templates/requirements.md.tmpl +0 -76
  218. package/templates/research.md.tmpl +0 -83
  219. package/templates/tasks.md.tmpl +0 -107
@@ -1,94 +0,0 @@
1
- #!/usr/bin/env bash
2
-
3
- set -u
4
-
5
- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
6
- . "$SCRIPT_DIR/common.sh"
7
-
8
- INPUT="$(cat 2>/dev/null || echo "{}")"
9
-
10
- if ! has_python3; then
11
- exit 0
12
- fi
13
-
14
- export CURDX_FLOW_CONTEXT_WATCH_INPUT="$INPUT"
15
-
16
- read_json_field() {
17
- local field="$1"
18
- python3 - "$field" <<'PY' 2>/dev/null
19
- import json
20
- import os
21
- import sys
22
-
23
- field = sys.argv[1]
24
- try:
25
- data = json.loads(os.environ.get("CURDX_FLOW_CONTEXT_WATCH_INPUT", "{}"))
26
- except Exception:
27
- data = {}
28
-
29
- value = data.get(field, "")
30
- if value is None:
31
- value = ""
32
- print(value)
33
- PY
34
- }
35
-
36
- HOOK_EVENT_NAME="$(read_json_field hook_event_name)"
37
- NEW_CWD="$(read_json_field new_cwd)"
38
- CURRENT_CWD="$(read_json_field cwd)"
39
-
40
- START_DIR="$CURRENT_CWD"
41
- if [ "$HOOK_EVENT_NAME" = "CwdChanged" ] && [ -n "$NEW_CWD" ]; then
42
- START_DIR="$NEW_CWD"
43
- fi
44
-
45
- FLOW_ROOT="$(resolve_flow_root "$START_DIR" 2>/dev/null || true)"
46
- ACTIVE_SPEC=""
47
- SPEC_DIR=""
48
-
49
- if [ -n "$FLOW_ROOT" ] && [ -f "$FLOW_ROOT/.flow/.active-spec" ]; then
50
- ACTIVE_SPEC="$(cat "$FLOW_ROOT/.flow/.active-spec" 2>/dev/null || true)"
51
- fi
52
-
53
- if [ -n "$FLOW_ROOT" ] && [ -n "$ACTIVE_SPEC" ]; then
54
- SPEC_DIR="$FLOW_ROOT/.flow/specs/$ACTIVE_SPEC"
55
- fi
56
-
57
- if [ -n "${CLAUDE_ENV_FILE:-}" ]; then
58
- {
59
- printf 'export CURDX_FLOW_PROJECT_ROOT=%s\n' "$(json_escape "$FLOW_ROOT")"
60
- printf 'export CURDX_FLOW_ACTIVE_SPEC=%s\n' "$(json_escape "$ACTIVE_SPEC")"
61
- if [ -n "$SPEC_DIR" ]; then
62
- printf 'export CURDX_FLOW_SPEC_DIR=%s\n' "$(json_escape "$SPEC_DIR")"
63
- else
64
- printf 'export CURDX_FLOW_SPEC_DIR=%s\n' "$(json_escape "")"
65
- fi
66
- } >> "$CLAUDE_ENV_FILE" 2>/dev/null || true
67
- fi
68
-
69
- if [ -z "$FLOW_ROOT" ]; then
70
- printf '{"watchPaths":[]}\n'
71
- exit 0
72
- fi
73
-
74
- export CURDX_FLOW_CONTEXT_WATCH_ROOT="$FLOW_ROOT"
75
- export CURDX_FLOW_CONTEXT_WATCH_ACTIVE="$ACTIVE_SPEC"
76
-
77
- python3 <<'PY' 2>/dev/null
78
- import json
79
- import os
80
- from pathlib import Path
81
-
82
- root = Path(os.environ["CURDX_FLOW_CONTEXT_WATCH_ROOT"])
83
- active = os.environ.get("CURDX_FLOW_CONTEXT_WATCH_ACTIVE", "").strip()
84
-
85
- watch_paths = [str(root / ".flow" / ".active-spec")]
86
- if active:
87
- spec_dir = root / ".flow" / "specs" / active
88
- watch_paths.append(str(spec_dir / ".state.json"))
89
- watch_paths.append(str(spec_dir / "tasks.md"))
90
-
91
- print(json.dumps({"watchPaths": watch_paths}, ensure_ascii=True))
92
- PY
93
-
94
- exit 0
@@ -1,53 +0,0 @@
1
- #!/usr/bin/env bash
2
- # CurDX-Flow SessionStart baseline injection
3
- # Injects the L1 baseline (Karpathy 4 principles + mandatory tool rules + 3 red lines)
4
- # as additionalContext at every session boot (startup, /clear, post-compact).
5
- #
6
- # Wired under SessionStart with matcher "startup|clear|compact" so the
7
- # baseline survives startup, /clear, and post-compact resume.
8
-
9
- set -u
10
-
11
- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
12
- . "$SCRIPT_DIR/common.sh"
13
-
14
- CONTEXT='## CurDX-Flow Mind Baseline (L1 — always on)
15
-
16
- ### 1. Think Before Coding
17
- - State assumptions before any non-trivial task
18
- - When uncertain, use AskUserQuestion — do not silently assume
19
- - When confused, stop — do not push forward
20
-
21
- ### 2. Simplicity First
22
- - YAGNI: do not write features beyond what was requested
23
- - No single-use abstractions
24
- - If 200 lines suffice, do not write 1000
25
-
26
- ### 3. Surgical Changes
27
- - Modify only the lines that must change
28
- - Match existing style
29
- - Do not delete pre-existing dead code
30
-
31
- ### 4. Goal-Driven
32
- - Define a verifiable success criterion first
33
- - Forbidden to say done/fixed/working without evidence
34
-
35
- ## Mandatory Tools (L2 — enforced)
36
-
37
- - library/framework questions → **context7 first** (`mcp__context7__*`); forbidden to rely on training memory
38
- - planning / design / architecture / review → **sequential-thinking first** (≥5 thoughts)
39
- - before any task → **claude-mem search** (if installed)
40
- - UI code → **frontend-design skill** (if installed)
41
- - browser QA → **chrome-devtools MCP**
42
-
43
- ## Three Red Lines (L3 — inherited from pua, universal)
44
-
45
- 1. **Closed loop**: claiming "done"? Provide evidence (build output / passing tests / curl result)
46
- 2. **Fact-driven**: verify before saying "probably". An unverified attribution is blame-shifting
47
- 3. **Exhaust everything**: before saying "I cannot", complete the systematic 4-stage debugging'
48
-
49
- if has_python3; then
50
- emit_session_start_context "$CONTEXT"
51
- fi
52
-
53
- exit 0
@@ -1,69 +0,0 @@
1
- #!/usr/bin/env bash
2
- # CurDX-Flow PreToolUse Hook for AskUserQuestion
3
- # Blocks AskUserQuestion when the active spec has quickMode=true.
4
- # This prevents quick execution loops 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
- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
12
- . "$SCRIPT_DIR/common.sh"
13
-
14
- # Read input (Claude sends JSON on stdin for PreToolUse)
15
- INPUT=$(cat 2>/dev/null || echo "{}")
16
-
17
- if ! command -v python3 >/dev/null 2>&1; then
18
- # Without JSON parsing, allow everything (fail open)
19
- exit 0
20
- fi
21
-
22
- # Parse the tool name being invoked
23
- TOOL_NAME=$(echo "$INPUT" | python3 -c '
24
- import json, sys
25
- try:
26
- d = json.load(sys.stdin)
27
- print(d.get("tool_name", ""))
28
- except Exception:
29
- print("")
30
- ' 2>/dev/null)
31
-
32
- # We only guard AskUserQuestion
33
- if [ "$TOOL_NAME" != "AskUserQuestion" ]; then
34
- exit 0
35
- fi
36
-
37
- # Check if we're in a flow project with quick mode enabled.
38
- FLOW_ROOT="$(resolve_flow_root 2>/dev/null || true)"
39
- [ -n "$FLOW_ROOT" ] || exit 0
40
-
41
- ACTIVE=$(cat "$FLOW_ROOT/.flow/.active-spec" 2>/dev/null)
42
- [ -z "$ACTIVE" ] && exit 0
43
-
44
- STATE_FILE="$FLOW_ROOT/.flow/specs/$ACTIVE/.state.json"
45
- [ ! -f "$STATE_FILE" ] && exit 0
46
-
47
- # Read quickMode. Pass STATE_FILE via env (NOT shell interpolation
48
- # into the python source) so an active-spec name containing quotes/$ cannot
49
- # inject python code.
50
- export STATE_FILE
51
- QUICK_MODE=$(python3 -c '
52
- import json, os
53
- try:
54
- s = json.load(open(os.environ["STATE_FILE"]))
55
- qm = s.get("quickMode", False)
56
- print("true" if qm else "false")
57
- except Exception:
58
- print("false")
59
- ' 2>/dev/null)
60
-
61
- if [ "$QUICK_MODE" = "true" ]; then
62
- # Block and inject guidance
63
- MSG="[CurDX-Flow quick-mode-guard] Active spec '$ACTIVE' is in quick mode — AskUserQuestion is forbidden. Decide based on user preferences in .flow/CONTEXT.md plus the most reasonable assumption, and record your assumption in .progress.md."
64
- emit_pretooluse_deny "$MSG"
65
- exit 0
66
- fi
67
-
68
- # Allow
69
- exit 0
@@ -1,94 +0,0 @@
1
- #!/usr/bin/env bash
2
- # CurDX-Flow SessionStart Hook
3
- # Duties:
4
- # 1. Daily dependency check — nudge user to `npx @curdx/flow install --all` if recommended plugins missing
5
- # 2. Load active spec progress into session context
6
- # 3. Persist stable CurDX-Flow environment hints for this session
7
- #
8
- # Design notes:
9
- # - Idempotent: marker file tracks last check date
10
- # - Silent when everything is healthy (no noise)
11
- # - Graceful degradation: missing `claude` CLI doesn't break hook
12
-
13
- set -u
14
-
15
- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
16
- . "$SCRIPT_DIR/common.sh"
17
-
18
- DATA_DIR="${CLAUDE_PLUGIN_DATA:-$HOME/.claude/plugins/data/curdx-flow}"
19
- MARKER="$DATA_DIR/.deps-checked"
20
- TODAY="$(date +%Y-%m-%d)"
21
- ADDITIONAL_CONTEXT=""
22
- FLOW_ROOT="$(resolve_flow_root 2>/dev/null || true)"
23
-
24
- mkdir -p "$DATA_DIR" 2>/dev/null || true
25
-
26
- # ---------- 1. Dependency check (once per day) ----------
27
- LAST_CHECK=""
28
- [ -f "$MARKER" ] && LAST_CHECK="$(cat "$MARKER" 2>/dev/null)"
29
-
30
- if env_flag_enabled "${CLAUDE_PLUGIN_OPTION_DAILY_DEPENDENCY_CHECK:-1}" && [ "$LAST_CHECK" != "$TODAY" ]; then
31
- MISSING=()
32
-
33
- # Skip if `claude` CLI is not on PATH
34
- if command -v claude >/dev/null 2>&1; then
35
- INSTALLED="$(claude plugin list 2>/dev/null || true)"
36
-
37
- # Names must stay in lockstep with cli/registry.js RECOMMENDED_PLUGINS.
38
- # Drift is guarded by test/registry-session-start-parity.test.js.
39
- echo "$INSTALLED" | grep -q 'pua' || MISSING+=("pua")
40
- echo "$INSTALLED" | grep -q 'claude-mem' || MISSING+=("claude-mem")
41
- echo "$INSTALLED" | grep -q 'frontend-design' || MISSING+=("frontend-design")
42
- echo "$INSTALLED" | grep -q 'chrome-devtools-mcp' || MISSING+=("chrome-devtools-mcp")
43
- fi
44
-
45
- if [ "${#MISSING[@]}" -gt 0 ]; then
46
- JOINED="$(IFS=,; echo "${MISSING[*]}")"
47
- ADDITIONAL_CONTEXT+="## CurDX-Flow Recommended Plugins Check\n\nThe following recommended plugins were not detected: **${JOINED}**\n\nRun \`npx @curdx/flow install --all\` for interactive one-shot install. Run \`npx @curdx/flow doctor\` for the full health report.\n\n"
48
- fi
49
-
50
- { echo "$TODAY" > "$MARKER"; } 2>/dev/null || true
51
- fi
52
-
53
- # ---------- 2. Load .flow/ state (if project is a flow project) ----------
54
- if [ -n "$FLOW_ROOT" ]; then
55
- ADDITIONAL_CONTEXT+="## CurDX-Flow Project Active\n\n"
56
- ADDITIONAL_CONTEXT+="- Plugin root: \`${CLAUDE_PLUGIN_ROOT:-unknown}\`\n"
57
- ADDITIONAL_CONTEXT+="- Plugin data: \`${CLAUDE_PLUGIN_DATA:-$DATA_DIR}\`\n"
58
- ADDITIONAL_CONTEXT+="- Best practice: write long agent artifacts to disk first; keep final assistant summaries short.\n\n"
59
-
60
- if [ -f "$FLOW_ROOT/.flow/PROJECT.md" ]; then
61
- ADDITIONAL_CONTEXT+="### Project Vision\n$(head -80 "$FLOW_ROOT/.flow/PROJECT.md")\n\n"
62
- fi
63
-
64
- if [ -f "$FLOW_ROOT/.flow/.active-spec" ]; then
65
- ACTIVE="$(cat "$FLOW_ROOT/.flow/.active-spec" 2>/dev/null)"
66
- if [ -n "$ACTIVE" ] && [ -d "$FLOW_ROOT/.flow/specs/$ACTIVE" ]; then
67
- ADDITIONAL_CONTEXT+="### Active Spec: \`$ACTIVE\`\n\n"
68
- if [ -f "$FLOW_ROOT/.flow/specs/$ACTIVE/.progress.md" ]; then
69
- ADDITIONAL_CONTEXT+="$(head -40 "$FLOW_ROOT/.flow/specs/$ACTIVE/.progress.md")\n\n"
70
- fi
71
- fi
72
- fi
73
- fi
74
-
75
- # ---------- 3. Persist session environment hints ----------
76
- if [ -n "${CLAUDE_ENV_FILE:-}" ]; then
77
- {
78
- printf 'export CURDX_FLOW_PLUGIN_ROOT=%s\n' "$(json_escape "${CLAUDE_PLUGIN_ROOT:-}")"
79
- printf 'export CURDX_FLOW_PLUGIN_DATA=%s\n' "$(json_escape "${CLAUDE_PLUGIN_DATA:-$DATA_DIR}")"
80
- if [ -n "$FLOW_ROOT" ]; then
81
- printf 'export CURDX_FLOW_PROJECT_ROOT=%s\n' "$(json_escape "$FLOW_ROOT")"
82
- fi
83
- if [ -n "$FLOW_ROOT" ] && [ -f "$FLOW_ROOT/.flow/.active-spec" ]; then
84
- printf 'export CURDX_FLOW_ACTIVE_SPEC=%s\n' "$(json_escape "$(cat "$FLOW_ROOT/.flow/.active-spec" 2>/dev/null)")"
85
- fi
86
- } >> "$CLAUDE_ENV_FILE" 2>/dev/null || true
87
- fi
88
-
89
- # ---------- 4. Emit hook output ----------
90
- if [ -n "$ADDITIONAL_CONTEXT" ]; then
91
- emit_session_start_context "$ADDITIONAL_CONTEXT"
92
- fi
93
-
94
- exit 0
@@ -1,87 +0,0 @@
1
- #!/usr/bin/env bash
2
-
3
- set -u
4
-
5
- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
6
- . "$SCRIPT_DIR/common.sh"
7
-
8
- INPUT="$(cat 2>/dev/null || echo "{}")"
9
- DATA_DIR="${CLAUDE_PLUGIN_DATA:-$HOME/.claude/plugins/data/curdx-flow}"
10
- FLOW_ROOT="$(resolve_flow_root 2>/dev/null || true)"
11
-
12
- [ -n "$FLOW_ROOT" ] || exit 0
13
-
14
- ACTIVE="$(cat "$FLOW_ROOT/.flow/.active-spec" 2>/dev/null || true)"
15
- [ -n "$ACTIVE" ] || exit 0
16
-
17
- if ! has_python3; then
18
- emit_userprompt_submit_title "curdx-flow/${ACTIVE}"
19
- exit 0
20
- fi
21
-
22
- export CURDX_FLOW_SESSION_TITLE_INPUT="$INPUT"
23
- export CURDX_FLOW_SESSION_TITLE_ACTIVE="$ACTIVE"
24
- export CURDX_FLOW_ROOT="$FLOW_ROOT"
25
-
26
- TITLE_INFO="$(python3 <<'PY' 2>/dev/null
27
- import json
28
- import os
29
- from pathlib import Path
30
-
31
- active = os.environ["CURDX_FLOW_SESSION_TITLE_ACTIVE"]
32
- data = json.loads(os.environ.get("CURDX_FLOW_SESSION_TITLE_INPUT", "{}"))
33
- session_id = data.get("session_id", "")
34
-
35
- phase = ""
36
- task_index = 0
37
- total_tasks = 0
38
-
39
- state_path = Path(os.environ["CURDX_FLOW_ROOT"]) / ".flow" / "specs" / active / ".state.json"
40
- if state_path.exists():
41
- try:
42
- state = json.loads(state_path.read_text(encoding="utf-8"))
43
- phase = state.get("phase") or ""
44
- execute_state = state.get("execute_state") or {}
45
- task_index = int(execute_state.get("task_index") or 0)
46
- total_tasks = int(execute_state.get("total_tasks") or 0)
47
- except Exception:
48
- phase = ""
49
- task_index = 0
50
- total_tasks = 0
51
-
52
- label = phase
53
- if phase == "execute" and total_tasks > 0:
54
- label = f"{phase} {task_index}/{total_tasks}"
55
-
56
- title = f"curdx-flow/{active}"
57
- if label:
58
- title = f"{title} [{label}]"
59
-
60
- print(session_id)
61
- print(title)
62
- PY
63
- )"
64
-
65
- SESSION_ID="$(printf '%s\n' "$TITLE_INFO" | sed -n '1p')"
66
- TITLE="$(printf '%s\n' "$TITLE_INFO" | sed -n '2p')"
67
- [ -n "$TITLE" ] || exit 0
68
-
69
- if [ -n "$SESSION_ID" ]; then
70
- MARKER_DIR="$DATA_DIR/session-titles"
71
- MARKER_FILE="$MARKER_DIR/$SESSION_ID"
72
- LAST_TITLE=""
73
-
74
- if [ -f "$MARKER_FILE" ]; then
75
- LAST_TITLE="$(cat "$MARKER_FILE" 2>/dev/null || true)"
76
- fi
77
-
78
- if [ "$LAST_TITLE" = "$TITLE" ]; then
79
- exit 0
80
- fi
81
-
82
- mkdir -p "$MARKER_DIR" 2>/dev/null || true
83
- printf '%s\n' "$TITLE" > "$MARKER_FILE" 2>/dev/null || true
84
- fi
85
-
86
- emit_userprompt_submit_title "$TITLE"
87
- exit 0
@@ -1,231 +0,0 @@
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
- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
19
- . "$SCRIPT_DIR/common.sh"
20
-
21
- DATA_DIR="${CLAUDE_PLUGIN_DATA:-$HOME/.claude/plugins/data/curdx-flow}"
22
-
23
- # ---------- helper: exit with "allow stop" ----------
24
- allow_stop() {
25
- # No output = allow stop normally
26
- exit 0
27
- }
28
-
29
- autonomous_blocking_enabled() {
30
- env_flag_enabled "${CLAUDE_PLUGIN_OPTION_AUTONOMOUS_BLOCKING:-1}"
31
- }
32
-
33
- # ---------- helper: block and inject continuation ----------
34
- block_continue() {
35
- local reason="$1"
36
- emit_stop_block "$reason"
37
- exit 0
38
- }
39
-
40
- # ---------- 1. Must be a flow project ----------
41
- FLOW_ROOT="$(resolve_flow_root 2>/dev/null || true)"
42
- [ -n "$FLOW_ROOT" ] || allow_stop
43
-
44
- # ---------- 2. Must have active spec ----------
45
- ACTIVE=$(cat "$FLOW_ROOT/.flow/.active-spec" 2>/dev/null)
46
- [ -z "$ACTIVE" ] && allow_stop
47
- SPEC_DIR="$FLOW_ROOT/.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
- # Export STATE_FILE BEFORE invoking python3 — the heredoc-based parser reads
60
- # os.environ["STATE_FILE"]. Previously the export was placed after the
61
- # heredoc, so python3 always got None, json.load(None) silently failed, and
62
- # the stop-hook strategy never activated.
63
- export STATE_FILE
64
-
65
- read STRATEGY PHASE TASK_INDEX TOTAL_TASKS FAILED ROUNDS RECOVERY_MODE MAX_FIX_TASKS <<EOF
66
- $(python3 <<'PY'
67
- import json, os, sys
68
- p = os.environ.get("STATE_FILE")
69
- try:
70
- s = json.load(open(p))
71
- except Exception:
72
- sys.exit(0)
73
- strategy = s.get("strategy", "auto")
74
- phase = s.get("phase", "")
75
- ex = s.get("execute_state", {}) or {}
76
- ti = ex.get("task_index", 0)
77
- tt = ex.get("total_tasks", 0)
78
- failed = ex.get("failed_attempts", 0)
79
- rounds = ex.get("global_iteration", 0)
80
- recovery_mode = ex.get("recovery_mode", "manual")
81
- max_fix_tasks = ex.get("max_fix_tasks_per_original", 2)
82
- print(strategy, phase, ti, tt, failed, rounds, recovery_mode, max_fix_tasks)
83
- PY
84
- )
85
- EOF
86
-
87
- # Only activate for stop-hook strategy + execute phase
88
- [ "$STRATEGY" != "stop-hook" ] && allow_stop
89
- [ "$PHASE" != "execute" ] && allow_stop
90
-
91
- if ! autonomous_blocking_enabled; then
92
- allow_stop
93
- fi
94
-
95
- # ---------- 5. Check hook input + completion signal in transcript ----------
96
- # Claude Code passes transcript path via stdin as JSON: {"transcript_path": "/path/..."}
97
- # We read stdin to detect ALL_TASKS_COMPLETE or TASK_FAILED
98
- INPUT=$(cat 2>/dev/null || echo "{}")
99
- TRANSCRIPT_PATH=$(echo "$INPUT" | python3 -c 'import json,sys;
100
- try: print(json.load(sys.stdin).get("transcript_path",""))
101
- except: print("")' 2>/dev/null)
102
-
103
- STOP_HOOK_ACTIVE=$(echo "$INPUT" | python3 -c 'import json,sys;
104
- try: print("true" if json.load(sys.stdin).get("stop_hook_active", False) else "false")
105
- except: print("false")' 2>/dev/null)
106
-
107
- if [ "$STOP_HOOK_ACTIVE" = "true" ]; then
108
- # Claude Code sets stop_hook_active during a stop-hook continuation.
109
- # Treat it as context only: the final decision still comes from transcript
110
- # signals, state-file progress, and tasks.md parity. Unconditionally allowing
111
- # stop here can terminate an in-flight stop-hook loop after the first
112
- # continuation, leaving remaining tasks stranded.
113
- echo "[CurDX-Flow stop-hook] stop_hook_active=true; evaluating transcript/state before deciding" >&2
114
- fi
115
-
116
- TRANSCRIPT_TAIL=""
117
- if [ -n "$TRANSCRIPT_PATH" ] && [ -f "$TRANSCRIPT_PATH" ]; then
118
- # Read last 50KB only (efficiency)
119
- TRANSCRIPT_TAIL=$(tail -c 51200 "$TRANSCRIPT_PATH" 2>/dev/null || echo "")
120
- fi
121
-
122
- # Python state-file updates: use quoted heredocs (<<'PY') + os.environ so
123
- # the spec-name-derived STATE_FILE path is NEVER interpolated into the
124
- # python source text. Previously a spec name containing single quotes or
125
- # $-signs could break the script or inject arbitrary code.
126
-
127
- # Helper: count unchecked tasks in tasks.md. If tasks.md is absent, return 0
128
- # to avoid blocking recovery for partially-initialized specs.
129
- unchecked_task_count() {
130
- local tasks_file="$SPEC_DIR/tasks.md"
131
- [ ! -f "$tasks_file" ] && { echo 0; return; }
132
- grep -Ec '^- \[ \] \*\*[0-9]+(\.[0-9]+|\.VF|\.X|\.X\+1)*\*\*' "$tasks_file" 2>/dev/null || echo 0
133
- }
134
-
135
- last_task_signal() {
136
- local msg="${1:-}"
137
- printf '%s' "$msg" \
138
- | grep -Eo 'ALL_TASKS_COMPLETE|TASK_(COMPLETE|FAILED):[[:space:]]*[0-9]+(\.([0-9]+|VF|X(\+[0-9]+)?))*' \
139
- | tail -1
140
- }
141
-
142
- failed_task_id() {
143
- local msg="${1:-}"
144
- printf '%s' "$msg" | sed -nE 's/.*TASK_FAILED:[[:space:]]*([0-9]+(\.([0-9]+|VF|X(\+[0-9]+)?))*).*/\1/p' | tail -1
145
- }
146
-
147
- mark_execute_complete() {
148
- python3 <<'PY' 2>/dev/null
149
- import json, os
150
- p = os.environ["STATE_FILE"]
151
- s = json.load(open(p))
152
- s.setdefault("phase_status", {})["execute"] = "completed"
153
- s["phase"] = "verify"
154
- json.dump(s, open(p, "w"), indent=2, ensure_ascii=False)
155
- PY
156
- }
157
-
158
- # Check for explicit completion signals
159
- LAST_TASK_SIGNAL="$(last_task_signal "$TRANSCRIPT_TAIL")"
160
-
161
- if [ "$LAST_TASK_SIGNAL" = "ALL_TASKS_COMPLETE" ]; then
162
- UNCHECKED="$(unchecked_task_count)"
163
- if [ "${UNCHECKED:-0}" -gt 0 ]; then
164
- block_continue "[CurDX-Flow stop-hook] ALL_TASKS_COMPLETE was emitted, but tasks.md still has ${UNCHECKED} unchecked task(s). Read .flow/specs/${ACTIVE}/tasks.md, complete only the remaining unchecked tasks, update tasks.md, then emit ALL_TASKS_COMPLETE again."
165
- fi
166
- mark_execute_complete
167
- allow_stop
168
- fi
169
-
170
- # Check for the latest fail signal (accumulate; actual stop decision below)
171
- if printf '%s' "$LAST_TASK_SIGNAL" | grep -q "^TASK_FAILED"; then
172
- FAILED_TASK="$(failed_task_id "$LAST_TASK_SIGNAL")"
173
- [ -z "$FAILED_TASK" ] && FAILED_TASK="the current task"
174
-
175
- # Increment failed_attempts
176
- python3 <<'PY' 2>/dev/null
177
- import json, os
178
- p = os.environ["STATE_FILE"]
179
- s = json.load(open(p))
180
- s.setdefault("execute_state", {})
181
- s["execute_state"]["failed_attempts"] = s["execute_state"].get("failed_attempts", 0) + 1
182
- json.dump(s, open(p, "w"), indent=2, ensure_ascii=False)
183
- PY
184
- # Re-read — again via os.environ, no shell interpolation into python.
185
- FAILED=$(python3 -c 'import json, os; print(json.load(open(os.environ["STATE_FILE"]))["execute_state"]["failed_attempts"])' 2>/dev/null || echo 0)
186
-
187
- if [ "${FAILED:-0}" -lt 3 ]; then
188
- if [ "${RECOVERY_MODE:-manual}" = "fix-task" ]; then
189
- block_continue "[CurDX-Flow stop-hook] TASK_FAILED observed for ${FAILED_TASK}. Do not skip it. Recovery mode is fix-task: insert one targeted [FIX ${FAILED_TASK}] task immediately after the failed task in tasks.md (max ${MAX_FIX_TASKS:-2} fix task(s) per original), update .state.json execute_state.fix_task_map, then execute the fix task before retrying ${FAILED_TASK}. The fix task must include Do, Files, Done when, Verify, and Commit fields."
190
- fi
191
-
192
- block_continue "[CurDX-Flow stop-hook] TASK_FAILED observed for ${FAILED_TASK}. Do not advance past the failed task. Re-read tasks.md, perform root-cause analysis, retry the first unchecked task, and emit TASK_COMPLETE only after its Verify command passes. failed_attempts=${FAILED}/3."
193
- fi
194
- fi
195
-
196
- # ---------- 6. Safety brakes ----------
197
- if [ "$FAILED" -ge 3 ]; then
198
- # Too many failures — stop and surface
199
- allow_stop
200
- fi
201
-
202
- if [ "$ROUNDS" -ge 100 ]; then
203
- # Runaway loop protection
204
- allow_stop
205
- fi
206
-
207
- # Check if all tasks done
208
- if [ "$TASK_INDEX" -ge "$TOTAL_TASKS" ] && [ "$TOTAL_TASKS" -gt 0 ]; then
209
- UNCHECKED="$(unchecked_task_count)"
210
- if [ "${UNCHECKED:-0}" -gt 0 ]; then
211
- block_continue "[CurDX-Flow stop-hook] State says execute is complete (${TASK_INDEX}/${TOTAL_TASKS}), but tasks.md still has ${UNCHECKED} unchecked task(s). Continue with the first unchecked task; do not add new tasks."
212
- fi
213
- mark_execute_complete
214
- allow_stop
215
- fi
216
-
217
- # ---------- 7. Block and continue ----------
218
- # Increment round counter
219
- python3 <<'PY' 2>/dev/null
220
- import json, os
221
- p = os.environ["STATE_FILE"]
222
- s = json.load(open(p))
223
- s.setdefault("execute_state", {})
224
- s["execute_state"]["global_iteration"] = s["execute_state"].get("global_iteration", 0) + 1
225
- json.dump(s, open(p, "w"), indent=2, ensure_ascii=False)
226
- PY
227
-
228
- NEXT_INDEX=$((TASK_INDEX + 1))
229
- 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."
230
-
231
- block_continue "$REASON"