Skip to main content
Alpha

@netscript/ai/skills

Load validated Agent Skills with progressive disclosure and optional semantic matching. A skill is a SKILL.md document — a small frontmatter block plus Markdown instructions — that an agent can discover cheaply by tags (or embeddings) and only fully disclose when it is actually selected. This subpath is a library surface only: it has no CLI verbs and takes no @netscript/* runtime dependency. For the full package index return to the @netscript/ai reference or the reference overview.

Installed NetScript skills

netscript agent init installs three first-party skills on the Claude Code host path (under .claude/skills/, alongside .mcp.json and the marked AGENTS.md section); the VS Code host path writes .vscode/mcp.json only, with no skills. They share one vocabulary with the netscript CLI and the NetScript MCP tools, so an agent routes to a workflow and then reaches for the matching CLI verb or MCP tool.

Skill Role
netscript Router. Dispatches a task to the build or operate skill (or to the Aspire skill for orchestration); it is not useful on its own.
netscript-build Scaffold and build with real netscript CLI verbs: init, the contract-first flow, the database lifecycle, adding and syncing plugins, adding services and UI, and generating registries.
netscript-operate Monitor, debug, and analyze a running app through the bounded MCP tools: health, recent runs, one execution end to end, recent errors, job results, service and database performance, and doc search.

These are the skills named in the bundle manifest and installed as an atomic set; re-running netscript agent init is idempotent. See Agent tooling for the CLI × skills × MCP combo.

import {
  createInMemorySkillContentSource,
  createSkillLoader,
} from "jsr:@netscript/ai@0.0.1-beta.11/skills";

The subpath is published as the ./skills export of @netscript/ai (version 0.0.1-beta.11). Nothing here reads the environment or performs IO on its own: content enters through an injected SkillContentSource, and semantic ranking is only performed when you supply a SkillEmbeddingProvider.

The skill document

parseSkillMarkdown is the single validator for the blessed SKILL.md shape. The frontmatter must be a closed --- block whose only allowed keys are the four required fields; the body is the Markdown that follows and must be non-empty.

Frontmatter field Rule
id Non-empty; matches ^[a-z0-9][a-z0-9._-]*$ (case-insensitive).
name Non-empty human-readable label.
tags A YAML list with at least one tag; drives tag matching.
description Non-empty short summary used for discovery and semantic matching.

Unknown keys, duplicate keys, a missing required key, an empty body, or a malformed frontmatter line each throw a TypeError. A minimal document:

---
id: review
name: Code review
tags: [review, quality]
description: Reviews a change for correctness.
---
Inspect the diff and report actionable findings.

Progressive disclosure

The design keeps cheap metadata enumeration separate from full-content reads. A SkillContentSource lists validated SkillSummary rows (id, name, tags, description — no body) and loads the raw SKILL.md for one id on demand. A SkillLoaderPort composes over a source: it matches summaries first, and only load(id) parses and validates the full SkillDocument (summary + body). This is the same two-phase shape the runtime's skills capability port speaks.

Surface

Composition

Symbol Kind Signature Description
createSkillLoader function createSkillLoader(source: SkillContentSource, options?: SkillLoaderOptions): SkillLoaderPort Compose a loader from an injected content source and optional embeddings.
createInMemorySkillContentSource function createInMemorySkillContentSource(entries: readonly SkillSourceEntry[]): SkillContentSource Build an effect-free source from caller-supplied SKILL.md strings (rejects duplicate or mismatched ids).
parseSkillMarkdown function parseSkillMarkdown(markdown: string): SkillDocument Parse and validate one SKILL.md into a full document, or throw TypeError.

Matching primitives

The loader delegates to two pure ranking functions, also exported directly for callers that hold their own summaries. Both return results sorted by descending score.

Symbol Kind Signature Description
matchByTags function matchByTags(skills: readonly SkillSummary[], triggers: readonly string[]): readonly SkillMatch[] Case-insensitive exact (score 1) and substring (score 0.5) tag triggers.
matchBySemantic function matchBySemantic(skills: readonly SkillSummary[], query: string, embeddings: SkillEmbeddingProvider, model?: string): Promise<readonly SkillMatch[]> Cosine-similarity ranking over name/description/tags text via an injected embedding provider.
SKILL_MATCH_MODES variable readonly ["tag", "semantic"] The trigger modes a SkillMatch can report.

Types

Symbol Kind Description
SkillLoaderPort interface Discovery + two-phase loading: list(), load(id), matchByTag(tags), matchByQuery(query, options?).
SkillContentSource interface Source boundary: list() of summaries and load(id) of raw content.
SkillSummary interface Cheap metadata (id, name, tags, description) with no body.
SkillDocument interface SkillSummary extended with the non-empty body.
SkillMatch interface A ranked result: skill (summary), score (0–1), and contributing modes.
SkillMatchMode type alias "tag" | "semantic".
SkillQueryOptions interface matchByQuery options: tags, semantic, minScore, limit.
SkillLoaderOptions interface Loader composition options: embeddings, embeddingModel.
SkillEmbeddingProvider interface Minimal embed(input, options?) structural view used for semantic matching.
SkillSourceEntry interface Input to the in-memory source: { id, markdown }.

Default runtime port

The runtime's skills capability is a SkillLoaderPort. When AiRuntimeConfig.skills is omitted, createAiRuntime injects a no-op loader that lists and matches nothing — so agent code can call the port unconditionally before any skills are wired.

AiRuntimeConfig field Port Default when omitted
skills SkillLoaderPort No-op returning no skills (createNoopSkillLoader from @netscript/ai/ports).

Provide a real loader by wiring one built here into the runtime config; see the runtime capability ports table on the package page.

Examples

Build a loader and match by tag

import {
  createInMemorySkillContentSource,
  createSkillLoader,
} from "@netscript/ai/skills";

const source = createInMemorySkillContentSource([
  {
    id: "review",
    markdown: `---
id: review
name: Code review
tags: [review, quality]
description: Reviews a change for correctness.
---
Inspect the diff and report actionable findings.
`,
  },
]);

const loader = createSkillLoader(source);
const matches = await loader.matchByTag(["review"]);
if (matches.length > 0) {
  const skill = await loader.load(matches[0].skill.id); // full body disclosed here
  console.log(skill?.body);
}

Add semantic ranking

Pass a SkillEmbeddingProvider at composition time and set semantic: true on the query. The @netscript/ai/openai-embeddings provider satisfies the structural embed shape.

import "@netscript/ai/openai-embeddings"; // self-registers the provider
import { getEmbeddingProvider } from "@netscript/ai";
import { createSkillLoader } from "@netscript/ai/skills";

const embeddings = getEmbeddingProvider("openai-embeddings", {
  apiKey: Deno.env.get("OPENAI_API_KEY"),
});

const loader = createSkillLoader(source, { embeddings });
const ranked = await loader.matchByQuery("find bugs in my patch", {
  semantic: true,
  limit: 5,
});

Rank summaries you already hold

matchByTags and matchBySemantic are pure and usable without a loader:

import { matchByTags } from "@netscript/ai/skills";

const summaries = await source.list();
const results = matchByTags(summaries, ["quality"]);
// results: [{ skill, score, modes: ["tag"] }, ...] sorted by descending score

Back to the @netscript/ai reference or the reference overview.