@walwal-harness/cli 1.1.0 → 1.3.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 +24 -2
- package/assets/templates/config.json +27 -0
- package/bin/init.js +146 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -46,15 +46,26 @@ Planner → Generator-BE → Evaluator-Func(API-only)
|
|
|
46
46
|
npm install @walwal-harness/cli
|
|
47
47
|
```
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
`postinstall`이 자동으로 프로젝트 루트에 설치합니다:
|
|
50
50
|
- `.harness/` 디렉토리 스캐폴딩 (actions, archive, gotchas)
|
|
51
51
|
- `.claude/skills/` 에 6개 에이전트 스킬 설치
|
|
52
52
|
- `AGENTS.md` 생성 + `CLAUDE.md` 심볼릭 링크
|
|
53
53
|
- 기존 프로젝트면 구조 스캔 → IA-MAP 자동 생성
|
|
54
54
|
|
|
55
|
+
> **Note:** `npm install` 후 Claude Code 세션을 **재시작**해야 skills가 인식됩니다.
|
|
56
|
+
> Claude Code는 세션 시작 시 `.claude/skills/`를 스캔하므로, 설치 후 `/exit` → 재진입이 필요합니다.
|
|
57
|
+
|
|
58
|
+
### CLI 명령
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
npx walwal-harness # 초기화 (postinstall과 동일)
|
|
62
|
+
npx walwal-harness --force # 강제 재초기화 (기존 파일 덮어쓰기)
|
|
63
|
+
npx walwal-harness --help # 도움말
|
|
64
|
+
```
|
|
65
|
+
|
|
55
66
|
## 사용법
|
|
56
67
|
|
|
57
|
-
Claude Code
|
|
68
|
+
Claude Code를 **재시작**한 뒤:
|
|
58
69
|
|
|
59
70
|
```
|
|
60
71
|
> 하네스 엔지니어링 시작
|
|
@@ -159,6 +170,17 @@ your-project/
|
|
|
159
170
|
| Unit Testing | Jest (BE) + Vitest (FE) |
|
|
160
171
|
| Database | PostgreSQL / SQLite |
|
|
161
172
|
|
|
173
|
+
## 권장 외부 스킬
|
|
174
|
+
|
|
175
|
+
하네스는 자체 reference 파일로 기본 가이드를 제공하지만, 아래 외부 스킬을 설치하면 품질이 향상됩니다. 초기화 시 자동으로 설치 여부를 체크하고 안내합니다.
|
|
176
|
+
|
|
177
|
+
| 스킬 | 설명 | 사용 에이전트 |
|
|
178
|
+
|------|------|-------------|
|
|
179
|
+
| **vercel** | Vercel/Next.js 배포, 성능 최적화, AI SDK | Generator-FE |
|
|
180
|
+
| **web-design-guidelines** | UI/UX 접근성, Web Interface Guidelines | Evaluator-Visual, Generator-FE |
|
|
181
|
+
| **taste-skill** | AI 생성 UI 디자인 품질, AI슬롭 감지 | Evaluator-Visual, Generator-FE |
|
|
182
|
+
| **supanova-design-skill** | 디자인 시스템 규칙, 시각적 일관성 | Evaluator-Visual, Generator-FE |
|
|
183
|
+
|
|
162
184
|
## Playwright MCP 설정
|
|
163
185
|
|
|
164
186
|
Evaluator가 브라우저 테스트를 수행하려면 Playwright MCP가 필요합니다. `~/.mcp.json`에 추가:
|
|
@@ -84,6 +84,33 @@
|
|
|
84
84
|
"gotchas": ".harness/gotchas",
|
|
85
85
|
"progress": ".harness/progress.txt"
|
|
86
86
|
},
|
|
87
|
+
"recommended_skills": {
|
|
88
|
+
"comment": "Claude Code에 설치하면 하네스 품질이 향상되는 외부 스킬 목록",
|
|
89
|
+
"vercel": {
|
|
90
|
+
"description": "Vercel/Next.js 배포, 성능 최적화, AI SDK 가이드",
|
|
91
|
+
"used_by": ["generator-frontend"],
|
|
92
|
+
"install": "claude skill install vercel",
|
|
93
|
+
"check_path": ".claude/skills/vercel"
|
|
94
|
+
},
|
|
95
|
+
"web-design-guidelines": {
|
|
96
|
+
"description": "UI/UX 접근성, 디자인 리뷰, Web Interface Guidelines",
|
|
97
|
+
"used_by": ["evaluator-visual", "generator-frontend"],
|
|
98
|
+
"install": "claude skill install web-design-guidelines",
|
|
99
|
+
"check_path": ".claude/skills/web-design-guidelines"
|
|
100
|
+
},
|
|
101
|
+
"taste-skill": {
|
|
102
|
+
"description": "AI 생성 UI의 디자인 품질 검증, AI슬롭 패턴 감지",
|
|
103
|
+
"used_by": ["evaluator-visual", "generator-frontend"],
|
|
104
|
+
"install": "npx claude-skill install leonxlnx/taste-skill",
|
|
105
|
+
"check_path": ".claude/skills/taste-skill"
|
|
106
|
+
},
|
|
107
|
+
"supanova-design-skill": {
|
|
108
|
+
"description": "디자인 시스템 규칙, 컴포넌트 패턴, 시각적 일관성",
|
|
109
|
+
"used_by": ["evaluator-visual", "generator-frontend"],
|
|
110
|
+
"install": "npx claude-skill install uxjoseph/supanova-design-skill",
|
|
111
|
+
"check_path": ".claude/skills/supanova-design-skill"
|
|
112
|
+
}
|
|
113
|
+
},
|
|
87
114
|
"tech_stack": {
|
|
88
115
|
"backend": {
|
|
89
116
|
"framework": "NestJS (TypeScript)",
|
package/bin/init.js
CHANGED
|
@@ -13,13 +13,43 @@ const fs = require('fs');
|
|
|
13
13
|
const path = require('path');
|
|
14
14
|
const { execSync } = require('child_process');
|
|
15
15
|
|
|
16
|
-
const PROJECT_ROOT = process.cwd();
|
|
17
16
|
const PKG_ROOT = path.resolve(__dirname, '..');
|
|
18
|
-
const HARNESS_DIR = path.join(PROJECT_ROOT, '.harness');
|
|
19
|
-
const CLAUDE_SKILLS_DIR = path.join(PROJECT_ROOT, '.claude', 'skills');
|
|
20
|
-
|
|
21
17
|
const isAuto = process.argv.includes('--auto');
|
|
22
18
|
const isForce = process.argv.includes('--force');
|
|
19
|
+
const isHelp = process.argv.includes('--help') || process.argv.includes('-h');
|
|
20
|
+
|
|
21
|
+
// ─────────────────────────────────────────
|
|
22
|
+
// Resolve project root
|
|
23
|
+
// ─────────────────────────────────────────
|
|
24
|
+
// During `npm install` postinstall, cwd is the dependency's own directory
|
|
25
|
+
// inside node_modules, NOT the consumer project root.
|
|
26
|
+
// We detect this and walk up to find the actual project root.
|
|
27
|
+
function resolveProjectRoot() {
|
|
28
|
+
let cwd = process.cwd();
|
|
29
|
+
|
|
30
|
+
// If we're running inside node_modules, walk up to the project root
|
|
31
|
+
// e.g. /project/node_modules/@walwal-harness/cli → /project
|
|
32
|
+
const nmIndex = cwd.indexOf(path.sep + 'node_modules' + path.sep);
|
|
33
|
+
if (nmIndex !== -1) {
|
|
34
|
+
return cwd.substring(0, nmIndex);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Also handle case where cwd IS a node_modules child (no trailing sep match)
|
|
38
|
+
if (cwd.includes(`${path.sep}node_modules`)) {
|
|
39
|
+
const parts = cwd.split(path.sep);
|
|
40
|
+
const nmIdx = parts.indexOf('node_modules');
|
|
41
|
+
if (nmIdx > 0) {
|
|
42
|
+
return parts.slice(0, nmIdx).join(path.sep);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// npx or direct invocation — cwd is the project root
|
|
47
|
+
return cwd;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const PROJECT_ROOT = resolveProjectRoot();
|
|
51
|
+
const HARNESS_DIR = path.join(PROJECT_ROOT, '.harness');
|
|
52
|
+
const CLAUDE_SKILLS_DIR = path.join(PROJECT_ROOT, '.claude', 'skills');
|
|
23
53
|
|
|
24
54
|
// ─────────────────────────────────────────
|
|
25
55
|
// Utility
|
|
@@ -253,28 +283,137 @@ function checkPlaywrightMcp() {
|
|
|
253
283
|
log('');
|
|
254
284
|
}
|
|
255
285
|
|
|
286
|
+
// ─────────────────────────────────────────
|
|
287
|
+
// 6. Recommended skills check
|
|
288
|
+
// ─────────────────────────────────────────
|
|
289
|
+
function checkRecommendedSkills() {
|
|
290
|
+
const configPath = path.join(HARNESS_DIR, 'config.json');
|
|
291
|
+
if (!fileExists(configPath)) return;
|
|
292
|
+
|
|
293
|
+
let config;
|
|
294
|
+
try {
|
|
295
|
+
config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
296
|
+
} catch (e) { return; }
|
|
297
|
+
|
|
298
|
+
const skills = config.recommended_skills;
|
|
299
|
+
if (!skills) return;
|
|
300
|
+
|
|
301
|
+
const missing = [];
|
|
302
|
+
const found = [];
|
|
303
|
+
|
|
304
|
+
for (const [name, info] of Object.entries(skills)) {
|
|
305
|
+
if (name === 'comment') continue;
|
|
306
|
+
const checkPath = path.join(PROJECT_ROOT, info.check_path);
|
|
307
|
+
if (fileExists(checkPath)) {
|
|
308
|
+
found.push(name);
|
|
309
|
+
} else {
|
|
310
|
+
missing.push({ name, ...info });
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
if (found.length > 0) {
|
|
315
|
+
log(`Recommended skills installed: ${found.join(', ')}`);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
if (missing.length > 0) {
|
|
319
|
+
console.log('');
|
|
320
|
+
log('╔═══════════════════════════════════════════════════════════╗');
|
|
321
|
+
log('║ Recommended skills (not installed) ║');
|
|
322
|
+
log('╠═══════════════════════════════════════════════════════════╣');
|
|
323
|
+
for (const skill of missing) {
|
|
324
|
+
const agents = skill.used_by.join(', ');
|
|
325
|
+
log(`║ ${skill.name}`);
|
|
326
|
+
log(`║ ${skill.description}`);
|
|
327
|
+
log(`║ Used by: ${agents}`);
|
|
328
|
+
log(`║ Install: ${skill.install}`);
|
|
329
|
+
log('║');
|
|
330
|
+
}
|
|
331
|
+
log('║ These skills are optional but improve output quality. ║');
|
|
332
|
+
log('║ Harness reference files provide baseline guidance ║');
|
|
333
|
+
log('║ even without these skills installed. ║');
|
|
334
|
+
log('╚═══════════════════════════════════════════════════════════╝');
|
|
335
|
+
} else if (found.length > 0) {
|
|
336
|
+
log('All recommended skills are installed!');
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// ─────────────────────────────────────────
|
|
341
|
+
// Help
|
|
342
|
+
// ─────────────────────────────────────────
|
|
343
|
+
function showHelp() {
|
|
344
|
+
const pkg = require(path.join(PKG_ROOT, 'package.json'));
|
|
345
|
+
console.log(`
|
|
346
|
+
╔══════════════════════════════════════╗
|
|
347
|
+
║ walwal-harness v${pkg.version.padEnd(16)}║
|
|
348
|
+
║ AI Agent Harness Engineering ║
|
|
349
|
+
╚══════════════════════════════════════╝
|
|
350
|
+
|
|
351
|
+
Usage:
|
|
352
|
+
npx walwal-harness Initialize project for harness engineering
|
|
353
|
+
npx walwal-harness --force Re-initialize (overwrites existing files)
|
|
354
|
+
npx walwal-harness --help Show this help
|
|
355
|
+
|
|
356
|
+
What it does:
|
|
357
|
+
1. Scaffolds .harness/ directory (actions, archive, gotchas, config)
|
|
358
|
+
2. Installs skills to .claude/skills/ (dispatcher, planner, generators, evaluators)
|
|
359
|
+
3. Copies helper scripts to scripts/
|
|
360
|
+
4. Creates AGENTS.md + CLAUDE.md symlink
|
|
361
|
+
5. Checks Playwright MCP configuration
|
|
362
|
+
6. Checks recommended external skills (Vercel, design skills)
|
|
363
|
+
|
|
364
|
+
After init:
|
|
365
|
+
1. Restart Claude Code session (exit and re-enter) for skills to load
|
|
366
|
+
2. Say "하네스 엔지니어링 시작" or invoke /harness-dispatcher
|
|
367
|
+
`);
|
|
368
|
+
}
|
|
369
|
+
|
|
256
370
|
// ─────────────────────────────────────────
|
|
257
371
|
// Main
|
|
258
372
|
// ─────────────────────────────────────────
|
|
259
373
|
function main() {
|
|
374
|
+
if (isHelp) {
|
|
375
|
+
showHelp();
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
const pkg = require(path.join(PKG_ROOT, 'package.json'));
|
|
260
380
|
console.log('');
|
|
261
381
|
console.log('╔══════════════════════════════════════╗');
|
|
262
|
-
console.log(
|
|
382
|
+
console.log(`║ walwal-harness v${pkg.version.padEnd(16)}║`);
|
|
263
383
|
console.log('║ AI Agent Harness Engineering ║');
|
|
264
384
|
console.log('╚══════════════════════════════════════╝');
|
|
265
385
|
console.log('');
|
|
266
386
|
|
|
387
|
+
log(`Project root: ${PROJECT_ROOT}`);
|
|
388
|
+
console.log('');
|
|
389
|
+
|
|
267
390
|
scaffoldHarness();
|
|
268
391
|
installSkills();
|
|
269
392
|
installScripts();
|
|
270
393
|
setupAgentsMd();
|
|
271
394
|
checkPlaywrightMcp();
|
|
395
|
+
checkRecommendedSkills();
|
|
272
396
|
|
|
273
397
|
console.log('');
|
|
274
398
|
log('═══ Initialization Complete ═══');
|
|
275
399
|
log('');
|
|
276
|
-
|
|
277
|
-
|
|
400
|
+
|
|
401
|
+
if (isAuto) {
|
|
402
|
+
// postinstall context — Claude Code is likely already running
|
|
403
|
+
log('╔═══════════════════════════════════════════════════════════╗');
|
|
404
|
+
log('║ IMPORTANT: Restart Claude Code for skills to activate! ║');
|
|
405
|
+
log('║ ║');
|
|
406
|
+
log('║ Claude Code discovers skills at session startup. ║');
|
|
407
|
+
log('║ Type /exit, then re-enter this directory to begin. ║');
|
|
408
|
+
log('║ ║');
|
|
409
|
+
log('║ Then say: "하네스 엔지니어링 시작" ║');
|
|
410
|
+
log('║ Or invoke: /harness-dispatcher ║');
|
|
411
|
+
log('╚═══════════════════════════════════════════════════════════╝');
|
|
412
|
+
} else {
|
|
413
|
+
log('Next steps:');
|
|
414
|
+
log(' 1. Restart Claude Code session (/exit → re-enter directory)');
|
|
415
|
+
log(' 2. Say "하네스 엔지니어링 시작" or invoke /harness-dispatcher');
|
|
416
|
+
}
|
|
278
417
|
console.log('');
|
|
279
418
|
}
|
|
280
419
|
|
package/package.json
CHANGED