@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.
- package/CHANGELOG.md +21 -87
- package/LICENSE +1 -1
- package/README.md +28 -129
- package/dist/index.mjs +995 -0
- package/package.json +33 -44
- package/.claude-plugin/marketplace.json +0 -48
- package/.claude-plugin/plugin.json +0 -52
- package/agent-preamble/preamble.md +0 -314
- package/agents/flow-adversary.md +0 -203
- package/agents/flow-architect.md +0 -198
- package/agents/flow-brownfield-analyst.md +0 -143
- package/agents/flow-debugger.md +0 -321
- package/agents/flow-edge-hunter.md +0 -289
- package/agents/flow-executor.md +0 -269
- package/agents/flow-orchestrator.md +0 -145
- package/agents/flow-planner.md +0 -247
- package/agents/flow-product-designer.md +0 -159
- package/agents/flow-qa-engineer.md +0 -282
- package/agents/flow-researcher.md +0 -166
- package/agents/flow-reviewer.md +0 -304
- package/agents/flow-security-auditor.md +0 -401
- package/agents/flow-triage-analyst.md +0 -272
- package/agents/flow-ui-researcher.md +0 -230
- package/agents/flow-ux-designer.md +0 -221
- package/agents/flow-verifier.md +0 -350
- package/bin/curdx-flow +0 -5
- package/bin/curdx-flow-state +0 -104
- package/bin/curdx-flow.js +0 -54
- package/cli/README.md +0 -104
- package/cli/doctor-workflow.js +0 -483
- package/cli/doctor.js +0 -73
- package/cli/help.js +0 -59
- package/cli/install-bundled-mcps.js +0 -37
- package/cli/install-companions.js +0 -19
- package/cli/install-context7-config.js +0 -80
- package/cli/install-curdx-plugin.js +0 -96
- package/cli/install-language.js +0 -35
- package/cli/install-next-steps.js +0 -29
- package/cli/install-options.js +0 -9
- package/cli/install-paths.js +0 -52
- package/cli/install-recommended-plugins.js +0 -104
- package/cli/install-required-plugins.js +0 -57
- package/cli/install-self-update.js +0 -62
- package/cli/install-workflow.js +0 -209
- package/cli/install.js +0 -101
- package/cli/lib/claude-commands.js +0 -41
- package/cli/lib/claude-ops.js +0 -47
- package/cli/lib/claude.js +0 -183
- package/cli/lib/config.js +0 -24
- package/cli/lib/doctor-claude-settings.js +0 -1186
- package/cli/lib/doctor-report.js +0 -978
- package/cli/lib/doctor-runtime-environment.js +0 -196
- package/cli/lib/frontmatter.js +0 -44
- package/cli/lib/json-schema.js +0 -57
- package/cli/lib/logging.js +0 -25
- package/cli/lib/process.js +0 -60
- package/cli/lib/prompts.js +0 -135
- package/cli/lib/runtime.js +0 -107
- package/cli/lib/semver.js +0 -109
- package/cli/lib/version.js +0 -12
- package/cli/protocols-body.md +0 -22
- package/cli/protocols.js +0 -162
- package/cli/registry.js +0 -123
- package/cli/router.js +0 -49
- package/cli/uninstall-actions.js +0 -360
- package/cli/uninstall-workflow.js +0 -146
- package/cli/uninstall.js +0 -42
- package/cli/upgrade-workflow.js +0 -80
- package/cli/upgrade.js +0 -91
- package/cli/utils.js +0 -40
- package/gates/adversarial-review-gate.md +0 -219
- package/gates/coverage-audit-gate.md +0 -182
- package/gates/devex-gate.md +0 -254
- package/gates/edge-case-gate.md +0 -194
- package/gates/karpathy-gate.md +0 -130
- package/gates/security-gate.md +0 -218
- package/gates/tdd-gate.md +0 -182
- package/gates/test-quality-gate.md +0 -59
- package/gates/verification-gate.md +0 -179
- package/hooks/hooks.json +0 -130
- package/hooks/scripts/common.sh +0 -237
- package/hooks/scripts/config-change-guard.sh +0 -94
- package/hooks/scripts/flow-context-watch.sh +0 -94
- package/hooks/scripts/inject-karpathy.sh +0 -53
- package/hooks/scripts/quick-mode-guard.sh +0 -69
- package/hooks/scripts/session-start.sh +0 -94
- package/hooks/scripts/session-title.sh +0 -87
- package/hooks/scripts/stop-watcher.sh +0 -231
- package/hooks/scripts/subagent-artifact-guard.sh +0 -92
- package/hooks/scripts/subagent-statusline.sh +0 -111
- package/hooks/scripts/task-lifecycle-guard.sh +0 -106
- package/hooks/scripts/teammate-idle-guard.sh +0 -83
- package/knowledge/artifact-output-discipline.md +0 -24
- package/knowledge/artifact-summary-contracts.md +0 -50
- package/knowledge/atomic-commits.md +0 -262
- package/knowledge/claude-code-runtime-contracts.md +0 -240
- package/knowledge/epic-decomposition.md +0 -307
- package/knowledge/execution-strategies.md +0 -303
- package/knowledge/karpathy-guidelines.md +0 -219
- package/knowledge/planning-reviews.md +0 -211
- package/knowledge/poc-first-workflow.md +0 -223
- package/knowledge/review-feedback-intake.md +0 -57
- package/knowledge/spec-driven-development.md +0 -180
- package/knowledge/systematic-debugging.md +0 -378
- package/knowledge/two-stage-review.md +0 -249
- package/knowledge/wave-execution.md +0 -403
- package/monitors/monitors.json +0 -8
- package/monitors/scripts/flow-state-monitor.sh +0 -102
- package/output-styles/curdx-evidence-first.md +0 -34
- package/output-styles/curdx-fast-mode.md +0 -42
- package/output-styles/curdx-spec-mode.md +0 -46
- package/schemas/agent-frontmatter.schema.json +0 -66
- package/schemas/config.schema.json +0 -134
- package/schemas/gate-frontmatter.schema.json +0 -30
- package/schemas/hooks.schema.json +0 -115
- package/schemas/output-style-frontmatter.schema.json +0 -22
- package/schemas/plugin-manifest.schema.json +0 -436
- package/schemas/plugin-settings.schema.json +0 -29
- package/schemas/skill-frontmatter.schema.json +0 -177
- package/schemas/spec-frontmatter.schema.json +0 -42
- package/schemas/spec-state.schema.json +0 -165
- package/settings.json +0 -8
- package/skills/brownfield-index/SKILL.md +0 -53
- package/skills/brownfield-index/references/applicability.md +0 -12
- package/skills/brownfield-index/references/handoff.md +0 -8
- package/skills/brownfield-index/references/index-contract.md +0 -10
- package/skills/browser-qa/SKILL.md +0 -39
- package/skills/browser-qa/references/handoff.md +0 -6
- package/skills/browser-qa/references/prerequisites.md +0 -10
- package/skills/browser-qa/references/qa-contract.md +0 -20
- package/skills/cancel/SKILL.md +0 -41
- package/skills/cancel/references/destructive-mode.md +0 -17
- package/skills/cancel/references/reporting.md +0 -18
- package/skills/cancel/references/state-recovery.md +0 -30
- package/skills/cancel/references/target-resolution.md +0 -7
- package/skills/debug/SKILL.md +0 -45
- package/skills/debug/references/context-gathering.md +0 -11
- package/skills/debug/references/failure-guard.md +0 -25
- package/skills/debug/references/intake.md +0 -12
- package/skills/debug/references/phase-workflow.md +0 -34
- package/skills/debug/references/reporting.md +0 -20
- package/skills/epic/SKILL.md +0 -39
- package/skills/epic/references/epic-artifacts.md +0 -20
- package/skills/epic/references/epic-intake.md +0 -9
- package/skills/epic/references/slice-handoff.md +0 -16
- package/skills/fast/SKILL.md +0 -62
- package/skills/fast/references/applicability.md +0 -25
- package/skills/fast/references/clarification.md +0 -20
- package/skills/fast/references/execution-contract.md +0 -56
- package/skills/help/SKILL.md +0 -55
- package/skills/help/references/dispatch.md +0 -20
- package/skills/help/references/overview.md +0 -39
- package/skills/help/references/troubleshoot.md +0 -47
- package/skills/help/references/workflow.md +0 -37
- package/skills/implement/SKILL.md +0 -104
- package/skills/implement/references/error-recovery.md +0 -36
- package/skills/implement/references/linear-execution.md +0 -43
- package/skills/implement/references/native-task-sync.md +0 -107
- package/skills/implement/references/preflight.md +0 -43
- package/skills/implement/references/progress-contract.md +0 -36
- package/skills/implement/references/state-init.md +0 -36
- package/skills/implement/references/stop-hook-execution.md +0 -50
- package/skills/implement/references/strategy-router.md +0 -38
- package/skills/implement/references/subagent-execution.md +0 -57
- package/skills/implement/references/wave-execution.md +0 -180
- package/skills/init/SKILL.md +0 -49
- package/skills/init/references/gitignore-and-health.md +0 -26
- package/skills/init/references/next-steps.md +0 -22
- package/skills/init/references/preflight.md +0 -15
- package/skills/init/references/scaffold-contract.md +0 -27
- package/skills/review/SKILL.md +0 -82
- package/skills/review/references/optional-passes.md +0 -48
- package/skills/review/references/preflight.md +0 -38
- package/skills/review/references/report-contract.md +0 -49
- package/skills/review/references/reporting.md +0 -20
- package/skills/review/references/stage-execution.md +0 -32
- package/skills/security-audit/SKILL.md +0 -47
- package/skills/security-audit/references/audit-contract.md +0 -21
- package/skills/security-audit/references/gate-handoff.md +0 -8
- package/skills/security-audit/references/scope-and-depth.md +0 -9
- package/skills/spec/SKILL.md +0 -100
- package/skills/spec/references/artifact-landing.md +0 -31
- package/skills/spec/references/phase-execution.md +0 -50
- package/skills/spec/references/planning-review.md +0 -31
- package/skills/spec/references/preflight-and-routing.md +0 -46
- package/skills/spec/references/reporting.md +0 -21
- package/skills/start/SKILL.md +0 -84
- package/skills/start/references/branch-routing.md +0 -51
- package/skills/start/references/mode-semantics.md +0 -12
- package/skills/start/references/preflight.md +0 -13
- package/skills/start/references/reporting.md +0 -20
- package/skills/start/references/state-seeding.md +0 -44
- package/skills/start/references/workflow-handoff.md +0 -26
- package/skills/status/SKILL.md +0 -41
- package/skills/status/references/gather-contract.md +0 -30
- package/skills/status/references/health-rules.md +0 -27
- package/skills/status/references/output-contract.md +0 -25
- package/skills/status/references/preflight.md +0 -10
- package/skills/status/references/recovery-hints.md +0 -18
- package/skills/ui-sketch/SKILL.md +0 -39
- package/skills/ui-sketch/references/brief-intake.md +0 -10
- package/skills/ui-sketch/references/iteration-handoff.md +0 -5
- package/skills/ui-sketch/references/variant-contract.md +0 -15
- package/skills/verify/SKILL.md +0 -56
- package/skills/verify/references/evidence-workflow.md +0 -39
- package/skills/verify/references/output-contract.md +0 -23
- package/skills/verify/references/preflight.md +0 -11
- package/skills/verify/references/report-handoff.md +0 -35
- package/skills/verify/references/strict-mode.md +0 -12
- package/templates/CONTEXT.md.tmpl +0 -53
- package/templates/PROJECT.md.tmpl +0 -59
- package/templates/ROADMAP.md.tmpl +0 -50
- package/templates/STATE.md.tmpl +0 -49
- package/templates/config.json.tmpl +0 -51
- package/templates/design.md.tmpl +0 -83
- package/templates/progress.md.tmpl +0 -77
- package/templates/requirements.md.tmpl +0 -76
- package/templates/research.md.tmpl +0 -83
- 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"
|