Skill3 is a lightweight, fully local Java CLI utility. It ingests technical
document sources, filters them for authority and freshness (anchored to a
target model's knowledge cutoff), synthesizes them with a local LLM into a
structured AI agent skill (SKILL.md), and validates the result against security
risks using NVIDIA's SkillSpector.
Design principles:
- General. No skill is hardcoded. Freshness is driven by a cutoff date, not per-topic keyword lists, so the tool works for any technology or concept.
- Local-first. Synthesis runs on a local LLM (Ollama / any OpenAI-compatible endpoint). No cloud provider, no API key. The only network egress is discovery (Brave Search + web scraping).
graph TD
CLI[Picocli CLI /learn] --> Pipeline[Evaluative Ingestion Pipeline]
subgraph "Phase 1: Discovery & Retrieval"
Pipeline --> Search[Brave Search API]
Search -- If insufficient results --Fallback--> Scraper[Direct Web Scraper]
end
subgraph "Phase 2: Evaluative Ingestion"
Search --> Dates[Publication-Date Extractor]
Scraper --> Dates
Dates --> Scorer[Authority Scorer]
Dates --> Fresh[Freshness Filter - cutoff anchored]
Dates --> Consensus[Consensus Cross-Referencer]
end
subgraph "Phase 3: Synthesis & Vetting"
Scorer & Fresh & Consensus --> Synth[Local LLM Synthesizer]
Synth --> Draft[SKILL.md Draft]
Draft --> Spector[SkillSpector Runner]
Spector -- Findings? --Sanitize + LLM revise--> Synth
Spector -- Clean --> Output[Final SKILL.md + index.html Preview]
end
Arguments are parsed with picocli. Two commands are exposed.
Prepares the local environment for verification.
- Actions:
- Locate a compatible Python by probing interpreters (
py -3.14/-3.13/-3.12on Windows,python3.x/python3on POSIX). SkillSpector requires>=3.12,<3.15; do not assumepythonon PATH is new enough. - Clone NVIDIA's SkillSpector from
https://github.com/NVIDIA/SkillSpector.gitto localskillspector-src. - Initialize a Python virtual environment (
.venv) inside theskill3directory. - Install dependencies into the venv (
pip install -e skillspector-src), resolving the venv binary path per platform (Scripts/on Windows,bin/on POSIX). - Verify by invoking the installed
skillspectorCLI and recording its actual subcommands/flags (do not assume them — see §4.2).
- Locate a compatible Python by probing interpreters (
Runs the evaluative pipeline to learn a new skill.
- Argument:
<skill-name>: technology or concept to learn (e.g.mcp,jdk26).
- Options:
--target-model <id>: the model the skill is for. Used only to look up a knowledge-cutoff date (no inference call). Default:claude-opus-4-8.--cutoff-time <iso-date>: explicit cutoff override; wins over--target-model. Use for unknown models or when the built-in table is stale.--strict-cutoff: hard-filter sources published at/before the cutoff (see §3B). Off by default (weighted ranking).--llm-model <name>: local synthesis model (e.g.qwen2.5-coder).--llm-endpoint <url>: OpenAI-compatible local endpoint. Defaulthttp://localhost:11434(Ollama).--brave-key <key>: Brave Search API key. If omitted, checksBRAVE_SEARCH_API_KEY.--output-dir <path>: output location. Default./skills/<skill-name>.
Note: there is no
--provideror--api-key. Synthesis is local-only.
A small built-in, overridable table maps known model IDs to cutoff dates,
e.g. claude-opus-4-8 → 2026-01. The table is expected to go stale; entries
should be sourced from model documentation and are always overridable with
--cutoff-time. An unknown --target-model without --cutoff-time is an error
that names the override flag.
Scraped data is processed through four general (skill-agnostic) modules.
Assigns trust multipliers to URLs before ingestion. Domain classification is heuristic/config-driven — not hardcoded per skill.
- Trust Weights (illustrative):
- Official documentation domains:
1.0 - Official SDK / language repositories:
1.0 - Verified developer portals / standard repositories:
0.7 - Unverified blogging platforms,
medium.com,dev.to, personal gists:0.2
- Official documentation domains:
- Rule: content scoring
< 0.5is used for structural reference only and never overrides constraints from official1.0-weighted sources.
Replaces all per-topic deprecation lists. The cutoff date (from --target-model
or --cutoff-time) is the freshness anchor: the value of relearning a skill is
the content published after the target model's training cutoff.
- Combined scoring (default): each source is scored
authority × recency, whererecencyis a multiplier for post-cutoff publication. Pre-cutoff content remains usable for baseline structure but can never override a post-cutoff authoritative claim (same override semantics as §3A). --strict-cutoff: promotes the soft floor to a hard filter — sources published at/before the cutoff are excluded entirely.- Depends on reliable publication dates (§3D).
- Scope of the promise: the filter surfaces fresh authoritative content; it does not infer deprecations. "X is now deprecated" is captured only when a fresh authoritative source states it (and the synthesizer carries it through), not by the filter itself.
General, schema-free. A snippet or claim is trusted when it recurs consistently across N independent high-authority sources. Snippets that conflict with the high-authority consensus are discarded. (No per-topic JSON-RPC schemas.)
Critical path — the entire freshness mechanism depends on it. Extracts a date per source from, in order:
- HTML metadata (
article:published_time, OpenGraph,<time>elements). - Sitemap
lastmod. - Repository signals (latest release / relevant commit date) for repo sources.
- Fallback when no date is found: mark the source undated — excluded under
--strict-cutoff, given a low recency weight otherwise.
Output target is the Agent Skills SKILL.md standard (used by Claude Code,
the Claude apps, and the API Skills feature). A skill is a directory containing
SKILL.md plus any bundled reference files.
The pipeline hands the local LLM a structured context bundle, not raw HTML:
The synthesis prompt enforces:
- Build only from
sources(reduces hallucination; limits prompt-injection impact — scraped text is delimited as untrusted data, see §6). - Prefer
post_cutoff: truecontent; carry through deprecations those sources explicitly state. Where fresh and stale content conflict, the fresh authoritative source wins (mirrors §3A/§3B override rules). - Cite provenance: each non-obvious claim/snippet traces to a source URL, surfaced in a Sources section.
---
name: <sanitized-kebab-name> # ≤64 chars; [a-z0-9-]; no "anthropic"/"claude"
description: <what it does + when to use it> # non-empty, ≤1024 chars, no XML tags
metadata:
version: 1.0.0
learned-date: 2026-06-22
target-model: claude-opus-4-8
cutoff: 2026-01
---
# <Skill Title>
## Overview / When to use
## Instructions # step-by-step, current patterns
## Modern vs deprecated # only when fresh sources state a deprecation
## Examples # concrete, from consensus-validated code blocks
## Sources # provenance URLsThe LLM drafts; the generator guarantees format compliance — it does not trust the model to obey the field rules:
name: lowercase,[a-z0-9-]only, ≤64 chars, strip/replace the reserved wordsanthropicandclaude. (learn claudemust not yieldname: claude— derive a compliant name likeclaude-mcp→ rejected too; use a neutral fallback such as<topic>-skillwhen the raw name collides with a reserved word.)description: non-empty, ≤1024 chars, no XML tags; truncate/regenerate if violated.- Frontmatter: valid YAML; all fenced code blocks closed.
- On violation: apply the deterministic fix, or re-prompt the local model once, then fail loudly rather than emit a malformed skill.
Keep the client generic (OpenAI-compatible chat completions). Stream long generations.
Run the installed SkillSpector CLI against the generated folder. Confirmed
against the NVIDIA/SkillSpector source and a live run: the command is
skillspector scan <path> --no-llm --format json (Typer CLI; formats
terminal|json|markdown|sarif). Skill3 passes --no-llm so SkillSpector runs
its static analysis only — without it the CLI exits 2 demanding an LLM API key,
which would break the fully-local design. SkillSpector exits non-zero when
findings exist; the runner ignores the exit code and parses stdout. With --format json the report is a JSON object whose findings live
under an issues[] array; each finding carries id, category, severity,
confidence, explanation (the message), and a nested
location.{file,start_line}. The report is written to stdout (logs go to
stderr). SkillSpector's optional stage-2 LLM semantic analysis is out of scope
unless explicitly wired to the local endpoint later.
If SkillSpector reports findings (prompt-injection vectors, excessive authority, credential exposure, etc.):
- Deterministic pre-pass: mechanically strip detected secrets and drop flagged instruction lines where the fix is unambiguous.
- Local-LLM revision pass: send the current draft + extracted findings to the local model to revise the offending sections.
- Rescan.
- Iterate up to 3 times. If findings remain, emit the file and warn the user in the CLI with the residual findings.
Generated after the pipeline succeeds; not required for the core value path.
The index.html preview should follow:
- Typography: Google Fonts "Inter" or "Outfit".
- Color Scheme (HSL Dark Theme):
- Background: deep gray-blue
hsl(220, 20%, 10%) - Secondary cards: translucent
hsla(220, 15%, 16%, 0.7)with glassmorphism backdrop blur. - Accents: electric teal
hsl(180, 100%, 45%)and neon magentahsl(320, 100%, 60%).
- Background: deep gray-blue
- Micro-Animations: hover states on rule selectors, copy-to-clipboard on code blocks, expand/collapse for deprecated-vs-modern comparisons.
- Prompt injection from scraped content. Scraped pages flow into the local synthesizer; a malicious page could attempt to steer synthesis. Mitigations: treat scraped text as data (clearly delimited in the prompt), constrain the synthesizer to the supplied context, and rely on the SkillSpector vetting pass on the output. Risk is bounded by the local-only execution (no data egress).
- Process execution. SkillSpector runs via
ProcessBuilderagainst the cloned venv; invoke the recorded binary path explicitly, never a shell string. - Scraper etiquette. Respect
robots.txtand rate limits in the fallback scraper.
{ "skill_name": "mcp", "target_model": "claude-opus-4-8", "cutoff": "2026-01", "sources": [ // ranked, highest combined score first { "url": "https://modelcontextprotocol.io/...", "authority": 1.0, "published": "2026-05-21", "post_cutoff": true, "recency_weight": 1.0, "combined_score": 1.0, "consensus_count": 3, // independent high-authority sources agreeing "excerpts": ["..."], // extracted prose, headings, API signatures "code_blocks": ["..."] // fenced snippets that survived consensus } ] }