@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.
@@ -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-\d+\]/m; // Gotcha entry heading
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.0",
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
  }