/** * v0.5 — SKILL.md frontmatter parser + validator. * * Per docs/plan/v0.5-workflow-maker.md §4 (schema), §11.4 (external corpus * round-trip), §11.5 (slash conflict / freq cap / stateful=false / meta * scanner). Anthropic Agent Skills compatible: `name` + `description` are * the only required fields; SoloSquad extensions are all optional. * * Two design choices that the round-trip case (`anthropics/skills` corpus) * depends on: * 1. `raw_frontmatter` preserves the YAML text verbatim. `writeSkillMd()` * re-emits it byte-for-byte. Re-serialization via `yaml.dump` is opt-in * (`serializeFrontmatter()`) — migrations need it, ingest does not. * 2. `extra` collects unknown fields so forward-compat doesn't drop data. * * v0.5 forces `stateful: false` on every new SKILL — stateful actors are * out of scope until v0.6 trajectory extraction (§12). */ export declare const SKILL_SCHEMA_VERSION = 1; /** PM mode slashes (v0.3) — reserved, cannot be registered as triggers.slash. */ export declare const RESERVED_SLASHES: ReadonlySet; /** Per-workspace cap on freq-enabled SKILLs (v0.5 §13). */ export declare const FREQ_SKILL_CAP = 20; /** v1.3.2 §4 — Anthropic Agent Skills naming/description limits. * The kebab id rule + length ceiling are the shared §9.5 convention * (util/naming); SKILL_NAME_MAX is re-exported as the canonical alias. */ export declare const SKILL_NAME_MAX = 64; export declare const SKILL_DESCRIPTION_MAX = 1024; export type SkillScope = "agent" | "workspace" | "org" | "repo"; export type LoopModeKind = "spec-gate"; export interface FreqTrigger { keywords: string[]; window_turns: number; threshold: number; /** Default 6 — see v0.5 §7 hysteresis. */ cooldown_turns?: number; } export interface SkillTriggers { slash?: string[]; keyword?: string[]; freq?: FreqTrigger; /** PM may call this SKILL explicitly even without trigger match. */ explicit?: boolean; } export interface SkillInputs { required?: string[]; optional?: string[]; } export interface SkillLoopMode { kind: LoopModeKind; spec_path?: string; stop_when?: string; } export interface SkillBudget { per_call_usd?: number; daily_usd?: number; } /** * v1.3.6 §3.4 — PM-mode conventions. Previously written under a `pm_conventions:` * block on ~28 SKILLs but never parsed (fell into `extra` = dead metadata). Now * surfaced + validator-enforced so the field is load-bearing (primitive-core.md §3.7 ⑵): * - anti_sycophancy / post_labeling — global discipline (default true); a SKILL * may set post_labeling:false for a documented exception. * - hard_gate — gate exit on exit_criteria (skill-level mirror of the workflow * stage hard_gate that workflow-validate already reads; §6.1). * - minimum_approaches — require comparing ≥N approaches before recommending. */ export interface SkillPmConventions { anti_sycophancy?: boolean; post_labeling?: boolean; hard_gate?: boolean; minimum_approaches?: number; } /** v0.6 §2.4 — 핸드오프 협업 패턴. v0.5에서는 `extra` bag으로 forward-compat 처리됐고 v0.6 출시 시점에 정식 필드로 격상. */ export type CollabPattern = "hierarchical" | "graph" | "dynamic"; /** * v0.8.2 §3.1 — dev_permissions sub-tree. SKILLs with `dev_capability: true` * declare what bash binaries they're allowed to invoke, whether outbound * network is OK, and whether `git push` / `gh pr merge` require user * confirmation. Workspace-level denylist (workspace.yaml.dev_capability. * bash_denylist) is always merged on top — SKILLs cannot override it. */ export interface SkillDevBashPerms { allowed?: string[]; denied?: string[]; } export interface SkillDevPushTargets { requires_confirmation?: boolean; } export interface SkillDevMergePolicy { /** Auto-merge is permanently `false` per v0.8.2 §2 / §3.1. */ auto?: boolean; } export interface SkillDevPermissions { bash?: SkillDevBashPerms; /** Outbound HTTP via curl/wget/etc. MCP servers are unaffected. */ network?: boolean; push_targets?: SkillDevPushTargets; merge?: SkillDevMergePolicy; } export interface SkillSpec { name: string; description: string; team?: string; stateful?: boolean; triggers?: SkillTriggers; inputs?: SkillInputs; outputs?: string[]; handoff_to?: string[]; scope?: SkillScope; confidence?: number; source?: string; loop_mode?: SkillLoopMode; budget?: SkillBudget; collab_pattern?: CollabPattern; /** v1.3.6 §3.4 — discovery/grouping category (kebab-case; org owns the taxonomy). */ category?: string; /** v1.3.6 §3.4 — PM-mode conventions (parsed + validated; formerly decorative). */ pm_conventions?: SkillPmConventions; /** v0.8.2 — SKILL declares it can perform code-modifying dev actions. */ dev_capability?: boolean; /** v0.8.2 — per-SKILL bash allowlist / push-confirm / merge policy. */ dev_permissions?: SkillDevPermissions; /** * v0.8.1 — explicit SKILL frontmatter schema version. Per * docs/plan/v0.8.1-security-lifecycle-pair.md §6.1 / §6.3. When absent * the validator emits a deprecation warning; bundled SKILL.md files were * backfilled in v0.8.1 via `scripts/inject-skill-schema-version.ts`. */ schema_version?: number; extra: Record; raw_frontmatter: string; body: string; } export interface SkillValidationError { code: string; message: string; field?: string; } export interface SkillValidationResult { ok: boolean; errors: SkillValidationError[]; warnings: SkillValidationError[]; } export interface WorkspaceValidationContext { /** Number of freq-enabled SKILLs already registered (this SKILL not counted). */ freq_skill_count?: number; /** Override reserved slashes (mostly for tests). */ reserved_slashes?: ReadonlySet; /** Expected directory name — when set, `name` must equal it (dir-match, §4). */ dir_name?: string; /** Reserved skill names that may not be used (workspace pass supplies these). */ reserved_names?: ReadonlySet; /** * Enforce SoloSquad naming convention (kebab-case ≤64, reserved names) as * errors. Off by default so *external/adopted* skills (Anthropic corpus, * §10 adopted repos) aren't rejected for style — the SoloSquad CLI sets it. */ strict_name?: boolean; } export declare class SkillParseError extends Error { source_path?: string | undefined; constructor(message: string, source_path?: string | undefined); } /** * Parse a SKILL.md file. Frontmatter (`---\n...\n---`) is required by * v0.5 — Anthropic spec also requires it. Bodies without frontmatter * throw `SkillParseError`. */ export declare function parseSkillMd(raw: string, source_path?: string): SkillSpec; /** * Validate a parsed SKILL against v0.5 invariants. The parser itself is * tolerant (drops malformed sub-fields silently for round-trip safety); * this is where we *reject* policy violations. */ export declare function validateSkill(spec: SkillSpec, ctx?: WorkspaceValidationContext): SkillValidationResult; /** * Re-emit a SkillSpec byte-for-byte using the captured raw frontmatter. * This is what `anthropics/skills` corpus round-trip tests use. */ export declare function writeSkillMd(spec: SkillSpec): string; /** * Serialize a (possibly modified) spec back to YAML frontmatter. Used by * migrations / author loop output where we *intentionally* update fields * and accept loss of original key order. Stable insertion order — Anthropic * required fields first, then SoloSquad extensions, then `extra`. */ export declare function serializeFrontmatter(spec: SkillSpec): string; /** Emit a fully serialized SKILL.md using `serializeFrontmatter()` + body. */ export declare function emitSkillMd(spec: SkillSpec): string;