@angar2/taskery 0.8.0 → 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/README.md +423 -188
- package/bin/add.js +27 -99
- package/bin/backlog.js +240 -113
- package/bin/check.js +166 -0
- package/bin/commands.js +151 -0
- package/bin/finish.js +353 -0
- package/bin/init.js +99 -190
- package/bin/install.js +143 -0
- package/bin/lib.js +515 -1320
- package/bin/mcp.js +31 -263
- package/bin/orche.js +240 -0
- package/bin/plan.js +58 -120
- package/bin/prepare.js +326 -0
- package/bin/prune.js +46 -176
- package/bin/status.js +57 -151
- package/bin/taskery.js +76 -120
- package/bin/update.js +66 -266
- package/package.json +5 -5
- package/template/.taskery/BACKLOG.md +38 -0
- package/template/.taskery/rules/CHANGELOG_RULE.md +31 -0
- package/template/.taskery/rules/DEV_RULE.local.md +8 -0
- package/template/.taskery/rules/GIT_RULE.md +82 -0
- package/template/.taskery/rules/MOCKUP_RULE.md +19 -0
- package/template/.taskery/rules/TASKERY_RULE.md +112 -0
- package/template/.taskery/rules/TASK_DOC_RULE.md +84 -0
- package/template/.taskery/rules/TEST_RULE.local.md +16 -0
- package/template/AGENTS.md +15 -53
- package/template/shared/skills/add-backlog/SKILL.md +8 -38
- package/template/shared/skills/log-friction/SKILL.md +10 -88
- package/template/shared/skills/plan-init/SKILL.md +10 -98
- package/template/shared/skills/project-init/SKILL.md +13 -342
- package/template/shared/skills/task-close/SKILL.md +16 -319
- package/template/shared/skills/task-dev/SKILL.md +19 -233
- package/template/shared/skills/task-init/SKILL.md +11 -68
- package/template/shared/skills/task-orche/SKILL.md +35 -0
- package/template/shared/skills/task-plan/SKILL.md +19 -498
- package/template/shared/skills/task-test/SKILL.md +23 -342
- package/bin/close.js +0 -86
- package/bin/fork.js +0 -112
- package/bin/migrate.js +0 -344
- package/bin/set-status.js +0 -98
- package/template/.claude/hooks/closed-immutable.sh +0 -77
- package/template/.claude/settings.json +0 -18
- package/template/.claude/skills/run-team/SKILL.md +0 -145
- package/template/.codex/agents/task-tester.toml +0 -41
- package/template/.codex/agents/test-plan-author.toml +0 -34
- package/template/.codex/config.toml +0 -17
- package/template/.codex/hooks/closed-immutable.sh +0 -66
- package/template/.project/changelog/.gitkeep +0 -0
- package/template/.project/flows/.gitkeep +0 -0
- package/template/.project/plans/.gitkeep +0 -0
- package/template/.project/rules/CHANGELOG_RULE.md +0 -97
- package/template/.project/rules/GIT_RULE.md +0 -325
- package/template/.project/rules/MOCKUP_RULE.md +0 -86
- package/template/.project/rules/TASKERY_RULE.md +0 -237
- package/template/.project/rules/TASK_DOC_RULE.md +0 -668
- package/template/.project/shared/received/completed/.gitkeep +0 -0
- package/template/.project/shared/sent/completed/.gitkeep +0 -0
- package/template/.project/tasks/.gitkeep +0 -0
- package/template/shared/hooks/git-guard.sh +0 -108
package/bin/update.js
CHANGED
|
@@ -1,283 +1,83 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
* bin/update.js
|
|
4
|
-
* `npx @angar2/taskery update` — template/ 최신 자산과 사용자 측 비교 + 머지 갱신.
|
|
5
|
-
*
|
|
6
|
-
* 머지 로직 (DISTRIBUTION.md §6):
|
|
7
|
-
* 1. .taskery-manifest.json (cwd) 읽기 — 없으면 에러 ('init' 먼저)
|
|
8
|
-
* 2. template/ 정독 → 새 해시 맵
|
|
9
|
-
* 3. 각 파일 분기:
|
|
10
|
-
* a. 동일 (new == manifest) → 미터치
|
|
11
|
-
* b. 신규 (manifest에 없음) → 카피 + manifest에 추가
|
|
12
|
-
* c. 변경 (new != manifest):
|
|
13
|
-
* - 사용자 측 == manifest 해시 → 사용자 customize 없음 → 자동 갱신
|
|
14
|
-
* - 사용자 측 != manifest 해시 → 사용자 customize → *.bak 백업 + confirm
|
|
15
|
-
* d. 사라짐 (manifest에 있는데 template에 없음) → 사용자에게 경고만, 사용자 파일 보존
|
|
16
|
-
* 4. *.local.md suffix → manifest에 없음 → 미터치 (방어)
|
|
17
|
-
* 5. manifest 갱신 + 결과 요약
|
|
18
|
-
*/
|
|
19
|
-
|
|
2
|
+
// update — 설치된 taskery 파일을 이 버전으로 갱신한다. 사용자가 고친 파일은 묻고, AGENTS.md는 프로젝트 절을 살려 재조립한다
|
|
20
3
|
const fs = require('fs');
|
|
21
4
|
const path = require('path');
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
const {
|
|
25
|
-
MANIFEST_NAME,
|
|
26
|
-
findTemplateDir,
|
|
27
|
-
walkTemplate,
|
|
28
|
-
sha256,
|
|
29
|
-
copyFile,
|
|
30
|
-
writeManifest,
|
|
31
|
-
readManifest,
|
|
32
|
-
getPackageVersion,
|
|
33
|
-
isLocalOverride,
|
|
34
|
-
generateProjectId,
|
|
35
|
-
DEFAULT_STALE_DAYS,
|
|
36
|
-
DEFAULT_LOCK_TIMEOUT_MS,
|
|
37
|
-
DEFAULT_PLATFORMS,
|
|
38
|
-
resolveInstallPlan,
|
|
39
|
-
} = require('./lib');
|
|
40
|
-
|
|
41
|
-
const {
|
|
42
|
-
ENTRY_DOCS_MARKER,
|
|
43
|
-
mergeGitignore,
|
|
44
|
-
ingestActivePlan,
|
|
45
|
-
migrateEntryDocs,
|
|
46
|
-
planGitignore,
|
|
47
|
-
planTestGuide,
|
|
48
|
-
} = require('./migrate');
|
|
49
|
-
|
|
50
|
-
async function confirm(msg) {
|
|
51
|
-
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
52
|
-
return new Promise((resolve) => {
|
|
53
|
-
rl.question(`${msg} (y/N) `, (ans) => {
|
|
54
|
-
rl.close();
|
|
55
|
-
resolve(ans.trim().toLowerCase() === 'y');
|
|
56
|
-
});
|
|
57
|
-
});
|
|
58
|
-
}
|
|
5
|
+
const L = require('./lib');
|
|
6
|
+
const I = require('./install');
|
|
59
7
|
|
|
60
8
|
async function main() {
|
|
61
9
|
const cwd = process.cwd();
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
console.log(` 새 패키지 버전: ${getPackageVersion()}\n`);
|
|
90
|
-
|
|
91
|
-
const newFiles = {};
|
|
92
|
-
const summary = { unchanged: 0, new: 0, autoUpdated: 0, customizedReplaced: 0, skipped: 0, removed: 0 };
|
|
93
|
-
|
|
94
|
-
// 2.5. 0.7.0 진입 문서 재편 — **본 루프보다 먼저.**
|
|
95
|
-
// CLAUDE.md/AGENTS.md를 본 루프에 맡기면 '사용자 customize 검출' 프롬프트가 뜨는데,
|
|
96
|
-
// 덮으면 리포 값이 .bak에만 남고 안 덮으면 구판 전문이 새 판과 함께 남아 규칙이 충돌한다.
|
|
97
|
-
// 이행이 최종 상태까지 기록하고, 처리한 경로는 아래 루프에서 제외한다.
|
|
98
|
-
const entryMig = migrateEntryDocs(cwd, templateDir, platforms, oldManifest);
|
|
99
|
-
const entryHandled = new Set(entryMig.handled);
|
|
100
|
-
for (const line of entryMig.notes) console.log(` ${line}`);
|
|
101
|
-
Object.assign(newFiles, entryMig.entries);
|
|
102
|
-
|
|
103
|
-
// 3. 새 template 파일 분기 (rel = 설치 경로 installRel)
|
|
104
|
-
for (const [rel, { hash: newHash, templateRel }] of Object.entries(newTemplateFiles)) {
|
|
105
|
-
if (entryHandled.has(rel)) {
|
|
106
|
-
// 2.5에서 이행 완료(또는 보류) — 본 루프가 다시 손대면 이행 결과를 덮는다.
|
|
107
|
-
if (entryMig.held && oldFiles[rel]) newFiles[rel] = oldFiles[rel];
|
|
108
|
-
continue;
|
|
109
|
-
}
|
|
110
|
-
if (isLocalOverride(rel)) {
|
|
111
|
-
// template에 .local.md 들어가면 안 되지만 방어적 스킵
|
|
112
|
-
console.log(` skip (.local override 영역): ${rel}`);
|
|
113
|
-
summary.skipped++;
|
|
114
|
-
continue;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
const src = path.join(templateDir, templateRel);
|
|
118
|
-
const dst = path.join(cwd, rel);
|
|
119
|
-
const oldEntry = oldFiles[rel];
|
|
120
|
-
|
|
121
|
-
// .gitignore는 *절대 덮어쓰지 않는다* — 사용자 무시 규칙이 통째로 날아간다.
|
|
122
|
-
// 없으면 복사, 있으면 빠진 줄만 덧붙인다(migrate.mergeGitignore).
|
|
123
|
-
if (rel === '.gitignore') {
|
|
124
|
-
const r = mergeGitignore(src, dst);
|
|
125
|
-
newFiles[rel] = { hash: newHash, core: true, managed: true };
|
|
126
|
-
if (!r.merged) { summary.new++; console.log(` new: ${rel}`); }
|
|
127
|
-
else if (r.added.length) { summary.autoUpdated++; console.log(` merge: ${rel} — ${r.added.length}줄 추가 (기존 줄 보존)`); }
|
|
128
|
-
else { summary.unchanged++; }
|
|
129
|
-
continue;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// a. 신규 — manifest에 없음
|
|
133
|
-
if (!oldEntry) {
|
|
134
|
-
copyFile(src, dst);
|
|
135
|
-
newFiles[rel] = { hash: newHash, core: true, managed: true };
|
|
136
|
-
summary.new++;
|
|
137
|
-
console.log(` new: ${rel}`);
|
|
138
|
-
continue;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
const oldManifestHash = oldEntry.hash;
|
|
142
|
-
|
|
143
|
-
// b. 동일 — new == manifest → 미터치 (manifest 항목 그대로)
|
|
144
|
-
if (newHash === oldManifestHash) {
|
|
145
|
-
newFiles[rel] = oldEntry;
|
|
146
|
-
summary.unchanged++;
|
|
147
|
-
continue;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// c. 변경 — new != manifest
|
|
151
|
-
// 사용자 측 해시 비교
|
|
152
|
-
let userHash = null;
|
|
153
|
-
if (fs.existsSync(dst)) {
|
|
154
|
-
userHash = sha256(dst);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
if (userHash === oldManifestHash) {
|
|
158
|
-
// 사용자 customize 없음 → 자동 갱신
|
|
159
|
-
copyFile(src, dst);
|
|
160
|
-
newFiles[rel] = { hash: newHash, core: true, managed: true };
|
|
161
|
-
summary.autoUpdated++;
|
|
162
|
-
console.log(` auto-update: ${rel}`);
|
|
163
|
-
} else {
|
|
164
|
-
// 사용자 customize 검출 — *.bak 백업 + confirm
|
|
165
|
-
console.log(`\n ⚠ ${rel} — 사용자 customize 검출`);
|
|
166
|
-
console.log(` manifest 해시: ${oldManifestHash}`);
|
|
167
|
-
console.log(` 사용자 해시: ${userHash}`);
|
|
168
|
-
console.log(` 새 코어 해시: ${newHash}`);
|
|
169
|
-
const ok = await confirm(` *.bak 백업 후 새 코어로 덮어쓸까?`);
|
|
170
|
-
if (ok) {
|
|
171
|
-
// .bak 백업
|
|
172
|
-
if (fs.existsSync(dst)) {
|
|
173
|
-
fs.copyFileSync(dst, dst + '.bak');
|
|
10
|
+
const main = L.findMain(cwd);
|
|
11
|
+
L.checkLegacyInstall(main, 'update');
|
|
12
|
+
const manifest = L.readManifest(main);
|
|
13
|
+
if (!manifest) L.fail("update: taskery가 설치돼 있지 않다. 'npx @angar2/taskery init'을 먼저 부른다.");
|
|
14
|
+
const platforms = Array.isArray(manifest.platforms) ? manifest.platforms : ['claude'];
|
|
15
|
+
const old = manifest.files || {};
|
|
16
|
+
const files = {};
|
|
17
|
+
const notes = [];
|
|
18
|
+
const asker = I.makeAsker();
|
|
19
|
+
try {
|
|
20
|
+
const plan = I.installPlan(platforms);
|
|
21
|
+
for (const it of plan) {
|
|
22
|
+
const dst = path.join(main, it.dst);
|
|
23
|
+
const tpl = I.readTemplate(it.src);
|
|
24
|
+
const cur = fs.existsSync(dst) ? fs.readFileSync(dst, 'utf8') : null;
|
|
25
|
+
if (it.kind === 'once') {
|
|
26
|
+
if (cur == null) {
|
|
27
|
+
I.writeFile(main, it.dst, tpl);
|
|
28
|
+
notes.push(`새로 만듦: ${it.dst}`);
|
|
29
|
+
}
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
if (it.kind === 'agents') {
|
|
33
|
+
const next = I.recomposeAgents(tpl, cur);
|
|
34
|
+
if (next !== cur) {
|
|
35
|
+
I.writeFile(main, it.dst, next);
|
|
36
|
+
notes.push(`재조립: ${it.dst} (프로젝트 절 유지)`);
|
|
174
37
|
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
38
|
+
files[it.dst] = I.hashText(next);
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
const newHash = I.hashText(tpl);
|
|
42
|
+
const curHash = cur == null ? null : I.hashText(cur);
|
|
43
|
+
if (cur == null) {
|
|
44
|
+
I.writeFile(main, it.dst, tpl);
|
|
45
|
+
notes.push(`새로 만듦: ${it.dst}`);
|
|
46
|
+
files[it.dst] = newHash;
|
|
47
|
+
} else if (curHash === newHash) {
|
|
48
|
+
files[it.dst] = newHash;
|
|
49
|
+
} else if (curHash === old[it.dst]) {
|
|
50
|
+
I.writeFile(main, it.dst, tpl);
|
|
51
|
+
notes.push(`갱신: ${it.dst}`);
|
|
52
|
+
files[it.dst] = newHash;
|
|
179
53
|
} else {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
54
|
+
const ans = await asker.ask(`'${it.dst}'를 사용자가 고쳤다 — .bak으로 백업하고 새 판으로 바꿀까? (y/N) `, 'n');
|
|
55
|
+
if (ans.toLowerCase() === 'y') {
|
|
56
|
+
fs.copyFileSync(dst, `${dst}.bak`);
|
|
57
|
+
I.writeFile(main, it.dst, tpl);
|
|
58
|
+
notes.push(`바꿈(.bak 백업): ${it.dst}`);
|
|
59
|
+
files[it.dst] = newHash;
|
|
60
|
+
} else {
|
|
61
|
+
notes.push(`유지(사용자 수정): ${it.dst}`);
|
|
62
|
+
if (old[it.dst]) files[it.dst] = old[it.dst];
|
|
63
|
+
}
|
|
184
64
|
}
|
|
185
65
|
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
// 4. 사라짐 검사 — manifest에 있는데 새 template에 없음
|
|
189
|
-
for (const rel of Object.keys(oldFiles)) {
|
|
190
|
-
if (!(rel in newTemplateFiles)) {
|
|
191
|
-
console.log(` ⚠ removed in new version (사용자 파일 보존): ${rel}`);
|
|
192
|
-
summary.removed++;
|
|
193
|
-
// manifest에서도 제거 (next update 시 다시 검사 X)
|
|
66
|
+
for (const rel of Object.keys(old)) {
|
|
67
|
+
if (!plan.some((it) => it.dst === rel)) notes.push(`이 버전에서 빠진 파일 — 지우지 않고 둔다: ${rel}`);
|
|
194
68
|
}
|
|
69
|
+
notes.push(...I.writeConfigs(main, platforms));
|
|
70
|
+
const added = L.ensureExclude(main);
|
|
71
|
+
if (added.length) notes.push(`.git/info/exclude — ${added.join(' ')}`);
|
|
72
|
+
L.writeManifest(main, { ...manifest, version: L.getPackageVersion(), updated_at: new Date().toISOString(), platforms, files });
|
|
73
|
+
console.log(`taskery update 완료 — ${manifest.version} → ${L.getPackageVersion()}`);
|
|
74
|
+
for (const n of notes) console.log(`- ${n}`);
|
|
75
|
+
} finally {
|
|
76
|
+
asker.close();
|
|
195
77
|
}
|
|
196
|
-
|
|
197
|
-
// 5. manifest 갱신 — 멀티세션(0.1.2+) / 플랫폼(0.2.0+) 필드 누락 시 자동 마이그레이션
|
|
198
|
-
const migration = { projectId: false, stale_days: false, lock_timeout_ms: false, platforms: false };
|
|
199
|
-
const projectId = oldManifest.projectId || (migration.projectId = true, generateProjectId());
|
|
200
|
-
const stale_days =
|
|
201
|
-
typeof oldManifest.stale_days === 'number'
|
|
202
|
-
? oldManifest.stale_days
|
|
203
|
-
: (migration.stale_days = true, DEFAULT_STALE_DAYS);
|
|
204
|
-
const lock_timeout_ms =
|
|
205
|
-
typeof oldManifest.lock_timeout_ms === 'number'
|
|
206
|
-
? oldManifest.lock_timeout_ms
|
|
207
|
-
: (migration.lock_timeout_ms = true, DEFAULT_LOCK_TIMEOUT_MS);
|
|
208
|
-
if (!Array.isArray(oldManifest.platforms)) migration.platforms = true;
|
|
209
|
-
|
|
210
|
-
// 활성 plan(0.7.0+) — 기존 값이 있으면 **반드시 그대로 보존**한다. newManifest는 아래에서
|
|
211
|
-
// 명시 필드 목록으로 새로 조립되므로, 여기서 넘기지 않으면 update 한 번에 활성 plan이
|
|
212
|
-
// 소리 없이 사라지고 구형 AGENT-GUIDE 폴백의 낡은 값으로 되돌아간다.
|
|
213
|
-
const { activePlan, ingested: activePlanIngested } = ingestActivePlan(cwd, oldManifest);
|
|
214
|
-
if (activePlanIngested) migration.activePlan = true;
|
|
215
|
-
|
|
216
|
-
const newManifest = {
|
|
217
|
-
version: getPackageVersion(),
|
|
218
|
-
installed_at: oldManifest.installed_at,
|
|
219
|
-
updated_at: new Date().toISOString(),
|
|
220
|
-
projectId,
|
|
221
|
-
platforms,
|
|
222
|
-
stale_days,
|
|
223
|
-
lock_timeout_ms,
|
|
224
|
-
activePlan: activePlan ?? null,
|
|
225
|
-
// 진입 문서 이행 완료 마커 — 있으면 다음 update가 이행을 재실행하지 않는다.
|
|
226
|
-
entryDocs: entryMig.done ? ENTRY_DOCS_MARKER : (oldManifest.entryDocs ?? null),
|
|
227
|
-
files: newFiles,
|
|
228
|
-
};
|
|
229
|
-
writeManifest(newManifest, manifestPath);
|
|
230
|
-
|
|
231
|
-
// 5.5. 부수 이행 — .gitignore 누락 패턴 / TEST-GUIDE 이관. 둘 다 사용자 승인 후에만.
|
|
232
|
-
const gi = planGitignore(cwd, platforms);
|
|
233
|
-
if (gi.missing.length > 0) {
|
|
234
|
-
console.log(`\n taskery 자산 중 .gitignore에 없는 항목: ${gi.missing.join(', ')}`);
|
|
235
|
-
console.log(` (등록하지 않으면 이 파일들이 커밋에 섞여 들어갑니다.)`);
|
|
236
|
-
if (await confirm(` .gitignore에 추가할까?`)) {
|
|
237
|
-
gi.apply();
|
|
238
|
-
console.log(` .gitignore 갱신 — ${gi.missing.length}개 패턴 추가`);
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
const tg = planTestGuide(cwd);
|
|
243
|
-
if (tg.conflict) {
|
|
244
|
-
// 자동 병합은 사용자 파일을 훼손할 수 있어 하지 않는다. 다만 침묵하면 TEST-GUIDE에 쌓인
|
|
245
|
-
// 내용이 아무도 안 읽는 채로 남으므로 반드시 알린다.
|
|
246
|
-
console.log(
|
|
247
|
-
`\n ⚠ .project/TEST-GUIDE.md(폐지)와 .project/rules/TEST_RULE.local.md가 모두 있습니다.`,
|
|
248
|
-
);
|
|
249
|
-
console.log(` TEST-GUIDE 쪽 내용은 이제 아무 스킬도 읽지 않습니다 — 필요한 항목을 직접 옮겨주세요.`);
|
|
250
|
-
}
|
|
251
|
-
if (tg.available) {
|
|
252
|
-
console.log(`\n .project/TEST-GUIDE.md는 0.7.0에서 폐지되고 역할이 TEST_RULE.local.md로 옮겨졌습니다.`);
|
|
253
|
-
console.log(` 지금 옮기지 않으면 /task-plan·/task-dev가 기존 내용을 읽지 않습니다. (원본은 보존)`);
|
|
254
|
-
if (await confirm(` 내용을 .project/rules/TEST_RULE.local.md로 이관할까?`)) {
|
|
255
|
-
const appended = tg.apply();
|
|
256
|
-
console.log(` 이관 완료 — .project/rules/TEST_RULE.local.md (원본 TEST-GUIDE.md 보존)`);
|
|
257
|
-
if (appended.length > 0) {
|
|
258
|
-
console.log(` 누락 절 골격 ${appended.length}개 추가(${appended.join(' / ')}) — 내용은 직접 채워주세요.`);
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
const migratedFields = Object.entries(migration)
|
|
264
|
-
.filter(([, added]) => added)
|
|
265
|
-
.map(([k]) => k);
|
|
266
|
-
if (migratedFields.length > 0) {
|
|
267
|
-
console.log(` manifest 마이그레이션: ${migratedFields.join(', ')} 필드 추가`);
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
// 결과 보고
|
|
271
|
-
console.log(`\n✅ taskery update 완료`);
|
|
272
|
-
console.log(` 동일: ${summary.unchanged}, 신규: ${summary.new}, 자동 갱신: ${summary.autoUpdated}`);
|
|
273
|
-
console.log(` *.bak 후 갱신: ${summary.customizedReplaced}, 사용자 보존: ${summary.skipped}`);
|
|
274
|
-
if (summary.removed > 0) {
|
|
275
|
-
console.log(` ⚠ 새 버전에서 사라진 파일: ${summary.removed} (사용자 파일 그대로 보존)`);
|
|
276
|
-
}
|
|
277
|
-
console.log();
|
|
278
78
|
}
|
|
279
79
|
|
|
280
80
|
main().catch((e) => {
|
|
281
|
-
console.error(`taskery update 실패: ${e.message}`);
|
|
81
|
+
console.error(e instanceof L.TaskeryError ? e.message : `taskery update 실패: ${e.stack || e.message}`);
|
|
282
82
|
process.exit(1);
|
|
283
83
|
});
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@angar2/taskery",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "AI 코딩 에이전트(Claude Code·Codex)에게 일을 태스크 단위로 맡겨 기획·개발·테스트·병합까지 진행하게 하는 작업 체계",
|
|
5
5
|
"bin": {
|
|
6
6
|
"taskery": "bin/taskery.js",
|
|
7
7
|
"create-taskery": "bin/create.js"
|
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
],
|
|
15
15
|
"scripts": {
|
|
16
16
|
"smoke:init": "node bin/init.js",
|
|
17
|
-
"smoke:update": "node bin/update.js"
|
|
17
|
+
"smoke:update": "node bin/update.js",
|
|
18
|
+
"test": "node --test test/*.test.js"
|
|
18
19
|
},
|
|
19
20
|
"keywords": [
|
|
20
21
|
"claude-code",
|
|
@@ -22,8 +23,7 @@
|
|
|
22
23
|
"task-management",
|
|
23
24
|
"ai-coding-workflow",
|
|
24
25
|
"skills",
|
|
25
|
-
"slash-commands"
|
|
26
|
-
"agent-hooks"
|
|
26
|
+
"slash-commands"
|
|
27
27
|
],
|
|
28
28
|
"author": "angar2 <angaridev@gmail.com>",
|
|
29
29
|
"license": "MIT",
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# BACKLOG
|
|
2
|
+
|
|
3
|
+
> 이 프로젝트에서 발견한 문제와 할 일을 모으는 곳이다. 백로그는 이 파일 하나뿐이다.
|
|
4
|
+
|
|
5
|
+
## 쓰는 법
|
|
6
|
+
|
|
7
|
+
1. 새 항목은 `taskery backlog-add "<제목>"`으로 만든다. 명령이 번호·등록일·상태를 채운 빈 양식을 `## 열린 항목` 맨 위에 넣는다. 번호는 직접 짓지 않는다.
|
|
8
|
+
2. 필수 칸은 반드시 채우고, 모자라면 사용자에게 묻는다. 선택 칸은 대화·작업 맥락에 근거가 있으면 적거나 제안하고, 근거가 없으면 적지 않는다. 칸을 채우려고 지어내지 않는다. `<…>`를 남기지 않는다.
|
|
9
|
+
3. 등록할 때는 코드를 깊게 파지 않는다. 자세한 분석은 태스크의 계획 단계에서 한다.
|
|
10
|
+
4. `상태`와 `연결 태스크`는 명령이 바꾼다. `보류`·`취소`만 직접 적는다.
|
|
11
|
+
5. 연결 태스크가 모두 닫히고 그중 하나 이상이 끝난(포기가 아닌) 항목은 명령이 `## 끝난 항목`으로 옮긴다. 모두 포기로 닫혔으면 `대기`로 되돌린다. 평소에는 `## 열린 항목`만 읽는다.
|
|
12
|
+
|
|
13
|
+
## 항목 양식 (명령이 넣는 모양)
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
### BL-<번호> [<종류>] <제목>
|
|
17
|
+
- 상태: 대기
|
|
18
|
+
- 등록: <날짜>
|
|
19
|
+
- 현상: <현상>
|
|
20
|
+
- 연결 태스크: –
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
| 칸 | 필수 여부 | 채우는 규칙 |
|
|
24
|
+
|---|---|---|
|
|
25
|
+
| 번호·등록일·상태·연결 태스크 | 자동 | 명령이 채운다 |
|
|
26
|
+
| 종류 | 필수 | `bug`·`improve`·`feature`·`refactor`·`docs`·`chore` 중 하나 |
|
|
27
|
+
| 제목 | 필수 | 한 줄. 무엇이 문제인지, 또는 무엇을 원하는지 |
|
|
28
|
+
| 현상 | 필수 | 무엇이 문제인지(기능 요청이면 왜 필요한지). 사용자 말에 근거가 있어야 한다 |
|
|
29
|
+
| 플랜 | 선택 | 속할 플랜 폴더 이름, `다음 플랜 후보`, 또는 `미정` |
|
|
30
|
+
| 원하는 결과 | 선택 | 끝났을 때 사용자가 보게 될 결과(화면·동작으로) |
|
|
31
|
+
| 우선순위 | 선택 | `높음`·`보통`·`낮음` |
|
|
32
|
+
| 발견 경위 | 선택 | 어디서 알게 됐는지 한 줄 |
|
|
33
|
+
| 대상 영역 | 선택 | 관련 파일·폴더. 짐작이면 끝에 `(추정)` |
|
|
34
|
+
| 세부 항목 | 선택 | 여러 개가 한 묶음이면 번호 목록 |
|
|
35
|
+
|
|
36
|
+
## 열린 항목
|
|
37
|
+
|
|
38
|
+
## 끝난 항목
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# CHANGELOG_RULE
|
|
2
|
+
|
|
3
|
+
> 변경 기록의 형식이다. `close-task`가 끝난 태스크를 닫을 때 아래 "항목 틀"을 읽어 한 항목을 쓴다.
|
|
4
|
+
|
|
5
|
+
## 위치
|
|
6
|
+
|
|
7
|
+
- `.taskery/changelog/<YYYY-MM>.md` — 달마다 한 파일. 없으면 명령이 만든다.
|
|
8
|
+
- 최신 항목이 맨 위에 온다.
|
|
9
|
+
|
|
10
|
+
## 항목 틀
|
|
11
|
+
|
|
12
|
+
명령은 아래 코드 블록을 읽어 `{num}`(세 자리 번호)·`{title}`(태스크 제목)·`{date}`(닫은 날)·`{type}`(유형)·`{goal}`(`## 목표` 첫 줄)을 채운다. 틀을 고치면 다음 항목부터 그 모양으로 적힌다.
|
|
13
|
+
|
|
14
|
+
```markdown
|
|
15
|
+
## [TASK-{num}] {title}
|
|
16
|
+
|
|
17
|
+
- 날짜: {date}
|
|
18
|
+
- 유형: {type}
|
|
19
|
+
- 요약: {goal}
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## 쓰는 때
|
|
23
|
+
|
|
24
|
+
- 끝난 태스크(병합했거나, 병합이 필요 없는 태스크의 마무리를 마친 태스크)만 적는다. 포기로 닫은 태스크는 적지 않는다.
|
|
25
|
+
- 적힌 항목은 사람이나 AI가 보강해도 된다.
|
|
26
|
+
|
|
27
|
+
## 수정 이력
|
|
28
|
+
|
|
29
|
+
| 날짜 | 변경 사항 |
|
|
30
|
+
|---|---|
|
|
31
|
+
| 2026-10-01 | 1.0판 — 형식만 남기고, 명령이 읽는 "항목 틀" 코드 블록을 두었다. 위치는 월별 파일 하나로 |
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# GIT_RULE
|
|
2
|
+
|
|
3
|
+
> 이 리포의 git 규칙이다. 맨 위 표는 taskery 명령이 읽어 그대로 따르고, 나머지 글은 에이전트가 읽고 따른다.
|
|
4
|
+
> 이 리포만의 규칙은 표의 값을 고치거나 `GIT_RULE.local.md`에 적는다.
|
|
5
|
+
|
|
6
|
+
| 항목 | 값 |
|
|
7
|
+
|---|---|
|
|
8
|
+
| 통합 브랜치 | `dev` |
|
|
9
|
+
| 브랜치 이름 | `{type}/{dev}_TASK-{num}_{slug}` |
|
|
10
|
+
| 커밋 메시지 | `{tag}: [TASK-{num}] {summary}` |
|
|
11
|
+
| 병합 방식 | `no-ff` |
|
|
12
|
+
|
|
13
|
+
- **통합 브랜치**: 빈 폴더에서 `init`이 만드는 부모 브랜치 이름이다. 기존 리포에서 태스크의 부모는 언제나 본진(리포 원래 폴더)이 서 있는 브랜치다.
|
|
14
|
+
- **브랜치 이름**: `{type}`은 태스크 유형, `{dev}`는 작업자 식별자, `{num}`은 세 자리 태스크 번호, `{slug}`는 영문 kebab-case 이름이다. `{dev}`를 빼면 작업자 식별자를 받지 않는다.
|
|
15
|
+
- **커밋 메시지**: 첫 줄 형식이다. `{tag}`는 아래 커밋 태그, `{summary}`는 medium·large면 `Phase 1 - <Phase 이름>`, small이면 태스크 제목이다. 둘째 줄부터는 명령이 파일 목록과 `- 사유: <사유>`를 붙인다.
|
|
16
|
+
- **병합 방식**: `no-ff`(병합 커밋을 남긴다) 또는 `ff-only`. 명령은 로컬 병합만 한다.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## 브랜치
|
|
21
|
+
|
|
22
|
+
| 브랜치 | 용도 |
|
|
23
|
+
|---|---|
|
|
24
|
+
| `main` | 프로덕션 |
|
|
25
|
+
| `dev` | 통합 개발 — 태스크가 병합되는 기본 부모 |
|
|
26
|
+
| 태스크 브랜치 | 태스크 하나만 담당한다. 부모에서 갈라져 부모로 병합된다 |
|
|
27
|
+
|
|
28
|
+
### 태스크 유형
|
|
29
|
+
|
|
30
|
+
| 유형 | 뜻 |
|
|
31
|
+
|---|---|
|
|
32
|
+
| `feature` | 새 기능 |
|
|
33
|
+
| `bug` | 버그 수정 |
|
|
34
|
+
| `improve` | 기존 기능 개선 |
|
|
35
|
+
| `refactor` | 동작을 바꾸지 않는 구조 정리 |
|
|
36
|
+
| `docs` | 문서 |
|
|
37
|
+
| `chore` | 설정·의존성 등 잡일 |
|
|
38
|
+
|
|
39
|
+
### 작업자 식별자
|
|
40
|
+
|
|
41
|
+
실제 작업자 이름을 쓴다. 예: 사용자 `angar2`, Claude `claude`, 그 밖의 AI는 그 이름(`codex` 등).
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## 커밋
|
|
46
|
+
|
|
47
|
+
### 커밋 태그
|
|
48
|
+
|
|
49
|
+
| 태그 | 유형 |
|
|
50
|
+
|---|---|
|
|
51
|
+
| `feat:` | feature |
|
|
52
|
+
| `fix:` | bug |
|
|
53
|
+
| `improve:` | improve |
|
|
54
|
+
| `refactor:` | refactor |
|
|
55
|
+
| `docs:` | docs, chore |
|
|
56
|
+
|
|
57
|
+
### 예시
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
feat: [TASK-001] Phase 1 - 사용자 인증 로직
|
|
61
|
+
|
|
62
|
+
- src/auth/login.ts
|
|
63
|
+
- src/auth/session.ts
|
|
64
|
+
- 사유: 기존 세션 방식의 한계로 토큰 인증으로 바꾼다
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
- 커밋은 `commit-task`가 한다. 커밋 대상은 코드뿐이다 — taskery 파일(`.taskery/` 등)은 git 밖이다.
|
|
68
|
+
- 병합 커밋 메시지는 git 기본 메시지를 쓴다.
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## 금지
|
|
73
|
+
|
|
74
|
+
- 비밀값(`.env`, API 키 등)을 커밋하지 않는다.
|
|
75
|
+
- 여러 태스크를 한 브랜치에서 하지 않는다.
|
|
76
|
+
- 되돌릴 수 없는 git 명령(`reset --hard`·`clean -fd`·`push --force`·`branch -D`)은 사용자 승인을 받은 뒤에만 쓴다.
|
|
77
|
+
|
|
78
|
+
## 수정 이력
|
|
79
|
+
|
|
80
|
+
| 날짜 | 변경 사항 |
|
|
81
|
+
|---|---|
|
|
82
|
+
| 2026-10-01 | 1.0판 — 맨 위에 명령이 읽는 표(통합 브랜치·브랜치 이름·커밋 메시지·병합 방식)를 두고, taskery 내부 동작 설명(워크트리 위치·병합 잠금·경로 찾기·훅·검수 환경)과 태스크 문서·CHANGELOG 커밋 순서를 뺐다 |
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# MOCKUP_RULE
|
|
2
|
+
|
|
3
|
+
> 화면 목업의 위치와 형식이다. 사용자가 목업을 요청했을 때만 만들고, 그때 읽는다.
|
|
4
|
+
|
|
5
|
+
## 위치
|
|
6
|
+
|
|
7
|
+
- 태스크 문서 옆 `mockup/` 폴더에 `<태스크 문서 이름>-mockup.html`로 둔다. 예: `.taskery/plans/001_mvp/tasks/mockup/012_settings-save-mockup.html`. 폴더는 처음 쓸 때 만든다.
|
|
8
|
+
- 태스크 하나에 목업 파일 하나다. 화면이 여럿이면 한 파일 안에서 절로 나눈다.
|
|
9
|
+
|
|
10
|
+
## 형식
|
|
11
|
+
|
|
12
|
+
- 정적 HTML 한 파일이다. 스타일은 파일 안 `<style>`, 움직임이 필요하면 파일 안 `<script>`로 쓰고 외부 라이브러리를 쓰지 않는다.
|
|
13
|
+
- 레이아웃·색·간격·글꼴처럼 화면에서 보이는 것을 담는다. 클릭·호버는 흉내만 내도 된다.
|
|
14
|
+
|
|
15
|
+
## 수정 이력
|
|
16
|
+
|
|
17
|
+
| 날짜 | 변경 사항 |
|
|
18
|
+
|---|---|
|
|
19
|
+
| 2026-10-01 | 1.0판 — 위치·형식만 남겼다. 목업 질문 강제·생성 흐름·두 시점 활용·시각 수정 사이클 예고·정독 의무를 뺐다 |
|