Skip to content

@chamvi/skills ​

Optional skill types, validation, catalogs, runtime loading and filesystem verification, including startup or setup/build preparation for explicitly selected Git skill sources. Requires Node >=26.0.0, like core/app, with Git for source preparation.

bash
npm install @chamvi/skills @chamvi/core

Runtime contracts ​

Skill data types and catalogs are exported by @chamvi/skills:

ts
import type { ToolSet } from 'ai';

export type SkillIdentity = Readonly<{
    name: string;
    version: string;
    hash: string
}>;
export type SkillMetadata = SkillIdentity & Readonly<{ description: string; ignoredFields: readonly string[] }>;
export type Skill = SkillMetadata & Readonly<{ body: string; references: Readonly<Record<string, string>> }>;
export interface ISkillSource {
    readonly skills: readonly SkillIdentity[];
    load(options: { signal: AbortSignal }): Promise<readonly Skill[]>;
}
export type SkillCatalog = Readonly<{
    skills: readonly SkillMetadata[];
    instructions: string;
    tools: ToolSet
}>;
export type LoadedSkill = {
    name: string;
    version: string;
    hash: string;
    reference: string | null;
    content: string;
    references: string[];
};

function createSkillCatalog(skills: readonly Skill[]): SkillCatalog;

SkillCatalog.instructions remains metadata-only text for manual composition. The first-class runtime converts nonempty catalog text into a frozen system-message array (an empty catalog contributes []), appended after the configured agent instructions. SkillContribution.instructions accepts core AgentInstructions, including legacy strings.

Core exports only the generic integration protocol. It coordinates preparation, cancellation and contributed tools; the optional runtime owns content validation, catalogs, source caches and fingerprints. Core/app work without skills installed, and the skills main entry has no app dependency.

ts
/*
 * Copyright (c) 2026.
 * Author Peter Placzek (tada5hi)
 * For the full copyright and license information,
 * view the LICENSE file that was distributed with this source code.
 */

import type { ToolSet } from 'ai';
import type { Agent, AgentInstructions } from '../agent/types.ts';

export type SkillInput = Readonly<{
    runtime: ISkillRuntimeProvider;
}>;

export interface ISkillRuntimeProvider {
    readonly protocol: string;
    /** Validate and copy inputs synchronously, without I/O or caller mutation. */
    snapshot(inputs: readonly SkillInput[], tools: ToolSet): readonly SkillInput[];
    /** Create fresh per-engine state without I/O. */
    create(globals: readonly SkillInput[]): ISkillRuntime;
}

export interface ISkillRuntime {
    /** Snapshot captured local inputs and validate their composition with globals. */
    checkAgent(agent: Agent, inputs: readonly SkillInput[]): void;
    /** Resolve globals even with no agents; settle all owned work before returning. */
    prepare(agents: readonly Agent[], options: { signal: AbortSignal }): Promise<ISkillPreparation>;
}

export interface ISkillPreparation {
    /** Single use: recheck conflicts and publish all attestations synchronously. */
    commit(): readonly PreparedAgentSkills[];
}

export type PreparedAgentSkills = Readonly<{
    agent: Agent;
    contribution: SkillContribution;
}>;

export type SkillContribution = Readonly<{
    instructions: AgentInstructions;
    tools: ToolSet;
    /** Check ordinary user/state tools before contributed tools are merged. */
    checkTools(tools: ToolSet): void;
}>;

For custom reviewed values or loaders, adapt the raw contract before using a skills array:

ts
import type { SkillInput } from '@chamvi/core';
import type { ISkillSource, Skill } from '@chamvi/skills';

function defineSkill(value: Skill): Skill & SkillInput;
function defineSkillSource(value: ISkillSource): ISkillSource & SkillInput;

Import both functions from @chamvi/skills. They validate/copy supplied values and attach the runtime without I/O or registration. They do not independently fetch or hash-verify supplied text; custom loaders own retrieval and verification against reviewed pins. createSkillCatalog(rawSkills) creates a standalone manual catalog, not a first-class engine input.

gitSkills ​

ts
import { gitSkills } from '@chamvi/skills';

function gitSkills(options: GitSkillsOptions): ISkillSource & SkillInput;
ts
/*
 * Copyright (c) 2026.
 * Author Peter Placzek (tada5hi)
 * For the full copyright and license information,
 * view the LICENSE file that was distributed with this source code.
 */

import type { SkillIdentity } from '../catalog/types.ts';

export type GitSkillsOptions = {
    source: string;
    revision: string;
    directory: string;
    approved: readonly SkillIdentity[];
};

This pure factory copies source configuration and reviewed headers. Pass its result to app/engine skills defaults or an agent's extra skills. Engine preparation verifies exactly the declared identities before publishing immutable results. No /module entry or app module is needed. See Approved skills for startup ordering, cancellation, deployment and replay limits.

Local snapshots and manual catalogs ​

ts
type ApprovedSkill = {
    name: string;
    version: string;
    directory: string; // absolute runtime path
    hash: string; // independently reviewed lowercase SHA-256 digest
};

function hashSkillBundle(directory: string): string;
function loadSkillBundle(approved: ApprovedSkill): Skill & SkillInput;
function loadSkillBundles(approved: readonly ApprovedSkill[]): SkillCatalog;

The singular loader verifies and returns a ready snapshot for first-class skills arrays. The plural loader returns the supported manual catalog (skills, metadata-only instructions, tools); callers compose it explicitly and guard load_skill collisions. Hashing is a separate review aid and never grants approval. All helpers read only when explicitly called; imports do no I/O. Bundle bounds and digest framing are in the guide.

prepareSkills ​

ts
function prepareSkills(options: PrepareSkillsOptions): Promise<readonly PreparedSkill[]>;
ts
/*
 * Copyright (c) 2026.
 * Author Peter Placzek (tada5hi)
 * For the full copyright and license information,
 * view the LICENSE file that was distributed with this source code.
 */

export type PreparedSkill = {
    readonly name: string;
    readonly description: string;
    readonly path: string;
};

export type PrepareSkillsOptions = {
    signal?: AbortSignal;
    source: string;
    revision: string;
    names: readonly string[];
    directory: string;
    verify?: (skill: PreparedSkill) => void | Promise<void>;
};

source is an operator-configured local Git repository, Git URL or repository shorthand supported by the source parser. revision must be a full 40-character commit SHA; the cloned HEAD must match it. Embedded source refs/skill filters are refused. Local repositories are cloned at that commit, excluding working-tree edits. Names use ^[a-z][a-z0-9-]{0,63}$, must be unique, and must each resolve to exactly one discovered skill. The complete committed source must be symlink-free, including paths outside selected bundles. After checking HEAD, preparation inspects the committed Git tree and rejects any source link before discovery or copying; failed tree inspection also rejects preparation. Return order follows names; paths are absolute.

directory is a dedicated managed corpus, created if missing. Preparation refuses symlinks and nonempty unowned directories. A .chamvi-skills ownership marker lives at its root, outside the individual bundles. Successful replacement removes the previous corpus, including previously selected names. Do not put application files there.

Selected copies and optional verify callbacks run in sibling staging before promotion. A verifier must throw to reject and return no value on success; any returned value is refused, including a boolean comparison. A missing/ambiguous selection or failed verifier leaves old output intact. Promotion failure restores the previous directory; a filesystem failure that also prevents restoration preserves the backup in the sibling staging directory. A sibling .<directory-name>.chamvi-skills-lock guard refuses concurrent preparation of the same target. The guard records its owner (host, process id, process start, creation time). A held guard is refused with a ChamviError of code SKILL_PREPARATION_LOCKED_ERROR_CODE (skill_preparation_locked) whose message names the guard path. A guard a crashed preparer left (a force exit or an OOM kill during the uncancellable clone, on a persistent volume) is recovered on the next attempt when its process no longer runs on this host, when the same process id belongs to a restarted process or to this process without holding it (a failed release), or once it is older than the stale bound whatever host holds it: SKILL_PREPARATION_LOCK_STALE_MS (1 hour), or twice SKILLS_CLONE_TIMEOUT_MS when you raised the SDK's clone timeout above 30 minutes, so a live slow clone on another replica keeps its guard. Recovery takes a second <guard>.recover guard (itself removed after a minute), so two recovering processes never remove a guard a third took meanwhile. Cleanup normally removes checkout, staging and guard.

Failures carry no raw source URLs, credentials, file bodies or verifier errors, and fall in three classes:

ErrorWhenExit code of a run-job of the agent
InvalidAgentDefinitionErrorInvalid options, a commit other than the pin, a committed symlink, a missing or ambiguous selection, a refused verifier, an unowned target, a failing or flooding Git metadata command, an authentication failure, a repository the source host does not have2
ChamviError skill_preparation_lockedAnother preparation holds the target's guard1
ChamviError skill_preparation_failed (SKILL_PREPARATION_FAILED_ERROR_CODE)Retryable: a clone that failed otherwise (network, timeout, a pinned commit the SDK could not fetch: the SDK reports a network failure and a missing commit alike), a Git metadata command that timed out or was killed, a filesystem failure while staging, promoting or cleaning up1

The agents folder treats all three alike: the agent is an issue, the others start, and polling retries with backoff. Importing the package performs no preparation. The package never changes the process cwd, invokes the skills CLI or executes source scripts. It copies source assets using the SDK's installer rules; independently reviewed bundle hashes detect omissions or changed bytes. Git LFS content remains pointer files. Preparation alone grants no runtime approval or tools.

Verified runtime loading ​

Review and record content hashes independently, then verify staged copies before replacement. This example's imports and public types remain the same when Chamvi replaces its internal source backend:

ts
import { InvalidAgentDefinitionError } from '@chamvi/core';
import { hashSkillBundle, loadSkillBundles, prepareSkills } from '@chamvi/skills';
import type { PrepareSkillsOptions, PreparedSkill } from '@chamvi/skills';

// Call in an explicit setup/build task, with separately reviewed pins from app configuration.
export async function prepareCatalog(options: Omit<PrepareSkillsOptions, 'verify'>, pins: Readonly<Record<string, string>>) {
    const prepared: readonly PreparedSkill[] = await prepareSkills({
        ...options,
        verify(skill) {
            if (hashSkillBundle(skill.path) !== pins[skill.name]) {
                throw new InvalidAgentDefinitionError('Skill content does not match the reviewed pin.');
            }
        },
    });

    return loadSkillBundles(prepared.map((skill) => ({
        name: skill.name,
        directory: skill.path,
        version: options.revision,
        hash: pins[skill.name]!,
    })));
}

The app owns source/commit configuration, role profiles and reviewed pins. Combine selected names and pins before constructing one manual catalog. Append catalog.instructions to role instructions and compose catalog.tools explicitly, checking for a load_skill collision. This manual workflow may run in a setup/build task with already deployed assets. For runtime installation, prefer gitSkills() and let the engine own preparation and cancellation. See Approved local skills for runtime validation, permissions and deployment.

Chamvi owns the temporary commit-pinned backend. Consumers do not import its SDK, fork or types. Backend replacement criteria are tracked in issue #97.

signal is optional for prepareSkills. Cancellation is checked around clone/copy/verification phases and before promotion. Native Git metadata commands receive it; SDK cloning is awaited to actual settlement before cleanup. Promotion/rollback is completed once entered. Stop may wait for cloning; no bounded-fast-shutdown guarantee is made. Git providers require runtime Git (absent from the base Node 26 image), writable corpus/tmp space, source credentials/network and sufficient startup probe time.

The pinned SDK's cloneRepo falls back to the host's own credentials when an HTTPS clone of a GitHub repository fails authentication: it runs gh auth status and gh repo clone with the host's GitHub CLI login, then retries over SSH (git@github.com:<owner>/<repo>.git, GIT_SSH_COMMAND or ssh -o BatchMode=yes) with the host's SSH agent and keys. On a server this means a private source can be fetched with whatever gh login, ~/.ssh keys or forwarded agent the process can reach, not only the credentials you configured for Git. Run the app as a user without a gh login or SSH keys (the base image's uid 1000 has neither), or give it exactly the read-only deploy credential the source needs. Prebuilt local snapshots need only the deployed readable bundle files.