Skip to content

Commit 6211333

Browse files
jisulee42claude
andcommitted
fix(host-paths): use $HOME instead of ~ in non-env-var host paths
`HOST_PATHS` for Claude (and any future host with `usesEnvVars=false`) was built from `~/${config.globalRoot}`. The resulting binDir is then interpolated into double-quoted bash strings by several preamble emitters, e.g. `generate-brain-sync-block.ts:45`: _BRAIN_SYNC_BIN="${ctx.paths.binDir}/gstack-brain-sync" POSIX shells do NOT expand `~` inside double quotes. The emitted line becomes a literal `"~/.claude/.../gstack-brain-sync"`, which fails with `exit 127, No such file or directory` when invoked. Both stderr and the exit code are suppressed by `2>/dev/null || true`, so the failure is silent: skill preambles call `gstack-brain-sync --once` on every skill boundary, but it never runs. The artifacts queue (~/.gstack/.brain-queue.jsonl) grows indefinitely while last-push stays at "never". Codex/Factory hosts are unaffected because they use `$GSTACK_BIN`, which DOES expand inside double quotes. Fix: emit `$HOME/${config.globalRoot}` instead. Identical semantics outside quotes, correct semantics inside quotes. Why CI didn't catch it: - `test/host-config.test.ts:246` asserted the broken form as the expected value (`expect(...).toBe('~/.claude/skills/gstack/bin')`). - The Claude golden fixture (`test/fixtures/golden/claude-ship-SKILL.md`) was generated from the broken code path, so the regression suite was comparing the bug against itself. Changes: - `scripts/resolvers/types.ts`: emit `$HOME/...` for non-env-var hosts (one-line change + WHY comment). - `test/host-config.test.ts`: update existing assertions to expect `$HOME/...`. Add a narrow regression guard that asserts no non-env-var host's binDir/skillRoot/browseDir/designDir/makePdfDir begins with `~` (i.e., is safe to interpolate inside double quotes). - Regenerated artifacts via `bun run gen:skill-docs --host all`: 42 Claude-host SKILL.md files + the claude-ship golden fixture flip from `~/...` to `$HOME/...`. Codex/Factory artifacts unchanged. Test plan: - `bun test test/host-config.test.ts` — 74 pass, 0 fail (includes new guard). - `bun test test/gen-skill-docs.test.ts test/brain-sync.test.ts` — 412 pass, 0 fail. - `bun run test` (main suite, e2e excluded) — green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent dc6252d commit 6211333

44 files changed

Lines changed: 1773 additions & 1750 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

SKILL.md

Lines changed: 34 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -24,66 +24,66 @@ triggers:
2424
## Preamble (run first)
2525

2626
```bash
27-
_UPD=$(~/.claude/skills/gstack/bin/gstack-update-check 2>/dev/null || .claude/skills/gstack/bin/gstack-update-check 2>/dev/null || true)
27+
_UPD=$($HOME/.claude/skills/gstack/bin/gstack-update-check 2>/dev/null || .claude/skills/gstack/bin/gstack-update-check 2>/dev/null || true)
2828
[ -n "$_UPD" ] && echo "$_UPD" || true
2929
mkdir -p ~/.gstack/sessions
3030
touch ~/.gstack/sessions/"$PPID"
3131
_SESSIONS=$(find ~/.gstack/sessions -mmin -120 -type f 2>/dev/null | wc -l | tr -d ' ')
3232
find ~/.gstack/sessions -mmin +120 -type f -exec rm {} + 2>/dev/null || true
33-
_PROACTIVE=$(~/.claude/skills/gstack/bin/gstack-config get proactive 2>/dev/null || echo "true")
33+
_PROACTIVE=$($HOME/.claude/skills/gstack/bin/gstack-config get proactive 2>/dev/null || echo "true")
3434
_PROACTIVE_PROMPTED=$([ -f ~/.gstack/.proactive-prompted ] && echo "yes" || echo "no")
3535
_BRANCH=$(git branch --show-current 2>/dev/null || echo "unknown")
3636
echo "BRANCH: $_BRANCH"
37-
_SKILL_PREFIX=$(~/.claude/skills/gstack/bin/gstack-config get skill_prefix 2>/dev/null || echo "false")
37+
_SKILL_PREFIX=$($HOME/.claude/skills/gstack/bin/gstack-config get skill_prefix 2>/dev/null || echo "false")
3838
echo "PROACTIVE: $_PROACTIVE"
3939
echo "PROACTIVE_PROMPTED: $_PROACTIVE_PROMPTED"
4040
echo "SKILL_PREFIX: $_SKILL_PREFIX"
41-
source <(~/.claude/skills/gstack/bin/gstack-repo-mode 2>/dev/null) || true
41+
source <($HOME/.claude/skills/gstack/bin/gstack-repo-mode 2>/dev/null) || true
4242
REPO_MODE=${REPO_MODE:-unknown}
4343
echo "REPO_MODE: $REPO_MODE"
4444
_LAKE_SEEN=$([ -f ~/.gstack/.completeness-intro-seen ] && echo "yes" || echo "no")
4545
echo "LAKE_INTRO: $_LAKE_SEEN"
46-
_TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || true)
46+
_TEL=$($HOME/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || true)
4747
_TEL_PROMPTED=$([ -f ~/.gstack/.telemetry-prompted ] && echo "yes" || echo "no")
4848
_TEL_START=$(date +%s)
4949
_SESSION_ID="$$-$(date +%s)"
5050
echo "TELEMETRY: ${_TEL:-off}"
5151
echo "TEL_PROMPTED: $_TEL_PROMPTED"
52-
_EXPLAIN_LEVEL=$(~/.claude/skills/gstack/bin/gstack-config get explain_level 2>/dev/null || echo "default")
52+
_EXPLAIN_LEVEL=$($HOME/.claude/skills/gstack/bin/gstack-config get explain_level 2>/dev/null || echo "default")
5353
if [ "$_EXPLAIN_LEVEL" != "default" ] && [ "$_EXPLAIN_LEVEL" != "terse" ]; then _EXPLAIN_LEVEL="default"; fi
5454
echo "EXPLAIN_LEVEL: $_EXPLAIN_LEVEL"
55-
_QUESTION_TUNING=$(~/.claude/skills/gstack/bin/gstack-config get question_tuning 2>/dev/null || echo "false")
55+
_QUESTION_TUNING=$($HOME/.claude/skills/gstack/bin/gstack-config get question_tuning 2>/dev/null || echo "false")
5656
echo "QUESTION_TUNING: $_QUESTION_TUNING"
5757
mkdir -p ~/.gstack/analytics
5858
if [ "$_TEL" != "off" ]; then
5959
echo '{"skill":"gstack","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null || echo "unknown")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true
6060
fi
6161
for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do
6262
if [ -f "$_PF" ]; then
63-
if [ "$_TEL" != "off" ] && [ -x "~/.claude/skills/gstack/bin/gstack-telemetry-log" ]; then
64-
~/.claude/skills/gstack/bin/gstack-telemetry-log --event-type skill_run --skill _pending_finalize --outcome unknown --session-id "$_SESSION_ID" 2>/dev/null || true
63+
if [ "$_TEL" != "off" ] && [ -x "$HOME/.claude/skills/gstack/bin/gstack-telemetry-log" ]; then
64+
$HOME/.claude/skills/gstack/bin/gstack-telemetry-log --event-type skill_run --skill _pending_finalize --outcome unknown --session-id "$_SESSION_ID" 2>/dev/null || true
6565
fi
6666
rm -f "$_PF" 2>/dev/null || true
6767
fi
6868
break
6969
done
70-
eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" 2>/dev/null || true
70+
eval "$($HOME/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" 2>/dev/null || true
7171
_LEARN_FILE="${GSTACK_HOME:-$HOME/.gstack}/projects/${SLUG:-unknown}/learnings.jsonl"
7272
if [ -f "$_LEARN_FILE" ]; then
7373
_LEARN_COUNT=$(wc -l < "$_LEARN_FILE" 2>/dev/null | tr -d ' ')
7474
echo "LEARNINGS: $_LEARN_COUNT entries loaded"
7575
if [ "$_LEARN_COUNT" -gt 5 ] 2>/dev/null; then
76-
~/.claude/skills/gstack/bin/gstack-learnings-search --limit 3 2>/dev/null || true
76+
$HOME/.claude/skills/gstack/bin/gstack-learnings-search --limit 3 2>/dev/null || true
7777
fi
7878
else
7979
echo "LEARNINGS: 0"
8080
fi
81-
~/.claude/skills/gstack/bin/gstack-timeline-log '{"skill":"gstack","event":"started","branch":"'"$_BRANCH"'","session":"'"$_SESSION_ID"'"}' 2>/dev/null &
81+
$HOME/.claude/skills/gstack/bin/gstack-timeline-log '{"skill":"gstack","event":"started","branch":"'"$_BRANCH"'","session":"'"$_SESSION_ID"'"}' 2>/dev/null &
8282
_HAS_ROUTING="no"
8383
if [ -f CLAUDE.md ] && grep -q "## Skill routing" CLAUDE.md 2>/dev/null; then
8484
_HAS_ROUTING="yes"
8585
fi
86-
_ROUTING_DECLINED=$(~/.claude/skills/gstack/bin/gstack-config get routing_declined 2>/dev/null || echo "false")
86+
_ROUTING_DECLINED=$($HOME/.claude/skills/gstack/bin/gstack-config get routing_declined 2>/dev/null || echo "false")
8787
echo "HAS_ROUTING: $_HAS_ROUTING"
8888
echo "ROUTING_DECLINED: $_ROUTING_DECLINED"
8989
_VENDORED="no"
@@ -94,8 +94,8 @@ if [ -d ".claude/skills/gstack" ] && [ ! -L ".claude/skills/gstack" ]; then
9494
fi
9595
echo "VENDORED_GSTACK: $_VENDORED"
9696
echo "MODEL_OVERLAY: claude"
97-
_CHECKPOINT_MODE=$(~/.claude/skills/gstack/bin/gstack-config get checkpoint_mode 2>/dev/null || echo "explicit")
98-
_CHECKPOINT_PUSH=$(~/.claude/skills/gstack/bin/gstack-config get checkpoint_push 2>/dev/null || echo "false")
97+
_CHECKPOINT_MODE=$($HOME/.claude/skills/gstack/bin/gstack-config get checkpoint_mode 2>/dev/null || echo "explicit")
98+
_CHECKPOINT_PUSH=$($HOME/.claude/skills/gstack/bin/gstack-config get checkpoint_push 2>/dev/null || echo "false")
9999
echo "CHECKPOINT_MODE: $_CHECKPOINT_MODE"
100100
echo "CHECKPOINT_PUSH: $_CHECKPOINT_PUSH"
101101
[ -n "$OPENCLAW_SESSION" ] && echo "SPAWNED_SESSION: true" || true
@@ -111,15 +111,15 @@ If the user invokes a skill in plan mode, the skill takes precedence over generi
111111

112112
If `PROACTIVE` is `"false"`, do not auto-invoke or proactively suggest skills. If a skill seems useful, ask: "I think /skillname might help here — want me to run it?"
113113

114-
If `SKILL_PREFIX` is `"true"`, suggest/invoke `/gstack-*` names. Disk paths stay `~/.claude/skills/gstack/[skill-name]/SKILL.md`.
114+
If `SKILL_PREFIX` is `"true"`, suggest/invoke `/gstack-*` names. Disk paths stay `$HOME/.claude/skills/gstack/[skill-name]/SKILL.md`.
115115

116-
If output shows `UPGRADE_AVAILABLE <old> <new>`: read `~/.claude/skills/gstack/gstack-upgrade/SKILL.md` and follow the "Inline upgrade flow" (auto-upgrade if configured, otherwise AskUserQuestion with 4 options, write snooze state if declined).
116+
If output shows `UPGRADE_AVAILABLE <old> <new>`: read `$HOME/.claude/skills/gstack/gstack-upgrade/SKILL.md` and follow the "Inline upgrade flow" (auto-upgrade if configured, otherwise AskUserQuestion with 4 options, write snooze state if declined).
117117

118118
If output shows `JUST_UPGRADED <from> <to>`: print "Running gstack v{to} (just updated!)". If `SPAWNED_SESSION` is true, skip feature discovery.
119119

120120
Feature discovery, max one prompt per session:
121-
- Missing `~/.claude/skills/gstack/.feature-prompted-continuous-checkpoint`: AskUserQuestion for Continuous checkpoint auto-commits. If accepted, run `~/.claude/skills/gstack/bin/gstack-config set checkpoint_mode continuous`. Always touch marker.
122-
- Missing `~/.claude/skills/gstack/.feature-prompted-model-overlay`: inform "Model overlays are active. MODEL_OVERLAY shows the patch." Always touch marker.
121+
- Missing `$HOME/.claude/skills/gstack/.feature-prompted-continuous-checkpoint`: AskUserQuestion for Continuous checkpoint auto-commits. If accepted, run `$HOME/.claude/skills/gstack/bin/gstack-config set checkpoint_mode continuous`. Always touch marker.
122+
- Missing `$HOME/.claude/skills/gstack/.feature-prompted-model-overlay`: inform "Model overlays are active. MODEL_OVERLAY shows the patch." Always touch marker.
123123

124124
After upgrade prompts, continue workflow.
125125

@@ -132,7 +132,7 @@ Options:
132132
- B) Restore V0 prose — set `explain_level: terse`
133133

134134
If A: leave `explain_level` unset (defaults to `default`).
135-
If B: run `~/.claude/skills/gstack/bin/gstack-config set explain_level terse`.
135+
If B: run `$HOME/.claude/skills/gstack/bin/gstack-config set explain_level terse`.
136136

137137
Always run (regardless of choice):
138138
```bash
@@ -159,7 +159,7 @@ Options:
159159
- A) Help gstack get better! (recommended)
160160
- B) No thanks
161161

162-
If A: run `~/.claude/skills/gstack/bin/gstack-config set telemetry community`
162+
If A: run `$HOME/.claude/skills/gstack/bin/gstack-config set telemetry community`
163163

164164
If B: ask follow-up:
165165

@@ -169,8 +169,8 @@ Options:
169169
- A) Sure, anonymous is fine
170170
- B) No thanks, fully off
171171

172-
If B→A: run `~/.claude/skills/gstack/bin/gstack-config set telemetry anonymous`
173-
If B→B: run `~/.claude/skills/gstack/bin/gstack-config set telemetry off`
172+
If B→A: run `$HOME/.claude/skills/gstack/bin/gstack-config set telemetry anonymous`
173+
If B→B: run `$HOME/.claude/skills/gstack/bin/gstack-config set telemetry off`
174174

175175
Always run:
176176
```bash
@@ -187,8 +187,8 @@ Options:
187187
- A) Keep it on (recommended)
188188
- B) Turn it off — I'll type /commands myself
189189

190-
If A: run `~/.claude/skills/gstack/bin/gstack-config set proactive true`
191-
If B: run `~/.claude/skills/gstack/bin/gstack-config set proactive false`
190+
If A: run `$HOME/.claude/skills/gstack/bin/gstack-config set proactive true`
191+
If B: run `$HOME/.claude/skills/gstack/bin/gstack-config set proactive false`
192192

193193
Always run:
194194
```bash
@@ -233,7 +233,7 @@ Key routing rules:
233233

234234
Then commit the change: `git add CLAUDE.md && git commit -m "chore: add gstack skill routing rules to CLAUDE.md"`
235235

236-
If B: run `~/.claude/skills/gstack/bin/gstack-config set routing_declined true` and say they can re-enable with `gstack-config set routing_declined false`.
236+
If B: run `$HOME/.claude/skills/gstack/bin/gstack-config set routing_declined true` and say they can re-enable with `gstack-config set routing_declined false`.
237237

238238
This only happens once per project. Skip if `HAS_ROUTING` is `yes` or `ROUTING_DECLINED` is `true`.
239239

@@ -249,15 +249,15 @@ Options:
249249
If A:
250250
1. Run `git rm -r .claude/skills/gstack/`
251251
2. Run `echo '.claude/skills/gstack/' >> .gitignore`
252-
3. Run `~/.claude/skills/gstack/bin/gstack-team-init required` (or `optional`)
252+
3. Run `$HOME/.claude/skills/gstack/bin/gstack-team-init required` (or `optional`)
253253
4. Run `git add .claude/ .gitignore CLAUDE.md && git commit -m "chore: migrate gstack from vendored to team mode"`
254254
5. Tell the user: "Done. Each developer now runs: `cd ~/.claude/skills/gstack && ./setup --team`"
255255

256256
If B: say "OK, you're on your own to keep the vendored copy up to date."
257257

258258
Always run (regardless of choice):
259259
```bash
260-
eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" 2>/dev/null || true
260+
eval "$($HOME/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" 2>/dev/null || true
261261
touch ~/.gstack/.vendoring-warned-${SLUG:-unknown}
262262
```
263263

@@ -281,8 +281,8 @@ if [ -f "$HOME/.gstack-artifacts-remote.txt" ]; then
281281
else
282282
_BRAIN_REMOTE_FILE="$HOME/.gstack-brain-remote.txt"
283283
fi
284-
_BRAIN_SYNC_BIN="~/.claude/skills/gstack/bin/gstack-brain-sync"
285-
_BRAIN_CONFIG_BIN="~/.claude/skills/gstack/bin/gstack-config"
284+
_BRAIN_SYNC_BIN="$HOME/.claude/skills/gstack/bin/gstack-brain-sync"
285+
_BRAIN_CONFIG_BIN="$HOME/.claude/skills/gstack/bin/gstack-config"
286286

287287
# /sync-gbrain context-load: teach the agent to use gbrain when it's available.
288288
# Per-worktree pin: post-spike redesign uses kubectl-style `.gbrain-source` in the
@@ -391,8 +391,8 @@ If A/B and `~/.gstack/.git` is missing, ask whether to run `gstack-artifacts-ini
391391
At skill END before telemetry:
392392

393393
```bash
394-
"~/.claude/skills/gstack/bin/gstack-brain-sync" --discover-new 2>/dev/null || true
395-
"~/.claude/skills/gstack/bin/gstack-brain-sync" --once 2>/dev/null || true
394+
"$HOME/.claude/skills/gstack/bin/gstack-brain-sync" --discover-new 2>/dev/null || true
395+
"$HOME/.claude/skills/gstack/bin/gstack-brain-sync" --once 2>/dev/null || true
396396
```
397397

398398

@@ -437,7 +437,7 @@ Escalate after 3 failed attempts, uncertain security-sensitive changes, or scope
437437
Before completing, if you discovered a durable project quirk or command fix that would save 5+ minutes next time, log it:
438438

439439
```bash
440-
~/.claude/skills/gstack/bin/gstack-learnings-log '{"skill":"SKILL_NAME","type":"operational","key":"SHORT_KEY","insight":"DESCRIPTION","confidence":N,"source":"observed"}'
440+
$HOME/.claude/skills/gstack/bin/gstack-learnings-log '{"skill":"SKILL_NAME","type":"operational","key":"SHORT_KEY","insight":"DESCRIPTION","confidence":N,"source":"observed"}'
441441
```
442442

443443
Do not log obvious facts or one-time transient errors.
@@ -541,7 +541,7 @@ Auto-shuts down after 30 min idle. State persists between calls (cookies, tabs,
541541
_ROOT=$(git rev-parse --show-toplevel 2>/dev/null)
542542
B=""
543543
[ -n "$_ROOT" ] && [ -x "$_ROOT/.claude/skills/gstack/browse/dist/browse" ] && B="$_ROOT/.claude/skills/gstack/browse/dist/browse"
544-
[ -z "$B" ] && B="$HOME/.claude/skills/gstack/browse/dist/browse"
544+
[ -z "$B" ] && B="$HOME$HOME/.claude/skills/gstack/browse/dist/browse"
545545
if [ -x "$B" ]; then
546546
echo "READY: $B"
547547
else

0 commit comments

Comments
 (0)