@angar2/taskery 0.8.1 → 1.0.1

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.
Files changed (61) hide show
  1. package/README.md +437 -188
  2. package/bin/add.js +27 -99
  3. package/bin/backlog.js +240 -113
  4. package/bin/check.js +182 -0
  5. package/bin/commands.js +151 -0
  6. package/bin/finish.js +365 -0
  7. package/bin/init.js +99 -190
  8. package/bin/install.js +143 -0
  9. package/bin/lib.js +591 -1336
  10. package/bin/mcp.js +31 -263
  11. package/bin/orche.js +240 -0
  12. package/bin/plan.js +58 -120
  13. package/bin/prepare.js +329 -0
  14. package/bin/prune.js +46 -176
  15. package/bin/status.js +57 -151
  16. package/bin/taskery.js +76 -120
  17. package/bin/update.js +66 -269
  18. package/package.json +5 -5
  19. package/template/.taskery/BACKLOG.md +38 -0
  20. package/template/.taskery/rules/CHANGELOG_RULE.md +31 -0
  21. package/template/.taskery/rules/DEV_RULE.local.md +8 -0
  22. package/template/.taskery/rules/GIT_RULE.md +82 -0
  23. package/template/.taskery/rules/MOCKUP_RULE.md +19 -0
  24. package/template/.taskery/rules/TASKERY_RULE.md +112 -0
  25. package/template/.taskery/rules/TASK_DOC_RULE.md +104 -0
  26. package/template/.taskery/rules/TEST_RULE.local.md +16 -0
  27. package/template/AGENTS.md +15 -53
  28. package/template/shared/skills/add-backlog/SKILL.md +8 -38
  29. package/template/shared/skills/log-friction/SKILL.md +10 -88
  30. package/template/shared/skills/plan-init/SKILL.md +10 -98
  31. package/template/shared/skills/project-init/SKILL.md +13 -342
  32. package/template/shared/skills/task-close/SKILL.md +16 -319
  33. package/template/shared/skills/task-dev/SKILL.md +19 -233
  34. package/template/shared/skills/task-init/SKILL.md +11 -68
  35. package/template/shared/skills/task-orche/SKILL.md +35 -0
  36. package/template/shared/skills/task-plan/SKILL.md +19 -498
  37. package/template/shared/skills/task-test/SKILL.md +23 -342
  38. package/bin/close.js +0 -86
  39. package/bin/fork.js +0 -112
  40. package/bin/legacy-quotes.js +0 -60
  41. package/bin/migrate.js +0 -481
  42. package/bin/set-status.js +0 -98
  43. package/template/.claude/hooks/closed-immutable.sh +0 -77
  44. package/template/.claude/settings.json +0 -18
  45. package/template/.claude/skills/run-team/SKILL.md +0 -145
  46. package/template/.codex/agents/task-tester.toml +0 -41
  47. package/template/.codex/agents/test-plan-author.toml +0 -34
  48. package/template/.codex/config.toml +0 -17
  49. package/template/.codex/hooks/closed-immutable.sh +0 -66
  50. package/template/.project/changelog/.gitkeep +0 -0
  51. package/template/.project/flows/.gitkeep +0 -0
  52. package/template/.project/plans/.gitkeep +0 -0
  53. package/template/.project/rules/CHANGELOG_RULE.md +0 -97
  54. package/template/.project/rules/GIT_RULE.md +0 -325
  55. package/template/.project/rules/MOCKUP_RULE.md +0 -86
  56. package/template/.project/rules/TASKERY_RULE.md +0 -237
  57. package/template/.project/rules/TASK_DOC_RULE.md +0 -668
  58. package/template/.project/shared/received/completed/.gitkeep +0 -0
  59. package/template/.project/shared/sent/completed/.gitkeep +0 -0
  60. package/template/.project/tasks/.gitkeep +0 -0
  61. package/template/shared/hooks/git-guard.sh +0 -108
package/bin/update.js CHANGED
@@ -1,286 +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 readline = require('readline');
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
- retireLegacyAgentGuide,
46
- recomposeEntryDocs,
47
- planGitignore,
48
- planTestGuide,
49
- } = require('./migrate');
50
-
51
- async function confirm(msg) {
52
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
53
- return new Promise((resolve) => {
54
- rl.question(`${msg} (y/N) `, (ans) => {
55
- rl.close();
56
- resolve(ans.trim().toLowerCase() === 'y');
57
- });
58
- });
59
- }
5
+ const L = require('./lib');
6
+ const I = require('./install');
60
7
 
61
8
  async function main() {
62
9
  const cwd = process.cwd();
63
- const templateDir = findTemplateDir();
64
- const manifestPath = path.join(cwd, MANIFEST_NAME);
65
-
66
- // 1. manifest 검사
67
- const oldManifest = readManifest(manifestPath);
68
- if (!oldManifest) {
69
- console.error(
70
- `taskery update: ${MANIFEST_NAME} 없음 — 'npx @angar2/taskery init' 먼저 실행 필요.`,
71
- );
72
- process.exit(1);
73
- }
74
-
75
- // 2. 설치된 플랫폼 자산만 갱신 대상 (platforms 누락 시 마이그레이션 기본값)
76
- // 설치 계획(installRel 기준)으로 전개 — shared/는 플랫폼 경로로 매핑된 상태.
77
- // newTemplateFiles: installRel → { hash(소스), templateRel(복사 원본) }
78
- const platforms = Array.isArray(oldManifest.platforms)
79
- ? oldManifest.platforms
80
- : DEFAULT_PLATFORMS;
81
- const plan = resolveInstallPlan(walkTemplate(templateDir), platforms);
82
- const newTemplateFiles = {};
83
- for (const it of plan) {
84
- newTemplateFiles[it.installRel] = { hash: it.hash, templateRel: it.templateRel };
85
- }
86
- const oldFiles = oldManifest.files || {};
87
-
88
- console.log(`\ntaskery update`);
89
- console.log(` 현재 manifest 버전: ${oldManifest.version}`);
90
- console.log(` 새 패키지 버전: ${getPackageVersion()}\n`);
91
-
92
- const newFiles = {};
93
- const summary = { unchanged: 0, new: 0, autoUpdated: 0, customizedReplaced: 0, skipped: 0, removed: 0 };
94
-
95
- // 2.5. 진입 문서 재조립 — **본 루프보다 먼저, 그리고 매 update 항상.**
96
- // AGENTS.md는 리포 값을 품어 템플릿과 바이트 일치가 원리적으로 불가능하다. 본 루프에 맡기면
97
- // '사용자 customize 검출'이 떠서, 덮으면 리포 값이 .bak에만 남고 거절하면 구판 문안에 고착된다.
98
- // 재조립이 최종 상태까지 기록하고, 처리한 경로는 아래 루프에서 제외한다.
99
- const entryMig = recomposeEntryDocs(cwd, templateDir, platforms, oldManifest);
100
- const entryHandled = new Set(entryMig.handled);
101
- for (const line of entryMig.notes) console.log(` ${line}`);
102
- Object.assign(newFiles, entryMig.entries);
103
-
104
- // 3. 새 template 파일 분기 (rel = 설치 경로 installRel)
105
- for (const [rel, { hash: newHash, templateRel }] of Object.entries(newTemplateFiles)) {
106
- if (entryHandled.has(rel)) {
107
- // 2.5에서 이행 완료(또는 보류) — 본 루프가 다시 손대면 이행 결과를 덮는다.
108
- if (entryMig.held && oldFiles[rel]) newFiles[rel] = oldFiles[rel];
109
- continue;
110
- }
111
- if (isLocalOverride(rel)) {
112
- // template에 .local.md 들어가면 안 되지만 방어적 스킵
113
- console.log(` skip (.local override 영역): ${rel}`);
114
- summary.skipped++;
115
- continue;
116
- }
117
-
118
- const src = path.join(templateDir, templateRel);
119
- const dst = path.join(cwd, rel);
120
- const oldEntry = oldFiles[rel];
121
-
122
- // .gitignore는 *절대 덮어쓰지 않는다* — 사용자 무시 규칙이 통째로 날아간다.
123
- // 없으면 복사, 있으면 빠진 줄만 덧붙인다(migrate.mergeGitignore).
124
- if (rel === '.gitignore') {
125
- const r = mergeGitignore(src, dst);
126
- newFiles[rel] = { hash: newHash, core: true, managed: true };
127
- if (!r.merged) { summary.new++; console.log(` new: ${rel}`); }
128
- else if (r.added.length) { summary.autoUpdated++; console.log(` merge: ${rel} — ${r.added.length}줄 추가 (기존 줄 보존)`); }
129
- else { summary.unchanged++; }
130
- continue;
131
- }
132
-
133
- // a. 신규 — manifest에 없음
134
- if (!oldEntry) {
135
- copyFile(src, dst);
136
- newFiles[rel] = { hash: newHash, core: true, managed: true };
137
- summary.new++;
138
- console.log(` new: ${rel}`);
139
- continue;
140
- }
141
-
142
- const oldManifestHash = oldEntry.hash;
143
-
144
- // b. 동일 — new == manifest → 미터치 (manifest 항목 그대로)
145
- if (newHash === oldManifestHash) {
146
- newFiles[rel] = oldEntry;
147
- summary.unchanged++;
148
- continue;
149
- }
150
-
151
- // c. 변경 — new != manifest
152
- // 사용자 측 해시 비교
153
- let userHash = null;
154
- if (fs.existsSync(dst)) {
155
- userHash = sha256(dst);
156
- }
157
-
158
- if (userHash === oldManifestHash) {
159
- // 사용자 customize 없음 → 자동 갱신
160
- copyFile(src, dst);
161
- newFiles[rel] = { hash: newHash, core: true, managed: true };
162
- summary.autoUpdated++;
163
- console.log(` auto-update: ${rel}`);
164
- } else {
165
- // 사용자 customize 검출 — *.bak 백업 + confirm
166
- console.log(`\n ⚠ ${rel} — 사용자 customize 검출`);
167
- console.log(` manifest 해시: ${oldManifestHash}`);
168
- console.log(` 사용자 해시: ${userHash}`);
169
- console.log(` 새 코어 해시: ${newHash}`);
170
- const ok = await confirm(` *.bak 백업 후 새 코어로 덮어쓸까?`);
171
- if (ok) {
172
- // .bak 백업
173
- if (fs.existsSync(dst)) {
174
- 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} (프로젝트 절 유지)`);
175
37
  }
176
- copyFile(src, dst);
177
- newFiles[rel] = { hash: newHash, core: true, managed: true };
178
- summary.customizedReplaced++;
179
- console.log(` replaced (.bak 백업): ${rel}`);
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;
180
53
  } else {
181
- // 사용자 customize 보존 — manifest 항목은 *그대로* 유지 (다음 update가 다시 비교)
182
- newFiles[rel] = oldEntry;
183
- summary.skipped++;
184
- console.log(` skipped (사용자 보존): ${rel}`);
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
+ }
185
64
  }
186
65
  }
187
- }
188
-
189
- // 4. 사라짐 검사 — manifest에 있는데 새 template에 없음
190
- for (const rel of Object.keys(oldFiles)) {
191
- if (!(rel in newTemplateFiles)) {
192
- console.log(` ⚠ removed in new version (사용자 파일 보존): ${rel}`);
193
- summary.removed++;
194
- // manifest에서도 제거 (next update 시 다시 검사 X)
66
+ for (const rel of Object.keys(old)) {
67
+ if (!plan.some((it) => it.dst === rel)) notes.push(`이 버전에서 빠진 파일 — 지우지 않고 둔다: ${rel}`);
195
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();
196
77
  }
197
-
198
- // 5. manifest 갱신 — 멀티세션(0.1.2+) / 플랫폼(0.2.0+) 필드 누락 시 자동 마이그레이션
199
- const migration = { projectId: false, stale_days: false, lock_timeout_ms: false, platforms: false };
200
- const projectId = oldManifest.projectId || (migration.projectId = true, generateProjectId());
201
- const stale_days =
202
- typeof oldManifest.stale_days === 'number'
203
- ? oldManifest.stale_days
204
- : (migration.stale_days = true, DEFAULT_STALE_DAYS);
205
- const lock_timeout_ms =
206
- typeof oldManifest.lock_timeout_ms === 'number'
207
- ? oldManifest.lock_timeout_ms
208
- : (migration.lock_timeout_ms = true, DEFAULT_LOCK_TIMEOUT_MS);
209
- if (!Array.isArray(oldManifest.platforms)) migration.platforms = true;
210
-
211
- // 활성 plan(0.7.0+) — 기존 값이 있으면 **반드시 그대로 보존**한다. newManifest는 아래에서
212
- // 명시 필드 목록으로 새로 조립되므로, 여기서 넘기지 않으면 update 한 번에 활성 plan이
213
- // 소리 없이 사라지고 구형 AGENT-GUIDE 폴백의 낡은 값으로 되돌아간다.
214
- const { activePlan, ingested: activePlanIngested } = ingestActivePlan(cwd, oldManifest);
215
- if (activePlanIngested) migration.activePlan = true;
216
- const retiredGuide = retireLegacyAgentGuide(cwd, activePlan);
217
- if (retiredGuide) console.log(`\n ${retiredGuide}`);
218
-
219
- const newManifest = {
220
- version: getPackageVersion(),
221
- installed_at: oldManifest.installed_at,
222
- updated_at: new Date().toISOString(),
223
- projectId,
224
- platforms,
225
- stale_days,
226
- lock_timeout_ms,
227
- activePlan: activePlan ?? null,
228
- // 진입 문서 이행 완료 마커 — 있으면 다음 update가 이행을 재실행하지 않는다.
229
- entryDocs: entryMig.done ? ENTRY_DOCS_MARKER : (oldManifest.entryDocs ?? null),
230
- files: newFiles,
231
- };
232
- writeManifest(newManifest, manifestPath);
233
-
234
- // 5.5. 부수 이행 — .gitignore 누락 패턴 / TEST-GUIDE 이관. 둘 다 사용자 승인 후에만.
235
- const gi = planGitignore(cwd, platforms);
236
- if (gi.missing.length > 0) {
237
- console.log(`\n taskery 자산 중 .gitignore에 없는 항목: ${gi.missing.join(', ')}`);
238
- console.log(` (등록하지 않으면 이 파일들이 커밋에 섞여 들어갑니다.)`);
239
- if (await confirm(` .gitignore에 추가할까?`)) {
240
- gi.apply();
241
- console.log(` .gitignore 갱신 — ${gi.missing.length}개 패턴 추가`);
242
- }
243
- }
244
-
245
- const tg = planTestGuide(cwd);
246
- if (tg.conflict) {
247
- // 자동 병합은 사용자 파일을 훼손할 수 있어 하지 않는다. 다만 침묵하면 TEST-GUIDE에 쌓인
248
- // 내용이 아무도 안 읽는 채로 남으므로 반드시 알린다.
249
- console.log(
250
- `\n ⚠ .project/TEST-GUIDE.md(폐지)와 .project/rules/TEST_RULE.local.md가 모두 있습니다.`,
251
- );
252
- console.log(` TEST-GUIDE 쪽 내용은 이제 아무 스킬도 읽지 않습니다 — 필요한 항목을 직접 옮겨주세요.`);
253
- }
254
- if (tg.available) {
255
- console.log(`\n .project/TEST-GUIDE.md는 0.7.0에서 폐지되고 역할이 TEST_RULE.local.md로 옮겨졌습니다.`);
256
- console.log(` 지금 옮기지 않으면 /task-plan·/task-dev가 기존 내용을 읽지 않습니다. (원본은 보존)`);
257
- if (await confirm(` 내용을 .project/rules/TEST_RULE.local.md로 이관할까?`)) {
258
- const appended = tg.apply();
259
- console.log(` 이관 완료 — .project/rules/TEST_RULE.local.md (원본 TEST-GUIDE.md 보존)`);
260
- if (appended.length > 0) {
261
- console.log(` 누락 절 골격 ${appended.length}개 추가(${appended.join(' / ')}) — 내용은 직접 채워주세요.`);
262
- }
263
- }
264
- }
265
-
266
- const migratedFields = Object.entries(migration)
267
- .filter(([, added]) => added)
268
- .map(([k]) => k);
269
- if (migratedFields.length > 0) {
270
- console.log(` manifest 마이그레이션: ${migratedFields.join(', ')} 필드 추가`);
271
- }
272
-
273
- // 결과 보고
274
- console.log(`\n✅ taskery update 완료`);
275
- console.log(` 동일: ${summary.unchanged}, 신규: ${summary.new}, 자동 갱신: ${summary.autoUpdated}`);
276
- console.log(` *.bak 후 갱신: ${summary.customizedReplaced}, 사용자 보존: ${summary.skipped}`);
277
- if (summary.removed > 0) {
278
- console.log(` ⚠ 새 버전에서 사라진 파일: ${summary.removed} (사용자 파일 그대로 보존)`);
279
- }
280
- console.log();
281
78
  }
282
79
 
283
80
  main().catch((e) => {
284
- console.error(`taskery update 실패: ${e.message}`);
81
+ console.error(e instanceof L.TaskeryError ? e.message : `taskery update 실패: ${e.stack || e.message}`);
285
82
  process.exit(1);
286
83
  });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@angar2/taskery",
3
- "version": "0.8.1",
4
- "description": "Lightweight task system for Claude Code & Codex — 1 메인 세션(또는 멀티세션) + 스킬 9종 + catastrophic hook 안전망 2종",
3
+ "version": "1.0.1",
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,8 @@
1
+ # DEV_RULE.local
2
+
3
+ > 이 리포의 구현 규칙이다. `task-dev`가 읽는다. 이 리포가 소유하는 파일이라 `update`가 건드리지 않는다.
4
+ > 아는 내용만 적는다. 코드·테스트 방식에 관한 새 규칙은 AI가 문장을 제안하고 사용자가 승인한 뒤 넣는다.
5
+
6
+ ## 구현 규칙
7
+
8
+ <!-- 코드 구조·이름 관행·금지 패턴처럼 이 프로젝트에서만 지키는 것 -->
@@ -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판 — 위치·형식만 남겼다. 목업 질문 강제·생성 흐름·두 시점 활용·시각 수정 사이클 예고·정독 의무를 뺐다 |