Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packaging/curl-installer/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ IFS=$'\n\t'
# AGENTLINUX_RELEASE_BASE is the test-mode seam consumed by 60-curl-installer.bats;
# when set it REPLACES the github.com base URL entirely, including the path.
# ------------------------------------------------------------------------------
: "${ORG:=agentlinux}"
: "${ORG:=Roo4L}"
: "${AGENTLINUX_ORG:=$ORG}" # alias for readability in docs
: "${AGENTLINUX_RELEASE_BASE:=}"
: "${AGENTLINUX_VERSION:=}"
Expand Down
11 changes: 10 additions & 1 deletion plugin/bin/agentlinux-install
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,11 @@ run_purge() {
# would resolve to `sudo -u agent -H -E -- -- bash <recipe>` and sudo
# would try to exec `--` ("command not found"). Pass the command
# verbatim without our own terminator.
as_user agent bash "$recipe" || log_warn "uninstall.sh for ${id} failed (continuing)"
# Recipes guard on `${AGENTLINUX_AGENT_HOME:?}` (set by runner.ts on
# normal install/remove). On --purge runner.ts is gone; provide it
# explicitly so the guard does not trip.
AGENTLINUX_AGENT_HOME=/home/agent \
as_user agent bash "$recipe" || log_warn "uninstall.sh for ${id} failed (continuing)"
else
log_warn "no uninstall.sh for ${id} at ${recipe}; skipping"
fi
Expand All @@ -261,6 +265,11 @@ run_purge() {
rm -f /etc/agentlinux.env
rm -f /etc/cron.d/agentlinux

# Step 3.5: sudoers drop-in placed by 20-sudoers.sh (Phase 5.1 / ADR-012).
# Symmetric uninstall must remove the NOPASSWD grant — leaving it behind
# orphans the privilege after the agent user is gone.
rm -f /etc/sudoers.d/agentlinux

# Step 4: NodeSource apt repo files placed by 30-nodejs.sh.
# Both filenames (deb822 + legacy) handled — setup_22.x may have created
# either depending on Ubuntu version / prior partial migrations.
Expand Down
31 changes: 30 additions & 1 deletion plugin/catalog/agents/gsd/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,33 @@ if ! printf '%s' "$banner" | grep -q -F "v${AGENTLINUX_PINNED_VERSION}"; then
exit 1
fi

echo "gsd: install complete (resolves at ${bin_path}; banner matches pin)"
## get-shit-done-cc is the BOOTSTRAPPER, not the slash-commands themselves.
## After npm install the binary lives on PATH but Claude Code does not yet
## see any /gsd-* commands or skills. The bootstrapper has to be invoked
## with --global --claude to copy the GSD skill set into ~/.claude/skills/
## (122+ skill dirs, hooks, statusline, settings) — that is what makes
## /gsd-* commands surface inside Claude Code.
##
## Discovered by dogfood: a fresh AgentLinux + `agentlinux install gsd`
## left ~/.claude/skills/gsd-* empty, so the user ran Claude Code and saw
## zero GSD commands. The recipe was technically correct (npm install
## succeeded, binary on PATH, banner matched pin) but the user-visible
## intent ("install GSD") was not satisfied.
## Wrap the bootstrapper non-fatally so the recipe stays idempotent on
## re-runs / `--force`. Upstream may exit non-zero on "already installed"
## paths or on partial-state recovery; what we actually care about is that
## the skill set ends up under ~/.claude/skills/ — verified below.
echo "gsd: wiring GSD skill set into ~/.claude/ via get-shit-done-cc --global --claude"
get-shit-done-cc --global --claude \
|| echo "gsd install: bootstrapper exited non-zero (re-run / partial-state path); verifying skill dirs anyway" >&2

# Sanity-check that at least one gsd-* skill dir landed where Claude Code
# looks. Without this assertion a regression to "binary on PATH but
# bootstrapper never copied skills" would silently slip through.
skill_dir="${AGENTLINUX_AGENT_HOME:-/home/agent}/.claude/skills"
if ! find "$skill_dir" -maxdepth 1 -type d -name 'gsd-*' -print -quit 2>/dev/null | grep -q .; then
printf 'gsd install: no gsd-* skill dirs under %s after bootstrapper run\n' "$skill_dir" >&2
exit 1
fi

echo "gsd: install complete (resolves at ${bin_path}; banner matches pin; skill set wired into ${skill_dir}/gsd-*)"
35 changes: 31 additions & 4 deletions plugin/catalog/agents/gsd/uninstall.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,41 @@
set -euo pipefail
# gsd uninstall.sh — symmetric inverse. npm uninstall -g is idempotent.

: "${AGENTLINUX_AGENT_HOME:?AGENTLINUX_AGENT_HOME not set}"

echo "gsd: removing get-shit-done-cc"

# npm uninstall -g on a missing package exits 0 with "up to date" — idempotent.
# We don't check npm's exit status aggressively; the post-step `command -v`
# check is the real truth.
# Step 1: ask the bootstrapper to undo what install.sh wired into ~/.claude/.
# Mirrors the install path's `--global --claude` invocation. Failure is
# non-fatal — the bootstrapper may be a future version that drops the flag
# or the user may have already removed bits manually; the defensive cleanup
# below catches whatever remains.
if command -v get-shit-done-cc >/dev/null 2>&1; then
get-shit-done-cc --global --claude --uninstall \
|| echo "gsd uninstall: bootstrapper --uninstall returned non-zero (continuing)" >&2
fi

# Step 2: defensive cleanup of GSD-installed Claude Code state. The
# bootstrapper's `--uninstall` flag is best-effort (older versions don't
# support it; failure modes leave skill dirs behind). install.sh's comment
# block notes that --global --claude writes "skill dirs, hooks, statusline,
# settings". We sweep the skills (deterministic, dir-naming convention) and
# leave settings.json + hooks alone (user-edited surface; touching it could
# clobber non-GSD config). The user can `rm ~/.claude/settings.json` if they
# want a clean slate.
find "${AGENTLINUX_AGENT_HOME}/.claude/skills" -maxdepth 1 -type d -name 'gsd-*' \
-exec rm -rf {} + 2>/dev/null \
|| true

# Step 3: npm uninstall -g on a missing package exits 0 with "up to date"
# — idempotent. Real truth check is `command -v` below.
npm uninstall -g get-shit-done-cc --no-fund --no-audit >/dev/null 2>&1 || true

# Verify removal.
# Verify removal. `hash -r` clears bash's command-name cache — without it,
# the prior `get-shit-done-cc --uninstall` invocation hashed the binary's
# path and `command -v` reports it as still-resolvable even after npm
# uninstall -g has deleted the file from disk.
hash -r
if command -v get-shit-done-cc >/dev/null 2>&1; then
echo "gsd uninstall: get-shit-done-cc still on PATH after npm uninstall -g" >&2
exit 1
Expand Down
76 changes: 76 additions & 0 deletions plugin/catalog/agents/playwright-cli/install.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#!/usr/bin/env bash
set -euo pipefail
# playwright-cli install.sh — Microsoft's @playwright/cli for coding agents.
#
# Two-part install:
# (1) npm install -g @playwright/cli@$PIN — bootstrapper binary at
# ~agent/.npm-global/bin/playwright-cli
# (2) playwright-cli install --skills — wires the bundled Claude
# Code skill into
# ~/.claude/skills/playwright-cli/
#
# Discovered by user dogfood: npm-installing the package alone leaves the
# binary on PATH but Claude Code sees no /playwright-cli skills. The
# `--skills` invocation is what makes the user-visible intent ("install
# Playwright CLI for the agent") work end-to-end.
#
# References:
# - https://playwright.dev/agent-cli/installation
# - https://www.npmjs.com/package/@playwright/cli
# - npm view @playwright/cli bin → { 'playwright-cli': 'playwright-cli.js' }

: "${AGENTLINUX_PINNED_VERSION:?AGENTLINUX_PINNED_VERSION not set}"
: "${AGENTLINUX_AGENT_HOME:?AGENTLINUX_AGENT_HOME not set}"

echo "playwright-cli: installing @playwright/cli@${AGENTLINUX_PINNED_VERSION}"

npm install -g \
--omit=dev \
--no-fund \
--no-audit \
"@playwright/cli@${AGENTLINUX_PINNED_VERSION}"

bin_path=$(command -v playwright-cli || true)
if [[ -z "$bin_path" ]]; then
echo "playwright-cli install: playwright-cli not on PATH after npm install -g" >&2
exit 1
fi

# Verify CLI version matches pin before invoking the skill bootstrapper.
pw_version=$(playwright-cli --version 2>&1 | head -1 | tr -d '[:space:]')
if [[ "$pw_version" != "${AGENTLINUX_PINNED_VERSION}" ]]; then
printf 'playwright-cli install: pinned=%s but --version: %s\n' \
"${AGENTLINUX_PINNED_VERSION}" "$pw_version" >&2
exit 1
fi

echo "playwright-cli: CLI at ${bin_path}, version ${pw_version}"
echo "playwright-cli: wiring Claude Code skill via 'playwright-cli install --skills'"

# Bootstrap the bundled Claude Code skill into ~/.claude/skills/.
# Non-fatal: upstream may exit non-zero on re-runs / "already installed"
# paths; what we actually care about is that the skill landed on disk —
# verified below.
#
# Must run from a writable CWD: `playwright-cli install` calls
# initWorkspace() which mkdirs ./.playwright in the current directory.
# AgentLinux dispatches recipes from /opt/agentlinux-src/ (read-only repo
# copy in Docker / read-only workspace in QEMU), so a bare invocation
# crashes with EACCES on .playwright. Anchor CWD to agent-home (always
# writable, agent-owned) so the workspace dir lives at
# /home/agent/.playwright — a per-user side-effect that purge cleans via
# `userdel -r agent`.
( cd "${AGENTLINUX_AGENT_HOME}" && playwright-cli install --skills ) \
|| echo "playwright-cli install: bootstrapper exited non-zero (re-run / partial-state); verifying skill anyway" >&2

# Sanity-check the skill landed where Claude Code looks for it. Anchor
# the match on `playwright-cli` (mirrors install side) — a broader
# `*playwright*` would match unrelated user-installed skills.
skill_dir="${AGENTLINUX_AGENT_HOME}/.claude/skills"
mkdir -p "$skill_dir"
if ! find "$skill_dir" -maxdepth 1 -type d -name 'playwright-cli*' -print -quit 2>/dev/null | grep -q .; then
printf 'playwright-cli install: no playwright-cli skill found under %s after bootstrapper run\n' "$skill_dir" >&2
exit 1
fi

echo "playwright-cli: install complete (binary at ${bin_path}; skill wired into ${skill_dir}/playwright-cli)"
45 changes: 45 additions & 0 deletions plugin/catalog/agents/playwright-cli/uninstall.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#!/usr/bin/env bash
set -euo pipefail
# playwright-cli uninstall.sh — symmetric inverse of install.sh.
#
# Order matters:
# 1. Tear down the wired Claude Code skill (mirror of `--skills` install)
# 2. npm uninstall -g @playwright/cli
# 3. hash -r so command -v reflects on-disk state, not bash's cache

: "${AGENTLINUX_AGENT_HOME:?AGENTLINUX_AGENT_HOME not set}"

echo "playwright-cli: removing @playwright/cli + Claude Code skill"

# Step 1: best-effort skill teardown via the bootstrapper itself. Some
# upstream versions support a symmetric --uninstall flag; if absent or it
# returns non-zero, we still proceed with the npm uninstall + defensive
# skill directory cleanup below. We do NOT swallow stderr — the tee
# transcript should preserve the actual upstream error if there is one.
if command -v playwright-cli >/dev/null 2>&1; then
playwright-cli install --skills --uninstall \
|| playwright-cli uninstall --skills \
|| echo "playwright-cli uninstall: bootstrapper teardown returned non-zero (continuing)" >&2
fi

# Step 2: defensive removal of the playwright-cli skill dirs under
# ~/.claude/skills/. Anchor the match on `playwright-cli` (mirroring the
# install side) so an unrelated user-authored `~/.claude/skills/playwright-
# notes/` is NOT collateral damage. `-name` (not `-iname`) is sufficient
# because upstream's skill dir is conventionally lower-case-kebab.
find "${AGENTLINUX_AGENT_HOME}/.claude/skills" -maxdepth 1 -type d -name 'playwright-cli*' \
-exec rm -rf {} + \
|| true

# Step 3: npm uninstall -g. Idempotent on missing package.
npm uninstall -g @playwright/cli --no-fund --no-audit >/dev/null 2>&1 || true

# Step 4: clear bash's command hash so command -v reflects on-disk state.
hash -r

if command -v playwright-cli >/dev/null 2>&1; then
echo "playwright-cli uninstall: playwright-cli still on PATH after npm uninstall -g" >&2
exit 1
fi

echo "playwright-cli: uninstall complete"
78 changes: 0 additions & 78 deletions plugin/catalog/agents/playwright/install.sh

This file was deleted.

22 changes: 0 additions & 22 deletions plugin/catalog/agents/playwright/uninstall.sh

This file was deleted.

16 changes: 8 additions & 8 deletions plugin/catalog/catalog.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,18 @@
"tags": ["workflow", "productivity"]
},
{
"id": "playwright",
"display_name": "Playwright",
"description": "Browser automation framework (canonical browser-access tool per ADR; replaces v0.2.0's chrome-devtools-mcp).",
"homepage": "https://playwright.dev/",
"id": "playwright-cli",
"display_name": "Playwright CLI",
"description": "Microsoft's token-efficient Playwright command-line tool for coding agents — exposes browser automation via concise CLI commands and bundles a Claude Code skill.",
"homepage": "https://playwright.dev/agent-cli/installation",
"license": "Apache-2.0",
"source_kind": "npm",
"npm_package_name": "playwright",
"pinned_version": "1.59.1",
"npm_package_name": "@playwright/cli",
"pinned_version": "0.1.11",
"install_recipe_path": "install.sh",
"uninstall_recipe_path": "uninstall.sh",
"post_install_verify": "npx playwright --version",
"tags": ["browser", "automation"]
"post_install_verify": "command -v playwright-cli && playwright-cli --version",
"tags": ["browser", "automation", "agent-skill"]
},
{
"id": "test-dummy",
Expand Down
Loading
Loading