@walwal-harness/cli 7.1.0 → 7.1.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/HR-Resource/ceo/SKILL.md +11 -0
- package/bin/init.js +85 -1
- package/init.sh +208 -0
- package/package.json +2 -1
package/HR-Resource/ceo/SKILL.md
CHANGED
|
@@ -34,3 +34,14 @@ You are the only direct conversation channel with the Owner.
|
|
|
34
34
|
- Before CTO/CDO/OPS allocate runnable services, agree with the Owner on a `{xx}000` base port and write it to project `.env` as `HARNESS_BASE_PORT={xx}000`. Mentioning the value in `ceo.md` is not sufficient.
|
|
35
35
|
- After writing `.env`, verify with `grep '^HARNESS_BASE_PORT=' .env` before routing service work.
|
|
36
36
|
- For service monitoring, collect the Owner's server mapping first: local PC, Docker, VM, AWS/cloud, host, port, health path, log path, and contact/source.
|
|
37
|
+
|
|
38
|
+
## Worktree Isolation Failure — No git Repo
|
|
39
|
+
|
|
40
|
+
When an Agent spawn fails with a worktree or git error (e.g., "Cannot create agent worktree: not in a git repository"), this is an **isolation constraint**, not a hiring failure. The correct response is:
|
|
41
|
+
|
|
42
|
+
1. **Do not skip `harness-hiring`.** Run `harness-hiring` as normal to register the worker in `hr-roster.json`.
|
|
43
|
+
2. **Do not replace hired workers with inline "You are a…" prompts.** That is impersonation, not hiring.
|
|
44
|
+
3. **Spawn the hired worker as a plain `Agent()` call** (no `subagent_type`, no worktree) and set the prompt to read the worker's SKILL.md from `.harness/shared/HR-Resource/{worker-name}/SKILL.md` before executing the task.
|
|
45
|
+
4. **Report the isolation constraint to the Owner** in the final summary: state that worktree isolation was unavailable and workers ran without isolation.
|
|
46
|
+
|
|
47
|
+
The worktree error only affects **isolation**. `harness-hiring`, `harness-resource-manager`, and `hr-roster.json` registration are independent of git and must always run.
|
package/bin/init.js
CHANGED
|
@@ -165,6 +165,26 @@ const V7_REMOVED_GOTCHA_FILES = [
|
|
|
165
165
|
'generator-backend-laravel.md',
|
|
166
166
|
];
|
|
167
167
|
|
|
168
|
+
const LEGACY_V7_FILE_OWNER = {
|
|
169
|
+
'conductor.md': 'ceo.md',
|
|
170
|
+
'dispatcher.md': 'ceo.md',
|
|
171
|
+
'coo-developer.md': 'coo.md',
|
|
172
|
+
'planner.md': 'coo.md',
|
|
173
|
+
'documentationer.md': 'coo.md',
|
|
174
|
+
'generator-frontend.md': 'cto.md',
|
|
175
|
+
'generator-backend.md': 'cto.md',
|
|
176
|
+
'generator-backend-laravel.md': 'cto.md',
|
|
177
|
+
'generator-devops.md': 'cto.md',
|
|
178
|
+
'evaluator-architecture.md': 'cto.md',
|
|
179
|
+
'generator-designer.md': 'cdo.md',
|
|
180
|
+
'evaluator-visual.md': 'cdo.md',
|
|
181
|
+
'evaluator-functional.md': 'cqo.md',
|
|
182
|
+
'evaluator-code-quality.md': 'cqo.md',
|
|
183
|
+
'evaluator-security.md': 'cqo.md',
|
|
184
|
+
'meeting-manager.md': 'ops.md',
|
|
185
|
+
'service-ops.md': 'ops.md',
|
|
186
|
+
};
|
|
187
|
+
|
|
168
188
|
function removeLegacyV7Files(dir, files) {
|
|
169
189
|
for (const file of files) {
|
|
170
190
|
const target = path.join(dir, file);
|
|
@@ -174,6 +194,65 @@ function removeLegacyV7Files(dir, files) {
|
|
|
174
194
|
}
|
|
175
195
|
}
|
|
176
196
|
|
|
197
|
+
function extractMarkdownEntryBlocks(md, prefix) {
|
|
198
|
+
const escapedPrefix = prefix.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
199
|
+
const re = new RegExp(
|
|
200
|
+
`^#{2,3}\\s+\\[(${escapedPrefix}-[A-Z0-9_-]+)\\][\\s\\S]*?(?=^#{2,3}\\s+\\[${escapedPrefix}-|$(?![\\s\\S]))`,
|
|
201
|
+
'gm',
|
|
202
|
+
);
|
|
203
|
+
const blocks = [];
|
|
204
|
+
let match;
|
|
205
|
+
while ((match = re.exec(md)) !== null) {
|
|
206
|
+
blocks.push({ id: match[1], body: match[0].trimEnd() });
|
|
207
|
+
}
|
|
208
|
+
return blocks;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function migrateLegacyRoleEntries(dir, files, kind) {
|
|
212
|
+
const prefix = kind === 'gotchas' ? 'G' : 'C';
|
|
213
|
+
const migrated = [];
|
|
214
|
+
const backupDir = path.join(HARNESS_DIR, 'archive', 'pre-v7-role-migration');
|
|
215
|
+
for (const file of files) {
|
|
216
|
+
const sourcePath = path.join(dir, file);
|
|
217
|
+
if (!fs.existsSync(sourcePath)) continue;
|
|
218
|
+
|
|
219
|
+
const content = fs.readFileSync(sourcePath, 'utf8');
|
|
220
|
+
const blocks = extractMarkdownEntryBlocks(content, prefix);
|
|
221
|
+
if (!blocks.length) continue;
|
|
222
|
+
|
|
223
|
+
const targetFile = LEGACY_V7_FILE_OWNER[file] || 'shared.md';
|
|
224
|
+
const targetPath = path.join(dir, targetFile);
|
|
225
|
+
const targetOriginal = fs.existsSync(targetPath) ? fs.readFileSync(targetPath, 'utf8') : '';
|
|
226
|
+
const targetIds = new Set(
|
|
227
|
+
kind === 'gotchas'
|
|
228
|
+
? extractGotchaEntryIds(targetOriginal)
|
|
229
|
+
: extractConventionEntryIds(targetOriginal),
|
|
230
|
+
);
|
|
231
|
+
const appendBlocks = [];
|
|
232
|
+
for (const block of blocks) {
|
|
233
|
+
if (targetIds.has(block.id)) continue;
|
|
234
|
+
appendBlocks.push([
|
|
235
|
+
block.body,
|
|
236
|
+
'',
|
|
237
|
+
`- **Migration source**: ${kind}/${file}`,
|
|
238
|
+
].join('\n'));
|
|
239
|
+
targetIds.add(block.id);
|
|
240
|
+
}
|
|
241
|
+
if (!appendBlocks.length) continue;
|
|
242
|
+
|
|
243
|
+
ensureDir(backupDir);
|
|
244
|
+
fs.writeFileSync(path.join(backupDir, `${kind}-${file}`), content);
|
|
245
|
+
const sep = targetOriginal.trim() ? '\n\n' : '';
|
|
246
|
+
fs.writeFileSync(targetPath, targetOriginal.replace(/\s*$/, '') + sep + appendBlocks.join('\n\n') + '\n');
|
|
247
|
+
migrated.push(`${file} → ${targetFile} (${appendBlocks.length})`);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
if (migrated.length) {
|
|
251
|
+
log(`${kind} legacy role entries migrated: ${migrated.join(', ')}`);
|
|
252
|
+
log(`Legacy role migration backup: ${backupDir}`);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
177
256
|
// ─────────────────────────────────────────
|
|
178
257
|
// First-install migration — extract Convention/Gotcha-shaped sections from
|
|
179
258
|
// existing CLAUDE.md / AGENTS.md into .harness/conventions and .harness/gotchas.
|
|
@@ -337,6 +416,9 @@ function scaffoldHarness() {
|
|
|
337
416
|
ensureDir(path.join(HARNESS_DIR, 'memories'));
|
|
338
417
|
ensureDir(path.join(HARNESS_DIR, 'shared'));
|
|
339
418
|
|
|
419
|
+
migrateLegacyRoleEntries(path.join(HARNESS_DIR, 'conventions'), V7_REMOVED_CONVENTION_FILES, 'conventions');
|
|
420
|
+
migrateLegacyRoleEntries(path.join(HARNESS_DIR, 'gotchas'), V7_REMOVED_GOTCHA_FILES, 'gotchas');
|
|
421
|
+
|
|
340
422
|
removeLegacyV7Files(path.join(HARNESS_DIR, 'conventions'), V7_REMOVED_CONVENTION_FILES);
|
|
341
423
|
removeLegacyV7Files(path.join(HARNESS_DIR, 'gotchas'), V7_REMOVED_GOTCHA_FILES);
|
|
342
424
|
|
|
@@ -362,7 +444,7 @@ function scaffoldHarness() {
|
|
|
362
444
|
// a file that has such entries, or user learning history is lost.
|
|
363
445
|
const gotchasSrc = path.join(PKG_ROOT, 'gotchas');
|
|
364
446
|
if (fs.existsSync(gotchasSrc)) {
|
|
365
|
-
const ENTRY_PATTERN = /^### \[G
|
|
447
|
+
const ENTRY_PATTERN = /^### \[G-[A-Z0-9_-]+\]/m; // Gotcha entry heading
|
|
366
448
|
const CUSTOM_MARKER = '## Custom Gotchas'; // Legacy marker still supported
|
|
367
449
|
const files = fs.readdirSync(gotchasSrc);
|
|
368
450
|
for (const file of files) {
|
|
@@ -1291,11 +1373,13 @@ function runMigrate(opts = {}) {
|
|
|
1291
1373
|
const dryRun = opts.dryRun || false;
|
|
1292
1374
|
const flags = detectMigrationNeeded();
|
|
1293
1375
|
const gotchaMissingTotal = Object.values(flags.gotchaMissingEntries || {}).reduce((n, a) => n + a.length, 0);
|
|
1376
|
+
const conventionMissingTotal = Object.values(flags.conventionMissingEntries || {}).reduce((n, a) => n + a.length, 0);
|
|
1294
1377
|
if (
|
|
1295
1378
|
!flags.progressV3toV4 &&
|
|
1296
1379
|
!flags.configMissingCompanyMode &&
|
|
1297
1380
|
(!flags.memoryMissingSystemEntries || flags.memoryMissingSystemEntries.length === 0) &&
|
|
1298
1381
|
gotchaMissingTotal === 0 &&
|
|
1382
|
+
conventionMissingTotal === 0 &&
|
|
1299
1383
|
!flags.bundleVersionStale
|
|
1300
1384
|
) {
|
|
1301
1385
|
console.log('');
|
package/init.sh
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Harness Init Script — 적응형(adaptive) 하네스 진입점 허브
|
|
3
|
+
#
|
|
4
|
+
# 서브커맨드:
|
|
5
|
+
# help — 사용법 출력
|
|
6
|
+
# check (default · 무인자) — 환경 확인 + scan + ref 상태 출력 (기존 동작)
|
|
7
|
+
# init [project_root] — 신규 프로젝트 초기화 (scan → ref placeholder → AGENTS.md → migration preview)
|
|
8
|
+
# migrate [project_root] — v7.1 마이그레이션 적용
|
|
9
|
+
# refresh-ref <role> <stack> — 기존 ref 파일 archive 백업 후 재생성 유도
|
|
10
|
+
#
|
|
11
|
+
# 스택별 서버 기동은 scan-result.json.tech_stack 과 .harness/ref/<role>-<stack>.md.runner 에서
|
|
12
|
+
# 가져온다. NestJS/React 하드코딩은 제거되었다.
|
|
13
|
+
|
|
14
|
+
set -e
|
|
15
|
+
|
|
16
|
+
SUBCOMMAND="${1:-check}"
|
|
17
|
+
shift || true
|
|
18
|
+
|
|
19
|
+
usage() {
|
|
20
|
+
cat <<'USAGE'
|
|
21
|
+
Harness Init — 적응형 하네스 진입점
|
|
22
|
+
|
|
23
|
+
Usage:
|
|
24
|
+
bash init.sh help — 이 메시지
|
|
25
|
+
bash init.sh check [project_root] — 환경 확인 + scan + ref 상태 (기본)
|
|
26
|
+
bash init.sh init [project_root] — scan + ref placeholder 생성 + AGENTS.md + migration dry-run
|
|
27
|
+
bash init.sh init [project_root] --apply-migration
|
|
28
|
+
— init 후 v7.1 마이그레이션까지 적용
|
|
29
|
+
bash init.sh migrate [project_root] — v7.1 마이그레이션 적용
|
|
30
|
+
bash init.sh migrate [project_root] --dry-run
|
|
31
|
+
— v7.1 마이그레이션 미리보기
|
|
32
|
+
bash init.sh refresh-ref <role> <stack> [path] — 기존 ref 파일 백업 후 재생성 안내
|
|
33
|
+
|
|
34
|
+
스택 독립: 개발 서버 기동 명령은 .harness/ref/<role>-<stack>.md 의 runner.dev_command 에서 조회한다.
|
|
35
|
+
USAGE
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
run_walwal_cli() {
|
|
39
|
+
local project_root="$1"
|
|
40
|
+
shift || true
|
|
41
|
+
local script_dir
|
|
42
|
+
script_dir="$(cd "$(dirname "$0")" && pwd)"
|
|
43
|
+
local cli_path="${script_dir}/bin/init.js"
|
|
44
|
+
|
|
45
|
+
if [ -f "$cli_path" ]; then
|
|
46
|
+
(cd "$project_root" && node "$cli_path" "$@")
|
|
47
|
+
else
|
|
48
|
+
(cd "$project_root" && npx walwal-harness "$@")
|
|
49
|
+
fi
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
run_migrate() {
|
|
53
|
+
local project_root="."
|
|
54
|
+
local mode=""
|
|
55
|
+
|
|
56
|
+
if [ "${1:-}" = "--dry-run" ]; then
|
|
57
|
+
mode="--dry-run"
|
|
58
|
+
else
|
|
59
|
+
project_root="${1:-.}"
|
|
60
|
+
mode="${2:-}"
|
|
61
|
+
fi
|
|
62
|
+
|
|
63
|
+
echo "=== Harness Migration: ${project_root} ==="
|
|
64
|
+
if [ "$mode" = "--dry-run" ]; then
|
|
65
|
+
run_walwal_cli "$project_root" migrate --dry-run
|
|
66
|
+
else
|
|
67
|
+
run_walwal_cli "$project_root" migrate
|
|
68
|
+
fi
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
run_check() {
|
|
72
|
+
local project_root="${1:-.}"
|
|
73
|
+
echo "=== Harness Environment Check ==="
|
|
74
|
+
echo "Working Directory: $(pwd)"
|
|
75
|
+
echo "Date: $(date)"
|
|
76
|
+
echo ""
|
|
77
|
+
|
|
78
|
+
# Git 상태
|
|
79
|
+
echo "--- Git Status ---"
|
|
80
|
+
if [ -d "${project_root}/.git" ]; then
|
|
81
|
+
git -C "$project_root" log --oneline -5 2>/dev/null || echo "No commits yet"
|
|
82
|
+
echo ""
|
|
83
|
+
git -C "$project_root" status --short
|
|
84
|
+
else
|
|
85
|
+
echo "Not a git repository — run: git init"
|
|
86
|
+
fi
|
|
87
|
+
echo ""
|
|
88
|
+
|
|
89
|
+
# Harness 상태
|
|
90
|
+
echo "--- Harness Status ---"
|
|
91
|
+
if [ -f "${project_root}/.harness/progress.json" ]; then
|
|
92
|
+
if [ -f "${project_root}/scripts/lib/harness-render-progress.sh" ]; then
|
|
93
|
+
(cd "$project_root" && source scripts/lib/harness-render-progress.sh && render_progress "." 2>/dev/null) || cat "${project_root}/.harness/progress.json"
|
|
94
|
+
else
|
|
95
|
+
cat "${project_root}/.harness/progress.json"
|
|
96
|
+
fi
|
|
97
|
+
else
|
|
98
|
+
echo "progress.json not found — run: bash init.sh init"
|
|
99
|
+
fi
|
|
100
|
+
echo ""
|
|
101
|
+
|
|
102
|
+
# scan 상태
|
|
103
|
+
echo "--- Scan Result ---"
|
|
104
|
+
if [ -f "${project_root}/.harness/actions/scan-result.json" ]; then
|
|
105
|
+
jq '{tech_stack, tech_stack_confidence, recommendation}' "${project_root}/.harness/actions/scan-result.json"
|
|
106
|
+
else
|
|
107
|
+
echo "scan-result.json not found — run: bash scripts/scan-project.sh ${project_root}"
|
|
108
|
+
fi
|
|
109
|
+
echo ""
|
|
110
|
+
|
|
111
|
+
# ref 상태
|
|
112
|
+
echo "--- Ref Docs ---"
|
|
113
|
+
if [ -d "${project_root}/.harness/ref" ]; then
|
|
114
|
+
ls -1 "${project_root}/.harness/ref/" 2>/dev/null | grep -v '^\.generated' | sed 's/^/ /' || echo " (none)"
|
|
115
|
+
else
|
|
116
|
+
echo " .harness/ref/ not present — run: bash init.sh init"
|
|
117
|
+
fi
|
|
118
|
+
echo ""
|
|
119
|
+
|
|
120
|
+
echo "=== Harness Ready ==="
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
run_init() {
|
|
124
|
+
local project_root="."
|
|
125
|
+
local migration_mode="--dry-run"
|
|
126
|
+
if [ "${1:-}" = "--apply-migration" ]; then
|
|
127
|
+
migration_mode="--apply-migration"
|
|
128
|
+
else
|
|
129
|
+
project_root="${1:-.}"
|
|
130
|
+
if [ "${2:-}" = "--apply-migration" ]; then
|
|
131
|
+
migration_mode="--apply-migration"
|
|
132
|
+
fi
|
|
133
|
+
fi
|
|
134
|
+
echo "=== Harness Init: ${project_root} ==="
|
|
135
|
+
|
|
136
|
+
# 1. scan
|
|
137
|
+
echo "[1/4] scan-project.sh"
|
|
138
|
+
bash "$(dirname "$0")/scripts/scan-project.sh" "$project_root"
|
|
139
|
+
echo ""
|
|
140
|
+
|
|
141
|
+
# 2. ref placeholder 생성 (감지된 스택 전체)
|
|
142
|
+
echo "[2/4] init-ref-docs.sh (placeholder)"
|
|
143
|
+
bash "$(dirname "$0")/scripts/init-ref-docs.sh" --yes --placeholder "$project_root"
|
|
144
|
+
echo ""
|
|
145
|
+
|
|
146
|
+
# 3. AGENTS.md 동적 생성
|
|
147
|
+
if [ -f "$(dirname "$0")/scripts/init-agents-md.sh" ]; then
|
|
148
|
+
echo "[3/4] init-agents-md.sh"
|
|
149
|
+
bash "$(dirname "$0")/scripts/init-agents-md.sh" "$project_root"
|
|
150
|
+
else
|
|
151
|
+
echo "[3/4] init-agents-md.sh — skip (not found)"
|
|
152
|
+
fi
|
|
153
|
+
echo ""
|
|
154
|
+
|
|
155
|
+
# 4. v7.1 migration preview/apply
|
|
156
|
+
if [ "$migration_mode" = "--apply-migration" ]; then
|
|
157
|
+
echo "[4/4] walwal-harness migrate"
|
|
158
|
+
run_walwal_cli "$project_root" migrate
|
|
159
|
+
else
|
|
160
|
+
echo "[4/4] walwal-harness migrate --dry-run"
|
|
161
|
+
run_walwal_cli "$project_root" migrate --dry-run
|
|
162
|
+
fi
|
|
163
|
+
echo ""
|
|
164
|
+
|
|
165
|
+
echo "=== Init Complete ==="
|
|
166
|
+
echo "다음 단계:"
|
|
167
|
+
echo " 1. 각 .harness/ref/<role>-<stack>.md 를 실제 컨텐츠로 채우려면:"
|
|
168
|
+
echo " bash init.sh refresh-ref <role> <stack> $project_root"
|
|
169
|
+
echo " 2. 그 결과 프롬프트를 Claude 세션에 붙여 WebSearch + WebFetch 로 ref 작성"
|
|
170
|
+
if [ "$migration_mode" != "--apply-migration" ]; then
|
|
171
|
+
echo " 3. 마이그레이션 미리보기가 적절하면 적용:"
|
|
172
|
+
echo " bash init.sh migrate $project_root"
|
|
173
|
+
fi
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
run_refresh_ref() {
|
|
177
|
+
local role="$1" stack="$2" project_root="${3:-.}"
|
|
178
|
+
if [ -z "$role" ] || [ -z "$stack" ]; then
|
|
179
|
+
echo "ERROR: bash init.sh refresh-ref <role> <stack> [project_root]" >&2
|
|
180
|
+
exit 1
|
|
181
|
+
fi
|
|
182
|
+
|
|
183
|
+
local ref_path="${project_root}/.harness/ref/${role}-${stack}.md"
|
|
184
|
+
local archive_dir="${project_root}/.harness/archive"
|
|
185
|
+
mkdir -p "$archive_dir"
|
|
186
|
+
|
|
187
|
+
if [ -f "$ref_path" ]; then
|
|
188
|
+
local backup="${archive_dir}/ref-${role}-${stack}-$(date +%Y%m%d%H%M%S).md"
|
|
189
|
+
mv "$ref_path" "$backup"
|
|
190
|
+
echo "archive: $backup"
|
|
191
|
+
fi
|
|
192
|
+
|
|
193
|
+
echo ""
|
|
194
|
+
bash "$(dirname "$0")/scripts/init-ref-docs.sh" --claude-prompt --stack "$stack" --role "$role" "$project_root"
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
case "$SUBCOMMAND" in
|
|
198
|
+
help|-h|--help) usage ;;
|
|
199
|
+
check) run_check "$@" ;;
|
|
200
|
+
init) run_init "$@" ;;
|
|
201
|
+
migrate) run_migrate "$@" ;;
|
|
202
|
+
refresh-ref) run_refresh_ref "$@" ;;
|
|
203
|
+
*)
|
|
204
|
+
echo "ERROR: unknown subcommand '$SUBCOMMAND'" >&2
|
|
205
|
+
usage
|
|
206
|
+
exit 1
|
|
207
|
+
;;
|
|
208
|
+
esac
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@walwal-harness/cli",
|
|
3
|
-
"version": "7.1.
|
|
3
|
+
"version": "7.1.1",
|
|
4
4
|
"description": "Company-style AI agent harness for Claude and Codex. Installs commands, CXX agents, skills, HR-Resource hiring pool, and project-local .harness runtime state.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"walwal-harness": "bin/init.js"
|
|
@@ -48,6 +48,7 @@
|
|
|
48
48
|
"assets/",
|
|
49
49
|
"gotchas/",
|
|
50
50
|
"conventions/",
|
|
51
|
+
"init.sh",
|
|
51
52
|
"CHANGELOG.md"
|
|
52
53
|
]
|
|
53
54
|
}
|