cc-devflow 2.4.6 → 4.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/.claude/CLAUDE.md +1065 -48
- package/.claude/agents/dev-implementer.md +195 -0
- package/.claude/commands/{flow-archive.md → flow/archive.md} +46 -11
- package/.claude/commands/flow/context.md +150 -0
- package/.claude/commands/flow/delta.md +245 -0
- package/.claude/commands/{flow-dev.md → flow/dev.md} +112 -11
- package/.claude/commands/flow/init.md +45 -0
- package/.claude/commands/flow/quality.md +159 -0
- package/.claude/commands/flow/spec.md +186 -0
- package/.claude/commands/flow/workspace.md +146 -0
- package/.claude/commands/{cancel-ralph.md → util/cancel-ralph.md} +1 -0
- package/.claude/config/quality-gates.yml +305 -0
- package/.claude/docs/guides/TEAM_MODE_GUIDE.md +313 -0
- package/.claude/docs/templates/DELTA_SPEC_TEMPLATE.md +91 -0
- package/.claude/docs/templates/DESIGN_DECISIONS_TEMPLATE.md +151 -0
- package/.claude/docs/templates/JOURNAL_TEMPLATE.md +75 -0
- package/.claude/docs/templates/_shared/CLAUDE.md +36 -0
- package/.claude/docs/templates/_shared/CONSTITUTION_CHECK.md +125 -0
- package/.claude/docs/templates/_shared/VALIDATION_CHECKLIST.md +187 -0
- package/.claude/docs/templates/_shared/YAML_FRONTMATTER.md +164 -0
- package/.claude/docs/templates/context/dev.jsonl.template +6 -0
- package/.claude/docs/templates/context/epic.jsonl.template +5 -0
- package/.claude/docs/templates/context/prd.jsonl.template +4 -0
- package/.claude/docs/templates/context/research.jsonl.template +4 -0
- package/.claude/docs/templates/context/review.jsonl.template +5 -0
- package/.claude/docs/templates/context/tech.jsonl.template +5 -0
- package/.claude/hooks/CLAUDE.md +342 -0
- package/.claude/hooks/inject-agent-context.ts +480 -0
- package/.claude/hooks/inject-skill-context.ts +359 -0
- package/.claude/hooks/ralph-loop.ts +931 -0
- package/.claude/hooks/task-completed-hook.ts +593 -0
- package/.claude/hooks/teammate-idle-hook.ts +690 -0
- package/.claude/hooks/types/team-types.d.ts +238 -0
- package/.claude/rules/devflow-conventions.md +82 -9
- package/.claude/scripts/archive-requirement.sh +44 -1
- package/.claude/scripts/common.sh +670 -3
- package/.claude/scripts/delta-parser.ts +527 -0
- package/.claude/scripts/detect-file-conflicts.sh +151 -0
- package/.claude/scripts/flow-context-add.sh +134 -0
- package/.claude/scripts/flow-context-init.sh +133 -0
- package/.claude/scripts/flow-context-validate.sh +144 -0
- package/.claude/scripts/flow-delta-apply.sh +297 -0
- package/.claude/scripts/flow-delta-archive.sh +71 -0
- package/.claude/scripts/flow-delta-create.sh +202 -0
- package/.claude/scripts/flow-delta-list.sh +142 -0
- package/.claude/scripts/flow-delta-status.sh +235 -0
- package/.claude/scripts/flow-quality-full.sh +184 -0
- package/.claude/scripts/flow-quality-quick.sh +64 -0
- package/.claude/scripts/flow-workspace-init.sh +117 -0
- package/.claude/scripts/flow-workspace-record.sh +164 -0
- package/.claude/scripts/flow-workspace-start.sh +88 -0
- package/.claude/scripts/get-workflow-status.sh +415 -0
- package/.claude/scripts/parse-task-dependencies.js +334 -0
- package/.claude/scripts/record-quality-error.sh +165 -0
- package/.claude/scripts/run-quality-gates.sh +242 -0
- package/.claude/scripts/team-dev-init.sh +319 -0
- package/.claude/scripts/team-state-recovery.sh +229 -0
- package/.claude/scripts/workflow-status.ts +433 -0
- package/.claude/settings.json +19 -0
- package/.claude/skills/cc-devflow-orchestrator/SKILL.md +85 -200
- package/.claude/skills/domain/using-git-worktrees/SKILL.md +252 -0
- package/.claude/skills/domain/using-git-worktrees/assets/SHELL_ALIASES.md +133 -0
- package/.claude/skills/domain/using-git-worktrees/context.jsonl +4 -0
- package/.claude/skills/domain/using-git-worktrees/scripts/worktree-cleanup.sh +218 -0
- package/.claude/skills/domain/using-git-worktrees/scripts/worktree-create.sh +232 -0
- package/.claude/skills/domain/using-git-worktrees/scripts/worktree-list.sh +130 -0
- package/.claude/skills/domain/using-git-worktrees/scripts/worktree-status.sh +140 -0
- package/.claude/skills/domain/using-git-worktrees/scripts/worktree-switch.sh +70 -0
- package/.claude/skills/skill-rules.json +72 -1
- package/.claude/skills/utility/journey-checker/SKILL.md +199 -0
- package/.claude/skills/utility/journey-checker/pressure-scenarios.md +164 -0
- package/.claude/skills/utility/skill-creator/LICENSE.txt +202 -0
- package/.claude/skills/utility/skill-creator/SKILL.md +356 -0
- package/.claude/skills/utility/skill-creator/references/output-patterns.md +82 -0
- package/.claude/skills/utility/skill-creator/references/workflows.md +28 -0
- package/.claude/skills/utility/skill-creator/scripts/init_skill.py +303 -0
- package/.claude/skills/utility/skill-creator/scripts/package_skill.py +110 -0
- package/.claude/skills/utility/skill-creator/scripts/quick_validate.py +95 -0
- package/.claude/skills/workflow/flow-dev/CLAUDE.md +78 -0
- package/.claude/skills/workflow/flow-dev/SKILL.md +96 -0
- package/.claude/skills/workflow/flow-dev/assets/IMPLEMENTATION_PLAN_TEMPLATE.md +71 -0
- package/.claude/skills/workflow/flow-dev/context.jsonl +8 -0
- package/.claude/skills/workflow/flow-dev/dev-implementer.jsonl +8 -0
- package/.claude/skills/workflow/flow-dev/scripts/entry-gate.sh +116 -0
- package/.claude/skills/workflow/flow-dev/scripts/exit-gate.sh +101 -0
- package/.claude/skills/workflow/flow-dev/scripts/task-orchestrator.sh +106 -0
- package/.claude/skills/workflow/flow-fix/SKILL.md +105 -0
- package/.claude/skills/workflow/flow-fix/context.jsonl +6 -0
- package/.claude/skills/workflow/flow-fix/references/bug-analyzer.md +381 -0
- package/.claude/skills/workflow/flow-init/SKILL.md +211 -0
- package/.claude/skills/workflow/flow-init/assets/BRAINSTORM_TEMPLATE.md +148 -0
- package/.claude/skills/workflow/flow-init/assets/INIT_FLOW_TEMPLATE.md +198 -0
- package/.claude/skills/workflow/flow-init/assets/RESEARCH_TEMPLATE.md +276 -0
- package/.claude/skills/workflow/flow-init/context.jsonl +5 -0
- package/.claude/skills/workflow/flow-init/references/flow-researcher.md +132 -0
- package/.claude/skills/workflow/flow-init/scripts/check-prerequisites.sh +232 -0
- package/.claude/skills/workflow/flow-init/scripts/consolidate-research.sh +182 -0
- package/.claude/skills/workflow/flow-init/scripts/create-requirement.sh +515 -0
- package/.claude/skills/workflow/flow-init/scripts/generate-research-tasks.sh +157 -0
- package/.claude/skills/workflow/flow-init/scripts/populate-research-tasks.sh +284 -0
- package/.claude/skills/workflow/flow-init/scripts/validate-research.sh +332 -0
- package/.claude/skills/workflow/flow-quality/SKILL.md +94 -0
- package/.claude/skills/workflow/flow-quality/context.jsonl +6 -0
- package/.claude/skills/workflow/flow-quality/references/code-quality-reviewer.md +205 -0
- package/.claude/skills/workflow/flow-quality/references/qa-tester.md +313 -0
- package/.claude/skills/workflow/flow-quality/references/security-reviewer.md +314 -0
- package/.claude/skills/workflow/flow-quality/references/spec-reviewer.md +221 -0
- package/.claude/skills/workflow/flow-release/SKILL.md +126 -0
- package/.claude/skills/workflow/flow-release/context.jsonl +7 -0
- package/.claude/skills/workflow/flow-release/references/release-manager.md +295 -0
- package/.claude/skills/workflow/flow-spec/CLAUDE.md +103 -0
- package/.claude/skills/workflow/flow-spec/SKILL.md +545 -0
- package/.claude/skills/workflow/flow-spec/context.jsonl +7 -0
- package/.claude/skills/workflow/flow-spec/scripts/entry-gate.sh +194 -0
- package/.claude/skills/workflow/flow-spec/scripts/exit-gate.sh +244 -0
- package/.claude/skills/workflow/flow-spec/scripts/parallel-orchestrator.sh +205 -0
- package/.claude/skills/workflow/flow-spec/scripts/team-communication.sh +353 -0
- package/.claude/skills/workflow/flow-spec/scripts/team-init.sh +195 -0
- package/.claude/skills/workflow/flow-spec/scripts/test-team-mode.sh +496 -0
- package/.claude/skills/workflow/flow-spec/team-config.json +165 -0
- package/.claude/skills/workflow.yaml +417 -0
- package/CHANGELOG.md +254 -0
- package/README.md +193 -33
- package/README.zh-CN.md +206 -46
- package/lib/compiler/CLAUDE.md +77 -46
- package/lib/compiler/__tests__/multi-module-emitters.test.js +508 -0
- package/lib/compiler/context-expander.js +179 -0
- package/lib/compiler/emitters/antigravity-emitter.js +195 -5
- package/lib/compiler/emitters/base-emitter.js +217 -2
- package/lib/compiler/emitters/codex-emitter.js +200 -4
- package/lib/compiler/emitters/cursor-emitter.js +307 -3
- package/lib/compiler/emitters/qwen-emitter.js +196 -4
- package/lib/compiler/index.js +197 -2
- package/lib/compiler/platforms.js +270 -21
- package/package.json +1 -1
- package/.claude/commands/flow-epic.md +0 -183
- package/.claude/commands/flow-init.md +0 -370
- package/.claude/commands/flow-prd.md +0 -144
- package/.claude/commands/flow-qa.md +0 -93
- package/.claude/commands/flow-review.md +0 -257
- package/.claude/commands/flow-tech.md +0 -142
- package/.claude/commands/flow-ui.md +0 -189
- package/.claude/skills/file-header-guardian/SKILL.md +0 -56
- package/.claude/skills/skill-developer/ADVANCED.md +0 -197
- package/.claude/skills/skill-developer/HOOK_MECHANISMS.md +0 -306
- package/.claude/skills/skill-developer/PATTERNS_LIBRARY.md +0 -152
- package/.claude/skills/skill-developer/SKILL.md +0 -426
- package/.claude/skills/skill-developer/SKILL_RULES_REFERENCE.md +0 -315
- package/.claude/skills/skill-developer/TRIGGER_TYPES.md +0 -305
- package/.claude/skills/skill-developer/TROUBLESHOOTING.md +0 -514
- package/.claude/skills/writing-skills/SKILL.md +0 -655
- package/.claude/skills/writing-skills/anthropic-best-practices.md +0 -1150
- package/.claude/skills/writing-skills/examples/CLAUDE_MD_TESTING.md +0 -189
- package/.claude/skills/writing-skills/graphviz-conventions.dot +0 -172
- package/.claude/skills/writing-skills/persuasion-principles.md +0 -187
- package/.claude/skills/writing-skills/render-graphs.js +0 -168
- package/.claude/skills/writing-skills/testing-skills-with-subagents.md +0 -384
- package/.claude/tsc-cache/795ba6e3-b98a-423b-bab2-51aa62812569/affected-repos.txt +0 -1
- package/.claude/tsc-cache/ae335694-be5a-4ba4-a1a0-b676c09a7906/affected-repos.txt +0 -1
- /package/.claude/commands/{core-architecture.md → core/architecture.md} +0 -0
- /package/.claude/commands/{core-guidelines.md → core/guidelines.md} +0 -0
- /package/.claude/commands/{core-roadmap.md → core/roadmap.md} +0 -0
- /package/.claude/commands/{core-style.md → core/style.md} +0 -0
- /package/.claude/commands/{flow-checklist.md → flow/checklist.md} +0 -0
- /package/.claude/commands/{flow-clarify.md → flow/clarify.md} +0 -0
- /package/.claude/commands/{flow-constitution.md → flow/constitution.md} +0 -0
- /package/.claude/commands/{flow-fix.md → flow/fix.md} +0 -0
- /package/.claude/commands/{flow-ideate.md → flow/ideate.md} +0 -0
- /package/.claude/commands/{flow-new.md → flow/new.md} +0 -0
- /package/.claude/commands/{flow-release.md → flow/release.md} +0 -0
- /package/.claude/commands/{flow-restart.md → flow/restart.md} +0 -0
- /package/.claude/commands/{flow-status.md → flow/status.md} +0 -0
- /package/.claude/commands/{flow-update.md → flow/update.md} +0 -0
- /package/.claude/commands/{flow-upgrade.md → flow/upgrade.md} +0 -0
- /package/.claude/commands/{flow-verify.md → flow/verify.md} +0 -0
- /package/.claude/commands/{code-review-high.md → util/code-review.md} +0 -0
- /package/.claude/commands/{git-commit.md → util/git-commit.md} +0 -0
- /package/.claude/commands/{problem-analyzer.md → util/problem-analyzer.md} +0 -0
- /package/.claude/skills/{flow-attention-refresh → domain/attention-refresh}/SKILL.md +0 -0
- /package/.claude/skills/{flow-brainstorming → domain/brainstorming}/SKILL.md +0 -0
- /package/.claude/skills/{flow-debugging → domain/debugging}/SKILL.md +0 -0
- /package/.claude/skills/{flow-finishing-branch → domain/finishing-branch}/SKILL.md +0 -0
- /package/.claude/skills/{flow-receiving-review → domain/receiving-review}/SKILL.md +0 -0
- /package/.claude/skills/{flow-tdd → domain/tdd}/SKILL.md +0 -0
- /package/.claude/skills/{verification-before-completion → domain/verification}/SKILL.md +0 -0
- /package/.claude/skills/{constitution-guardian → guardrail/constitution-guardian}/SKILL.md +0 -0
- /package/.claude/skills/{devflow-tdd-enforcer → guardrail/tdd-enforcer}/SKILL.md +0 -0
- /package/.claude/skills/{devflow-constitution-quick-ref → utility/constitution-quick-ref}/SKILL.md +0 -0
- /package/.claude/skills/{devflow-file-standards → utility/file-standards}/SKILL.md +0 -0
- /package/.claude/skills/{fractal-docs-generator → utility/fractal-docs}/SKILL.md +0 -0
- /package/.claude/skills/{npm-release → utility/npm-release}/SKILL.md +0 -0
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# =============================================================================
|
|
3
|
+
# Team State Recovery Script
|
|
4
|
+
# =============================================================================
|
|
5
|
+
# [INPUT]: 依赖 common.sh 的 Team 函数
|
|
6
|
+
# [OUTPUT]: 恢复 Team 状态,支持断点续传
|
|
7
|
+
# [POS]: flow-dev 的状态恢复器,被 /flow:dev --resume 调用
|
|
8
|
+
# [PROTOCOL]: 变更时更新此头部,然后检查 CLAUDE.md
|
|
9
|
+
# =============================================================================
|
|
10
|
+
|
|
11
|
+
set -euo pipefail
|
|
12
|
+
|
|
13
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
14
|
+
source "$SCRIPT_DIR/common.sh"
|
|
15
|
+
|
|
16
|
+
# =============================================================================
|
|
17
|
+
# State Recovery Functions
|
|
18
|
+
# =============================================================================
|
|
19
|
+
|
|
20
|
+
# Check if Team state can be recovered
|
|
21
|
+
# Args: $1 - repo root, $2 - requirement ID
|
|
22
|
+
can_recover_state() {
|
|
23
|
+
local repo_root="$1"
|
|
24
|
+
local req_id="$2"
|
|
25
|
+
local status_file="$repo_root/devflow/requirements/$req_id/orchestration_status.json"
|
|
26
|
+
|
|
27
|
+
if [[ ! -f "$status_file" ]]; then
|
|
28
|
+
echo "false"
|
|
29
|
+
return 0
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
local team_mode
|
|
33
|
+
team_mode=$(jq -r '.team.mode // "none"' "$status_file")
|
|
34
|
+
|
|
35
|
+
if [[ "$team_mode" == "none" || "$team_mode" == "null" ]]; then
|
|
36
|
+
echo "false"
|
|
37
|
+
return 0
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
echo "true"
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
# Get recovery summary
|
|
44
|
+
# Args: $1 - repo root, $2 - requirement ID
|
|
45
|
+
get_recovery_summary() {
|
|
46
|
+
local repo_root="$1"
|
|
47
|
+
local req_id="$2"
|
|
48
|
+
local status_file="$repo_root/devflow/requirements/$req_id/orchestration_status.json"
|
|
49
|
+
|
|
50
|
+
if [[ ! -f "$status_file" ]]; then
|
|
51
|
+
echo '{"canRecover": false, "reason": "Status file not found"}'
|
|
52
|
+
return 0
|
|
53
|
+
fi
|
|
54
|
+
|
|
55
|
+
local team_data
|
|
56
|
+
team_data=$(jq '{
|
|
57
|
+
canRecover: (.team.mode != null and .team.mode != "none"),
|
|
58
|
+
mode: .team.mode,
|
|
59
|
+
lead: .team.lead,
|
|
60
|
+
teammateCount: (.team.teammates | length),
|
|
61
|
+
completedTasks: [.team.teammates[].completedTasks | length] | add,
|
|
62
|
+
pendingTasks: (.team.taskAssignments | keys | length),
|
|
63
|
+
lastActiveAt: ([.team.teammates[].lastActiveAt] | max),
|
|
64
|
+
ralphLoop: {
|
|
65
|
+
globalIteration: .ralphLoop.globalIteration,
|
|
66
|
+
maxIterations: .ralphLoop.maxIterations
|
|
67
|
+
}
|
|
68
|
+
}' "$status_file")
|
|
69
|
+
|
|
70
|
+
echo "$team_data"
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
# Recover Team state
|
|
74
|
+
# Args: $1 - repo root, $2 - requirement ID
|
|
75
|
+
recover_state() {
|
|
76
|
+
local repo_root="$1"
|
|
77
|
+
local req_id="$2"
|
|
78
|
+
local status_file="$repo_root/devflow/requirements/$req_id/orchestration_status.json"
|
|
79
|
+
|
|
80
|
+
echo "Recovering Team state for $req_id..."
|
|
81
|
+
|
|
82
|
+
# Reset teammate statuses to idle
|
|
83
|
+
local updated
|
|
84
|
+
updated=$(jq '
|
|
85
|
+
.team.teammates = [.team.teammates[] | .status = "idle" | .currentTask = null]
|
|
86
|
+
' "$status_file")
|
|
87
|
+
|
|
88
|
+
echo "$updated" > "$status_file"
|
|
89
|
+
|
|
90
|
+
# Get pending tasks
|
|
91
|
+
local pending_tasks
|
|
92
|
+
pending_tasks=$(get_unassigned_tasks "$repo_root" "$req_id")
|
|
93
|
+
|
|
94
|
+
echo "Recovery complete."
|
|
95
|
+
echo "Pending tasks: $pending_tasks"
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
# Create state snapshot
|
|
99
|
+
# Args: $1 - repo root, $2 - requirement ID
|
|
100
|
+
create_snapshot() {
|
|
101
|
+
local repo_root="$1"
|
|
102
|
+
local req_id="$2"
|
|
103
|
+
local status_file="$repo_root/devflow/requirements/$req_id/orchestration_status.json"
|
|
104
|
+
local snapshot_dir="$repo_root/devflow/requirements/$req_id/.snapshots"
|
|
105
|
+
local timestamp
|
|
106
|
+
timestamp=$(date +%Y%m%d_%H%M%S)
|
|
107
|
+
|
|
108
|
+
mkdir -p "$snapshot_dir"
|
|
109
|
+
|
|
110
|
+
if [[ -f "$status_file" ]]; then
|
|
111
|
+
cp "$status_file" "$snapshot_dir/orchestration_status_$timestamp.json"
|
|
112
|
+
echo "Snapshot created: $snapshot_dir/orchestration_status_$timestamp.json"
|
|
113
|
+
else
|
|
114
|
+
echo "No status file to snapshot"
|
|
115
|
+
fi
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
# Restore from snapshot
|
|
119
|
+
# Args: $1 - repo root, $2 - requirement ID, $3 - snapshot timestamp (optional)
|
|
120
|
+
restore_snapshot() {
|
|
121
|
+
local repo_root="$1"
|
|
122
|
+
local req_id="$2"
|
|
123
|
+
local timestamp="${3:-}"
|
|
124
|
+
local status_file="$repo_root/devflow/requirements/$req_id/orchestration_status.json"
|
|
125
|
+
local snapshot_dir="$repo_root/devflow/requirements/$req_id/.snapshots"
|
|
126
|
+
|
|
127
|
+
if [[ -z "$timestamp" ]]; then
|
|
128
|
+
# Get latest snapshot
|
|
129
|
+
local latest
|
|
130
|
+
latest=$(ls -t "$snapshot_dir"/orchestration_status_*.json 2>/dev/null | head -1)
|
|
131
|
+
if [[ -z "$latest" ]]; then
|
|
132
|
+
echo "No snapshots found"
|
|
133
|
+
return 1
|
|
134
|
+
fi
|
|
135
|
+
cp "$latest" "$status_file"
|
|
136
|
+
echo "Restored from: $latest"
|
|
137
|
+
else
|
|
138
|
+
local snapshot_file="$snapshot_dir/orchestration_status_$timestamp.json"
|
|
139
|
+
if [[ -f "$snapshot_file" ]]; then
|
|
140
|
+
cp "$snapshot_file" "$status_file"
|
|
141
|
+
echo "Restored from: $snapshot_file"
|
|
142
|
+
else
|
|
143
|
+
echo "Snapshot not found: $snapshot_file"
|
|
144
|
+
return 1
|
|
145
|
+
fi
|
|
146
|
+
fi
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
# List available snapshots
|
|
150
|
+
# Args: $1 - repo root, $2 - requirement ID
|
|
151
|
+
list_snapshots() {
|
|
152
|
+
local repo_root="$1"
|
|
153
|
+
local req_id="$2"
|
|
154
|
+
local snapshot_dir="$repo_root/devflow/requirements/$req_id/.snapshots"
|
|
155
|
+
|
|
156
|
+
if [[ ! -d "$snapshot_dir" ]]; then
|
|
157
|
+
echo "[]"
|
|
158
|
+
return 0
|
|
159
|
+
fi
|
|
160
|
+
|
|
161
|
+
local snapshots='[]'
|
|
162
|
+
for file in "$snapshot_dir"/orchestration_status_*.json; do
|
|
163
|
+
if [[ -f "$file" ]]; then
|
|
164
|
+
local timestamp
|
|
165
|
+
timestamp=$(basename "$file" | sed 's/orchestration_status_//' | sed 's/.json//')
|
|
166
|
+
snapshots=$(echo "$snapshots" | jq --arg ts "$timestamp" '. + [$ts]')
|
|
167
|
+
fi
|
|
168
|
+
done
|
|
169
|
+
|
|
170
|
+
echo "$snapshots"
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
# =============================================================================
|
|
174
|
+
# Main
|
|
175
|
+
# =============================================================================
|
|
176
|
+
|
|
177
|
+
main() {
|
|
178
|
+
local action="${1:-help}"
|
|
179
|
+
|
|
180
|
+
case "$action" in
|
|
181
|
+
check)
|
|
182
|
+
local repo_root="${2:-$(get_repo_root)}"
|
|
183
|
+
local req_id="${3:-$(get_current_req_id)}"
|
|
184
|
+
can_recover_state "$repo_root" "$req_id"
|
|
185
|
+
;;
|
|
186
|
+
summary)
|
|
187
|
+
local repo_root="${2:-$(get_repo_root)}"
|
|
188
|
+
local req_id="${3:-$(get_current_req_id)}"
|
|
189
|
+
get_recovery_summary "$repo_root" "$req_id"
|
|
190
|
+
;;
|
|
191
|
+
recover)
|
|
192
|
+
local repo_root="${2:-$(get_repo_root)}"
|
|
193
|
+
local req_id="${3:-$(get_current_req_id)}"
|
|
194
|
+
recover_state "$repo_root" "$req_id"
|
|
195
|
+
;;
|
|
196
|
+
snapshot)
|
|
197
|
+
local repo_root="${2:-$(get_repo_root)}"
|
|
198
|
+
local req_id="${3:-$(get_current_req_id)}"
|
|
199
|
+
create_snapshot "$repo_root" "$req_id"
|
|
200
|
+
;;
|
|
201
|
+
restore)
|
|
202
|
+
local repo_root="${2:-$(get_repo_root)}"
|
|
203
|
+
local req_id="${3:-$(get_current_req_id)}"
|
|
204
|
+
local timestamp="${4:-}"
|
|
205
|
+
restore_snapshot "$repo_root" "$req_id" "$timestamp"
|
|
206
|
+
;;
|
|
207
|
+
list-snapshots)
|
|
208
|
+
local repo_root="${2:-$(get_repo_root)}"
|
|
209
|
+
local req_id="${3:-$(get_current_req_id)}"
|
|
210
|
+
list_snapshots "$repo_root" "$req_id"
|
|
211
|
+
;;
|
|
212
|
+
help|*)
|
|
213
|
+
echo "Usage: $0 <action> [args...]"
|
|
214
|
+
echo ""
|
|
215
|
+
echo "Actions:"
|
|
216
|
+
echo " check [repo_root] [req_id] - Check if state can be recovered"
|
|
217
|
+
echo " summary [repo_root] [req_id] - Get recovery summary"
|
|
218
|
+
echo " recover [repo_root] [req_id] - Recover Team state"
|
|
219
|
+
echo " snapshot [repo_root] [req_id] - Create state snapshot"
|
|
220
|
+
echo " restore [repo_root] [req_id] [timestamp] - Restore from snapshot"
|
|
221
|
+
echo " list-snapshots [repo_root] [req_id] - List available snapshots"
|
|
222
|
+
;;
|
|
223
|
+
esac
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
# Run if executed directly
|
|
227
|
+
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
|
228
|
+
main "$@"
|
|
229
|
+
fi
|
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* [INPUT]: 依赖 fs, path, yaml (js-yaml) 模块
|
|
3
|
+
* [OUTPUT]: 对外提供 WorkflowStatus 类型、isArtifactComplete, getNextActions, getWorkflowStatus 函数
|
|
4
|
+
* [POS]: .claude/scripts 的工作流状态检测工具,被 flow-* 命令和 hooks 消费
|
|
5
|
+
* [PROTOCOL]: 变更时更新此头部,然后检查 CLAUDE.md
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import * as fs from 'fs';
|
|
9
|
+
import * as path from 'path';
|
|
10
|
+
import * as yaml from 'js-yaml';
|
|
11
|
+
import { glob } from 'glob';
|
|
12
|
+
|
|
13
|
+
// =============================================================================
|
|
14
|
+
// 类型定义
|
|
15
|
+
// =============================================================================
|
|
16
|
+
|
|
17
|
+
/** 工件定义 */
|
|
18
|
+
export interface Artifact {
|
|
19
|
+
id: string;
|
|
20
|
+
generates: string;
|
|
21
|
+
requires: string[];
|
|
22
|
+
skill: string;
|
|
23
|
+
optional?: boolean;
|
|
24
|
+
glob?: boolean;
|
|
25
|
+
instruction?: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** 工件状态 */
|
|
29
|
+
export type ArtifactStatus = 'complete' | 'ready' | 'blocked';
|
|
30
|
+
|
|
31
|
+
/** 工件状态详情 */
|
|
32
|
+
export interface ArtifactStatusDetail {
|
|
33
|
+
id: string;
|
|
34
|
+
status: ArtifactStatus;
|
|
35
|
+
generates: string;
|
|
36
|
+
resolvedPath: string;
|
|
37
|
+
skill: string;
|
|
38
|
+
optional: boolean;
|
|
39
|
+
blockedBy?: string[];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** 工作流状态 */
|
|
43
|
+
export interface WorkflowStatus {
|
|
44
|
+
reqId: string;
|
|
45
|
+
complete: ArtifactStatusDetail[];
|
|
46
|
+
ready: ArtifactStatusDetail[];
|
|
47
|
+
optionalPending: ArtifactStatusDetail[];
|
|
48
|
+
blocked: ArtifactStatusDetail[];
|
|
49
|
+
progress: {
|
|
50
|
+
completed: number;
|
|
51
|
+
total: number;
|
|
52
|
+
percent: number;
|
|
53
|
+
};
|
|
54
|
+
nextAction?: {
|
|
55
|
+
artifactId: string;
|
|
56
|
+
skill: string;
|
|
57
|
+
command: string;
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** workflow.yaml 结构 */
|
|
62
|
+
interface WorkflowSchema {
|
|
63
|
+
name: string;
|
|
64
|
+
version: string;
|
|
65
|
+
description: string;
|
|
66
|
+
artifacts: Artifact[];
|
|
67
|
+
detection: {
|
|
68
|
+
method: string;
|
|
69
|
+
fallback: string;
|
|
70
|
+
glob_options?: {
|
|
71
|
+
ignore?: string[];
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
skills: Record<string, Array<{ id: string; path: string; description: string }>>;
|
|
75
|
+
context: {
|
|
76
|
+
global: Array<{ file: string; reason: string }>;
|
|
77
|
+
defaults: Record<string, Array<{ file: string; reason: string; optional?: boolean }>>;
|
|
78
|
+
};
|
|
79
|
+
deprecated: Array<{ id: string; replacement: string; message: string }>;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// =============================================================================
|
|
83
|
+
// 配置
|
|
84
|
+
// =============================================================================
|
|
85
|
+
|
|
86
|
+
const PROJECT_ROOT = path.resolve(__dirname, '../..');
|
|
87
|
+
const WORKFLOW_YAML_PATH = path.join(PROJECT_ROOT, '.claude/skills/workflow.yaml');
|
|
88
|
+
|
|
89
|
+
// =============================================================================
|
|
90
|
+
// 核心函数
|
|
91
|
+
// =============================================================================
|
|
92
|
+
|
|
93
|
+
/** 加载 workflow.yaml */
|
|
94
|
+
export function loadWorkflowSchema(): WorkflowSchema {
|
|
95
|
+
const content = fs.readFileSync(WORKFLOW_YAML_PATH, 'utf-8');
|
|
96
|
+
return yaml.load(content) as WorkflowSchema;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** 替换路径中的占位符 */
|
|
100
|
+
export function resolvePath(pattern: string, reqId: string, bugId?: string): string {
|
|
101
|
+
let resolved = pattern.replace(/\{REQ\}/g, reqId);
|
|
102
|
+
if (bugId) {
|
|
103
|
+
resolved = resolved.replace(/\{BUG\}/g, bugId);
|
|
104
|
+
}
|
|
105
|
+
return resolved;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** 检查文件/目录是否存在(支持 glob) */
|
|
109
|
+
export async function checkExists(pattern: string, isGlob: boolean): Promise<boolean> {
|
|
110
|
+
const fullPath = path.join(PROJECT_ROOT, pattern);
|
|
111
|
+
|
|
112
|
+
if (isGlob) {
|
|
113
|
+
// glob 模式:检查是否有匹配的文件
|
|
114
|
+
const matches = await glob(fullPath, { nodir: false });
|
|
115
|
+
return matches.length > 0;
|
|
116
|
+
} else {
|
|
117
|
+
// 精确路径:检查文件或目录存在
|
|
118
|
+
return fs.existsSync(fullPath);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** 同步版本的检查存在性 */
|
|
123
|
+
export function checkExistsSync(pattern: string, isGlob: boolean): boolean {
|
|
124
|
+
const fullPath = path.join(PROJECT_ROOT, pattern);
|
|
125
|
+
|
|
126
|
+
if (isGlob) {
|
|
127
|
+
// 简化的 glob 检查:使用 fs.readdirSync
|
|
128
|
+
const dir = path.dirname(fullPath);
|
|
129
|
+
const filePattern = path.basename(fullPath);
|
|
130
|
+
|
|
131
|
+
if (!fs.existsSync(dir)) return false;
|
|
132
|
+
|
|
133
|
+
try {
|
|
134
|
+
const files = fs.readdirSync(dir);
|
|
135
|
+
// 简单的通配符匹配
|
|
136
|
+
const regex = new RegExp('^' + filePattern.replace(/\*/g, '.*') + '$');
|
|
137
|
+
return files.some(f => regex.test(f));
|
|
138
|
+
} catch {
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
} else {
|
|
142
|
+
return fs.existsSync(fullPath);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** 检查工件是否完成 */
|
|
147
|
+
export function isArtifactComplete(artifact: Artifact, reqId: string): boolean {
|
|
148
|
+
const resolvedPath = resolvePath(artifact.generates, reqId);
|
|
149
|
+
return checkExistsSync(resolvedPath, artifact.glob ?? false);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** 获取工件的依赖状态 */
|
|
153
|
+
export function getBlockingDependencies(
|
|
154
|
+
artifact: Artifact,
|
|
155
|
+
reqId: string,
|
|
156
|
+
schema: WorkflowSchema
|
|
157
|
+
): string[] {
|
|
158
|
+
const blocking: string[] = [];
|
|
159
|
+
|
|
160
|
+
for (const depId of artifact.requires) {
|
|
161
|
+
const depArtifact = schema.artifacts.find(a => a.id === depId);
|
|
162
|
+
if (!depArtifact) continue;
|
|
163
|
+
|
|
164
|
+
if (!isArtifactComplete(depArtifact, reqId)) {
|
|
165
|
+
blocking.push(depId);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return blocking;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** 获取工件状态 */
|
|
173
|
+
export function getArtifactStatus(
|
|
174
|
+
artifact: Artifact,
|
|
175
|
+
reqId: string,
|
|
176
|
+
schema: WorkflowSchema
|
|
177
|
+
): ArtifactStatus {
|
|
178
|
+
if (isArtifactComplete(artifact, reqId)) {
|
|
179
|
+
return 'complete';
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const blocking = getBlockingDependencies(artifact, reqId, schema);
|
|
183
|
+
if (blocking.length === 0) {
|
|
184
|
+
return 'ready';
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return 'blocked';
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/** 获取下一步可执行的工件 */
|
|
191
|
+
export function getNextActions(reqId: string): Artifact[] {
|
|
192
|
+
const schema = loadWorkflowSchema();
|
|
193
|
+
const ready: Artifact[] = [];
|
|
194
|
+
|
|
195
|
+
// 筛选需求级工件
|
|
196
|
+
const reqArtifacts = schema.artifacts.filter(a => a.generates.includes('{REQ}'));
|
|
197
|
+
|
|
198
|
+
for (const artifact of reqArtifacts) {
|
|
199
|
+
const status = getArtifactStatus(artifact, reqId, schema);
|
|
200
|
+
|
|
201
|
+
// 只返回非可选的 ready 工件
|
|
202
|
+
if (status === 'ready' && !artifact.optional) {
|
|
203
|
+
ready.push(artifact);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return ready;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/** 获取完整的工作流状态 */
|
|
211
|
+
export function getWorkflowStatus(reqId: string): WorkflowStatus {
|
|
212
|
+
const schema = loadWorkflowSchema();
|
|
213
|
+
|
|
214
|
+
const complete: ArtifactStatusDetail[] = [];
|
|
215
|
+
const ready: ArtifactStatusDetail[] = [];
|
|
216
|
+
const optionalPending: ArtifactStatusDetail[] = [];
|
|
217
|
+
const blocked: ArtifactStatusDetail[] = [];
|
|
218
|
+
|
|
219
|
+
// 筛选需求级工件
|
|
220
|
+
const reqArtifacts = schema.artifacts.filter(a => a.generates.includes('{REQ}'));
|
|
221
|
+
|
|
222
|
+
for (const artifact of reqArtifacts) {
|
|
223
|
+
const status = getArtifactStatus(artifact, reqId, schema);
|
|
224
|
+
const resolvedPath = resolvePath(artifact.generates, reqId);
|
|
225
|
+
|
|
226
|
+
const detail: ArtifactStatusDetail = {
|
|
227
|
+
id: artifact.id,
|
|
228
|
+
status,
|
|
229
|
+
generates: artifact.generates,
|
|
230
|
+
resolvedPath,
|
|
231
|
+
skill: artifact.skill,
|
|
232
|
+
optional: artifact.optional ?? false,
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
if (status === 'blocked') {
|
|
236
|
+
detail.blockedBy = getBlockingDependencies(artifact, reqId, schema);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
switch (status) {
|
|
240
|
+
case 'complete':
|
|
241
|
+
complete.push(detail);
|
|
242
|
+
break;
|
|
243
|
+
case 'ready':
|
|
244
|
+
if (artifact.optional) {
|
|
245
|
+
optionalPending.push(detail);
|
|
246
|
+
} else {
|
|
247
|
+
ready.push(detail);
|
|
248
|
+
}
|
|
249
|
+
break;
|
|
250
|
+
case 'blocked':
|
|
251
|
+
blocked.push(detail);
|
|
252
|
+
break;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// 计算进度
|
|
257
|
+
const total = complete.length + ready.length + blocked.length;
|
|
258
|
+
const percent = total > 0 ? Math.round((complete.length / total) * 100) : 0;
|
|
259
|
+
|
|
260
|
+
// 确定下一步
|
|
261
|
+
let nextAction: WorkflowStatus['nextAction'];
|
|
262
|
+
if (ready.length > 0) {
|
|
263
|
+
const next = ready[0];
|
|
264
|
+
nextAction = {
|
|
265
|
+
artifactId: next.id,
|
|
266
|
+
skill: next.skill,
|
|
267
|
+
command: `/${next.skill} "${reqId}"`,
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
return {
|
|
272
|
+
reqId,
|
|
273
|
+
complete,
|
|
274
|
+
ready,
|
|
275
|
+
optionalPending,
|
|
276
|
+
blocked,
|
|
277
|
+
progress: {
|
|
278
|
+
completed: complete.length,
|
|
279
|
+
total,
|
|
280
|
+
percent,
|
|
281
|
+
},
|
|
282
|
+
nextAction,
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/** 获取项目级工件状态 */
|
|
287
|
+
export function getProjectStatus(): {
|
|
288
|
+
complete: ArtifactStatusDetail[];
|
|
289
|
+
missing: ArtifactStatusDetail[];
|
|
290
|
+
} {
|
|
291
|
+
const schema = loadWorkflowSchema();
|
|
292
|
+
|
|
293
|
+
const complete: ArtifactStatusDetail[] = [];
|
|
294
|
+
const missing: ArtifactStatusDetail[] = [];
|
|
295
|
+
|
|
296
|
+
// 筛选项目级工件(不包含 {REQ} 或 {BUG})
|
|
297
|
+
const projectArtifacts = schema.artifacts.filter(
|
|
298
|
+
a => !a.generates.includes('{REQ}') && !a.generates.includes('{BUG}')
|
|
299
|
+
);
|
|
300
|
+
|
|
301
|
+
for (const artifact of projectArtifacts) {
|
|
302
|
+
const exists = checkExistsSync(artifact.generates, artifact.glob ?? false);
|
|
303
|
+
|
|
304
|
+
const detail: ArtifactStatusDetail = {
|
|
305
|
+
id: artifact.id,
|
|
306
|
+
status: exists ? 'complete' : 'ready',
|
|
307
|
+
generates: artifact.generates,
|
|
308
|
+
resolvedPath: artifact.generates,
|
|
309
|
+
skill: artifact.skill,
|
|
310
|
+
optional: artifact.optional ?? false,
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
if (exists) {
|
|
314
|
+
complete.push(detail);
|
|
315
|
+
} else {
|
|
316
|
+
missing.push(detail);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
return { complete, missing };
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/** 格式化输出工作流状态 */
|
|
324
|
+
export function formatWorkflowStatus(status: WorkflowStatus): string {
|
|
325
|
+
const lines: string[] = [];
|
|
326
|
+
|
|
327
|
+
lines.push(`\n=== 需求 ${status.reqId} 工作流状态 ===\n`);
|
|
328
|
+
|
|
329
|
+
if (status.complete.length > 0) {
|
|
330
|
+
lines.push(`已完成 (${status.complete.length}):`);
|
|
331
|
+
for (const a of status.complete) {
|
|
332
|
+
lines.push(` ✓ ${a.id} → ${a.resolvedPath}`);
|
|
333
|
+
}
|
|
334
|
+
lines.push('');
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
if (status.ready.length > 0) {
|
|
338
|
+
lines.push(`可执行 (${status.ready.length}):`);
|
|
339
|
+
for (const a of status.ready) {
|
|
340
|
+
lines.push(` → ${a.id} (skill: ${a.skill})`);
|
|
341
|
+
}
|
|
342
|
+
lines.push('');
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
if (status.optionalPending.length > 0) {
|
|
346
|
+
lines.push(`可选工件 (${status.optionalPending.length}):`);
|
|
347
|
+
for (const a of status.optionalPending) {
|
|
348
|
+
lines.push(` ○ ${a.id} (skill: ${a.skill})`);
|
|
349
|
+
}
|
|
350
|
+
lines.push('');
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
if (status.blocked.length > 0) {
|
|
354
|
+
lines.push(`阻塞中 (${status.blocked.length}):`);
|
|
355
|
+
for (const a of status.blocked) {
|
|
356
|
+
lines.push(` ✗ ${a.id} (等待: ${a.blockedBy?.join(', ')})`);
|
|
357
|
+
}
|
|
358
|
+
lines.push('');
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
lines.push(`进度: ${status.progress.completed}/${status.progress.total} (${status.progress.percent}%)`);
|
|
362
|
+
|
|
363
|
+
if (status.nextAction) {
|
|
364
|
+
lines.push('');
|
|
365
|
+
lines.push(`建议下一步: ${status.nextAction.command}`);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
return lines.join('\n');
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// =============================================================================
|
|
372
|
+
// CLI 入口
|
|
373
|
+
// =============================================================================
|
|
374
|
+
|
|
375
|
+
if (require.main === module) {
|
|
376
|
+
const args = process.argv.slice(2);
|
|
377
|
+
|
|
378
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
379
|
+
console.log(`
|
|
380
|
+
用法: npx ts-node workflow-status.ts [REQ-ID] [选项]
|
|
381
|
+
|
|
382
|
+
选项:
|
|
383
|
+
--json JSON 格式输出
|
|
384
|
+
--next 仅输出下一步可执行的工件
|
|
385
|
+
--project 检查项目级工件状态
|
|
386
|
+
-h, --help 显示帮助
|
|
387
|
+
`);
|
|
388
|
+
process.exit(0);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
const isJson = args.includes('--json');
|
|
392
|
+
const isNext = args.includes('--next');
|
|
393
|
+
const isProject = args.includes('--project');
|
|
394
|
+
const reqId = args.find(a => a.startsWith('REQ-'));
|
|
395
|
+
|
|
396
|
+
if (isProject) {
|
|
397
|
+
const status = getProjectStatus();
|
|
398
|
+
if (isJson) {
|
|
399
|
+
console.log(JSON.stringify(status, null, 2));
|
|
400
|
+
} else {
|
|
401
|
+
console.log('\n=== 项目级工件状态 ===\n');
|
|
402
|
+
if (status.complete.length > 0) {
|
|
403
|
+
console.log(`已完成 (${status.complete.length}):`);
|
|
404
|
+
for (const a of status.complete) {
|
|
405
|
+
console.log(` ✓ ${a.id} → ${a.generates}`);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
if (status.missing.length > 0) {
|
|
409
|
+
console.log(`\n缺失 (${status.missing.length}):`);
|
|
410
|
+
for (const a of status.missing) {
|
|
411
|
+
console.log(` ○ ${a.id} → ${a.generates} (skill: ${a.skill})`);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
} else if (reqId) {
|
|
416
|
+
if (isNext) {
|
|
417
|
+
const actions = getNextActions(reqId);
|
|
418
|
+
for (const a of actions) {
|
|
419
|
+
console.log(`${a.id}:${a.skill}`);
|
|
420
|
+
}
|
|
421
|
+
} else {
|
|
422
|
+
const status = getWorkflowStatus(reqId);
|
|
423
|
+
if (isJson) {
|
|
424
|
+
console.log(JSON.stringify(status, null, 2));
|
|
425
|
+
} else {
|
|
426
|
+
console.log(formatWorkflowStatus(status));
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
} else {
|
|
430
|
+
console.error('错误: 需要指定 REQ-ID 或使用 --project');
|
|
431
|
+
process.exit(1);
|
|
432
|
+
}
|
|
433
|
+
}
|
package/.claude/settings.json
CHANGED
|
@@ -60,6 +60,15 @@
|
|
|
60
60
|
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/pre-tool-use-guardrail.sh"
|
|
61
61
|
}
|
|
62
62
|
]
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"matcher": "Task",
|
|
66
|
+
"hooks": [
|
|
67
|
+
{
|
|
68
|
+
"type": "command",
|
|
69
|
+
"command": "npx ts-node $CLAUDE_PROJECT_DIR/.claude/hooks/inject-agent-context.ts"
|
|
70
|
+
}
|
|
71
|
+
]
|
|
63
72
|
}
|
|
64
73
|
],
|
|
65
74
|
"Stop": [
|
|
@@ -71,6 +80,16 @@
|
|
|
71
80
|
}
|
|
72
81
|
]
|
|
73
82
|
}
|
|
83
|
+
],
|
|
84
|
+
"SubagentStop": [
|
|
85
|
+
{
|
|
86
|
+
"hooks": [
|
|
87
|
+
{
|
|
88
|
+
"type": "command",
|
|
89
|
+
"command": "npx ts-node $CLAUDE_PROJECT_DIR/.claude/hooks/ralph-loop.ts"
|
|
90
|
+
}
|
|
91
|
+
]
|
|
92
|
+
}
|
|
74
93
|
]
|
|
75
94
|
}
|
|
76
95
|
}
|