@walwal-harness/cli 5.5.3 → 5.6.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.
- package/README.md +96 -12
- package/assets/templates/config.json +12 -6
- package/bin/init.js +185 -6
- package/package.json +9 -1
- package/skills/dispatcher/SKILL.md +25 -9
- package/skills/dispatcher/references/convention-flow.md +111 -0
- package/skills/evaluator-code-quality/SKILL.md +8 -7
- package/skills/evaluator-functional/SKILL.md +8 -6
- package/skills/evaluator-visual/SKILL.md +6 -4
- package/skills/generator-backend/SKILL.md +13 -8
- package/skills/generator-frontend/SKILL.md +16 -11
- package/skills/planner/SKILL.md +9 -7
package/README.md
CHANGED
|
@@ -92,7 +92,17 @@ npx walwal-harness team
|
|
|
92
92
|
- 단축키: `tmux prefix + 방향키` 로 패널 이동, `prefix + z` 로 확대/축소.
|
|
93
93
|
- 종료: `npx walwal-harness team --kill`.
|
|
94
94
|
|
|
95
|
-
### Step 5.
|
|
95
|
+
### Step 5. Feedback 등록 — 학습 누적
|
|
96
|
+
|
|
97
|
+
대화 중 사용자 피드백은 **성격에 따라 3개 저장소** 중 하나로 자동 분류됩니다.
|
|
98
|
+
|
|
99
|
+
| 유형 | 성격 | 시그널 | 저장 위치 | ID |
|
|
100
|
+
|------|------|--------|----------|-----|
|
|
101
|
+
| **Gotcha** | 에이전트 실수(부정) | "~하지 마", "잘못됐어" | `.harness/gotchas/<agent>.md` | `G-NNN` |
|
|
102
|
+
| **Convention** | 하우스 스타일(긍정) | "~해야 해", "이렇게 해줘" | `.harness/conventions/<scope>.md` | `C-NNN` |
|
|
103
|
+
| **Memory** | 전체 공통 교훈 | "모든 에이전트가~" | `.harness/memory.md` | `M-NNN` |
|
|
104
|
+
|
|
105
|
+
#### 5a. Gotcha — "이러면 안 돼"
|
|
96
106
|
|
|
97
107
|
사용자가 에이전트의 실수를 지적하면 Dispatcher 가 해당 에이전트의 `.harness/gotchas/<agent>.md` 에 자동 append. 다음 세션부터 그 에이전트는 세션 시작 시 자기 gotcha 파일을 읽고 같은 실수를 피합니다.
|
|
98
108
|
|
|
@@ -122,7 +132,60 @@ Dispatcher 는 자동으로 분류:
|
|
|
122
132
|
- **Applies to**: generator-backend
|
|
123
133
|
```
|
|
124
134
|
|
|
125
|
-
####
|
|
135
|
+
#### 5b. Convention — "이렇게 해줘"
|
|
136
|
+
|
|
137
|
+
긍정 가이드는 하우스 스타일로 등록됩니다:
|
|
138
|
+
|
|
139
|
+
```
|
|
140
|
+
API 응답 필드는 전부 snake_case 로 해야 해. FE TS 모델이
|
|
141
|
+
snake_case 로 정의돼 있어서 변환 레이어를 두기 싫어.
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Dispatcher 자동 분류:
|
|
145
|
+
- **스코프**: `generator-backend` (API 응답 → BE 스코프)
|
|
146
|
+
- **저장 위치**: `.harness/conventions/generator-backend.md`
|
|
147
|
+
- **ID 할당**: `[C-001]` (해당 파일의 기존 최댓값 + 1)
|
|
148
|
+
|
|
149
|
+
기록 포맷 (자동 작성):
|
|
150
|
+
|
|
151
|
+
```markdown
|
|
152
|
+
### [C-001] API 응답 필드는 snake_case
|
|
153
|
+
- **Date**: 2026-04-22
|
|
154
|
+
- **Scope**: generator-backend
|
|
155
|
+
- **Rule**: 모든 API 응답 JSON 필드는 snake_case (created_at, user_id 등).
|
|
156
|
+
- **Rationale**: FE TS 모델이 snake_case 로 정의돼 있어 변환 레이어 불필요.
|
|
157
|
+
- **Applies to**: generator-backend, libs/shared-dto
|
|
158
|
+
- **Added from**: user prompt (2026-04-22 16:12)
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
#### 스코프 판별 (Convention)
|
|
162
|
+
|
|
163
|
+
| 키워드 | 스코프 (파일) |
|
|
164
|
+
|--------|-------------|
|
|
165
|
+
| backend, API, controller, service, DTO, NestJS | `generator-backend.md` |
|
|
166
|
+
| frontend, React, Next.js, UI, component, hook | `generator-frontend.md` |
|
|
167
|
+
| plan, sprint, feature-list | `planner.md` |
|
|
168
|
+
| Playwright, E2E, browser | `evaluator-functional.md` |
|
|
169
|
+
| layout, screenshot, a11y, responsive | `evaluator-visual.md` |
|
|
170
|
+
| code quality, lint, architecture | `evaluator-code-quality.md` |
|
|
171
|
+
| 매칭 실패 + 에이전트 국한 | `shared.md` |
|
|
172
|
+
| 프로젝트 철학 (예: "우리는 TDD") | 루트 `CONVENTIONS.md` 권고 |
|
|
173
|
+
|
|
174
|
+
#### 에이전트 와이어링
|
|
175
|
+
|
|
176
|
+
각 에이전트는 세션 시작 시 다음 순서로 읽고 적용:
|
|
177
|
+
|
|
178
|
+
```
|
|
179
|
+
1. CONVENTIONS.md (루트, 최상위 원칙)
|
|
180
|
+
2. .harness/conventions/shared.md (공통)
|
|
181
|
+
3. .harness/conventions/<self>.md (자기 스코프)
|
|
182
|
+
4. .harness/gotchas/<self>.md (과거 실수)
|
|
183
|
+
5. .harness/memory.md (공유 교훈)
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
충돌 시 우선순위: `<self>` > `shared` > 루트.
|
|
187
|
+
|
|
188
|
+
#### 5c. Memory — 프로젝트 전체 규칙
|
|
126
189
|
|
|
127
190
|
한 에이전트 실수가 아니라 **모든 에이전트에 적용할 구조적 교훈** 이면 `.harness/memory.md` 로 승격:
|
|
128
191
|
|
|
@@ -136,7 +199,18 @@ Dispatcher 는 자동으로 분류:
|
|
|
136
199
|
|
|
137
200
|
#### 주의 — 데이터 보존
|
|
138
201
|
|
|
139
|
-
|
|
202
|
+
`npm install` postinstall 은 **누적 엔트리(`[G-NNN]` 또는 `[C-NNN]`)가 있는 파일을 절대 덮어쓰지 않습니다**. 스캐폴드 템플릿인 경우에만 갱신됩니다. v5.5.2 이전 버전은 gotchas 에 이 버그가 있었으므로 `5.6.0+` 사용을 권장합니다.
|
|
203
|
+
|
|
204
|
+
#### 기존 프로젝트 마이그레이션 (첫 설치 시 자동)
|
|
205
|
+
|
|
206
|
+
기존 `CLAUDE.md` / `AGENTS.md` 에 Convention/Gotcha 성격의 섹션(`Conventions`, `Coding Standards`, `Best Practices`, `Gotchas`, `Don't`, `주의사항`, `금지사항` 등)이 있다면 첫 설치 시 자동으로 추출되어:
|
|
207
|
+
|
|
208
|
+
- **Convention 성격** → `.harness/conventions/<scope>.md` 에 `[C-NNN]` 으로 이관
|
|
209
|
+
- **Gotcha 성격** → `.harness/gotchas/<agent>.md` 에 `[G-NNN]` 으로 이관
|
|
210
|
+
- **원본** → `.harness/archive/pre-harness-*.md.bak` 에 백업
|
|
211
|
+
- **리포트** → `.harness/MIGRATION_REPORT.md` 에 이관 내역 + 수동 확인 요청 사항 기록
|
|
212
|
+
|
|
213
|
+
마이그레이션은 heuristic(키워드 기반)이므로 리포트를 확인해 스코프 재배정이 필요한지 검토하세요. 이미 하네스 서명(`[BE]`/`[FE]`/`[HARNESS]`)이 있는 문서는 skip 됩니다.
|
|
140
214
|
|
|
141
215
|
---
|
|
142
216
|
|
|
@@ -298,14 +372,23 @@ your-project/
|
|
|
298
372
|
│ │ ├── evaluation-code-quality.md
|
|
299
373
|
│ │ ├── evaluation-functional.md
|
|
300
374
|
│ │ └── evaluation-visual.md
|
|
301
|
-
│ ├── archive/ # 완료 스프린트 보관 (
|
|
302
|
-
│
|
|
303
|
-
│
|
|
304
|
-
│
|
|
305
|
-
│
|
|
306
|
-
│
|
|
307
|
-
│
|
|
308
|
-
│
|
|
375
|
+
│ ├── archive/ # 완료 스프린트 보관 (불변, 마이그레이션 백업도 여기)
|
|
376
|
+
│ ├── gotchas/ # 에이전트 실수 기록 [G-NNN] (누적 보존)
|
|
377
|
+
│ │ ├── planner.md
|
|
378
|
+
│ │ ├── generator-backend.md
|
|
379
|
+
│ │ ├── generator-frontend.md
|
|
380
|
+
│ │ ├── evaluator-code-quality.md
|
|
381
|
+
│ │ ├── evaluator-functional.md
|
|
382
|
+
│ │ └── evaluator-visual.md
|
|
383
|
+
│ ├── conventions/ # 하우스 스타일 [C-NNN] (v5.6+, 누적 보존)
|
|
384
|
+
│ │ ├── shared.md
|
|
385
|
+
│ │ ├── planner.md
|
|
386
|
+
│ │ ├── generator-backend.md
|
|
387
|
+
│ │ ├── generator-frontend.md
|
|
388
|
+
│ │ ├── evaluator-code-quality.md
|
|
389
|
+
│ │ ├── evaluator-functional.md
|
|
390
|
+
│ │ └── evaluator-visual.md
|
|
391
|
+
│ └── MIGRATION_REPORT.md # 첫 설치 시 기존 문서 이관 내역 (있을 때만)
|
|
309
392
|
├── .claude/
|
|
310
393
|
│ ├── skills/harness-*/ # 에이전트 스킬 (8개)
|
|
311
394
|
│ ├── commands/harness-*.md # 모드 제어 커맨드 (3개)
|
|
@@ -323,7 +406,8 @@ your-project/
|
|
|
323
406
|
│ ├── harness-prompt-history.sh # 프롬프트 히스토리
|
|
324
407
|
│ └── lib/ # 공유 라이브러리
|
|
325
408
|
├── AGENTS.md # 프로젝트 컨텍스트 (IA-MAP)
|
|
326
|
-
|
|
409
|
+
├── CLAUDE.md → AGENTS.md # 심볼릭 링크
|
|
410
|
+
└── CONVENTIONS.md # 최상위 원칙 (사용자 자유 기술, 하위는 .harness/conventions/)
|
|
327
411
|
```
|
|
328
412
|
|
|
329
413
|
---
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"harness": {
|
|
3
3
|
"name": "7-Agent Production Harness",
|
|
4
|
-
"version": "5.
|
|
5
|
-
"description": "Solo/Team 통합 하네스 — Dispatcher + 3-Evaluator Chain (Code-Quality → Functional → Visual) + NestJS MSA + React/Next.js + Playwright",
|
|
4
|
+
"version": "5.6.0",
|
|
5
|
+
"description": "Solo/Team 통합 하네스 — Dispatcher + 3-Evaluator Chain (Code-Quality → Functional → Visual) + Hierarchical Conventions/Gotchas + NestJS MSA + React/Next.js + Playwright",
|
|
6
6
|
"source": "https://www.anthropic.com/engineering/harness-design-long-running-apps"
|
|
7
7
|
},
|
|
8
8
|
"agents": {
|
|
@@ -368,11 +368,17 @@
|
|
|
368
368
|
"statuses": ["start", "complete", "fail", "pass", "skip", "warn"]
|
|
369
369
|
},
|
|
370
370
|
"conventions": {
|
|
371
|
-
"comment": "CONVENTIONS.md
|
|
372
|
-
"
|
|
371
|
+
"comment": "House-style registry. 루트 CONVENTIONS.md(사용자 자유 기술) + .harness/conventions/<scope>.md(Dispatcher 자동 누적). 모든 에이전트가 세션 시작 시 읽고 적용한다. Gotcha 의 긍정 대칭판.",
|
|
372
|
+
"root_file": "CONVENTIONS.md",
|
|
373
|
+
"scoped_dir": ".harness/conventions/",
|
|
374
|
+
"scopes": ["shared", "planner", "generator-backend", "generator-frontend", "evaluator-code-quality", "evaluator-functional", "evaluator-visual"],
|
|
375
|
+
"entry_id_prefix": "C",
|
|
373
376
|
"read_by": "all_agents",
|
|
374
|
-
"
|
|
375
|
-
"
|
|
377
|
+
"read_order": ["CONVENTIONS.md", ".harness/conventions/shared.md", ".harness/conventions/<self>.md", ".harness/gotchas/<self>.md", ".harness/memory.md"],
|
|
378
|
+
"conflict_priority": "self > shared > root",
|
|
379
|
+
"write_by": "dispatcher (auto-append C-NNN) or user_only (manual edit)",
|
|
380
|
+
"read_timing": "session_start",
|
|
381
|
+
"preserve_on_postinstall": true
|
|
376
382
|
},
|
|
377
383
|
"recommended_skills": {
|
|
378
384
|
"comment": "Claude Code에 설치하면 하네스 품질이 향상되는 외부 스킬 목록",
|
package/bin/init.js
CHANGED
|
@@ -90,16 +90,162 @@ function log(msg) {
|
|
|
90
90
|
console.log(`[walwal-harness] ${msg}`);
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
+
// ─────────────────────────────────────────
|
|
94
|
+
// First-install migration — extract Convention/Gotcha-shaped sections from
|
|
95
|
+
// existing CLAUDE.md / AGENTS.md into .harness/conventions and .harness/gotchas.
|
|
96
|
+
// Conservative: only triggers when these docs are NOT already harness-scaffolded
|
|
97
|
+
// (detected by IA-MAP tags like "[BE]" or "[HARNESS]").
|
|
98
|
+
// ─────────────────────────────────────────
|
|
99
|
+
function migrateExistingDocs() {
|
|
100
|
+
// Match heading titles. Use (?=\s|$) instead of \b — Korean chars are
|
|
101
|
+
// not "word" in JS regex, so \b produces inconsistent matches.
|
|
102
|
+
const CONVENTION_HEADINGS = /^#{1,4}\s+(Conventions?|Coding Standards?|Style Guide|Rules|Guidelines|Best Practices|Do's and Don'ts|규칙|하우스 스타일|명명 규칙|코딩 규칙|코드 스타일)(?=[\s:]|$)/im;
|
|
103
|
+
const GOTCHA_HEADINGS = /^#{1,4}\s+(Gotchas?|Anti[- ]?patterns?|Don'?ts?|Avoid|Pitfalls?|주의사항|금지사항|실수|함정|안티[- ]?패턴)(?=[\s:]|$)/im;
|
|
104
|
+
const HARNESS_SIGNATURE = /\[(BE|FE|HARNESS|META|INFRA|ROOT)\]|walwal-harness|harness-dispatcher/;
|
|
105
|
+
|
|
106
|
+
const candidates = [
|
|
107
|
+
path.join(PROJECT_ROOT, 'CLAUDE.md'),
|
|
108
|
+
path.join(PROJECT_ROOT, 'AGENTS.md')
|
|
109
|
+
];
|
|
110
|
+
|
|
111
|
+
const report = [];
|
|
112
|
+
const extractedConv = { counter: 0, byScope: {} };
|
|
113
|
+
const extractedGotcha = { counter: 0, byAgent: {} };
|
|
114
|
+
|
|
115
|
+
const scopeFor = (body) => {
|
|
116
|
+
const b = body.toLowerCase();
|
|
117
|
+
if (/\b(backend|api|nestjs|controller|dto|service|msa|repository)\b/.test(b)) return 'generator-backend';
|
|
118
|
+
if (/\b(frontend|react|next\.?js|ui|component|tsx|tailwind|hook)\b/.test(b)) return 'generator-frontend';
|
|
119
|
+
if (/\b(planner|plan\.md|sprint|feature-list|api-contract)\b/.test(b)) return 'planner';
|
|
120
|
+
if (/\b(playwright|e2e|functional test)\b/.test(b)) return 'evaluator-functional';
|
|
121
|
+
if (/\b(visual|layout|screenshot|a11y|accessibility|responsive)\b/.test(b)) return 'evaluator-visual';
|
|
122
|
+
if (/\b(code quality|lint|tsc|architecture|typescript strict)\b/.test(b)) return 'evaluator-code-quality';
|
|
123
|
+
return 'shared';
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const appendEntry = (filePath, id, kind, title, body, source) => {
|
|
127
|
+
const existing = fs.existsSync(filePath) ? fs.readFileSync(filePath, 'utf8') : '';
|
|
128
|
+
const entry = [
|
|
129
|
+
``,
|
|
130
|
+
`### [${id}] ${title}`,
|
|
131
|
+
`- **Date**: ${new Date().toISOString().split('T')[0]}`,
|
|
132
|
+
`- **Source**: ${source} (migrated)`,
|
|
133
|
+
``,
|
|
134
|
+
body.trim(),
|
|
135
|
+
``
|
|
136
|
+
].join('\n');
|
|
137
|
+
fs.writeFileSync(filePath, existing.replace(/\s*$/, '') + '\n' + entry + '\n');
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
for (const docPath of candidates) {
|
|
141
|
+
if (!fileExists(docPath)) continue;
|
|
142
|
+
const content = fs.readFileSync(docPath, 'utf8');
|
|
143
|
+
if (HARNESS_SIGNATURE.test(content)) {
|
|
144
|
+
// Already a harness-managed doc — skip
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Backup
|
|
149
|
+
const backupPath = path.join(HARNESS_DIR, 'archive', `pre-harness-${path.basename(docPath)}.bak`);
|
|
150
|
+
fs.writeFileSync(backupPath, content);
|
|
151
|
+
report.push(`Backed up: ${docPath} → ${backupPath}`);
|
|
152
|
+
|
|
153
|
+
// Split by top-level and H2 headings to get sections
|
|
154
|
+
// Simple approach: find heading lines, slice until next heading of same-or-higher level
|
|
155
|
+
const lines = content.split('\n');
|
|
156
|
+
const sections = [];
|
|
157
|
+
let current = null;
|
|
158
|
+
lines.forEach((line, idx) => {
|
|
159
|
+
const m = /^(#{1,4})\s+(.+?)\s*$/.exec(line);
|
|
160
|
+
if (m) {
|
|
161
|
+
if (current) sections.push(current);
|
|
162
|
+
current = { level: m[1].length, title: m[2], startLine: idx + 1, endLine: idx + 1, body: [] };
|
|
163
|
+
} else if (current) {
|
|
164
|
+
current.body.push(line);
|
|
165
|
+
current.endLine = idx + 1;
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
if (current) sections.push(current);
|
|
169
|
+
|
|
170
|
+
for (const sec of sections) {
|
|
171
|
+
const header = `${'#'.repeat(sec.level)} ${sec.title}`;
|
|
172
|
+
const isConv = CONVENTION_HEADINGS.test(header);
|
|
173
|
+
const isGotcha = GOTCHA_HEADINGS.test(header);
|
|
174
|
+
if (!isConv && !isGotcha) continue;
|
|
175
|
+
const body = sec.body.join('\n').trim();
|
|
176
|
+
if (!body) continue;
|
|
177
|
+
|
|
178
|
+
const sourceRef = `${path.basename(docPath)}:${sec.startLine}-${sec.endLine}`;
|
|
179
|
+
|
|
180
|
+
if (isConv) {
|
|
181
|
+
const scope = scopeFor(sec.title + '\n' + body);
|
|
182
|
+
extractedConv.counter += 1;
|
|
183
|
+
const id = `C-${String(extractedConv.counter).padStart(3, '0')}`;
|
|
184
|
+
const target = path.join(HARNESS_DIR, 'conventions', `${scope}.md`);
|
|
185
|
+
appendEntry(target, id, 'convention', sec.title, body, sourceRef);
|
|
186
|
+
extractedConv.byScope[scope] = (extractedConv.byScope[scope] || 0) + 1;
|
|
187
|
+
report.push(`[${id}] "${sec.title}" → conventions/${scope}.md (from ${sourceRef})`);
|
|
188
|
+
} else {
|
|
189
|
+
const scope = scopeFor(sec.title + '\n' + body);
|
|
190
|
+
const agent = scope === 'shared' ? 'planner' : scope; // default shared-gotchas to planner
|
|
191
|
+
extractedGotcha.counter += 1;
|
|
192
|
+
const id = `G-${String(extractedGotcha.counter).padStart(3, '0')}`;
|
|
193
|
+
const target = path.join(HARNESS_DIR, 'gotchas', `${agent}.md`);
|
|
194
|
+
appendEntry(target, id, 'gotcha', sec.title, body, sourceRef);
|
|
195
|
+
extractedGotcha.byAgent[agent] = (extractedGotcha.byAgent[agent] || 0) + 1;
|
|
196
|
+
report.push(`[${id}] "${sec.title}" → gotchas/${agent}.md (from ${sourceRef})`);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (report.length === 0) return;
|
|
202
|
+
|
|
203
|
+
const reportPath = path.join(HARNESS_DIR, 'MIGRATION_REPORT.md');
|
|
204
|
+
const reportContent = [
|
|
205
|
+
`# Walwal-Harness Migration Report`,
|
|
206
|
+
``,
|
|
207
|
+
`Generated on first install at ${new Date().toISOString()}.`,
|
|
208
|
+
``,
|
|
209
|
+
`## Summary`,
|
|
210
|
+
``,
|
|
211
|
+
`- Conventions extracted: ${extractedConv.counter}`,
|
|
212
|
+
`- Gotchas extracted: ${extractedGotcha.counter}`,
|
|
213
|
+
``,
|
|
214
|
+
`## Manual Review Required`,
|
|
215
|
+
``,
|
|
216
|
+
`Migration is heuristic (keyword-based). Please review each extracted entry:`,
|
|
217
|
+
`- Verify scope assignment is correct`,
|
|
218
|
+
`- Split entries into smaller atomic rules if appropriate`,
|
|
219
|
+
`- Adjust wording to positive-rule form for conventions, negative/anti-pattern form for gotchas`,
|
|
220
|
+
``,
|
|
221
|
+
`## Entries`,
|
|
222
|
+
``,
|
|
223
|
+
...report.map(r => `- ${r}`),
|
|
224
|
+
``,
|
|
225
|
+
`## Backups`,
|
|
226
|
+
``,
|
|
227
|
+
`Original documents were preserved in \`.harness/archive/pre-harness-*.md.bak\`.`,
|
|
228
|
+
``
|
|
229
|
+
].join('\n');
|
|
230
|
+
fs.writeFileSync(reportPath, reportContent);
|
|
231
|
+
log(`Migration: ${extractedConv.counter} convention(s), ${extractedGotcha.counter} gotcha(s) extracted.`);
|
|
232
|
+
log(`Migration report: ${reportPath}`);
|
|
233
|
+
}
|
|
234
|
+
|
|
93
235
|
// ─────────────────────────────────────────
|
|
94
236
|
// 1. .harness/ scaffolding
|
|
95
237
|
// ─────────────────────────────────────────
|
|
96
238
|
function scaffoldHarness() {
|
|
97
239
|
log('Scaffolding .harness/ directory...');
|
|
98
240
|
|
|
241
|
+
// Detect first install BEFORE ensureDir creates the root
|
|
242
|
+
const isFirstInstall = !fs.existsSync(HARNESS_DIR);
|
|
243
|
+
|
|
99
244
|
// Core directories
|
|
100
245
|
ensureDir(path.join(HARNESS_DIR, 'actions'));
|
|
101
246
|
ensureDir(path.join(HARNESS_DIR, 'archive'));
|
|
102
247
|
ensureDir(path.join(HARNESS_DIR, 'gotchas'));
|
|
248
|
+
ensureDir(path.join(HARNESS_DIR, 'conventions'));
|
|
103
249
|
|
|
104
250
|
// Copy gotchas — preserve any existing file that has accumulated entries.
|
|
105
251
|
// Dispatcher appends `### [G-NNN] ...` entries directly; we must NEVER overwrite
|
|
@@ -136,6 +282,39 @@ function scaffoldHarness() {
|
|
|
136
282
|
}
|
|
137
283
|
}
|
|
138
284
|
|
|
285
|
+
// Copy conventions — mirror gotchas preservation: never overwrite files with
|
|
286
|
+
// accumulated `### [C-NNN]` entries.
|
|
287
|
+
const conventionsSrc = path.join(PKG_ROOT, 'conventions');
|
|
288
|
+
if (fs.existsSync(conventionsSrc)) {
|
|
289
|
+
const CONV_ENTRY = /^### \[C-\d+\]/m;
|
|
290
|
+
const files = fs.readdirSync(conventionsSrc);
|
|
291
|
+
for (const file of files) {
|
|
292
|
+
const destPath = path.join(HARNESS_DIR, 'conventions', file);
|
|
293
|
+
const srcPath = path.join(conventionsSrc, file);
|
|
294
|
+
if (!fileExists(destPath)) {
|
|
295
|
+
copyFile(srcPath, destPath);
|
|
296
|
+
continue;
|
|
297
|
+
}
|
|
298
|
+
if (!file.endsWith('.md')) continue;
|
|
299
|
+
const existing = fs.readFileSync(destPath, 'utf8');
|
|
300
|
+
if (CONV_ENTRY.test(existing)) {
|
|
301
|
+
if (file === 'README.md') copyFile(srcPath, destPath);
|
|
302
|
+
continue;
|
|
303
|
+
}
|
|
304
|
+
copyFile(srcPath, destPath);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// First-install migration: extract Convention/Gotcha-shaped sections from
|
|
309
|
+
// existing CLAUDE.md / AGENTS.md and copy into the hierarchical stores.
|
|
310
|
+
if (isFirstInstall) {
|
|
311
|
+
try {
|
|
312
|
+
migrateExistingDocs();
|
|
313
|
+
} catch (e) {
|
|
314
|
+
log('WARNING: migration failed — ' + e.message);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
139
318
|
// Copy templates as initial files
|
|
140
319
|
const templateMap = {
|
|
141
320
|
'progress.json.template': path.join(HARNESS_DIR, 'progress.json'),
|
|
@@ -232,12 +411,12 @@ function scaffoldHarness() {
|
|
|
232
411
|
copyFile(memorySrc, memoryDest);
|
|
233
412
|
}
|
|
234
413
|
|
|
235
|
-
// Copy CONVENTIONS.md to project root
|
|
236
|
-
const
|
|
237
|
-
const
|
|
238
|
-
if (fs.existsSync(
|
|
239
|
-
copyFile(
|
|
240
|
-
log('CONVENTIONS.md created — edit to define
|
|
414
|
+
// Copy CONVENTIONS.md to project root (legacy — root still supported)
|
|
415
|
+
const rootConvSrc = path.join(PKG_ROOT, 'assets', 'templates', 'CONVENTIONS.md');
|
|
416
|
+
const rootConvDest = path.join(PROJECT_ROOT, 'CONVENTIONS.md');
|
|
417
|
+
if (fs.existsSync(rootConvSrc) && (!fileExists(rootConvDest) || isForce)) {
|
|
418
|
+
copyFile(rootConvSrc, rootConvDest);
|
|
419
|
+
log('CONVENTIONS.md created — edit to define top-level project conventions');
|
|
241
420
|
}
|
|
242
421
|
|
|
243
422
|
// Create progress.log
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@walwal-harness/cli",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.6.1",
|
|
4
4
|
"description": "Production harness for AI agent engineering — Solo/Team mode, Planner, Generator(BE/FE), Evaluator chain (Code-Quality → Functional → Visual), optional Brainstormer. Supports React, Next.js, and Flutter FE stacks.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"walwal-harness": "bin/init.js"
|
|
@@ -31,6 +31,14 @@
|
|
|
31
31
|
],
|
|
32
32
|
"author": "",
|
|
33
33
|
"license": "MIT",
|
|
34
|
+
"homepage": "https://github.com/30eggis/walwal-harness#readme",
|
|
35
|
+
"repository": {
|
|
36
|
+
"type": "git",
|
|
37
|
+
"url": "git+https://github.com/30eggis/walwal-harness.git"
|
|
38
|
+
},
|
|
39
|
+
"bugs": {
|
|
40
|
+
"url": "https://github.com/30eggis/walwal-harness/issues"
|
|
41
|
+
},
|
|
34
42
|
"files": [
|
|
35
43
|
"bin/",
|
|
36
44
|
"skills/",
|
|
@@ -46,19 +46,25 @@ Claude 는 기본적으로 Dispatcher 경유로 분류/라우팅해야 한다.
|
|
|
46
46
|
|
|
47
47
|
사용자 입력을 먼저 분류합니다:
|
|
48
48
|
|
|
49
|
-
- **실수
|
|
49
|
+
- **실수 지적 (부정)** ("아니", "잘못", "그렇게 하면 안 돼", "~하지 마") → **Gotcha Flow**
|
|
50
|
+
- **긍정 규범** ("~해야 해", "~이렇게 해줘", "항상 ~", "우리는 ~ 방식") → **Convention Flow**
|
|
50
51
|
- **기능 요청** ("만들어", "추가", "시작", PRD, OpenAPI) → **Pipeline Flow**
|
|
51
|
-
- **혼합** → Gotcha 먼저 기록 → Pipeline 이어서
|
|
52
|
+
- **혼합** → Gotcha/Convention 먼저 기록 → Pipeline 이어서
|
|
52
53
|
- **메타/인사/Claude 자체 질문** → Dispatcher skip, 짧은 일반 응답 허용
|
|
53
54
|
|
|
54
|
-
|
|
55
|
+
**부정 vs 긍정 구분법**: "X 하지 마 / X 가 틀렸어 / 그렇게 하면 안 돼" 는 **Gotcha**. "X 를 해야 해 / X 로 해줘 / 항상 X" 는 **Convention**. 동일 주제도 시그널에 따라 저장 위치가 달라집니다.
|
|
55
56
|
|
|
56
|
-
|
|
57
|
+
## 2. Feedback Taxonomy — Gotcha / Convention / Memory
|
|
57
58
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
|
61
|
-
|
|
59
|
+
사용자의 교정/가이드를 받으면 **먼저 분류**:
|
|
60
|
+
|
|
61
|
+
| 유형 | 성격 | 저장 위치 | ID | 예시 |
|
|
62
|
+
|------|------|----------|-----|------|
|
|
63
|
+
| **Gotcha** | 특정 에이전트의 **일회성 실수(사고)** 기록 (negative) | `.harness/gotchas/<agent>.md` | `[G-NNN]` | "Generator-BE 가 MockServer 무시하고 실 DB 붙지 마" |
|
|
64
|
+
| **Convention** | 에이전트/스코프의 **하우스 스타일(norm)** (positive) | `.harness/conventions/<scope>.md` | `[C-NNN]` | "API 응답 필드는 snake_case" |
|
|
65
|
+
| **Memory** | **모든 에이전트** 공통 구조적 교훈 | `.harness/memory.md` | `[M-NNN]` | "Playwright 스크린샷은 단계 완료 후 항상 삭제" |
|
|
66
|
+
|
|
67
|
+
Scope 가 특정 에이전트를 넘어서면 Memory. 특정 에이전트에 해당하면 Gotcha(부정) 혹은 Convention(긍정).
|
|
62
68
|
|
|
63
69
|
### Gotcha Flow (에이전트별 실수)
|
|
64
70
|
|
|
@@ -67,7 +73,17 @@ Claude 는 기본적으로 Dispatcher 경유로 분류/라우팅해야 한다.
|
|
|
67
73
|
핵심:
|
|
68
74
|
1. 교정 시그널 감지 (HIGH/MEDIUM만 기록)
|
|
69
75
|
2. 도메인 분석 → 대상 에이전트 판별
|
|
70
|
-
3. `.harness/gotchas/[agent].md`에
|
|
76
|
+
3. `.harness/gotchas/[agent].md`에 `[G-NNN]` 추가 (중복 시 Occurrences 증가)
|
|
77
|
+
4. 사용자에게 기록 확인
|
|
78
|
+
|
|
79
|
+
### Convention Flow (에이전트별 하우스 스타일)
|
|
80
|
+
|
|
81
|
+
긍정 가이드 감지 시 → [Convention 상세 가이드](references/convention-flow.md)
|
|
82
|
+
|
|
83
|
+
핵심:
|
|
84
|
+
1. 긍정 시그널 감지 ("해야 해", "이렇게 해줘", "항상" 등)
|
|
85
|
+
2. 스코프 판별: 특정 에이전트(`generator-backend` 등) / `shared` / 프로젝트 전체(루트 `CONVENTIONS.md`)
|
|
86
|
+
3. `.harness/conventions/<scope>.md` 에 `[C-NNN]` 추가
|
|
71
87
|
4. 사용자에게 기록 확인
|
|
72
88
|
|
|
73
89
|
### Memory Flow (프로젝트 공유 규칙)
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
---
|
|
2
|
+
docmeta:
|
|
3
|
+
id: convention-flow
|
|
4
|
+
title: Convention Flow — Positive Guide Classifier
|
|
5
|
+
type: output
|
|
6
|
+
createdAt: 2026-04-22T00:00:00Z
|
|
7
|
+
updatedAt: 2026-04-22T00:00:00Z
|
|
8
|
+
source:
|
|
9
|
+
producer: agent
|
|
10
|
+
skillId: harness-dispatcher
|
|
11
|
+
inputs:
|
|
12
|
+
- documentId: harness-dispatcher-skill
|
|
13
|
+
uri: ../SKILL.md
|
|
14
|
+
relation: output-from
|
|
15
|
+
sections:
|
|
16
|
+
- sourceRange:
|
|
17
|
+
startLine: 45
|
|
18
|
+
endLine: 100
|
|
19
|
+
targetRange:
|
|
20
|
+
startLine: 30
|
|
21
|
+
endLine: 140
|
|
22
|
+
tags:
|
|
23
|
+
- dispatcher
|
|
24
|
+
- convention
|
|
25
|
+
- positive-guidance
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
# Convention Flow — Positive Guide Classifier
|
|
29
|
+
|
|
30
|
+
긍정 가이드("~해야 해", "항상 ~", "이렇게 해줘")를 감지하면
|
|
31
|
+
해당 스코프의 `.harness/conventions/<scope>.md` 에 `[C-NNN]` 엔트리로 append.
|
|
32
|
+
|
|
33
|
+
## 1. 긍정 시그널 감지
|
|
34
|
+
|
|
35
|
+
다음 패턴 중 하나라도 포함되면 Convention 후보:
|
|
36
|
+
|
|
37
|
+
- 명령형: "해야 해", "~로 해줘", "이렇게 만들어", "~를 사용해"
|
|
38
|
+
- 원칙 선언: "항상 ~", "모든 ~ 는", "우리는 ~ 방식", "표준은 ~"
|
|
39
|
+
- 하우스 스타일: "이 프로젝트에서는 ~", "컨벤션상 ~", "규칙은 ~"
|
|
40
|
+
- 영어: "always", "must", "should", "we use", "prefer", "standard is"
|
|
41
|
+
|
|
42
|
+
**부정 시그널과 충돌 시 부정 우선** (Gotcha 로 라우팅). 예: "~ 하지 말고 ~ 해줘" 는 Gotcha.
|
|
43
|
+
|
|
44
|
+
## 2. Scope 판별
|
|
45
|
+
|
|
46
|
+
엔트리의 적용 범위를 다음 순서로 판별:
|
|
47
|
+
|
|
48
|
+
1. **특정 에이전트 명시** → 해당 에이전트 파일
|
|
49
|
+
- 사용자가 명시적으로 이름 언급 ("Generator-BE 는 ~") 또는 문맥상 확실한 키워드
|
|
50
|
+
2. **도메인 키워드 매칭** → 대응 에이전트
|
|
51
|
+
| 키워드 | 스코프 |
|
|
52
|
+
|--------|-------|
|
|
53
|
+
| backend, API, controller, service, DTO, NestJS, MSA | `generator-backend` |
|
|
54
|
+
| frontend, React, Next.js, UI, component, hook, Tailwind | `generator-frontend` |
|
|
55
|
+
| plan, sprint, feature-list, api-contract, roadmap | `planner` |
|
|
56
|
+
| Playwright, E2E, browser, functional test | `evaluator-functional` |
|
|
57
|
+
| layout, screenshot, a11y, responsive, viewport, AI slop | `evaluator-visual` |
|
|
58
|
+
| code quality, lint, tsc, architecture, type safety | `evaluator-code-quality` |
|
|
59
|
+
3. **매칭 실패 + 여전히 에이전트 국한** → `shared.md`
|
|
60
|
+
4. **프로젝트 전체 철학/원칙** (예: "우리는 TDD 한다", "보안 우선") → 루트 `CONVENTIONS.md` 에 사용자 권고 (Dispatcher 직접 수정 금지)
|
|
61
|
+
|
|
62
|
+
## 3. 중복 감지
|
|
63
|
+
|
|
64
|
+
대상 파일에서 기존 `[C-NNN]` 엔트리를 읽어:
|
|
65
|
+
- **완전 중복** (같은 rule) → append 하지 않고 기존 엔트리의 Date 를 갱신
|
|
66
|
+
- **부분 중복** (관련 주제) → 새 엔트리로 추가하되 기존 엔트리 ID 를 `Related:` 필드로 참조
|
|
67
|
+
|
|
68
|
+
## 4. 엔트리 포맷
|
|
69
|
+
|
|
70
|
+
```markdown
|
|
71
|
+
### [C-NNN] 간결한 제목 (긍정형, 70자 이내)
|
|
72
|
+
- **Date**: YYYY-MM-DD
|
|
73
|
+
- **Scope**: <agent> | shared
|
|
74
|
+
- **Rule**: 사용자가 말한 내용을 긍정 규칙으로 정제. 명령형 문장.
|
|
75
|
+
- **Rationale**: 사용자가 설명한 이유 (없으면 "미지정" 표기, 추정 금지)
|
|
76
|
+
- **Applies to**: 적용 대상 상세 (특정 파일 경로, 특정 상황, 엔드포인트 등)
|
|
77
|
+
- **Added from**: 사용자 프롬프트 (YYYY-MM-DD HH:MM) | migration | manual
|
|
78
|
+
- **Related**: C-XXX, C-YYY (선택)
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### ID 할당
|
|
82
|
+
|
|
83
|
+
대상 파일의 기존 `[C-NNN]` 최댓값 + 1 을 3자리 zero-pad. 예: 기존에 C-001, C-003 이 있으면 다음은 C-004 (비어있는 번호는 재사용하지 않음).
|
|
84
|
+
|
|
85
|
+
## 5. 루트 CONVENTIONS.md 처리
|
|
86
|
+
|
|
87
|
+
루트 `CONVENTIONS.md` 는 **사용자가 자유 기술하는 최상위 원칙 파일**. Dispatcher 가 직접 수정하지 않고, 사용자에게 안내:
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
이 규칙은 프로젝트 전체 철학에 가까워 보여서 CONVENTIONS.md(루트) 에
|
|
91
|
+
직접 추가하시는 게 좋겠습니다. 추가 후 모든 에이전트가 세션 시작 시 읽습니다.
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## 6. 사용자 확인 메시지 포맷
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
Convention 등록 완료:
|
|
98
|
+
- ID: [C-004]
|
|
99
|
+
- Scope: generator-backend
|
|
100
|
+
- Rule: API 응답 필드는 snake_case
|
|
101
|
+
- 저장 위치: .harness/conventions/generator-backend.md
|
|
102
|
+
|
|
103
|
+
Generator-Backend 는 다음 세션 시작 시 이 규칙을 읽고 적용합니다.
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## 7. 금지 사항
|
|
107
|
+
|
|
108
|
+
- Convention 파일의 기존 엔트리 삭제/수정 (사용자만 가능)
|
|
109
|
+
- `CONVENTIONS.md` (루트) 직접 수정
|
|
110
|
+
- 추정성 rationale 작성 (근거 없으면 "미지정")
|
|
111
|
+
- 에이전트 SKILL.md 자체 수정 (구조적 변경은 사용자 권고)
|
|
@@ -63,13 +63,14 @@ disable-model-invocation: true
|
|
|
63
63
|
## Startup
|
|
64
64
|
|
|
65
65
|
1. `AGENTS.md` 읽기 — IA-MAP (레이어 경계)
|
|
66
|
-
2. `CONVENTIONS.md` 읽기 — 프로젝트
|
|
67
|
-
3. `.harness/
|
|
68
|
-
4. `.harness/
|
|
69
|
-
5.
|
|
70
|
-
6. `actions/
|
|
71
|
-
7. `actions/
|
|
72
|
-
8.
|
|
66
|
+
2. `CONVENTIONS.md` (루트) 읽기 — 프로젝트 최상위 원칙 (있을 때만)
|
|
67
|
+
3. `.harness/conventions/shared.md` + `.harness/conventions/evaluator-code-quality.md` — **긍정 하우스 스타일 (C-NNN) — PASS 판정의 기준이 된다**
|
|
68
|
+
4. `.harness/gotchas/evaluator-code-quality.md` 읽기 — **과거 실수 반복 금지**
|
|
69
|
+
5. `.harness/memory.md` 읽기 — **프로젝트 공유 학습 규칙 적용**
|
|
70
|
+
6. `actions/sprint-contract.md` — 이번 스프린트 변경 범위
|
|
71
|
+
7. `actions/feature-list.json` — 기능 정의
|
|
72
|
+
8. `actions/api-contract.json` — DTO 형태 (계약 vs 구현 일치 확인용)
|
|
73
|
+
9. `.harness/progress.json`
|
|
73
74
|
|
|
74
75
|
## Evaluation Steps
|
|
75
76
|
|
|
@@ -63,12 +63,14 @@ disable-model-invocation: true
|
|
|
63
63
|
## Startup
|
|
64
64
|
|
|
65
65
|
1. `AGENTS.md` 읽기 — IA-MAP
|
|
66
|
-
2.
|
|
67
|
-
3. `.harness/
|
|
68
|
-
4.
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
66
|
+
2. `CONVENTIONS.md` (루트) 읽기 — 프로젝트 최상위 원칙 (있을 때만)
|
|
67
|
+
3. `.harness/conventions/shared.md` + `.harness/conventions/evaluator-functional.md` — **긍정 하우스 스타일 적용**
|
|
68
|
+
4. `.harness/gotchas/evaluator-functional.md` 읽기 — **과거 실수 반복 금지**
|
|
69
|
+
5. `.harness/memory.md` 읽기 — **프로젝트 공유 학습 규칙 적용**
|
|
70
|
+
6. `actions/sprint-contract.md` — BE + FE 성공 기준 전체
|
|
71
|
+
7. `actions/feature-list.json` — 이번 스프린트 범위
|
|
72
|
+
8. `actions/api-contract.json` — 기대 API 동작
|
|
73
|
+
9. `.harness/progress.json`
|
|
72
74
|
|
|
73
75
|
## Feature-Level Mode (Team Mode)
|
|
74
76
|
|
|
@@ -56,10 +56,12 @@ disable-model-invocation: true
|
|
|
56
56
|
## Startup
|
|
57
57
|
|
|
58
58
|
1. `AGENTS.md` 읽기
|
|
59
|
-
2.
|
|
60
|
-
3. `.harness/
|
|
61
|
-
4.
|
|
62
|
-
5.
|
|
59
|
+
2. `CONVENTIONS.md` (루트) 읽기 — 프로젝트 최상위 원칙 (있을 때만)
|
|
60
|
+
3. `.harness/conventions/shared.md` + `.harness/conventions/evaluator-visual.md` — **긍정 하우스 스타일 적용**
|
|
61
|
+
4. `.harness/gotchas/evaluator-visual.md` 읽기 — **과거 실수 반복 금지**
|
|
62
|
+
5. `.harness/memory.md` 읽기 — **프로젝트 공유 학습 규칙 적용**
|
|
63
|
+
6. `actions/evaluation-functional.md` — Verdict: PASS 확인
|
|
64
|
+
7. **Stack-Adaptive Gate** (v5.2) — `scan-result.json.tech_stack` 으로 스택 확인 후 `.harness/ref/fe-<stack>.md` 의 `validation.visual` 파싱:
|
|
63
65
|
- `visual.enabled == false`: 즉시 **MANUAL_REQUIRED 모드** 로 전환 — 아래 "Visual Skip Flow" 수행 후 종료
|
|
64
66
|
- `visual.enabled == true` (또는 ref-docs 없이 웹 전통 스택): 계속 진행, ref 에 `visual.base_url` 이 있으면 그 URL 로, 없으면 `ref.runner.dev_command` 로 서버 기동 후 Playwright 접속
|
|
65
67
|
|
|
@@ -27,18 +27,23 @@ disable-model-invocation: true
|
|
|
27
27
|
## Startup (Adaptive Loading)
|
|
28
28
|
|
|
29
29
|
1. `AGENTS.md` 읽기 — IA-MAP, 권한 확인
|
|
30
|
-
2.
|
|
31
|
-
3. **
|
|
30
|
+
2. `CONVENTIONS.md` (루트) 읽기 — 프로젝트 최상위 원칙 (있을 때만)
|
|
31
|
+
3. **Conventions 로드** — 세 파일 모두 (있는 것만):
|
|
32
|
+
- `.harness/conventions/shared.md` (모든 에이전트 공통)
|
|
33
|
+
- `.harness/conventions/generator-backend.md` (BE 스코프)
|
|
34
|
+
- `.harness/conventions/generator-backend-<stack>.md` (스택별, 선택)
|
|
35
|
+
4. `.harness/actions/scan-result.json` 읽기 → `tech_stack.backend` 또는 `tech_stack.language` 로 현재 스택 확정 (이하 `<stack>`)
|
|
36
|
+
5. **Ref-docs 로드** — `.harness/ref/be-<stack>.md`
|
|
32
37
|
- 파일 없음 → STOP + 안내: `"ref-docs 가 없습니다. bash init.sh init 실행 또는 bash scripts/init-ref-docs.sh --claude-prompt --stack <stack> --role be . 실행하세요."`
|
|
33
38
|
- frontmatter 파싱 실패 → 경고 출력 + 기본값으로 degrade
|
|
34
|
-
|
|
39
|
+
6. **Gotchas 로드** — 두 파일 모두 (있는 것만):
|
|
35
40
|
- `.harness/gotchas/generator-backend.md` (공통)
|
|
36
41
|
- `.harness/gotchas/generator-backend-<stack>.md` (스택별)
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
+
7. `.harness/memory.md` 읽기 — 프로젝트 공유 학습 규칙
|
|
43
|
+
8. `pwd` + `.harness/progress.json` + `git log --oneline -20`
|
|
44
|
+
9. `.harness/actions/api-contract.json` 읽기 — **이 계약이 유일한 BE 외부 인터페이스**
|
|
45
|
+
10. `.harness/actions/feature-list.json` — 지정된 `FEATURE_ID` 또는 `layer: "backend"` 필터
|
|
46
|
+
11. **DB / 외부 의존성 부트스트랩**:
|
|
42
47
|
- `ref.runner.install_command` 가 있으면 1회 실행
|
|
43
48
|
- `ref.runner.dev_command` 를 백그라운드 실행 (있는 경우)
|
|
44
49
|
|
|
@@ -27,21 +27,26 @@ disable-model-invocation: true
|
|
|
27
27
|
## Startup (Adaptive Loading)
|
|
28
28
|
|
|
29
29
|
1. `AGENTS.md` 읽기 — IA-MAP, 권한 확인
|
|
30
|
-
2.
|
|
31
|
-
3. **
|
|
30
|
+
2. `CONVENTIONS.md` (루트) 읽기 — 프로젝트 최상위 원칙 (있을 때만)
|
|
31
|
+
3. **Conventions 로드** — 세 파일 모두 (있는 것만):
|
|
32
|
+
- `.harness/conventions/shared.md` (모든 에이전트 공통)
|
|
33
|
+
- `.harness/conventions/generator-frontend.md` (FE 스코프)
|
|
34
|
+
- `.harness/conventions/generator-frontend-<stack>.md` (스택별, 선택)
|
|
35
|
+
4. `.harness/actions/scan-result.json` 읽기 → `tech_stack.fe_stack` 또는 `tech_stack.frontend` 로 현재 스택 확정 (이하 `<stack>`)
|
|
36
|
+
5. **Ref-docs 로드** — `.harness/ref/fe-<stack>.md`
|
|
32
37
|
- 파일 없음 → STOP + 안내: `"ref-docs 가 없습니다. bash init.sh init 실행 또는 bash scripts/init-ref-docs.sh --claude-prompt --stack <stack> --role fe . 실행하세요."`
|
|
33
38
|
- frontmatter 파싱 실패 → 경고 출력 + 기본값(runner/paths/api 모두 null)으로 degrade
|
|
34
|
-
|
|
39
|
+
6. **Gotchas 로드** — 두 파일 모두 (있는 것만):
|
|
35
40
|
- `.harness/gotchas/generator-frontend.md` (공통)
|
|
36
41
|
- `.harness/gotchas/generator-frontend-<stack>.md` (스택별)
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
7. `.harness/memory.md` 읽기 — 프로젝트 공유 학습 규칙
|
|
43
|
+
8. `pwd` + `.harness/progress.json` + `git log --oneline -20`
|
|
44
|
+
9. `.harness/actions/api-contract.json` 읽기
|
|
45
|
+
10. `.harness/actions/feature-list.json` — 지정된 `FEATURE_ID` 또는 `layer: "frontend"` 필터
|
|
46
|
+
11. **개발 서버 기동**:
|
|
47
|
+
- `ref.runner.dev_command` 가 `null` 이 아니면 해당 명령 백그라운드 실행
|
|
48
|
+
- `null` 이면 "개발 서버 기동은 스택 특성상 생략" 로그만 남김
|
|
49
|
+
12. **API Gateway 체크**:
|
|
45
50
|
- `ref.api.base_url` 이 `null` 이 아니면 `curl -s <base_url>/health` 로 헬스체크
|
|
46
51
|
- `null` (네이티브 앱 등) 이면 체크 스킵
|
|
47
52
|
|
package/skills/planner/SKILL.md
CHANGED
|
@@ -24,18 +24,20 @@ disable-model-invocation: true
|
|
|
24
24
|
## Startup
|
|
25
25
|
|
|
26
26
|
1. `AGENTS.md` 읽기
|
|
27
|
-
2.
|
|
28
|
-
3. `.harness/
|
|
29
|
-
4. `.harness/
|
|
30
|
-
5. `.harness/
|
|
31
|
-
6. `.harness/
|
|
32
|
-
7.
|
|
27
|
+
2. `CONVENTIONS.md` (루트) 읽기 — 프로젝트 최상위 원칙 (있을 때만)
|
|
28
|
+
3. `.harness/conventions/shared.md` + `.harness/conventions/planner.md` — **긍정 하우스 스타일 적용 (feature 분할/AC 작성 시)**
|
|
29
|
+
4. `.harness/gotchas/planner.md` 읽기 — **과거 실수 반복 금지**
|
|
30
|
+
5. `.harness/memory.md` 읽기 — **프로젝트 공유 학습 규칙 적용**
|
|
31
|
+
6. `.harness/progress.json` 읽기
|
|
32
|
+
7. `.harness/actions/pipeline.json` 읽기 — `planner_mode`, `fe_stack` 확인
|
|
33
|
+
8. `.harness/actions/scan-result.json` 읽기 — `tech_stack.fe_stack` 확인 (없으면 `react` 기본)
|
|
34
|
+
9. **Brainstorm Spec 우선 로드** — `.harness/actions/brainstorm-spec.md` 가 존재하면
|
|
33
35
|
**이 파일이 PRD 대체 입력**. Brainstormer 가 이미 사용자와 대화하여 확정한
|
|
34
36
|
결과이므로 **승인된 결정을 뒤엎지 않는다**. 없으면 사용자의 원본 요청 텍스트를 입력으로 사용.
|
|
35
37
|
- brainstorm-spec.md 에 `## Open Questions` 섹션이 있으면 Planner 가 해소 (API 계약으로 확정)
|
|
36
38
|
- brainstorm-spec.md 의 `## 7. 주요 컴포넌트 / 엔티티` → `feature-list.json` 초기 feature 목록 시드
|
|
37
39
|
- brainstorm-spec.md 의 `## 5. 선택된 접근법` / `## 6. 아키텍처 스케치` → MSA 서비스 분할 베이스
|
|
38
|
-
|
|
40
|
+
10. **FE Stack 확정** → [FE Stack 결정 가이드](references/fe-stack-detection.md)
|
|
39
41
|
- `pubspec.yaml` + `flutter:` 키 → `fe_stack = "flutter"`
|
|
40
42
|
- 혼재/불명확 → 사용자에게 단 한 번 질문
|
|
41
43
|
- 확정 후 `pipeline.json.fe_stack` 갱신 (없으면 생성)
|