Claude Skill

setup

Post-install setup for the oss plugin. Run once after installing on a new machine, or after a plugin version upgrade, to deliver this plugin's rules/*.md into ~/.claude/rules/ as namespaced symlinks. TRIGGER when: user installed or upgraded the oss plugin and its rules are not lo

LLM Mart · 0 points · 15 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download Borda-AI-Rig-plugins_cc_oss_skills_setup-39e3a48.zip · 4 KB
borda/ai-rig 27 4 forks Apache-2.0 Updated 2d ago
Part of borda/ai-rig — 82 skills

Install

skills CLI npx skills add https://github.com/Borda/AI-Rig/tree/main/plugins/cc_oss/skills/setup
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install borda-ai-rig@llmmart
Git git clone https://github.com/Borda/AI-Rig.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole borda/ai-rig collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

Files (ai-rig)
  • SKILL.md 10 KB
    ---
    name: setup
    description: "Post-install setup for the oss plugin. Run once after installing on a new machine, or after a plugin version upgrade, to deliver this plugin's rules/*.md into ~/.claude/rules/ as namespaced symlinks and merge its own permissions.allow and permissions.deny entries into ~/.claude/settings.json. TRIGGER when: user installed or upgraded the oss plugin and its rules are not loading, or its deny rules are not blocking; phrases: 'set up oss', 'oss rules not loading', 'merge oss permissions', 'after upgrading oss'. SKIP: statusLine/TEAM_PROTOCOL/plugin-cache setup (use /foundry:setup (requires `foundry` plugin)); editing rule content (edit the plugin source)."
    argument-hint: '[--approve]'
    allowed-tools: Bash, AskUserQuestion
    effort: low
    model: sonnet
    ---
    
    <objective>
    
    Deliver oss's rules to Claude's user-level rule namespace, and its own permission rules to Claude's settings.
    
    | Action | What happens |
    | -- | -- |
    | `rules/*.md` → `~/.claude/rules/oss-<name>.md` | symlink |
    | Stale link from an older oss version | refreshed silently |
    | Link whose source left the plugin | removed |
    | Real file or foreign link at a destination | preserved, reported as conflict |
    | Anything outside `~/.claude/rules/` | never touched |
    | `.claude-plugin/permissions-{allow,deny}.json` → `~/.claude/settings.json` | merged additively; nothing removed |
    | Any other key of `~/.claude/settings.json` | never touched |
    
    **Why namespaced?** Claude loads user rules from one flat directory. Four plugins ship a `rules/quality-gates.md`; installing source basenames would collide. Every rule installs as `<plugin>-<source-name>.md`, so `quality-gates.md` becomes `oss-quality-gates.md`. The prefix is inert — verified against Claude Code 2.1.220 that a filename prefix changes neither unconditional loading nor `paths:` frontmatter matching.
    
    **Why symlink, not copy?** Rules load at session start. A symlink serves the installed version after every upgrade; a copy silently serves stale content forever.
    
    **Why does oss deliver only its own rules?** Each plugin installs independently. A plugin that shipped a sibling's rules would break standalone installation and couple releases.
    
    NOT for: statusLine, `TEAM_PROTOCOL.md`, or plugin-cache purging — those are `/foundry:setup` (requires `foundry` plugin). Of `~/.claude/settings.json` only `permissions.allow` and `permissions.deny` arrays are touched, only additively. Writes nothing under `~/.codex/`.
    
    </objective>
    
    <inputs>
    
    - **No arguments** — interactive; prompts before replacing a conflicting destination.
    - **`--approve`** — non-interactive; replaces conflicting destinations without asking. Used by `make sync-claude`.
    
    </inputs>
    
    <workflow>
    
    ## Step 0: Flags
    
    Parse `$ARGUMENTS` for `--approve` (case-insensitive) → `APPROVE_ALL=true`, else `false`. Any other `--<token>` → print `` ! Unknown flag(s): `--<token>`. Supported: `--approve`. `` and stop.
    
    ## Step 1: Python
    
    ```bash
    PYTHON_CMD=""
    for c in python python3; do
        command -v "$c" >/dev/null 2>&1 && "$c" --version 2>/dev/null | grep -qE "Python 3\.(1[0-9]|[2-9][0-9])" && PYTHON_CMD="$c" && break
    done
    if [ -z "$PYTHON_CMD" ] && command -v py >/dev/null 2>&1 && py -3 --version 2>/dev/null | grep -qE "Python 3\.(1[0-9]|[2-9][0-9])"; then
        PYTHON_CMD="py -3"
    fi
    [ -z "$PYTHON_CMD" ] && { printf "! Python 3.10+ not found — install it and re-run /oss:setup\n"; exit 1; }
    printf "  Python: %s\n" "$PYTHON_CMD"
    ```
    
    ## Step 2: Dry run — see what would change
    
    `$CLAUDE_PLUGIN_ROOT` is the installed plugin version. `sync_rules.py` re-validates it (manifest exists, parses, declares `oss`; `rules/` is a real directory holding at least one non-empty regular `*.md`) and aborts before touching anything if any check fails.
    
    ```bash
    PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-plugins/cc_oss}"
    python "$PLUGIN_ROOT/bin/sync_rules.py" --plugin-name oss --plugin-root "$PLUGIN_ROOT" --dry-run  # timeout: 15000
    ```
    
    Non-zero exit → print stderr verbatim and stop; nothing was modified.
    
    ## Step 3: Apply
    
    Run without `--dry-run`. Add `--approve` only when `APPROVE_ALL=true`:
    
    ```bash
    PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-plugins/cc_oss}"
    python "$PLUGIN_ROOT/bin/sync_rules.py" --plugin-name oss --plugin-root "$PLUGIN_ROOT"  # timeout: 15000
    ```
    
    Output lines, one per destination: `linked:` · `unchanged:` · `replaced (--approve):` · `removed obsolete:` · `conflict, kept as-is:` · `FAILED:`.
    
    Ownership proved before any replace or remove: existing link must resolve under current plugin root, or under same `~/.claude/plugins/cache/<marketplace>/oss/` lineage as current install. A link into another marketplace, another plugin, a source checkout, or a dotfiles tree is never adopted — path substrings are not evidence of ownership.
    
    ## Step 4: Conflicts
    
    No `conflict, kept as-is:` lines → skip to Step 5.
    
    `APPROVE_ALL=true` → conflicts were already replaced in Step 3; skip to Step 5.
    
    Otherwise invoke `AskUserQuestion`, listing each conflicting destination and its current state:
    
    - (a) **Keep them** — those rules stay undelivered
    - (b) **Replace all with plugin links** ★ recommended — existing content is overwritten
    - (c) **Abort** — leave everything as it is
    
    On **(b)**, re-run Step 3's command with `--approve` appended and report the resulting `replaced (--approve):` lines. On (a) or (c), report which rules remain undelivered.
    
    ## Step 5: Merge permissions.allow and permissions.deny
    
    This plugin ships its own `permissions-allow.json` and `permissions-deny.json`. Claude Code doesn't read them from plugin manifest, so without this step they're inert files — allow entries never suppress a prompt, deny entries never block anything.
    
    Merge is additive and idempotent: `unique` keeps entries already present from being duplicated, and no entry is ever removed. Each plugin merges only its own pair.
    
    Create the file on first install, back it up before any write — a standalone install may reach this step with no `~/.claude/settings.json` at all:
    
    ```bash
    export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
    SETUP_BAK_TS=$(date -u +%Y-%m-%dT%H-%M-%SZ)
    echo "$SETUP_BAK_TS" > "${TMPDIR:-/tmp}/oss-setup-bak-ts-${CSID}"
    [ -f ~/.claude/settings.json ] || printf '{}\n' > ~/.claude/settings.json  # created in-bash — no Write-tool prompt, headless-safe
    cp ~/.claude/settings.json "$HOME/.claude/settings.json.bak-${SETUP_BAK_TS}"  # timeout: 5000
    ```
    
    Report: "Backed up ~/.claude/settings.json → ~/.claude/settings.json.bak-<timestamp>"
    
    Writes merged `permissions.allow` array:
    
    ```bash
    export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
    PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-plugins/cc_oss}"
    _jq_result=$(jq --slurpfile perms "$PLUGIN_ROOT/.claude-plugin/permissions-allow.json" \
        '.permissions.allow = ((.permissions.allow // []) + $perms[0] | unique)' \
        ~/.claude/settings.json)  # timeout: 5000
    [ $? -eq 0 ] && [ -n "$_jq_result" ] && printf '%s\n' "$_jq_result" > "${TMPDIR:-/tmp}/oss_setup_tmp.json-${CSID}" && mv "${TMPDIR:-/tmp}/oss_setup_tmp.json-${CSID}" ~/.claude/settings.json || { printf "! jq failed merging permissions.allow — settings.json unchanged\n"; exit 1; }
    ```
    
    Report: "Added N new permissions.allow entries (M already present)."
    
    Writes merged `permissions.deny` array:
    
    ```bash
    export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
    PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-plugins/cc_oss}"
    _jq_result=$(jq --slurpfile deny "$PLUGIN_ROOT/.claude-plugin/permissions-deny.json" \
        '.permissions.deny = ((.permissions.deny // []) + $deny[0] | unique)' \
        ~/.claude/settings.json)  # timeout: 5000
    [ $? -eq 0 ] && [ -n "$_jq_result" ] && printf '%s\n' "$_jq_result" > "${TMPDIR:-/tmp}/oss_setup_tmp.json-${CSID}" && mv "${TMPDIR:-/tmp}/oss_setup_tmp.json-${CSID}" ~/.claude/settings.json || { printf "! jq failed merging permissions.deny — settings.json unchanged\n"; exit 1; }
    ```
    
    Report: "Added N new permissions.deny entries (M already present)."
    
    Deny wins over allow in Claude Code, so merging both in either order yields the same effective policy.
    
    Validate what was written; settings.json that no longer parses is restored from the backup taken above:
    
    ```bash
    jq empty ~/.claude/settings.json  # timeout: 5000
    ```
    
    Zero exit → continue to Step 6. Non-zero exit → restore, report the failure, and stop:
    
    ```bash
    export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
    IFS= read -r SETUP_BAK_TS < "${TMPDIR:-/tmp}/oss-setup-bak-ts-${CSID}" 2>/dev/null || SETUP_BAK_TS=$(ls -t "$HOME/.claude/settings.json.bak-"* 2>/dev/null | head -1 | sed 's/.*\.bak-//')
    cp "$HOME/.claude/settings.json.bak-${SETUP_BAK_TS}" ~/.claude/settings.json  # timeout: 5000
    ```
    
    ## Step 6: Report
    
    - Rules linked: N → `~/.claude/rules/oss-*.md`
    - Unchanged: N · Obsolete removed: N · Conflicts kept: N (name each)
    - Permissions: N allow entries added, N deny entries added
    - Failures: N (name each; a failure means the platform refused the symlink — rules are never copied as a fallback)
    
    </workflow>
    
    <notes>
    
    **Upgrade path**: `claude plugin install oss@borda-ai-rig` then `/oss:setup`. Links from the previous version share the install-cache lineage, so they refresh without prompting; a rule dropped in the new version has its link removed. `make sync-claude` runs `/oss:setup --approve` headlessly for every installed managed plugin that ships a setup skill, so a normal sync needs no manual step.
    
    **Uninstall leaves state behind**: Claude Code runs no cleanup hook on uninstall, neither `claude plugin uninstall` nor `make clear-all` removes what setup created. After removing the plugin, delete `~/.claude/rules/oss-*.md` by hand — they become dangling symlinks once plugin cache version is gone.
    
    **Testing**: setup is reachable only as `/oss:setup` after the plugin is installed. To exercise it locally, bump `version` in `plugins/cc_oss/.claude-plugin/plugin.json`, run `claude plugin install oss@borda-ai-rig` from the repo root to refresh the cache, then invoke the skill. `bin/sync_rules.py` is a byte-identical propagated copy of the canonical helper; its regression suite lives beside that canonical copy in the AI-Rig repository.
    
    **Follow-up gate omitted** — setup is one-shot; Step 6 is terminal output.
    
    </notes>
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related