@techfides/tf-skills-manager
v0.2.1
Published
Readme
tf-skills
The CLI for the TechFides AI skills library (TechFides/tf-skills-manager-library). It
fetches and installs skills, checks their status, and opens pull requests to
contribute new or changed ones — all through GitHub's API, with no git clone,
no git pull, and no local checkout of the library.
What it can do
Two audiences, one tool: people who use skills, and people who write them. Neither ever clones the repository.
| You want to | Command |
|---|---|
| See every skill in the library, with descriptions and authors | list |
| Install some, picked from a menu | install |
| Install specific ones, or a whole bundle | install <name...>, install --bundle <name> |
| Install everything | install --all |
| Find out what is stale, locally edited, or untracked | status |
| Bring installed skills back in line with the library | update |
| Start a new skill from a template, offline | new <name> --section <section> |
| Open a pull request adding or changing a skill | contribute [name] |
| Read the usage text | help, --help, -h |
How it works
- Nothing is cloned. Every read is a GitHub Git Data API call. The library's
mainis resolved to a single commit once per run and pinned for the rest of it, so one invocation can never mix content from two states of the library. - Each skill is copied into
--target. The default is./.claude/skills, under the folder you ran the command from, so a skill installed while working on a project belongs to that project and can be committed with it.install,updateandcontributeoffer the machine-wide~/.claude/skillsas the alternative. A skill lands at<target>/<skill-name>/, without its section prefix, which is the layout Claude Code expects. - The install record is per user, not per project.
~/.claude/tf-skills/state.jsonstays in your home directory even for a project install, so nobody commits their install record. The trade-off is that skills you acquire by cloning a project read asunmanageduntil you install them yourself. - A local record makes drift visible.
~/.claude/tf-skills/state.jsonholds a SHA-256 per installed file, keyed by target directory. That is howstatustellscurrentfromoutdatedfrom locallymodified, and howupdateknows to refuse rather than quietly overwrite your edits. - Bundles and dependencies resolve themselves. Installing a skill also
installs whatever
bundles.jsonrecords it as depending on, cycle-safe. - Authoring is clone-free too.
newscaffolds on disk with no token and no network;contributeturns local files into a branch, commit, and pull request through the API. Anyone can contribute a new skill or change an existing one; merging the pull request is what makes it live, and there is no status field and no owner list anywhere in the library.
Content is read from main only. A skill that exists on a branch is invisible to
this CLI until it merges, which is deliberate: everyone running install gets the
same library. Use --repo owner/name to read from somewhere else entirely.
Quick start
No install needed — run it with npx, which always resolves the latest
published version:
npx @techfides/tf-skills-manager@latestWith no arguments at all, that is install's interactive picker (requires a
TTY). To install by name, the install keyword is required — a bare
npx @techfides/tf-skills-manager@latest <name> is not the same as
install <name>; the leading token is taken as the command word, and an
unrecognized one is a usage error (see "Exit codes" below). Without a TTY,
install needs install <names...>, --bundle <name>, or --all instead.
If you install the package globally or as a dev dependency, the same commands
are available as tf-skills:
npm install -g @techfides/tf-skills-manager
tf-skills listEvery example below uses tf-skills; substitute npx
@techfides/tf-skills-manager@latest for it if you are not installing globally.
Requirements
Node.js >= 24. Older versions warn (
EBADENGINE) and are untested.A GitHub token with read access to
TechFides/tf-skills-manager-library(write access too, forcontribute). Resolved in this order:GITHUB_TOKENenvironment variable.GH_TOKENenvironment variable.gh auth token, if the GitHub CLI is installed and logged in.
If none of these resolve, every command except
newfails naming the fix (gh auth login, or setGITHUB_TOKEN).newneeds no token at all — it never makes a network call.
Commands
install (default)
tf-skills install [names...] [--all] [--bundle <name>]install is the default command, but only for a bare invocation with no
arguments at all (or one starting with a flag) — tf-skills alone, or
tf-skills --all, dispatches to install. A leading skill name does not:
tf-skills dev/sonar-fix-pr is parsed as the command word dev/sonar-fix-pr,
which is unknown, not as install dev/sonar-fix-pr. Whenever one or more
names are passed, write the install keyword explicitly.
Fetches and places skills into the target directory. With no --target, and a
terminal to ask on, it first asks whether you mean this project's
./.claude/skills (the default, so Enter takes it) or the machine-wide
~/.claude/skills. Accepts a bare skill name
(sonar-fix-pr) or a <section>/<name> path (dev/sonar-fix-pr); both forms
resolve to the same skill. Any dependency a requested skill declares is
installed automatically alongside it.
- With one or more names and/or
--bundle <name>, installs exactly that selection (plus dependencies).--allsilently wins over both if given alongside them. - With
--all, installs every skill in the library. - With no names, no
--bundle, and no--all, and a TTY, prompts interactively with a multiselect. - With none of the above and no TTY, fails naming
--all/--bundle/a name as the fix — it never guesses in a script or agent context.
A skill already installed and unchanged is reported as already current and
left untouched. A directory that already exists at the destination and either
isn't tracked by this CLI, or has local edits, is refused by name; re-run with
--force to overwrite it, or use contribute first to keep the changes.
Unlike update, install stops at the first such conflict in a
multi-skill run rather than accumulating failures across all of them.
Exit code: 0 on success (including "nothing to do"); 1 on a hard failure
(unknown skill/bundle name, an unresolved conflict without --force).
update
tf-skills update [names...] [--all]Refreshes skills already installed under the target directory to the
library's current main. update never installs something new — a name that
isn't already installed is reported, not installed.
- No names and no
--all: refreshes everything currently installed. This is also what bareupdatedoes — it never prompts. update <names...>: refreshes only those (accepts the same bare-name or<section>/<name>forms asinstall).--allsilently wins over any names given alongside it, same asinstall.
A skill with local edits is refused (name and reason reported) unless
--force is given; other requested names keep processing rather than
aborting the whole run. A name no longer on disk, a locally-created (new)
skill, or a skill removed from the library upstream, is reported and skipped
without ever refetching.
Exit code: 1 if anything was refused or not found, 0 otherwise.
list
tf-skills list [--json]Prints every skill the library currently offers, grouped under its section in the canonical role order, with names aligned into a column and authors in a column of their own on the right. Bundles, if the library defines any, come first:
15 skills in TechFides/tf-skills-manager-library
Bundles (1)
docs Documentation authoring set
docs-base, docs-commands, docs-tests, ff-analysis
dev (3)
review-ticket-branch Review the branch implementing one defined ticket… Dalibor Šimon
sonar-fix-pr List and fix all open SonarQube quality gate issu… Filip Koukal
mam-adhd Piš výstup tak, aby podle něj mozek s ADHD dokáz… Tomáš Pokorný
analyza (4)
docs-base Documentation foundation for the entire ana-docs… Dávid ŠilonDescriptions are shortened to one line and the layout follows the terminal width,
because a description is written for an agent deciding whether a skill applies
and the longest in the library runs past a thousand characters. The author is
never shortened: it has its own column precisely so that clipping a description
cannot take it with it. A skill that names no author leaves the cell empty.
Pass --json for a machine-readable
{ "skills": [{ section, name, description, author }], "bundles": [{ name, description, skills }] }
with every description in full — that output is unaffected by the layout above.
A skill with no author has no author key at all, rather than a null one.
status
tf-skills status [--json]Reports where every skill stands relative to both the library and the target
directory on disk: current, outdated, modified, unmanaged,
not-installed, local-only (created with new, never contributed), or
removed-from-library. This is a report only — it never writes anything, and
always exits 0 on a successful run regardless of what it finds.
tf-skills help explains each of those seven states and what to do about it.
Pass --json for { "<name>": { "state": ..., "section": ... } } instead of
the plain-text table.
new
tf-skills new <name> --section <qa|pm|dev|analyza|shared>Scaffolds <name>/SKILL.md locally under the target directory, with name,
description and metadata.author frontmatter and a placeholder body, and
records it as not yet part of the library. Fill in the real description and
instructions, add any supporting files, then run contribute when it's ready to
become a pull request.
The author is --author "Your Name" if you pass it, otherwise your git
user.name. If neither is available the block is left out entirely rather than
filled with a placeholder — the library's validator warns about a skill that
names no author, which is a better outcome than a fake name showing up in list.
Needs no GitHub token — it is entirely local (reading git config user.name is
the one subprocess it runs, and it carries on without it). Without --section
and with a TTY it prompts for one; without a TTY it fails naming --section.
contribute
tf-skills contribute [name] [--section <section>]Opens (or, if one is already open, reports the URL of) a pull request from a
local skill's current content — a new-scaffolded skill, or a locally-edited
one — via GitHub's API: blob, tree, commit, branch, PR. No local git branch,
no git push.
- With a name, contributes exactly that skill.
- With no name and a TTY, offers a multiselect of local candidates (skills that are local-only or otherwise unmanaged by the library).
- With no name and no TTY, fails asking for one.
The pull request contains the skill's local content in full: a file present locally is added or updated, and a file the library has that no longer exists locally is deleted, so an edit that removes or renames a supporting file contributes correctly.
A skill that names no metadata.author still contributes — the check is one
stderr warning before the pull request opens, not a refusal. The library's own CI
warns about it too, but only once the PR is already up.
The section is resolved from --section, else a <section>/ prefix typed on
the name itself (contribute dev/foo), else the section recorded for that skill
locally (set by new, or by a prior install/update), else an interactive
prompt; without a TTY and without any of those, it fails naming --section.
contribute itself never writes to that local record — only new, install,
and update do, which is why a successful run prints how to re-sync once the
pull request is merged. Validation runs before anything is written — a failure
names every issue and nothing is created. Those checks mirror the errors the tf-skills-manager-library validator enforces in CI, though a few messages are the CLI's own:
an unquoted keyword-shaped name or description (name: no) is reported here
as the quoting problem it is, where CI reports only the missing or mismatched
value it resolves to.
Exit code: 1 if any requested skill failed to contribute, 0 otherwise.
help
tf-skills help
tf-skills --help
tf-skills -hPrints the usage summary: every command with its arguments, every flag with the commands that read it, how the token is resolved, and the exit codes. Needs no token and makes no network call.
--help and -h are answered wherever they appear, so tf-skills install --help
prints this text rather than installing anything. Everything else on the command
line is ignored in that case — including flags that would otherwise be rejected,
so a half-remembered invocation still gets you the help instead of a usage error.
Exit code: 0.
Global flags
| Flag | Read by | Meaning |
|---|---|---|
| --target <dir> | all (list accepts but ignores it) | Skills directory to read/write. Default ./.claude/skills, relative to where the command ran. Passing it skips the project-or-global question. ~ and ~/... are expanded. |
| --json | list, status | Machine-readable output instead of a plain-text table. |
| --force | install, update | Overwrite an unmanaged or locally-modified directory instead of refusing. |
| --repo <owner/name> | commands that talk to GitHub | Fetch from a different repository instead of TechFides/tf-skills-manager-library. Must be owner/name. |
| --all | install, update | Every skill in the library (install) or every skill currently installed (update). |
| --bundle <name> | install | Add a named bundle from bundles.json to the install selection. |
| --section <section> | new, contribute | One of qa, pm, dev, analyza, shared. |
| --author <name> | new | Who to record as the skill's author. Defaults to git config user.name. Must contain no double quote or backslash. |
| --help, -h | any | Print the usage text and exit 0, ignoring the rest of the command line. |
Every flag above is accepted on every command — the parser has no per-command
restriction — but only the commands listed in "Read by" do anything with it.
An unrecognized --flag is a usage error (exit 2), not silently ignored.
--help/-h are the one exception to both rules: they are resolved before any
other parsing, so they work on every command and are never rejected.
Exit codes
| Code | Meaning |
|---|---|
| 0 | Success, including "nothing to do" and (for status) a clean report of drift. |
| 1 | Operational failure: a refusal, a validation failure, an unresolved conflict, missing auth. |
| 2 | Usage error: unknown command or flag, wrong number of arguments, malformed flag value. |
An expected failure prints one message and exits 1 or 2. An unexpected
one — a bug — prints a single Unexpected error: line; set TF_SKILLS_DEBUG=1
to get the stack trace with it.
Notes for agent / scripted use
Every command runs non-interactively when given enough on the command line —
none of them require a TTY as long as the relevant flag or name is supplied
(see each command's section above for exactly what that is). Use --json on
list/status to consume their output programmatically instead of parsing
the plain-text table.
