@walwal-harness/cli 7.1.51 → 7.1.52

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/CHANGELOG.md CHANGED
@@ -31,6 +31,12 @@ docmeta:
31
31
 
32
32
  ## Unreleased
33
33
 
34
+ ## 7.1.52 — Single shared dashboard install (drop per-project `node_modules`) (2026-06-23)
35
+ - `scripts/harness-dashboard-up.sh` now installs the Brick Office dashboard **once per version** under `~/.walwal-harness/dashboard/<version>/` (override base with `WALWAL_HARNESS_HOME`) and serves every project from that one copy. Previously each project got its own copy plus a full `npm install`, duplicating identical `node_modules` (~597MB) + `.next` (~75MB) per project — 100 projects cost ~64GB of identical bytes. Now total cost is ~640MB regardless of project count.
36
+ - `npm install` + `next build` run only on the first launch of a given version (guarded by a `.walwal-dashboard-ready` marker and an atomic `mkdir` build lock so concurrent launches don't race). Each project is then served with its own `HARNESS_ROOT` + `--port` via `next start`; since `next start` reads `.next` read-only, many dashboards run concurrently from the same install safely.
37
+ - This is correct because the dashboard code is byte-identical across projects and `HARNESS_ROOT` is resolved at request time (`lib/harness-root.ts`; all routes are `force-dynamic`), so build output never bakes in a project. Supersedes both the pre-7.1.37 per-project-hashed home cache and the 7.1.37 project-local `.harness/dashboard/` copy.
38
+ - `bin/init.js` now writes hook/statusLine commands in `settings.json` as project-root-relative paths (`bash scripts/...`) instead of absolute `PROJECT_ROOT` paths, so moving or renaming a project no longer breaks the harness Stop hook with `No such file` errors.
39
+
34
40
  ## 7.1.48 — Auto-managed `.harness` `.gitignore` + runtime untrack on init (2026-06-05)
35
41
  - `walwal-harness init` now writes/refreshes a marked managed block in the project-root `.gitignore` so machine-specific harness runtime/local state is never committed and never pollutes teammates' diffs. Ignored: live conductor `progress.json`/`progress.log`/`handoff.json`/`events.jsonl`, the `todos/` live queue (`state.json` + `events.jsonl`), `actions/`, `ops/`, `activity/`, `logs/`, `archive/` migration backups, and the `dashboard/` visualization tooling. Durable team knowledge stays tracked: `conventions/`, `gotchas/`, `documents/`, `memories/`, `memory.md`, `shared/`, `config.json`, `HARNESS.md`, `.bundle-version`.
36
42
  - Init also best-effort `git rm --cached`s any of those runtime paths committed before the ignore block existed (files stay on disk), fixing the "`.gitignore` added but already-tracked files keep showing as changes" case.
package/README.md CHANGED
@@ -164,7 +164,7 @@ Run on a different port:
164
164
  bash scripts/harness-dashboard-up.sh --port 3002
165
165
  ```
166
166
 
167
- Force-refresh the project-local dashboard runtime when an older dashboard is still shown:
167
+ Force-rebuild the shared dashboard install when an older dashboard is still shown:
168
168
 
169
169
  ```bash
170
170
  bash scripts/harness-dashboard-up.sh --port 3002 --reinstall
@@ -175,9 +175,9 @@ Dashboard options:
175
175
  | Option | Role |
176
176
  |---|---|
177
177
  | `--port <port>` | Run Brick Office on a custom port, e.g. `3002` |
178
- | `--reinstall` | Delete and rebuild the project-local dashboard runtime under `.harness/dashboard/` |
178
+ | `--reinstall` | Discard and rebuild the shared dashboard install for this version |
179
179
 
180
- The dashboard app copy lives under `.harness/dashboard/` for the current project. Generated dependencies and build output are ignored via `.gitignore`; the runtime files the dashboard reads remain the normal `.harness/` files.
180
+ The dashboard code is identical across all projects, so it is installed **once per version** under `~/.walwal-harness/dashboard/<version>/` and shared by every project — a single `node_modules` plus one production build (`npm install` + `next build`) the first time, then reused. Each project is served with its own `HARNESS_ROOT` and port via `next start`, so you can run many dashboards at once without duplicating dependencies (100 projects still cost ~640MB, not 100×). Override the base directory with `WALWAL_HARNESS_HOME`.
181
181
 
182
182
  ### Company Auto-Chain State
183
183
 
package/bin/init.js CHANGED
@@ -190,7 +190,11 @@ function shellQuote(value) {
190
190
  }
191
191
 
192
192
  function harnessScriptCommand(scriptName) {
193
- return `bash ${shellQuote(path.join(PROJECT_ROOT, 'scripts', scriptName))}`;
193
+ // Relative path (resolved from the project root — where Claude Code runs hooks
194
+ // and the statusLine). A relative path survives the project being moved or
195
+ // renamed; an absolute PROJECT_ROOT path bakes in the install-time location
196
+ // and breaks the moment the directory is relocated.
197
+ return `bash ${shellQuote(path.join('scripts', scriptName))}`;
194
198
  }
195
199
 
196
200
  function replaceHarnessHookCommand(entries, marker, command) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@walwal-harness/cli",
3
- "version": "7.1.51",
3
+ "version": "7.1.52",
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"
@@ -1,14 +1,23 @@
1
1
  #!/usr/bin/env bash
2
- # walwal-harness — Brick Office dashboard launcher
2
+ # walwal-harness — Brick Office dashboard launcher (shared single-install)
3
3
  #
4
- # 처음 쓰는 사용자도 한 줄로 대시보드를 띄워볼 수 있게 만든 helper.
5
- # npm 패키지에 포함된 apps/harness-dashboard 를 프로젝트별 runtime 영역
6
- # (.harness/dashboard/) 에 복사 후 dev 실행한다.
7
- # 구버전 호환을 위해 패키지에 dashboard 가 없으면 git sparse-checkout 으로 fallback 한다.
4
+ # 대시보드 코드는 모든 프로젝트에서 byte 단위로 동일하다 (패키지가 버전 고정으로
5
+ # 배포). 프로젝트마다 다른 것은 HARNESS_ROOT(어느 .harness/ 를 볼지)와 PORT 뿐이고,
6
+ # HARNESS_ROOT 는 lib/harness-root.ts 가 *런타임*에 읽는다. 따라서 설치본은 하나면
7
+ # 충분하다.
8
+ #
9
+ # 이 런처는 버전별 단일 설치본을 ~/.walwal-harness/dashboard/<version>/ 에 두고
10
+ # (npm install + next build 를 버전당 딱 한 번), 각 프로젝트는 HARNESS_ROOT+PORT 만
11
+ # 바꿔 `next start` 로 띄운다. next start 는 .next 를 읽기전용으로 쓰므로 같은 폴더에서
12
+ # 여러 프로젝트를 동시에 서빙해도 안전하다. 프로젝트가 100개여도 node_modules/.next 는
13
+ # 단 1벌(~640MB).
14
+ #
15
+ # 구버전 호환: 패키지에 dashboard 가 없으면 git sparse-checkout 으로 fallback 한다.
8
16
  #
9
17
  # Usage:
10
- # bash scripts/harness-dashboard-up.sh # install + dev (port 3001)
18
+ # bash scripts/harness-dashboard-up.sh # build(최초 1회) + serve (port 3001)
11
19
  # bash scripts/harness-dashboard-up.sh --port 3050 # override port
20
+ # bash scripts/harness-dashboard-up.sh --reinstall # 공유 설치본 폐기 후 재빌드
12
21
  # HARNESS_ROOT=/path/to/project bash ... # explicit harness root
13
22
  set -euo pipefail
14
23
 
@@ -23,12 +32,13 @@ while [[ $# -gt 0 ]]; do
23
32
  --port) PORT="$2"; shift 2 ;;
24
33
  --reinstall) REINSTALL=true; shift ;;
25
34
  --help|-h)
26
- sed -n '2,16p' "$0"
35
+ sed -n '2,28p' "$0"
27
36
  exit 0 ;;
28
37
  *) echo "[brick-office] unknown arg: $1" >&2; exit 2 ;;
29
38
  esac
30
39
  done
31
40
 
41
+ # --- target project (the one whose .harness/ we visualize) ---
32
42
  if [[ -z "${HARNESS_ROOT:-}" ]]; then
33
43
  HARNESS_ROOT="$(pwd)"
34
44
  fi
@@ -38,87 +48,91 @@ if [[ ! -d "${HARNESS_ROOT}/.harness" ]]; then
38
48
  exit 3
39
49
  fi
40
50
 
41
- LOCAL_DIR="${HARNESS_ROOT}/.harness/dashboard"
42
- VERSION_FILE="${LOCAL_DIR}/.walwal-dashboard-version"
43
-
51
+ # --- locate the packaged dashboard source + its version ---
44
52
  PACKAGE_ROOT="${HARNESS_ROOT}/node_modules/@walwal-harness/cli"
45
53
  PACKAGED_DASHBOARD="${PACKAGE_ROOT}/${DASHBOARD_PATH}"
46
54
  if [[ -f "${PACKAGE_ROOT}/package.json" ]]; then
47
55
  PACKAGE_VERSION="$(node -p "require('${PACKAGE_ROOT}/package.json').version" 2>/dev/null || echo unknown)"
48
56
  fi
49
57
 
58
+ # --- shared, version-keyed install (the whole point: one copy for ALL projects) ---
59
+ SHARED_BASE="${WALWAL_HARNESS_HOME:-$HOME/.walwal-harness}/dashboard"
60
+ VERSION_KEY="${PACKAGE_VERSION}"
61
+ [[ "$VERSION_KEY" == "unknown" || -z "$VERSION_KEY" ]] && VERSION_KEY="edge"
62
+ SHARED_DIR="${SHARED_BASE}/${VERSION_KEY}" # node_modules + .next + source live here
63
+ READY_MARKER="${SHARED_DIR}/.walwal-dashboard-ready" # written only after a successful build
64
+ LOCK_DIR="${SHARED_BASE}/.build-lock-${VERSION_KEY}" # mkdir-based build mutex (atomic)
65
+
50
66
  if [[ "$REINSTALL" == "true" ]]; then
51
- rm -rf "$LOCAL_DIR"
67
+ rm -rf "$SHARED_DIR"
52
68
  fi
53
69
 
54
- ensure_gitignore() {
55
- local gitignore="${HARNESS_ROOT}/.gitignore"
56
- # `walwal-harness init` owns the canonical managed block (covers .harness/dashboard/
57
- # plus all other runtime/local state). If it's already present, nothing to add.
58
- local managed_marker="# >>> walwal-harness managed"
59
- if [[ -f "$gitignore" ]] && grep -Fq "$managed_marker" "$gitignore"; then
60
- return
61
- fi
62
- # Prefer the package's canonical template (single source of truth shared with init.js);
63
- # fall back to a dashboard-only block for older packages that lack the template.
64
- local tpl="${PACKAGE_ROOT}/assets/templates/gitignore-append.txt"
65
- local block
66
- if [[ -f "$tpl" ]]; then
67
- block="$(cat "$tpl")"
70
+ echo "[brick-office] HARNESS_ROOT = ${HARNESS_ROOT}"
71
+ echo "[brick-office] shared install = ${SHARED_DIR} (version ${VERSION_KEY})"
72
+
73
+ # Copy the dashboard source into the shared dir. Leaves any existing node_modules
74
+ # in place (the package tarball ships no node_modules), so a re-entry after a failed
75
+ # build keeps the installed deps.
76
+ populate_source() {
77
+ mkdir -p "$SHARED_DIR"
78
+ if [[ -d "${PACKAGED_DASHBOARD}" ]]; then
79
+ cp -R "${PACKAGED_DASHBOARD}/." "${SHARED_DIR}/"
68
80
  else
69
- block="$(cat <<'EOF'
70
- # walwal-harness dashboard runtime
71
- .harness/dashboard/
72
- EOF
73
- )"
74
- fi
75
- mkdir -p "$(dirname "$gitignore")"
76
- if [[ -f "$gitignore" ]] && [[ -s "$gitignore" ]] && [[ "$(tail -c 1 "$gitignore" 2>/dev/null || true)" != "" ]]; then
77
- printf '\n' >> "$gitignore"
81
+ echo "[brick-office] 패키지에 dashboard 없음 — git sparse-checkout fallback (~5MB)."
82
+ local dl="${SHARED_BASE}/.download-${VERSION_KEY}"
83
+ rm -rf "$dl"; mkdir -p "$dl"
84
+ (
85
+ cd "$dl"
86
+ git init -q
87
+ git remote add origin "${REPO_URL}" 2>/dev/null || true
88
+ git config core.sparseCheckout true
89
+ echo "${DASHBOARD_PATH}/" > .git/info/sparse-checkout
90
+ git fetch --depth=1 origin main -q
91
+ git checkout main -q
92
+ )
93
+ cp -R "${dl}/${DASHBOARD_PATH}/." "${SHARED_DIR}/"
94
+ rm -rf "$dl"
78
95
  fi
79
- printf '%s\n' "$block" >> "$gitignore"
80
96
  }
81
97
 
82
- ensure_gitignore
98
+ # build = install deps + production build, guarded by a single-writer lock so two
99
+ # projects launching at once don't race on the same shared dir. The EXIT trap is a
100
+ # safety net: under `set -e` a failed npm install / next build aborts the script, and
101
+ # the trap then frees the lock (and leaves no READY_MARKER → next launch rebuilds).
102
+ build_shared() {
103
+ local waited=0
104
+ mkdir -p "$SHARED_BASE" # lock dir is created with plain mkdir, so its parent must exist
105
+ while ! mkdir "$LOCK_DIR" 2>/dev/null; do
106
+ # someone else finished the build while we were waiting → done, nothing to own.
107
+ [[ -f "$READY_MARKER" ]] && return 0
108
+ sleep 1; waited=$((waited + 1))
109
+ if (( waited > 600 )); then
110
+ echo "[brick-office] stale build lock (>10m) — 강제 해제." >&2
111
+ rm -rf "$LOCK_DIR"
112
+ fi
113
+ done
114
+ trap 'rm -rf "$LOCK_DIR"' EXIT
83
115
 
84
- echo "[brick-office] HARNESS_ROOT = ${HARNESS_ROOT}"
85
- echo "[brick-office] dashboard runtime path = ${LOCAL_DIR}"
86
-
87
- if [[ -d "${PACKAGED_DASHBOARD}" ]]; then
88
- INSTALLED_VERSION=""
89
- if [[ -f "${VERSION_FILE}" ]]; then
90
- INSTALLED_VERSION="$(cat "${VERSION_FILE}" 2>/dev/null || true)"
91
- fi
92
- if [[ ! -d "${LOCAL_DIR}/${DASHBOARD_PATH}" || "${INSTALLED_VERSION}" != "${PACKAGE_VERSION}" ]]; then
93
- echo "[brick-office] dashboard sync from npm package @walwal-harness/cli@${PACKAGE_VERSION}"
94
- rm -rf "${LOCAL_DIR:?}/${DASHBOARD_PATH}"
95
- mkdir -p "${LOCAL_DIR}/apps"
96
- cp -R "${PACKAGED_DASHBOARD}" "${LOCAL_DIR}/apps/"
97
- printf '%s\n' "${PACKAGE_VERSION}" > "${VERSION_FILE}"
116
+ # double-checked: another waiter may have built it just before we got the lock.
117
+ if [[ -f "$READY_MARKER" && "$(cat "$READY_MARKER" 2>/dev/null || true)" == "$VERSION_KEY" ]]; then
118
+ rm -rf "$LOCK_DIR"; trap - EXIT; return 0
98
119
  fi
99
- elif [[ ! -d "${LOCAL_DIR}/${DASHBOARD_PATH}" ]]; then
100
- echo "[brick-office] 첫 실행 — git sparse-checkout 으로 dashboard 만 가져옵니다 (~5MB)."
101
- rm -rf "${LOCAL_DIR}/.download"
102
- mkdir -p "${LOCAL_DIR}/.download"
103
- cd "${LOCAL_DIR}/.download"
104
- git init -q
105
- git remote add origin "${REPO_URL}" 2>/dev/null || true
106
- git config core.sparseCheckout true
107
- echo "${DASHBOARD_PATH}/" > .git/info/sparse-checkout
108
- git fetch --depth=1 origin main -q
109
- git checkout main -q
110
- mkdir -p "${LOCAL_DIR}/apps"
111
- rm -rf "${LOCAL_DIR:?}/${DASHBOARD_PATH}"
112
- cp -R "${DASHBOARD_PATH}" "${LOCAL_DIR}/apps/"
113
- rm -rf "${LOCAL_DIR}/.download"
114
- echo "[brick-office] 다운로드 완료."
115
- fi
116
120
 
117
- cd "${LOCAL_DIR}/${DASHBOARD_PATH}"
121
+ [[ -f "${SHARED_DIR}/package.json" ]] || populate_source
122
+
123
+ echo "[brick-office] npm install (버전당 1회, ~30s)..."
124
+ ( cd "$SHARED_DIR" && npm install --no-audit --no-fund --loglevel=error )
125
+
126
+ echo "[brick-office] next build (버전당 1회)..."
127
+ ( cd "$SHARED_DIR" && ./node_modules/.bin/next build )
128
+
129
+ printf '%s\n' "$VERSION_KEY" > "$READY_MARKER"
130
+ echo "[brick-office] 빌드 완료 — 이후 모든 프로젝트가 이 설치본을 공유합니다."
131
+ rm -rf "$LOCK_DIR"; trap - EXIT
132
+ }
118
133
 
119
- if [[ ! -d node_modules ]]; then
120
- echo "[brick-office] npm install (한 번만 실행, ~30s)..."
121
- npm install --no-audit --no-fund --loglevel=error
134
+ if [[ ! -f "$READY_MARKER" || "$(cat "$READY_MARKER" 2>/dev/null || true)" != "$VERSION_KEY" ]]; then
135
+ build_shared
122
136
  fi
123
137
 
124
138
  echo ""
@@ -130,4 +144,5 @@ echo "║ 실시간 (SSE) 시각화합니다. Ctrl+C 로 종료.
130
144
  echo "╚══════════════════════════════════════════════════════════╝"
131
145
  echo ""
132
146
 
133
- HARNESS_ROOT="${HARNESS_ROOT}" npm run dev:dashboard -- -H 127.0.0.1 -p "${PORT}"
147
+ cd "$SHARED_DIR"
148
+ HARNESS_ROOT="${HARNESS_ROOT}" exec ./node_modules/.bin/next start -H 127.0.0.1 -p "${PORT}"