mirror of https://github.com/garrytan/gstack.git
Merge c26266a97e into 2beb636f7c
This commit is contained in:
commit
847bee6946
23
README.md
23
README.md
|
|
@ -48,14 +48,21 @@ Fork it. Improve it. Make it yours. And if you want to hate on free open source
|
||||||
|
|
||||||
Open Claude Code and paste this. Claude does the rest.
|
Open Claude Code and paste this. Claude does the rest.
|
||||||
|
|
||||||
> Install gstack: run **`git clone --single-branch --depth 1 https://github.com/garrytan/gstack.git ~/.claude/skills/gstack && cd ~/.claude/skills/gstack && ./setup`** then add a "gstack" section to CLAUDE.md that says to use the /browse skill from gstack for all web browsing, never use mcp\_\_claude-in-chrome\_\_\* tools, and lists the available skills: /office-hours, /plan-ceo-review, /plan-eng-review, /plan-design-review, /design-consultation, /design-shotgun, /design-html, /review, /ship, /land-and-deploy, /canary, /benchmark, /browse, /connect-chrome, /qa, /qa-only, /design-review, /setup-browser-cookies, /setup-deploy, /setup-gbrain, /retro, /investigate, /document-release, /document-generate, /codex, /cso, /autoplan, /plan-devex-review, /devex-review, /careful, /freeze, /guard, /unfreeze, /gstack-upgrade, /learn. Then ask the user if they also want to add gstack to the current project so teammates get it.
|
> Install gstack: run **`git clone --single-branch --depth 1 https://github.com/garrytan/gstack.git ~/.gstack/repos/gstack && cd ~/.gstack/repos/gstack && ./setup`** then add a "gstack" section to CLAUDE.md that says to use the /browse skill from gstack for all web browsing, never use mcp\_\_claude-in-chrome\_\_\* tools, and lists the available skills: /office-hours, /plan-ceo-review, /plan-eng-review, /plan-design-review, /design-consultation, /design-shotgun, /design-html, /review, /ship, /land-and-deploy, /canary, /benchmark, /browse, /connect-chrome, /qa, /qa-only, /design-review, /setup-browser-cookies, /setup-deploy, /setup-gbrain, /retro, /investigate, /document-release, /document-generate, /codex, /cso, /autoplan, /plan-devex-review, /devex-review, /careful, /freeze, /guard, /unfreeze, /gstack-upgrade, /learn. Then ask the user if they also want to add gstack to the current project so teammates get it.
|
||||||
|
|
||||||
|
The checkout lives at `~/.gstack/repos/gstack` (outside Claude's skill-discovery
|
||||||
|
root), and `~/.claude/skills/` holds the skill aliases plus a small runtime-only
|
||||||
|
`gstack/` sidecar. Existing installs at `~/.claude/skills/gstack` are migrated
|
||||||
|
automatically on the next `./setup` or `/gstack-upgrade`. This keeps recursive
|
||||||
|
skill scanners (e.g. Cursor) from indexing the repo's generated host-mirror
|
||||||
|
skills. See [#1202](https://github.com/garrytan/gstack/issues/1202).
|
||||||
|
|
||||||
### Step 2: Team mode — auto-update for shared repos (recommended)
|
### Step 2: Team mode — auto-update for shared repos (recommended)
|
||||||
|
|
||||||
From inside your repo, paste this. Switches you to team mode, bootstraps the repo so teammates get gstack automatically, and commits the change:
|
From inside your repo, paste this. Switches you to team mode, bootstraps the repo so teammates get gstack automatically, and commits the change:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
(cd ~/.claude/skills/gstack && ./setup --team) && ~/.claude/skills/gstack/bin/gstack-team-init required && git add .claude/ CLAUDE.md && git commit -m "require gstack for AI-assisted work"
|
(cd ~/.gstack/repos/gstack && ./setup --team) && ~/.gstack/repos/gstack/bin/gstack-team-init required && git add .claude/ CLAUDE.md && git commit -m "require gstack for AI-assisted work"
|
||||||
```
|
```
|
||||||
|
|
||||||
No vendored files in your repo, no version drift, no manual upgrades. Every Claude Code session starts with a fast auto-update check (throttled to once/hour, network-failure-safe, completely silent).
|
No vendored files in your repo, no version drift, no manual upgrades. Every Claude Code session starts with a fast auto-update check (throttled to once/hour, network-failure-safe, completely silent).
|
||||||
|
|
@ -67,7 +74,7 @@ Swap `required` for `optional` if you'd rather nudge teammates than block them.
|
||||||
OpenClaw spawns Claude Code sessions via ACP, so every gstack skill just works
|
OpenClaw spawns Claude Code sessions via ACP, so every gstack skill just works
|
||||||
when Claude Code has gstack installed. Paste this to your OpenClaw agent:
|
when Claude Code has gstack installed. Paste this to your OpenClaw agent:
|
||||||
|
|
||||||
> Install gstack: run `git clone --single-branch --depth 1 https://github.com/garrytan/gstack.git ~/.claude/skills/gstack && cd ~/.claude/skills/gstack && ./setup` to install gstack for Claude Code. Then add a "Coding Tasks" section to AGENTS.md that says: when spawning Claude Code sessions for coding work, tell the session to use gstack skills. Include these examples — security audit: "Load gstack. Run /cso", code review: "Load gstack. Run /review", QA test a URL: "Load gstack. Run /qa https://...", build a feature end-to-end: "Load gstack. Run /autoplan, implement the plan, then run /ship", plan before building: "Load gstack. Run /office-hours then /autoplan. Save the plan, don't implement."
|
> Install gstack: run `git clone --single-branch --depth 1 https://github.com/garrytan/gstack.git ~/.gstack/repos/gstack && cd ~/.gstack/repos/gstack && ./setup` to install gstack for Claude Code. Then add a "Coding Tasks" section to AGENTS.md that says: when spawning Claude Code sessions for coding work, tell the session to use gstack skills. Include these examples — security audit: "Load gstack. Run /cso", code review: "Load gstack. Run /review", QA test a URL: "Load gstack. Run /qa https://...", build a feature end-to-end: "Load gstack. Run /autoplan, implement the plan, then run /ship", plan before building: "Load gstack. Run /office-hours then /autoplan. Save the plan, don't implement."
|
||||||
|
|
||||||
**After setup, just talk to your OpenClaw agent naturally:**
|
**After setup, just talk to your OpenClaw agent naturally:**
|
||||||
|
|
||||||
|
|
@ -456,21 +463,21 @@ Data is stored in [Supabase](https://supabase.com) (open source Firebase alterna
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
**Skill not showing up?** `cd ~/.claude/skills/gstack && ./setup`
|
**Skill not showing up?** `cd ~/.gstack/repos/gstack && ./setup`
|
||||||
|
|
||||||
**`/browse` fails?** `cd ~/.claude/skills/gstack && bun install && bun run build`
|
**`/browse` fails?** `cd ~/.gstack/repos/gstack && bun install && bun run build`
|
||||||
|
|
||||||
**Stale install?** Run `/gstack-upgrade` — or set `auto_upgrade: true` in `~/.gstack/config.yaml`
|
**Stale install?** Run `/gstack-upgrade` — or set `auto_upgrade: true` in `~/.gstack/config.yaml`
|
||||||
|
|
||||||
**Want shorter commands?** `cd ~/.claude/skills/gstack && ./setup --no-prefix` — switches from `/gstack-qa` to `/qa`. Your choice is remembered for future upgrades.
|
**Want shorter commands?** `cd ~/.gstack/repos/gstack && ./setup --no-prefix` — switches from `/gstack-qa` to `/qa`. Your choice is remembered for future upgrades.
|
||||||
|
|
||||||
**Want namespaced commands?** `cd ~/.claude/skills/gstack && ./setup --prefix` — switches from `/qa` to `/gstack-qa`. Useful if you run other skill packs alongside gstack.
|
**Want namespaced commands?** `cd ~/.gstack/repos/gstack && ./setup --prefix` — switches from `/qa` to `/gstack-qa`. Useful if you run other skill packs alongside gstack.
|
||||||
|
|
||||||
**Codex says "Skipped loading skill(s) due to invalid SKILL.md"?** Your Codex skill descriptions are stale. Fix: `cd ~/.codex/skills/gstack && git pull && ./setup --host codex` — or for repo-local installs: `cd "$(readlink -f .agents/skills/gstack)" && git pull && ./setup --host codex`
|
**Codex says "Skipped loading skill(s) due to invalid SKILL.md"?** Your Codex skill descriptions are stale. Fix: `cd ~/.codex/skills/gstack && git pull && ./setup --host codex` — or for repo-local installs: `cd "$(readlink -f .agents/skills/gstack)" && git pull && ./setup --host codex`
|
||||||
|
|
||||||
**Windows users:** gstack works on Windows 11 via Git Bash or WSL. Node.js is required in addition to Bun — Bun has a known bug with Playwright's pipe transport on Windows ([bun#4253](https://github.com/oven-sh/bun/issues/4253)). The browse server automatically falls back to Node.js. Make sure both `bun` and `node` are on your PATH.
|
**Windows users:** gstack works on Windows 11 via Git Bash or WSL. Node.js is required in addition to Bun — Bun has a known bug with Playwright's pipe transport on Windows ([bun#4253](https://github.com/oven-sh/bun/issues/4253)). The browse server automatically falls back to Node.js. Make sure both `bun` and `node` are on your PATH.
|
||||||
|
|
||||||
On Windows without Developer Mode (MSYS2 / Git Bash), `setup` falls back to file copies instead of symlinks because `ln -snf` produces frozen copies that don't refresh on `git pull`. **Re-run `cd ~/.claude/skills/gstack && ./setup` after every `git pull`** so your skill files match the repo. `setup` prints a one-line note reminding you. Unix and WSL keep symlinks and don't need the re-run.
|
On Windows without Developer Mode (MSYS2 / Git Bash), `setup` falls back to file copies instead of symlinks because `ln -snf` produces frozen copies that don't refresh on `git pull`. **Re-run `cd ~/.gstack/repos/gstack && ./setup` after every `git pull`** so your skill files match the repo. `setup` prints a one-line note reminding you. Unix and WSL keep symlinks and don't need the re-run.
|
||||||
|
|
||||||
**Claude says it can't see the skills?** Make sure your project's `CLAUDE.md` has a gstack section. Add this:
|
**Claude says it can't see the skills?** Make sure your project's `CLAUDE.md` has a gstack section. Add this:
|
||||||
|
|
||||||
|
|
|
||||||
101
setup
101
setup
|
|
@ -249,6 +249,59 @@ if [ "$INSTALL_CODEX" -eq 1 ]; then
|
||||||
migrate_direct_codex_install "$SOURCE_GSTACK_DIR" "$CODEX_GSTACK"
|
migrate_direct_codex_install "$SOURCE_GSTACK_DIR" "$CODEX_GSTACK"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# ─── Claude: relocate a direct ~/.claude/skills/gstack checkout ──────────────
|
||||||
|
# A full repo checkout sitting at ~/.claude/skills/gstack is itself under
|
||||||
|
# Claude Code's (and Cursor's) recursive skill-discovery root, so every
|
||||||
|
# generated host-mirror SKILL.md (.cursor/, .hermes/, .agents/, …) and source
|
||||||
|
# skill SKILL.md inside it gets indexed — hundreds of phantom skills. Claude
|
||||||
|
# Code drops descriptions ("N skill descriptions dropped"); Cursor's cursor-agent
|
||||||
|
# CLI hard-freezes. This mirrors migrate_direct_codex_install: move the checkout
|
||||||
|
# out of the discovery root to ~/.gstack/repos/gstack and keep ~/.claude/skills
|
||||||
|
# as the alias root (a runtime-only sidecar is created later in the Claude
|
||||||
|
# install step). See issues #1202 and #1694.
|
||||||
|
_CLAUDE_RELOCATED=0
|
||||||
|
migrate_direct_claude_install() {
|
||||||
|
local claude_gstack="$HOME/.claude/skills/gstack"
|
||||||
|
local migrated_dir="$HOME/.gstack/repos/gstack"
|
||||||
|
|
||||||
|
# Only the standard *global* layout migrates. Leave --local, symlinked
|
||||||
|
# worktree/vendored installs, and anything not living directly at
|
||||||
|
# ~/.claude/skills/gstack untouched.
|
||||||
|
[ "$LOCAL_INSTALL" -eq 1 ] && return 0
|
||||||
|
|
||||||
|
if [ "$SOURCE_GSTACK_DIR" = "$claude_gstack" ] && [ ! -L "$claude_gstack" ]; then
|
||||||
|
mkdir -p "$(dirname "$migrated_dir")"
|
||||||
|
if [ -e "$migrated_dir" ]; then
|
||||||
|
echo "gstack setup failed: direct Claude install detected at $claude_gstack" >&2
|
||||||
|
echo "A migrated repo already exists at $migrated_dir; move one aside and rerun setup." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
log "Migrating direct Claude install to $migrated_dir to avoid duplicate skill discovery..."
|
||||||
|
mv "$SOURCE_GSTACK_DIR" "$migrated_dir"
|
||||||
|
SOURCE_GSTACK_DIR="$migrated_dir"
|
||||||
|
BROWSE_BIN="$SOURCE_GSTACK_DIR/browse/dist/browse"
|
||||||
|
_CLAUDE_RELOCATED=1
|
||||||
|
elif [ "$SOURCE_GSTACK_DIR" = "$migrated_dir" ]; then
|
||||||
|
# Already-relocated install re-running setup (e.g. via /gstack-upgrade,
|
||||||
|
# which cd's into the install dir and runs ./setup). Keep aliases in
|
||||||
|
# ~/.claude/skills and refresh the sidecar instead of falling through to
|
||||||
|
# the worktree-symlink branch (which would replant a full-repo symlink
|
||||||
|
# back inside the discovery root).
|
||||||
|
_CLAUDE_RELOCATED=1
|
||||||
|
else
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Decouple the alias root from the checkout location: top-level skill aliases
|
||||||
|
# stay in ~/.claude/skills; the checkout lives outside the discovery root.
|
||||||
|
INSTALL_SKILLS_DIR="$HOME/.claude/skills"
|
||||||
|
INSTALL_GSTACK_DIR="$INSTALL_SKILLS_DIR/gstack"
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ "$INSTALL_CLAUDE" -eq 1 ]; then
|
||||||
|
migrate_direct_claude_install
|
||||||
|
fi
|
||||||
|
|
||||||
ensure_playwright_browser() {
|
ensure_playwright_browser() {
|
||||||
if [ "$IS_WINDOWS" -eq 1 ]; then
|
if [ "$IS_WINDOWS" -eq 1 ]; then
|
||||||
# On Windows, Bun can't launch Chromium due to broken pipe handling
|
# On Windows, Bun can't launch Chromium due to broken pipe handling
|
||||||
|
|
@ -817,6 +870,48 @@ create_codex_runtime_root() {
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# ─── Helper: create a minimal ~/.claude/skills/gstack runtime sidecar ────────
|
||||||
|
# Counterpart to create_codex_runtime_root, for relocated Claude installs.
|
||||||
|
# After the checkout moves to ~/.gstack/repos/gstack, skills and the ~9 bin/
|
||||||
|
# scripts that hard-code absolute paths like ~/.claude/skills/gstack/bin/
|
||||||
|
# gstack-config (the most-referenced path in the whole repo) still need them to
|
||||||
|
# resolve. Expose ONLY runtime assets here, and — unlike the Codex sidecar —
|
||||||
|
# NO nested SKILL.md at all: Claude's root skill is the separate _gstack-command
|
||||||
|
# alias and gstack-upgrade is a top-level skill, so the sidecar needs none. That
|
||||||
|
# keeps recursive scanners (Cursor walks ~/.claude/skills recursively) at zero
|
||||||
|
# phantom skills from this directory.
|
||||||
|
create_claude_runtime_root() {
|
||||||
|
local gstack_dir="$1"
|
||||||
|
local claude_gstack="$2"
|
||||||
|
|
||||||
|
if [ -L "$claude_gstack" ]; then
|
||||||
|
rm -f "$claude_gstack"
|
||||||
|
elif [ -d "$claude_gstack" ] && [ "$claude_gstack" != "$gstack_dir" ]; then
|
||||||
|
rm -rf "$claude_gstack"
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$claude_gstack" "$claude_gstack/browse" "$claude_gstack/design" \
|
||||||
|
"$claude_gstack/review" "$claude_gstack/plan-devex-review"
|
||||||
|
|
||||||
|
# Whole-dir runtime assets that contain no SKILL.md
|
||||||
|
for asset in bin scripts extension browse/dist browse/bin design/dist review/specialists; do
|
||||||
|
if [ -e "$gstack_dir/$asset" ]; then
|
||||||
|
_link_or_copy "$gstack_dir/$asset" "$claude_gstack/$asset"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Individual runtime files (NOT the whole skill dirs that hold their SKILL.md)
|
||||||
|
for file in ETHOS.md \
|
||||||
|
review/checklist.md review/design-checklist.md \
|
||||||
|
review/greptile-triage.md review/TODOS-format.md \
|
||||||
|
plan-devex-review/dx-hall-of-fame.md \
|
||||||
|
.feature-prompted-model-overlay .feature-prompted-continuous-checkpoint; do
|
||||||
|
if [ -f "$gstack_dir/$file" ]; then
|
||||||
|
_link_or_copy "$gstack_dir/$file" "$claude_gstack/$file"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
create_factory_runtime_root() {
|
create_factory_runtime_root() {
|
||||||
local gstack_dir="$1"
|
local gstack_dir="$1"
|
||||||
local factory_gstack="$2"
|
local factory_gstack="$2"
|
||||||
|
|
@ -1000,6 +1095,12 @@ if [ "$INSTALL_CLAUDE" -eq 1 ]; then
|
||||||
if [ -x "$GSTACK_RELINK" ]; then
|
if [ -x "$GSTACK_RELINK" ]; then
|
||||||
GSTACK_SKILLS_DIR="$INSTALL_SKILLS_DIR" GSTACK_INSTALL_DIR="$SOURCE_GSTACK_DIR" "$GSTACK_RELINK" >/dev/null 2>&1 || true
|
GSTACK_SKILLS_DIR="$INSTALL_SKILLS_DIR" GSTACK_INSTALL_DIR="$SOURCE_GSTACK_DIR" "$GSTACK_RELINK" >/dev/null 2>&1 || true
|
||||||
fi
|
fi
|
||||||
|
# Relocated install: build the runtime-only sidecar at ~/.claude/skills/gstack
|
||||||
|
# so absolute asset paths (bin/gstack-config, ETHOS.md, review/*, …) resolve
|
||||||
|
# while the full checkout stays out of the recursive discovery root. #1202/#1694
|
||||||
|
if [ "$_CLAUDE_RELOCATED" -eq 1 ]; then
|
||||||
|
create_claude_runtime_root "$SOURCE_GSTACK_DIR" "$INSTALL_SKILLS_DIR/gstack"
|
||||||
|
fi
|
||||||
# Backwards-compat alias: /connect-chrome → /open-gstack-browser
|
# Backwards-compat alias: /connect-chrome → /open-gstack-browser
|
||||||
_OGB_LINK="$INSTALL_SKILLS_DIR/connect-chrome"
|
_OGB_LINK="$INSTALL_SKILLS_DIR/connect-chrome"
|
||||||
if [ "$SKILL_PREFIX" -eq 1 ]; then
|
if [ "$SKILL_PREFIX" -eq 1 ]; then
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,102 @@
|
||||||
|
/**
|
||||||
|
* Regression coverage for #1202 / #1694 — a direct ~/.claude/skills/gstack
|
||||||
|
* checkout sits inside Claude/Cursor's recursive skill-discovery root, so every
|
||||||
|
* generated host-mirror and source SKILL.md gets indexed (hundreds of phantom
|
||||||
|
* skills; Claude drops descriptions, cursor-agent hard-freezes).
|
||||||
|
*
|
||||||
|
* setup's migrate_direct_claude_install + create_claude_runtime_root move the
|
||||||
|
* checkout to ~/.gstack/repos/gstack and leave a runtime-only sidecar with
|
||||||
|
* ZERO nested SKILL.md. These tests source those two functions straight out of
|
||||||
|
* ./setup and exercise them against an isolated fake HOME — no real install is
|
||||||
|
* touched and no full ./setup run (binary build / playwright) is needed.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { describe, test, expect } from 'bun:test';
|
||||||
|
import { spawnSync } from 'bun';
|
||||||
|
import { mkdtempSync, rmSync } from 'node:fs';
|
||||||
|
import { tmpdir } from 'node:os';
|
||||||
|
import { join } from 'node:path';
|
||||||
|
|
||||||
|
const SETUP = join(import.meta.dir, '..', 'setup');
|
||||||
|
|
||||||
|
// Run a bash snippet with the two setup functions sourced in, under a fresh
|
||||||
|
// fake HOME. Returns stdout (assertions are echoed as PASS/FAIL lines).
|
||||||
|
function runHarness(body: string): { out: string; code: number } {
|
||||||
|
const home = mkdtempSync(join(tmpdir(), 'gstack-reloc-'));
|
||||||
|
const script = `
|
||||||
|
set -u
|
||||||
|
export HOME='${home}'
|
||||||
|
log() { :; }
|
||||||
|
_link_or_copy() { ln -snf "$1" "$2"; }
|
||||||
|
eval "$(sed -n '/^migrate_direct_claude_install() {/,/^}/p' '${SETUP}')"
|
||||||
|
eval "$(sed -n '/^create_claude_runtime_root() {/,/^}/p' '${SETUP}')"
|
||||||
|
|
||||||
|
make_checkout() {
|
||||||
|
local r="$1"; rm -rf "$r"
|
||||||
|
mkdir -p "$r/.git" "$r/bin" "$r/scripts" "$r/browse/dist" "$r/review/specialists" "$r/plan-devex-review"
|
||||||
|
printf '#!/bin/sh\\necho stub\\n' > "$r/bin/gstack-config"; chmod +x "$r/bin/gstack-config"
|
||||||
|
: > "$r/ETHOS.md"; : > "$r/scripts/jargon-list.json"; : > "$r/review/checklist.md"
|
||||||
|
: > "$r/plan-devex-review/dx-hall-of-fame.md"
|
||||||
|
for s in office-hours ship review browse gstack-upgrade; do mkdir -p "$r/$s"; : > "$r/$s/SKILL.md"; done
|
||||||
|
mkdir -p "$r/.cursor/skills/x"; : > "$r/.cursor/skills/x/SKILL.md"
|
||||||
|
}
|
||||||
|
${body}
|
||||||
|
`;
|
||||||
|
const proc = spawnSync(['bash', '-c', script]);
|
||||||
|
rmSync(home, { recursive: true, force: true });
|
||||||
|
return { out: proc.stdout.toString(), code: proc.exitCode ?? 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('migrate_direct_claude_install + create_claude_runtime_root (#1202)', () => {
|
||||||
|
test('fresh global install relocates and builds a zero-SKILL.md sidecar', () => {
|
||||||
|
const { out } = runHarness(`
|
||||||
|
mkdir -p "$HOME/.claude/skills"
|
||||||
|
make_checkout "$HOME/.claude/skills/gstack"
|
||||||
|
SOURCE_GSTACK_DIR="$HOME/.claude/skills/gstack"; INSTALL_SKILLS_DIR="$HOME/.claude/skills"
|
||||||
|
LOCAL_INSTALL=0; BROWSE_BIN=""; _CLAUDE_RELOCATED=0
|
||||||
|
migrate_direct_claude_install
|
||||||
|
create_claude_runtime_root "$SOURCE_GSTACK_DIR" "$INSTALL_SKILLS_DIR/gstack"
|
||||||
|
echo "relocated=$_CLAUDE_RELOCATED"
|
||||||
|
echo "moved=$([ -d "$HOME/.gstack/repos/gstack/.git" ] && echo 1 || echo 0)"
|
||||||
|
echo "aliasRoot=$INSTALL_SKILLS_DIR"
|
||||||
|
echo "nestedSkillMd=$(find "$HOME/.claude/skills/gstack" -name SKILL.md | wc -l | tr -d ' ')"
|
||||||
|
echo "binResolves=$([ -x "$HOME/.claude/skills/gstack/bin/gstack-config" ] && echo 1 || echo 0)"
|
||||||
|
echo "ethosResolves=$([ -e "$HOME/.claude/skills/gstack/ETHOS.md" ] && echo 1 || echo 0)"
|
||||||
|
`);
|
||||||
|
expect(out).toContain('relocated=1');
|
||||||
|
expect(out).toContain('moved=1');
|
||||||
|
expect(out).toContain(`aliasRoot=`);
|
||||||
|
expect(out).toMatch(/aliasRoot=.*\/\.claude\/skills$/m);
|
||||||
|
expect(out).toContain('nestedSkillMd=0');
|
||||||
|
expect(out).toContain('binResolves=1');
|
||||||
|
expect(out).toContain('ethosResolves=1');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('re-run from the relocated path is idempotent and keeps the alias root', () => {
|
||||||
|
const { out } = runHarness(`
|
||||||
|
make_checkout "$HOME/.gstack/repos/gstack"
|
||||||
|
SOURCE_GSTACK_DIR="$HOME/.gstack/repos/gstack"; INSTALL_SKILLS_DIR="$HOME/.gstack/repos"
|
||||||
|
LOCAL_INSTALL=0; _CLAUDE_RELOCATED=0
|
||||||
|
migrate_direct_claude_install
|
||||||
|
echo "relocated=$_CLAUDE_RELOCATED"
|
||||||
|
echo "aliasRoot=$INSTALL_SKILLS_DIR"
|
||||||
|
echo "repoIntact=$([ -d "$HOME/.gstack/repos/gstack/.git" ] && echo 1 || echo 0)"
|
||||||
|
`);
|
||||||
|
expect(out).toContain('relocated=1');
|
||||||
|
expect(out).toMatch(/aliasRoot=.*\/\.claude\/skills$/m);
|
||||||
|
expect(out).toContain('repoIntact=1');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('--local installs are never relocated', () => {
|
||||||
|
const { out } = runHarness(`
|
||||||
|
mkdir -p "$HOME/.claude/skills"
|
||||||
|
make_checkout "$HOME/.claude/skills/gstack"
|
||||||
|
SOURCE_GSTACK_DIR="$HOME/.claude/skills/gstack"; LOCAL_INSTALL=1; _CLAUDE_RELOCATED=0
|
||||||
|
migrate_direct_claude_install
|
||||||
|
echo "relocated=$_CLAUDE_RELOCATED"
|
||||||
|
echo "stillThere=$([ -d "$HOME/.claude/skills/gstack/.git" ] && echo 1 || echo 0)"
|
||||||
|
`);
|
||||||
|
expect(out).toContain('relocated=0');
|
||||||
|
expect(out).toContain('stillThere=1');
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
Reference in New Issue