uctm 1.3.2 → 1.5.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/.claude-plugin/plugin.json +29 -0
- package/agents/agent-flow.md +145 -41
- package/agents/builder.md +25 -7
- package/agents/committer.md +41 -30
- package/agents/ko/agent-flow.md +97 -41
- package/agents/ko/builder.md +18 -7
- package/agents/ko/committer.md +34 -30
- package/agents/ko/planner.md +22 -8
- package/agents/ko/scheduler.md +21 -7
- package/agents/ko/shared-prompt-sections.md +58 -37
- package/agents/ko/specifier.md +19 -6
- package/agents/ko/verifier.md +17 -6
- package/agents/ko/work-activity-log.md +14 -12
- package/agents/planner.md +22 -8
- package/agents/scheduler.md +21 -7
- package/agents/shared-prompt-sections.md +58 -37
- package/agents/specifier.md +19 -6
- package/agents/verifier.md +24 -6
- package/agents/work-activity-log.md +14 -12
- package/agents/xml-schema.md +50 -0
- package/bin/cli.mjs +1 -1
- package/lib/constants.mjs +54 -25
- package/lib/init.mjs +108 -21
- package/package.json +4 -2
- package/skills/init/SKILL.md +95 -0
- package/skills/sdd-pipeline/SKILL.md +16 -0
- package/skills/sdd-pipeline/references/agent-flow.md +279 -0
- package/skills/sdd-pipeline/references/context-policy.md +94 -0
- package/skills/sdd-pipeline/references/file-content-schema.md +249 -0
- package/skills/sdd-pipeline/references/shared-prompt-sections.md +250 -0
- package/skills/sdd-pipeline/references/work-activity-log.md +47 -0
- package/skills/sdd-pipeline/references/xml-schema.md +159 -0
- package/skills/work-pipeline/SKILL.md +49 -0
- package/skills/work-status/SKILL.md +35 -0
|
@@ -28,7 +28,7 @@ elif [ -f "Cargo.toml" ]; then
|
|
|
28
28
|
elif [ -f "go.mod" ]; then
|
|
29
29
|
go build ./... 2>&1
|
|
30
30
|
elif [ -f "pyproject.toml" ] || [ -f "setup.py" ]; then
|
|
31
|
-
python -m py_compile $(find . -name "*.py" -not -path "*/venv/*"
|
|
31
|
+
python -m py_compile $(find . -maxdepth 3 -name "*.py" -not -path "*/venv/*" 2>/dev/null) 2>&1
|
|
32
32
|
elif [ -f "Makefile" ]; then
|
|
33
33
|
make build 2>&1 || make 2>&1
|
|
34
34
|
fi
|
|
@@ -68,22 +68,20 @@ works/{WORK_ID}/
|
|
|
68
68
|
|
|
69
69
|
## § 4. File System Discovery Scripts
|
|
70
70
|
|
|
71
|
-
```
|
|
71
|
+
```
|
|
72
72
|
# Find latest WORK with incomplete TASKs
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
done
|
|
73
|
+
# Use Glob tool: pattern "works/WORK-*/" → list all WORK directories (sorted)
|
|
74
|
+
# For each WORK (descending), compare:
|
|
75
|
+
# Glob "works/WORK-NN/TASK-*.md" (exclude *_result.md, *_progress.md) → TOTAL
|
|
76
|
+
# Glob "works/WORK-NN/TASK-*_result.md" → DONE
|
|
77
|
+
# First WORK where DONE < TOTAL is the active WORK
|
|
79
78
|
|
|
80
79
|
# List all WORKs
|
|
81
|
-
|
|
80
|
+
# Use Glob tool: pattern "works/WORK-*/"
|
|
82
81
|
|
|
83
82
|
# TASK completion status
|
|
84
|
-
TOTAL
|
|
85
|
-
DONE
|
|
86
|
-
echo "$DONE / $TOTAL"
|
|
83
|
+
# TOTAL = count of Glob "works/${WORK_ID}/TASK-??.md"
|
|
84
|
+
# DONE = count of Glob "works/${WORK_ID}/TASK-*_result.md"
|
|
87
85
|
```
|
|
88
86
|
|
|
89
87
|
---
|
|
@@ -164,28 +162,17 @@ Rules:
|
|
|
164
162
|
|
|
165
163
|
## § 10. Callback Transmission Template
|
|
166
164
|
|
|
165
|
+
→ **Bash command rules: see § 13** — each step below is a separate tool call.
|
|
166
|
+
|
|
167
167
|
Replace `{CallbackType}` with the actual key name (e.g., `ProgressCallback`, `TaskCallback`).
|
|
168
168
|
|
|
169
|
+
**Step 1.** Use `Grep` tool to find `{CallbackType}:` line in CLAUDE.md. If not found, skip callback entirely.
|
|
170
|
+
|
|
171
|
+
**Step 2.** Use `Grep` tool to find `CallbackToken:` line in CLAUDE.md (optional).
|
|
172
|
+
|
|
173
|
+
**Step 3.** Send callback with a single `curl` command:
|
|
169
174
|
```bash
|
|
170
|
-
CALLBACK_URL
|
|
171
|
-
CALLBACK_TOKEN=$(grep "^CallbackToken:" CLAUDE.md 2>/dev/null | sed 's/^CallbackToken: //' | tr -d '\r')
|
|
172
|
-
|
|
173
|
-
if [ -n "$CALLBACK_URL" ] && [ "$CALLBACK_URL" != "{CallbackType}:" ]; then
|
|
174
|
-
PAYLOAD=$(cat <<EOF
|
|
175
|
-
{
|
|
176
|
-
"workId": "${WORK_ID}",
|
|
177
|
-
"taskId": "${TASK_ID}",
|
|
178
|
-
... agent-specific fields ...
|
|
179
|
-
}
|
|
180
|
-
EOF
|
|
181
|
-
)
|
|
182
|
-
AUTH_HEADER=""
|
|
183
|
-
[ -n "$CALLBACK_TOKEN" ] && AUTH_HEADER="-H \"X-Runner-Api-Key: ${CALLBACK_TOKEN}\""
|
|
184
|
-
curl -s -X POST "$CALLBACK_URL" \
|
|
185
|
-
-H "Content-Type: application/json" \
|
|
186
|
-
$AUTH_HEADER \
|
|
187
|
-
-d "$PAYLOAD" > /dev/null 2>&1
|
|
188
|
-
fi
|
|
175
|
+
curl -s -X POST "CALLBACK_URL" -H "Content-Type: application/json" -H "X-Runner-Api-Key: TOKEN" -d '{"workId":"WORK-01","taskId":"TASK-00",...}'
|
|
189
176
|
```
|
|
190
177
|
|
|
191
178
|
Agent-specific payload fields:
|
|
@@ -201,13 +188,13 @@ Agent-specific payload fields:
|
|
|
201
188
|
grep -oP '(?<=Language:\s?)[a-z]{2}' CLAUDE.md 2>/dev/null
|
|
202
189
|
|
|
203
190
|
# 2. Tech stack
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
191
|
+
head -50 package.json 2>/dev/null
|
|
192
|
+
head -30 pyproject.toml 2>/dev/null
|
|
193
|
+
head -20 Cargo.toml 2>/dev/null
|
|
194
|
+
head -10 go.mod 2>/dev/null
|
|
208
195
|
|
|
209
196
|
# 3. Structure (when needed)
|
|
210
|
-
find . -maxdepth 3 -type f \( -name "*.md" -o -name "*.json" -o -name "*.toml" \)
|
|
197
|
+
find . -maxdepth 3 -type f \( -name "*.md" -o -name "*.json" -o -name "*.toml" \) -not -path "*/node_modules/*" 2>/dev/null
|
|
211
198
|
```
|
|
212
199
|
|
|
213
200
|
---
|
|
@@ -223,7 +210,41 @@ On gate failure → return FAIL task-result immediately. Do not proceed to subse
|
|
|
223
210
|
|
|
224
211
|
---
|
|
225
212
|
|
|
213
|
+
## § 13. Bash Command Rules
|
|
214
|
+
|
|
215
|
+
Bash commands MUST follow these rules for permission compatibility.
|
|
216
|
+
|
|
217
|
+
**MANDATORY:**
|
|
218
|
+
- One simple command per Bash call — NO compound commands (`&&`, `||`, `;`, `|`)
|
|
219
|
+
- NO `cd dir && command` — you are already in the project root
|
|
220
|
+
- NO multi-line scripts — split into separate Bash calls
|
|
221
|
+
- NO sub-shell expansions in arguments — e.g., `$(date ...)` inside `printf`
|
|
222
|
+
- Use relative paths from project root (e.g., `works/WORK-01/`) — NO absolute paths
|
|
223
|
+
- Use `git add file`, `git commit -m "msg"` — NO `git -C path` flag
|
|
224
|
+
|
|
225
|
+
**For file operations, prefer dedicated tools over Bash:**
|
|
226
|
+
- Read files → `Read` tool (NOT `cat`)
|
|
227
|
+
- Write/append files → `Write` tool (NOT `echo >>` or `printf >>`)
|
|
228
|
+
- Edit files → `Edit` tool (NOT `sed -i`)
|
|
229
|
+
- Search files → `Grep` tool (NOT `grep`)
|
|
230
|
+
- Find files → `Glob` tool (NOT `find`)
|
|
231
|
+
|
|
232
|
+
**Activity log example:**
|
|
233
|
+
```
|
|
234
|
+
WRONG: printf '[%s]_%s\n' "$(date ...)" "INIT" >> work.log
|
|
235
|
+
RIGHT: Use Write tool to append a line to the log file
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
**Git example:**
|
|
239
|
+
```
|
|
240
|
+
WRONG: cd /path/to/project && git add file && git commit -m "msg"
|
|
241
|
+
RIGHT: git add file (one call)
|
|
242
|
+
git commit -m "msg" (next call)
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
226
247
|
## Version
|
|
227
248
|
|
|
228
249
|
- Created: 2026-03-10
|
|
229
|
-
- Updated: 2026-03-
|
|
250
|
+
- Updated: 2026-03-28
|
package/agents/specifier.md
CHANGED
|
@@ -34,12 +34,23 @@ You are the **Specifier** — the agent that transforms user requests into requi
|
|
|
34
34
|
|
|
35
35
|
**Resolve REFERENCES_DIR**: Check your input for `REFERENCES_DIR=...` line. Use that absolute path. If not provided, default to `.claude/agents`.
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
37
|
+
#### Reference Loading (ref-cache)
|
|
38
|
+
|
|
39
|
+
1. Check if `<ref-cache>` exists in the received dispatch XML
|
|
40
|
+
2. For each required reference file:
|
|
41
|
+
- If present in ref-cache → **SKIP file read**, use cached content
|
|
42
|
+
- If absent from ref-cache → Read from `{REFERENCES_DIR}/{filename}.md` and add to ref-cache
|
|
43
|
+
3. On task completion, include the merged `<ref-cache>` in the returned task-result XML
|
|
44
|
+
4. **Backward compatibility**: If dispatch contains no `<ref-cache>`, read all reference files normally (existing behavior)
|
|
45
|
+
|
|
46
|
+
Required reference files for this agent:
|
|
47
|
+
|
|
48
|
+
| File | ref-cache key |
|
|
49
|
+
|------|---------------|
|
|
50
|
+
| `{REFERENCES_DIR}/file-content-schema.md` | `file-content-schema` |
|
|
51
|
+
| `{REFERENCES_DIR}/shared-prompt-sections.md` | `shared-prompt-sections` |
|
|
52
|
+
| `{REFERENCES_DIR}/xml-schema.md` | `xml-schema` |
|
|
53
|
+
| `{REFERENCES_DIR}/work-activity-log.md` | `work-activity-log` |
|
|
43
54
|
|
|
44
55
|
### 3-2. WORK ID Determination
|
|
45
56
|
|
|
@@ -116,6 +127,7 @@ Requirement complexity assessment:
|
|
|
116
127
|
```
|
|
117
128
|
|
|
118
129
|
→ dispatch XML format: see `xml-schema.md` § 1 (to="builder", task="TASK-00", execution-mode="direct")
|
|
130
|
+
→ Include `<ref-cache>` with all reference files loaded (see `xml-schema.md` § 6)
|
|
119
131
|
|
|
120
132
|
### 3-7. Planner Delegation — Complex Requirements (pipeline/full)
|
|
121
133
|
|
|
@@ -136,6 +148,7 @@ Requirement complexity assessment:
|
|
|
136
148
|
```
|
|
137
149
|
|
|
138
150
|
→ dispatch XML format: see `xml-schema.md` § 1 (to="planner", execution-mode="full")
|
|
151
|
+
→ Include `<ref-cache>` with all reference files loaded (see `xml-schema.md` § 6)
|
|
139
152
|
|
|
140
153
|
### 3-8. Output Language Rule
|
|
141
154
|
|
package/agents/verifier.md
CHANGED
|
@@ -35,12 +35,23 @@ Verifies the results of TASKs completed by the Builder, checking build, lint, te
|
|
|
35
35
|
|
|
36
36
|
**Resolve REFERENCES_DIR**: Check your input for `REFERENCES_DIR=...` line or `<references-dir>` XML element. Use that absolute path. If not provided, default to `.claude/agents`.
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
38
|
+
#### Reference Loading (ref-cache)
|
|
39
|
+
|
|
40
|
+
1. Check if `<ref-cache>` exists in the received dispatch XML
|
|
41
|
+
2. For each required reference file:
|
|
42
|
+
- If present in ref-cache → **SKIP file read**, use cached content
|
|
43
|
+
- If absent from ref-cache → Read from `{REFERENCES_DIR}/{filename}.md` and add to ref-cache
|
|
44
|
+
3. On task completion, include the merged `<ref-cache>` in the returned task-result XML
|
|
45
|
+
4. **Backward compatibility**: If dispatch contains no `<ref-cache>`, read all reference files normally (existing behavior)
|
|
46
|
+
|
|
47
|
+
Required reference files for this agent:
|
|
48
|
+
|
|
49
|
+
| File | ref-cache key |
|
|
50
|
+
|------|---------------|
|
|
51
|
+
| `{REFERENCES_DIR}/shared-prompt-sections.md` | `shared-prompt-sections` |
|
|
52
|
+
| `{REFERENCES_DIR}/xml-schema.md` | `xml-schema` |
|
|
53
|
+
| `{REFERENCES_DIR}/context-policy.md` | `context-policy` |
|
|
54
|
+
| `{REFERENCES_DIR}/work-activity-log.md` | `work-activity-log` |
|
|
44
55
|
|
|
45
56
|
### 3-2. XML Input Parsing
|
|
46
57
|
|
|
@@ -96,6 +107,7 @@ Only check conventions specified in CLAUDE.md or project config.
|
|
|
96
107
|
|
|
97
108
|
→ task-result XML base structure: see `xml-schema.md` § 2
|
|
98
109
|
→ context-handoff element: see `xml-schema.md` § 4
|
|
110
|
+
→ ref-cache element: see `xml-schema.md` § 6
|
|
99
111
|
|
|
100
112
|
Verifier-specific additional fields:
|
|
101
113
|
|
|
@@ -116,6 +128,12 @@ Verifier-specific additional fields:
|
|
|
116
128
|
<suggested-fix>{suggestion}</suggested-fix>
|
|
117
129
|
</failure>
|
|
118
130
|
</failure-details>
|
|
131
|
+
<ref-cache>
|
|
132
|
+
<!-- Include all reference files loaded during this execution (from disk or received ref-cache) -->
|
|
133
|
+
<ref key="shared-prompt-sections">{content}</ref>
|
|
134
|
+
<ref key="xml-schema">{content}</ref>
|
|
135
|
+
<!-- ... other keys loaded ... -->
|
|
136
|
+
</ref-cache>
|
|
119
137
|
```
|
|
120
138
|
|
|
121
139
|
---
|
|
@@ -10,19 +10,21 @@ Defines the rules for each agent to record WORK progress in the `works/{WORK_ID}
|
|
|
10
10
|
* During work: Work items and work content
|
|
11
11
|
* On task completion: The prompt message sent to other agents** Content of the prompt message received at agent startup (Required)
|
|
12
12
|
|
|
13
|
-
## log_work
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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
|
-
}
|
|
13
|
+
## log_work Method
|
|
14
|
+
|
|
15
|
+
**Do NOT use Bash** for activity log writes. Use the `Write` tool (or `Edit` tool to append).
|
|
16
|
+
|
|
17
|
+
Format each log entry as:
|
|
25
18
|
```
|
|
19
|
+
[YYYY-MM-DDTHH:MM:SS]_AGENT_STAGE_description
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Example: to log an INIT stage, use the **Write** tool to append to `works/{WORK_ID}/work_{WORK_ID}.log`:
|
|
23
|
+
```
|
|
24
|
+
[2026-03-28T14:30:00]_SPECIFIER_INIT_WORK-09 created — Execution-Mode: direct
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
→ **Bash command rules: see `shared-prompt-sections.md` § 13**
|
|
26
28
|
|
|
27
29
|
---
|
|
28
30
|
|
package/agents/xml-schema.md
CHANGED
|
@@ -8,6 +8,13 @@ XML communication format definition for uc-taskmanager agents.
|
|
|
8
8
|
|
|
9
9
|
```xml
|
|
10
10
|
<dispatch to="{receiver}" work="{WORK_ID}" task="{TASK_ID}" execution-mode="{direct|pipeline|full}">
|
|
11
|
+
<ref-cache> <!-- optional -->
|
|
12
|
+
<ref key="shared-prompt-sections">{file content}</ref>
|
|
13
|
+
<ref key="file-content-schema">{file content}</ref>
|
|
14
|
+
<ref key="xml-schema">{file content}</ref>
|
|
15
|
+
<ref key="context-policy">{file content}</ref>
|
|
16
|
+
<ref key="work-activity-log">{file content}</ref>
|
|
17
|
+
</ref-cache>
|
|
11
18
|
<references-dir>{absolute path to references directory}</references-dir>
|
|
12
19
|
<context>
|
|
13
20
|
<project>{project name}</project>
|
|
@@ -47,6 +54,13 @@ XML communication format definition for uc-taskmanager agents.
|
|
|
47
54
|
<check name="{type}" status="{PASS|FAIL|N/A}">{output}</check>
|
|
48
55
|
</verification>
|
|
49
56
|
<notes>{notes}</notes>
|
|
57
|
+
<ref-cache> <!-- optional -->
|
|
58
|
+
<ref key="shared-prompt-sections">{file content}</ref>
|
|
59
|
+
<ref key="file-content-schema">{file content}</ref>
|
|
60
|
+
<ref key="xml-schema">{file content}</ref>
|
|
61
|
+
<ref key="context-policy">{file content}</ref>
|
|
62
|
+
<ref key="work-activity-log">{file content}</ref>
|
|
63
|
+
</ref-cache>
|
|
50
64
|
</task-result>
|
|
51
65
|
```
|
|
52
66
|
|
|
@@ -107,3 +121,39 @@ Invariants (regardless of mode):
|
|
|
107
121
|
| `TASK-XX_result.md` | Committer | Committer |
|
|
108
122
|
| COMMITTER DONE callback | Committer | Committer |
|
|
109
123
|
| `WORK-LIST.md` IN_PROGRESS | Specifier | Specifier |
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## 6. ref-cache Element Definition
|
|
128
|
+
|
|
129
|
+
`<ref-cache>` is an optional container element that carries pre-loaded reference file contents within dispatch and task-result XML. When present, receiving agents MUST use these contents instead of reading files from disk.
|
|
130
|
+
|
|
131
|
+
### Structure
|
|
132
|
+
|
|
133
|
+
```xml
|
|
134
|
+
<ref-cache>
|
|
135
|
+
<ref key="{filename-without-extension}">{full file content}</ref>
|
|
136
|
+
...
|
|
137
|
+
</ref-cache>
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
| Element | Required | Description |
|
|
141
|
+
|---------|----------|-------------|
|
|
142
|
+
| `<ref-cache>` | Optional | Container for cached reference files. Omit entirely if no cache is available. |
|
|
143
|
+
| `<ref key="...">` | — | Individual reference file. `key` is the filename without extension (e.g., `shared-prompt-sections`). |
|
|
144
|
+
|
|
145
|
+
### Recognized Keys
|
|
146
|
+
|
|
147
|
+
| Key | Corresponding File |
|
|
148
|
+
|-----|--------------------|
|
|
149
|
+
| `shared-prompt-sections` | `{REFERENCES_DIR}/shared-prompt-sections.md` |
|
|
150
|
+
| `file-content-schema` | `{REFERENCES_DIR}/file-content-schema.md` |
|
|
151
|
+
| `xml-schema` | `{REFERENCES_DIR}/xml-schema.md` |
|
|
152
|
+
| `context-policy` | `{REFERENCES_DIR}/context-policy.md` |
|
|
153
|
+
| `work-activity-log` | `{REFERENCES_DIR}/work-activity-log.md` |
|
|
154
|
+
|
|
155
|
+
### Backward Compatibility
|
|
156
|
+
|
|
157
|
+
- Dispatch or task-result XML without `<ref-cache>` is fully valid — agents fall back to reading files from `REFERENCES_DIR`.
|
|
158
|
+
- Agents that do not yet support ref-cache simply ignore the element and read files normally.
|
|
159
|
+
- Partial ref-cache (only some keys present) is allowed — missing keys are read from disk.
|
package/bin/cli.mjs
CHANGED
package/lib/constants.mjs
CHANGED
|
@@ -31,34 +31,63 @@ export function getAgentsSrcDir(lang) {
|
|
|
31
31
|
return join(__dirname, '..', 'agents', lang);
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
export const CLAUDE_MD_SECTION_KO = `
|
|
35
|
-
## Agent 호출 규칙
|
|
36
34
|
|
|
37
|
-
|
|
35
|
+
/**
|
|
36
|
+
* Bash permissions required by uc-taskmanager agents.
|
|
37
|
+
* Merged into .claude/settings.local.json during init.
|
|
38
|
+
*
|
|
39
|
+
* Categories:
|
|
40
|
+
* - File discovery: ls, cat, basename, find, wc, sort, tail, head
|
|
41
|
+
* - Pattern matching: grep, sed, cut, tr
|
|
42
|
+
* - Formatting: printf, echo
|
|
43
|
+
* - Build/Lint: node, npm, bun, yarn, cargo, go, python, ruff, make
|
|
44
|
+
* - Git: git add, git commit, git log, git rev-parse
|
|
45
|
+
* - Network: curl (callback)
|
|
46
|
+
*/
|
|
47
|
+
export const REQUIRED_PERMISSIONS = [
|
|
48
|
+
// File read/write tools (project-root scoped)
|
|
49
|
+
'Read(/**)',
|
|
50
|
+
'Edit(/**)',
|
|
51
|
+
'Write(/**)',
|
|
52
|
+
'Read(**)',
|
|
53
|
+
'Edit(**)',
|
|
54
|
+
'Write(**)',
|
|
38
55
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
56
|
+
// File discovery & text utilities
|
|
57
|
+
'Bash(ls:*)',
|
|
58
|
+
'Bash(cat:*)',
|
|
59
|
+
'Bash(mkdir:*)',
|
|
60
|
+
'Bash(basename:*)',
|
|
61
|
+
'Bash(find:*)',
|
|
62
|
+
'Bash(wc:*)',
|
|
63
|
+
'Bash(sort:*)',
|
|
64
|
+
'Bash(tail:*)',
|
|
65
|
+
'Bash(head:*)',
|
|
66
|
+
'Bash(echo:*)',
|
|
67
|
+
'Bash(printf:*)',
|
|
44
68
|
|
|
45
|
-
|
|
46
|
-
|
|
69
|
+
// Pattern matching & text processing
|
|
70
|
+
'Bash(grep:*)',
|
|
71
|
+
'Bash(sed:*)',
|
|
72
|
+
'Bash(cut:*)',
|
|
73
|
+
'Bash(tr:*)',
|
|
47
74
|
|
|
48
|
-
|
|
49
|
-
|
|
75
|
+
// Build & Lint (auto-detect per project type)
|
|
76
|
+
'Bash(node:*)',
|
|
77
|
+
'Bash(npm run:*)',
|
|
78
|
+
'Bash(npm test:*)',
|
|
79
|
+
'Bash(bun run:*)',
|
|
80
|
+
'Bash(yarn:*)',
|
|
81
|
+
'Bash(cargo:*)',
|
|
82
|
+
'Bash(go build:*)',
|
|
83
|
+
'Bash(go test:*)',
|
|
84
|
+
'Bash(python:*)',
|
|
85
|
+
'Bash(ruff:*)',
|
|
86
|
+
'Bash(make:*)',
|
|
50
87
|
|
|
51
|
-
|
|
88
|
+
// Git operations (committer)
|
|
89
|
+
'Bash(git:*)',
|
|
52
90
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
- Main Claude receives return values and invokes the next agent in sequence.
|
|
57
|
-
- Pipeline flow follows \`.claude/agents/agent-flow.md\`.
|
|
58
|
-
|
|
59
|
-
Examples: \`[new-feature]\`, \`[bugfix]\`, \`[enhancement]\`, \`[new-work]\`, etc.
|
|
60
|
-
`.trimStart();
|
|
61
|
-
|
|
62
|
-
export function getClaudeMdSection(lang) {
|
|
63
|
-
return lang === 'ko' ? CLAUDE_MD_SECTION_KO : CLAUDE_MD_SECTION_EN;
|
|
64
|
-
}
|
|
91
|
+
// Network (callback transmission)
|
|
92
|
+
'Bash(curl:*)',
|
|
93
|
+
];
|
package/lib/init.mjs
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, copyFileSync, readFileSync, writeFileSync } from 'node:fs';
|
|
1
|
+
import { existsSync, mkdirSync, copyFileSync, readFileSync, writeFileSync, readdirSync, statSync } from 'node:fs';
|
|
2
2
|
import { join, dirname } from 'node:path';
|
|
3
3
|
import { fileURLToPath } from 'node:url';
|
|
4
4
|
import { homedir } from 'node:os';
|
|
5
|
-
import { AGENT_FILES, getAgentsSrcDir,
|
|
5
|
+
import { AGENT_FILES, getAgentsSrcDir, REQUIRED_PERMISSIONS } from './constants.mjs';
|
|
6
6
|
|
|
7
7
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
8
|
|
|
9
|
+
import { createInterface } from 'node:readline';
|
|
10
|
+
|
|
9
11
|
const green = (s) => `\x1b[32m${s}\x1b[0m`;
|
|
10
12
|
const dim = (s) => `\x1b[2m${s}\x1b[0m`;
|
|
13
|
+
const yellow = (s) => `\x1b[33m${s}\x1b[0m`;
|
|
11
14
|
|
|
12
15
|
function copyAgents(destDir, lang) {
|
|
13
16
|
const srcDir = getAgentsSrcDir(lang);
|
|
@@ -22,6 +25,41 @@ function copyAgents(destDir, lang) {
|
|
|
22
25
|
return count;
|
|
23
26
|
}
|
|
24
27
|
|
|
28
|
+
function copyDirRecursive(src, dest) {
|
|
29
|
+
mkdirSync(dest, { recursive: true });
|
|
30
|
+
let count = 0;
|
|
31
|
+
for (const entry of readdirSync(src)) {
|
|
32
|
+
const srcPath = join(src, entry);
|
|
33
|
+
const destPath = join(dest, entry);
|
|
34
|
+
if (statSync(srcPath).isDirectory()) {
|
|
35
|
+
count += copyDirRecursive(srcPath, destPath);
|
|
36
|
+
} else {
|
|
37
|
+
copyFileSync(srcPath, destPath);
|
|
38
|
+
count++;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return count;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function copyPluginResources(destBaseDir) {
|
|
45
|
+
const pkgRoot = join(__dirname, '..');
|
|
46
|
+
let count = 0;
|
|
47
|
+
|
|
48
|
+
// .claude-plugin/
|
|
49
|
+
const pluginSrc = join(pkgRoot, '.claude-plugin');
|
|
50
|
+
if (existsSync(pluginSrc)) {
|
|
51
|
+
count += copyDirRecursive(pluginSrc, join(destBaseDir, '.claude-plugin'));
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// skills/
|
|
55
|
+
const skillsSrc = join(pkgRoot, 'skills');
|
|
56
|
+
if (existsSync(skillsSrc)) {
|
|
57
|
+
count += copyDirRecursive(skillsSrc, join(destBaseDir, 'skills'));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return count;
|
|
61
|
+
}
|
|
62
|
+
|
|
25
63
|
function ensureRouterConfig(projectDir) {
|
|
26
64
|
const configDir = join(projectDir, '.agent');
|
|
27
65
|
const configPath = join(configDir, 'router_rule_config.json');
|
|
@@ -41,31 +79,68 @@ function ensureWorksDir(projectDir) {
|
|
|
41
79
|
return true;
|
|
42
80
|
}
|
|
43
81
|
|
|
44
|
-
function
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
82
|
+
function mergePermissions(projectDir) {
|
|
83
|
+
const settingsPath = join(projectDir, '.claude', 'settings.local.json');
|
|
84
|
+
let settings = {};
|
|
85
|
+
|
|
86
|
+
if (existsSync(settingsPath)) {
|
|
87
|
+
try {
|
|
88
|
+
settings = JSON.parse(readFileSync(settingsPath, 'utf8'));
|
|
89
|
+
} catch {
|
|
90
|
+
settings = {};
|
|
51
91
|
}
|
|
52
|
-
writeFileSync(claudeMdPath, content.trimEnd() + '\n\n' + section);
|
|
53
|
-
} else {
|
|
54
|
-
writeFileSync(claudeMdPath, '# CLAUDE.md\n\n' + section);
|
|
55
92
|
}
|
|
56
|
-
|
|
93
|
+
|
|
94
|
+
if (!settings.permissions) settings.permissions = {};
|
|
95
|
+
if (!Array.isArray(settings.permissions.allow)) settings.permissions.allow = [];
|
|
96
|
+
|
|
97
|
+
const existing = new Set(settings.permissions.allow);
|
|
98
|
+
let added = 0;
|
|
99
|
+
|
|
100
|
+
for (const perm of REQUIRED_PERMISSIONS) {
|
|
101
|
+
if (!existing.has(perm)) {
|
|
102
|
+
settings.permissions.allow.push(perm);
|
|
103
|
+
added++;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (added > 0) {
|
|
108
|
+
const dir = dirname(settingsPath);
|
|
109
|
+
mkdirSync(dir, { recursive: true });
|
|
110
|
+
writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n');
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return { added, total: settings.permissions.allow.length };
|
|
57
114
|
}
|
|
58
115
|
|
|
59
|
-
|
|
116
|
+
async function promptPermissions() {
|
|
117
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
118
|
+
return new Promise((resolve) => {
|
|
119
|
+
console.log(`\n ${yellow('?')} Auto-configure Bash permissions for agents? (recommended)`);
|
|
120
|
+
console.log(` ${dim('Adds required permissions to .claude/settings.local.json')}`);
|
|
121
|
+
rl.question(' [Y/n] ', (answer) => {
|
|
122
|
+
rl.close();
|
|
123
|
+
const choice = answer.trim().toLowerCase();
|
|
124
|
+
resolve(choice === '' || choice === 'y' || choice === 'yes');
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export async function init(isGlobal, lang) {
|
|
60
130
|
const exampleTag = lang === 'ko'
|
|
61
131
|
? `[추가기능] Add a hello world feature`
|
|
62
132
|
: `[new-feature] Add a hello world feature`;
|
|
63
133
|
|
|
64
134
|
if (isGlobal) {
|
|
65
|
-
const
|
|
135
|
+
const globalClaudeDir = join(homedir(), '.claude');
|
|
136
|
+
const globalDir = join(globalClaudeDir, 'agents');
|
|
66
137
|
const count = copyAgents(globalDir, lang);
|
|
67
138
|
console.log(`\n Installing to ${dim('~/.claude/agents/')} (${lang}) ...`);
|
|
68
139
|
console.log(` ${green('✓')} ${count} agent files copied`);
|
|
140
|
+
const globalResCount = copyPluginResources(globalClaudeDir);
|
|
141
|
+
if (globalResCount > 0) {
|
|
142
|
+
console.log(` ${green('✓')} ${globalResCount} plugin resource files copied`);
|
|
143
|
+
}
|
|
69
144
|
console.log(`\n ${dim('Next steps:')}`);
|
|
70
145
|
console.log(` 1. Open any project and run ${dim("'claude'")}`);
|
|
71
146
|
console.log(` 2. Type: ${dim(exampleTag)}\n`);
|
|
@@ -80,24 +155,36 @@ export function init(isGlobal, lang) {
|
|
|
80
155
|
const count = copyAgents(destDir, lang);
|
|
81
156
|
console.log(` ${green('✓')} ${count} agent files copied`);
|
|
82
157
|
|
|
158
|
+
const claudeDir = join(projectDir, '.claude');
|
|
159
|
+
const resCount = copyPluginResources(claudeDir);
|
|
160
|
+
if (resCount > 0) {
|
|
161
|
+
console.log(` ${green('✓')} ${resCount} plugin resource files copied (.claude-plugin, skills)`);
|
|
162
|
+
}
|
|
163
|
+
|
|
83
164
|
if (ensureRouterConfig(projectDir)) {
|
|
84
165
|
console.log(` ${green('✓')} .agent/router_rule_config.json created`);
|
|
85
166
|
} else {
|
|
86
167
|
console.log(` ${dim('-')} .agent/router_rule_config.json already exists`);
|
|
87
168
|
}
|
|
88
169
|
|
|
89
|
-
if (updateClaudeMd(projectDir, lang)) {
|
|
90
|
-
console.log(` ${green('✓')} CLAUDE.md updated`);
|
|
91
|
-
} else {
|
|
92
|
-
console.log(` ${dim('-')} CLAUDE.md already has agent rules`);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
170
|
if (ensureWorksDir(projectDir)) {
|
|
96
171
|
console.log(` ${green('✓')} works/ directory created`);
|
|
97
172
|
} else {
|
|
98
173
|
console.log(` ${dim('-')} works/ directory already exists`);
|
|
99
174
|
}
|
|
100
175
|
|
|
176
|
+
const wantPermissions = await promptPermissions();
|
|
177
|
+
if (wantPermissions) {
|
|
178
|
+
const { added, total } = mergePermissions(projectDir);
|
|
179
|
+
if (added > 0) {
|
|
180
|
+
console.log(` ${green('✓')} ${added} permissions added to .claude/settings.local.json (total: ${total})`);
|
|
181
|
+
} else {
|
|
182
|
+
console.log(` ${dim('-')} All permissions already configured (${total})`);
|
|
183
|
+
}
|
|
184
|
+
} else {
|
|
185
|
+
console.log(` ${dim('-')} Skipped permission setup`);
|
|
186
|
+
}
|
|
187
|
+
|
|
101
188
|
console.log(`\n ${dim('Next steps:')}`);
|
|
102
189
|
console.log(` 1. Run ${dim("'claude'")} to start Claude Code`);
|
|
103
190
|
console.log(` 2. Type: ${dim(exampleTag)}\n`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "uctm",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "Universal Claude Task Manager — SDD-based task pipeline subagent system for Claude Code CLI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -10,7 +10,9 @@
|
|
|
10
10
|
"bin/",
|
|
11
11
|
"lib/",
|
|
12
12
|
"agents/",
|
|
13
|
-
".agent/"
|
|
13
|
+
".agent/",
|
|
14
|
+
".claude-plugin/",
|
|
15
|
+
"skills/"
|
|
14
16
|
],
|
|
15
17
|
"engines": {
|
|
16
18
|
"node": ">=18"
|