agentleFS
scolladonscolladon/craft/skills/promote-config/SKILL.md

promote-config

Skill2 starsChanged 34 days agoMITView on GitHub

Relocate a named craft config between scopes — promote local-scope to user-scope by default, or demote user-scope to local-scope with --demote. Triggers — "craft:promote-config", "promote a named config", "promote a craft config to user scope", "move a craft config to user scope", "demote a craft config to local scope".

The file

We split this skill into the rules it gives an agent

4 rules. About 0 are common, 2 are distinctive, and 2 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈2

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 2

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    If either test fails, surface a diagnostic and stop — the plugin installation is incomplete.

    line 30
  2. Very specific:

    Every decision — direction, source-existence, destination-exists refuse-vs-force, and $HOME-containment — is made by promote-plan.js, never …

    line 46
  3. Very specific:

    Ensure the destination directory exists, then copy the source bytes into a mktemp file created INSIDE that same directory — trailing-X …

    line 71
  4. Distinctive:

    Remove the source so the config lives at exactly one scope:

    line 106

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

craft:promote-config — relocate a named config between scopes

Standalone, session-owned skill. You (the session) ask the plan computer where the config should go, stage the source bytes at that destination, land through the existing lint-then-move bin, and finalize by removing the source. No worker agent is spawned.

Input: $ARGUMENTS — <name> (required), plus optional --demote and --force.

This is a MOVE by default: the source is removed once the destination copy lands clean, so the config lives at exactly one scope afterward, never both.


Preamble — plugin-root probe (read-only)

Confirm both entrypoints this skill composes exist:

test -f "${CRAFT_ROOT:-${CLAUDE_PLUGIN_ROOT}}/engine/bin/promote-plan.js"
test -f "${CRAFT_ROOT:-${CLAUDE_PLUGIN_ROOT}}/engine/bin/init-land.js"

If either test fails, surface a diagnostic and stop — the plugin installation is incomplete.

Parse $ARGUMENTS:

  • <name> — required. Absent ⇒ STOP: "a config name is required".
  • --demote — direction flag. Absent ⇒ promote (local→user, the default). Present ⇒ demote (user→local).
  • --force — overwrite flag, forwarded verbatim when the caller supplied it.

Procedure

Step 1 — Plan

Every decision — direction, source-existence, destination-exists refuse-vs-force, and $HOME-containment — is made by promote-plan.js, never re-derived here:

plan_out="$(node "${CRAFT_ROOT:-${CLAUDE_PLUGIN_ROOT}}/engine/bin/promote-plan.js" "$name" [--demote] [--force])"

(append --demote and/or --force only when the caller passed them).

Non-zero exit ⇒ STOP — surface the stderr diagnostic verbatim (no source config to relocate, a destination that already exists without --force, a $HOME containment escape, or an invalid name). Nothing is touched.

On exit 0, parse the three key=value lines from $plan_out:

source=<absolute source path>
dest=<absolute destination path>
scope=<user|local — the destination scope>

Bind source, dest, scope from these lines.

Step 2 — Stage

Ensure the destination directory exists, then copy the source bytes into a mktemp file created INSIDE that same directory — trailing-X template built only from the already-validated $name:

dest_dir="$(dirname "$dest")"
mkdir -p "$dest_dir"
manifest_tmp="$(mktemp "$dest_dir/.craft-${name}.tmp.XXXXXX")"
cp "$source" "$manifest_tmp"

Reuse $manifest_tmp verbatim from here on — never re-splice $name into a fresh path. mktemp's unpredictable suffix plus its O_EXCL creation is what closes the TOCTOU window between lint and move.

Step 3 — Land

Delegate the atomic lint-then-move to the existing land bin — the same one craft:init uses:

node "${CRAFT_ROOT:-${CLAUDE_PLUGIN_ROOT}}/engine/bin/init-land.js" "$manifest_tmp" "$name" --scope "$scope"

init-land.js lints $manifest_tmp at the destination root before it moves anything, so a config referencing a repo-relative path (e.g. context: docs/x.md) is REFUSED when promoted to ~/.claude — a user-scope config must be self-contained.

Non-zero exit ⇒ STOP — surface the stderr diagnostic; remove $manifest_tmp; the source is untouched (the relocation never reaches Step 4).

Step 4 — Finalize (move)

On exit 0 from Step 3, the destination copy already lints clean and is in place. Remove the source so the config lives at exactly one scope:

rm "$source"

Report the relocation with the greppable token (defined ONLY here — it does not join the skills/run/SKILL.md token family):

PROMOTE-CONFIG(<name>): local→user

or, for a --demote run:

PROMOTE-CONFIG(<name>): user→local

Error semantics

ConditionBehaviour
Missing <name>STOP: "a config name is required"
No source config at the from-scopeSTOP; promote-plan.js names the missing scope and name on stderr; nothing touched
Destination already exists, no --forceSTOP; promote-plan.js refuses with a destination-exists diagnostic naming the --force escape hatch
Destination already exists, --force passedLand proceeds; init-land.js's atomic rename overwrites the prior destination file
Source or destination user-scope path escapes $HOME containmentSTOP; promote-plan.js reports a containment failure; nothing touched
Ref-bearing / non-portable source configinit-land.js REFUSES at the destination lint before any move; $manifest_tmp is removed; source is untouched
Plugin root missing (promote-plan.js or init-land.js absent)STOP; surface which entrypoint is missing; do not invoke either bin

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.