Hacker News

Show HN: Skillscript – A declarative, sandboxed language for tool orchestration

The Problem

AI agents are mostly transient. Every routine task is re-derived from prose reasoning. The agent that summarized a thread yesterday will summarize one tomorrow by reasoning from scratch about how to summarize threads, burning frontier inference on a procedure with a known shape, a known output format, and known failure modes.

The waste compounds in three directions: cost (every routine operation runs through the most expensive reasoning layer in the system), latency (every operation pays the full inference cost), and drift (the same task produces slightly different results each invocation because nothing crystallizes).

The deeper problem is that agents have no substrate to write themselves down in. Agents are partly defined by what they can do, and what they can do is currently held entirely in a soft, transient form of reasoning at inference time. There's no hard form. No place for an agent to crystallize a learned procedure into something cheap to execute, cheap to inspect, and cheap to improve.

Most agent infrastructure projects today focus on memory - episodic recall, retrieval-augmented context, conversation summarization. Those projects answer "what does the agent know." They don't answer "what can the agent do" in any persistent, executable, inspectable form. Skillscript intends to answer the second question.

The Frame

Agents are code, and skillscript is the language they write themselves in. Not memory in the recall sense. Not prompt templates. Not configuration. Code, in the strict sense of named, typed, composable, executable artifacts that constitute capability.

A skillscript skill is a declarative recipe - a small program with a dependency DAG of typed operations - that an agent authors once and the runtime fires many times. Where typical agent code is procedural (Python scripts, TypeScript handlers), skillscript is orchestration-only: it composes calls into tools, models, and data stores through swappable connector contracts. Computation lives in tools; coordination lives in skills.

# Skill: hello
# Status: Approved
# Description: The canonical first-run example.
# Vars: WHO=world

Hello, ${WHO}! Welcome to Skillscript.

That's a complete, runnable skill - and the body text is the output. No target, no boilerplate, no emit() ceremony: the runtime renders the body against the skill's variables and publishes it. The same shape scales to multi-stage DAGs that classify inputs, dispatch to LLMs, query data stores, branch on conditions, and orchestrate sub-agents, all in the same declarative grammar.

Why a New Language

The obvious alternative is "let the agent write Python." Python is Turing-complete, has mature tooling, and models write it well. For one-shot exploratory work or where computation matters, Python is the right tool, and we're not proposing anyone stop using it for that.

But agent-authored persistent automation has a different shape:

  • An agent (not a human) writes the code.
  • The code runs autonomously - cron-fired, event-triggered - with no human in the loop at execution time.
  • The work is dispatch-shaped: call a tool, classify a result, branch, call another tool. Not algorithmic computation.
  • The code needs to be auditable by humans at human tempo even though it's authored at agent tempo.

For this shape, Python's strengths invert into liabilities:

  • Turing completeness becomes a liability. An agent-authored script can do anything, including things the agent didn't realize were dangerous. subprocess.run, arbitrary network calls, file writes. None of these are gated. The blast radius of a buggy agent-authored script is the whole host.
  • Mature tooling doesn't help when the author isn't human. Debuggers and REPLs are for human iteration. Agents don't iterate that way.
  • Direct execution magnifies failure. When an agent ships a broken Python script to production cron, there's no validation layer. The script fails silently at 3am and the human discovers it the next day.
  • The package ecosystem becomes an unbounded attack surface. Agents that can pip install anything can install anything - including supply-chain-compromised packages. The package ecosystem assumes human review before adoption; agent adoption breaks that assumption.

Skillscript deliberately constrains expressiveness. It's not Turing complete. It can't eval, can't subprocess, can't import arbitrary code. The constraint is the safety story - enforced at the language level, not as an aspiration.

Why Not Just Have the Agent Write a Skill?

Skills (Anthropic/OpenAI) are the existing convention for giving agents named, reusable capabilities - hand-authored markdown that loads instructions into the model's context. They work, and skillscript is complementary to them, not competing.

The problem with hand-authoring is that both authoring populations produce badly-shaped artifacts when working in prose:

  • Agents authoring markdown produce artifacts shaped for humans, not agents - verbose explanations, hedging language, redundant context-setting, prose where structure would do. The result is expensive to load, noisy to parse, and hard to maintain.
  • Humans authoring markdown produce the opposite failure modes. Either ultra-terse and missing context, or kitchen-sink comprehensive in ways that bury the actual procedure under hedges and edge cases.

Making this a programming problem disciplines both populations into the right shape. The grammar doesn't permit rambling. The compiler emits structure, not prose-pretending-to-be-structure.

A skillscript skill compiles into an artifact of the same shape as a hand-authored Skill - # Skill: header, instructional markdown body - and that artifact can be loaded into an agent's context the same way. Skillscript is what you author in; the compiled Skill is what runs. Mature deployments use both: Skills as agent-facing capability descriptions, skillscript as the higher-leverage authoring layer underneath.

Three Kinds of Skill

Every skillscript skill is one of three shapes, determined by the relationship to a frontier agent:

Kind Output goes to Use case
Headless a downstream system or human, consumed asynchronously Cron-fired monitors, batch processors, autonomous workflows
Augmenting a frontier agent's reasoning context, immediately at session start or wake Session-start briefings, alerts, prepared context
Template a frontier agent's execution loop, as a prompt the agent runs itself Reusable recipes the agent fetches and follows

The kinds compose. A Headless monitor fires on cron, evaluates a condition, and routes into an Augmenting skill that wakes an agent with context, which itself references a Template skill for the agent to execute.

The three kinds describe the skill's role (who consumes the output). Orthogonal to that is how the result ships. The default is the body-text output template: any non-op text in the skill body is rendered against the skill's final variables and published as its canonical output - no ceremony, as in the hello skill above. For what the template can't express, three delivery ops are first-class - emit() for incremental/per-item output, $ data_write for data handoff, file_write for files - and they coexist with the template (which owns canonical output; emit lines are additional). See the Language Reference §1 for the full taxonomy.

The canonical use for emit is per-item output inside a loop, where there's no single template to render - one line per iteration:

# Vars: TICKETS=[...]

process:
  foreach T in ${TICKETS}:
    emit(text="${T.id}: ${T.urgency}")
default: process

What You Get

In exchange for constrained expressiveness:

  • Sandboxed grammar. The language can only do what configured connectors permit.
  • Declarative legibility. Skills are DAGs of typed dispatches. A human reading a skill sees exactly which tools get called, which data writes happen, which model prompts fire. The same source produces the same audit diagram every time.
  • Connector-mediated capability. Skills don't import packages, they invoke connectors - gated artifacts with curated tool surfaces. Python doesn't disappear from the system; it moves out of the agent's hands and into the connector implementations adopters write deliberately. The safety boundary moves to the connector edge.
  • Static validation before admission. A skill that fails the linter can't enter the library. Structural issues, missing dependencies, undeclared variables, mutation paths without confirmation gates are caught at authorship time, not at 3am.
  • Signed approval before effect. In secured mode, only skills carrying a valid operator signature perform effectful ops - an unapproved or tampered skill is inert no matter how it's dispatched (CLI, cron, /event, MCP, composition). Approval is an Ed25519 signature applied operator-side and verified on every execution; the runtime never holds the signing key.
  • Asymmetric cost. Routine work (classify, dispatch, transform) costs local-model tokens. The frontier model is reserved for the small fraction of work that actually needs frontier judgment.

The Bet

Most agent systems treat local models as substitutes for frontier inference. Call them instead of the frontier when latency or cost matters. Skillscript treats them as something different: delegation targets the frontier orchestrates.

The frontier composes the workflow; each LLM dispatch is the frontier handing off a bounded sub-task (classify a message, extract a field, judge whether two strings refer to the same thing, summarize a chunk, format a response) to a local or smaller model and consuming the result.

In skillscript, this isn't a separate "local-model interplay" pattern adopters bolt on - it's just MCP dispatch through a connector named whatever your substrate calls it. $ llm prompt="..." -> RESULT (one shop wires llm pointing at Ollama; another wires openai_chat against the OpenAI API; another wires claude_messages against Anthropic) lives next to any other $ tool args -> RESULT in the skill body, with the same op-level discipline, the same trace surface, the same lint coverage. The language has no built-in LLM keyword - adopters wire their substrate.

The cost shape that follows: routine work runs at local-model cost (free at scale, fast, private to the host); the frontier model intervenes only at orchestration boundaries and ambiguous cases. Customer data flowing through bounded sub-tasks never reaches an external API when the wired connector is local. The local-model layer becomes the privacy boundary, not a separate add-on.

Quickstart

npm install -g skillscript-runtime && skillfile init && skillfile dashboard .

Connector Model

A skill can invoke another skill via execute_skill(...):

Extracted: ${RESULT.final_vars.VALUE|trim}
parent:
  execute_skill(name="extract-json-number", JSON_BLOB="${RAW}", FIELD_PATH="total_count") -> RESULT
default: parent

The child skill runs to completion against the runtime's wired connectors, returns its full execution record (final vars, transcript, outputs), and binds to the parent's named variable. Field access on the bound result (${RESULT.final_vars.X}) lets the parent reach into whatever the child produced. Composition is what makes skill libraries accumulate - utility skills (extract-json-number, summarize-thread, classify-urgency) authored once, orchestrated forever. You can dry-run a multi-skill chain before committing to it.

Augmenting and Template skills deliver to a frontier agent through AgentConnector - a substrate-neutral seam. A Headless monitor detects a condition and either resolves silently or calls AgentConnector.deliver(...); your impl decides where that lands - a data store the agent reads next session, a chat thread, a push notification, a tmux pane, a webhook, anything that wakes the agent. The runtime ships a no-op default; production wires their own. Skills don't know what they're waking into, and the substrate doesn't know what triggered them - the contract handles the seam. (See Connector Contract Reference to implement one.)

Configuration & Security Knobs

Skills have an execution model orthogonal to their kind. A dynamic skill requires the Skillscript runtime to execute - the runtime walks the DAG, fires dispatches against wired connectors, threads outputs. A static skill compiles to a portable artifact that any agent capable of reading prose can execute without the runtime.

The static case is shareable artifacts: a skill whose body is just a template or emit(...) lines - no $/shell/file_* dispatches - compiles to a self-contained recipe you can email, post, or hand to an agent in another environment, which executes the steps with its own tools. The skill becomes the deliverable.

Template-kind skills are the canonical static shape; Headless and Augmenting are usually dynamic. The axes are independent - author the combination the work calls for.

# A static recipe (no runtime dispatches; just procedure + data)
# Skill: triage-customer-tickets
# Status: Approved
# Vars: TICKETS_JSON=[...]

For each ticket in the input, classify urgency as critical/normal/low.
For critical tickets, suggest immediate owner from the runbook.

Input: ${TICKETS_JSON}

That compiles to a procedure + data bundle a recipient can run anywhere.

CLI

The runtime ships with a full CLI for skill authoring, validation, execution, and dashboard management. See the Quickstart for initial setup commands.

MCP Server Surface

The runtime exposes an MCP server surface for integration with MCP-compatible tools and agents.

Examples

See the Examples section in the repository for complete skill examples covering all three kinds and common patterns.

Architecture and Deep Documentation

For the full architecture, language reference, connector contract reference, and deployment guide, see the project documentation.

Status

Active development. See the repository for current status and roadmap.

Contributing

Contributions welcome. See the contributing guide in the repository.

License

Standard open source license. See LICENSE in the repository.

Comments

No comments yet. Start the discussion.