initialise-skills
Populate and keep accurate the per-skill
files that the shared
agent skills (
,
,
,
,
,
…) read at runtime. Run inside the host repo, it detects repo facts, maps them
onto each installed skill's config schema, and writes accurate configs — without
ever clobbering a value a human deliberately set.
It is dry-run first and idempotent: the first step always previews the
diff, writes happen only after you confirm, and a re-run with nothing new to
detect leaves every file byte-for-byte unchanged.
How it decides what to write
For each installed skill it loads two things: the skill's own
(which defines the
set of keys to reconcile) and the
existing
(which may be absent on a fresh install). Each key is then
classified by a three-way comparison — example placeholder vs existing value vs
detected value:
| Status | Meaning | Action |
|---|
| No value yet, or still the example placeholder | Write the detected value |
| Existing value already equals what we detected | No-op |
| A real value that differs from detection — a deliberate edit | Keep it; report both values |
| No detector and no value (e.g. a Linear slug with no MCP) | Leave for you to supply |
| A real value we have no detector for | Keep it |
| A key in no skill template knows about | Keep it, untouched |
Detection is keyed by config-
key name, not by skill, so one detector serves
every skill that uses a key (one
detector covers
,
; one
detector covers
,
,
). See
references/detectable-keys.md
for the full table of keys, their detection sources, and fallbacks.
is intentionally skipped: it self-detects its base branch and
workspaces and reads an
optional at the repo root, not
an in-bundle
— so there is nothing for this skill to populate. (Its
one trace here is the
step below: when preflight is installed, its
scratch output is added to the repo's
.)
The step
The
skill writes
to the repo root on every
real run, so without an ignore rule it surfaces as an untracked change after a
run. When
is installed, this skill ensures the host repo's
root
excludes it — the
one mutation it makes outside a skill's
. The edit is
append-only and idempotent: it adds the commented
entry only when absent (creating
if there is none), and never
reorders or removes existing lines. The dry-run report shows the pending edit
(
); a re-run after writing reports
.
The step
Alongside the config reconcile, this skill emits a committed
at the repo root — a machine-readable inventory of which skill versions are installed
and where they came from:
json
{ "source": "https://github.com/acme-skunkworks/agent-skills", "ref": "main",
"skills": { "changelog": "1.2.0", "send-it": "2.1.3", "…": "…" } }
- — a full inventory of every installed bundle (including
and this skill), read from each . Keys are sorted, so
a re-run with no version changes is a byte-stable no-op (the file only rewrites
when a version actually moves). The lock lives at the fixed
path regardless of where the bundles were vendored (, ,
), and consumers commit it.
- / — provenance the script cannot derive (skills.sh records
nowhere where a consumer installed from). Supply them as /
in the write step's stdin (see step 2); an existing lock's values
are preserved when omitted. When neither is available the field is written as
and the report flags it () — never fabricated.
This is the foundation for detecting which repos are behind — see
Checking for updates below.
Process
-
Dry run. From the host repo root, run the bundled script for a machine-readable preview:
bash
node <skills-dir>/initialise-skills/scripts/initialise.mjs --dry-run --json
is wherever the bundles are installed (e.g.
,
,
); the script auto-detects its siblings
relative to its own location, so usually you can just run it from the repo
root. Parse the JSON:
with per-key
, plus
,
, and
.
-
Fill the facts. For each
Linear key
(
,
), fetch the value via the Linear MCP
when it is available —
mcp__linear-server__list_teams
for the team name, and
the workspace slug from the team/organisation — otherwise ask the user. Collect
these into a
object. Also add the
lock provenance here:
(the source repo the skills were installed from — the
agent-skills repo URL) and
(the ref installed from;
default ,
the fleet convention, unless a tag/SHA was pinned). Skip either when an existing
already records it — its value is preserved.
-
Present the diff and confirm. Show the human report (re-run without
, or render the parsed JSON). Call out the
keys that will be
written, the
keys that will be kept, and the
keys.
For each key, ask whether to accept the detected value (the per-key
opt-in). Gather the accepted ones into an
map keyed by skill name,
e.g.
{ "changelog": ["issueKeys"] }
. This is the confirmation gate — do not
write before it.
-
Write. Re-run with
, piping the gathered facts and drift opt-ins
as stdin JSON:
bash
echo '{"facts":{"linearTeamName":"…","linearWorkspaceSlug":"…","lockSource":"https://github.com/acme-skunkworks/agent-skills","lockRef":"main"},"acceptDrift":{"changelog":["issueKeys"]}}' \
| node <skills-dir>/initialise-skills/scripts/initialise.mjs --write --json
Report what was written from the returned
, plus the
field
(its
—
,
,
, or
; the field is absent
entirely when
isn't installed, as the
step is skipped)
and the
field (its
—
,
, or
;
means
/
still need supplying).
-
Confirm idempotency. Run the dry run once more; every key should now be
(apart from drifts you chose to keep and any still-missing manual
values). When
is installed,
should be
(or
, if the repo deliberately un-ignores the file — also a stable
no-op); when it isn't, the
step is skipped and there's no
field to check.
should be
. This proves the
configs, the
, and the
are stable and a future re-run
is a no-op.
-
GitHub App & token check. If this repo will run the shared Claude workflows
(
and their caller stubs), the GitHub App must be installed
and the
repository Actions secret set — the workflows
authenticate with it and fail on an empty token (A-646). The required secret is
, not .
Probe for the secret (best-effort — skip silently if
is unavailable or
unauthenticated; a repo that runs no Claude workflows needs neither). Run the
listing and the name-check as
two separate steps and read each result — do
not collapse them into one
pipe, which would report the same
failure for a
error and a genuine absence, hiding the can't-verify case:
bash
# step 1 — list the repo's Actions secrets; a non-zero exit here is "can't verify"
gh secret list --repo <owner>/<repo> --app actions
# step 2 — only when step 1 succeeded, check whether the name is in that output
- present (step 1 succeeds and lists the name) → report OK; nothing to do.
- absent (step 1 succeeds but the name is missing) → warn and remind the
operator to run , which installs the App and adds the
secret.
- can't verify (step 1 itself errors — e.g. a without repo-admin scope,
or not installed) → surface it as "couldn't verify the token — please
confirm is set manually", never block or fail the
run. A can't-tell is not an absence.
The App install itself can't be reliably introspected without the App's own token,
so the secret's presence is the reliable proxy; the
reminder
covers installing the App and setting the secret together.
-
Multi-bundle repos — one manual step. If this repo itself ships several
independently-versioned skill bundles,
's
is
not
auto-written (it isn't in
's
key set, so detection
has nothing to populate). Add it to
by hand —
{ "root": "<bundle-dir>", "manifest": "package.json", "skillFile": "SKILL.md" }
— to enable the per-bundle version-bump check. Single-package repos skip this.
Reviewing an existing config
To inspect what a repo's skills are currently configured with — without
reconciling or writing anything — run the read-only review:
bash
node <skills-dir>/initialise-skills/scripts/initialise.mjs --review
For each installed skill it prints its full
: every key's current
value, its classification (
/
/
/
/
/
— see the table above), and a one-line
description of what the key is and where its value comes from, drawn from
references/detectable-keys.md
. Keys a consumer
set that no skill template knows about show as
(kept verbatim, no
description), and template keys not yet present in
show as
, so the review is the whole picture rather than just the pending
diff a dry-run would show. The human text shows each key's
used by … — <detection source>
line, and — for an unset key — the
default that
applies until it's configured (set keys omit it, since the live value already
shows what's in effect). Add
for the
machine-readable form (a
array of
{ key, value, isSet, status, usedBy, detectionSource, fallback }
entries, plus
). It never writes to
disk and skips the
step.
Changing a setting later
Once a consumer's
exists,
hand-editing it is a supported way to
change a setting — you don't have to route every change through this skill. Open
skills/<name>/config.json
(or wherever the bundle is vendored), change the value,
and save. It is a real file the consumer owns; the shared skills read it at runtime.
A manual edit like that
survives future re-runs. On the next
run the reconcile classifies your value as
— a real value that differs from
what detection would produce — and
keeps it, reporting both the kept value and
the detected one (see the
status table above). It is
never silently overwritten: drift is only replaced if you explicitly opt in for that
key (the per-key
gate in step 3). So a deliberate manual edit and a
detected fact coexist — the tool reconciles the facts it can detect without clobbering
the ones you set by hand.
Prefer
--set <skill>.<key>=<value>
below when you
want the same change made through the tool — it validates the key against the
skill's
and preserves key order and formatting — but a direct
hand-edit is equally valid and equally safe.
Setting an arbitrary value
Detection, the stdin
, and
between them cover every value the
script can derive or accept — but not a value you simply want to
choose (a
non-default base branch, a bespoke changelog directory, a boolean toggle). For
those,
--set <skill>.<key>=<value>
writes an arbitrary value straight into a
named skill's
:
bash
# dry-run first (default) — preview the change, write nothing
node <skills-dir>/initialise-skills/scripts/initialise.mjs \
--set changelog.baseBranch=develop \
--set changelog.affectedPackages=false
# re-run with --write to apply
node <skills-dir>/initialise-skills/scripts/initialise.mjs \
--set changelog.baseBranch=develop --write
The flag is
repeatable and the address is
— the skill's bundle
directory name, then a top-level key. The value is parsed as JSON (
/
/
type correctly) and falls back to a bare string when it isn't valid JSON
(so
stays
). It is validated up front, before anything is
written: the skill must be installed, the key must exist in that skill's
(unknown keys are
refused, never silently created), and
the value's type must match that key's example placeholder (so a string can't land
in a boolean field). Any failure exits non-zero and touches nothing.
rides the normal reconcile — detection still runs and your values are
layered on top, winning over what a detector would produce for the same key — and
goes through the same merge/serialise path, so key order and formatting are
preserved and a re-run with the same value is a no-op. It is a write mode, so it
cannot be combined with (which is read-only). In the report a set
key shows as
set to <value> (was <old>)
.
Flags
- (default) — detect, merge and report; write nothing.
- — apply the reconcile to each skill's .
- — read-only. Print every installed skill's full current config:
each key's current value, its classification (the same six statuses), and a
short description sourced from
references/detectable-keys.md
. Unlike the
dry-run it shows the current value of every key — including keys
no template knows about — so it is a complete picture, not just the pending
diff. Writes nothing and skips the step. See
Reviewing an existing config.
--set <skill>.<key>=<value>
— repeatable. Write an arbitrary value into a
named skill's (a value detection wouldn't produce). The key must
exist in that skill's and the value's type must match its
placeholder, else it's refused. Rides the normal reconcile (dry-run first;
to apply) and overrides detection for that key. Cannot be combined with
. See Setting an arbitrary value.
- — emit the machine-readable report (parse this to drive steps 2–3, or
to consume the snapshot); human text otherwise.
- — the host repo the detectors scan (default: cwd).
- — where the sibling bundles live (default: auto-detected
relative to this script).
- stdin JSON —
{ "facts": { … }, "acceptDrift": { "<skill>": ["<key>"] } }
,
read when stdin is piped (not a TTY). Each key may be a skill
name () or the repo-relative config path
("skills/changelog/config.json"
); its value is an array of key names.
also carries the lock provenance / (see step 2).
Checking for updates
To see which installed skills are behind the source repo, run the bundled
against a checkout of the source (the consumer holds only its
old vendored copies, so the target versions come from the source):
bash
node <skills-dir>/initialise-skills/scripts/check-updates.mjs \
--source <path-to-agent-skills-checkout> [--ref <tag-or-sha>] [--json]
It diffs the consumer's
against the source's bundle versions —
at
(via
) when given, else the source working tree — and prints
the per-skill bump list:
(behind — the actionable list), plus
(new upstream skills),
,
(consumer ahead), and
.
targets a specific consumer's lock (default
<cwd>/.claude/skills.lock
),
so a fleet orchestrator can check any repo without changing directory. See
references/skills-lock.md
for the lock schema.
Safety
- Dry-run first, write only after confirmation. Nothing is written without an
explicit pass gated on the user's go-ahead.
- Never clobbers deliberate edits. Drift is preserved unless you opt in per key.
- No deletes, no reordering. Existing keys keep their order; only changed keys
are touched; consumer-added keys are left alone. A malformed existing
is skipped (reported, never overwritten).
- The edit is append-only. One file touched outside a
skill's is the repo's root , and only to append the
entry when it is missing — never reordering or removing
existing lines, and a no-op once present.
- The write is deterministic and byte-stable. The other file
touched outside a is , fully regenerated with
sorted keys and no timestamp — so it only rewrites when a version actually changes,
and a no-op run leaves it byte-identical. It preserves an existing lock's
/ and never fabricates them.
- The GitHub App / token probe is read-only. returns secret
names only, never values, and the skill makes no GitHub writes of any kind
— on an absent or unverifiable secret it only ever prints a reminder.
Prerequisites
- The skills whose configs you want populated are installed alongside this one.
- A git repository with an remote for full base-branch / issue-key
detection (both degrade to sensible fallbacks when absent).
- The Linear MCP server for the team name / workspace slug (optional — those two
keys are flagged for manual input without it).
- The CLI authenticated with repo-admin scope enables the GitHub App / token
probe (step 6). Without that scope (or without at all) the probe can't read
the secret list, so it degrades to a "couldn't verify — confirm
manually" note — a can't-tell, never a failure. The
textual reminder is the separate absent outcome, emitted
only when the probe succeeds and finds the secret genuinely missing. Either way
the skill still runs fully.