@angar2/taskery 0.3.1 → 0.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/README.md +4 -1
- package/bin/backlog.js +131 -0
- package/bin/close.js +74 -0
- package/bin/fork.js +109 -0
- package/bin/init.js +31 -3
- package/bin/lib.js +613 -1
- package/bin/mcp.js +279 -0
- package/bin/plan.js +85 -0
- package/bin/set-status.js +83 -0
- package/bin/taskery.js +41 -2
- package/package.json +4 -2
- package/template/.claude/skills/run-team/SKILL.md +145 -0
- package/template/.project/rules/GIT_RULE.md +2 -1
- package/template/AGENTS.md +9 -0
- package/template/CLAUDE.md +17 -1
- package/template/shared/skills/add-backlog/SKILL.md +16 -87
- package/template/shared/skills/plan-init/SKILL.md +41 -116
- package/template/shared/skills/task-close/SKILL.md +52 -141
- package/template/shared/skills/task-dev/SKILL.md +4 -4
- package/template/shared/skills/task-init/SKILL.md +37 -244
- package/template/shared/skills/task-plan/SKILL.md +3 -2
- package/template/shared/skills/task-test/SKILL.md +5 -4
package/README.md
CHANGED
|
@@ -110,7 +110,7 @@ my-app/
|
|
|
110
110
|
├─ .gitignore
|
|
111
111
|
├─ .claude/
|
|
112
112
|
│ ├─ settings.json # hook 등록 (PreToolUse 매칭)
|
|
113
|
-
│ ├─ skills/<skill-name>/SKILL.md # 9 skill
|
|
113
|
+
│ ├─ skills/<skill-name>/SKILL.md # 9 skill (+ Claude 전용 run-team)
|
|
114
114
|
│ └─ hooks/<hook-name>.sh # 2 hook
|
|
115
115
|
└─ .project/
|
|
116
116
|
├─ PROJECT.md # 프로젝트 개요
|
|
@@ -192,9 +192,12 @@ my-app/
|
|
|
192
192
|
| `/task-close` | task | 최종 검증 후 git 커밋 + dev 브랜치 `--no-ff` 병합 |
|
|
193
193
|
| `/add-backlog` | meta | 사용자 발화로 plan(기능 그룹)별 `tasks/<NNN_slug>/BACKLOG.md`에 task 후보 1건씩 추가 (0.1.2+) |
|
|
194
194
|
| `/log-friction` | meta | 사용자 불편을 `.project/FRICTION_LOG.md`에 한 행 기록 |
|
|
195
|
+
| `/run-team` | meta | 여러 작업을 묶을 수 있는 단위로 task로 나눠 자동 병렬 처리 — 리더 세션이 팀원(독립 세션)에게 task를 1건씩 분배하고 흐름을 관리 (Claude 전용 · 실험 기능 전제 · 0.3.x+) |
|
|
195
196
|
|
|
196
197
|
각 스킬은 슬래시로 직접 호출하거나, 사용자 발화의 의미가 스킬의 frontmatter description과 매칭되면 메인 세션이 자동으로 발동시킨다.
|
|
197
198
|
|
|
199
|
+
> `/run-team`은 Claude의 실험적 멀티 세션 기능(agent teams)을 사용하는 Claude 전용 스킬이다. 기본 흐름이 아니라 *"이 작업들 한 번에 팀으로 진행해"* 같은 발화가 있을 때만 발동하며, 활성화에는 환경 변수(`CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1`) 설정이 필요하다. Codex 설치에는 포함되지 않는다.
|
|
200
|
+
|
|
198
201
|
`/project-init`, `/plan-init`, `/task-init` 세 스킬은 호출 시 다음 문서를 생성한다.
|
|
199
202
|
|
|
200
203
|
| 스킬 | 생성 위치 | 생성 문서 |
|
package/bin/backlog.js
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// 활성 plan별 BACKLOG.md 조작 CLI — add(추가) / get(조회) / mark(확인 마킹) 묶음
|
|
3
|
+
/**
|
|
4
|
+
* bin/backlog.js
|
|
5
|
+
* `npx @angar2/taskery backlog-add|backlog-get|backlog-mark ...` — 백로그 조작.
|
|
6
|
+
*
|
|
7
|
+
* dispatcher(taskery.js)가 서브op(add|get|mark)를 첫 인자로 주입한다.
|
|
8
|
+
* add --type <t> --title <제목> --slug <slug> --summary <개요> --target <대상영역>
|
|
9
|
+
* → withMetaLock + BL-NNN 채번 + append. stdout: { blNum, blId }
|
|
10
|
+
* get <BL-NNN> → 항목 메타 JSON ({ blId, status, type, title, slug, summary, target, taskNums }). 없으면 exit 1
|
|
11
|
+
* mark <BL-NNN> <TASK-NNN> → withMetaLock + [ ]→[x] + TASK 마크. stdout: { blId, taskNum }
|
|
12
|
+
*
|
|
13
|
+
* 채번·서식·락은 전부 코드(lib.js)가 보장 — LLM 손 파싱/Edit 제거.
|
|
14
|
+
* 실패(인자 미달 / 활성 plan 부재 / 항목 없음 등) 시 stderr + exit 1.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const path = require('path');
|
|
18
|
+
|
|
19
|
+
const {
|
|
20
|
+
MANIFEST_NAME,
|
|
21
|
+
readManifest,
|
|
22
|
+
getMainWorktreePath,
|
|
23
|
+
appendBacklogItem,
|
|
24
|
+
parseBacklogItem,
|
|
25
|
+
markBacklogChecked,
|
|
26
|
+
} = require('./lib');
|
|
27
|
+
|
|
28
|
+
function parseFlags(argv) {
|
|
29
|
+
// --key value | --key=value 둘 다 지원. 정의된 키만 수집.
|
|
30
|
+
const KEYS = ['type', 'title', 'slug', 'summary', 'target'];
|
|
31
|
+
const out = {};
|
|
32
|
+
for (let i = 0; i < argv.length; i++) {
|
|
33
|
+
const a = argv[i];
|
|
34
|
+
if (!a.startsWith('--')) continue;
|
|
35
|
+
const eq = a.indexOf('=');
|
|
36
|
+
let key;
|
|
37
|
+
let val;
|
|
38
|
+
if (eq !== -1) {
|
|
39
|
+
key = a.slice(2, eq);
|
|
40
|
+
val = a.slice(eq + 1);
|
|
41
|
+
} else {
|
|
42
|
+
key = a.slice(2);
|
|
43
|
+
val = argv[++i];
|
|
44
|
+
}
|
|
45
|
+
if (KEYS.includes(key)) out[key] = val;
|
|
46
|
+
}
|
|
47
|
+
return out;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function parseTaskNum(token) {
|
|
51
|
+
// 'TASK-007' | 'TASK-7' | '7' → 7
|
|
52
|
+
const m = String(token).match(/(\d+)/);
|
|
53
|
+
return m ? parseInt(m[1], 10) : NaN;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function resolveMainWt() {
|
|
57
|
+
let mainWt;
|
|
58
|
+
try {
|
|
59
|
+
mainWt = getMainWorktreePath();
|
|
60
|
+
} catch (e) {
|
|
61
|
+
console.error(`taskery backlog 실패: ${e.message}`);
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
if (!readManifest(path.join(mainWt, MANIFEST_NAME))) {
|
|
65
|
+
console.error(`${MANIFEST_NAME} 없음 — 'npx @angar2/taskery init' 먼저 실행 필요.`);
|
|
66
|
+
process.exit(1);
|
|
67
|
+
}
|
|
68
|
+
return mainWt;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async function doAdd(rest) {
|
|
72
|
+
const meta = parseFlags(rest);
|
|
73
|
+
const missing = ['type', 'title', 'slug', 'summary', 'target'].filter((k) => !meta[k]);
|
|
74
|
+
if (missing.length) {
|
|
75
|
+
console.error(
|
|
76
|
+
`사용법: npx @angar2/taskery backlog-add --type <t> --title <제목> --slug <slug> --summary <개요> --target <대상영역>`,
|
|
77
|
+
);
|
|
78
|
+
console.error(`누락: ${missing.map((k) => '--' + k).join(', ')}`);
|
|
79
|
+
process.exit(1);
|
|
80
|
+
}
|
|
81
|
+
const mainWt = resolveMainWt();
|
|
82
|
+
const blNum = await appendBacklogItem(mainWt, meta);
|
|
83
|
+
const blId = `BL-${String(blNum).padStart(3, '0')}`;
|
|
84
|
+
process.stdout.write(JSON.stringify({ blNum, blId }) + '\n');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function doGet(rest) {
|
|
88
|
+
const blId = rest[0];
|
|
89
|
+
if (!blId || !/^BL-\d+$/.test(blId)) {
|
|
90
|
+
console.error(`사용법: npx @angar2/taskery backlog-get <BL-NNN>`);
|
|
91
|
+
process.exit(1);
|
|
92
|
+
}
|
|
93
|
+
const mainWt = resolveMainWt();
|
|
94
|
+
const item = parseBacklogItem(mainWt, blId);
|
|
95
|
+
if (!item) {
|
|
96
|
+
console.error(`${blId} 항목 없음.`);
|
|
97
|
+
process.exit(1);
|
|
98
|
+
}
|
|
99
|
+
process.stdout.write(JSON.stringify(item) + '\n');
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async function doMark(rest) {
|
|
103
|
+
const blId = rest[0];
|
|
104
|
+
const taskNum = parseTaskNum(rest[1]);
|
|
105
|
+
if (!blId || !/^BL-\d+$/.test(blId) || !Number.isInteger(taskNum)) {
|
|
106
|
+
console.error(`사용법: npx @angar2/taskery backlog-mark <BL-NNN> <TASK-NNN>`);
|
|
107
|
+
process.exit(1);
|
|
108
|
+
}
|
|
109
|
+
const mainWt = resolveMainWt();
|
|
110
|
+
await markBacklogChecked(mainWt, blId, taskNum);
|
|
111
|
+
process.stdout.write(JSON.stringify({ blId, taskNum }) + '\n');
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async function main() {
|
|
115
|
+
const op = process.argv[2];
|
|
116
|
+
const rest = process.argv.slice(3);
|
|
117
|
+
try {
|
|
118
|
+
if (op === 'add') await doAdd(rest);
|
|
119
|
+
else if (op === 'get') doGet(rest);
|
|
120
|
+
else if (op === 'mark') await doMark(rest);
|
|
121
|
+
else {
|
|
122
|
+
console.error(`backlog: 알 수 없는 op '${op}' (add | get | mark)`);
|
|
123
|
+
process.exit(1);
|
|
124
|
+
}
|
|
125
|
+
} catch (e) {
|
|
126
|
+
console.error(`taskery backlog-${op} 실패: ${e.message}`);
|
|
127
|
+
process.exit(1);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
main();
|
package/bin/close.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// task close 결정적 준비 CLI — Phase 커밋·status=closed·flows/문서 커밋·추적마커 (비가역 머지/정리는 스킬)
|
|
3
|
+
/**
|
|
4
|
+
* bin/close.js
|
|
5
|
+
* `npx @angar2/taskery close <TASK-NNN>` — close 결정적 준비.
|
|
6
|
+
*
|
|
7
|
+
* 게이트(status=tested) → Phase 커밋(D2 자동 매핑) → status=closed → flows/문서 커밋 → 추적마커까지
|
|
8
|
+
* 코드가 수행하고 멈춘다. 이후 비가역(사전 rebase+충돌해결 / 머지 락 / dev --no-ff 머지 /
|
|
9
|
+
* worktree remove / branch -d)은 스킬(task-close)이 git-guard hook + 복구출력 보호 하에 수행.
|
|
10
|
+
*
|
|
11
|
+
* stdout: { prepped, branch, wtPath, registered, commits, aheadOfDev }. exit 0.
|
|
12
|
+
* 콜백(스킬이 LLM 판단 후 처리):
|
|
13
|
+
* - exit 3 + { blocked:'status', current } — status가 tested 아님 (게이트 차단)
|
|
14
|
+
* - exit 2 + { blocked:'mapping', files, phases, reason } — Phase↔파일 매핑 모호 (수동 커밋 필요)
|
|
15
|
+
* 그 외 실패 시 stderr + exit 1.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
const path = require('path');
|
|
19
|
+
|
|
20
|
+
const {
|
|
21
|
+
MANIFEST_NAME,
|
|
22
|
+
readManifest,
|
|
23
|
+
getMainWorktreePath,
|
|
24
|
+
closeTask,
|
|
25
|
+
} = require('./lib');
|
|
26
|
+
|
|
27
|
+
async function main() {
|
|
28
|
+
const arg = process.argv[2];
|
|
29
|
+
const m = String(arg || '').match(/(\d+)/);
|
|
30
|
+
if (!m) {
|
|
31
|
+
console.error('사용법: npx @angar2/taskery close <TASK-NNN>');
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
const taskNum = parseInt(m[1], 10);
|
|
35
|
+
|
|
36
|
+
let mainWt;
|
|
37
|
+
try {
|
|
38
|
+
mainWt = getMainWorktreePath();
|
|
39
|
+
} catch (e) {
|
|
40
|
+
console.error(`taskery close 실패: ${e.message}`);
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
if (!readManifest(path.join(mainWt, MANIFEST_NAME))) {
|
|
44
|
+
console.error(`${MANIFEST_NAME} 없음 — 'npx @angar2/taskery init' 먼저 실행 필요.`);
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
let result;
|
|
49
|
+
try {
|
|
50
|
+
result = await closeTask(mainWt, { taskNum });
|
|
51
|
+
} catch (e) {
|
|
52
|
+
console.error(`taskery close 실패: ${e.message}`);
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (result.blocked === 'status') {
|
|
57
|
+
console.error(
|
|
58
|
+
`close 게이트 차단 — status=${result.current} (tested 아님). /task-test 통과 후 재호출.`,
|
|
59
|
+
);
|
|
60
|
+
process.stdout.write(JSON.stringify(result) + '\n');
|
|
61
|
+
process.exit(3);
|
|
62
|
+
}
|
|
63
|
+
if (result.blocked === 'mapping') {
|
|
64
|
+
console.error(
|
|
65
|
+
`Phase↔파일 매핑 모호 (${result.reason}) — Dev Plan '파일' 필드 확인 후 수동 Phase 커밋 필요.`,
|
|
66
|
+
);
|
|
67
|
+
process.stdout.write(JSON.stringify(result) + '\n');
|
|
68
|
+
process.exit(2);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
process.stdout.write(JSON.stringify(result) + '\n');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
main();
|
package/bin/fork.js
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* bin/fork.js
|
|
4
|
+
* `npx @angar2/taskery fork <type> <dev> <src> <slug> [--size <s> --title "<t>" --promote]` — task 분기.
|
|
5
|
+
*
|
|
6
|
+
* [채번 → 워크트리·브랜치 생성]을 init 락으로 원자 실행 (병렬 task-init 번호 충돌 차단).
|
|
7
|
+
* --size·--title 주면 fork 직후 §1.3 빈 골격 task.md도 코드가 자동 생성(C1, 생성일·status=draft 자동).
|
|
8
|
+
* 성공 시 결과 JSON 한 줄을 stdout으로 출력 ({ taskNum, nnn, branch, wtPath, projectId, [docPath, registered, promoted | scaffoldError] }).
|
|
9
|
+
* 실패(정책 위배 / 동일 항목 진행중 / 동일 브랜치명 등) 시 stderr + exit 1.
|
|
10
|
+
* 골격 생성 실패는 워크트리 생성을 무효화하지 않음 — exit 0 + scaffoldError로 보고(스킬이 폴백 Write).
|
|
11
|
+
*
|
|
12
|
+
* 호출 주체: `/task-init` Step 6 (사용자 직접 호출도 가능하나 통상 스킬 경유).
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const path = require('path');
|
|
16
|
+
|
|
17
|
+
const {
|
|
18
|
+
MANIFEST_NAME,
|
|
19
|
+
readManifest,
|
|
20
|
+
getMainWorktreePath,
|
|
21
|
+
forkTask,
|
|
22
|
+
scaffoldTaskDoc,
|
|
23
|
+
} = require('./lib');
|
|
24
|
+
|
|
25
|
+
const VALID_TYPES = ['feature', 'bug', 'improve', 'refactor', 'docs', 'chore'];
|
|
26
|
+
const VALID_SIZES = ['micro', 'small', 'medium', 'large'];
|
|
27
|
+
|
|
28
|
+
async function main() {
|
|
29
|
+
// 위치 인자 4개 + 플래그(--size <s> / --title "<t>" / --promote).
|
|
30
|
+
const argv = process.argv.slice(2);
|
|
31
|
+
const positional = [];
|
|
32
|
+
const opts = {};
|
|
33
|
+
for (let i = 0; i < argv.length; i++) {
|
|
34
|
+
const a = argv[i];
|
|
35
|
+
if (a === '--promote') {
|
|
36
|
+
opts.promote = true;
|
|
37
|
+
} else if (a.startsWith('--')) {
|
|
38
|
+
opts[a.slice(2)] = argv[++i];
|
|
39
|
+
} else {
|
|
40
|
+
positional.push(a);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
const [type, dev, src, slug] = positional;
|
|
44
|
+
|
|
45
|
+
if (!type || !dev || !src || !slug) {
|
|
46
|
+
console.error(
|
|
47
|
+
'사용법: npx @angar2/taskery fork <type> <dev> <src> <slug> [--size <s> --title "<제목>" --promote]',
|
|
48
|
+
);
|
|
49
|
+
console.error('예: npx @angar2/taskery fork feature claude BL-003 login --size medium --title "로그인 기능"');
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
if (opts.size && !VALID_SIZES.includes(opts.size)) {
|
|
53
|
+
console.error(`--size는 ${VALID_SIZES.join(' | ')} 중 하나 (받음: ${opts.size})`);
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
56
|
+
if (!VALID_TYPES.includes(type)) {
|
|
57
|
+
console.error(`type은 ${VALID_TYPES.join(' | ')} 중 하나 (받음: ${type})`);
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
if (!/^(BL-\d+|RM-\d+|DR)$/.test(src)) {
|
|
61
|
+
console.error(`src는 BL-NNN | RM-NNN | DR 형식 (받음: ${src})`);
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
if (!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(slug)) {
|
|
65
|
+
console.error(`slug은 영어 kebab-case (받음: ${slug})`);
|
|
66
|
+
process.exit(1);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
let mainWt;
|
|
70
|
+
try {
|
|
71
|
+
mainWt = getMainWorktreePath();
|
|
72
|
+
} catch (e) {
|
|
73
|
+
console.error(`taskery fork 실패: ${e.message}`);
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
if (!readManifest(path.join(mainWt, MANIFEST_NAME))) {
|
|
77
|
+
console.error(`${MANIFEST_NAME} 없음 — 'npx @angar2/taskery init' 먼저 실행 필요.`);
|
|
78
|
+
process.exit(1);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
let result;
|
|
82
|
+
try {
|
|
83
|
+
result = await forkTask(mainWt, { type, dev, src, slug });
|
|
84
|
+
} catch (e) {
|
|
85
|
+
console.error(`taskery fork 실패: ${e.message}`);
|
|
86
|
+
process.exit(1);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// 골격 자동 생성 (--size·--title 제공 시). 워크트리는 이미 생성됐으므로 실패해도 exit 1 X.
|
|
90
|
+
let out = result;
|
|
91
|
+
if (opts.size && opts.title) {
|
|
92
|
+
try {
|
|
93
|
+
const doc = scaffoldTaskDoc(mainWt, result.wtPath, {
|
|
94
|
+
taskNum: result.taskNum,
|
|
95
|
+
slug,
|
|
96
|
+
title: opts.title,
|
|
97
|
+
type,
|
|
98
|
+
size: opts.size,
|
|
99
|
+
promoted: !!opts.promote,
|
|
100
|
+
});
|
|
101
|
+
out = { ...result, docPath: doc.file, registered: doc.registered, promoted: doc.promoted };
|
|
102
|
+
} catch (e) {
|
|
103
|
+
out = { ...result, scaffoldError: e.message };
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
process.stdout.write(JSON.stringify(out) + '\n');
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
main();
|
package/bin/init.js
CHANGED
|
@@ -161,6 +161,29 @@ async function main() {
|
|
|
161
161
|
console.log(` .gitignore — 사용자 선택으로 패스`);
|
|
162
162
|
}
|
|
163
163
|
|
|
164
|
+
// 4.7. MCP 서버 등록 — Claude는 .mcp.json 생성(첫 세션 승인 게이트), Codex는 명령 안내.
|
|
165
|
+
// MCP 도구 8종 = lib core의 구조화 입구(CLI와 동일 함수). 미등록 환경은 npx CLI가 폴백.
|
|
166
|
+
if (platforms.includes('claude')) {
|
|
167
|
+
const mcpJsonPath = path.join(cwd, '.mcp.json');
|
|
168
|
+
let mcpConfig = { mcpServers: {} };
|
|
169
|
+
if (fs.existsSync(mcpJsonPath)) {
|
|
170
|
+
// 기존 .mcp.json의 다른 서버 보존 — taskery 항목만 머지.
|
|
171
|
+
try {
|
|
172
|
+
mcpConfig = JSON.parse(fs.readFileSync(mcpJsonPath, 'utf8'));
|
|
173
|
+
} catch (e) {
|
|
174
|
+
mcpConfig = { mcpServers: {} };
|
|
175
|
+
}
|
|
176
|
+
if (!mcpConfig.mcpServers) mcpConfig.mcpServers = {};
|
|
177
|
+
}
|
|
178
|
+
if (!mcpConfig.mcpServers.taskery) {
|
|
179
|
+
mcpConfig.mcpServers.taskery = { command: 'npx', args: ['-y', '@angar2/taskery', 'mcp'] };
|
|
180
|
+
fs.writeFileSync(mcpJsonPath, JSON.stringify(mcpConfig, null, 2) + '\n');
|
|
181
|
+
console.log(` .mcp.json — taskery MCP 서버 등록 (첫 세션에서 승인 필요)`);
|
|
182
|
+
} else {
|
|
183
|
+
console.log(` .mcp.json — taskery 서버 이미 등록됨, 스킵`);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
164
187
|
// 5. 결과 보고
|
|
165
188
|
console.log(`\n✅ taskery init 완료`);
|
|
166
189
|
console.log(` 카피: ${copied}개 / 스킵: ${skipped}개`);
|
|
@@ -168,11 +191,16 @@ async function main() {
|
|
|
168
191
|
console.log(` manifest: ${MANIFEST_NAME}\n`);
|
|
169
192
|
const entryFile = platforms.includes('claude') ? 'CLAUDE.md' : 'AGENTS.md';
|
|
170
193
|
console.log(`다음 단계:`);
|
|
171
|
-
|
|
194
|
+
let step = 1;
|
|
195
|
+
console.log(` ${step++}. ${entryFile} 정독 + 프로젝트 메타 + 검증 명령 채우기`);
|
|
196
|
+
if (platforms.includes('claude')) {
|
|
197
|
+
console.log(` ${step++}. Claude 첫 세션에서 taskery MCP 서버 승인 (.mcp.json — 구조화 도구 8종; 미승인 시 npx CLI 폴백)`);
|
|
198
|
+
}
|
|
172
199
|
if (platforms.includes('codex')) {
|
|
173
|
-
console.log(`
|
|
200
|
+
console.log(` ${step++}. Codex 최초 1회 '/hooks'로 hook trust 승인 (.codex/config.toml — git-guard / closed-immutable)`);
|
|
201
|
+
console.log(` + MCP 등록: codex mcp add taskery -- npx -y @angar2/taskery mcp`);
|
|
174
202
|
}
|
|
175
|
-
console.log(`
|
|
203
|
+
console.log(` ${step++}. 에이전트 진입 → '/project-init' → '/plan-init <기능그룹>' → '/task-init'\n`);
|
|
176
204
|
}
|
|
177
205
|
|
|
178
206
|
main().catch((e) => {
|