@sduck/sduck-cli 0.1.3 → 0.1.4
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/.sduck/sduck-assets/agent-rules/antigravity.md +1 -0
- package/.sduck/sduck-assets/agent-rules/claude-code.md +1 -0
- package/.sduck/sduck-assets/agent-rules/codex.md +1 -0
- package/.sduck/sduck-assets/agent-rules/core.md +15 -0
- package/.sduck/sduck-assets/agent-rules/cursor.mdc +1 -0
- package/.sduck/sduck-assets/agent-rules/gemini-cli.md +1 -0
- package/.sduck/sduck-assets/agent-rules/hooks/sdd-guard.sh +125 -0
- package/.sduck/sduck-assets/agent-rules/opencode.md +1 -0
- package/README.md +40 -0
- package/dist/cli.js +265 -71
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# SDD Workflow Rules
|
|
2
2
|
|
|
3
|
+
## ⚠️ CRITICAL: 상태별 파일 접근 제한
|
|
4
|
+
|
|
5
|
+
- 코드를 작성하기 전에 반드시 `.sduck/sduck-workspace/`의 활성 태스크 `meta.yml`을 읽고 `status`를 확인한다.
|
|
6
|
+
- `IN_PROGRESS` 상태에서만 구현 코드를 작성할 수 있다.
|
|
7
|
+
- 승인된 `spec.md`/`plan.md`는 수정하지 않는다. 요구사항이 바뀌면 새 태스크를 시작한다.
|
|
8
|
+
- `spec.md` 또는 `plan.md`를 작성한 직후라도, 사용자가 승인하기 전까지 구현하지 않는다.
|
|
9
|
+
- "같은 세션"이라는 이유로 승인을 생략하지 않는다.
|
|
10
|
+
|
|
11
|
+
| 상태 | spec.md | plan.md | 구현 파일 |
|
|
12
|
+
|---|---|---|---|
|
|
13
|
+
| PENDING_SPEC_APPROVAL | 허용 | 허용 | 차단 |
|
|
14
|
+
| SPEC_APPROVED | 차단 | 허용 | 차단 |
|
|
15
|
+
| IN_PROGRESS | 차단 | 차단 | 허용 |
|
|
16
|
+
| DONE | 차단 | 차단 | 차단 |
|
|
17
|
+
|
|
3
18
|
## 디렉토리 구조
|
|
4
19
|
|
|
5
20
|
에이전트는 아래 경로를 기준으로 `sduck` 상태를 읽고 쓴다.
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# sduck SDD guard hook for Claude Code
|
|
3
|
+
# Enforces the state-based file access matrix via PreToolUse hook.
|
|
4
|
+
# Exit 0 = allow, Exit 2 = block (message sent to Claude via stderr)
|
|
5
|
+
|
|
6
|
+
set -euo pipefail
|
|
7
|
+
|
|
8
|
+
INPUT=$(cat)
|
|
9
|
+
|
|
10
|
+
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
|
|
11
|
+
|
|
12
|
+
# No file path (e.g. Bash tool) → allow
|
|
13
|
+
if [[ -z "$FILE_PATH" ]]; then
|
|
14
|
+
exit 0
|
|
15
|
+
fi
|
|
16
|
+
|
|
17
|
+
# Resolve relative to cwd
|
|
18
|
+
CWD=$(echo "$INPUT" | jq -r '.cwd // empty')
|
|
19
|
+
if [[ -z "$CWD" ]]; then
|
|
20
|
+
exit 0
|
|
21
|
+
fi
|
|
22
|
+
|
|
23
|
+
# Find sduck workspace
|
|
24
|
+
WORKSPACE_DIR="$CWD/.sduck/sduck-workspace"
|
|
25
|
+
if [[ ! -d "$WORKSPACE_DIR" ]]; then
|
|
26
|
+
exit 0
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
# Always-allowed paths (relative check)
|
|
30
|
+
REL_PATH="${FILE_PATH#"$CWD"/}"
|
|
31
|
+
|
|
32
|
+
# Always allow: meta.yml, sduck assets, agent rule files, editor configs
|
|
33
|
+
case "$REL_PATH" in
|
|
34
|
+
*/meta.yml|.sduck/sduck-assets/*|.sduck/sduck-archive/*|\
|
|
35
|
+
CLAUDE.md|AGENT.md|GEMINI.md|\
|
|
36
|
+
.cursor/*|.agents/*|.claude/*|\
|
|
37
|
+
.serena/*)
|
|
38
|
+
exit 0
|
|
39
|
+
;;
|
|
40
|
+
esac
|
|
41
|
+
|
|
42
|
+
# Find the most recent active task (non-DONE)
|
|
43
|
+
STATUS=""
|
|
44
|
+
TASK_DIR=""
|
|
45
|
+
for dir in "$WORKSPACE_DIR"/*/; do
|
|
46
|
+
[[ -d "$dir" ]] || continue
|
|
47
|
+
META="$dir/meta.yml"
|
|
48
|
+
[[ -f "$META" ]] || continue
|
|
49
|
+
|
|
50
|
+
TASK_STATUS=$(grep -m1 '^status:' "$META" | sed 's/^status:[[:space:]]*//')
|
|
51
|
+
|
|
52
|
+
case "$TASK_STATUS" in
|
|
53
|
+
PENDING_SPEC_APPROVAL|SPEC_APPROVED|PENDING_PLAN_APPROVAL|IN_PROGRESS)
|
|
54
|
+
STATUS="$TASK_STATUS"
|
|
55
|
+
TASK_DIR="$dir"
|
|
56
|
+
;;
|
|
57
|
+
DONE)
|
|
58
|
+
# Track DONE as fallback if no active task found
|
|
59
|
+
if [[ -z "$STATUS" ]]; then
|
|
60
|
+
STATUS="DONE"
|
|
61
|
+
TASK_DIR="$dir"
|
|
62
|
+
fi
|
|
63
|
+
;;
|
|
64
|
+
esac
|
|
65
|
+
done
|
|
66
|
+
|
|
67
|
+
# No task at all → allow
|
|
68
|
+
if [[ -z "$STATUS" ]]; then
|
|
69
|
+
exit 0
|
|
70
|
+
fi
|
|
71
|
+
|
|
72
|
+
# Check if the file is spec.md or plan.md (within the task workspace)
|
|
73
|
+
IS_SPEC=false
|
|
74
|
+
IS_PLAN=false
|
|
75
|
+
case "$REL_PATH" in
|
|
76
|
+
*/spec.md|spec.md) IS_SPEC=true ;;
|
|
77
|
+
*/plan.md|plan.md) IS_PLAN=true ;;
|
|
78
|
+
esac
|
|
79
|
+
|
|
80
|
+
# Apply the state-based access matrix
|
|
81
|
+
case "$STATUS" in
|
|
82
|
+
PENDING_SPEC_APPROVAL)
|
|
83
|
+
# spec.md, plan.md → allow; implementation files → block
|
|
84
|
+
if $IS_SPEC || $IS_PLAN; then
|
|
85
|
+
exit 0
|
|
86
|
+
fi
|
|
87
|
+
echo "⛔ [sduck] 현재 상태: $STATUS — 스펙 승인 전에는 구현 코드를 작성할 수 없습니다. \`sduck spec approve\`를 먼저 실행하세요." >&2
|
|
88
|
+
exit 2
|
|
89
|
+
;;
|
|
90
|
+
SPEC_APPROVED|PENDING_PLAN_APPROVAL)
|
|
91
|
+
# plan.md → allow; spec.md, implementation files → block
|
|
92
|
+
if $IS_PLAN; then
|
|
93
|
+
exit 0
|
|
94
|
+
fi
|
|
95
|
+
if $IS_SPEC; then
|
|
96
|
+
echo "⛔ [sduck] 현재 상태: $STATUS — 승인된 스펙은 수정할 수 없습니다. 요구사항이 바뀌었다면 새 태스크를 시작하세요." >&2
|
|
97
|
+
exit 2
|
|
98
|
+
fi
|
|
99
|
+
echo "⛔ [sduck] 현재 상태: $STATUS — 플랜 승인 전에는 구현 코드를 작성할 수 없습니다. \`sduck plan approve\`를 먼저 실행하세요." >&2
|
|
100
|
+
exit 2
|
|
101
|
+
;;
|
|
102
|
+
IN_PROGRESS)
|
|
103
|
+
# implementation files → allow; spec.md, plan.md → block
|
|
104
|
+
if $IS_SPEC; then
|
|
105
|
+
echo "⛔ [sduck] 현재 상태: IN_PROGRESS — 승인된 스펙은 수정할 수 없습니다. 요구사항이 바뀌었다면 새 태스크를 시작하세요." >&2
|
|
106
|
+
exit 2
|
|
107
|
+
fi
|
|
108
|
+
if $IS_PLAN; then
|
|
109
|
+
echo "⛔ [sduck] 현재 상태: IN_PROGRESS — 승인된 플랜은 수정할 수 없습니다. 요구사항이 바뀌었다면 새 태스크를 시작하세요." >&2
|
|
110
|
+
exit 2
|
|
111
|
+
fi
|
|
112
|
+
exit 0
|
|
113
|
+
;;
|
|
114
|
+
DONE)
|
|
115
|
+
# everything blocked except always-allowed paths (already handled above)
|
|
116
|
+
if $IS_SPEC || $IS_PLAN; then
|
|
117
|
+
echo "⛔ [sduck] 현재 상태: DONE — 완료된 태스크의 문서는 수정할 수 없습니다. \`sduck start\`로 새 태스크를 시작하세요." >&2
|
|
118
|
+
exit 2
|
|
119
|
+
fi
|
|
120
|
+
echo "⛔ [sduck] 현재 상태: DONE — 새 코드를 작성하려면 \`sduck start\`로 새 태스크를 시작하세요." >&2
|
|
121
|
+
exit 2
|
|
122
|
+
;;
|
|
123
|
+
esac
|
|
124
|
+
|
|
125
|
+
exit 0
|
package/README.md
CHANGED
|
@@ -41,6 +41,46 @@ npm install -g @sduck/sduck-cli
|
|
|
41
41
|
sduck init --agents claude-code,cursor,codex
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
+
### Windows 환경에서 `sduck`을 찾지 못할 때
|
|
45
|
+
|
|
46
|
+
Windows(PowerShell)에서 `npm install -g` 후 `sduck`을 실행했을 때 아래와 같은 에러가 발생할 수 있습니다.
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
sduck : 이 시스템에서 스크립트를 실행할 수 없습니다.
|
|
50
|
+
# 또는
|
|
51
|
+
sduck: The term 'sduck' is not recognized as the name of a cmdlet...
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
이는 npm 전역 설치 경로가 시스템 PATH에 포함되지 않아서 발생합니다.
|
|
55
|
+
|
|
56
|
+
**1. npm 전역 경로 확인**
|
|
57
|
+
|
|
58
|
+
```powershell
|
|
59
|
+
npm config get prefix
|
|
60
|
+
# 예: C:\Users\사용자이름\AppData\Roaming\npm
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**2. PATH에 포함되어 있는지 확인**
|
|
64
|
+
|
|
65
|
+
```powershell
|
|
66
|
+
$env:PATH -split ';' | Select-String 'npm'
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
결과가 없으면 PATH에 추가해야 합니다.
|
|
70
|
+
|
|
71
|
+
**3. PATH에 영구 추가 (PowerShell)**
|
|
72
|
+
|
|
73
|
+
```powershell
|
|
74
|
+
# 현재 사용자에게 영구 추가
|
|
75
|
+
[Environment]::SetEnvironmentVariable(
|
|
76
|
+
'Path',
|
|
77
|
+
[Environment]::GetEnvironmentVariable('Path', 'User') + ';' + (npm config get prefix),
|
|
78
|
+
'User'
|
|
79
|
+
)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
설정 후 PowerShell을 재시작하면 `sduck` 명령어가 정상 동작합니다.
|
|
83
|
+
|
|
44
84
|
## 📖 주요 명령어
|
|
45
85
|
|
|
46
86
|
### 빠른 시작 예시
|