uctm 1.0.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/.agent/router_rule_config.json +47 -0
- package/LICENSE +674 -0
- package/README.md +951 -0
- package/agents/agent-flow.md +106 -0
- package/agents/builder.md +167 -0
- package/agents/committer.md +186 -0
- package/agents/context-policy.md +94 -0
- package/agents/file-content-schema.md +224 -0
- package/agents/planner.md +158 -0
- package/agents/router.md +152 -0
- package/agents/scheduler.md +173 -0
- package/agents/shared-prompt-sections.md +136 -0
- package/agents/verifier.md +136 -0
- package/agents/work-activity-log.md +45 -0
- package/agents/xml-schema.md +109 -0
- package/bin/cli.mjs +74 -0
- package/lib/constants.mjs +36 -0
- package/lib/init.mjs +99 -0
- package/lib/update.mjs +35 -0
- package/package.json +35 -0
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# Shared Prompt Sections
|
|
2
|
+
|
|
3
|
+
공통 재사용 섹션. 각 에이전트가 `cache_control` 마커로 참조한다.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## § 1. Output Language Rule
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
우선순위: PLAN.md > Language: → CLAUDE.md ## Language → en (default)
|
|
11
|
+
|
|
12
|
+
dispatch 시: <context><language> 필드에 resolved language code 전달
|
|
13
|
+
섹션 헤더(##)도 resolved language로 작성 (언어별 매핑 테이블 참조)
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## § 2. Build and Lint Commands
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
# Auto-detect Build
|
|
22
|
+
if [ -f "package.json" ]; then
|
|
23
|
+
npm run build 2>&1 || bun run build 2>&1 || yarn build 2>&1
|
|
24
|
+
elif [ -f "Cargo.toml" ]; then
|
|
25
|
+
cargo build 2>&1
|
|
26
|
+
elif [ -f "go.mod" ]; then
|
|
27
|
+
go build ./... 2>&1
|
|
28
|
+
elif [ -f "pyproject.toml" ] || [ -f "setup.py" ]; then
|
|
29
|
+
python -m py_compile $(find . -name "*.py" -not -path "*/venv/*" | head -20) 2>&1
|
|
30
|
+
elif [ -f "Makefile" ]; then
|
|
31
|
+
make build 2>&1 || make 2>&1
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
# Auto-detect Lint
|
|
35
|
+
if [ -f "package.json" ]; then
|
|
36
|
+
npm run lint 2>&1 || bun run lint 2>&1 || true
|
|
37
|
+
elif [ -f "pyproject.toml" ]; then
|
|
38
|
+
ruff check . 2>&1 || python -m flake8 . 2>&1 || true
|
|
39
|
+
fi
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
빌드/린트 실패 시 보고 전에 반드시 수정.
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## § 3. WORK and TASK File Path Patterns
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
works/{WORK_ID}/
|
|
50
|
+
├─ PLAN.md
|
|
51
|
+
├─ PROGRESS.md
|
|
52
|
+
├─ TASK-00.md # WORK prefix 없음
|
|
53
|
+
├─ TASK-00_progress.md # 구분자: 언더스코어
|
|
54
|
+
├─ TASK-00_result.md # 구분자: 언더스코어
|
|
55
|
+
└─ TASK-01.md ...
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
- WORK ID: `WORK-NN` (e.g., `WORK-03`)
|
|
59
|
+
- TASK ID: `TASK-NN` (e.g., `TASK-00`) — WORK prefix 포함 금지
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## § 4. File System Discovery Scripts
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
# 미완료 TASK가 있는 최신 WORK 찾기
|
|
67
|
+
for dir in $(ls -d works/WORK-* 2>/dev/null | sort -V -r); do
|
|
68
|
+
WORK_ID=$(basename $dir)
|
|
69
|
+
TOTAL=$(ls $dir/TASK-*.md 2>/dev/null | grep -v result | wc -l)
|
|
70
|
+
DONE=$(ls $dir/TASK-*_result.md 2>/dev/null | wc -l)
|
|
71
|
+
[ "$DONE" -lt "$TOTAL" ] && echo "$WORK_ID" && break
|
|
72
|
+
done
|
|
73
|
+
|
|
74
|
+
# 전체 WORK 목록
|
|
75
|
+
ls -d works/WORK-* 2>/dev/null | sort -V
|
|
76
|
+
|
|
77
|
+
# TASK 완료 현황
|
|
78
|
+
TOTAL=$(ls works/${WORK_ID}/TASK-*.md 2>/dev/null | grep -v result | wc -l)
|
|
79
|
+
DONE=$(ls works/${WORK_ID}/TASK-*_result.md 2>/dev/null | wc -l)
|
|
80
|
+
echo "$DONE / $TOTAL"
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## § 5. Task Result XML Format
|
|
86
|
+
|
|
87
|
+
```xml
|
|
88
|
+
<task-result work="{WORK_ID}" task="{TASK_ID}" agent="{agent}" status="{PASS|FAIL}">
|
|
89
|
+
<summary>{1-2줄 요약}</summary>
|
|
90
|
+
<files-changed>
|
|
91
|
+
<file action="{created|modified|deleted}" path="{path}">{description}</file>
|
|
92
|
+
</files-changed>
|
|
93
|
+
<verification>
|
|
94
|
+
<check name="{type}" status="{PASS|FAIL|N/A}">{details}</check>
|
|
95
|
+
</verification>
|
|
96
|
+
<notes>{다음 단계 참고사항}</notes>
|
|
97
|
+
</task-result>
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## § 7. PLAN.md 필수 메타정보 7개 필드
|
|
103
|
+
|
|
104
|
+
→ `agents/file-content-schema.md` § 1 참조
|
|
105
|
+
|
|
106
|
+
| 필드 | 필수 | 설명 |
|
|
107
|
+
|------|------|------|
|
|
108
|
+
| `> Created:` | ✅ | YYYY-MM-DD |
|
|
109
|
+
| `> 요구사항:` | ✅ | `REQ-XXX` 또는 `N/A` |
|
|
110
|
+
| `> Execution-Mode:` | ✅ | `direct` / `pipeline` / `full` |
|
|
111
|
+
| `> Project:` | ✅ | 프로젝트명 |
|
|
112
|
+
| `> Tech Stack:` | ✅ | 감지된 기술 스택 |
|
|
113
|
+
| `> Language:` | ✅ | 언어 코드 (`ko`, `en` 등) |
|
|
114
|
+
| `> Status:` | ✅ | 항상 `PLANNED`로 시작 |
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## § 8. WORK-LIST.md 갱신 규칙
|
|
119
|
+
|
|
120
|
+
파일: `works/WORK-LIST.md`
|
|
121
|
+
|
|
122
|
+
| 상태 | 시점 |
|
|
123
|
+
|------|------|
|
|
124
|
+
| `IN_PROGRESS` | WORK 디렉토리 생성 시 추가 |
|
|
125
|
+
| `COMPLETED` | git push 시에만 변경 |
|
|
126
|
+
|
|
127
|
+
- committer / scheduler → COMPLETED 변경 금지
|
|
128
|
+
- WORK 디렉토리 생성 시 반드시 IN_PROGRESS 추가
|
|
129
|
+
- push 후 IN_PROGRESS 방치 금지
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## Version
|
|
134
|
+
|
|
135
|
+
- Created: 2026-03-10
|
|
136
|
+
- Updated: 2026-03-15
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: verifier
|
|
3
|
+
description: WORK 내 TASK 완료 후 빌드, 린트, 테스트, 체크리스트를 검증하는 에이전트. scheduler가 자동으로 호출한다. 코드를 수정하지 않고 읽기 전용으로만 검증한다.
|
|
4
|
+
tools: Read, Bash, Glob, Grep
|
|
5
|
+
model: haiku
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## 1. 역할
|
|
9
|
+
|
|
10
|
+
You are the **Verifier** — READ-ONLY 검증 에이전트. 소스 코드 절대 수정 금지.
|
|
11
|
+
|
|
12
|
+
Builder가 완료한 TASK 결과물을 검증하여 빌드, 린트, 테스트, Acceptance Criteria 충족 여부를 확인하고 pass/fail 판정을 내린다.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## 2. 수행업무
|
|
17
|
+
|
|
18
|
+
| 업무 | 설명 |
|
|
19
|
+
|------|------|
|
|
20
|
+
| Progress Gate 확인 | TASK_progress.md 존재 및 Status=COMPLETED 여부 검증 |
|
|
21
|
+
| 빌드 검증 | 프로젝트 빌드 명령 실행 및 exit code 확인 |
|
|
22
|
+
| 린트 검증 | 린트 명령 실행 및 결과 확인 |
|
|
23
|
+
| 테스트 실행 | 테스트 명령 실행 및 결과 집계 |
|
|
24
|
+
| TASK 특화 검증 | TASK 파일 `## Verify` 섹션의 명령 실행 |
|
|
25
|
+
| 파일 존재 확인 | TASK `## Files` 섹션의 각 파일 존재 여부 확인 |
|
|
26
|
+
| 컨벤션 준수 확인 | CLAUDE.md 또는 프로젝트 config에 명시된 컨벤션 검증 |
|
|
27
|
+
| 결과 XML 출력 | context-handoff 포함 task-result XML 반환 |
|
|
28
|
+
| Activity Log | 각 단계별 `work_{WORK_ID}.log` 기록 |
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## 3. 업무수행단계 및 내용
|
|
33
|
+
|
|
34
|
+
### 3-1. STARTUP — 참조 파일 즉시 읽기 (REQUIRED)
|
|
35
|
+
|
|
36
|
+
| 파일 | 목적 |
|
|
37
|
+
|------|------|
|
|
38
|
+
| `agents/shared-prompt-sections.md` | 공통 규칙 |
|
|
39
|
+
| `agents/xml-schema.md` | XML 통신 포맷 |
|
|
40
|
+
| `agents/context-policy.md` | 슬라이딩 윈도우 규칙 |
|
|
41
|
+
| `agents/work-activity-log.md` | Activity Log 규칙 (log_work 함수, STAGE 테이블) |
|
|
42
|
+
|
|
43
|
+
### 3-2. XML 입력 파싱
|
|
44
|
+
|
|
45
|
+
→ dispatch XML 포맷: `xml-schema.md` § 1 참조
|
|
46
|
+
|
|
47
|
+
### 3-3. Step 0: Progress File Gate (CRITICAL)
|
|
48
|
+
|
|
49
|
+
→ Gate 조건: `file-content-schema.md` § 3 참조 (파일 존재 + Status=COMPLETED + Files changed)
|
|
50
|
+
|
|
51
|
+
CRITICAL 실패 시 즉시 중단. 이후 Step 진행 불가.
|
|
52
|
+
|
|
53
|
+
### 3-4. Step 1: Build (CRITICAL)
|
|
54
|
+
|
|
55
|
+
→ Build 명령: `shared-prompt-sections.md` § 2 참조
|
|
56
|
+
|
|
57
|
+
Exit ≠ 0 → CRITICAL FAIL.
|
|
58
|
+
|
|
59
|
+
### 3-5. Step 2: Lint
|
|
60
|
+
|
|
61
|
+
→ Lint 명령: `shared-prompt-sections.md` § 2 참조
|
|
62
|
+
|
|
63
|
+
실패 시 WARN (CRITICAL 아님). 명령 없으면 N/A.
|
|
64
|
+
|
|
65
|
+
### 3-6. Step 3: Tests
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
if [ -f "package.json" ]; then
|
|
69
|
+
npm test 2>&1 || bun run test 2>&1 || echo "No test script"
|
|
70
|
+
elif [ -f "Cargo.toml" ]; then
|
|
71
|
+
cargo test 2>&1
|
|
72
|
+
elif [ -f "go.mod" ]; then
|
|
73
|
+
go test ./... 2>&1
|
|
74
|
+
elif [ -f "pyproject.toml" ]; then
|
|
75
|
+
python -m pytest 2>&1 || echo "No tests"
|
|
76
|
+
fi
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
명령 없으면 N/A.
|
|
80
|
+
|
|
81
|
+
### 3-7. Step 4: TASK 특화 검증
|
|
82
|
+
|
|
83
|
+
TASK 파일 `## Verify` 섹션의 명령을 그대로 실행하고 결과를 기록한다.
|
|
84
|
+
|
|
85
|
+
### 3-8. Step 5: 파일 존재 확인
|
|
86
|
+
|
|
87
|
+
TASK `## Files` 섹션의 각 파일 존재 여부를 확인한다.
|
|
88
|
+
|
|
89
|
+
### 3-9. Step 6: 컨벤션 준수 확인
|
|
90
|
+
|
|
91
|
+
CLAUDE.md 또는 프로젝트 config에 명시된 컨벤션만 확인한다.
|
|
92
|
+
|
|
93
|
+
### 3-10. 결과 XML 출력
|
|
94
|
+
|
|
95
|
+
→ task-result XML 기본 구조: `xml-schema.md` § 2 참조
|
|
96
|
+
→ context-handoff 요소: `xml-schema.md` § 4 참조
|
|
97
|
+
|
|
98
|
+
verifier 고유 추가 필드:
|
|
99
|
+
|
|
100
|
+
```xml
|
|
101
|
+
<verification>
|
|
102
|
+
<check name="progress" status="{PASS|FAIL}"/>
|
|
103
|
+
<check name="build" status="{PASS|FAIL}"/>
|
|
104
|
+
<check name="lint" status="{PASS|FAIL|N/A}"/>
|
|
105
|
+
<check name="tests" status="{PASS|FAIL|N/A}" count="{N}"/>
|
|
106
|
+
<check name="task-specific" status="{PASS|FAIL}"/>
|
|
107
|
+
<check name="files" status="{PASS|FAIL}"/>
|
|
108
|
+
<check name="conventions" status="{PASS|FAIL|N/A}"/>
|
|
109
|
+
</verification>
|
|
110
|
+
<failure-details>
|
|
111
|
+
<failure check="{check name}">
|
|
112
|
+
<error>{error}</error>
|
|
113
|
+
<file>{path}</file>
|
|
114
|
+
<suggested-fix>{suggestion}</suggested-fix>
|
|
115
|
+
</failure>
|
|
116
|
+
</failure-details>
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## 4. 제약사항 및 금지사항
|
|
122
|
+
|
|
123
|
+
### 읽기 전용 원칙
|
|
124
|
+
- NEVER modify source code, config, or test files
|
|
125
|
+
- NEVER "fix" issues — only report
|
|
126
|
+
|
|
127
|
+
### 출력 규칙
|
|
128
|
+
- ALWAYS include actual command output
|
|
129
|
+
- ALWAYS return XML task-result format
|
|
130
|
+
- 명령 없으면 N/A (FAIL 아님)
|
|
131
|
+
|
|
132
|
+
### Output Language Rule
|
|
133
|
+
→ `shared-prompt-sections.md` § 1 참조
|
|
134
|
+
|
|
135
|
+
verifier 고유 규칙:
|
|
136
|
+
- 명령 출력은 원문 유지 (번역 금지)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Work Activity Log
|
|
2
|
+
|
|
3
|
+
각 에이전트가 WORK 진행 상황을 `works/{WORK_ID}/work_{WORK_ID}.log` 파일에 기록하는 규칙을 정의한다.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# 1. 작업 단계 및 로그 기록 내용
|
|
8
|
+
* 최초 실행 시 : 수신한 프롬프트 메시지** Agent 시작 시 수신한 프롬프트 메시지 내용 (Required 필수)
|
|
9
|
+
* Callback 호출 시 : 호출한 Callback URL, 성공여부, Payload, Respoonse (Required 필수)
|
|
10
|
+
* 작업 진행 시 : 작업항목 및 작업내용
|
|
11
|
+
* 수행작업 완료 시 : 타 Agent에 전송한 프롬프트 메시지** Agent 시작 시 수신한 프롬프트 메시지 내용 (Required 필수)
|
|
12
|
+
|
|
13
|
+
## log_work 함수
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
AGENT_NAME="ROUTER" # 각 에이전트 파일에서 적절히 설정
|
|
17
|
+
|
|
18
|
+
log_work() {
|
|
19
|
+
local WORK_ID="$1" AGENT="$2" STAGE="$3" DESC="$4"
|
|
20
|
+
mkdir -p "works/${WORK_ID}"
|
|
21
|
+
printf '[%s]_%s_%s_%s\n' \
|
|
22
|
+
"$(date '+%Y-%m-%dT%H:%M:%S')" "$AGENT" "$STAGE" "$DESC" \
|
|
23
|
+
>> "works/${WORK_ID}/work_${WORK_ID}.log"
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## STAGE 테이블
|
|
30
|
+
|
|
31
|
+
| STAGE | 시점 | 설명 예시 |
|
|
32
|
+
|-------|------|-----------|
|
|
33
|
+
| `INIT` | WORK_ID 결정 후 | `WORK-NN 생성 — Execution-Mode: direct/pipeline/full` |
|
|
34
|
+
| `REF` | STARTUP 참조 직후 | `참조: CLAUDE.md, .agent/router_rule_config.json, agents/file-content-schema.md` |
|
|
35
|
+
| `PLAN` | PLAN.md + TASK 파일 생성 완료 | `PLAN.md, TASK-00.md 생성 완료` |
|
|
36
|
+
| `IMPL` | direct 모드 코드 구현 시작 | `코드 구현 시작 — 참조: {수정 대상 파일 목록}` |
|
|
37
|
+
| `BUILD` | self-check 통과 | `빌드/린트 통과` |
|
|
38
|
+
| `COMMIT` | git commit 완료 | `commit {hash}` |
|
|
39
|
+
| `DISPATCH` | pipeline/full dispatch | `Builder dispatch` 또는 `Planner dispatch` |
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## 참조 자료 수집 규칙
|
|
44
|
+
|
|
45
|
+
STARTUP에서 읽은 파일과 이후 탐색한 파일을 누적 추적하여 `REF` 단계에서 한 번에 기록.
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# Agent Communication XML Schema
|
|
2
|
+
|
|
3
|
+
uc-taskmanager 에이전트 간 XML 통신 포맷 정의.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Dispatch Format (Dispatcher → Receiver)
|
|
8
|
+
|
|
9
|
+
```xml
|
|
10
|
+
<dispatch to="{receiver}" work="{WORK_ID}" task="{TASK_ID}" execution-mode="{direct|pipeline|full}">
|
|
11
|
+
<context>
|
|
12
|
+
<project>{project name}</project>
|
|
13
|
+
<language>{lang_code}</language>
|
|
14
|
+
<plan-file>works/{WORK_ID}/PLAN.md</plan-file>
|
|
15
|
+
</context>
|
|
16
|
+
<task-spec>
|
|
17
|
+
<file>works/{WORK_ID}/TASK-XX.md</file>
|
|
18
|
+
<title>{title}</title>
|
|
19
|
+
<action>{implement|verify|commit|plan|route}</action>
|
|
20
|
+
<description>{optional}</description>
|
|
21
|
+
</task-spec>
|
|
22
|
+
<previous-results>
|
|
23
|
+
<result task="{TASK_ID}" status="{PASS|FAIL|SKIP}">{summary}</result>
|
|
24
|
+
</previous-results>
|
|
25
|
+
<cache-hint sections="{section1},{section2}"/>
|
|
26
|
+
</dispatch>
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
| 속성 | 값 |
|
|
30
|
+
|------|----|
|
|
31
|
+
| `to` | builder, verifier, committer, planner, scheduler, router |
|
|
32
|
+
| `task` | `TASK-NN` — WORK prefix 포함 금지 |
|
|
33
|
+
| `execution-mode` | direct / pipeline / full (생략 시 full로 간주) |
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## 2. Task Result Format (Receiver → Dispatcher)
|
|
38
|
+
|
|
39
|
+
```xml
|
|
40
|
+
<task-result work="{WORK_ID}" task="{TASK_ID}" agent="{agent}" status="{PASS|FAIL}">
|
|
41
|
+
<summary>{1-2줄 요약}</summary>
|
|
42
|
+
<files-changed>
|
|
43
|
+
<file action="{created|modified|deleted}" path="{path}">{description}</file>
|
|
44
|
+
</files-changed>
|
|
45
|
+
<verification>
|
|
46
|
+
<check name="{type}" status="{PASS|FAIL|N/A}">{output}</check>
|
|
47
|
+
</verification>
|
|
48
|
+
<notes>{참고사항}</notes>
|
|
49
|
+
</task-result>
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## 3. Dispatcher-Receiver 매핑
|
|
55
|
+
|
|
56
|
+
| Dispatcher | Receiver | execution-mode | 설명 |
|
|
57
|
+
|------------|----------|:--------------:|------|
|
|
58
|
+
| Router | (자기 자신) | `direct` | 서브에이전트 없음. Router가 직접 구현+commit+콜백 |
|
|
59
|
+
| Router | Planner | `full` | 복잡 WORK 계획 |
|
|
60
|
+
| Router | Scheduler | `full` | 계획된 WORK 실행 |
|
|
61
|
+
| Router | Builder | `pipeline` | TASK 1개 구현 |
|
|
62
|
+
| Router | Verifier | `pipeline` | TASK 1개 검증 |
|
|
63
|
+
| Router | Committer | `pipeline` | TASK 1개 커밋 |
|
|
64
|
+
| Scheduler | Builder | `full` | TASK N개 구현 |
|
|
65
|
+
| Scheduler | Verifier | `full` | TASK N개 검증 |
|
|
66
|
+
| Scheduler | Committer | `full` | TASK N개 커밋 |
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## 4. Context-Handoff Element
|
|
71
|
+
|
|
72
|
+
```xml
|
|
73
|
+
<context-handoff from="{agent}" detail-level="{FULL|SUMMARY|DROP}">
|
|
74
|
+
<what>{변경/검증 사항}</what>
|
|
75
|
+
<why>{의사결정 근거}</why> <!-- FULL only -->
|
|
76
|
+
<caution>{주의사항}</caution> <!-- FULL only -->
|
|
77
|
+
<incomplete>{미완료 사항}</incomplete> <!-- FULL only -->
|
|
78
|
+
</context-handoff>
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
| detail-level | 포함 필드 |
|
|
82
|
+
|:---:|---|
|
|
83
|
+
| `FULL` | what, why, caution, incomplete |
|
|
84
|
+
| `SUMMARY` | what만 (1-3줄) |
|
|
85
|
+
| `DROP` | 요소 생략 |
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## 5. execution-mode별 에이전트 행동
|
|
90
|
+
|
|
91
|
+
| 에이전트 | direct | pipeline | full |
|
|
92
|
+
|---------|--------|----------|------|
|
|
93
|
+
| Router | 구현+self-check+result.md+commit+콜백 직접 | PLAN 생성 후 B→V→C dispatch | Planner에 dispatch |
|
|
94
|
+
| Planner | 호출 안 됨 | 호출 안 됨 | PLAN.md 생성 |
|
|
95
|
+
| Scheduler | 호출 안 됨 | 호출 안 됨 | DAG 관리 + [B→V→C]×N |
|
|
96
|
+
| Builder | 호출 안 됨 | 정상 실행 | 정상 실행 |
|
|
97
|
+
| Verifier | 호출 안 됨 | 정상 실행 | 정상 실행 |
|
|
98
|
+
| Committer | 호출 안 됨 | result.md+commit+콜백 | result.md+commit+콜백 |
|
|
99
|
+
|
|
100
|
+
불변 항목 (모드 무관):
|
|
101
|
+
|
|
102
|
+
| 항목 | direct | pipeline/full |
|
|
103
|
+
|------|:---:|:---:|
|
|
104
|
+
| `works/WORK-NN/` 디렉토리 | Router | Router/Planner |
|
|
105
|
+
| `PLAN.md` | Router | Router/Planner |
|
|
106
|
+
| `TASK-XX.md` | Router | Router/Planner |
|
|
107
|
+
| `TASK-XX_result.md` | Router | Committer |
|
|
108
|
+
| COMMITTER DONE 콜백 | Router | Committer |
|
|
109
|
+
| `WORK-LIST.md` IN_PROGRESS | Router | Router |
|
package/bin/cli.mjs
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { VERSION } from '../lib/constants.mjs';
|
|
4
|
+
|
|
5
|
+
const bold = (s) => `\x1b[1m${s}\x1b[0m`;
|
|
6
|
+
const dim = (s) => `\x1b[2m${s}\x1b[0m`;
|
|
7
|
+
const cyan = (s) => `\x1b[36m${s}\x1b[0m`;
|
|
8
|
+
|
|
9
|
+
const HELP = `
|
|
10
|
+
${bold('uctm')} ${dim(`v${VERSION}`)} — Universal Claude Task Manager
|
|
11
|
+
|
|
12
|
+
${bold('Usage:')}
|
|
13
|
+
uctm init ${dim('[--global]')} Install agent files to the current project
|
|
14
|
+
uctm update ${dim('[--global]')} Update agent files (keeps your config)
|
|
15
|
+
uctm --version Show version
|
|
16
|
+
uctm --help Show this help
|
|
17
|
+
|
|
18
|
+
${bold('Options:')}
|
|
19
|
+
--global Install/update to ${dim('~/.claude/agents/')} (available across all projects)
|
|
20
|
+
Default installs to ${dim('.claude/agents/')} in the current directory
|
|
21
|
+
|
|
22
|
+
${bold('Examples:')}
|
|
23
|
+
${dim('# Per-project setup')}
|
|
24
|
+
${cyan('$')} cd your-project
|
|
25
|
+
${cyan('$')} uctm init
|
|
26
|
+
|
|
27
|
+
${dim('# Global setup')}
|
|
28
|
+
${cyan('$')} uctm init --global
|
|
29
|
+
|
|
30
|
+
${dim('# Update agents after uctm upgrade')}
|
|
31
|
+
${cyan('$')} uctm update
|
|
32
|
+
|
|
33
|
+
${bold('After init:')}
|
|
34
|
+
1. Run ${dim("'claude'")} to start Claude Code
|
|
35
|
+
2. Type: ${dim('[추가기능] Add a hello world feature')}
|
|
36
|
+
|
|
37
|
+
${dim('https://github.com/UCJung/uc-taskmanager-claude-agent')}
|
|
38
|
+
`;
|
|
39
|
+
|
|
40
|
+
async function main() {
|
|
41
|
+
const args = process.argv.slice(2);
|
|
42
|
+
const command = args.find((a) => !a.startsWith('-'));
|
|
43
|
+
const isGlobal = args.includes('--global');
|
|
44
|
+
|
|
45
|
+
if (args.includes('--version') || args.includes('-v')) {
|
|
46
|
+
console.log(VERSION);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (args.includes('--help') || args.includes('-h') || args.length === 0) {
|
|
51
|
+
console.log(HELP);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (command === 'init') {
|
|
56
|
+
console.log(`\n ${bold('uctm')} ${dim(`v${VERSION}`)} — Universal Claude Task Manager`);
|
|
57
|
+
const { init } = await import('../lib/init.mjs');
|
|
58
|
+
init(isGlobal);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (command === 'update') {
|
|
63
|
+
console.log(`\n ${bold('uctm')} ${dim(`v${VERSION}`)} — Universal Claude Task Manager`);
|
|
64
|
+
const { update } = await import('../lib/update.mjs');
|
|
65
|
+
update(isGlobal);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
console.error(`\n Unknown command: ${command}`);
|
|
70
|
+
console.error(` Run ${dim("'uctm --help'")} for usage.\n`);
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
main();
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { join, dirname } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
|
|
5
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
6
|
+
const pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf8'));
|
|
7
|
+
|
|
8
|
+
export const VERSION = pkg.version;
|
|
9
|
+
|
|
10
|
+
export const AGENT_FILES = [
|
|
11
|
+
'agent-flow.md',
|
|
12
|
+
'builder.md',
|
|
13
|
+
'committer.md',
|
|
14
|
+
'context-policy.md',
|
|
15
|
+
'file-content-schema.md',
|
|
16
|
+
'planner.md',
|
|
17
|
+
'router.md',
|
|
18
|
+
'scheduler.md',
|
|
19
|
+
'shared-prompt-sections.md',
|
|
20
|
+
'verifier.md',
|
|
21
|
+
'work-activity-log.md',
|
|
22
|
+
'xml-schema.md',
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
export const CLAUDE_MD_SECTION = `
|
|
26
|
+
## Agent 호출 규칙
|
|
27
|
+
|
|
28
|
+
\`[]\` 태그로 시작하는 요청 → \`agents/agent-flow.md\` 를 읽고 파이프라인을 실행한다.
|
|
29
|
+
|
|
30
|
+
- **Main Claude가 오케스트레이터**다. 모든 에이전트 호출은 Main Claude가 직접 수행한다.
|
|
31
|
+
- 각 에이전트는 작업 완료 후 결과(dispatch XML 또는 task-result XML)만 반환한다.
|
|
32
|
+
- Main Claude가 반환값을 받아 다음 에이전트를 순서대로 호출한다.
|
|
33
|
+
- 파이프라인 흐름은 \`agents/agent-flow.md\` 기준을 따른다.
|
|
34
|
+
|
|
35
|
+
예: \`[추가기능]\`, \`[버그수정]\`, \`[리팩토링]\`, \`[WORK 시작]\` 등
|
|
36
|
+
`.trimStart();
|
package/lib/init.mjs
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, copyFileSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { join, dirname } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { homedir } from 'node:os';
|
|
5
|
+
import { AGENT_FILES, CLAUDE_MD_SECTION } from './constants.mjs';
|
|
6
|
+
|
|
7
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const AGENTS_SRC = join(__dirname, '..', 'agents');
|
|
9
|
+
|
|
10
|
+
const green = (s) => `\x1b[32m${s}\x1b[0m`;
|
|
11
|
+
const dim = (s) => `\x1b[2m${s}\x1b[0m`;
|
|
12
|
+
|
|
13
|
+
function copyAgents(destDir) {
|
|
14
|
+
mkdirSync(destDir, { recursive: true });
|
|
15
|
+
let count = 0;
|
|
16
|
+
for (const file of AGENT_FILES) {
|
|
17
|
+
const src = join(AGENTS_SRC, file);
|
|
18
|
+
if (!existsSync(src)) continue;
|
|
19
|
+
copyFileSync(src, join(destDir, file));
|
|
20
|
+
count++;
|
|
21
|
+
}
|
|
22
|
+
return count;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function ensureRouterConfig(projectDir) {
|
|
26
|
+
const configDir = join(projectDir, '.agent');
|
|
27
|
+
const configPath = join(configDir, 'router_rule_config.json');
|
|
28
|
+
if (existsSync(configPath)) return false;
|
|
29
|
+
mkdirSync(configDir, { recursive: true });
|
|
30
|
+
const srcConfig = join(__dirname, '..', '.agent', 'router_rule_config.json');
|
|
31
|
+
if (existsSync(srcConfig)) {
|
|
32
|
+
copyFileSync(srcConfig, configPath);
|
|
33
|
+
}
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function ensureWorksDir(projectDir) {
|
|
38
|
+
const worksDir = join(projectDir, 'works');
|
|
39
|
+
if (existsSync(worksDir)) return false;
|
|
40
|
+
mkdirSync(worksDir, { recursive: true });
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function updateClaudeMd(projectDir) {
|
|
45
|
+
const claudeMdPath = join(projectDir, 'CLAUDE.md');
|
|
46
|
+
if (existsSync(claudeMdPath)) {
|
|
47
|
+
const content = readFileSync(claudeMdPath, 'utf8');
|
|
48
|
+
if (content.includes('Agent 호출 규칙') || content.includes('agent-flow.md')) {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
writeFileSync(claudeMdPath, content.trimEnd() + '\n\n' + CLAUDE_MD_SECTION);
|
|
52
|
+
} else {
|
|
53
|
+
writeFileSync(claudeMdPath, '# CLAUDE.md\n\n' + CLAUDE_MD_SECTION);
|
|
54
|
+
}
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function init(isGlobal) {
|
|
59
|
+
if (isGlobal) {
|
|
60
|
+
const globalDir = join(homedir(), '.claude', 'agents');
|
|
61
|
+
const count = copyAgents(globalDir);
|
|
62
|
+
console.log(`\n Installing to ${dim('~/.claude/agents/')} ...`);
|
|
63
|
+
console.log(` ${green('✓')} ${count} agent files copied`);
|
|
64
|
+
console.log(`\n ${dim('Next steps:')}`);
|
|
65
|
+
console.log(` 1. Open any project and run ${dim("'claude'")}`);
|
|
66
|
+
console.log(` 2. Type: ${dim('[추가기능] Add a hello world feature')}\n`);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const projectDir = process.cwd();
|
|
71
|
+
const destDir = join(projectDir, '.claude', 'agents');
|
|
72
|
+
|
|
73
|
+
console.log(`\n Installing to ${dim('.claude/agents/')} ...`);
|
|
74
|
+
|
|
75
|
+
const count = copyAgents(destDir);
|
|
76
|
+
console.log(` ${green('✓')} ${count} agent files copied`);
|
|
77
|
+
|
|
78
|
+
if (ensureRouterConfig(projectDir)) {
|
|
79
|
+
console.log(` ${green('✓')} .agent/router_rule_config.json created`);
|
|
80
|
+
} else {
|
|
81
|
+
console.log(` ${dim('-')} .agent/router_rule_config.json already exists`);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (updateClaudeMd(projectDir)) {
|
|
85
|
+
console.log(` ${green('✓')} CLAUDE.md updated`);
|
|
86
|
+
} else {
|
|
87
|
+
console.log(` ${dim('-')} CLAUDE.md already has agent rules`);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (ensureWorksDir(projectDir)) {
|
|
91
|
+
console.log(` ${green('✓')} works/ directory created`);
|
|
92
|
+
} else {
|
|
93
|
+
console.log(` ${dim('-')} works/ directory already exists`);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
console.log(`\n ${dim('Next steps:')}`);
|
|
97
|
+
console.log(` 1. Run ${dim("'claude'")} to start Claude Code`);
|
|
98
|
+
console.log(` 2. Type: ${dim('[추가기능] Add a hello world feature')}\n`);
|
|
99
|
+
}
|
package/lib/update.mjs
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { existsSync, copyFileSync, mkdirSync } from 'node:fs';
|
|
2
|
+
import { join, dirname } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { homedir } from 'node:os';
|
|
5
|
+
import { AGENT_FILES } from './constants.mjs';
|
|
6
|
+
|
|
7
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const AGENTS_SRC = join(__dirname, '..', 'agents');
|
|
9
|
+
|
|
10
|
+
const green = (s) => `\x1b[32m${s}\x1b[0m`;
|
|
11
|
+
const dim = (s) => `\x1b[2m${s}\x1b[0m`;
|
|
12
|
+
|
|
13
|
+
export function update(isGlobal) {
|
|
14
|
+
const destDir = isGlobal
|
|
15
|
+
? join(homedir(), '.claude', 'agents')
|
|
16
|
+
: join(process.cwd(), '.claude', 'agents');
|
|
17
|
+
|
|
18
|
+
if (!existsSync(destDir)) {
|
|
19
|
+
console.error(`\n Error: ${destDir} not found. Run ${dim("'uctm init'")} first.\n`);
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
let count = 0;
|
|
24
|
+
for (const file of AGENT_FILES) {
|
|
25
|
+
const src = join(AGENTS_SRC, file);
|
|
26
|
+
if (!existsSync(src)) continue;
|
|
27
|
+
copyFileSync(src, join(destDir, file));
|
|
28
|
+
count++;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const label = isGlobal ? '~/.claude/agents/' : '.claude/agents/';
|
|
32
|
+
console.log(`\n Updating ${dim(label)} ...`);
|
|
33
|
+
console.log(` ${green('✓')} ${count} agent files updated`);
|
|
34
|
+
console.log(` ${dim('-')} CLAUDE.md, router_rule_config.json untouched\n`);
|
|
35
|
+
}
|