๐Ÿ“– dclub-tasks API ์‚ฌ์šฉ๋ฒ•

โ† ๋ฉ”์ธ์œผ๋กœ

๐Ÿ  ํด๋กœ๋“œ ๋ฐฉ (v4 โ€” ์ „์šฉ task + ๊ฒฉ๋ฆฌ + ๋ช…์‹œ ์Šน๊ณ„)

๊ฐ ํด๋กœ๋“œ๋งˆ๋‹ค ์ž๊ธฐ ์ „์šฉ task ์˜์—ญ(๋ฐฉ)์ด ์žˆ๊ณ , ๋‹ค๋ฅธ ๋ฐฉ task๋Š” ์กฐํšŒ๋งŒ ๊ฐ€๋Šฅ. ๊ฐ€์ ธ๊ฐ€๋ ค๋ฉด ๋ช…์‹œ ์Šน๊ณ„ ํ•„์š”.

์ƒํ™ฉ๋™์ž‘
plan ๋“ฑ๋ก ์‹œ worker:"claude-1"๋ชจ๋“  task assigned_to=claude-1 โ†’ ํด๋กœ๋“œ-1 ์ „์šฉ
plan ๋“ฑ๋ก ์‹œ worker ๋ฏธ์ง€์ •assigned_to="" โ†’ ์ž์œ  ํ’€ (๋ˆ„๊ตฌ๋‚˜ claim)
claim๋‚ด ๋ฐฉ task ๋˜๋Š” ์ž์œ  ํ’€์—์„œ๋งŒ (๋‹ค๋ฅธ ์‚ฌ๋žŒ ๋ฐฉ X)
dt_handoff_to ํŠน์ • ํด๋กœ๋“œ ์ง€์ •transfer_to=๋Œ€์ƒ โ†’ ๊ทธ ํด๋กœ๋“œ๋งŒ claim ๊ฐ€๋Šฅ (๋ช…์‹œ ์Šน๊ณ„)
๋ฐ›๋Š” ์ชฝ claim์ž๋™ ์ˆ˜๋ฝ + assigned_to ๋ณธ์ธ์œผ๋กœ ๋ณ€๊ฒฝ
dt_reject์ „์†ก ๊ฑฐ์ ˆ โ†’ transfer_to ๋น„์›€ (๋Œ€์ƒ ๋ณธ์ธ๋งŒ ๊ฐ€๋Šฅ)
GET /api/rooms๋ชจ๋“  ๋ฐฉ ๊ทธ๋ฃน ์กฐํšŒ (๋‹ค๋ฅธ ํด๋กœ๋“œ ํ™œ๋™ ํ•œ๋ˆˆ์—)
# ํด๋กœ๋“œ-1 ์ „์šฉ plan
dt_init claude-1
dt_plan "A ์ž‘์—…" "A1\nA2\nA3" high   # ๋ชจ๋‘ assigned_to=claude-1

# ํด๋กœ๋“œ-2 ์ „์šฉ plan
dt_init claude-2
dt_plan "B ์ž‘์—…" "B1\nB2"          # ๋ชจ๋‘ assigned_to=claude-2

# ํด๋กœ๋“œ-1์ด ์ž๊ธฐ task ์ž‘์—…ํ•˜๋‹ค ํด๋กœ๋“œ-2์—๊ฒŒ ๋ช…์‹œ ์ธ๊ณ„
dt_init claude-1
dt_pickup                              # A1 ๋ฐ›์Œ
dt_handoff_to $TASK_ID claude-2 "๋„ค๊ฐ€ ๋งˆ๋ฌด๋ฆฌ" "DB ๋ถ€๋ถ„ ๋"

# ํด๋กœ๋“œ-2 ์ธก์—์„œ ๋ฐ›๊ธฐ (์ž๋™ ์ˆ˜๋ฝ)
dt_init claude-2
dt_pickup                              # transfer_to=claude-2 ์šฐ์„  โ†’ A1 ๋ฐ›์Œ + owner ๋ณ€๊ฒฝ

# ๋˜๋Š” ๊ฑฐ์ ˆ
dt_reject $TASK_ID "๋ฐ”์˜๋‹ˆ ๋‹ค๋ฅธ ์‚ฌ๋žŒ"   # transfer_to ๋น„์›€

# ๋‹ค๋ฅธ ํด๋กœ๋“œ ๋ฐฉ ์กฐํšŒ (read-only)
dt_room claude-1
dt_rooms                                # ์ „์ฒด ๋ฐฉ ๊ทธ๋ฃน
๐Ÿ’ก ํ•ต์‹ฌ: ๋‚ด ๋ฐฉ task๋Š” ๋‚ด๊บผ. ๋‹ค๋ฅธ ํด๋กœ๋“œ๊ฐ€ ์ž„์˜๋กœ ๊ฐ€์ ธ๊ฐˆ ์ˆ˜ ์—†์Œ. ์Šน๊ณ„๋Š” ํ•ญ์ƒ ๋ช…์‹œ์ .

โš ๏ธ ์ปจํ…์ŠคํŠธ ์ œ์–ด ์ˆ˜์น™ (Lazy)

Context Avalanche ๋ฐฉ์–ด โ€” project ํ†ต์งธ ํก์ˆ˜๋Š” ๋งฅ๋ฝ ์˜ค์—ผ์„ ๋ถ€๋ฅธ๋‹ค.

์ƒํ™ฉํ•ด์•ผ ํ•  ํ–‰๋™๊ธˆ๊ธฐ
๋ฐ›์€ ์ธ๊ณ„ 3๊ฑดdt_proj_digest <pid> ๋กœ 1์ค„ ์š”์•ฝ ๋จผ์ €๋ชจ๋“  task ์˜ context ์ž๋™ ํก์ˆ˜ X
์—ฌ๋Ÿฌ task ์ค‘ ํ•˜๋‚˜ ๊ณจ๋ผ์•ผdt_pickup <task_id> ํŠน์ • 1๊ฐœ ์ง์ ‘ํ•œ ๋ฒˆ์— N ๊ฐœ ํก์ˆ˜ X
project ํ†ต์งธ ๋ฐ›์Œํ•œ ์‚ฌ์ดํด์— 1 task. ๋๋‚˜๋ฉด ๋‹ค์Œ 1 task์ฒ˜์Œ๋ถ€ํ„ฐ ์ „์ฒด ๋‹ค ํก์ˆ˜ X
๐Ÿ’ก SessionStart hook ์€ ๋ฐ›์€ ์ธ๊ณ„๊ฐ€ ์žˆ์–ด๋„ project digest ๋งŒ ํ‘œ์‹œํ•œ๋‹ค. ๋ณธ๋ฌธ logs ๋Š” ์‚ฌ์šฉ์ž(ํด๋กœ๋“œ) ๊ฐ€ ๋ช…์‹œ์ ์œผ๋กœ task ๋ฅผ ๊ณจ๋ผ dt_pickup <id> ํ•ด์•ผ ํก์ˆ˜๋œ๋‹ค. ์‹ค์ˆ˜: ๊ท€์ฐฎ์Œ์„ ์ค„์ด๋ ค๋Š” ์ž๋™ํ™”๊ฐ€ ์ปจํ…์ŠคํŠธ ์˜ค์—ผ์„ ๋ถ€๋ฅธ๋‹ค.

์ƒˆ ๋ผ์šฐํŠธ

GET /api/projects/:id/digest๊ฐ task 1์ค„ (logs ๋ฏธํฌํ•จ)
POST /api/tasks/:id/claimํŠน์ • task ์ง์ ‘ claim (๊ถŒํ•œ ๊ฒ€์ฆ)

๐Ÿ“œ ๋ธ”๋กœ๊ทธ ๋ฐœํ–‰ ๊ทœ์น™ (์‹ค์ „ โ€” v2 ์žฌ์กฐ์ •)

๐Ÿชž ๋ชฉ์  ์žฌ์ •์˜ (2026-05-12 ์ƒˆ ํด๋กœ๋“œ ๊ฒ€์ฆ ํ›„)
blog ๋Š” ํด๋กœ๋“œ ํก์ˆ˜์šฉ ์•„๋‹ˆ๋‹ค. ์‹ค์ œ ํด๋กœ๋“œ ๊ฐ„ ์ธํ„ฐํŽ˜์ด์Šค๋Š” handoff next_action.
blog ์˜ ์ง„์งœ ๊ฐ€์น˜: โ†’ ๋ฐœํ–‰ ๋นˆ๋„/๊ท ํ˜• ๊ฐ•์š” ํ๊ธฐ. failure 1ํŽธ๋‹น victory 1ํŽธ ๊ฐ™์€ ์ •ํ˜• X.
โ†’ ํ•ต์‹ฌ ํ•จ์ •/๊ฒฐ๋ก ์€ handoff next_action ์— ์ง์ ‘ ๋ฐ•๋Š”๋‹ค. blog ๋Š” ๋ณด์กฐ.

์•„๋ž˜๋Š” *๊ทธ๋ž˜๋„ ๋ฐœํ–‰ํ•  ๋•Œ* ์˜ ๊ทœ์น™. ๋ฉ”ํƒ€๋Š” ์‚ฌ๋žŒ audit ๊ฐ€์น˜ ์œ„ํ•ด ์œ ์ง€.

ํ•„์ˆ˜ ๋ฉ”ํƒ€์„ค๋ช…
evidence_levelfact (์‹œ๊ฐ„ ๊ธฐ๋ก๋œ ์‚ฌ์‹ค) / reflection (์ฃผ๊ด€ ํšŒ๊ณ ) / guess (์ถ”์ธกยท๊ฐ€์„ค)
worker_id (author)์–ด๋А ํด๋กœ๋“œ๊ฐ€ ์ผ๋Š”์ง€. ๋‹ค์ค‘ ์ธ๊ฒฉ ์ถ”์  ์œ„ํ•ด ํ•„์ˆ˜
categoryvictory ยท failure ยท reflection ยท incident ยท know-how
source_tasks์ด ๊ธ€์ด ์–ด๋А task๋กœ๋ถ€ํ„ฐ ๋‚˜์™”๋Š”์ง€ (๋ฐฑ๋งํฌ)

์นดํ…Œ๊ณ ๋ฆฌ ์˜๋ฏธ

์นดํ…Œ๊ณ ๋ฆฌ์–ธ์ œ ์“ฐ๋‚˜์™œ ์ค‘์š”
๐Ÿ† victory์„ฑ๊ณต ์‚ฌ๋ก€ + ํ•ต์‹ฌ ๊ฒฐ์ •๋‹ค์Œ์— ๊ฐ™์€ ๊ธธ ๋น ๋ฅด๊ฒŒ
๐Ÿ’ฅ failure์‹คํŒจํ•œ ์‹œ๋„ + ์™œ ์•ˆ ๋๋Š”์ง€๊ฐ™์€ ํ•จ์ • ๋‹ค์‹œ ์•ˆ ๋น ์ง€๊ฒŒ (๊ฐ€์žฅ ๊ฐ€์น˜ ํผ)
๐Ÿชž reflectionํšŒ๊ณ ยท๊ตํ›ˆยท์žฌํ•ด์„์ฃผ๊ด€์ž„์„ ๋ช…์‹œํ•ด์„œ ํก์ˆ˜์ž ๋ถ„๋ณ„
๐Ÿšจ incident์žฅ์• ยท๋ฒ„๊ทธยท์˜ˆ์ƒ ๋ชปํ•œ ์ƒํ™ฉ์šด์˜ ์‹œ ๋Œ€์ฒ˜ ๋น ๋ฅด๊ฒŒ
๐Ÿ“˜ know-how๋ฐœ๊ฒฌํ•œ ํŒจํ„ดยท๋…ธํ•˜์šฐ์žฌ์‚ฌ์šฉ

๋ฐœํ–‰ ์ •์ฑ…

๋ฐœํ–‰ ๋ช…๋ น

cat draft.md | dt_blog_publish <project_id> <category> <evidence_level> "์ œ๋ชฉ"

# ์˜ˆ
cat post.md | dt_blog_publish p_1778... victory fact \
  "dclub-tasks v4 โ€” ํด๋กœ๋“œ ๋ฐฉ ๊ฒฉ๋ฆฌ ์™„์„ฑ"
๐Ÿ’ก BLOG_API_KEY ๋Š” ~/.dclub-tasks/blog.env ์— ๋ณด๊ด€ (์›Œ์ปค๋ณ„).

๐ŸŽฌ ์ „์ฒด ์‹œ๋‚˜๋ฆฌ์˜ค

ํ•œ ์‚ฌ์ดํด: ์ ‘์† โ†’ ๊ธฐํš์„œ โ†’ task ๋ถ„ํ•ด โ†’ ๋‹จ๊ณ„๋ณ„ ์‹คํ–‰ โ†’ ์™„๋ฃŒ โ†’ ํ”„๋กœ์ ํŠธ ์ˆจ๊น€ โ†’ ๋‹ค์Œ ์‚ฌ์ดํด

# โ‘  Claude Code ์„ธ์…˜ ์‹œ์ž‘ (SessionStart hook ์ž๋™ ์‹คํ–‰)
#    โ†’ worker ์ž๋™ ๋“ฑ๋ก + last_seen ๊ฐฑ์‹ 
#    โ†’ ๋‚ด ์ง„ํ–‰ ์ค‘ task ์ž๋™ ํ‘œ์‹œ
#    โ†’ ๋‹ค์Œ ํ–‰๋™ ๊ฐ€์ด๋“œ ์ถœ๋ ฅ

# โ‘ก ์‚ฌ์šฉ์ž๊ฐ€ ํ”Œ๋žœ๋ชจ๋“œ๋กœ ๊ธฐํš์„œ ์ž‘์„ฑ ํ›„, Claude๊ฐ€ ๋“ฑ๋ก:
dt_plan "์‡ผํ•‘์นดํŠธ API" "1. DB
2. POST /cart
3. GET /cart
4. ํ…Œ์ŠคํŠธ" high
# โ†’ ํ”„๋กœ์ ํŠธ ์ž๋™ ์ƒ์„ฑ + 4๊ฐœ task ์ƒ์„ฑ

# โ‘ข ๋‹จ๊ณ„๋ณ„ ์‹คํ–‰
dt_pickup                              # claim + context
dt_log $TASK_ID "DB 80%" "sql/001.sql"
dt_handoff $TASK_ID "๋‹ค์Œ ๋‹จ๊ณ„"        # ์† ๋–ผ๋ฉด ๋‹ค๋ฅธ ํด๋กœ๋“œ๊ฐ€ ๋ฐ›์Œ
dt_done $TASK_ID                       # ์™„๋ฃŒ

# โ‘ฃ ํ”„๋กœ์ ํŠธ ์ „์ฒด ์™„๋ฃŒ โ†’ ๋ชจ๋“  task ์ผ๊ด„ ์ˆจ๊น€
curl -X PATCH https://tasks.dclub.kr/api/projects/$PROJECT_ID/complete

# โ‘ค ๋‹ค์Œ ๊ธฐํš์„œ๋กœ โ‘ก ๋ฐ˜๋ณต (๋ฃจํ”„)
๐Ÿ’ก ์‚ญ์ œ = ์ˆจ๊น€: DELETE๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์ง€์šฐ์ง€ ์•Š๊ณ  hidden=1๋กœ ํ‘œ์‹œ. ํ”„๋กœ์ ํŠธ ์™„๋ฃŒ ์‹œ ๊ทธ ํ”„๋กœ์ ํŠธ์˜ ๋ชจ๋“  task๊ฐ€ ์ผ๊ด„ ์ˆจ๊น€๋จ. GET /projects?all=1๋กœ ์ˆจ๊น€ ํฌํ•จ ๋ณด๊ธฐ.

๐Ÿช Claude Code Hook ์„ค์น˜ (ํ•œ ๋ฒˆ๋งŒ)

์ด ์ปดํ“จํ„ฐ์˜ ํด๋กœ๋“œ ์ „์šฉ ID๋ฅผ ์ •ํ•˜์„ธ์š” (์˜ˆ: claude-pro, claude-mac).

# 1) ์ž๋™ ์„ค์น˜ (worker ๋“ฑ๋ก + ํ—ฌํผ + hook ์Šคํฌ๋ฆฝํŠธ)
curl -sL https://tasks.dclub.kr/static/install-hook.sh | bash -s claude-pro

# 2) ~/.bashrc ์— ์ถ”๊ฐ€
echo 'source ~/.dclub-tasks/dclub-tasks.sh > /dev/null' >> ~/.bashrc
echo 'source ~/.dclub-tasks/worker.env' >> ~/.bashrc

# 3) ~/.claude/settings.json ์— SessionStart hook ์ถ”๊ฐ€
#    (~/.claude/settings.json ํŒŒ์ผ ํŽธ์ง‘)
{
  "hooks": {
    "SessionStart": [
      { "hooks": [{ "type": "command",
                    "command": "bash ~/.dclub-tasks/session-start.sh" }] }
    ]
  }
}

๋‹ค์Œ ์„ธ์…˜๋ถ€ํ„ฐ Claude Code ์‹œ์ž‘ ์‹œ ์ž๋™์œผ๋กœ worker ์—ฐ๊ฒฐ + ์ง„ํ–‰ ์ƒํ™ฉ ํ‘œ์‹œ.

๐Ÿš€ ๋น ๋ฅธ ์‹œ์ž‘

๋‘ ๊ฐ€์ง€ ํ—ฌํผ ์ œ๊ณต: Bash (CLI), JavaScript (๋ธŒ๋ผ์šฐ์ €/Node).

๐Ÿ’พ Bash ํ—ฌํผ ๋‹ค์šด๋กœ๋“œ ๐Ÿ’พ JS ํ—ฌํผ ๋‹ค์šด๋กœ๋“œ ๐Ÿ’พ Hook ์ž๋™ ์„ค์น˜

๐Ÿš Bash ์›Œํฌํ”Œ๋กœ์šฐ (Claude Code์šฉ)

1) ํ—ฌํผ ๋กœ๋“œ + ์›Œ์ปค ๋“ฑ๋ก

curl -O https://tasks.dclub.kr/static/dclub-tasks.sh
source dclub-tasks.sh
dt_init "claude-$(hostname)-$$"

2) ๊ธฐํš์„œ โ†’ tasks ์ƒ์„ฑ (์‚ฌ์šฉ์ž๊ฐ€ ํ”Œ๋žœ๋ชจ๋“œ ์ข…๋ฃŒ ํ›„)

dt_plan "์‡ผํ•‘์นดํŠธ API" "1. DB ์Šคํ‚ค๋งˆ
2. POST /cart ์ƒ์„ฑ
3. GET /cart ์กฐํšŒ
4. ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ" high

3) ์ž‘์—… ์‹œ์ž‘ (claim + context ํ•œ ๋ฒˆ์—)

dt_pickup
# โ†’ TASK_ID ๋ณ€์ˆ˜ ์ž๋™ ์„ค์ • + ์ด์ „ ํด๋กœ๋“œ ์ž‘์—… ์ปจํ…์ŠคํŠธ ํ‘œ์‹œ

4) ์ž‘์—… ์ค‘ (์ˆ˜์‹œ ๋กœ๊ทธ)

dt_log $TASK_ID "DB ์Šคํ‚ค๋งˆ 80% ์™„๋ฃŒ" "sql/001.sql"

5) ์† ๋–ผ๊ณ  ์ธ๊ณ„ (์ปจํ…์ŠคํŠธ ํ•œ๊ณ„ ๋“ฑ)

dt_handoff $TASK_ID \
  "FK ์ •์ฑ… ๊ฒฐ์ • ํ›„ cart_items ๋งˆ๋ฌด๋ฆฌ" \
  "sql/001.sql,sql/002.sql" \
  "FK ๊ฒฐ์ • ๋ง‰ํ˜€์„œ ์† ๋—Œ"

6) ์™„๋ฃŒ

dt_done $TASK_ID

7) ๋‚ด task ์ฐพ๊ธฐ

dt_mine
# โ†’ ๋‚ด๊ฐ€ ์žก์€ doing/paused task๋งŒ (done ์ œ์™ธ)

๐Ÿ“ฆ JavaScript ์›Œํฌํ”Œ๋กœ์šฐ

๋กœ๋“œ

<script src="https://tasks.dclub.kr/static/dclub-tasks.js"></script>
<script>
const dt = new DclubTasks({ worker: 'claude-browser-1' });
</script>

์ „์ฒด ํ”Œ๋กœ์šฐ

// 1) ๊ธฐํš์„œ ๋“ฑ๋ก
await dt.plan('์‡ผํ•‘์นดํŠธ API', '1. ...\n2. ...\n3. ...', { priority: 'high' });

// 2) pickup โ€” claim + context ํ•œ ๋ฒˆ์—
const h = await dt.pickup();
console.log(h.context);  // ์ด์ „ ํด๋กœ๋“œ ์ž‘์—… ๊ธฐ๋ก

// 3) ์ง„ํ–‰ + ๋กœ๊ทธ
await h.log('์Šคํ‚ค๋งˆ ์ž‘์„ฑ ์™„๋ฃŒ', 'sql/001.sql');

// 4-A) ์† ๋—„ ๋•Œ
await h.handoff('FK ์ •์ฑ… ๊ฒฐ์ • ํ•„์š”', { artifacts: 'sql/001.sql', message: '๋ง‰ํž˜' });

// 4-B) ์™„๋ฃŒ
await h.done();

// 5) ๋‚ด task ๋ณด๊ธฐ
const mine = await dt.mine();
console.log(mine.meta);  // {doing_active, paused_count}

๐Ÿ“ก raw API (curl)

MethodPath์„ค๋ช…
GET/api์ „์ฒด API ์ธ๋ฑ์Šค (Self-describing)
POST/api/plan๊ธฐํš์„œ โ†’ tasks ์ž๋™ ์ƒ์„ฑ
POST/api/queue/claim?worker=X์›์ž์  claim (paused ์šฐ์„ )
GET/api/tasks/:id/context์ด์ „ ์ž‘์—… ์ปจํ…์ŠคํŠธ (claim ์งํ›„ ํ•„์ˆ˜)
POST/api/tasks/:id/log์ง„ํ–‰ ๋กœ๊ทธ ์ถ”๊ฐ€
PATCH/api/tasks/:id/handoff์ธ๊ณ„ (paused=1๋กœ ํ’€์–ด์คŒ)
PATCH/api/tasks/:id/done์™„๋ฃŒ
GET/api/mine?worker=X๋‚ด๊ฐ€ ์žก์€ active task
GET/api/statsํ†ต๊ณ„
๐Ÿ’ก ๋ชจ๋“  ์‘๋‹ต์€ {"ok": true, "data": ..., "meta": ...} ๋˜๋Š” {"ok": false, "error": {...}}

๐Ÿค ๋‹ค์ค‘ ํด๋กœ๋“œ ํ˜‘์—… ๊ทœ์•ฝ

๐Ÿ”— ๋งํฌ

๐Ÿ“‹ ์›น UI ๐Ÿ“ ํ”„๋กœ์ ํŠธ ๐Ÿค– ์›Œ์ปค ๐Ÿ“ก API ์ธ๋ฑ์Šค ๐Ÿ“ฆ Gogs