{% extends "base.html" %} {% block title %}Skills{% endblock %} {% block topbar_main %}

Skills

What your agents load, and where it comes from {% endblock %} {% block topbar_actions %}
{% if shadowed %}{% endif %}
{% if shadowed %}Hide shadowed{% else %}Show shadowed{% endif %} Proposals {% endblock %} {% block content %}
{% if said %}
{{ said }}
{% endif %} {% if report.summary.defects %}
{{ report.summary.defects }} defect{{ '' if report.summary.defects == 1 else 's' }} found. Nothing is broken on its own, but an agent may be loading a copy you did not mean it to. The list is at the foot of this page.
{% endif %}
{{ report.summary.effective }}
In effect
{{ report.summary.shadowed }}
Shadowed
{{ report.summary.by_scope.plugin }}
From plugins
{{ report.summary.defects }}
Defects
{% if rows %}
{% if scope %}{{ scope }} skills{% else %}Skills{% endif %} {{ rows | length }} of {{ report.summary.packages }}
SkillScopeFrom DescriptionFiles
{% for row in rows %}
{{ row.name }} {% if not row.effective %}shadowed{% endif %} {{ row.directory }} {{ row.scope }} {{ row.plugin or row.scope }} {{ row.description or '—' }} {{ row.file_count }} files
{% endfor %}
{% else %}

Nothing found

No skill package matched. A skill is a directory holding a SKILL.md, under .claude/skills in a project, in your home directory, or inside an installed plugin.

Flanner reads those directories and never writes to them. Nothing in a package is executed by a scan.

{% endif %} {% set scope_row = (observe.scopes | selectattr('observing') | list | first) or (observe.scopes | list | first) %}
Watching skill use {% if scope_row and scope_row.observing %}on{% else %}off{% endif %}

Records that a named skill was invoked, when, and by which local session. Not your prompts, not the agent's replies. Nothing leaves this machine. Only explicit invocations are visible: Claude Code shows every skill's description to the model without reporting it, so reads are unknown rather than zero.

{% if scope_row and scope_row.observing %}
Keeping {{ scope_row.retention_days }} days · {{ scope_row.observations }} recorded {%- if scope_row.dropped %} · {{ scope_row.dropped }} dropped{% endif %} {% else %}
Off. Nothing is being recorded. {% endif %}
Used in the last {{ usage.window_days }} days {% if usage.coverage.watching %}{{ usage.rows | length }} skill(s) {% else %}nothing was watching{% endif %}
{% if usage.rows %}
SkillUsesLast usedModels
{% for row in usage.rows %}
{{ row.skill }} {{ row.invocations }}{% if row.attributed < row.invocations %} {{ row.invocations - row.attributed }} unversioned{% endif %} {{ row.last_used_at[:16].replace('T', ' ') }} {% for k, v in row.by_model.items() %}{{ k }} ({{ v }}){% if not loop.last %}, {% endif %}{% endfor %}
{% endfor %} {% else %}

{% if usage.coverage.watching %}Nothing was invoked in this window. {% else %}Nothing has been watching, so a zero here means nothing rather than "unused".{% endif %}

{% endif %} {# The coverage, spelled out. A closed window still counts as coverage — correctly, something was watching for part of the period — but a reader seeing "nothing was invoked" while the switch reads off needs telling which of those two facts they are looking at. #}

{% if usage.coverage.watching %} Watched during {{ usage.coverage.windows }} period{{ '' if usage.coverage.windows == 1 else 's' }} of these {{ usage.window_days }} days{% if not (scope_row and scope_row.observing) %}, and not watching now{% endif %}. {% else %} Nothing watched any part of these {{ usage.window_days }} days. {% endif %} {% if usage.coverage.dropped %} {{ usage.coverage.dropped }} event(s) arrived and could not be stored, so the counts are short by at least that. {% endif %}

Versions flanner is holding {{ stored | length }} stored · {{ installs | length }} install(s)
{% if installs %}
InstalledWhereState
{% for row in installs %}
{{ row.installed_at[:16].replace('T', ' ') }} {{ row.name }} {{ row.manifest_hash[:23] }}… {% if row.intact %}intact {% elif row.status == 'installed' %}edited since {% else %}{{ row.status }}{% endif %} {% if row.replaced_hash and row.status == 'installed' %}
{% endif %}
{% endfor %} {% else %}

Nothing installed by flanner. Skills stay where their owner put them; flanner skills adopt <name> keeps a copy flanner can put back.

{% endif %}
From your team {{ transfers | length }} arrived · {{ channels | selectattr('subscribed') | list | length }} followed
{% if transfers %}
SkillStateFromHow
{% for row in transfers %}
{{ row.skill }}{% if row.detail and row.state == 'rejected' %} {{ row.detail }}{% endif %} {% if row.state == 'installed' %}installed {% elif row.state == 'verified' %}verified, not installed {% elif row.state == 'rejected' %}rejected {% else %}{{ row.state }}{% endif %} {{ row.from_device[:12] or '-' }} {% if row.pinned %}a one-time copy{% else %}channel {{ row.channel }}{% endif %} {% if row.state == 'verified' %}
{% endif %}
{% endfor %} {% else %}

Nothing has arrived. A package a teammate sends shows up here and waits: receiving one is not installing it.

{% endif %} {% if channels %}
{% for row in channels %}
{{ row.name }} {% if row.subscribed %}following{% else %}not following{% endif %}
{% endfor %}

Following tells you about a new version. It never installs one: that would hand whoever publishes it control of what your agent reads.

{% endif %}
{% if report.findings %}
What is wrong {{ report.summary.defects }} defect{{ '' if report.summary.defects == 1 else 's' }}, {{ report.summary.advice }} advisory
FindingSkillWhat to do
{% for f in report.findings %}
{{ f.code }} {{ f.skill }}{{ f.detail }} {{ f.remedy }}
{% endfor %}
{% endif %}

Where this was read from

{% for note in report.coverage.notes %}

{{ note }}

{% endfor %}
{% endblock %}