Skip to content

Add agent-skills infrastructure and installer#940

Open
MaxDall wants to merge 1 commit into
masterfrom
add-skill-infrastructure
Open

Add agent-skills infrastructure and installer#940
MaxDall wants to merge 1 commit into
masterfrom
add-skill-infrastructure

Conversation

@MaxDall

@MaxDall MaxDall commented Jun 16, 2026

Copy link
Copy Markdown
Collaborator

What

Introduces a home for agent skills — playbooks/scripts meant to be executed by AI coding agents — under skills/, plus an installer that copies them into an agent's config directory.

The repo stays agent-neutral: we track skill sources here, but never commit any agent's config dir (.claude/ is now gitignored). Each developer installs the skills they want into their own agent.

Contents

  • skills/install.py — thin launcher for the installer dashboard.
  • skills/installer/core.py — stdlib-only install/uninstall + skill discovery (any folder with a SKILL.md is a skill). Install is atomic: if a skill's requirements.txt step fails, the copied folder is rolled back, so always means the skill and its deps landed.
  • skills/installer/tui.py — Textual status dashboard: a live skills × scopes matrix (project ./.claude/skills/ vs user ~/.claude/skills/); toggle to (un)install in place.
  • skills/README.md — layout conventions, install flow, how to add a skill.
  • pyproject.toml — adds textual to the dev extra (the dashboard's only dependency).
  • .gitignore — ignores the installed-skills dir (.claude/).

Notes

  • Only Claude Code is wired up today; other agents are a documented future mapping in available_agents().
  • The first skill built on this — review-publisher — ships as a companion PR. This PR is the framework; on its own the installer matrix is empty until a skill folder exists.

Testing

python skills/install.py opens the dashboard; toggling project/user installs and uninstalls the discovered skill folders.

@MaxDall MaxDall requested a review from addie9800 June 16, 2026 14:17

@addie9800 addie9800 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a very promising in new step towards making fundus more skalable. I am looking forward to seeing the progress this allows us to make 🚀

Comment thread skills/install.py
except ModuleNotFoundError as exc:
if exc.name in {"textual", "rich"}:
print(
"The skill installer dashboard needs the 'textual' package.\n"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: It might be more helpful to not selectively test for textual and one of it's dependencies here, but rather either:

  • always suggest running the dev extra install or
  • check for all necessary dependencies (i.e. platformdirs is also essential) and
    • explicitely name the missing package or
    • rephrase the error message to say needs the 'textual' package and all dependencies.

Comment thread skills/installer/tui.py
Text(name, style="bold"),
_status(agent.is_installed("project", skill)),
_status(agent.is_installed("user", skill)),
Text(textwrap.shorten(skill.description, width=70, placeholder="…")),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One could consider making the width dynamic here, when supported by the screen and if it doesn't complicate the code too much. For me for example, there is a lot of unnecessary dead space:

Image

Comment thread skills/installer/core.py
if dest.exists():
shutil.rmtree(dest)
return [f"removed '{skill.name}' for {self.name} ({scope}) -> {dest}"]
return [f"nothing installed at {dest}"]

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would also expect all the packages, that were installed and are now obsolete to be uninstalled. When typing this out, I realized that one would have to ensure that no requirement is removed that is still required by other skills. On a similar note: not really a big issue for now, but with more skills, one might eventually encounter issues with contradicting (version) requirements for skills. As of now, only the most recently installed skill will be available, right?

Comment thread skills/installer/core.py


def _install_requirements(skill: Skill) -> Tuple[bool, List[str]]:
"""If the skill bundles a ``requirements.txt``, pip-install it into the current interpreter.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you perhaps provide a couple more details for the use-case of the user scope? Usually the skills we install will probably be quite project specific and unlikely to be required on the user-level. In case required, the execution can still be problematic from what I can see, because the requirements are always installed from the project-level interpreter. Even if the user-level is specified, if a venv/conda env are used when running this installer, the deps will not be installed globally, if I am not mistaken.

Comment thread skills/README.md

## Adding a skill

Create `skills/<name>/` with a `SKILL.md` (frontmatter `name`/`description`) and a `PLAYBOOK.md`

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having not too much experience with Markdown, I didn't know what exactly you meant with frontmatter until looking into the review-skill PR. Maybe a template or short example might make adding the first skill simpler.

Comment thread skills/installer/core.py
"""
try:
text = skill_md.read_text(encoding="utf-8")
except OSError:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a bit too broad. FileNotFoundError might be a better choice to allow other exceptions to be raised.

Comment thread skills/installer/core.py
req_ok, req_log = _install_requirements(skill)
if not req_ok:
shutil.rmtree(dest, ignore_errors=True) # undo the copy — keep the matrix honest
return InstallResult(False, [*req_log, f" rolled back '{skill.name}' ({scope}) — nothing left installed"])

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also here, when rolled back, I would also assume the successful installations to be rolled back as well.

Comment thread skills/installer/core.py
noise = ("A new release of pip", "To update, run")
lines = [ln.rstrip() for ln in f"{stdout}\n{stderr}".splitlines() if ln.strip()]
lines = [ln for ln in lines if not any(n in ln for n in noise)]
errors = [ln for ln in lines if ln.lstrip().startswith("ERROR")]

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would include WARNING level as well - might make debugging easier for the user:

WARNING: Retrying (Retry(total=0, connect=None, read=None, redirect=None, status=None)) after connection broken by 'NewConnectionError('<pip._vendor.urllib3.connection.HTTPSConnection object at 0x7fa8d8545450>: Failed to establish a new connection: [Errno -3] Temporary failure in name resolution')': /simple/numpy/
ERROR: Could not find a version that satisfies the requirement numpy (from versions: none)
ERROR: No matching distribution found for numpy

Comment thread skills/installer/tui.py
DataTable { height: 1fr; min-height: 6; margin: 1 1 0 1; border: round $primary; }
#agent { height: auto; border: round $primary; padding: 0 1; margin: 1 1 0 1; }
#agent > RadioButton { width: auto; }
RichLog { height: 5; border: round $panel; padding: 0 1; margin: 1; }

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be good to increase the size of the log. For longer error messages (e.g. version mismatch in numpy) it is very difficult to read and get an overview of the output.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants