@simplysm/sd-claude 13.0.69 → 13.0.71
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/README.md +12 -601
- package/claude/agents/sd-api-reviewer.md +0 -1
- package/claude/agents/sd-code-reviewer.md +0 -1
- package/claude/agents/sd-code-simplifier.md +1 -1
- package/claude/agents/sd-security-reviewer.md +0 -1
- package/claude/refs/sd-angular.md +26 -26
- package/claude/refs/sd-orm-v12.md +17 -17
- package/claude/rules/sd-refs-linker.md +14 -14
- package/claude/sd-statusline.js +21 -21
- package/claude/skills/sd-api-name-review/SKILL.md +1 -2
- package/claude/skills/sd-brainstorm/SKILL.md +3 -4
- package/claude/skills/sd-check/SKILL.md +1 -2
- package/claude/skills/sd-commit/SKILL.md +2 -3
- package/claude/skills/sd-debug/SKILL.md +1 -2
- package/claude/skills/sd-discuss/SKILL.md +1 -3
- package/claude/skills/sd-document/SKILL.md +99 -0
- package/claude/skills/sd-document/extract_docx.py +92 -0
- package/claude/skills/sd-document/extract_pdf.py +102 -0
- package/claude/skills/sd-document/extract_pptx.py +77 -0
- package/claude/skills/sd-document/extract_xlsx.py +83 -0
- package/claude/skills/sd-email-analyze/SKILL.md +6 -6
- package/claude/skills/sd-plan/SKILL.md +1 -3
- package/claude/skills/sd-plan-dev/SKILL.md +94 -111
- package/claude/skills/sd-plan-dev/code-quality-reviewer-prompt.md +1 -1
- package/claude/skills/sd-plan-dev/final-review-prompt.md +1 -1
- package/claude/skills/sd-plan-dev/spec-reviewer-prompt.md +1 -1
- package/claude/skills/sd-readme/SKILL.md +107 -88
- package/claude/skills/sd-review/SKILL.md +14 -16
- package/claude/skills/sd-skill/SKILL.md +6 -317
- package/claude/skills/sd-skill/cso-guide.md +161 -0
- package/claude/skills/sd-skill/writing-guide.md +163 -0
- package/claude/skills/sd-tdd/SKILL.md +1 -3
- package/claude/skills/sd-use/SKILL.md +1 -3
- package/claude/skills/sd-worktree/SKILL.md +52 -2
- package/dist/commands/auth-add.d.ts +2 -0
- package/dist/commands/auth-add.d.ts.map +1 -0
- package/dist/commands/auth-add.js +32 -0
- package/dist/commands/auth-add.js.map +6 -0
- package/dist/commands/auth-list.d.ts +2 -0
- package/dist/commands/auth-list.d.ts.map +1 -0
- package/dist/commands/auth-list.js +37 -0
- package/dist/commands/auth-list.js.map +6 -0
- package/dist/commands/auth-remove.d.ts +2 -0
- package/dist/commands/auth-remove.d.ts.map +1 -0
- package/dist/commands/auth-remove.js +22 -0
- package/dist/commands/auth-remove.js.map +6 -0
- package/dist/commands/auth-use.d.ts +2 -0
- package/dist/commands/auth-use.d.ts.map +1 -0
- package/dist/commands/auth-use.js +33 -0
- package/dist/commands/auth-use.js.map +6 -0
- package/dist/commands/auth-utils.d.ts +11 -0
- package/dist/commands/auth-utils.d.ts.map +1 -0
- package/dist/commands/auth-utils.js +57 -0
- package/dist/commands/auth-utils.js.map +6 -0
- package/dist/commands/install.js +3 -3
- package/dist/commands/install.js.map +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/sd-claude.js +68 -3
- package/dist/sd-claude.js.map +1 -1
- package/package.json +3 -2
- package/scripts/sync-claude-assets.mjs +1 -1
- package/src/commands/auth-add.ts +36 -0
- package/src/commands/auth-list.ts +44 -0
- package/src/commands/auth-remove.ts +26 -0
- package/src/commands/auth-use.ts +53 -0
- package/src/commands/auth-utils.ts +65 -0
- package/src/commands/install.ts +19 -19
- package/src/index.ts +5 -0
- package/src/sd-claude.ts +81 -3
- package/tests/auth-add.spec.ts +74 -0
- package/tests/auth-list.spec.ts +175 -0
- package/tests/auth-remove.spec.ts +74 -0
- package/tests/auth-use.spec.ts +153 -0
- package/tests/auth-utils.spec.ts +173 -0
- package/claude/skills/sd-explore/SKILL.md +0 -78
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""PDF 파일에서 텍스트, 표, 이미지를 페이지별로 추출한다."""
|
|
3
|
+
|
|
4
|
+
import sys
|
|
5
|
+
import io
|
|
6
|
+
import subprocess
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
|
|
10
|
+
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding="utf-8", errors="replace")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def ensure_packages():
|
|
14
|
+
packages = {"pdfplumber": "pdfplumber", "pypdf": "pypdf"}
|
|
15
|
+
for pip_name, import_name in packages.items():
|
|
16
|
+
try:
|
|
17
|
+
__import__(import_name)
|
|
18
|
+
except ImportError:
|
|
19
|
+
print(f"패키지 설치 중: {pip_name}...", file=sys.stderr)
|
|
20
|
+
subprocess.check_call([sys.executable, "-m", "pip", "install", pip_name],
|
|
21
|
+
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def extract(file_path):
|
|
25
|
+
import pdfplumber
|
|
26
|
+
from pypdf import PdfReader
|
|
27
|
+
|
|
28
|
+
stem = Path(file_path).stem
|
|
29
|
+
out_dir = Path(file_path).parent / f"{stem}_files"
|
|
30
|
+
img_idx = 0
|
|
31
|
+
total_text_len = 0
|
|
32
|
+
|
|
33
|
+
print(f"# {Path(file_path).name}\n")
|
|
34
|
+
|
|
35
|
+
# 텍스트 + 표 추출 (pdfplumber)
|
|
36
|
+
with pdfplumber.open(file_path) as pdf:
|
|
37
|
+
for page_num, page in enumerate(pdf.pages, 1):
|
|
38
|
+
print(f"## Page {page_num}\n")
|
|
39
|
+
|
|
40
|
+
text = page.extract_text()
|
|
41
|
+
if text and text.strip():
|
|
42
|
+
total_text_len += len(text.strip())
|
|
43
|
+
print(text.strip())
|
|
44
|
+
print()
|
|
45
|
+
|
|
46
|
+
tables = page.extract_tables()
|
|
47
|
+
for t_idx, table in enumerate(tables):
|
|
48
|
+
if table:
|
|
49
|
+
print(f"### Table {t_idx + 1}\n")
|
|
50
|
+
for row in table:
|
|
51
|
+
cells = [(c or "").strip().replace("\n", " ") for c in row]
|
|
52
|
+
print("| " + " | ".join(cells) + " |")
|
|
53
|
+
print()
|
|
54
|
+
|
|
55
|
+
# 이미지 추출 (pypdf)
|
|
56
|
+
reader = PdfReader(file_path)
|
|
57
|
+
for page_num, page in enumerate(reader.pages, 1):
|
|
58
|
+
if "/XObject" not in (page.get("/Resources") or {}):
|
|
59
|
+
continue
|
|
60
|
+
xobjects = page["/Resources"]["/XObject"].get_object()
|
|
61
|
+
for obj_name in xobjects:
|
|
62
|
+
obj = xobjects[obj_name].get_object()
|
|
63
|
+
if obj.get("/Subtype") == "/Image":
|
|
64
|
+
img_idx += 1
|
|
65
|
+
filters = obj.get("/Filter", "")
|
|
66
|
+
if isinstance(filters, list):
|
|
67
|
+
filters = filters[0] if filters else ""
|
|
68
|
+
ext = "png"
|
|
69
|
+
if "/DCTDecode" in str(filters):
|
|
70
|
+
ext = "jpg"
|
|
71
|
+
elif "/JPXDecode" in str(filters):
|
|
72
|
+
ext = "jp2"
|
|
73
|
+
out_dir.mkdir(parents=True, exist_ok=True)
|
|
74
|
+
img_path = out_dir / f"img_{img_idx:03d}.{ext}"
|
|
75
|
+
try:
|
|
76
|
+
img_path.write_bytes(obj.get_data())
|
|
77
|
+
except Exception:
|
|
78
|
+
img_path = out_dir / f"img_{img_idx:03d}.bin"
|
|
79
|
+
img_path.write_bytes(obj._data if hasattr(obj, "_data") else b"")
|
|
80
|
+
print(f"[IMG] (page={page_num}) {img_path}")
|
|
81
|
+
|
|
82
|
+
# OCR 안내
|
|
83
|
+
if total_text_len == 0:
|
|
84
|
+
print("\n⚠ 텍스트가 추출되지 않았습니다 (스캔 PDF일 수 있음).")
|
|
85
|
+
print("OCR이 필요합니다:")
|
|
86
|
+
print(" 1. Tesseract OCR 설치: https://github.com/tesseract-ocr/tesseract")
|
|
87
|
+
print(" 2. pip install pytesseract pdf2image")
|
|
88
|
+
print(" 3. pytesseract.image_to_string() 으로 추출")
|
|
89
|
+
|
|
90
|
+
print()
|
|
91
|
+
if img_idx > 0:
|
|
92
|
+
print(f"---\n이미지 {img_idx}개 저장: {out_dir}")
|
|
93
|
+
else:
|
|
94
|
+
print("---\n이미지 없음")
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
if __name__ == "__main__":
|
|
98
|
+
if len(sys.argv) < 2:
|
|
99
|
+
print("Usage: python extract_pdf.py <file.pdf>", file=sys.stderr)
|
|
100
|
+
sys.exit(1)
|
|
101
|
+
ensure_packages()
|
|
102
|
+
extract(sys.argv[1])
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""PPTX 파일에서 텍스트와 이미지를 슬라이드별 좌표와 함께 추출한다."""
|
|
3
|
+
|
|
4
|
+
import sys
|
|
5
|
+
import io
|
|
6
|
+
import subprocess
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
|
|
10
|
+
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding="utf-8", errors="replace")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def ensure_packages():
|
|
14
|
+
packages = {"python-pptx": "pptx"}
|
|
15
|
+
for pip_name, import_name in packages.items():
|
|
16
|
+
try:
|
|
17
|
+
__import__(import_name)
|
|
18
|
+
except ImportError:
|
|
19
|
+
print(f"패키지 설치 중: {pip_name}...", file=sys.stderr)
|
|
20
|
+
subprocess.check_call([sys.executable, "-m", "pip", "install", pip_name],
|
|
21
|
+
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def emu_to_inches(emu):
|
|
25
|
+
"""EMU를 인치로 변환 (소수점 1자리)."""
|
|
26
|
+
if emu is None:
|
|
27
|
+
return "?"
|
|
28
|
+
return f"{emu / 914400:.1f}"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def extract(file_path):
|
|
32
|
+
from pptx import Presentation
|
|
33
|
+
from pptx.enum.shapes import MSO_SHAPE_TYPE
|
|
34
|
+
|
|
35
|
+
prs = Presentation(file_path)
|
|
36
|
+
stem = Path(file_path).stem
|
|
37
|
+
out_dir = Path(file_path).parent / f"{stem}_files"
|
|
38
|
+
img_idx = 0
|
|
39
|
+
|
|
40
|
+
print(f"# {Path(file_path).name}\n")
|
|
41
|
+
|
|
42
|
+
for slide_num, slide in enumerate(prs.slides, 1):
|
|
43
|
+
print(f"## Slide {slide_num}\n")
|
|
44
|
+
|
|
45
|
+
for shape in slide.shapes:
|
|
46
|
+
left = emu_to_inches(shape.left)
|
|
47
|
+
top = emu_to_inches(shape.top)
|
|
48
|
+
pos = f"(left={left}\", top={top}\")"
|
|
49
|
+
|
|
50
|
+
if shape.shape_type == MSO_SHAPE_TYPE.PICTURE:
|
|
51
|
+
img_idx += 1
|
|
52
|
+
blob = shape.image.blob
|
|
53
|
+
content_type = shape.image.content_type
|
|
54
|
+
ext = content_type.split("/")[-1].replace("jpeg", "jpg")
|
|
55
|
+
out_dir.mkdir(parents=True, exist_ok=True)
|
|
56
|
+
img_path = out_dir / f"img_{img_idx:03d}.{ext}"
|
|
57
|
+
img_path.write_bytes(blob)
|
|
58
|
+
print(f"[IMG] {pos} {img_path}")
|
|
59
|
+
|
|
60
|
+
elif hasattr(shape, "text") and shape.text.strip():
|
|
61
|
+
text = shape.text.strip().replace("\n", "\n ")
|
|
62
|
+
print(f"[TXT] {pos} {text}")
|
|
63
|
+
|
|
64
|
+
print()
|
|
65
|
+
|
|
66
|
+
if img_idx > 0:
|
|
67
|
+
print(f"---\n이미지 {img_idx}개 저장: {out_dir}")
|
|
68
|
+
else:
|
|
69
|
+
print("---\n이미지 없음")
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
if __name__ == "__main__":
|
|
73
|
+
if len(sys.argv) < 2:
|
|
74
|
+
print("Usage: python extract_pptx.py <file.pptx>", file=sys.stderr)
|
|
75
|
+
sys.exit(1)
|
|
76
|
+
ensure_packages()
|
|
77
|
+
extract(sys.argv[1])
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""XLSX 파일에서 데이터와 이미지를 셀 위치와 함께 추출한다."""
|
|
3
|
+
|
|
4
|
+
import sys
|
|
5
|
+
import io
|
|
6
|
+
import os
|
|
7
|
+
import subprocess
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
|
|
11
|
+
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding="utf-8", errors="replace")
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def ensure_packages():
|
|
15
|
+
packages = {"openpyxl": "openpyxl"}
|
|
16
|
+
for pip_name, import_name in packages.items():
|
|
17
|
+
try:
|
|
18
|
+
__import__(import_name)
|
|
19
|
+
except ImportError:
|
|
20
|
+
print(f"패키지 설치 중: {pip_name}...", file=sys.stderr)
|
|
21
|
+
subprocess.check_call([sys.executable, "-m", "pip", "install", pip_name],
|
|
22
|
+
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def extract(file_path):
|
|
26
|
+
from openpyxl import load_workbook
|
|
27
|
+
from openpyxl.drawing.image import Image as XlImage
|
|
28
|
+
|
|
29
|
+
wb = load_workbook(file_path, data_only=True)
|
|
30
|
+
stem = Path(file_path).stem
|
|
31
|
+
out_dir = Path(file_path).parent / f"{stem}_files"
|
|
32
|
+
img_idx = 0
|
|
33
|
+
|
|
34
|
+
print(f"# {Path(file_path).name}\n")
|
|
35
|
+
|
|
36
|
+
for sheet_name in wb.sheetnames:
|
|
37
|
+
ws = wb[sheet_name]
|
|
38
|
+
print(f"## Sheet: {sheet_name}\n")
|
|
39
|
+
|
|
40
|
+
# 데이터 추출
|
|
41
|
+
rows = list(ws.iter_rows(values_only=False))
|
|
42
|
+
if not rows:
|
|
43
|
+
print("(빈 시트)\n")
|
|
44
|
+
continue
|
|
45
|
+
|
|
46
|
+
for row in rows:
|
|
47
|
+
cells = []
|
|
48
|
+
for cell in row:
|
|
49
|
+
val = cell.value
|
|
50
|
+
if val is None:
|
|
51
|
+
cells.append("")
|
|
52
|
+
else:
|
|
53
|
+
cells.append(str(val).strip())
|
|
54
|
+
print(f"[{row[0].coordinate.split('1')[0]}{row[0].row}] " + " | ".join(cells))
|
|
55
|
+
|
|
56
|
+
# 이미지 추출
|
|
57
|
+
if ws._images:
|
|
58
|
+
for img in ws._images:
|
|
59
|
+
img_idx += 1
|
|
60
|
+
anchor = ""
|
|
61
|
+
if hasattr(img.anchor, '_from'):
|
|
62
|
+
anchor = f" (near {img.anchor._from.col},{img.anchor._from.row})"
|
|
63
|
+
ext = "png"
|
|
64
|
+
out_dir.mkdir(parents=True, exist_ok=True)
|
|
65
|
+
img_path = out_dir / f"img_{img_idx:03d}.{ext}"
|
|
66
|
+
with open(img_path, "wb") as f:
|
|
67
|
+
f.write(img._data())
|
|
68
|
+
print(f"[IMG]{anchor} {img_path}")
|
|
69
|
+
|
|
70
|
+
print()
|
|
71
|
+
|
|
72
|
+
if img_idx > 0:
|
|
73
|
+
print(f"---\n이미지 {img_idx}개 저장: {out_dir}")
|
|
74
|
+
else:
|
|
75
|
+
print("---\n이미지 없음")
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
if __name__ == "__main__":
|
|
79
|
+
if len(sys.argv) < 2:
|
|
80
|
+
print("Usage: python extract_xlsx.py <file.xlsx>", file=sys.stderr)
|
|
81
|
+
sys.exit(1)
|
|
82
|
+
ensure_packages()
|
|
83
|
+
extract(sys.argv[1])
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: sd-email-analyze
|
|
3
|
-
description:
|
|
4
|
-
|
|
3
|
+
description: "Use when the user's request involves .eml or .msg files. Triggers: email file analysis, email content extraction, attachment extraction, email summary."
|
|
4
|
+
model: haiku
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Email Analyzer
|
|
8
8
|
|
|
9
9
|
## Overview
|
|
10
10
|
|
|
11
|
-
Python script that parses `.eml` and `.msg` (Outlook) email files. Extracts mail headers, body text, inline images, and attachments to disk. Content analysis of extracted files is delegated to Claude's Read tool and document
|
|
11
|
+
Python script that parses `.eml` and `.msg` (Outlook) email files. Extracts mail headers, body text, inline images, and attachments to disk. Content analysis of extracted files is delegated to Claude's Read tool and `sd-document` skill.
|
|
12
12
|
|
|
13
13
|
## When to Use
|
|
14
14
|
|
|
15
15
|
- User provides a `.eml` or `.msg` file to analyze or summarize
|
|
16
|
-
-
|
|
16
|
+
- Email content needs proper decoding
|
|
17
17
|
|
|
18
18
|
## Usage
|
|
19
19
|
|
|
@@ -27,7 +27,7 @@ First run auto-installs: `extract-msg`.
|
|
|
27
27
|
|
|
28
28
|
1. Read the markdown output (mail info, body text, file paths)
|
|
29
29
|
2. **Inline images**: Use **Read** tool on each saved path to view
|
|
30
|
-
3. **Attachments**: Use **Read** tool (
|
|
30
|
+
3. **Attachments**: Use **Read** tool (images) or **sd-document** skill scripts (DOCX, XLSX, PPTX, PDF)
|
|
31
31
|
|
|
32
32
|
## Output
|
|
33
33
|
|
|
@@ -49,4 +49,4 @@ Two sources extracted:
|
|
|
49
49
|
|
|
50
50
|
- **Wrong Python**: Ensure `python` points to Python 3.8+
|
|
51
51
|
- **Firewall blocking pip**: First run needs internet for `extract-msg` install
|
|
52
|
-
- **Forgetting inline images**: Always check "
|
|
52
|
+
- **Forgetting inline images**: Always check the "Inline images" section and read each path
|
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: sd-plan-dev
|
|
3
|
-
description: Parallel execution of
|
|
4
|
-
disable-model-invocation: true
|
|
5
|
-
model: opus
|
|
3
|
+
description: "Parallel execution of plan tasks (explicit invocation only)"
|
|
6
4
|
---
|
|
7
5
|
|
|
8
6
|
# Parallel Plan Execution
|
|
9
7
|
|
|
10
|
-
Execute plan tasks via parallel
|
|
8
|
+
Execute plan tasks via parallel implementers with dependency-aware scheduling.
|
|
11
9
|
|
|
12
|
-
**Core principle:** Dependency analysis + parallel
|
|
10
|
+
**Core principle:** Dependency analysis + parallel implementers + orchestrator-managed reviews = maximum throughput
|
|
13
11
|
|
|
14
12
|
## When to Use
|
|
15
13
|
|
|
@@ -28,11 +26,16 @@ digraph when_to_use {
|
|
|
28
26
|
|
|
29
27
|
All execution uses `Task(general-purpose)` for parallel execution.
|
|
30
28
|
|
|
31
|
-
- **
|
|
32
|
-
- **spec reviewer**: `Task(general-purpose
|
|
33
|
-
- **quality reviewer**: `Task(general-purpose
|
|
29
|
+
- **implementer**: `Task(general-purpose, model: min(sonnet, current))` — implements one task, commits, reports
|
|
30
|
+
- **spec reviewer**: `Task(general-purpose)` — dispatched by orchestrator after implementer completes (read-only)
|
|
31
|
+
- **quality reviewer**: `Task(general-purpose)` — dispatched by orchestrator in parallel with spec reviewer (read-only)
|
|
32
|
+
- **final reviewer**: `Task(general-purpose)` — dispatched by orchestrator after all batches complete (read-only)
|
|
34
33
|
|
|
35
|
-
|
|
34
|
+
**Model selection:**
|
|
35
|
+
- **implementer**: use `min(sonnet, current model)`. If the user's current model is haiku, use haiku. Otherwise use sonnet.
|
|
36
|
+
- **All other agents**: inherit current model (no explicit `model` parameter).
|
|
37
|
+
|
|
38
|
+
Independent tasks run as **parallel Task calls in a single message**. After implementers complete, spec and quality reviews run as **parallel Task calls**.
|
|
36
39
|
|
|
37
40
|
**CRITICAL: Do NOT use `run_in_background: true`** — achieve parallelism by making multiple Task calls in a single message (foreground parallel). This ensures the orchestrator waits for all tasks to complete before proceeding to the next batch, and prevents Stop hooks from firing prematurely.
|
|
38
41
|
|
|
@@ -48,33 +51,38 @@ digraph process {
|
|
|
48
51
|
subgraph cluster_batch {
|
|
49
52
|
label="Per Batch (independent tasks)";
|
|
50
53
|
|
|
51
|
-
subgraph
|
|
52
|
-
label="Parallel Task calls (single message)";
|
|
54
|
+
subgraph cluster_parallel_implementers {
|
|
55
|
+
label="Parallel implementer Task calls (single message)";
|
|
53
56
|
style=dashed;
|
|
54
57
|
|
|
55
|
-
subgraph
|
|
56
|
-
label="Each
|
|
58
|
+
subgraph cluster_implementer {
|
|
59
|
+
label="Each Implementer";
|
|
57
60
|
"Implement the task" [shape=box];
|
|
58
61
|
"Questions?" [shape=diamond];
|
|
59
62
|
"Return questions to orchestrator" [shape=box];
|
|
60
63
|
"Re-launch with answers" [shape=box];
|
|
64
|
+
"Commit and report" [shape=box];
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
subgraph cluster_review {
|
|
69
|
+
label="Orchestrator review loop (per implementer)";
|
|
61
70
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
"Any issues?" [shape=diamond];
|
|
70
|
-
"Fix all issues" [shape=box];
|
|
71
|
-
"Re-review failed aspects (parallel sub-Task)" [shape=box];
|
|
72
|
-
"Report results" [shape=box];
|
|
71
|
+
subgraph cluster_parallel_reviewers {
|
|
72
|
+
label="Parallel reviewer Task calls (single message)";
|
|
73
|
+
style=dashed;
|
|
74
|
+
"Task: spec reviewer" [shape=box];
|
|
75
|
+
"Task: quality reviewer" [shape=box];
|
|
73
76
|
}
|
|
77
|
+
|
|
78
|
+
"Any issues?" [shape=diamond];
|
|
79
|
+
"Task: implementer fix" [shape=box];
|
|
80
|
+
"Re-review (parallel Task calls)" [shape=box];
|
|
74
81
|
}
|
|
75
82
|
}
|
|
76
83
|
|
|
77
84
|
"More batches?" [shape=diamond];
|
|
85
|
+
"Batch integration check (typecheck + lint)" [shape=box];
|
|
78
86
|
"Task: final review for entire implementation" [shape=box];
|
|
79
87
|
"Done" [shape=ellipse];
|
|
80
88
|
|
|
@@ -84,17 +92,17 @@ digraph process {
|
|
|
84
92
|
"Questions?" -> "Return questions to orchestrator" [label="yes"];
|
|
85
93
|
"Return questions to orchestrator" -> "Re-launch with answers";
|
|
86
94
|
"Re-launch with answers" -> "Implement the task";
|
|
87
|
-
"Questions?" -> "
|
|
88
|
-
"
|
|
89
|
-
"
|
|
90
|
-
"
|
|
91
|
-
"
|
|
92
|
-
"
|
|
93
|
-
"Re-review
|
|
94
|
-
"
|
|
95
|
-
"
|
|
96
|
-
"
|
|
97
|
-
"
|
|
95
|
+
"Questions?" -> "Commit and report" [label="no"];
|
|
96
|
+
"Commit and report" -> "Task: spec reviewer";
|
|
97
|
+
"Commit and report" -> "Task: quality reviewer";
|
|
98
|
+
"Task: spec reviewer" -> "Any issues?";
|
|
99
|
+
"Task: quality reviewer" -> "Any issues?";
|
|
100
|
+
"Any issues?" -> "Task: implementer fix" [label="yes"];
|
|
101
|
+
"Task: implementer fix" -> "Re-review (parallel Task calls)";
|
|
102
|
+
"Re-review (parallel Task calls)" -> "Any issues?";
|
|
103
|
+
"Any issues?" -> "Batch integration check (typecheck + lint)" [label="no"];
|
|
104
|
+
"Batch integration check (typecheck + lint)" -> "More batches?";
|
|
105
|
+
"More batches?" -> "Implement the task" [label="yes, next batch"];
|
|
98
106
|
"More batches?" -> "Task: final review for entire implementation" [label="no"];
|
|
99
107
|
"Task: final review for entire implementation" -> "Done";
|
|
100
108
|
}
|
|
@@ -122,46 +130,30 @@ Example: 5 tasks
|
|
|
122
130
|
Batch 3: [Task 4] — depends on Task 1
|
|
123
131
|
```
|
|
124
132
|
|
|
125
|
-
##
|
|
126
|
-
|
|
127
|
-
Each task agent receives a prompt combining implementation + review instructions:
|
|
133
|
+
## Implementer Prompt
|
|
128
134
|
|
|
129
|
-
|
|
130
|
-
You are implementing and reviewing Task N: [task name]
|
|
135
|
+
Each implementer receives a prompt based on `./implementer-prompt.md`. Fill in all `[bracketed]` sections before dispatching.
|
|
131
136
|
|
|
132
|
-
##
|
|
137
|
+
## Reviewer Dispatch
|
|
133
138
|
|
|
134
|
-
|
|
139
|
+
After an implementer completes and reports, the orchestrator dispatches reviewers:
|
|
135
140
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
4. Self-review: did I implement everything? Did I over-build?
|
|
146
|
-
5. Commit your work (record the BASE_SHA before and HEAD_SHA after)
|
|
147
|
-
6. Launch TWO parallel sub-Tasks (spec review + quality review):
|
|
148
|
-
- Sub-Task 1: spec reviewer — send spec-reviewer-prompt.md based prompt
|
|
149
|
-
- Sub-Task 2: quality reviewer — send code-quality-reviewer-prompt.md based prompt, include BASE_SHA and HEAD_SHA
|
|
150
|
-
7. If either reviewer finds issues → fix them → re-review only failed aspects (parallel sub-Tasks again)
|
|
151
|
-
8. Repeat until both reviewers approve
|
|
152
|
-
9. Report back with: what you implemented, test results, files changed, commit SHA, review outcomes
|
|
153
|
-
|
|
154
|
-
If you have questions about requirements — return them immediately WITHOUT implementing. Don't guess.
|
|
155
|
-
If you encounter unexpected issues mid-implementation — ask rather than guess.
|
|
156
|
-
|
|
157
|
-
Work from: [directory]
|
|
158
|
-
```
|
|
141
|
+
1. Record the implementer's commit SHA and files changed from its report
|
|
142
|
+
2. Dispatch TWO parallel Task calls (single message):
|
|
143
|
+
- spec reviewer — fill `./spec-reviewer-prompt.md` with task requirements + implementer report
|
|
144
|
+
- quality reviewer — fill `./code-quality-reviewer-prompt.md` with implementer report + BASE_SHA/HEAD_SHA
|
|
145
|
+
3. If either reviewer returns CHANGES_NEEDED:
|
|
146
|
+
- Re-dispatch implementer with fix instructions (all issues from both reviewers combined)
|
|
147
|
+
- After fix, re-dispatch only the failed reviewers (parallel Task calls)
|
|
148
|
+
- Repeat until both approve
|
|
149
|
+
4. Proceed to next task or batch
|
|
159
150
|
|
|
160
151
|
## Prompt Templates
|
|
161
152
|
|
|
162
|
-
- `./implementer-prompt.md` —
|
|
163
|
-
- `./spec-reviewer-prompt.md` — spec compliance review
|
|
164
|
-
- `./code-quality-reviewer-prompt.md` — code quality review
|
|
153
|
+
- `./implementer-prompt.md` — implementer instructions
|
|
154
|
+
- `./spec-reviewer-prompt.md` — spec compliance review prompt
|
|
155
|
+
- `./code-quality-reviewer-prompt.md` — code quality review prompt
|
|
156
|
+
- `./final-review-prompt.md` — final integration review prompt
|
|
165
157
|
|
|
166
158
|
## Example Workflow
|
|
167
159
|
|
|
@@ -181,56 +173,47 @@ You: Using sd-plan-dev to execute this plan.
|
|
|
181
173
|
Batch 1: [Task 1, Task 2, Task 5]
|
|
182
174
|
Batch 2: [Task 3, Task 4]
|
|
183
175
|
|
|
184
|
-
--- Batch 1: parallel ---
|
|
176
|
+
--- Batch 1: parallel implementers ---
|
|
185
177
|
|
|
186
|
-
[3 parallel Task calls in single message]
|
|
178
|
+
[3 parallel implementer Task calls in single message]
|
|
187
179
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
→ Done
|
|
180
|
+
Implementer 1: Implemented validator, tests 5/5 pass → committed
|
|
181
|
+
Implementer 2: "Should auth use JWT or session?" (question returned)
|
|
182
|
+
Implementer 5: Implemented endpoints, tests 3/3 pass → committed
|
|
192
183
|
|
|
193
|
-
|
|
194
|
-
|
|
184
|
+
[Answer Implementer 2 question: "JWT"]
|
|
185
|
+
[Re-launch Implementer 2 with answer]
|
|
186
|
+
Implementer 2: Implemented auth hook with JWT, tests 4/4 pass → committed
|
|
195
187
|
|
|
196
|
-
|
|
197
|
-
- Implemented endpoints, tests 3/3 pass
|
|
198
|
-
- Parallel sub-Tasks: spec ✅, quality: Issues (magic number)
|
|
199
|
-
- Fixed magic number
|
|
200
|
-
- Parallel re-review: quality ✅
|
|
201
|
-
→ Done
|
|
188
|
+
[Orchestrator dispatches reviewers for each completed implementer]
|
|
202
189
|
|
|
203
|
-
|
|
204
|
-
|
|
190
|
+
Task 1 reviews: [parallel] spec ✅, quality ✅ → Done
|
|
191
|
+
Task 2 reviews: [parallel] spec ✅, quality ✅ → Done
|
|
192
|
+
Task 5 reviews: [parallel] spec ✅, quality ❌ (magic number)
|
|
193
|
+
→ Re-dispatch Implementer 5 to fix → committed
|
|
194
|
+
→ Re-review quality ✅ → Done
|
|
205
195
|
|
|
206
|
-
|
|
207
|
-
- Implemented auth hook with JWT, tests 4/4 pass
|
|
208
|
-
- Parallel sub-Tasks: spec ✅, quality ✅
|
|
209
|
-
→ Done
|
|
196
|
+
[Batch 1 complete → integration check]
|
|
210
197
|
|
|
211
|
-
|
|
198
|
+
--- Batch 2: parallel implementers ---
|
|
212
199
|
|
|
213
|
-
|
|
200
|
+
[2 parallel implementer Task calls in single message]
|
|
214
201
|
|
|
215
|
-
|
|
202
|
+
Implementer 3: Implemented login component → committed
|
|
203
|
+
Implementer 4: Updated validator → committed
|
|
216
204
|
|
|
217
|
-
|
|
218
|
-
- Implemented login component using Task 2's auth hook
|
|
219
|
-
- Parallel sub-Tasks: spec ❌ (missing error state), quality ✅
|
|
220
|
-
- Fixed error state
|
|
221
|
-
- spec re-review ✅
|
|
222
|
-
→ Done
|
|
205
|
+
[Orchestrator dispatches reviewers]
|
|
223
206
|
|
|
224
|
-
Task
|
|
225
|
-
-
|
|
226
|
-
|
|
227
|
-
|
|
207
|
+
Task 3 reviews: [parallel] spec ❌ (missing error state), quality ✅
|
|
208
|
+
→ Re-dispatch Implementer 3 to fix → committed
|
|
209
|
+
→ Re-review spec ✅ → Done
|
|
210
|
+
Task 4 reviews: [parallel] spec ✅, quality ✅ → Done
|
|
228
211
|
|
|
229
|
-
[Batch 2 complete]
|
|
212
|
+
[Batch 2 complete → integration check]
|
|
230
213
|
|
|
231
214
|
--- Final ---
|
|
232
215
|
|
|
233
|
-
[Task
|
|
216
|
+
[Task: final review for entire implementation]
|
|
234
217
|
Final reviewer: All requirements met, ready to merge
|
|
235
218
|
|
|
236
219
|
Done!
|
|
@@ -256,26 +239,26 @@ This catches cross-task integration issues early — especially when the next ba
|
|
|
256
239
|
- Proceed with unfixed issues
|
|
257
240
|
- Put tasks with file overlap in the same parallel batch
|
|
258
241
|
- Skip dependency analysis
|
|
259
|
-
- Make
|
|
242
|
+
- Make implementer read plan file directly (provide full text instead)
|
|
260
243
|
- Skip scene-setting context
|
|
261
244
|
- Accept "close enough" on spec compliance
|
|
262
245
|
- Skip review loops (issue found → fix → re-review)
|
|
263
246
|
- Skip batch integration checks between batches
|
|
264
247
|
- Use `run_in_background: true` on Task calls (use foreground parallel instead)
|
|
265
248
|
|
|
266
|
-
**If
|
|
249
|
+
**If implementer returns questions:**
|
|
267
250
|
|
|
268
251
|
- Answer clearly and completely
|
|
269
|
-
- Re-launch that
|
|
270
|
-
- Other parallel
|
|
252
|
+
- Re-launch that implementer with answers included
|
|
253
|
+
- Other parallel implementers continue unaffected
|
|
271
254
|
|
|
272
255
|
**If reviewers find issues:**
|
|
273
256
|
|
|
274
|
-
-
|
|
275
|
-
-
|
|
257
|
+
- Orchestrator re-dispatches implementer with all issues from both reviewers combined
|
|
258
|
+
- After fix, re-dispatch only the failed reviewers (parallel Task calls)
|
|
276
259
|
- Repeat until both approved
|
|
277
260
|
|
|
278
|
-
**If
|
|
261
|
+
**If implementer fails or times out:**
|
|
279
262
|
|
|
280
263
|
- Do NOT silently proceed — the affected files may be in an indeterminate state
|
|
281
264
|
- Check if other tasks in the same batch depend on the failed task's output
|
|
@@ -288,5 +271,5 @@ This catches cross-task integration issues early — especially when the next ba
|
|
|
288
271
|
**Related skills:**
|
|
289
272
|
|
|
290
273
|
- **sd-plan** — creates the plan this skill executes
|
|
291
|
-
- **sd-tdd** —
|
|
274
|
+
- **sd-tdd** — implementers follow TDD
|
|
292
275
|
- **sd-worktree** — branch isolation for worktree-based workflows
|