specline 2.0.0 → 2.0.2
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/core/agents/specline-spec-creator.yaml +16 -0
- package/core/agents/specline-spec-reviewer.yaml +14 -2
- package/core/skills/specline-pipeline/SKILL.md +63 -9
- package/lib/merge.mjs +7 -3
- package/package.json +1 -1
- package/templates/.cursor/README.md +0 -18
- package/templates/.cursor/agents/specline-backend-dev.md +0 -47
- package/templates/.cursor/agents/specline-code-reviewer.md +0 -110
- package/templates/.cursor/agents/specline-config-dev.md +0 -52
- package/templates/.cursor/agents/specline-config-reviewer.md +0 -79
- package/templates/.cursor/agents/specline-explore-assistant.md +0 -81
- package/templates/.cursor/agents/specline-frontend-dev.md +0 -47
- package/templates/.cursor/agents/specline-spec-creator.md +0 -376
- package/templates/.cursor/agents/specline-spec-reviewer.md +0 -144
- package/templates/.cursor/agents/specline-test-runner.md +0 -107
- package/templates/.cursor/agents/specline-test-writer.md +0 -170
- package/templates/.cursor/hooks/specline-agent-guard.sh +0 -131
- package/templates/.cursor/hooks/specline-auto-format.sh +0 -12
- package/templates/.cursor/hooks/specline-phase-guard.sh +0 -201
- package/templates/.cursor/hooks/specline-pipeline-gate-checks/a1-covers-ref.sh +0 -125
- package/templates/.cursor/hooks/specline-pipeline-gate-checks/a2-a3-reverse.sh +0 -171
- package/templates/.cursor/hooks/specline-pipeline-gate-checks/c1-exception.sh +0 -71
- package/templates/.cursor/hooks/specline-pipeline-gate-checks/c2-vague.sh +0 -60
- package/templates/.cursor/hooks/specline-pipeline-gate-checks/common.sh +0 -68
- package/templates/.cursor/hooks/specline-pipeline-gate-checks/d1-cycle.sh +0 -149
- package/templates/.cursor/hooks/specline-pipeline-gate-checks/d3-type-file.sh +0 -260
- package/templates/.cursor/hooks/specline-pipeline-gate.sh +0 -1569
- package/templates/.cursor/hooks/specline-reminder.sh +0 -147
- package/templates/.cursor/hooks/specline-session-start.sh +0 -259
- package/templates/.cursor/hooks/specline-shell-guard.sh +0 -18
- package/templates/.cursor/hooks.json +0 -46
- package/templates/.cursor/skills/specline-apply-change/SKILL.md +0 -197
- package/templates/.cursor/skills/specline-archive-change/SKILL.md +0 -173
- package/templates/.cursor/skills/specline-explore/SKILL.md +0 -504
- package/templates/.cursor/skills/specline-knowledge/SKILL.md +0 -539
- package/templates/.cursor/skills/specline-pipeline/SKILL.md +0 -616
- package/templates/.cursor/skills/specline-pipeline/references/error-recovery-details.md +0 -49
- package/templates/.cursor/skills/specline-pipeline/references/event-log-spec.md +0 -59
- package/templates/.cursor/skills/specline-pipeline/references/pipeline-state-schema.md +0 -87
- package/templates/.cursor/skills/specline-pipeline/templates/subagent-prompts.md +0 -253
- package/templates/.cursor/skills/specline-propose/SKILL.md +0 -186
- package/templates/.cursor/skills/specline-quickfix/SKILL.md +0 -265
- package/templates/specline/config.yaml +0 -64
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# specline-reminder.sh — postToolUse Hook
|
|
3
|
-
# 关键操作后注入下一步提醒到 Agent 上下文
|
|
4
|
-
#
|
|
5
|
-
# Input (stdin JSON):
|
|
6
|
-
# { "tool_name": "Task"|"Write"|..., "tool_input": {...}, "tool_output": "...", ... }
|
|
7
|
-
#
|
|
8
|
-
# Output (stdout JSON):
|
|
9
|
-
# { "additional_context": "<提醒文本>" } 或 {}
|
|
10
|
-
|
|
11
|
-
set -euo pipefail
|
|
12
|
-
|
|
13
|
-
input=$(cat)
|
|
14
|
-
tool_name=$(echo "$input" | jq -r '.tool_name // empty')
|
|
15
|
-
tool_input=$(echo "$input" | jq -r '.tool_input // "{}"')
|
|
16
|
-
SESSION_ID=$(echo "$input" | jq -r '.session_id // empty')
|
|
17
|
-
|
|
18
|
-
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
19
|
-
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
20
|
-
CHANGES_DIR="$PROJECT_ROOT/specline/changes"
|
|
21
|
-
|
|
22
|
-
# ===== 辅助函数 =====
|
|
23
|
-
|
|
24
|
-
resolve_pipeline_for_session() {
|
|
25
|
-
local session_id="$1"
|
|
26
|
-
local bindings_file="$PROJECT_ROOT/specline/.pipeline-sessions.json"
|
|
27
|
-
|
|
28
|
-
if [ ! -f "$bindings_file" ]; then
|
|
29
|
-
return 1
|
|
30
|
-
fi
|
|
31
|
-
|
|
32
|
-
local change_name
|
|
33
|
-
change_name=$(jq -r --arg sid "$session_id" '.[$sid].change // empty' "$bindings_file" 2>/dev/null)
|
|
34
|
-
|
|
35
|
-
if [ -z "$change_name" ]; then
|
|
36
|
-
return 1
|
|
37
|
-
fi
|
|
38
|
-
|
|
39
|
-
local state_file="$CHANGES_DIR/$change_name/.pipeline-state.json"
|
|
40
|
-
|
|
41
|
-
if [ ! -f "$state_file" ]; then
|
|
42
|
-
# 脏数据:绑定存在但 pipeline 已不存在,清理绑定
|
|
43
|
-
jq --arg sid "$session_id" 'del(.[$sid])' "$bindings_file" > "$bindings_file.tmp" && mv "$bindings_file.tmp" "$bindings_file"
|
|
44
|
-
return 1
|
|
45
|
-
fi
|
|
46
|
-
|
|
47
|
-
STATE_FILE="$state_file"
|
|
48
|
-
return 0
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
STATE_FILE=""
|
|
52
|
-
if ! resolve_pipeline_for_session "$SESSION_ID"; then
|
|
53
|
-
echo '{}'
|
|
54
|
-
exit 0
|
|
55
|
-
fi
|
|
56
|
-
|
|
57
|
-
phase=$(jq -r '.current_phase' "$STATE_FILE")
|
|
58
|
-
change=$(jq -r '.change_name' "$STATE_FILE")
|
|
59
|
-
|
|
60
|
-
# ---- Task (子Agent) 完成后 ----
|
|
61
|
-
if [ "$tool_name" = "Task" ]; then
|
|
62
|
-
subagent_type=$(echo "$tool_input" | jq -r '.subagent_type // ""')
|
|
63
|
-
|
|
64
|
-
if [ "$phase" = "coding" ] && echo "$subagent_type" | grep -qE "^specline-"; then
|
|
65
|
-
# 检查是否还有同批次未完成的任务
|
|
66
|
-
pending_in_batch="?"
|
|
67
|
-
pending_tasks=$(jq -r '[.phases.coding.tasks[]? | select(.status == "pending")] | length' "$STATE_FILE" 2>/dev/null || echo "0")
|
|
68
|
-
|
|
69
|
-
reminder="🔔 **[Pipeline] 子 Agent ($subagent_type) 已完成**\n"
|
|
70
|
-
reminder+="- 变更: $change | 阶段: CODING\n"
|
|
71
|
-
|
|
72
|
-
if [ "$pending_tasks" != "0" ] && [ "$pending_tasks" != "?" ]; then
|
|
73
|
-
reminder+="- 还有 ${pending_tasks} 个任务未完成\n"
|
|
74
|
-
fi
|
|
75
|
-
|
|
76
|
-
reminder+="- ⚠️ 请更新 tasks.md 中对应任务的 checkbox:[ ] → [x]\n"
|
|
77
|
-
reminder+="- ⚠️ 当前批次全部完成后,运行 Build Gate:\n"
|
|
78
|
-
reminder+=" \`.cursor/hooks/specline-pipeline-gate.sh build --change \"$change\"\`"
|
|
79
|
-
|
|
80
|
-
escaped=$(echo -e "$reminder" | jq -Rs '.')
|
|
81
|
-
cat << EOF
|
|
82
|
-
{
|
|
83
|
-
"additional_context": ${escaped}
|
|
84
|
-
}
|
|
85
|
-
EOF
|
|
86
|
-
exit 0
|
|
87
|
-
fi
|
|
88
|
-
|
|
89
|
-
if [ "$phase" = "test" ] && echo "$subagent_type" | grep -qE "^specline-"; then
|
|
90
|
-
reminder="🔔 **[Pipeline] 测试修复 Agent ($subagent_type) 已完成**\n"
|
|
91
|
-
reminder+="- 变更: $change | 阶段: TEST\n"
|
|
92
|
-
reminder+="- ⚠️ 请重新运行对应的测试 Gate:\n"
|
|
93
|
-
reminder+=" \`.cursor/hooks/specline-pipeline-gate.sh test-unit --change \"$change\"\`"
|
|
94
|
-
|
|
95
|
-
escaped=$(echo -e "$reminder" | jq -Rs '.')
|
|
96
|
-
cat << EOF
|
|
97
|
-
{
|
|
98
|
-
"additional_context": ${escaped}
|
|
99
|
-
}
|
|
100
|
-
EOF
|
|
101
|
-
exit 0
|
|
102
|
-
fi
|
|
103
|
-
fi
|
|
104
|
-
|
|
105
|
-
# ---- Write / StrReplace 完成后 ----
|
|
106
|
-
if [ "$tool_name" = "Write" ] || [ "$tool_name" = "StrReplace" ]; then
|
|
107
|
-
file_path=$(echo "$tool_input" | jq -r '.path // .file_path // empty')
|
|
108
|
-
|
|
109
|
-
if [ "$phase" = "coding" ] && [ -n "$file_path" ]; then
|
|
110
|
-
# 编辑 tasks.md → 不提醒(这是正确的行为)
|
|
111
|
-
if echo "$file_path" | grep -q "tasks.md"; then
|
|
112
|
-
echo '{}'
|
|
113
|
-
exit 0
|
|
114
|
-
fi
|
|
115
|
-
|
|
116
|
-
# 编辑代码文件 → 提醒(Orchestrator 不应该直接编辑)
|
|
117
|
-
if echo "$file_path" | grep -qE '\.(ts|tsx|js|jsx|py|go|rs)$'; then
|
|
118
|
-
reminder="🔔 **[Pipeline] 检测到直接编辑代码文件**\n"
|
|
119
|
-
reminder+="- 文件: $(basename "$file_path")\n"
|
|
120
|
-
reminder+="- 编码应通过子 Agent 完成,而非直接编辑\n"
|
|
121
|
-
reminder+="- 如果这是 Orchestrator 的错误行为,请改用 Task 工具启动对应子 Agent"
|
|
122
|
-
|
|
123
|
-
escaped=$(echo -e "$reminder" | jq -Rs '.')
|
|
124
|
-
cat << EOF
|
|
125
|
-
{
|
|
126
|
-
"additional_context": ${escaped}
|
|
127
|
-
}
|
|
128
|
-
EOF
|
|
129
|
-
exit 0
|
|
130
|
-
fi
|
|
131
|
-
fi
|
|
132
|
-
fi
|
|
133
|
-
|
|
134
|
-
# ---- Shell 执行后检查 Gate ----
|
|
135
|
-
if [ "$tool_name" = "Shell" ]; then
|
|
136
|
-
command=$(echo "$tool_input" | jq -r '.command // empty')
|
|
137
|
-
|
|
138
|
-
# 如果是执行 gate 脚本 → 不提醒
|
|
139
|
-
if echo "$command" | grep -q "specline-pipeline-gate.sh"; then
|
|
140
|
-
echo '{}'
|
|
141
|
-
exit 0
|
|
142
|
-
fi
|
|
143
|
-
fi
|
|
144
|
-
|
|
145
|
-
# 默认:无提醒
|
|
146
|
-
echo '{}'
|
|
147
|
-
exit 0
|
|
@@ -1,259 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# specline-session-start.sh — sessionStart Hook (Pipeline Session Binding)
|
|
3
|
-
#
|
|
4
|
-
# 新会话启动时:
|
|
5
|
-
# 1. 清理过期绑定(7 天未更新)
|
|
6
|
-
# 2. 检查当前 session 是否已有绑定
|
|
7
|
-
# 3. 有绑定且 pipeline 仍活跃 → 使用已有绑定,注入上下文
|
|
8
|
-
# 4. 有绑定但 pipeline 已失效 → 清理脏数据,重新扫描
|
|
9
|
-
# 5. 无绑定 → 透明放行(echo '{}')——不自动绑定,避免跨窗口污染
|
|
10
|
-
#
|
|
11
|
-
# Input (stdin JSON):
|
|
12
|
-
# { "session_id": "...", "is_background_agent": bool, ... }
|
|
13
|
-
#
|
|
14
|
-
# Output (stdout JSON):
|
|
15
|
-
# { "additional_context": "<pipeline 上下文>" } 或 {}(无活跃 pipeline)
|
|
16
|
-
|
|
17
|
-
set -euo pipefail
|
|
18
|
-
|
|
19
|
-
# ============================================================================
|
|
20
|
-
# Input
|
|
21
|
-
# ============================================================================
|
|
22
|
-
|
|
23
|
-
input=$(cat)
|
|
24
|
-
|
|
25
|
-
# 跳过 background agent(子 Agent 不需要 pipeline 上下文)
|
|
26
|
-
is_bg=$(echo "$input" | jq -r '.is_background_agent // false')
|
|
27
|
-
if [ "$is_bg" = "true" ]; then
|
|
28
|
-
echo '{}'
|
|
29
|
-
exit 0
|
|
30
|
-
fi
|
|
31
|
-
|
|
32
|
-
session_id=$(echo "$input" | jq -r '.session_id // empty')
|
|
33
|
-
if [ -z "$session_id" ]; then
|
|
34
|
-
echo '{}'
|
|
35
|
-
exit 0
|
|
36
|
-
fi
|
|
37
|
-
|
|
38
|
-
# ============================================================================
|
|
39
|
-
# Paths
|
|
40
|
-
# ============================================================================
|
|
41
|
-
|
|
42
|
-
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
43
|
-
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
44
|
-
BINDINGS_FILE="$PROJECT_ROOT/specline/.pipeline-sessions.json"
|
|
45
|
-
CHANGES_DIR="$PROJECT_ROOT/specline/changes"
|
|
46
|
-
|
|
47
|
-
# ============================================================================
|
|
48
|
-
# Helper Functions
|
|
49
|
-
# ============================================================================
|
|
50
|
-
|
|
51
|
-
# init_bindings_file — ensure bindings file exists with a valid JSON object
|
|
52
|
-
init_bindings_file() {
|
|
53
|
-
if [ ! -f "$BINDINGS_FILE" ]; then
|
|
54
|
-
mkdir -p "$(dirname "$BINDINGS_FILE")"
|
|
55
|
-
echo '{}' > "$BINDINGS_FILE"
|
|
56
|
-
fi
|
|
57
|
-
|
|
58
|
-
# Validate it's readable JSON; reset if corrupted
|
|
59
|
-
if ! jq empty "$BINDINGS_FILE" 2>/dev/null; then
|
|
60
|
-
echo '{}' > "$BINDINGS_FILE"
|
|
61
|
-
fi
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
# clean_expired_bindings — remove bindings where bound_at > 7 days ago
|
|
65
|
-
# Uses ISO-8601 lexical comparison: works on both macOS and Linux
|
|
66
|
-
clean_expired_bindings() {
|
|
67
|
-
[ -f "$BINDINGS_FILE" ] || return 0
|
|
68
|
-
|
|
69
|
-
local cutoff_date
|
|
70
|
-
# macOS date, fallback to Linux date
|
|
71
|
-
cutoff_date=$(date -u -v-7d +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || \
|
|
72
|
-
date -u -d '7 days ago' +%Y-%m-%dT%H:%M:%SZ 2>/dev/null)
|
|
73
|
-
|
|
74
|
-
if [ -z "$cutoff_date" ]; then
|
|
75
|
-
return 0 # Cannot determine cutoff, skip cleanup
|
|
76
|
-
fi
|
|
77
|
-
|
|
78
|
-
local tmp_file="${BINDINGS_FILE}.tmp"
|
|
79
|
-
# Keep only entries whose bound_at is >= cutoff (i.e. within the last 7 days)
|
|
80
|
-
jq --arg cutoff "$cutoff_date" \
|
|
81
|
-
'with_entries(select(.value.bound_at >= $cutoff))' \
|
|
82
|
-
"$BINDINGS_FILE" > "$tmp_file" 2>/dev/null && \
|
|
83
|
-
mv "$tmp_file" "$BINDINGS_FILE" 2>/dev/null || true
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
# is_pipeline_active — check if a pipeline's state file exists and is not archived
|
|
87
|
-
# Returns 0 (active) or 1 (inactive)
|
|
88
|
-
is_pipeline_active() {
|
|
89
|
-
local change_name="$1"
|
|
90
|
-
local state_file="$CHANGES_DIR/$change_name/.pipeline-state.json"
|
|
91
|
-
|
|
92
|
-
[ -f "$state_file" ] || return 1
|
|
93
|
-
|
|
94
|
-
local phase
|
|
95
|
-
phase=$(jq -r '.current_phase // "unknown"' "$state_file" 2>/dev/null)
|
|
96
|
-
[ "$phase" != "archive" ] || return 1
|
|
97
|
-
|
|
98
|
-
return 0
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
# scan_active_pipelines — scan changes/ (excluding archive/) for active pipelines
|
|
102
|
-
# Output: one line per pipeline: change_name|phase|state_file_path
|
|
103
|
-
scan_active_pipelines() {
|
|
104
|
-
if [ ! -d "$CHANGES_DIR" ]; then
|
|
105
|
-
return 0
|
|
106
|
-
fi
|
|
107
|
-
|
|
108
|
-
for state_file in "$CHANGES_DIR"/*/.pipeline-state.json; do
|
|
109
|
-
[ -f "$state_file" ] || continue
|
|
110
|
-
|
|
111
|
-
# Exclude archive/
|
|
112
|
-
case "$state_file" in
|
|
113
|
-
*/archive/*) continue ;;
|
|
114
|
-
esac
|
|
115
|
-
|
|
116
|
-
local change_name phase
|
|
117
|
-
change_name=$(jq -r '.change_name // ""' "$state_file" 2>/dev/null)
|
|
118
|
-
phase=$(jq -r '.current_phase // ""' "$state_file" 2>/dev/null)
|
|
119
|
-
|
|
120
|
-
[ -n "$change_name" ] || continue
|
|
121
|
-
[ "$phase" != "archive" ] || continue
|
|
122
|
-
|
|
123
|
-
printf '%s|%s|%s\n' "$change_name" "$phase" "$state_file"
|
|
124
|
-
done
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
# phase_constraints — output constraint text for a given phase (to stdout)
|
|
128
|
-
phase_constraints() {
|
|
129
|
-
local phase="$1"
|
|
130
|
-
case "$phase" in
|
|
131
|
-
spec)
|
|
132
|
-
printf '%s\n' "- 只能通过 specline-spec-creator / specline-spec-reviewer 子 Agent 工作"
|
|
133
|
-
printf '%s\n' "- 禁止编辑任何应用代码文件(.ts/.tsx/.py/.go 等)"
|
|
134
|
-
printf '%s\n' "- 规划文件生成后需运行 Spec Gate"
|
|
135
|
-
;;
|
|
136
|
-
coding)
|
|
137
|
-
printf '%s\n' "- 编码必须通过子 Agent:specline-frontend-dev / specline-backend-dev / specline-config-dev"
|
|
138
|
-
printf '%s\n' "- 禁止直接编辑应用代码文件"
|
|
139
|
-
printf '%s\n' "- 每批次任务完成后运行 Build Gate"
|
|
140
|
-
printf '%s\n' "- 每个 Task 完成后更新 tasks.md 的 checkbox"
|
|
141
|
-
;;
|
|
142
|
-
code_review)
|
|
143
|
-
printf '%s\n' "- 只能运行 specline-code-reviewer + Lint Gate"
|
|
144
|
-
printf '%s\n' "- 如需修复代码,通过子 Agent 完成"
|
|
145
|
-
;;
|
|
146
|
-
test)
|
|
147
|
-
printf '%s\n' "- 运行测试 Gate 链:unit → integration → e2e"
|
|
148
|
-
printf '%s\n' "- 测试失败时通过 specline-test-runner 分析原因"
|
|
149
|
-
printf '%s\n' "- 代码修复通过子 Agent,测试修复通过 specline-test-writer"
|
|
150
|
-
;;
|
|
151
|
-
*)
|
|
152
|
-
printf '%s\n' "- 遵循 specline-pipeline SKILL 的当前阶段约束"
|
|
153
|
-
;;
|
|
154
|
-
esac
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
# build_context — generate a JSON-escaped additional_context string for a bound pipeline
|
|
158
|
-
build_context() {
|
|
159
|
-
local change_name="$1"
|
|
160
|
-
local phase="$2"
|
|
161
|
-
local state_file="$3"
|
|
162
|
-
|
|
163
|
-
{
|
|
164
|
-
printf '🚨 **Specline Pipeline 运行中**\n\n'
|
|
165
|
-
printf '**当前变更**: %s\n' "$change_name"
|
|
166
|
-
printf '**当前阶段**: %s\n' "$phase"
|
|
167
|
-
|
|
168
|
-
# Task progress for coding phase
|
|
169
|
-
if [ "$phase" = "coding" ] && [ -f "$state_file" ]; then
|
|
170
|
-
local completed total
|
|
171
|
-
completed=$(jq -r '[.phases.coding.tasks[]? | select(.status == "completed")] | length' "$state_file" 2>/dev/null || printf '0')
|
|
172
|
-
total=$(jq -r '[.phases.coding.tasks[]?] | length' "$state_file" 2>/dev/null || printf '0')
|
|
173
|
-
if [ "$total" != "0" ]; then
|
|
174
|
-
printf '**任务进度**: %s/%s 完成\n' "$completed" "$total"
|
|
175
|
-
fi
|
|
176
|
-
fi
|
|
177
|
-
|
|
178
|
-
printf '\n**阶段约束**:\n'
|
|
179
|
-
phase_constraints "$phase"
|
|
180
|
-
printf '\n'
|
|
181
|
-
printf '**重要**: 你是 Specline Pipeline 编排者。上述约束具有最高优先级,必须在每个操作前检查是否符合当前阶段要求。'
|
|
182
|
-
} | jq -Rs '.'
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
# phase_constraint_table — static phase constraint reference table (markdown)
|
|
186
|
-
phase_constraint_table() {
|
|
187
|
-
cat << 'TABLEEOF'
|
|
188
|
-
| 阶段 | 约束 |
|
|
189
|
-
|------|------|
|
|
190
|
-
| spec | 只能通过 specline-spec-creator/specline-spec-reviewer 子 Agent 工作,禁止编辑代码 |
|
|
191
|
-
| coding | 必须通过子 Agent 编码,批次完成后运行 Build Gate,更新 tasks.md |
|
|
192
|
-
| code_review | 只能运行 specline-code-reviewer + Lint Gate |
|
|
193
|
-
| test | 运行测试 Gate 链:unit → integration → e2e |
|
|
194
|
-
TABLEEOF
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
# write_binding — write a session_id → change_name binding to .pipeline-sessions.json
|
|
198
|
-
write_binding() {
|
|
199
|
-
local sid="$1"
|
|
200
|
-
local change="$2"
|
|
201
|
-
|
|
202
|
-
local now_iso
|
|
203
|
-
now_iso=$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
|
204
|
-
|
|
205
|
-
local tmp_file="${BINDINGS_FILE}.tmp"
|
|
206
|
-
jq --arg sid "$sid" --arg change "$change" --arg now "$now_iso" \
|
|
207
|
-
'.[$sid] = {"change": $change, "bound_at": $now}' \
|
|
208
|
-
"$BINDINGS_FILE" > "$tmp_file" 2>/dev/null && \
|
|
209
|
-
mv "$tmp_file" "$BINDINGS_FILE" 2>/dev/null || true
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
# delete_binding — remove a session_id entry from .pipeline-sessions.json
|
|
213
|
-
delete_binding() {
|
|
214
|
-
local session_id="$1"
|
|
215
|
-
|
|
216
|
-
[ -f "$BINDINGS_FILE" ] || return 0
|
|
217
|
-
|
|
218
|
-
local tmp_file="${BINDINGS_FILE}.tmp"
|
|
219
|
-
jq --arg sid "$session_id" 'del(.[$sid])' "$BINDINGS_FILE" > "$tmp_file" 2>/dev/null && \
|
|
220
|
-
mv "$tmp_file" "$BINDINGS_FILE" 2>/dev/null || true
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
# ============================================================================
|
|
224
|
-
# Main Logic
|
|
225
|
-
# ============================================================================
|
|
226
|
-
|
|
227
|
-
init_bindings_file
|
|
228
|
-
|
|
229
|
-
# 1. Clean expired bindings (bound_at > 7 days ago)
|
|
230
|
-
clean_expired_bindings
|
|
231
|
-
|
|
232
|
-
# 2. Check existing binding for this session
|
|
233
|
-
existing_change=""
|
|
234
|
-
if [ -f "$BINDINGS_FILE" ]; then
|
|
235
|
-
existing_change=$(jq -r --arg sid "$session_id" '.[$sid].change // empty' "$BINDINGS_FILE" 2>/dev/null)
|
|
236
|
-
fi
|
|
237
|
-
|
|
238
|
-
if [ -n "$existing_change" ]; then
|
|
239
|
-
if is_pipeline_active "$existing_change"; then
|
|
240
|
-
# Binding is still valid — use it
|
|
241
|
-
state_file="$CHANGES_DIR/$existing_change/.pipeline-state.json"
|
|
242
|
-
phase=$(jq -r '.current_phase // "unknown"' "$state_file" 2>/dev/null)
|
|
243
|
-
|
|
244
|
-
ctx_json=$(build_context "$existing_change" "$phase" "$state_file")
|
|
245
|
-
|
|
246
|
-
printf '{\n "additional_context": %s\n}\n' "$ctx_json"
|
|
247
|
-
exit 0
|
|
248
|
-
else
|
|
249
|
-
# Dirty data: pipeline archived or deleted — clean up and rescan
|
|
250
|
-
delete_binding "$session_id"
|
|
251
|
-
fi
|
|
252
|
-
fi
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
# 3. No (valid) binding → transparent pass-through
|
|
256
|
-
# 不再自动绑定或注入任何 pipeline 上下文,避免跨窗口污染。
|
|
257
|
-
# 用户需通过 /specline-pipeline --change <name> 显式绑定。
|
|
258
|
-
echo '{}'
|
|
259
|
-
exit 0
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# shell-guard.sh — beforeShellExecution Hook: 拦截危险命令
|
|
3
|
-
input=$(cat)
|
|
4
|
-
command=$(echo "$input" | jq -r '.command // empty')
|
|
5
|
-
if echo "$command" | grep -qE "rm -rf|rm -r "; then
|
|
6
|
-
echo '{"permission": "deny", "user_message": "危险命令被拦截: rm -rf"}'
|
|
7
|
-
exit 0
|
|
8
|
-
fi
|
|
9
|
-
if echo "$command" | grep -qE "curl.*\|.*bash|wget.*\|.*sh"; then
|
|
10
|
-
echo '{"permission": "deny", "user_message": "危险命令被拦截: 管道执行远程脚本"}'
|
|
11
|
-
exit 0
|
|
12
|
-
fi
|
|
13
|
-
if echo "$command" | grep -qE "^sudo "; then
|
|
14
|
-
echo '{"permission": "deny", "user_message": "sudo 需要人工审批"}'
|
|
15
|
-
exit 0
|
|
16
|
-
fi
|
|
17
|
-
echo '{"permission": "allow"}'
|
|
18
|
-
exit 0
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 1,
|
|
3
|
-
"hooks": {
|
|
4
|
-
"sessionStart": [
|
|
5
|
-
{
|
|
6
|
-
"command": ".cursor/hooks/specline-session-start.sh",
|
|
7
|
-
"timeout": 10
|
|
8
|
-
}
|
|
9
|
-
],
|
|
10
|
-
"preToolUse": [
|
|
11
|
-
{
|
|
12
|
-
"command": ".cursor/hooks/specline-phase-guard.sh",
|
|
13
|
-
"matcher": "Write|StrReplace|EditNotebook|Task|Delete",
|
|
14
|
-
"timeout": 5,
|
|
15
|
-
"failClosed": false
|
|
16
|
-
}
|
|
17
|
-
],
|
|
18
|
-
"postToolUse": [
|
|
19
|
-
{
|
|
20
|
-
"command": ".cursor/hooks/specline-reminder.sh",
|
|
21
|
-
"matcher": "Task|Write|StrReplace|Shell",
|
|
22
|
-
"timeout": 5
|
|
23
|
-
}
|
|
24
|
-
],
|
|
25
|
-
"subagentStart": [
|
|
26
|
-
{
|
|
27
|
-
"command": ".cursor/hooks/specline-agent-guard.sh",
|
|
28
|
-
"matcher": "specline-spec-creator|specline-spec-reviewer|specline-frontend-dev|specline-backend-dev|specline-config-dev|specline-code-reviewer|specline-config-reviewer|specline-test-writer|specline-test-runner",
|
|
29
|
-
"failClosed": true
|
|
30
|
-
}
|
|
31
|
-
],
|
|
32
|
-
"beforeShellExecution": [
|
|
33
|
-
{
|
|
34
|
-
"command": ".cursor/hooks/specline-shell-guard.sh",
|
|
35
|
-
"matcher": "rm -rf|curl.*\\|.*bash|wget.*\\|.*sh|^sudo ",
|
|
36
|
-
"failClosed": true
|
|
37
|
-
}
|
|
38
|
-
],
|
|
39
|
-
"afterFileEdit": [
|
|
40
|
-
{
|
|
41
|
-
"command": ".cursor/hooks/specline-auto-format.sh",
|
|
42
|
-
"matcher": "\\.(py|ts|tsx|js)$"
|
|
43
|
-
}
|
|
44
|
-
]
|
|
45
|
-
}
|
|
46
|
-
}
|
|
@@ -1,197 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: specline-apply-change
|
|
3
|
-
description: Implement tasks from a Specline change. Use when the user wants to start implementing, continue implementation, or work through tasks.
|
|
4
|
-
license: MIT
|
|
5
|
-
compatibility: Compatible with specline.
|
|
6
|
-
metadata:
|
|
7
|
-
author: specline
|
|
8
|
-
version: "1.0"
|
|
9
|
-
generatedBy: "1.3.1"
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
Implement tasks from a Specline change.
|
|
13
|
-
|
|
14
|
-
**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
|
|
15
|
-
|
|
16
|
-
## 速览 (Layer 1)
|
|
17
|
-
|
|
18
|
-
> **一句话**:实现 Specline change 中的编码任务。
|
|
19
|
-
> **入口**:`/specline-apply-change [change-name]` 或直接说「继续实现」
|
|
20
|
-
> **流程**:选 change → 读上下文 → 逐任务实现 → 标记完成
|
|
21
|
-
|
|
22
|
-
> ⚠️ **人机门禁策略感知**:当调用方传递了 `HUMAN_GATE_POLICY=minimal` 或 `HUMAN_GATE_POLICY=none` 上下文时,本 Skill 内所有的 `AskUserQuestion` 交互应自动采用默认安全选项:
|
|
23
|
-
> - change 选择 → 若上下文中已有 change name,直接使用;否则取第一个活跃 change
|
|
24
|
-
> - 其他确认交互 → 自动采用默认安全选项继续,不暂停等待人工输入
|
|
25
|
-
|
|
26
|
-
**Fluid Workflow Integration**
|
|
27
|
-
|
|
28
|
-
This skill supports the "actions on a change" model:
|
|
29
|
-
- **Can be invoked anytime**: Before all artifacts are done (if tasks exist), after partial implementation, interleaved with other actions
|
|
30
|
-
- **Allows artifact updates**: If implementation reveals design issues, suggest updating artifacts - not phase-locked, work fluidly
|
|
31
|
-
|
|
32
|
-
**开始前请确认:**
|
|
33
|
-
- [ ] Change 已选中(`/specline-pipeline --change <name>`)
|
|
34
|
-
- [ ] 已读取 proposal.md(知道做什么)
|
|
35
|
-
- [ ] 已读取 spec.md(知道需求和场景)
|
|
36
|
-
- [ ] 已读取 design.md(知道技术决策)
|
|
37
|
-
- [ ] 已读取 tasks.md(知道实现清单)
|
|
38
|
-
|
|
39
|
-
## 详细步骤 — Happy Path (Layer 2)
|
|
40
|
-
|
|
41
|
-
**Steps**
|
|
42
|
-
|
|
43
|
-
1. **Select the change**
|
|
44
|
-
|
|
45
|
-
If a name is provided, use it. Otherwise:
|
|
46
|
-
- Infer from conversation context if the user mentioned a change
|
|
47
|
-
- Auto-select if only one active change exists
|
|
48
|
-
- If ambiguous, run `specline-pipeline-gate.sh list --json` to get available changes and use the **AskUserQuestion tool** to let the user select
|
|
49
|
-
|
|
50
|
-
Always announce: "Using change: <name>" and how to override (e.g., `/specline-pipeline --change <other>`).
|
|
51
|
-
|
|
52
|
-
2. **Check status to understand the schema**
|
|
53
|
-
```bash
|
|
54
|
-
specline-pipeline-gate.sh artifacts --change "<name>" --json
|
|
55
|
-
```
|
|
56
|
-
Parse the JSON to understand:
|
|
57
|
-
- `schemaName`: The workflow being used (e.g., "spec-driven")
|
|
58
|
-
- Which artifact contains the tasks (typically "tasks" for spec-driven, check status for others)
|
|
59
|
-
|
|
60
|
-
3. **Read context files**
|
|
61
|
-
|
|
62
|
-
Read the planning files at `specline/changes/<name>/`:
|
|
63
|
-
- `proposal.md` — what & why
|
|
64
|
-
- `specs/<capability>/spec.md` — requirements & scenarios
|
|
65
|
-
- `design.md` — architecture & decisions
|
|
66
|
-
- `tasks.md` — implementation checklist
|
|
67
|
-
|
|
68
|
-
Check task completion: count `- [ ]` (incomplete) vs `- [x]` (complete).
|
|
69
|
-
|
|
70
|
-
4. **Show current progress**
|
|
71
|
-
|
|
72
|
-
Display:
|
|
73
|
-
- Progress: "N/M tasks complete"
|
|
74
|
-
- Remaining tasks overview
|
|
75
|
-
|
|
76
|
-
5. **Implement tasks (loop until done or blocked)**
|
|
77
|
-
|
|
78
|
-
For each pending task:
|
|
79
|
-
- Show which task is being worked on
|
|
80
|
-
- Make the code changes required
|
|
81
|
-
- Keep changes minimal and focused
|
|
82
|
-
- Mark task complete in the tasks file: `- [ ]` → `- [x]`
|
|
83
|
-
- Continue to next task
|
|
84
|
-
|
|
85
|
-
**Pause if:**
|
|
86
|
-
- Task is unclear → ask for clarification
|
|
87
|
-
- Implementation reveals a design issue → suggest updating artifacts
|
|
88
|
-
- Error or blocker encountered → report and wait for guidance
|
|
89
|
-
- User interrupts
|
|
90
|
-
|
|
91
|
-
6. **On completion or pause, show status**
|
|
92
|
-
|
|
93
|
-
Display:
|
|
94
|
-
- Tasks completed this session
|
|
95
|
-
- Overall progress: "N/M tasks complete"
|
|
96
|
-
- If all done: suggest archive
|
|
97
|
-
- If paused: explain why and wait for guidance
|
|
98
|
-
|
|
99
|
-
## 输出模板 & 高级话题 (Layer 3)
|
|
100
|
-
|
|
101
|
-
### 输出模板
|
|
102
|
-
|
|
103
|
-
**Output During Implementation**
|
|
104
|
-
|
|
105
|
-
```
|
|
106
|
-
## Implementing: <change-name> (schema: <schema-name>)
|
|
107
|
-
|
|
108
|
-
Working on task 3/7: <task description>
|
|
109
|
-
[...implementation happening...]
|
|
110
|
-
✓ Task complete
|
|
111
|
-
|
|
112
|
-
Working on task 4/7: <task description>
|
|
113
|
-
[...implementation happening...]
|
|
114
|
-
✓ Task complete
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
**Output On Completion**
|
|
118
|
-
|
|
119
|
-
```
|
|
120
|
-
## Implementation Complete
|
|
121
|
-
|
|
122
|
-
**Change:** <change-name>
|
|
123
|
-
**Schema:** <schema-name>
|
|
124
|
-
**Progress:** 7/7 tasks complete ✓
|
|
125
|
-
|
|
126
|
-
### Completed This Session
|
|
127
|
-
- [x] Task 1
|
|
128
|
-
- [x] Task 2
|
|
129
|
-
...
|
|
130
|
-
|
|
131
|
-
All tasks complete! Ready to archive this change.
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
**Output On Pause (Issue Encountered)**
|
|
135
|
-
|
|
136
|
-
```
|
|
137
|
-
## Implementation Paused
|
|
138
|
-
|
|
139
|
-
**Change:** <change-name>
|
|
140
|
-
**Schema:** <schema-name>
|
|
141
|
-
**Progress:** 4/7 tasks complete
|
|
142
|
-
|
|
143
|
-
### Issue Encountered
|
|
144
|
-
<description of the issue>
|
|
145
|
-
|
|
146
|
-
**Options:**
|
|
147
|
-
1. <option 1>
|
|
148
|
-
2. <option 2>
|
|
149
|
-
3. Other approach
|
|
150
|
-
|
|
151
|
-
What would you like to do?
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
### Guardrails
|
|
155
|
-
- Keep going through tasks until done or blocked
|
|
156
|
-
- Always read context files before starting (from the apply instructions output)
|
|
157
|
-
- If task is ambiguous, pause and ask before implementing
|
|
158
|
-
- If implementation reveals issues, pause and suggest artifact updates
|
|
159
|
-
- Keep code changes minimal and scoped to each task
|
|
160
|
-
- Update task checkbox immediately after completing each task
|
|
161
|
-
- Pause on errors, blockers, or unclear requirements - don't guess
|
|
162
|
-
- Use contextFiles from CLI output, don't assume specific file names
|
|
163
|
-
- **Hook blocked → no silent fallback**: If this skill is invoked because a coding subagent (specline-frontend-dev / specline-backend-dev) was blocked by a hook, you MUST first notify the user of the blocking cause and attempt diagnosis. Do not silently execute tasks that should have been handled by the blocked subagent. Reference the Hook Blocking Resolution Protocol in the specline-pipeline skill.
|
|
164
|
-
|
|
165
|
-
---
|
|
166
|
-
|
|
167
|
-
## Anti-Rationalization 表格
|
|
168
|
-
|
|
169
|
-
逐任务实现时,Agent 容易偏离规范:
|
|
170
|
-
|
|
171
|
-
| 借口 | 现实 |
|
|
172
|
-
|------|------|
|
|
173
|
-
| "不用读 Spec/Design/Tasks,我理解需求" | 记忆不可靠。实现前读上下文文件是防止方向偏离的最便宜保险。 |
|
|
174
|
-
| "顺便把这个相邻函数也重构了" | Scope Discipline 是 Core Behaviors。越界修改让 Code Review 和回溯都变困难。 |
|
|
175
|
-
| "checkbox 我最后一起标记" | Checkbox 是断点续跑的唯一信号源。不及时标记意味着下次恢复时状态丢失。 |
|
|
176
|
-
| "这个任务没有测试也没关系,下一个任务会补" | 每个 Testable=true 的任务必须产出测试。推迟 = 不写。 |
|
|
177
|
-
| "tasks.md 的 Covers 追溯链我不用管,代码写对就行" | Covers 链是 Spec → Code 的可追溯纽带。不维护它,Code Review 和测试失败定位都失去锚点。 |
|
|
178
|
-
|
|
179
|
-
## Verification Checklist
|
|
180
|
-
|
|
181
|
-
每完成一个任务后自查,全部完成后终查:
|
|
182
|
-
|
|
183
|
-
- [ ] 开始前已读 proposal.md / spec.md / design.md / tasks.md
|
|
184
|
-
- [ ] 每个任务的实现范围未超出 Files 声明
|
|
185
|
-
- [ ] 每个 Testable=true 的任务产出了测试文件(在 tests/unit/ 或 tests/models/)
|
|
186
|
-
- [ ] tasks.md 中每个已完成任务的 `[ ]` 已改为 `[x]`
|
|
187
|
-
- [ ] task-{id}-result.json 已写入 .tmp/ 目录
|
|
188
|
-
- [ ] 本 session 修改的文件与 tasks.md 的 Files 声明一致
|
|
189
|
-
- [ ] 未修改其他任务负责的文件
|
|
190
|
-
|
|
191
|
-
### 暂停场景处理
|
|
192
|
-
|
|
193
|
-
当实现过程中出现以下情况时,暂停并等待用户指引:
|
|
194
|
-
- 任务描述不清晰 → 请求用户澄清
|
|
195
|
-
- 实现中暴露出设计问题 → 建议更新 Artifact(proposal / spec / design / tasks)
|
|
196
|
-
- 遇到错误或阻塞 → 报告具体问题并等待指导
|
|
197
|
-
- 用户主动中断 → 记录当前进度,下次可从断点继续
|