@angar2/taskery 0.1.2 → 0.2.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 +21 -6
- package/bin/add.js +113 -0
- package/bin/init.js +44 -24
- package/bin/lib.js +84 -0
- package/bin/taskery.js +7 -2
- package/bin/update.js +20 -7
- package/package.json +3 -2
- package/template/.codex/agents/task-tester.toml +20 -0
- package/template/.codex/config.toml +17 -0
- package/template/.codex/hooks/closed-immutable.sh +66 -0
- package/template/.project/rules/GIT_RULE.md +8 -2
- package/template/.project/rules/MOCKUP_RULE.md +2 -1
- package/template/.project/rules/TASK_DOC_RULE.md +24 -0
- package/template/AGENTS.md +178 -0
- package/template/CLAUDE.md +2 -1
- package/template/{.claude → shared}/hooks/git-guard.sh +38 -4
- package/template/{.claude → shared}/skills/task-close/SKILL.md +16 -5
- package/template/{.claude → shared}/skills/task-dev/SKILL.md +1 -1
- package/template/{.claude → shared}/skills/task-init/SKILL.md +1 -1
- package/template/{.claude → shared}/skills/task-plan/SKILL.md +30 -1
- package/template/{.claude → shared}/skills/task-test/SKILL.md +2 -1
- /package/template/{.claude → shared}/skills/add-backlog/SKILL.md +0 -0
- /package/template/{.claude → shared}/skills/log-friction/SKILL.md +0 -0
- /package/template/{.claude → shared}/skills/plan-init/SKILL.md +0 -0
- /package/template/{.claude → shared}/skills/project-init/SKILL.md +0 -0
package/README.md
CHANGED
|
@@ -57,10 +57,13 @@ draft → planned → developing → developed → testing → tested → closed
|
|
|
57
57
|
# 새 프로젝트
|
|
58
58
|
npx -p @angar2/taskery create-taskery <project-name>
|
|
59
59
|
|
|
60
|
-
# 기존 프로젝트에 도입
|
|
60
|
+
# 기존 프로젝트에 도입 (init 시 에이전트 플랫폼 선택)
|
|
61
61
|
cd <project-name>
|
|
62
62
|
npx @angar2/taskery init
|
|
63
63
|
|
|
64
|
+
# 다른 플랫폼 자산 추가 (예: 기존 설치에 Codex 추가)
|
|
65
|
+
npx @angar2/taskery add codex
|
|
66
|
+
|
|
64
67
|
# 최신 버전 머지 갱신
|
|
65
68
|
npx @angar2/taskery update
|
|
66
69
|
|
|
@@ -79,10 +82,13 @@ npm install -g @angar2/taskery
|
|
|
79
82
|
# 새 프로젝트
|
|
80
83
|
create-taskery <project-name>
|
|
81
84
|
|
|
82
|
-
# 기존 프로젝트에 도입
|
|
85
|
+
# 기존 프로젝트에 도입 (init 시 에이전트 플랫폼 선택)
|
|
83
86
|
cd <project-name>
|
|
84
87
|
taskery init
|
|
85
88
|
|
|
89
|
+
# 다른 플랫폼 자산 추가 (예: 기존 설치에 Codex 추가)
|
|
90
|
+
taskery add codex
|
|
91
|
+
|
|
86
92
|
# 최신 버전 머지 갱신
|
|
87
93
|
taskery update
|
|
88
94
|
|
|
@@ -95,7 +101,7 @@ taskery prune
|
|
|
95
101
|
|
|
96
102
|
## 패키지 디렉토리 구조
|
|
97
103
|
|
|
98
|
-
`taskery init` 직후
|
|
104
|
+
`taskery init` 직후 골격 (아래는 Claude Code 선택 시 예시):
|
|
99
105
|
|
|
100
106
|
```
|
|
101
107
|
my-app/
|
|
@@ -128,6 +134,15 @@ my-app/
|
|
|
128
134
|
└─ FRICTION_LOG.md # taskery 불편사항 누적 로그
|
|
129
135
|
```
|
|
130
136
|
|
|
137
|
+
`.project/`는 플랫폼 무관 공통 자산이다. 진입 문서와 에이전트 자산 위치는 선택한 플랫폼에 따라 갈린다.
|
|
138
|
+
|
|
139
|
+
| 플랫폼 | 진입 문서 | 스킬 위치 | hook 위치 | hook 등록 |
|
|
140
|
+
|--------|----------|----------|----------|----------|
|
|
141
|
+
| Claude Code | `CLAUDE.md` | `.claude/skills/` | `.claude/hooks/` | `.claude/settings.json` |
|
|
142
|
+
| Codex | `AGENTS.md` | `.agents/skills/` | `.codex/hooks/` | `.codex/config.toml` |
|
|
143
|
+
|
|
144
|
+
> 스킬 9종과 `git-guard.sh`는 양 플랫폼 동일 자산이다. 진입 문서, hook 등록 방식, 완료 보호 hook(`closed-immutable.sh`)의 구현만 플랫폼별로 다르다. `init`에서 둘 다 선택하면 폴더가 갈려 충돌 없이 공존한다.
|
|
145
|
+
|
|
131
146
|
---
|
|
132
147
|
|
|
133
148
|
## 멀티세션 (병렬 작업)
|
|
@@ -135,7 +150,7 @@ my-app/
|
|
|
135
150
|
같은 프로젝트에서 여러 메인 세션을 동시에 운영해 독립 task를 병렬로 진행한다.
|
|
136
151
|
|
|
137
152
|
- 새 task를 시작하면(`/task-init`) 작업 폴더(`~/.taskery/worktrees/<projectId>/TASK-NNN_<출처>_<슬러그>/`)와 작업 브랜치를 함께 분기한다.
|
|
138
|
-
-
|
|
153
|
+
- 작업 폴더에서 새 메인 세션을 열어 진행하거나, 메인 세션 1개로 모든 작업을 지휘하거나, 메인이 서브 세션을 호출해 병렬로 진행한다 — 운영 방식은 자유. 메인 워크트리(원본 폴더)는 `dev` 전용 상태로 유지된다.
|
|
139
154
|
- task 완료(`/task-close`) 시 머지 락으로 직렬화한 후 dev에 `--no-ff` 병합한다. 다른 세션이 먼저 머지해 충돌이 발생하면 본 세션이 단순/의미적/판단 불가 3단계로 해결을 시도한다.
|
|
140
155
|
- 머지 직후 작업 폴더와 작업 브랜치는 자동 정리된다(보존 키워드 사용 시 양쪽 유지). 안전망으로 복구 명령이 함께 출력된다.
|
|
141
156
|
|
|
@@ -196,7 +211,7 @@ my-app/
|
|
|
196
211
|
| `git-guard.sh` | git 명령 실행 직전 | 주력 브랜치 직접 커밋 / `--force` / `--no-verify` / 강제 브랜치 삭제 / `reset --hard` / `clean -fd` |
|
|
197
212
|
| `closed-immutable.sh` | 파일 수정 직전 | 완료(`closed`)된 task.md 본 파일 재수정 (관련 spec-diff·스크린샷은 자유) |
|
|
198
213
|
|
|
199
|
-
hook은 catastrophic 사고만 차단한다. 정상 흐름에는 간섭하지 않는다.
|
|
214
|
+
hook은 catastrophic 사고만 차단한다. 정상 흐름에는 간섭하지 않는다. 등록 방식은 플랫폼별로 다르다 — Claude Code는 `.claude/settings.json`, Codex는 `.codex/config.toml`(최초 1회 `/hooks` 신뢰 승인 필요).
|
|
200
215
|
|
|
201
216
|
> 각 hook의 영역 분리 정신, 등록 매칭, 차단 정책, 예외 처리 절차는 [HOOKS.md](https://github.com/angar2/taskery/blob/main/plan/HOOKS.md)에서 확인 가능
|
|
202
217
|
|
|
@@ -210,7 +225,7 @@ cd <project-name>
|
|
|
210
225
|
npx @angar2/taskery init
|
|
211
226
|
```
|
|
212
227
|
|
|
213
|
-
이후 메인 세션에 진입(`CLAUDE.md` 자동 정독)하여 다음 시퀀스로 호출한다. task 5 스킬은 호출과 동시에 상태를 전이시킨다.
|
|
228
|
+
이후 메인 세션에 진입(진입 문서 `CLAUDE.md` 또는 `AGENTS.md` 자동 정독)하여 다음 시퀀스로 호출한다. task 5 스킬은 호출과 동시에 상태를 전이시킨다.
|
|
214
229
|
|
|
215
230
|
```
|
|
216
231
|
/project-init # 진입 메타 문서와 디렉토리 골격 생성
|
package/bin/add.js
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// bin/add.js
|
|
3
|
+
// `npx @angar2/taskery add <platform>` — 기존 설치 리포에 다른 플랫폼 자산 추가.
|
|
4
|
+
//
|
|
5
|
+
// 동작:
|
|
6
|
+
// 1. <platform> 인자 검증 (claude | codex)
|
|
7
|
+
// 2. .taskery-manifest.json 필수 — 없으면 'init' 안내
|
|
8
|
+
// 3. 이미 manifest.platforms에 있으면 변경 없이 종료
|
|
9
|
+
// 4. 해당 플랫폼 전용 자산만 카피 (공통 .project/는 init이 이미 설치)
|
|
10
|
+
// 5. manifest.platforms push + files 갱신
|
|
11
|
+
|
|
12
|
+
const fs = require('fs');
|
|
13
|
+
const path = require('path');
|
|
14
|
+
const readline = require('readline');
|
|
15
|
+
|
|
16
|
+
const {
|
|
17
|
+
MANIFEST_NAME,
|
|
18
|
+
findTemplateDir,
|
|
19
|
+
walkTemplate,
|
|
20
|
+
copyFile,
|
|
21
|
+
writeManifest,
|
|
22
|
+
readManifest,
|
|
23
|
+
getPackageVersion,
|
|
24
|
+
resolveInstallPlan,
|
|
25
|
+
PLATFORMS,
|
|
26
|
+
} = require('./lib');
|
|
27
|
+
|
|
28
|
+
async function confirm(msg) {
|
|
29
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
30
|
+
return new Promise((resolve) => {
|
|
31
|
+
rl.question(`${msg} (y/N) `, (ans) => {
|
|
32
|
+
rl.close();
|
|
33
|
+
resolve(ans.trim().toLowerCase() === 'y');
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async function main() {
|
|
39
|
+
const cwd = process.cwd();
|
|
40
|
+
const platform = (process.argv[2] || '').trim();
|
|
41
|
+
|
|
42
|
+
// 1. 플랫폼 인자 검증
|
|
43
|
+
if (!PLATFORMS.includes(platform)) {
|
|
44
|
+
console.error(
|
|
45
|
+
`taskery add: 플랫폼 인자 필요 (${PLATFORMS.join(' | ')}). 예: npx @angar2/taskery add codex`,
|
|
46
|
+
);
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// 2. manifest 필수
|
|
51
|
+
const manifestPath = path.join(cwd, MANIFEST_NAME);
|
|
52
|
+
const manifest = readManifest(manifestPath);
|
|
53
|
+
if (!manifest) {
|
|
54
|
+
console.error(
|
|
55
|
+
`taskery add: ${MANIFEST_NAME} 없음 — 'npx @angar2/taskery init' 먼저 실행 필요.`,
|
|
56
|
+
);
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// 3. 이미 설치된 플랫폼?
|
|
61
|
+
const platforms = Array.isArray(manifest.platforms) ? manifest.platforms : ['claude'];
|
|
62
|
+
if (platforms.includes(platform)) {
|
|
63
|
+
console.log(`taskery add: '${platform}' 이미 설치됨 — 변경 없음.`);
|
|
64
|
+
process.exit(0);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// 4. 해당 플랫폼 전용 + 공통(shared/) 자산만 카피 (agnostic은 init이 이미 설치)
|
|
68
|
+
const templateDir = findTemplateDir();
|
|
69
|
+
const templateFiles = walkTemplate(templateDir);
|
|
70
|
+
const plan = resolveInstallPlan(templateFiles, [platform], { includeAgnostic: false });
|
|
71
|
+
|
|
72
|
+
console.log(`\ntaskery v${getPackageVersion()} add ${platform}`);
|
|
73
|
+
console.log(`카피 대상: ${plan.length}개 파일\n`);
|
|
74
|
+
|
|
75
|
+
const files = { ...(manifest.files || {}) };
|
|
76
|
+
let copied = 0;
|
|
77
|
+
let skipped = 0;
|
|
78
|
+
|
|
79
|
+
for (const { templateRel, installRel, hash } of plan) {
|
|
80
|
+
const src = path.join(templateDir, templateRel);
|
|
81
|
+
const dst = path.join(cwd, installRel);
|
|
82
|
+
if (fs.existsSync(dst)) {
|
|
83
|
+
const ok = await confirm(` '${installRel}' 이미 존재 — 덮어쓸까?`);
|
|
84
|
+
if (!ok) {
|
|
85
|
+
console.log(` skip: ${installRel}`);
|
|
86
|
+
skipped++;
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
copyFile(src, dst);
|
|
91
|
+
files[installRel] = { hash, core: true, managed: true };
|
|
92
|
+
copied++;
|
|
93
|
+
console.log(` copy: ${installRel}`);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// 5. manifest 갱신
|
|
97
|
+
manifest.platforms = [...platforms, platform];
|
|
98
|
+
manifest.files = files;
|
|
99
|
+
manifest.updated_at = new Date().toISOString();
|
|
100
|
+
writeManifest(manifest, manifestPath);
|
|
101
|
+
|
|
102
|
+
console.log(`\n✅ taskery add ${platform} 완료 (카피 ${copied} / 스킵 ${skipped})`);
|
|
103
|
+
console.log(` platforms: ${manifest.platforms.join(', ')}`);
|
|
104
|
+
if (platform === 'codex') {
|
|
105
|
+
console.log(` 다음: Codex 최초 1회 '/hooks'로 hook trust 승인 (.codex/config.toml)`);
|
|
106
|
+
console.log(` AGENTS.md 정독 (코덱스 진입점)`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
main().catch((e) => {
|
|
111
|
+
console.error(`taskery add 실패: ${e.message}`);
|
|
112
|
+
process.exit(1);
|
|
113
|
+
});
|
package/bin/init.js
CHANGED
|
@@ -27,6 +27,8 @@ const {
|
|
|
27
27
|
generateProjectId,
|
|
28
28
|
DEFAULT_STALE_DAYS,
|
|
29
29
|
DEFAULT_LOCK_TIMEOUT_MS,
|
|
30
|
+
resolveInstallPlan,
|
|
31
|
+
gitignorePatternsFor,
|
|
30
32
|
} = require('./lib');
|
|
31
33
|
|
|
32
34
|
async function confirm(msg) {
|
|
@@ -39,6 +41,23 @@ async function confirm(msg) {
|
|
|
39
41
|
});
|
|
40
42
|
}
|
|
41
43
|
|
|
44
|
+
// 에이전트 플랫폼 선택 — 고른 것만 독립 설치 (공통 .project/는 항상)
|
|
45
|
+
async function selectPlatforms() {
|
|
46
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
47
|
+
return new Promise((resolve) => {
|
|
48
|
+
rl.question(
|
|
49
|
+
`\n에이전트 플랫폼 선택:\n 1) Claude Code\n 2) Codex\n 3) 둘 다\n선택 (1/2/3, 기본 1): `,
|
|
50
|
+
(ans) => {
|
|
51
|
+
rl.close();
|
|
52
|
+
const c = ans.trim() || '1';
|
|
53
|
+
if (c === '2') resolve(['codex']);
|
|
54
|
+
else if (c === '3') resolve(['claude', 'codex']);
|
|
55
|
+
else resolve(['claude']);
|
|
56
|
+
},
|
|
57
|
+
);
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
42
61
|
async function main() {
|
|
43
62
|
const cwd = process.cwd();
|
|
44
63
|
const templateDir = findTemplateDir();
|
|
@@ -55,26 +74,28 @@ async function main() {
|
|
|
55
74
|
}
|
|
56
75
|
}
|
|
57
76
|
|
|
58
|
-
// 2. template/ 정독
|
|
77
|
+
// 2. 플랫폼 선택 + template/ 정독 → 설치 계획 전개 (공통 shared/는 플랫폼 경로로 매핑)
|
|
78
|
+
const platforms = await selectPlatforms();
|
|
59
79
|
const templateFiles = walkTemplate(templateDir);
|
|
60
|
-
const
|
|
80
|
+
const plan = resolveInstallPlan(templateFiles, platforms);
|
|
61
81
|
|
|
62
82
|
console.log(`\ntaskery v${getPackageVersion()} init`);
|
|
63
83
|
console.log(`대상: ${cwd}`);
|
|
64
|
-
console.log(
|
|
84
|
+
console.log(`플랫폼: ${platforms.join(', ')}`);
|
|
85
|
+
console.log(`카피 대상: ${plan.length}개 파일\n`);
|
|
65
86
|
|
|
66
|
-
// 3. 카피 + manifest 빌드
|
|
87
|
+
// 3. 카피 + manifest 빌드 (manifest 키 = 설치 경로 installRel)
|
|
67
88
|
const manifestFiles = {};
|
|
68
89
|
let copied = 0;
|
|
69
90
|
let skipped = 0;
|
|
70
91
|
|
|
71
|
-
for (const
|
|
72
|
-
const src = path.join(templateDir,
|
|
73
|
-
const dst = path.join(cwd,
|
|
92
|
+
for (const { templateRel, installRel, hash } of plan) {
|
|
93
|
+
const src = path.join(templateDir, templateRel);
|
|
94
|
+
const dst = path.join(cwd, installRel);
|
|
74
95
|
|
|
75
96
|
// .local.md suffix는 사용자 영역 — taskery template에는 없어야 정상이지만 방어적으로 스킵
|
|
76
|
-
if (isLocalOverride(
|
|
77
|
-
console.log(` skip (.local override 영역): ${
|
|
97
|
+
if (isLocalOverride(installRel)) {
|
|
98
|
+
console.log(` skip (.local override 영역): ${installRel}`);
|
|
78
99
|
skipped++;
|
|
79
100
|
continue;
|
|
80
101
|
}
|
|
@@ -82,18 +103,18 @@ async function main() {
|
|
|
82
103
|
// 사용자 .local.md 충돌 검사 — 사용자가 직접 만든 *.local.md 보호
|
|
83
104
|
// 본 init은 신규 설치 default, 충돌 거의 없지만 update 흐름과 일관성 유지
|
|
84
105
|
if (fs.existsSync(dst)) {
|
|
85
|
-
const ok = await confirm(` '${
|
|
106
|
+
const ok = await confirm(` '${installRel}' 이미 존재 — 덮어쓸까?`);
|
|
86
107
|
if (!ok) {
|
|
87
|
-
console.log(` skip: ${
|
|
108
|
+
console.log(` skip: ${installRel}`);
|
|
88
109
|
skipped++;
|
|
89
110
|
continue;
|
|
90
111
|
}
|
|
91
112
|
}
|
|
92
113
|
|
|
93
114
|
copyFile(src, dst);
|
|
94
|
-
manifestFiles[
|
|
115
|
+
manifestFiles[installRel] = { hash, core: true, managed: true };
|
|
95
116
|
copied++;
|
|
96
|
-
console.log(` copy: ${
|
|
117
|
+
console.log(` copy: ${installRel}`);
|
|
97
118
|
}
|
|
98
119
|
|
|
99
120
|
// 4. manifest 작성 — 멀티세션(0.1.2+): projectId / stale_days / lock_timeout_ms 포함
|
|
@@ -101,6 +122,7 @@ async function main() {
|
|
|
101
122
|
version: getPackageVersion(),
|
|
102
123
|
installed_at: new Date().toISOString(),
|
|
103
124
|
projectId: generateProjectId(),
|
|
125
|
+
platforms,
|
|
104
126
|
stale_days: DEFAULT_STALE_DAYS,
|
|
105
127
|
lock_timeout_ms: DEFAULT_LOCK_TIMEOUT_MS,
|
|
106
128
|
files: manifestFiles,
|
|
@@ -111,14 +133,9 @@ async function main() {
|
|
|
111
133
|
// 공개 repo면 내부 워크플로 파일 노출 회피 — taskery 내부 영역 .gitignore 등록 제안.
|
|
112
134
|
// 사용자 NO면 패스 (그대로 둠). 이미 등록되어 있으면 스킵.
|
|
113
135
|
const gitignorePath = path.join(cwd, '.gitignore');
|
|
114
|
-
const taskeryPatterns =
|
|
115
|
-
'.project/',
|
|
116
|
-
'.claude/',
|
|
117
|
-
'CLAUDE.md',
|
|
118
|
-
'.taskery-manifest.json',
|
|
119
|
-
];
|
|
136
|
+
const taskeryPatterns = gitignorePatternsFor(platforms);
|
|
120
137
|
const addGitignore = await confirm(
|
|
121
|
-
`\ntaskery 내부 파일(.
|
|
138
|
+
`\ntaskery 내부 파일(${taskeryPatterns.join(', ')})을 .gitignore에 등록할까?\n (공개 repo면 권장 — 내부 워크플로 파일 노출 회피)`,
|
|
122
139
|
);
|
|
123
140
|
if (addGitignore) {
|
|
124
141
|
const existing = fs.existsSync(gitignorePath)
|
|
@@ -147,12 +164,15 @@ async function main() {
|
|
|
147
164
|
// 5. 결과 보고
|
|
148
165
|
console.log(`\n✅ taskery init 완료`);
|
|
149
166
|
console.log(` 카피: ${copied}개 / 스킵: ${skipped}개`);
|
|
167
|
+
console.log(` 플랫폼: ${platforms.join(', ')}`);
|
|
150
168
|
console.log(` manifest: ${MANIFEST_NAME}\n`);
|
|
169
|
+
const entryFile = platforms.includes('claude') ? 'CLAUDE.md' : 'AGENTS.md';
|
|
151
170
|
console.log(`다음 단계:`);
|
|
152
|
-
console.log(` 1.
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
171
|
+
console.log(` 1. ${entryFile} 정독 + 프로젝트 메타 + 검증 명령 채우기`);
|
|
172
|
+
if (platforms.includes('codex')) {
|
|
173
|
+
console.log(` 2. Codex 최초 1회 '/hooks'로 hook trust 승인 (.codex/config.toml — git-guard / closed-immutable)`);
|
|
174
|
+
}
|
|
175
|
+
console.log(` 3. 에이전트 진입 → '/project-init' → '/plan-init <vX.X>' → '/task-init'\n`);
|
|
156
176
|
}
|
|
157
177
|
|
|
158
178
|
main().catch((e) => {
|
package/bin/lib.js
CHANGED
|
@@ -43,6 +43,81 @@ const { execFileSync } = require('child_process');
|
|
|
43
43
|
const LOCAL_SUFFIX = '.local.md';
|
|
44
44
|
const MANIFEST_NAME = '.taskery-manifest.json';
|
|
45
45
|
|
|
46
|
+
// === 멀티 플랫폼 (0.2.0+) ===
|
|
47
|
+
// 공통 소스 + 플랫폼별 조립 설치: 공통 자산(shared/)은 단일 소스로 보관하고
|
|
48
|
+
// 설치 시 고른 플랫폼의 실제 경로로 매핑 복사한다. 플랫폼 고유 자산은 그대로 복사.
|
|
49
|
+
const PLATFORMS = ['claude', 'codex'];
|
|
50
|
+
const DEFAULT_PLATFORMS = ['claude']; // 기존(0.1.x) 설치 마이그레이션 기본값
|
|
51
|
+
|
|
52
|
+
// shared/ 공통 자산의 플랫폼별 설치 경로 매핑.
|
|
53
|
+
// shared 하위 첫 세그먼트(skills|hooks) → 각 플랫폼의 실제 설치 루트.
|
|
54
|
+
// 스킬·git-guard 본문은 양 플랫폼 동일(검증 완료) — 단일 소스로 보관, 설치 때 갈라 깐다.
|
|
55
|
+
const SHARED_DEST = {
|
|
56
|
+
claude: { skills: '.claude/skills', hooks: '.claude/hooks' },
|
|
57
|
+
codex: { skills: '.agents/skills', hooks: '.codex/hooks' },
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// template 상대경로 → 소속 ('agnostic' | 'shared' | 'claude' | 'codex')
|
|
61
|
+
function platformOf(relPath) {
|
|
62
|
+
if (relPath.startsWith('shared/')) return 'shared'; // 공통 — 설치 시 플랫폼별 매핑
|
|
63
|
+
if (relPath === 'CLAUDE.md' || relPath.startsWith('.claude/')) return 'claude';
|
|
64
|
+
if (relPath === 'AGENTS.md' || relPath.startsWith('.codex/')) return 'codex';
|
|
65
|
+
return 'agnostic'; // .project/, .gitignore 등 — 항상 설치
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// shared/ 상대경로를 특정 플랫폼의 설치 경로로 변환.
|
|
69
|
+
// 예: 'shared/skills/task-init/SKILL.md' + 'codex' → '.agents/skills/task-init/SKILL.md'
|
|
70
|
+
function mapSharedDest(relPath, platform) {
|
|
71
|
+
const rest = relPath.slice('shared/'.length); // 'skills/task-init/SKILL.md'
|
|
72
|
+
const sep = rest.indexOf('/');
|
|
73
|
+
const top = sep === -1 ? rest : rest.slice(0, sep); // 'skills'
|
|
74
|
+
const tail = sep === -1 ? '' : rest.slice(sep); // '/task-init/SKILL.md'
|
|
75
|
+
const root = SHARED_DEST[platform] && SHARED_DEST[platform][top];
|
|
76
|
+
if (!root) {
|
|
77
|
+
throw new Error(`shared 자산 매핑 미정의: ${relPath} (platform=${platform})`);
|
|
78
|
+
}
|
|
79
|
+
return root + tail;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// template 파일맵을 설치 계획으로 전개 — [{ templateRel, installRel, hash }].
|
|
83
|
+
// - agnostic: 그대로 (includeAgnostic=false면 제외 — add 용)
|
|
84
|
+
// - shared: 선택 플랫폼마다 매핑 경로로 1건씩 전개 (1:N)
|
|
85
|
+
// - 플랫폼 고유: 선택 플랫폼이면 그대로
|
|
86
|
+
function resolveInstallPlan(templateFiles, platforms, { includeAgnostic = true } = {}) {
|
|
87
|
+
const plan = [];
|
|
88
|
+
for (const [rel, hash] of Object.entries(templateFiles)) {
|
|
89
|
+
const p = platformOf(rel);
|
|
90
|
+
if (p === 'agnostic') {
|
|
91
|
+
if (includeAgnostic) plan.push({ templateRel: rel, installRel: rel, hash });
|
|
92
|
+
} else if (p === 'shared') {
|
|
93
|
+
for (const platform of platforms) {
|
|
94
|
+
plan.push({ templateRel: rel, installRel: mapSharedDest(rel, platform), hash });
|
|
95
|
+
}
|
|
96
|
+
} else if (platforms.includes(p)) {
|
|
97
|
+
plan.push({ templateRel: rel, installRel: rel, hash });
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return plan;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// .gitignore 등록 패턴 (플랫폼별)
|
|
104
|
+
const GITIGNORE_PATTERNS = {
|
|
105
|
+
agnostic: ['.project/', '.taskery-manifest.json'],
|
|
106
|
+
claude: ['.claude/', 'CLAUDE.md'],
|
|
107
|
+
codex: ['.codex/', '.agents/', 'AGENTS.md'],
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
// 선택 플랫폼 + agnostic의 gitignore 패턴 합성 (중복 제거)
|
|
111
|
+
function gitignorePatternsFor(platforms) {
|
|
112
|
+
const out = [...GITIGNORE_PATTERNS.agnostic];
|
|
113
|
+
for (const p of platforms) {
|
|
114
|
+
for (const pat of GITIGNORE_PATTERNS[p] || []) {
|
|
115
|
+
if (!out.includes(pat)) out.push(pat);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return out;
|
|
119
|
+
}
|
|
120
|
+
|
|
46
121
|
function findTemplateDir() {
|
|
47
122
|
// 패키지 루트는 이 lib.js 파일의 부모(bin/)의 부모
|
|
48
123
|
const pkgRoot = path.resolve(__dirname, '..');
|
|
@@ -461,6 +536,15 @@ async function markBacklogChecked(mainWtPath, blId, taskNum) {
|
|
|
461
536
|
module.exports = {
|
|
462
537
|
LOCAL_SUFFIX,
|
|
463
538
|
MANIFEST_NAME,
|
|
539
|
+
// 멀티 플랫폼 (0.2.0+)
|
|
540
|
+
PLATFORMS,
|
|
541
|
+
DEFAULT_PLATFORMS,
|
|
542
|
+
SHARED_DEST,
|
|
543
|
+
platformOf,
|
|
544
|
+
mapSharedDest,
|
|
545
|
+
resolveInstallPlan,
|
|
546
|
+
GITIGNORE_PATTERNS,
|
|
547
|
+
gitignorePatternsFor,
|
|
464
548
|
findTemplateDir,
|
|
465
549
|
sha256,
|
|
466
550
|
walkTemplate,
|
package/bin/taskery.js
CHANGED
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
* `npx @angar2/taskery <subcommand>` — 진입점 dispatcher.
|
|
5
5
|
*
|
|
6
6
|
* 서브커맨드:
|
|
7
|
-
* init — 현재 디렉토리에 taskery 자산 카피 + manifest 생성
|
|
7
|
+
* init — 현재 디렉토리에 taskery 자산 카피 + manifest 생성 (플랫폼 선택)
|
|
8
|
+
* add — 기존 설치 리포에 플랫폼 자산 추가 (claude | codex)
|
|
8
9
|
* update — 최신 버전 fetch + manifest 비교 + 머지 갱신
|
|
9
10
|
* status — 멀티세션 현황 요약 (진행중 태스크 / 워크트리 / 머지 락, 0.1.2+)
|
|
10
11
|
* prune — stale 워크트리 / 브랜치 대화형 정리 (0.1.2+)
|
|
@@ -28,7 +29,8 @@ function help() {
|
|
|
28
29
|
console.log(`taskery v${require('./lib').getPackageVersion()}
|
|
29
30
|
|
|
30
31
|
사용법:
|
|
31
|
-
npx @angar2/taskery init 현재 디렉토리에 taskery 자산 설치
|
|
32
|
+
npx @angar2/taskery init 현재 디렉토리에 taskery 자산 설치 (플랫폼 선택)
|
|
33
|
+
npx @angar2/taskery add <p> 기존 설치에 플랫폼 추가 (claude | codex)
|
|
32
34
|
npx @angar2/taskery update 최신 버전 fetch + 머지 갱신
|
|
33
35
|
npx @angar2/taskery status 멀티세션 현황 (진행중 태스크 / 워크트리 / 머지 락)
|
|
34
36
|
npx @angar2/taskery prune stale 워크트리 / 브랜치 대화형 정리
|
|
@@ -45,6 +47,9 @@ switch (sub) {
|
|
|
45
47
|
case 'init':
|
|
46
48
|
runScript('init.js');
|
|
47
49
|
break;
|
|
50
|
+
case 'add':
|
|
51
|
+
runScript('add.js');
|
|
52
|
+
break;
|
|
48
53
|
case 'update':
|
|
49
54
|
runScript('update.js');
|
|
50
55
|
break;
|
package/bin/update.js
CHANGED
|
@@ -34,6 +34,8 @@ const {
|
|
|
34
34
|
generateProjectId,
|
|
35
35
|
DEFAULT_STALE_DAYS,
|
|
36
36
|
DEFAULT_LOCK_TIMEOUT_MS,
|
|
37
|
+
DEFAULT_PLATFORMS,
|
|
38
|
+
resolveInstallPlan,
|
|
37
39
|
} = require('./lib');
|
|
38
40
|
|
|
39
41
|
async function confirm(msg) {
|
|
@@ -60,8 +62,17 @@ async function main() {
|
|
|
60
62
|
process.exit(1);
|
|
61
63
|
}
|
|
62
64
|
|
|
63
|
-
// 2.
|
|
64
|
-
|
|
65
|
+
// 2. 설치된 플랫폼 자산만 갱신 대상 (platforms 누락 시 마이그레이션 기본값)
|
|
66
|
+
// 설치 계획(installRel 기준)으로 전개 — shared/는 플랫폼 경로로 매핑된 상태.
|
|
67
|
+
// newTemplateFiles: installRel → { hash(소스), templateRel(복사 원본) }
|
|
68
|
+
const platforms = Array.isArray(oldManifest.platforms)
|
|
69
|
+
? oldManifest.platforms
|
|
70
|
+
: DEFAULT_PLATFORMS;
|
|
71
|
+
const plan = resolveInstallPlan(walkTemplate(templateDir), platforms);
|
|
72
|
+
const newTemplateFiles = {};
|
|
73
|
+
for (const it of plan) {
|
|
74
|
+
newTemplateFiles[it.installRel] = { hash: it.hash, templateRel: it.templateRel };
|
|
75
|
+
}
|
|
65
76
|
const oldFiles = oldManifest.files || {};
|
|
66
77
|
|
|
67
78
|
console.log(`\ntaskery update`);
|
|
@@ -71,8 +82,8 @@ async function main() {
|
|
|
71
82
|
const newFiles = {};
|
|
72
83
|
const summary = { unchanged: 0, new: 0, autoUpdated: 0, customizedReplaced: 0, skipped: 0, removed: 0 };
|
|
73
84
|
|
|
74
|
-
// 3. 새 template 파일 분기
|
|
75
|
-
for (const [rel, newHash] of Object.entries(newTemplateFiles)) {
|
|
85
|
+
// 3. 새 template 파일 분기 (rel = 설치 경로 installRel)
|
|
86
|
+
for (const [rel, { hash: newHash, templateRel }] of Object.entries(newTemplateFiles)) {
|
|
76
87
|
if (isLocalOverride(rel)) {
|
|
77
88
|
// template에 .local.md 들어가면 안 되지만 방어적 스킵
|
|
78
89
|
console.log(` skip (.local override 영역): ${rel}`);
|
|
@@ -80,7 +91,7 @@ async function main() {
|
|
|
80
91
|
continue;
|
|
81
92
|
}
|
|
82
93
|
|
|
83
|
-
const src = path.join(templateDir,
|
|
94
|
+
const src = path.join(templateDir, templateRel);
|
|
84
95
|
const dst = path.join(cwd, rel);
|
|
85
96
|
const oldEntry = oldFiles[rel];
|
|
86
97
|
|
|
@@ -149,8 +160,8 @@ async function main() {
|
|
|
149
160
|
}
|
|
150
161
|
}
|
|
151
162
|
|
|
152
|
-
// 5. manifest 갱신 — 멀티세션(0.1.2+) 필드 누락 시 자동 마이그레이션
|
|
153
|
-
const migration = { projectId: false, stale_days: false, lock_timeout_ms: false };
|
|
163
|
+
// 5. manifest 갱신 — 멀티세션(0.1.2+) / 플랫폼(0.2.0+) 필드 누락 시 자동 마이그레이션
|
|
164
|
+
const migration = { projectId: false, stale_days: false, lock_timeout_ms: false, platforms: false };
|
|
154
165
|
const projectId = oldManifest.projectId || (migration.projectId = true, generateProjectId());
|
|
155
166
|
const stale_days =
|
|
156
167
|
typeof oldManifest.stale_days === 'number'
|
|
@@ -160,12 +171,14 @@ async function main() {
|
|
|
160
171
|
typeof oldManifest.lock_timeout_ms === 'number'
|
|
161
172
|
? oldManifest.lock_timeout_ms
|
|
162
173
|
: (migration.lock_timeout_ms = true, DEFAULT_LOCK_TIMEOUT_MS);
|
|
174
|
+
if (!Array.isArray(oldManifest.platforms)) migration.platforms = true;
|
|
163
175
|
|
|
164
176
|
const newManifest = {
|
|
165
177
|
version: getPackageVersion(),
|
|
166
178
|
installed_at: oldManifest.installed_at,
|
|
167
179
|
updated_at: new Date().toISOString(),
|
|
168
180
|
projectId,
|
|
181
|
+
platforms,
|
|
169
182
|
stale_days,
|
|
170
183
|
lock_timeout_ms,
|
|
171
184
|
files: newFiles,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@angar2/taskery",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Lightweight task system for Claude Code — 1 메인 세션(또는 멀티세션) + 스킬 9종 + catastrophic hook 안전망 2종",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Lightweight task system for Claude Code & Codex — 1 메인 세션(또는 멀티세션) + 스킬 9종 + catastrophic hook 안전망 2종",
|
|
5
5
|
"bin": {
|
|
6
6
|
"taskery": "bin/taskery.js",
|
|
7
7
|
"create-taskery": "bin/create.js"
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
},
|
|
19
19
|
"keywords": [
|
|
20
20
|
"claude-code",
|
|
21
|
+
"codex",
|
|
21
22
|
"task-management",
|
|
22
23
|
"ai-coding-workflow",
|
|
23
24
|
"skills",
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# taskery 격리 검증 서브에이전트 — /task-test가 spawn하는 독립 tester
|
|
2
|
+
#
|
|
3
|
+
# 클로드의 "Task tool 격리 세션"에 대응하는 코덱스 서브에이전트 정의.
|
|
4
|
+
# 메인 세션의 plan/dev 컨텍스트(확증편향) 없이 task.md만 보고 독립 검증한다.
|
|
5
|
+
#
|
|
6
|
+
# ⚠ model 명시 필수 — 생략하면 자식 모델 resolve가 service tier validation에서 실패한다.
|
|
7
|
+
# 환경의 사용 가능 모델에 맞게 조정한다(기본 gpt-5.5).
|
|
8
|
+
|
|
9
|
+
name = "task-tester"
|
|
10
|
+
description = "격리 검증 에이전트. 메인의 확증편향 없이 task.md만 보고 Test Plan을 독립 수행한다. /task-test가 spawn한다."
|
|
11
|
+
model = "gpt-5.5"
|
|
12
|
+
sandbox_mode = "read-only"
|
|
13
|
+
developer_instructions = """
|
|
14
|
+
너는 격리된 tester다. 메인 세션의 가정을 일체 신뢰하지 말고, 넘겨받은 task.md 하나만 보고 자기완결적으로 검증하라.
|
|
15
|
+
- task.md의 ## Test Plan 시나리오를 순서대로 그대로 수행한다(임의 추가/스킵/변형 금지).
|
|
16
|
+
- [AUTO] 시나리오는 실제 명령을 실행하고 로그를 확보한다. 실행 없이 pass 처리 금지.
|
|
17
|
+
- [USER] 시나리오는 UNCERTAIN으로 분류하고 사용자 직접 검수 항목으로 리턴한다.
|
|
18
|
+
- 코드와 동작만 신뢰한다. "잘 될 것"이라는 가정 금지. 코드 파일 수정 금지.
|
|
19
|
+
- 검증을 마치면 task-test SKILL.md의 결과 형식(종합 판정 / 시나리오별 결과 / 검증·테스트 명령 결과)을 그대로 리턴한다.
|
|
20
|
+
"""
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# taskery Codex 프로젝트 설정 — PreToolUse hook 2종 등록 (git 안전망 + closed 보호)
|
|
2
|
+
#
|
|
3
|
+
# hook 스크립트 경로는 git 리포 루트 기준으로 해석한다($(git rev-parse --show-toplevel)).
|
|
4
|
+
# 설치 위치와 무관하게 동작하도록 절대경로 하드코딩을 피한다.
|
|
5
|
+
# 최초 1회 코덱스 `/hooks`로 trust 승인이 필요하다(승인 전 hook은 자동 skip).
|
|
6
|
+
|
|
7
|
+
[[hooks.PreToolUse]]
|
|
8
|
+
matcher = "^Bash$"
|
|
9
|
+
[[hooks.PreToolUse.hooks]]
|
|
10
|
+
type = "command"
|
|
11
|
+
command = 'bash "$(git rev-parse --show-toplevel)/.codex/hooks/git-guard.sh"'
|
|
12
|
+
|
|
13
|
+
[[hooks.PreToolUse]]
|
|
14
|
+
matcher = "apply_patch"
|
|
15
|
+
[[hooks.PreToolUse.hooks]]
|
|
16
|
+
type = "command"
|
|
17
|
+
command = 'bash "$(git rev-parse --show-toplevel)/.codex/hooks/closed-immutable.sh"'
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# .codex/hooks/closed-immutable.sh
|
|
3
|
+
# Codex PreToolUse(apply_patch) 훅 — closed 상태 task.md 재수정 차단 (코덱스 전용판)
|
|
4
|
+
#
|
|
5
|
+
# 클로드판(.claude/hooks/closed-immutable.sh)과의 차이:
|
|
6
|
+
# - 클로드: tool_input.file_path 1개를 읽음 (Write|Edit 도구)
|
|
7
|
+
# - 코덱스: 파일 편집이 apply_patch 도구 1개로 처리되며 tool_input.command 안에
|
|
8
|
+
# patch envelope 전체(`*** Begin Patch` ... `*** Update File: <경로>` ...)가 들어옴.
|
|
9
|
+
# file_path 키가 없으므로 command에서 `*** Update File:` / `*** Add File:` 헤더의
|
|
10
|
+
# 경로를 정규식 추출해 각각 검사한다. matcher = "apply_patch".
|
|
11
|
+
#
|
|
12
|
+
# 잡는 것:
|
|
13
|
+
# 1. .project/tasks/<vX.X>/<NNN>_<slug>.md (단일 파일)
|
|
14
|
+
# 2. .project/tasks/<vX.X>/TASK-<NNN>_<slug>/task.md (폴더 승격)
|
|
15
|
+
# → 헤더 status가 'closed'면 차단 (exit 2)
|
|
16
|
+
#
|
|
17
|
+
# 차단 안 함 (의도적): spec-diffs / screenshots / mockup — 역사적 자료 자유 수정.
|
|
18
|
+
# Add File은 대상 파일이 존재하지 않으므로 자연 통과(새 파일은 closed일 수 없음).
|
|
19
|
+
#
|
|
20
|
+
# 우회: 헤더 status를 closed → developing 등으로 직접 변경 후 편집.
|
|
21
|
+
# Catastrophic 영역 (완료 보호). 잘 지키면 작동 0회 (무해).
|
|
22
|
+
|
|
23
|
+
INPUT=$(cat)
|
|
24
|
+
|
|
25
|
+
# tool_input.command (apply_patch envelope 전체) 추출
|
|
26
|
+
CMD=$(echo "$INPUT" | python3 -c "
|
|
27
|
+
import sys, json
|
|
28
|
+
try:
|
|
29
|
+
print(json.load(sys.stdin).get('tool_input', {}).get('command', ''))
|
|
30
|
+
except Exception:
|
|
31
|
+
print('')
|
|
32
|
+
" 2>/dev/null)
|
|
33
|
+
|
|
34
|
+
# Update/Add File 헤더에서 대상 경로 추출 (한 patch에 여러 파일 가능)
|
|
35
|
+
PATHS=$(echo "$CMD" | grep -oE '^\*\*\* (Update|Add) File: .+$' | sed -E 's/^\*\*\* (Update|Add) File: //')
|
|
36
|
+
|
|
37
|
+
[ -z "$PATHS" ] && exit 0
|
|
38
|
+
|
|
39
|
+
while IFS= read -r FILE_PATH; do
|
|
40
|
+
# task.md 본 파일 패턴만 검사 (spec-diffs/screenshots/mockup 직속은 보호 대상 X)
|
|
41
|
+
echo "$FILE_PATH" | grep -qE '\.project/tasks/v[^/]+/([0-9]+_[^/]+\.md|TASK-[^/]+/task\.md)$' || continue
|
|
42
|
+
# 존재하는 파일만 (Add File 신규 생성은 통과)
|
|
43
|
+
[ -f "$FILE_PATH" ] || continue
|
|
44
|
+
|
|
45
|
+
# 헤더 status 추출 (5컬럼 표 — 생성일 / 플랜 / 유형 / 규모 / 상태)
|
|
46
|
+
STATUS=$(TM="$FILE_PATH" python3 -c "
|
|
47
|
+
import os, re
|
|
48
|
+
try:
|
|
49
|
+
content = open(os.environ['TM']).read()
|
|
50
|
+
except Exception:
|
|
51
|
+
print('')
|
|
52
|
+
raise SystemExit(0)
|
|
53
|
+
WL = r'(draft|planned|developing|developed|testing|tested|closed)'
|
|
54
|
+
m = re.search(rf'^\|[^|]*\|[^|]*\|[^|]*\|[^|]*\|\s*{WL}\s*\|\s*\$', content, re.MULTILINE)
|
|
55
|
+
print(m.group(1) if m else '')
|
|
56
|
+
" 2>/dev/null)
|
|
57
|
+
|
|
58
|
+
if [ "$STATUS" = "closed" ]; then
|
|
59
|
+
echo "closed-immutable: '$FILE_PATH' 차단. task 상태가 'closed' — 재수정 금지." >&2
|
|
60
|
+
echo " 의도적 재진입 시: sed로 헤더 status를 'developing' 등으로 변경 후 편집 가능." >&2
|
|
61
|
+
echo " closed task 코드 / spec-diffs / screenshots / mockup은 보호 영역 외 — 자유 수정. 코드 변경은 새 task로 처리가 정상 흐름." >&2
|
|
62
|
+
exit 2
|
|
63
|
+
fi
|
|
64
|
+
done <<< "$PATHS"
|
|
65
|
+
|
|
66
|
+
exit 0
|
|
@@ -191,7 +191,9 @@ docs: [TASK-{NNN}] CHANGELOG 업데이트
|
|
|
191
191
|
### 메인 워크트리 = dev 전용
|
|
192
192
|
|
|
193
193
|
- 메인 워크트리는 항상 dev 체크아웃 상태 유지
|
|
194
|
-
- 모든 태스크 작업은
|
|
194
|
+
- 모든 태스크 작업은 *예외 없이* 별도 워크트리에서 수행
|
|
195
|
+
- **메인 워크트리 HEAD를 dev에서 떼는 어떤 명령도 영구 금지**: `git checkout <task-branch>` / `git switch <task-branch>` / `git reset` HEAD 이동 / `git rebase` HEAD 이동 등. *"잠깐만 메인에서"* / *"테스트 한 번만"* 같은 예외 발화도 거부 (별도 워크트리로 처리)
|
|
196
|
+
- **모호 발화 자의 해석 금지**: *"워크트리 없이"* / *"메인에서"* / *"이 자리에서"* 류 발화 = 본 규칙 충돌 신호 → 즉시 정지 + 규칙 명시 + 1줄 confirm 요청 (자의 해석 후 진행 영구 금지). 사용자 의도가 *오타/모호*일 가능성은 *워크트리 사용*으로 읽는 게 자연스러움
|
|
195
197
|
- `/task-init` / `/task-close` 시작 시 검증 (위배 시 사용자 호출 + 중단)
|
|
196
198
|
|
|
197
199
|
### 워크트리 위치
|
|
@@ -243,11 +245,14 @@ git -C "$MAIN_WT" branch --no-merged dev --list \
|
|
|
243
245
|
## Hook 안전망 — `git-guard.sh`
|
|
244
246
|
|
|
245
247
|
`.claude/hooks/git-guard.sh`가 PreToolUse(Bash)로 다음 명령 차단:
|
|
246
|
-
- `main` / `dev` 직접 커밋
|
|
248
|
+
- `main` / `dev` 직접 커밋 — **5종 변형 인식** (0.1.3+): `git -C <경로>` / `git --git-dir=<경로>` / `git --git-dir <경로>` / `git --work-tree=<경로>` / `git --work-tree <경로>` 모두 인식해 *대상 경로의 브랜치*로 검사. 어느 cwd에서 호출하든 워크트리 브랜치 커밋이 dev 직접 커밋으로 오인 차단되지 않음
|
|
247
249
|
- `git push --force` (작업 브랜치 외 / 사용자 미승인)
|
|
248
250
|
- `git commit --no-verify`
|
|
249
251
|
- `git branch -D` (사용자 미승인)
|
|
250
252
|
- `git reset --hard` (사용자 미승인)
|
|
253
|
+
- `git clean -fd` (사용자 미승인)
|
|
254
|
+
|
|
255
|
+
**셸 prefix 금지**: `cd <경로> && git ...` / `(cd <경로> && git ...)` 형태는 hook이 정확히 인식 X (변형 우회 위험). 모든 워크트리 대상 명령은 `git -C <경로> ...` 형태로만 발행.
|
|
251
256
|
|
|
252
257
|
이 hook은 *catastrophic 차단 안전망*. 잘 지키면 작동 0회 (무해).
|
|
253
258
|
|
|
@@ -278,3 +283,4 @@ git -C "$MAIN_WT" branch --no-merged dev --list \
|
|
|
278
283
|
| 2026-05-30 | docs/* 브랜치 ff-only 예외 명시 — plan-init 단일 커밋 한정. task 진행 중 ROADMAP/플랜 갱신은 작업 브랜치 + --no-ff 명시 추가 (stash FRICTION_LOG #4 반영) |
|
|
279
284
|
| 2026-05-31 | 멀티세션 0.1.2 오버라이드 — 브랜치명에 출처(BL/RM/DR) 추가 / 케이스 2(TASK 없는 브랜치) 시스템 외 명시 / `/task-close` 자동 삭제 + 워크트리 제거 면제 조항 / 멀티세션 워크트리 정책 섹션 신규 (메인=dev 전용 / SSoT 조회 / 머지 락 직렬화) |
|
|
280
285
|
| 2026-05-31 | 0.1.2 백로그 스킬 추가 정합 — 출처 표 `BL-NNN` 채번 주체 `/backlog-add` → `/add-backlog`로 갱신 + 버전별 경로(`.project/tasks/<vX.X>/BACKLOG.md`) 명시 |
|
|
286
|
+
| 2026-06-02 | 0.1.3 F2·F3 정합 — §멀티세션 워크트리 정책 *메인 HEAD 떼기 금지* + *모호 발화 자의 해석 금지* / §git-guard.sh 5종 변형 인식 (-C / --git-dir / --work-tree) + 셸 prefix 금지 명시. stash FRICTION_LOG 2026-06-01 반영 |
|
|
@@ -51,7 +51,7 @@ UX/UI 구현 task의 *시각 영역 사전 정합* + *task-test 시 USER 검수
|
|
|
51
51
|
## 5. 생성 흐름 (`/task-plan` Step 4.5 정합)
|
|
52
52
|
|
|
53
53
|
1. UX/UI 구현 task 판단 (Step 2 Requirements 인터뷰 결과)
|
|
54
|
-
2. 사용자에게 *"HTML 목업 만들까?"* 한 줄 confirm
|
|
54
|
+
2. 사용자에게 *"HTML 목업 만들까?"* 한 줄 confirm — **예외 없이 발화 강제**. 메인의 효용 판단(*"이 케이스는 목업 효용 낮음"* / *"SF Symbol은 HTML 재현 X"* 등)으로 confirm 자체를 생략하는 행위 영구 금지. 효용 판단은 질문에 *곁들이는 의견*으로만 가능 (생략 결정 대체 X — 목업 제작 여부는 *사용자 검수 방식 선택권*이라 *개발 자율 판단* 영역 X).
|
|
55
55
|
3. OK → 메인이 정적 HTML 생성 → 사용자 시각 승인 (✓ → 진행 / ✗ → 수정 재승인)
|
|
56
56
|
4. 승인된 목업 = task-test의 *시각 영역 USER 검수 기준*
|
|
57
57
|
|
|
@@ -83,3 +83,4 @@ UX/UI 구현 task의 *시각 영역 사전 정합* + *task-test 시 USER 검수
|
|
|
83
83
|
| 날짜 | 변경 사항 |
|
|
84
84
|
|------|----------|
|
|
85
85
|
| 2026-05-30 | 신규 작성 — UX/UI task HTML 목업 위치 / 형식 / 생성 흐름 / 두 시점 활용 / 시각 fix 사이클 예고 / 디자인 정독 의무 정의 (stash FRICTION_LOG #14+19 / #12 반영) |
|
|
86
|
+
| 2026-06-02 | §5 #2 — 목업 confirm *예외 없이 발화 강제* 명시 추가 (메인 효용 판단으로 confirm 자체 생략 금지). stash FRICTION_LOG 2026-06-01 F1 반영 |
|
|
@@ -208,6 +208,29 @@ draft → planned → developing → developed → testing → tested → closed
|
|
|
208
208
|
- **자기완결적**: `/task-test` 격리 세션이 *task.md만 보고도* 수행 가능. 메인의 plan/dev 컨텍스트 없이.
|
|
209
209
|
- **명령 / 기대값 포함**: 무엇을 실행하고 무엇이 기대되는지 명확.
|
|
210
210
|
- **메인 가정 X**: 격리 세션이 *코드와 동작만 신뢰*. *"잘 될 거야"* 같은 가정 금지.
|
|
211
|
+
- **grep/Read-only 존재 확인 시나리오 = 보조용** (stash FRICTION_LOG 2026-06-01 반영) — 구현 코드의 존재 여부를 grep/Read로 재확인하는 시나리오는 *동어반복*. PASS 카운트에서 제외하거나 실동작 시나리오의 *보조*로만 사용. 단독 검증 가치 X.
|
|
212
|
+
- **요구사항당 end-to-end 1개 이상 강제** — Requirements 각 항목이 *사용자 관점에서 실제 작동하는지* 검증하는 end-to-end 실동작 시나리오 최소 1개 보장. 산출물 실행 → 결과 확인 형태.
|
|
213
|
+
- **무거운 검증 회피 영구 금지** — 부담(빌드 수 분+ / 외부 의존 / 환경 설정) 이유로 실동작 시나리오를 grep 동어반복으로 대체 X. 최소 1회는 실제 산출물로 확인. 부담은 *fix 사이클 1~2회 예상* 같은 사전 예고로 처리 (생략 사유 X).
|
|
214
|
+
|
|
215
|
+
**요구사항 ↔ 시나리오 커버리지 점검** (필수 — stash FRICTION_LOG 2026-06-01 반영):
|
|
216
|
+
|
|
217
|
+
Test Plan 작성 완료 후 *반드시* 점검. *"누적/리셋 안 함"* 같은 요구사항이 시나리오에서 통째로 빠지는 누락 차단용.
|
|
218
|
+
|
|
219
|
+
1. Requirements 합의 항목 전체를 *매트릭스*로 펼침.
|
|
220
|
+
2. 각 항목당 *최소 1개 시나리오 커버* 확인.
|
|
221
|
+
3. 미커버 발견 시 → 시나리오 보강 후 재점검.
|
|
222
|
+
|
|
223
|
+
형식 (Test Plan 끝에 첨부):
|
|
224
|
+
|
|
225
|
+
```markdown
|
|
226
|
+
### Requirements ↔ Test Plan 매트릭스
|
|
227
|
+
|
|
228
|
+
| 요구사항 | 커버 시나리오 # |
|
|
229
|
+
|---------|----------------|
|
|
230
|
+
| <Req 1 한 줄> | #1, #3 |
|
|
231
|
+
| <Req 2 한 줄> | #2 (end-to-end) |
|
|
232
|
+
| <Req 3 한 줄> | #5 |
|
|
233
|
+
```
|
|
211
234
|
|
|
212
235
|
### 2.6 Result 기록 (`/task-dev`, `/task-test` 끝)
|
|
213
236
|
|
|
@@ -614,3 +637,4 @@ draft → planned → developing → developed → testing → tested → closed
|
|
|
614
637
|
| 2026-05-08 | §1.5 추가 — task 파일/폴더/부속 자료 위치 단일 진실 소스. spec-diffs/screenshots는 vX.X 공통 통일. closed-immutable hook 보호 범위는 task.md 본 파일만 (스킬 본문 간 위치 모순 통일) |
|
|
615
638
|
| 2026-05-09 | 표현 정제 — 인명 / 경박 표현 / 스킬 용어 / 이전 버전 비교 단락 정리 |
|
|
616
639
|
| 2026-05-30 | stash FRICTION_LOG 기반 정합 — §1.5 mockup 행 추가 (vX.X 공통, 단일 진실 소스 MOCKUP_RULE) + §2.5 Test Plan 본질 재정의 (실질 동작 시나리오 + `[AUTO]` / `[USER]` 분류 강제 + 카탈로그 7방식 + UX/UI 영역 분리 매트릭스 + 시각 fix 사이클 사전 예고 + 검증 / 테스트 명령 두 섹션 참조) + §4.5 / §5 완성 예시 3개 Test Plan 형식 갱신 (분류 표 + PASS 기준 명시. 기존 번호 매김 시나리오 + 검증 명령 나열은 옛 형식). (stash FRICTION_LOG #14+19 / #25 정합) |
|
|
640
|
+
| 2026-06-02 | 0.1.3 F5 정합 — §2.5 가이드라인 안티패턴 3종 추가 (grep/Read-only 보조용 / 요구사항당 end-to-end 1개 이상 / 무거운 검증 회피 금지) + 요구사항 ↔ 시나리오 커버리지 점검 단계 신설 (stash FRICTION_LOG 2026-06-01 Test Plan 동어반복 + 누락 마찰 반영) |
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
> 이 파일은 Codex 세션이 *매 세션 시작 시 자동 로드*하는 단일 진입점이다.
|
|
4
|
+
> 프로젝트 메타 + 검증 명령 + 룰 참조 + 스킬 9종 + 동기화 룰만 포함한다.
|
|
5
|
+
> 사람용 도메인 설명은 `.project/PROJECT.md`, 진입 가이드는 `.project/AGENT-GUIDE.md` 참조.
|
|
6
|
+
>
|
|
7
|
+
> Codex 환경 차이 (Claude와 다른 점만):
|
|
8
|
+
> - 스킬 본문: `.agents/skills/<스킬>/SKILL.md` (Claude의 `.claude/skills/`가 아님)
|
|
9
|
+
> - Hook: `.codex/config.toml`의 `[[hooks.PreToolUse]]`로 등록 — 최초 1회 `/hooks` trust 승인 필요
|
|
10
|
+
> - 격리 검증(/task-test): `.codex/agents/task-tester.toml` 서브에이전트로 spawn
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## 프로젝트 메타
|
|
15
|
+
|
|
16
|
+
- **이름**: <프로젝트명>
|
|
17
|
+
- **타입**: <frontend / backend / fullstack / cli / library / other>
|
|
18
|
+
- **소개 (한 줄)**: <도메인 / 비전>
|
|
19
|
+
- **상세**: `.project/PROJECT.md` 참조
|
|
20
|
+
|
|
21
|
+
> `/project-init` 호출 시 위 4 항목 채워짐. 변경 필요하면 직접 편집.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 메인 세션 최상위 룰
|
|
26
|
+
|
|
27
|
+
> 본 룰은 *모든 스킬 호출 / 사용자 발화 처리 / 작업 진행*에 적용. 위반 시 사용자 마찰 직결.
|
|
28
|
+
|
|
29
|
+
1. **범위 준수** — 사용자가 명시한 범위/행동만 수행. 명시 외 자체 진입 영구 금지. 예: 사용자가 *"X부터 Y까지"* 지시 → Y 끝에서 정지 + 상태 보고 + 다음 단계 명시 호출 대기.
|
|
30
|
+
2. **Skill 정식 발동** — task 단계는 반드시 해당 스킬(`.agents/skills/<스킬>/SKILL.md`)을 정식 호출. 가이드 본문을 머릿속 절차로 대체 금지 (컴팩트 세션 / 시스템 리마인더 인지 상태에서도 동일).
|
|
31
|
+
3. **워크트리 자가 진단** — 세션 시작 시 현재 cwd가 *메인 워크트리*인지 *태스크 워크트리*인지 자가 판단 (`git rev-parse --show-toplevel` + `~/.taskery/worktrees/` 경로 비교). 태스크 워크트리면 *해당 진행중 태스크 컨텍스트*로 진입, 메인 워크트리면 *새 태스크 / 진행중 목록 인터뷰* 흐름.
|
|
32
|
+
4. **모호 발화 자의 해석 금지** — *"워크트리 없이"* / *"메인에서"* / *"이 자리에서 그냥"* 류로 읽히는 발화 = 코어 규칙(메인 워크트리 = dev 전용) 충돌 신호. 즉시 정지 + 규칙 한 줄 명시 + 1줄 confirm 요청. 자의 해석 후 진행 영구 금지. 사용자 의도가 *오타/모호*일 가능성을 *워크트리 사용*으로 읽는 게 자연스러움.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## 멀티세션 워크트리 (0.1.2+)
|
|
37
|
+
|
|
38
|
+
> 멀티세션 = 같은 프로젝트에서 *여러 메인 세션이 독립 태스크를 병렬*로 진행. git worktree로 작업 폴더 격리, dev 머지 시 직렬화.
|
|
39
|
+
|
|
40
|
+
- **메인 워크트리 = dev 전용**: 메인 워크트리는 항상 `dev` 체크아웃 상태. 모든 태스크 작업은 *예외 없이* 별도 워크트리에서 수행. 메인 워크트리 HEAD를 dev에서 떼는 어떤 작업(`git checkout <task-branch>` / `git switch <task-branch>` / `git reset` HEAD 이동 / `git rebase` HEAD 이동 등)도 영구 금지. *"잠깐만 메인에서"* / *"테스트 한 번만 메인에서"* 같은 예외 발화도 거부 — 별도 워크트리에서 처리.
|
|
41
|
+
- **워크트리 위치**: `~/.taskery/worktrees/<projectId>/TASK-NNN_<출처>_<슬러그>/`
|
|
42
|
+
- `<projectId>`: `.taskery-manifest.json` `projectId` (8자 hex)
|
|
43
|
+
- `<출처>`: `BL-NNN` / `RM-NNN` / `DR`
|
|
44
|
+
- **`/task-init`이 워크트리 생성** + **`/task-close`가 dev 머지 + 워크트리 자동 제거** (자동 흐름. 보존 키워드 `keep` / `브랜치 남겨` 등 발화 시 둘 다 보존).
|
|
45
|
+
- **메인 메타 접근**: 워크트리에서 `.project/`, `AGENTS.md` 등 접근 시 *메인 워크트리 절대 경로* 사용:
|
|
46
|
+
```sh
|
|
47
|
+
MAIN_WT=$(dirname "$(git rev-parse --path-format=absolute --git-common-dir)")
|
|
48
|
+
```
|
|
49
|
+
- **수동 git 작업 시 정합성 보장 X** — taskery 명령(스킬 / `npx @angar2/taskery <서브>`)으로만 운영.
|
|
50
|
+
- **CLI 보조 명령**:
|
|
51
|
+
- `npx @angar2/taskery status` — 진행중 태스크 + 워크트리 + dev 머지 상태 요약
|
|
52
|
+
- `npx @angar2/taskery prune` — stale 워크트리 대화형 정리
|
|
53
|
+
|
|
54
|
+
상세: `.project/rules/GIT_RULE.md` "멀티세션 워크트리 정책" 섹션.
|
|
55
|
+
|
|
56
|
+
요건: git ≥ 2.31 (`--path-format=absolute` 옵션 필요).
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## 백로그 (0.1.2+)
|
|
61
|
+
|
|
62
|
+
> `.project/tasks/<vX.X>/BACKLOG.md` = *버전별 task 후보 누적*. 사용자 발화로 1건씩 얕은 분석(개요 / 대상 영역) 곁들여 추가.
|
|
63
|
+
|
|
64
|
+
- **`/add-backlog`**: 사용자 *"~ 백로그에 추가"* 발화 → 얕은 분석(코드 탐색 X, 추정 수준) → BL-NNN 채번 → BACKLOG.md append (`[ ]` 대기).
|
|
65
|
+
- **`/task-init` 연동**: 사용자 *"백로그의 BL-NNN 진행"* 발화 → 해당 항목 *확인 마킹*. `[ ]` → `[x]` + `- TASK: TASK-NNN` 추가 (다회 진행 시 콤마).
|
|
66
|
+
- **체크박스 의미**: `[ ]` = 미확인 (task로 옮기지 않은 메모) / `[x]` = 확인 완료 (task로 옮김). **dev 머지 완료 의미 X** — 완료 여부는 `git log dev --grep 'BL-NNN'` + 브랜치명 추적 + `taskery status`.
|
|
67
|
+
|
|
68
|
+
상세: `.agents/skills/add-backlog/SKILL.md`.
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## 검증 명령
|
|
73
|
+
|
|
74
|
+
> *코드 상태 검증* (빌드 / 린트 / 타입체크). 테스트 실행은 본 섹션 X — `## 테스트 명령` 참조.
|
|
75
|
+
> `/task-dev` self-check / `/task-close` 최종 게이트가 *이 섹션을 단일 진실 소스로 참조*.
|
|
76
|
+
> 백틱(`...`) 안 명령 그대로 실행됨. 언어/프레임워크 따라 변경.
|
|
77
|
+
|
|
78
|
+
- 린트: `<예: npm run lint>`
|
|
79
|
+
- 타입체크: `<예: npm run typecheck>` (TypeScript / Flow / mypy 등 — 해당 시)
|
|
80
|
+
- 빌드: `<예: npm run build>`
|
|
81
|
+
|
|
82
|
+
> 위 항목 중 *프로젝트에 해당 없는 것*은 행 자체 삭제. 빈 백틱(``)은 사용 금지.
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## 테스트 명령
|
|
87
|
+
|
|
88
|
+
> *테스트 실행* (단위 / 통합 / E2E 등). 코드 상태 검증은 `## 검증 명령` 참조.
|
|
89
|
+
> `/task-dev` 구현 후 테스트 + `/task-test` 격리 세션이 *이 섹션을 단일 진실 소스로 참조*.
|
|
90
|
+
> 백틱(`...`) 안 명령 그대로 실행됨. 언어/프레임워크 따라 변경.
|
|
91
|
+
|
|
92
|
+
- 단위 테스트: `<예: npm test>`
|
|
93
|
+
- 통합 테스트: `<예: npm run test:integration>` (있을 시)
|
|
94
|
+
- E2E 테스트: `<예: npx playwright test>` (있을 시)
|
|
95
|
+
|
|
96
|
+
> 위 항목 중 *프로젝트에 해당 없는 것*은 행 자체 삭제. 빈 백틱(``)은 사용 금지.
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## 룰 문서 참조
|
|
101
|
+
|
|
102
|
+
| 룰 | 위치 | 역할 |
|
|
103
|
+
|----|------|------|
|
|
104
|
+
| TASK_DOC_RULE | `.project/rules/TASK_DOC_RULE.md` | task 문서 양식 (헤더 5컬럼 / 6 섹션 / 7 상태) |
|
|
105
|
+
| GIT_RULE | `.project/rules/GIT_RULE.md` (있으면) → 글로벌 `~/.claude/rules/GIT_RULE.md` (fallback) | git 정책 (브랜치 / 커밋 / 머지) |
|
|
106
|
+
| CHANGELOG_RULE | `.project/rules/CHANGELOG_RULE.md` | CHANGELOG 작성 정책 (위치 / 형식 / 필수 필드) |
|
|
107
|
+
| MOCKUP_RULE | `.project/rules/MOCKUP_RULE.md` | UX/UI task의 HTML 목업 위치 / 형식 / 네이밍 |
|
|
108
|
+
| `*.local.md` | `.project/rules/` | (옵션) 사용자 오버라이드 — `*.local.md` suffix는 npx 미터치 |
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## 스킬 9종 — project > plan > task 위계 + 회고
|
|
113
|
+
|
|
114
|
+
| 스킬 | 레벨 | 역할 |
|
|
115
|
+
|------|------|------|
|
|
116
|
+
| `/project-init` | project | PROJECT/AGENT-GUIDE/LINKED-REPOS/.env 골격 생성 (1회성) |
|
|
117
|
+
| `/plan-init` | plan | `.project/plans/<vX.X>/` 안 기획 문서 작성 |
|
|
118
|
+
| `/task-init` | task | task.md 빈 골격 + status=draft |
|
|
119
|
+
| `/task-plan` | task | Requirements/Scope/Dev Plan/Test Plan 작성 (draft → planned) |
|
|
120
|
+
| `/task-dev` | task | Phase 순서 구현 + self-check (planned → developed) |
|
|
121
|
+
| `/task-test` | task | task-tester 서브에이전트 격리 검증 (developed → tested) |
|
|
122
|
+
| `/task-close` | task | 검증 명령 게이트 + 커밋 + dev 병합 (tested → closed) |
|
|
123
|
+
| `/add-backlog` | meta | 사용자 발화로 버전별 BACKLOG.md에 항목 1건 추가 (얕은 분석 + BL-NNN 채번 — 0.1.2+) |
|
|
124
|
+
| `/log-friction` | meta | FRICTION_LOG.md에 사용자 불편 한 행 기록 |
|
|
125
|
+
|
|
126
|
+
본문은 `.agents/skills/<스킬>/SKILL.md` 참조.
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## Hook 안전망 — Catastrophic 2종
|
|
131
|
+
|
|
132
|
+
> `.codex/config.toml`의 `[[hooks.PreToolUse]]`로 등록. 최초 1회 `/hooks` trust 승인 후 자동 작동.
|
|
133
|
+
|
|
134
|
+
| Hook | 영역 | 잡는 것 |
|
|
135
|
+
|------|------|--------|
|
|
136
|
+
| `git-guard.sh` | PreToolUse(Bash) | main/dev 직접 커밋 / `--force` / `--no-verify` / `branch -D` / `reset --hard` / `clean -fd` |
|
|
137
|
+
| `closed-immutable.sh` | PreToolUse(apply_patch) | `closed` 상태 task.md 본 파일 재수정 차단 (정상 흐름은 새 task로). spec-diffs / screenshots / mockup은 자유 수정 (역사적 자료) |
|
|
138
|
+
|
|
139
|
+
**잘 지키면 hook 작동 0회 (무해). catastrophic만 차단.**
|
|
140
|
+
|
|
141
|
+
> closed-immutable.sh는 코덱스 파일편집 도구(apply_patch)의 patch envelope에서 `*** Update File:` 경로를 파싱해 검사한다(Claude판은 `file_path` 키를 읽음 — 메커니즘만 다르고 효과 동일).
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## 불편 데이터
|
|
146
|
+
|
|
147
|
+
- **누적**: `.project/FRICTION_LOG.md` — 메인이 한 줄씩 추가 (사용자 불만 발화 캐치 또는 사용자 명시)
|
|
148
|
+
- **등록**: `/log-friction` 스킬 — 사용자 명시 호출 / 불만 발화 캐치 / task-close 직후 마찰 신호 자체 감지 시 한 행 기록
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## 동기화 룰 (사용자 의무)
|
|
153
|
+
|
|
154
|
+
> taskery는 *plan/ ↔ template/ 자동 빌드 X*. 사용자가 직접 정합 유지.
|
|
155
|
+
|
|
156
|
+
- **`.project/plans/<vX.X>/` 변경 시** → 관련 task의 `spec-diffs/` 갱신 (`/task-plan` Phase 0 흐름 — 진행 중 task 있으면)
|
|
157
|
+
- **`AGENTS.md` 검증 명령 변경 시** → 모든 스킬 + hook이 그대로 따름. 별도 동기화 불필요
|
|
158
|
+
- **룰(`*.md` in `.project/rules/`) 변경 시** → 스킬 instruction이 다음 호출부터 변경 반영
|
|
159
|
+
- **`*.local.md` 사용자 오버라이드** → `npx @angar2/taskery update`가 미터치. 코어 룰 갱신 시 `*.bak` 백업 후 사용자 confirm
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## 멀티리포 통신 (해당 시)
|
|
164
|
+
|
|
165
|
+
- 송신: `.project/shared/sent/<filename>.md` 작성
|
|
166
|
+
- 수신: 연결 리포에서 `received/`로 카피 후 정독
|
|
167
|
+
- 처리 완료: `sent/completed/` 또는 `received/completed/`로 이동
|
|
168
|
+
- 연결 리포 경로: `.project/.env` (gitignore)
|
|
169
|
+
- 리포 목록: `.project/LINKED-REPOS.md`
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## 메인 세션 진입 시
|
|
174
|
+
|
|
175
|
+
1. 본 파일(`AGENTS.md`) 정독 — 검증 명령 + 룰 위치 + 스킬 목록 파악
|
|
176
|
+
2. `.project/AGENT-GUIDE.md` 정독 — 활성 plan 버전 + 폴더 구조 + 작업 흐름
|
|
177
|
+
3. `.project/plans/<활성버전>/PLAN.md` 정독 — 기획 문서 인덱스 + 진행 상태
|
|
178
|
+
4. 사용자 발화 받기 → 적절한 스킬 호출 또는 직접 작업
|
package/template/CLAUDE.md
CHANGED
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
1. **범위 준수** — 사용자가 명시한 범위/행동만 수행. 명시 외 자체 진입 영구 금지. 예: 사용자가 *"X부터 Y까지"* 지시 → Y 끝에서 정지 + 상태 보고 + 다음 단계 명시 호출 대기.
|
|
25
25
|
2. **Skill 정식 발동** — task 단계는 반드시 Skill 도구로 정식 호출. 가이드 본문을 머릿속 절차로 대체 금지 (컴팩트 세션 / 시스템 리마인더 인지 상태에서도 동일).
|
|
26
26
|
3. **워크트리 자가 진단** — 세션 시작 시 현재 cwd가 *메인 워크트리*인지 *태스크 워크트리*인지 자가 판단 (`git rev-parse --show-toplevel` + `~/.taskery/worktrees/` 경로 비교). 태스크 워크트리면 *해당 진행중 태스크 컨텍스트*로 진입, 메인 워크트리면 *새 태스크 / 진행중 목록 인터뷰* 흐름.
|
|
27
|
+
4. **모호 발화 자의 해석 금지** — *"워크트리 없이"* / *"메인에서"* / *"이 자리에서 그냥"* 류로 읽히는 발화 = 코어 규칙(메인 워크트리 = dev 전용) 충돌 신호. 즉시 정지 + 규칙 한 줄 명시 + 1줄 confirm 요청. 자의 해석 후 진행 영구 금지. 사용자 의도가 *오타/모호*일 가능성을 *워크트리 사용*으로 읽는 게 자연스러움.
|
|
27
28
|
|
|
28
29
|
---
|
|
29
30
|
|
|
@@ -31,7 +32,7 @@
|
|
|
31
32
|
|
|
32
33
|
> 멀티세션 = 같은 프로젝트에서 *여러 메인 세션이 독립 태스크를 병렬*로 진행. git worktree로 작업 폴더 격리, dev 머지 시 직렬화.
|
|
33
34
|
|
|
34
|
-
- **메인 워크트리 = dev 전용**: 메인 워크트리는 항상 `dev` 체크아웃 상태. 모든 태스크 작업은 별도 워크트리에서 수행.
|
|
35
|
+
- **메인 워크트리 = dev 전용**: 메인 워크트리는 항상 `dev` 체크아웃 상태. 모든 태스크 작업은 *예외 없이* 별도 워크트리에서 수행. 메인 워크트리 HEAD를 dev에서 떼는 어떤 작업(`git checkout <task-branch>` / `git switch <task-branch>` / `git reset` HEAD 이동 / `git rebase` HEAD 이동 등)도 영구 금지. *"잠깐만 메인에서"* / *"테스트 한 번만 메인에서"* 같은 예외 발화도 거부 — 별도 워크트리에서 처리.
|
|
35
36
|
- **워크트리 위치**: `~/.taskery/worktrees/<projectId>/TASK-NNN_<출처>_<슬러그>/`
|
|
36
37
|
- `<projectId>`: `.taskery-manifest.json` `projectId` (8자 hex)
|
|
37
38
|
- `<출처>`: `BL-NNN` / `RM-NNN` / `DR`
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
# PreToolUse(Bash) 훅 — catastrophic git 명령 차단
|
|
4
4
|
#
|
|
5
5
|
# 잡는 것:
|
|
6
|
-
# 1. main / dev 브랜치 직접 커밋
|
|
6
|
+
# 1. main / dev 브랜치 직접 커밋 (5종 변형 인식: -C / --git-dir / --work-tree)
|
|
7
7
|
# 2. git push --force / -f
|
|
8
8
|
# 3. git commit --no-verify
|
|
9
9
|
# 4. git branch -D (강제 삭제)
|
|
@@ -31,12 +31,46 @@ if ! echo "$CMD" | grep -qE '(^|;|&&|\|\|)\s*git\s'; then
|
|
|
31
31
|
exit 0
|
|
32
32
|
fi
|
|
33
33
|
|
|
34
|
-
#
|
|
34
|
+
# 헬퍼: git 명령에서 대상 워크트리 경로 추출 (5종 변형)
|
|
35
|
+
# 1. git -C <경로>
|
|
36
|
+
# 2. git --git-dir=<경로> (= 형태)
|
|
37
|
+
# 3. git --git-dir <경로> (공백 분리)
|
|
38
|
+
# 4. git --work-tree=<경로> (= 형태)
|
|
39
|
+
# 5. git --work-tree <경로> (공백 분리)
|
|
40
|
+
# 추출 실패 시 빈 문자열 (cwd 사용).
|
|
41
|
+
# --git-dir 변형은 `.git` 디렉토리를 가리키므로 dirname 처리해 워크트리 경로로 변환.
|
|
42
|
+
extract_target_path() {
|
|
43
|
+
local cmd="$1"
|
|
44
|
+
local path=""
|
|
45
|
+
|
|
46
|
+
if [[ "$cmd" =~ git[[:space:]]+-C[[:space:]]+([^[:space:]]+) ]]; then
|
|
47
|
+
path="${BASH_REMATCH[1]}"
|
|
48
|
+
elif [[ "$cmd" =~ --git-dir=([^[:space:]]+) ]]; then
|
|
49
|
+
path=$(dirname "${BASH_REMATCH[1]}")
|
|
50
|
+
elif [[ "$cmd" =~ --git-dir[[:space:]]+([^[:space:]]+) ]]; then
|
|
51
|
+
path=$(dirname "${BASH_REMATCH[1]}")
|
|
52
|
+
elif [[ "$cmd" =~ --work-tree=([^[:space:]]+) ]]; then
|
|
53
|
+
path="${BASH_REMATCH[1]}"
|
|
54
|
+
elif [[ "$cmd" =~ --work-tree[[:space:]]+([^[:space:]]+) ]]; then
|
|
55
|
+
path="${BASH_REMATCH[1]}"
|
|
56
|
+
fi
|
|
57
|
+
|
|
58
|
+
echo "$path"
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
# 1. main / dev 직접 커밋 차단 (5종 변형 인식)
|
|
35
62
|
# 현재 브랜치 확인 + git commit 명령 감지 시 차단
|
|
36
|
-
if echo "$CMD" | grep -qE '(^|;|&&|\|\|)\s*git\s+commit\b'; then
|
|
37
|
-
|
|
63
|
+
if echo "$CMD" | grep -qE '(^|;|&&|\|\|)\s*git(\s+(-C\s+\S+|--git-dir[= ]\S+|--work-tree[= ]\S+))*\s+commit\b'; then
|
|
64
|
+
TARGET_PATH=$(extract_target_path "$CMD")
|
|
65
|
+
if [ -n "$TARGET_PATH" ]; then
|
|
66
|
+
CURRENT_BRANCH=$(git -C "$TARGET_PATH" branch --show-current 2>/dev/null)
|
|
67
|
+
else
|
|
68
|
+
CURRENT_BRANCH=$(git branch --show-current 2>/dev/null)
|
|
69
|
+
fi
|
|
38
70
|
if [ "$CURRENT_BRANCH" = "main" ] || [ "$CURRENT_BRANCH" = "dev" ]; then
|
|
39
71
|
echo "git-guard: '$CURRENT_BRANCH' 브랜치 직접 커밋 차단. 작업 브랜치({타입}/{개발자}_TASK-NNN_slug)에서 커밋. (.project/rules/GIT_RULE.md §기본 원칙)" >&2
|
|
72
|
+
echo " 변형 인식: 'git -C <경로> ...' / 'git --git-dir=<경로> ...' / 'git --work-tree=<경로> ...' — 메인 cwd에서 워크트리 대상 커밋 가능." >&2
|
|
73
|
+
echo " 셸 prefix('cd <경로> && git ...' / '(cd ... && git ...)')는 hook이 정확히 인식 X — 'git -C <경로> ...' 형태로만 발행." >&2
|
|
40
74
|
exit 2
|
|
41
75
|
fi
|
|
42
76
|
fi
|
|
@@ -7,7 +7,7 @@ description: task git 마무리 — 검증 게이트 + 커밋 순서 + 머지
|
|
|
7
7
|
|
|
8
8
|
## 개요
|
|
9
9
|
|
|
10
|
-
`tested` 상태 task의 git 마무리.
|
|
10
|
+
`tested` 상태 task의 git 마무리. **호출 위치 자유** — 워크트리 cwd / 메인 워크트리 cwd / 다른 세션이 호출한 서브 세션 모두 동작 동일 (모든 git 명령이 `git -C <경로>` 형태라 cwd 무관). 사전 rebase → 충돌 해결 (3단계 에스컬레이션) → 머지 락 → 락 안 재 rebase → dev `--no-ff` 머지 → 워크트리 제거 + 작업 브랜치 자동 삭제.
|
|
11
11
|
|
|
12
12
|
**최종 게이트**: 모든 검증 명령 재실행 — 린트/타입체크/빌드 PASS여야 진행.
|
|
13
13
|
|
|
@@ -18,8 +18,19 @@ description: task git 마무리 — 검증 게이트 + 커밋 순서 + 머지
|
|
|
18
18
|
|
|
19
19
|
## 호출 위치 정책
|
|
20
20
|
|
|
21
|
-
-
|
|
22
|
-
-
|
|
21
|
+
- **호출 위치 자유** — 운영 모델에 따라 다양:
|
|
22
|
+
- *멀티세션 병렬* (system default) — 각 워크트리에 새 메인 세션 열어 그 세션이 본 스킬 호출
|
|
23
|
+
- *단일 메인 지휘* — 메인 cwd 세션 1개가 모든 task 호출 (서브 세션 X)
|
|
24
|
+
- *메인이 서브 세션 호출 병렬* — 메인이 다른 세션 spawn해서 각 task 진행
|
|
25
|
+
- **호출 분기**:
|
|
26
|
+
- 인자 명시 (`TASK-NNN`) → 해당 워크트리 컨텍스트로 진입 (`$WT_PATH = ~/.taskery/worktrees/<projectId>/TASK-NNN_<...>/`).
|
|
27
|
+
- 인자 없음 → 워크트리 cwd면 *그 워크트리의 태스크* 자동 / 메인 cwd면 진행중 태스크 (status=tested) 인터뷰 + 사용자 선택.
|
|
28
|
+
- **cwd 무관 동작** — 본 SKILL의 모든 git 명령이 `git -C "$WT_PATH" ...` / `git -C "$MAIN_WT" ...` 형태라 어느 cwd에서 호출되든 결과 동일.
|
|
29
|
+
- **내부 git 명령 형태 강제** (메인 cwd 호출 시 git-guard 오판 + 변형 우회 방지):
|
|
30
|
+
- 본 SKILL의 모든 git 명령은 *`git -C <경로> ...` 형태로만 발행*.
|
|
31
|
+
- 셸 prefix(`cd <경로> && git ...` / `(cd <경로> && git ...)`) **영구 금지** — git-guard.sh가 변형 인식 X (catastrophic 우회 위험).
|
|
32
|
+
- `--git-dir=` / `--work-tree=` 변형 **영구 금지** — 가독성 ↓, 다른 워크트리 조작 혼동.
|
|
33
|
+
- 근거: stash FRICTION_LOG 2026-06-01 — 메인 cwd 세션에서 워크트리 브랜치 커밋이 git-guard에 dev 직접 커밋으로 오인 차단된 마찰.
|
|
23
34
|
|
|
24
35
|
## 입력 처리
|
|
25
36
|
|
|
@@ -33,7 +44,7 @@ description: task git 마무리 — 검증 게이트 + 커밋 순서 + 머지
|
|
|
33
44
|
|
|
34
45
|
```sh
|
|
35
46
|
MAIN_WT=$(dirname "$(git rev-parse --path-format=absolute --git-common-dir)")
|
|
36
|
-
WT_PATH=$(pwd) #
|
|
47
|
+
WT_PATH=$(pwd) # 워크트리 cwd 호출 시. 메인 cwd / 서브 세션 호출 시 인자 또는 인터뷰 결과로 결정
|
|
37
48
|
PROJECT_ID=$(jq -r '.projectId' "$MAIN_WT/.taskery-manifest.json")
|
|
38
49
|
BRANCH=$(git -C "$WT_PATH" rev-parse --abbrev-ref HEAD)
|
|
39
50
|
LOCK_TIMEOUT_MS=$(jq -r '.lock_timeout_ms // 30000' "$MAIN_WT/.taskery-manifest.json")
|
|
@@ -324,7 +335,7 @@ git -C "$MAIN_WT" worktree remove "$WT_PATH"
|
|
|
324
335
|
|
|
325
336
|
- **사용자 명시 호출 외 자체 진입 영구 금지** (stash FRICTION_LOG #6+10/#20) — task-close는 git 영역. 사용자가 *"X부터 Y까지"* 범위 명시했는데 Y가 close 이전 단계면 close 자체 진입 영구 X.
|
|
326
337
|
- **검증 명령 재실행 게이트** — `/task-dev` self-check와 *별개*. 환경 변화 / 부분 작업 잡는 안전망.
|
|
327
|
-
- **dev 직접 커밋 절대 X** —
|
|
338
|
+
- **dev 직접 커밋 절대 X** — 작업 브랜치에서만. dev에서 `git commit` 시도 시 git-guard.sh 차단. 모든 git 명령은 cwd 무관 `git -C "$WT_PATH" ...` / `git -C "$MAIN_WT" ...` 형태 강제. 셸 prefix(`cd && git`) / `--git-dir=` / `--work-tree=` 변형 영구 금지 (§호출 위치 정책).
|
|
328
339
|
- **`--no-ff` 강제** — 머지 커밋 없으면 분기 정보 영구 손실.
|
|
329
340
|
- **머지 락 직렬화** — 두 세션이 동시 머지 시도해도 한쪽씩 순차 진행. 락 외 rebase로 race 흡수 + 락 안 재 rebase로 다른 세션 머지 흡수.
|
|
330
341
|
- **충돌 자체 해결 3단계** — 단순 자동 / 의미적 자료 / 판단 불가 사용자. 자료 한계 발견 시 *반드시 사용자에게 보고* (1순위 자료 부재 등).
|
|
@@ -13,7 +13,7 @@ self-check 명령은 프로젝트 루트 `CLAUDE.md`의 *검증 명령* 섹션
|
|
|
13
13
|
|
|
14
14
|
## 멀티세션 메타 위치 (0.1.2+)
|
|
15
15
|
|
|
16
|
-
본 스킬은 워크트리에서
|
|
16
|
+
본 스킬은 워크트리에서 호출되는 게 default (멀티세션 — 각 워크트리 = 독립 세션). 다만 호출 위치 자유 — 메인 cwd / 서브 세션 호출 등 다른 운영 모델에서도 cwd 무관 동작. 메타(`.project/`, `CLAUDE.md`) 접근 시 **메인 워크트리 절대 경로** 우선.
|
|
17
17
|
|
|
18
18
|
```sh
|
|
19
19
|
MAIN_WT=$(dirname "$(git rev-parse --path-format=absolute --git-common-dir)")
|
|
@@ -244,7 +244,7 @@ git -C "$MAIN_WT" check-ignore -q "$MAIN_WT/.project/dummy"
|
|
|
244
244
|
- task 문서: <TASK_DOC_PATH> (메인 워크트리 / 워크트리 안 — 위 케이스에 따라)
|
|
245
245
|
- 헤더: <생성일> / <vX.X> / <유형> / <규모> / draft
|
|
246
246
|
- BL 마킹: BL-<NNN> [x] (BL 출처 시. RM/DR이면 생략)
|
|
247
|
-
- 다음: 워크트리 폴더에서 새 세션
|
|
247
|
+
- 다음: /task-plan TASK-<NNN> 으로 기획 채우기 (워크트리 폴더에서 새 세션 열어 진행 / 메인 세션 그대로 진행 / 메인이 서브 세션 spawn 등 운영 모델 자유)
|
|
248
248
|
```
|
|
249
249
|
|
|
250
250
|
## 도구 가이드
|
|
@@ -13,7 +13,7 @@ description: task 기획 채우기 — Requirements / Scope / Dev Plan / Test Pl
|
|
|
13
13
|
|
|
14
14
|
## 멀티세션 메타 위치 (0.1.2+)
|
|
15
15
|
|
|
16
|
-
본 스킬은 워크트리에서
|
|
16
|
+
본 스킬은 워크트리에서 호출되는 게 default (멀티세션 — 각 워크트리 = 독립 세션). 다만 호출 위치 자유 — 메인 cwd / 서브 세션 호출 등 다른 운영 모델에서도 cwd 무관 동작. 메타(`.project/`, `CLAUDE.md`) 접근 시 **메인 워크트리 절대 경로** 우선 — *task 문서 단일 진실 소스* 유지.
|
|
17
17
|
|
|
18
18
|
```sh
|
|
19
19
|
MAIN_WT=$(dirname "$(git rev-parse --path-format=absolute --git-common-dir)")
|
|
@@ -150,9 +150,15 @@ Step 2 Requirements 인터뷰 결과로 본 task가 *UX/UI 구현 (페이지/컴
|
|
|
150
150
|
|
|
151
151
|
#### 2. 목업 confirm
|
|
152
152
|
|
|
153
|
+
**예외 없이 사용자에게 발화 강제** — UX/UI 포함 판단=O이면 본 confirm 질문은 *반드시* 발화. 메인의 효용 판단(*"이 케이스는 목업 효용 낮음"* / *"SF Symbol은 HTML 재현 X"* 등)으로 confirm 단계 자체를 건너뛰는 행위 영구 금지. 효용 판단은 질문에 *곁들이는 의견*으로만 표현 가능 (생략 결정 대체 X — 목업 제작 여부는 *사용자 검수 방식 선택권*이라 *개발 자율 판단* 영역 X).
|
|
154
|
+
|
|
155
|
+
근거: stash FRICTION_LOG 2026-06-01 — UX/UI 구현 task인데 메인이 *목업 효용 낮음* 자체 판단으로 Step 4.5 confirm 자체를 생략한 마찰.
|
|
156
|
+
|
|
153
157
|
사용자에게 한 줄 질문:
|
|
154
158
|
*"이 task UX/UI 구현 포함되어 있어. HTML 목업 만들까? 구현 정확도 + 사용자 사전 시각 확인 + task-test 검수 기준 확보 효과."*
|
|
155
159
|
|
|
160
|
+
(효용 한계 의견 곁들이기 — 해당 시): *"단, <기술 한계 — 예: SF Symbol 아이콘은 HTML 재현 X> 영역은 목업 효용 제한적."* — 의견 표현만 가능, 생략 결정 대체 X.
|
|
161
|
+
|
|
156
162
|
- 사용자 *"OK"* → 3 진행
|
|
157
163
|
- 사용자 *"NO"* → 본 Step 패스 (목업 없이 진행)
|
|
158
164
|
|
|
@@ -233,6 +239,29 @@ Test Plan = *본 task에서 구현한 요구사항이 정상 동작하는지*
|
|
|
233
239
|
- **자기완결적**: `/task-test` 격리 세션이 *task.md만 보고* 수행 가능. 메인의 plan/dev 컨텍스트 없이.
|
|
234
240
|
- **명령/기대값 포함**: 무엇을 실행하고 무엇이 기대되는지 명확.
|
|
235
241
|
- **메인 가정 X**: *"잘 될 거야"* / *"문제 없을 듯"* 같은 가정 금지.
|
|
242
|
+
- **grep/Read-only 존재 확인 시나리오 = 보조용** (stash FRICTION_LOG 2026-06-01 반영) — 구현 코드의 존재 여부를 grep/Read로 재확인하는 시나리오는 *동어반복*. PASS 카운트에서 제외하거나 실동작 시나리오의 *보조*로만 사용. 단독 검증 가치 X.
|
|
243
|
+
- **요구사항당 end-to-end 1개 이상 강제** — Requirements 각 항목이 *사용자 관점에서 실제 작동하는지* 검증하는 end-to-end 실동작 시나리오 최소 1개 보장. 산출물 실행 → 결과 확인 형태.
|
|
244
|
+
- **무거운 검증 회피 영구 금지** — 부담(빌드 수 분+ / 외부 의존 / 환경 설정) 이유로 실동작 시나리오를 grep 동어반복으로 대체 X. 최소 1회는 실제 산출물로 확인. 부담은 *fix 사이클 1~2회 예상* 같은 사전 예고로 처리 (생략 사유 X).
|
|
245
|
+
|
|
246
|
+
#### 요구사항 ↔ 시나리오 커버리지 점검 (필수 — stash FRICTION_LOG 2026-06-01 반영)
|
|
247
|
+
|
|
248
|
+
Test Plan 작성 완료 후 *반드시* 점검 단계 수행. *"누적/리셋 안 함"* 같은 요구사항이 시나리오에서 통째로 빠지는 누락 차단용.
|
|
249
|
+
|
|
250
|
+
1. Requirements 섹션 합의 항목 전체를 *매트릭스*로 펼침.
|
|
251
|
+
2. 각 요구사항 항목당 *최소 1개 시나리오 커버* 확인.
|
|
252
|
+
3. 미커버 항목 발견 시 → 시나리오 보강 후 재점검.
|
|
253
|
+
|
|
254
|
+
형식 (Test Plan 끝에 첨부):
|
|
255
|
+
|
|
256
|
+
```markdown
|
|
257
|
+
### Requirements ↔ Test Plan 매트릭스
|
|
258
|
+
|
|
259
|
+
| 요구사항 | 커버 시나리오 # |
|
|
260
|
+
|---------|----------------|
|
|
261
|
+
| <Req 1 한 줄> | #1, #3 |
|
|
262
|
+
| <Req 2 한 줄> | #2 (end-to-end) |
|
|
263
|
+
| <Req 3 한 줄> | #5 |
|
|
264
|
+
```
|
|
236
265
|
|
|
237
266
|
#### 검증/테스트 명령 참조
|
|
238
267
|
|
|
@@ -13,7 +13,7 @@ description: task 격리 세션 검증 — Task tool 격리 호출 + Test Plan
|
|
|
13
13
|
|
|
14
14
|
## 멀티세션 메타 위치 (0.1.2+)
|
|
15
15
|
|
|
16
|
-
본 스킬은 워크트리에서
|
|
16
|
+
본 스킬은 워크트리에서 호출되는 게 default (멀티세션 — 각 워크트리 = 독립 세션). 다만 호출 위치 자유 — 메인 cwd / 서브 세션 호출 등 다른 운영 모델에서도 cwd 무관 동작 (격리 세션 prompt에 *task 문서 절대 경로* + *작업 디렉토리=$WT_PATH* 명시). 격리 세션 prompt에 넘기는 task 문서 + 검증/테스트 명령 출처:
|
|
17
17
|
|
|
18
18
|
```sh
|
|
19
19
|
MAIN_WT=$(dirname "$(git rev-parse --path-format=absolute --git-common-dir)")
|
|
@@ -83,6 +83,7 @@ Task tool로 sub-agent spawn. prompt는 *자기완결적* — task.md 경로 +
|
|
|
83
83
|
7. 코드와 동작만 신뢰. *"잘 될 거야"* / *"문제 없을 듯"* 같은 가정 일체 금지.
|
|
84
84
|
8. 코드 파일 직접 수정 절대 금지. 임시 파일(테스트 스크립트 등) 생성 시 종료 후 삭제.
|
|
85
85
|
9. **task.md 본문 메타 발언과 raw 결과 분리** — task.md (Requirements / Scope / Dev Plan / Test Plan 본문)의 *어떠한 메타 발언* (*"본 Test Plan은 Dev Plan과 의도적 mismatch"* / *"본 task는 FAIL이 정상"* / *"aborted 시뮬"* 등) 도 raw 시나리오 결과 판정을 *덮을 수 없다*. raw 결과 판정 = *실행 명령 exit code / 로그 출력 / 어서션 통과 여부*만. 본문 의도 해석 영역 외 — 결과 기반 판정만.
|
|
86
|
+
10. **grep/Read-only 존재 확인 시나리오 = 보조 검증** (stash FRICTION_LOG 2026-06-01 반영) — 시나리오가 *코드/파일에 X가 존재하나*를 grep / Read로 재확인하는 형태(동어반복)면 *보조 검증*으로 분류. 종합 판정의 PASS 카운트에서 제외하고, 같은 영역의 *실동작 시나리오*가 PASS인 경우에만 보조 자료로 첨부. 단독 PASS 가치 X. 실동작 시나리오 없이 grep-only 시나리오만으로 종합 PASS 단정 영구 금지 — 그 경우 *실동작 시나리오 부재*로 UNCERTAIN/FAIL 판정 + 메인 보고.
|
|
86
87
|
|
|
87
88
|
## 결과 형식 (이 포맷 그대로 리턴)
|
|
88
89
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|