{"slug": "show-hn-skillscript-a-declarative-sandboxed-language-for-tool-orchestration", "title": "Show HN: Skillscript – A declarative, sandboxed language for tool orchestration", "summary": "Skillscript is a new declarative, sandboxed programming language designed for AI agents to write persistent, executable procedures, reducing cost, latency, and drift compared to transient reasoning. The language allows agents to crystallize learned routines into composable skills that can be reused and inspected.", "body_md": "*A language for agents to write themselves in.*\n\nTL;DR—`npm install -g skillscript-runtime && skillfile init && skillfile dashboard`\n\n. See[Quickstart].\n\n[The problem](#the-problem)[The frame](#the-frame)[Why a new language](#why-a-new-language)[Why not just have the agent write a Skill?](#why-not-just-have-the-agent-write-a-skill)[Three kinds of skill](#three-kinds-of-skill)[What you get](#what-you-get)[The bet](#the-bet)[Quickstart](#quickstart)[Connector model](#connector-model)[Configuration & security knobs](#configuration--security-knobs)[CLI](#cli)[MCP server surface](#mcp-server-surface)[Examples](#examples)[Architecture and deep documentation](#architecture-and-deep-documentation)[Status](#status)[Contributing](#contributing)[License](#license)\n\nAI 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.\n\nThe 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).\n\nThe 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.\n\nMost 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.\n\nSkillscript intends to answer the second question.\n\n**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.\n\nA 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.\n\n```\n# Skill: hello\n# Status: Approved\n# Description: The canonical first-run example.\n# Vars: WHO=world\n\nHello, ${WHO}!\nWelcome to Skillscript.\n```\n\nThat's a complete, runnable skill — and the body text **is** the output. No target, no boilerplate, no `emit()`\n\nceremony: 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.\n\nThe 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.\n\nBut agent-authored *persistent* automation has a different shape:\n\n- An\n**agent**(not a human) writes the code. - The code runs\n**autonomously**— cron-fired, event-triggered — with no human in the loop at execution time. - The work is\n**dispatch-shaped**: call a tool, classify a result, branch, call another tool. Not algorithmic computation. - The code needs to be\n**auditable by humans at human tempo** even though it's authored at agent tempo.\n\nFor this shape, Python's strengths invert into liabilities:\n\n**Turing completeness becomes a liability.** An agent-authored script can do anything including things the agent didn't realize were dangerous.`subprocess.run`\n\n, 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`\n\nanything can install anything — including supply-chain-compromised packages. The package ecosystem assumes human review before adoption; agent adoption breaks that assumption.\n\nSkillscript deliberately constrains expressiveness. It's not Turing complete. It can't `eval`\n\n, can't `subprocess`\n\n, can't import arbitrary code. **The constraint is the safety story** — enforced at the language level, not as an aspiration. In exchange:\n\n**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`\n\n, 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.\n\nSkills (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.\n\nThe problem with hand-authoring is that **both authoring populations produce badly-shaped artifacts when working in prose:**\n\n**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.\n\nMaking 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.\n\nA skillscript skill **compiles** into an artifact of the same shape as a hand-authored Skill — `# Skill: <name>`\n\nheader, 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.\n\nEvery skillscript skill is one of three shapes, determined by the relationship to a frontier agent:\n\n| Kind | Output goes to | Use case |\n|---|---|---|\nHeadless |\na downstream system or human, consumed asynchronously | Cron-fired monitors, batch processors, autonomous workflows |\nAugmenting |\na frontier agent's reasoning context, immediately at session start or wake | Session-start briefings, alerts, prepared context |\nTemplate |\na frontier agent's execution loop, as a prompt the agent runs itself | Reusable recipes the agent fetches and follows |\n\nThe 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.\n\nThe 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`\n\nskill above. For what the template can't express, three delivery ops are first-class — `emit()`\n\nfor incremental/per-item output, `$ data_write`\n\nfor data handoff, `file_write`\n\nfor files — and they coexist with the template (which owns canonical output; `emit`\n\nlines are additional). See the [Language Reference](/sshwarts/skillscript/blob/main/docs/language-reference.md) §1 for the full taxonomy.\n\nThe canonical use for `emit`\n\nis per-item output inside a loop, where there's no single template to render — one line per iteration:\n\n```\n# Vars: TICKETS=[...]\n\nprocess:\n    foreach T in ${TICKETS}:\n        emit(text=\"${T.id}: ${T.urgency}\")\n\ndefault: process\n```\n\nMost 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.\n\nIn 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`\n\n(one shop wires `llm`\n\npointing at Ollama; another wires `openai_chat`\n\nagainst the OpenAI API; another wires `claude_messages`\n\nagainst Anthropic) lives next to any other `$ tool args -> RESULT`\n\nin 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.\n\nThe 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.\n\nA skill can invoke another skill via `execute_skill(...)`\n\n:\n\n```\nExtracted: ${RESULT.final_vars.VALUE|trim}\n\nparent:\n    execute_skill(name=\"extract-json-number\", JSON_BLOB=\"${RAW}\", FIELD_PATH=\"total_count\") -> RESULT\n\ndefault: parent\n```\n\nThe 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}`\n\n) lets the parent reach into whatever the child produced.\n\nComposition is what makes skill libraries accumulate — utility skills (`extract-json-number`\n\n, `summarize-thread`\n\n, `classify-urgency`\n\n) authored once, orchestrated forever. You can dry-run a multi-skill chain before committing to it.\n\nAugmenting and Template skills deliver to a frontier agent through `AgentConnector`\n\n— a substrate-neutral seam. A Headless monitor detects a condition and either resolves silently or calls `AgentConnector.deliver(...)`\n\n; 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](/sshwarts/skillscript/blob/main/docs/connector-contract-reference.md) to implement one.)\n\nSkills 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.\n\nThe static case is shareable artifacts: a skill whose body is just a template or `emit(...)`\n\nlines — no `$`\n\n/`shell`\n\n/`file_*`\n\ndispatches — 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.\n\n```\n# A static recipe (no runtime dispatches; just procedure + data)\n# Skill: triage-customer-tickets\n# Status: Approved\n# Vars: TICKETS_JSON=[...]\n\nFor each ticket in the input, classify urgency as critical/normal/low.\nFor critical tickets, suggest immediate owner from the runbook.\nInput: ${TICKETS_JSON}\n```\n\nThat compiles to a procedure + data bundle a recipient can run anywhere.\n\n**For operators:**\n\n*Cost reduction at scale.*Routine operations stop hitting frontier inference. As the library matures, an increasing fraction of agent work executes on cheaper substrate, with the frontier model invoked only for orchestration and judgment.*Auditability.*Agent behavior becomes inspectable by reading skills, not by trusting agent narration. Renderer, linter, and conformance tests operate on parsed skillscript regardless of where it's stored.*Safety boundaries that scale.*The runtime bounds what skills can do via connector configuration, independent of what the authoring agent's tool surface looks like. Mutating operations require explicit user confirmation as a language primitive — visible to static analysis, not dependent on author discipline.*Behavioral consistency.*Procedures don't drift across invocations because the procedure is stored, not re-derived. When the procedure needs to change, the change is a versioned edit, not a hope that the agent reasons identically next time.\n\n**For agent capability:**\n\n*Reduced token budget on routine work.*Authoring a skill is a one-time cost paid against an indefinite stream of cheap executions.*Composition over re-derivation.*New tasks built by orchestrating existing skills rather than starting from scratch. Capability accumulates rather than evaporating at the end of each invocation.\n\nSkillscript bets that **the majority of agent-authored automation work is dispatch-shaped, not computation-shaped**. Neither agents nor humans produce well-shaped procedural artifacts when authoring in prose. Both populations need the structural discipline of a programming language to converge on the right shape for the work, the audience that runs it, and the audit tooling that has to operate on it.\n\nIf that bet is wrong, skillscript stays a nice niche tool. If it's right, skillscript becomes a default substrate for agent-fired automation in the same way SQL became the default substrate for data access: declarative, composable, auditable, and outliving any specific runtime underneath it.\n\nSkillscript is **operated by a human and authored with an agent**. You install and run the runtime, wire it into your agent as an MCP server, then build skills together — the agent writes them (they land as `Draft`\n\n), and you approve what's allowed to run. The three steps below follow that division of labor.\n\nFastest path — the defaults work; this gets you a running server on `http://localhost:7878`\n\n:\n\n```\nnpm install -g skillscript-runtime && skillfile init && skillfile dashboard\n```\n\n`init`\n\nscaffolds `~/.skillscript/`\n\n(config, signing keys, demo skills); `dashboard`\n\nis a foreground server (Ctrl-C to stop). The annotated breakdown:\n\n```\nnpm install -g skillscript-runtime\nskillfile init             # scaffolds ~/.skillscript/ — config, signing keys, demo skills\n```\n\nIf\n\n`npm install -g`\n\nfails with`EACCES: permission denied`\n\n, your global prefix is root-owned (system-managed Node). Re-run with`sudo`\n\n, or use an nvm-style prefix you own. Not a Skillscript issue — standard npm-global behavior.\n\nSet up your environment the way you want it — all optional; the defaults work:\n\n```\ncp ~/.skillscript/.env.example ~/.skillscript/.env\n# edit ~/.skillscript/.env — e.g. SKILLSCRIPT_PORT (default 7878),\n# SKILLSCRIPT_SECURED_MODE, the shell allowlist\n```\n\nStart the runtime — this is the MCP server your agent connects to:\n\n```\nskillfile dashboard --host 0.0.0.0 --port 7878    # foreground server — Ctrl-C to stop\n# then, in your browser, open http://localhost:7878\n```\n\nOnbinds all interfaces so a containerized client (e.g. NanoClaw) can reach the runtime across the namespace — this is the primary deployment posture. On a laptop or a shared/untrusted network it exposes the control surface; use`--host 0.0.0.0`\n\n:`--host 127.0.0.1`\n\nfor localhost-only, or set the`/event`\n\ningress auth token (`SKILLSCRIPT_EVENT_INGRESS_AUTH_TOKEN`\n\n) so the exposed surface isn't open.\n\nPrefer headless? `skillfile serve`\n\nruns the same MCP server without the dashboard SPA. Or run the container:\n\n```\ndocker run -p 7878:7878 -v $(pwd)/skills:/data/skills \\\n  -e SKILLSCRIPT_HOME=/data \\\n  ghcr.io/sshwarts/skillscript-runtime:latest\n```\n\nOn Claude Code (and similar hosts) the simplest path is to just ask: *\"Add the skillscript MCP server at http://localhost:7878/rpc\"* — the host writes the config for you. Or wire it manually:\n\n```\n{\n  \"mcpServers\": {\n    \"skillscript\": {\n      \"type\": \"http\",\n      \"url\": \"http://localhost:7878/rpc\"\n    }\n  }\n}\n```\n\nFor stdio-only clients, bridge it the same way you'd bridge any HTTP MCP — via `mcp-remote`\n\n:\n\n```\n{\n  \"mcpServers\": {\n    \"skillscript\": {\n      \"command\": \"npx\",\n      \"args\": [\"mcp-remote\", \"http://localhost:7878/rpc\"]\n    }\n  }\n}\n```\n\nThe agent learns the workflow on connect (the MCP server delivers its usage instructions automatically). Just ask it to build something:\n\n\"Author a skill that greets someone by name.\"\n\nThe agent discovers what's available with `skill_list`\n\n, checks the contract with `skill_preflight`\n\n, and authors via `skill_write`\n\n— the skill lands as **Draft**. You review and approve it (the dashboard's approve button, or `skillfile approve <name>`\n\n), and it's live. From there the agent runs it via `execute_skill`\n\n, or you can from the CLI with `skillfile execute <name>`\n\n.\n\nThe runtime starts in **unsecured** mode, where a bare `# Status: Approved`\n\nis sufficient and approval is one click. For a deployment that should only run key-signed skills, set `SKILLSCRIPT_SECURED_MODE=true`\n\n— approval then signs with your operator key. See [Approval + secured mode](/sshwarts/skillscript/blob/main/docs/adopter-playbook.md#approval--secured-mode).\n\nThe hello example is a single static target. A more representative shape is a cron-fired skill that pulls data, processes it, and delivers via file. The example below uses only runtime-intrinsic ops (`shell`\n\n, `file_write`\n\n, `emit`\n\n) — no adopter-wired connectors. The runtime gates shell binaries and filesystem paths default-deny, so you allowlist exactly what this skill touches before it runs:\n\n```\n# Skill: daily-disk-check\n# Status: Approved\n# Description: Cron-fired daily disk usage snapshot to /var/log/skillscript/disk.txt.\n# Triggers: cron:\"0 6 * * *\"\n# Autonomous: true\n\nSnapshot written for ${NOW}.\n\nsnapshot:\n    shell(command=\"df -h --output=source,pcent,target\") -> USAGE\n    file_write(path=\"/var/log/skillscript/disk-${EVENT.fired_at_unix}.txt\",\n               content=\"${USAGE}\")\n\ndefault: snapshot\n```\n\nThe `df`\n\nbinary and the `/var/log/skillscript`\n\npath are both default-deny until the operator allowlists them:\n\n```\nexport SKILLSCRIPT_SHELL_ALLOWLIST=df\nexport SKILLSCRIPT_FS_ALLOWLIST=/var/log/skillscript\nskillfile execute daily-disk-check\n```\n\nFive things to notice:\n\n— the runtime registers the schedule at load; no external scheduler.`# Triggers: cron:\"...\"`\n\n— authorizes the mutation op (`# Autonomous: true`\n\n`file_write`\n\n) to fire without per-call confirmation; without it, each mutation needs an inline`approved=\"<reason>\"`\n\n.— ambient refs the runtime fills per-fire (`${EVENT.fired_at_unix}`\n\n+`${NOW}`\n\n`EVENT.*`\n\n= trigger payload;`NOW`\n\n= dispatch-time ISO timestamp).**Body text above**— rendered against final vars and published as canonical output; no`snapshot:`\n\nis the output template`emit()`\n\nneeded.**Default-deny allowlists**—`shell`\n\n/`file_*`\n\nops refuse until the operator allowlists the binary + path roots; the author can't escape it (see[Configuration & security knobs](#configuration--security-knobs)).\n\nSwap in `$ ticketing_search`\n\n, `$ llm`\n\n, `$ data_write`\n\nonce you've wired connectors, and the same skill shape becomes a real triage pipeline.\n\nSkills don't know what they're talking to. Five contracts decouple language from substrate:\n\n| Contract | Purpose | Base config |\n|---|---|---|\n`SkillStore` |\nSkill source persistence | `FilesystemSkillStore` (default); switch via `substrate.skill_store` in `connectors.json` |\n`DataStore` |\nGeneric data persistence with query | `SqliteDataStore` (conditional on dbPath); switch via `substrate.data_store` |\n`LocalModel` |\nLocal LLM dispatch | null (adopter wires explicitly via `substrate.local_model` ) |\n`McpConnector` |\nMCP tool invocation — external dispatch | adopter wires named instances in `connectors.json` |\n`AgentConnector` |\nDelivery to a frontier agent | adopter wires explicitly (no bundled default) |\n\nRuntime hosts (MCP server + web dashboard) honor whichever substrate the deployment configures. Authoring CLI commands (`skillfile compile`\n\n, `skillfile lint`\n\n, `skillfile audit`\n\n, `skillfile list`\n\n) stay filesystem-pinned by design — they're the FS-authoring loop.\n\nSee for the full substrate config reference.\n\n`docs/configuration.md`\n\nWire your own by implementing the interface and registering in `connectors.json`\n\n. See [ docs/language-reference.md](/sshwarts/skillscript/blob/main/docs/language-reference.md) §10 for full contracts.\n\nPer-host configuration. The runtime loads it at startup. Two top-level concerns:\n\n— which`substrate`\n\n`SkillStore`\n\n/`DataStore`\n\n/`LocalModel`\n\nthe runtime hosts use**Named MCP connector instances**— each becomes a connector referenced via`$ <name>`\n\nin skill source\n\n```\n{\n  \"substrate\": {\n    \"skill_store\": \"sqlite\",\n    \"data_store\": \"sqlite\",\n    \"local_model\": null\n  },\n\n  \"youtrack\": {\n    \"class\": \"RemoteMcpConnector\",\n    \"config\": {\n      \"command\": \"npx\",\n      \"args\": [\"mcp-remote\", \"https://example.youtrack.cloud/mcp\"],\n      \"env\": { \"AUTH_HEADER\": \"Bearer ${YOUTRACK_TOKEN}\" }\n    }\n  }\n}\n```\n\nSubstrate short-form (`\"sqlite\"`\n\netc.) wires bundled defaults. Object form (`{type, config}`\n\n) overrides config. See for the full schema + adopter-custom impl path.\n\n`docs/configuration.md`\n\n**Credentials** resolve via `${VAR}`\n\nsubstitution — `\"AUTH_HEADER\": \"Bearer ${YOUTRACK_TOKEN}\"`\n\npulls `${NAME}`\n\nfrom `process.env`\n\nat load time (a missing var is a clear startup error, not a silent empty string). Commit the `${...}`\n\nreferences; keep the real values in your deployment environment.\n\n**Connector classes are a fixed, recognized set** (no arbitrary plugin loading — deliberate). Introspect what your runtime supports with `runtime_capabilities({include:[\"mcpConnectorClasses\"]})`\n\n.\n\nOperator settings come from `$SKILLSCRIPT_HOME/.env`\n\n(auto-loaded by the CLI), the shell environment, or `skillscript.config.json`\n\n. The first three are **default-deny** boundaries you opt into, not out of; the fourth provisions named secrets a skill can use but never read — a skill can only do what the operator has permitted:\n\n| Knob | Default | Effect |\n|---|---|---|\n`SKILLSCRIPT_SHELL_ALLOWLIST` |\ndeny-all | which binaries `shell(...)` may invoke |\n`SKILLSCRIPT_FS_ALLOWLIST` |\ndeny-all | which path roots `file_read` / `file_write` may touch |\n`SKILLSCRIPT_SECURED_MODE` |\noff | require an operator signature for any effectful op (unapproved skills inert) |\n`SKILLSCRIPT_SECRET_<NAME>` |\nunset | a value a skill references as `{{secret.NAME}}` and uses at a sink (`shell` / `$ connector.tool` ) — never readable, emittable, or traced |\n\n`skillfile shell-audit`\n\nenumerates the binaries your skill corpus needs, ready to paste into the allowlist. The full env-var surface (ports, timeouts, identity headers, `/event`\n\ningress, approval key paths, dashboard auth) lives in the ** Configuration reference**.\n\nThe CLI covers the full authoring + ops lifecycle:\n\n| Command | Purpose |\n|---|---|\n`skillfile init` |\nScaffold the `~/.skillscript/` tree + bundled examples (provisions keys, locally approves the demos) |\n`skillfile execute <path|name>` |\nExecute a skill against configured connectors (mirrors the `execute_skill` MCP tool) |\n`skillfile compile <path|name>` |\nCompile a skill to its rendered artifact |\n`skillfile audit <provenance-path>` |\nDetect recompile-staleness via the `.provenance.json` sidecar |\n`skillfile lint <path|name>` |\nTier-1/2/3 lint diagnostics |\n`skillfile list` |\nList available skills in the configured SkillStore |\n`skillfile fires <skill>` |\nRecent fire history with trace IDs |\n`skillfile diagram <path|name>` |\nMermaid DAG visualization |\n`skillfile sign <path|name>` |\nGenerate content-hash signature |\n`skillfile verify <path|name> <hash>` |\nVerify against a known signature |\n`skillfile approve <name>` |\nApprove a stored skill (sign it for secured-mode execution) |\n`skillfile reapprove [<name>] [--apply]` |\nBatch re-sign Approved skills lacking a valid signature (pre-secured-mode migration) |\n`skillfile delete <name>` |\nPermanently delete a stored skill (destructive — no restore; aborts on dependents unless `--force` ). Operator-only — no agent/MCP delete surface |\n`skillfile replay <trace_id>` |\nRe-run from a captured trace |\n`skillfile health` |\nAggregate runtime health metrics |\n`skillfile serve [--port N]` |\nHeadless: scheduler + MCP server, no SPA |\n`skillfile dashboard [--port N]` |\nSame as `serve` plus dashboard SPA at `/` |\n\nRun `skillfile <command> --help`\n\nfor per-command flags. Use `serve`\n\nfor production / containerized deployments and `dashboard`\n\nfor development. CLI command names mirror the MCP tool names where they overlap (`execute`\n\n↔ `execute_skill`\n\n, `compile`\n\n↔ `compile_skill`\n\n, `lint`\n\n↔ `lint_skill`\n\n), so authors who learn one surface can transfer immediately to the other.\n\nThe runtime exposes its tools over MCP (HTTP at `/rpc`\n\n) for cold-client authoring + observability. It also serves `POST /event`\n\nfor external HTTP-triggered skills when `SKILLSCRIPT_EVENT_INGRESS_ENABLED=true`\n\n:\n\n| Category | Tools |\n|---|---|\n| Skill management | `skill_list` , `skill_preflight` , `skill_read` , `skill_status` , `skill_write` |\n| Data | `data_read` |\n| Authoring | `lint_skill` , `compile_skill` |\n| Composition | `execute_skill` |\n| Triggers | `list_triggers` , `register_trigger` , `unregister_trigger` , `set_trigger_enabled` |\n| Observability | `health_metrics` , `blocked_shell_attempts` |\n| Discovery | `runtime_capabilities` , `help` |\n\nThis is the **agent-reaches-MCP** path: any MCP-speaking agent (Claude, GPT, anything that speaks the protocol) can discover, author, validate, and run skills entirely over the wire. On connect the server hands the agent its usage contract automatically, and `help()`\n\nreturns a quickstart — each tool's own description carries the specifics (execution modes, the approval + mutation gates, dry-run preview), so an agent learns the surface without leaving the wire.\n\nCurated example skills in [ examples/](/sshwarts/skillscript/blob/main/examples), covering:\n\n- Multi-target DAG with\n`needs:`\n\ndependencies - Cron triggers with\n`# OnError:`\n\nfallback - Session-start\n`# Output: agent:`\n\ndelivery `# Requires:`\n\ncascade for compile-time data`inline(skill=...)`\n\nskill composition`execute_skill(...)`\n\nskill-to-skill composition\n\nEach example is annotated with the language pattern it demonstrates.\n\n— canonical spec. The single source of truth on syntax + semantics.[Language Reference](/sshwarts/skillscript/blob/main/docs/language-reference.md)—[Configuration](/sshwarts/skillscript/blob/main/docs/configuration.md)`connectors.json`\n\nsubstrate selection + named MCP connector wiring + adopter-custom impl path.— patterns for adopters embedding skillscript-runtime in their own deployment.[Adopter Playbook](/sshwarts/skillscript/blob/main/docs/adopter-playbook.md)— interface contracts for adopters writing their own connector impls.[Connector Contract Reference](/sshwarts/skillscript/blob/main/docs/connector-contract-reference.md)— the bundled DB-backed SkillStore: schema, semantics, forking checklist.[SqliteSkillStore](/sshwarts/skillscript/blob/main/docs/sqlite-skill-store.md)— pre-1.0 version transitions; the secured-mode re-approval migration,[Upgrading](/sshwarts/skillscript/blob/main/UPGRADING.md)`connectors.json`\n\nschema changes, and per-release upgrade-impact.**ROADMAP**—*coming soon to docs/*\n\nPre-1.0, no external adopters. Core language stable; connector contracts locked; distribution polish in progress.\n\nBug reports and feature requests welcome via Issues. PRs accepted but please open an Issue first to discuss the design — skillscript's value proposition rests on a constrained grammar, and not every \"small extension\" earns its keep.\n\nFor language design questions, see the cold-agent-driven precedent in [Open spec questions](/sshwarts/skillscript/blob/main/docs/language-reference.md#open-spec-questions): if cold-context sub-agents writing skills from spec alone hit the same syntactic friction across multiple authors, that's a signal to extend the language.\n\nMIT. See [LICENSE](/sshwarts/skillscript/blob/main/LICENSE).\n\n*\"Made by agents, for agents.\"* Skills are the agent's programming language.", "url": "https://wpnews.pro/news/show-hn-skillscript-a-declarative-sandboxed-language-for-tool-orchestration", "canonical_source": "https://github.com/sshwarts/skillscript", "published_at": "2026-07-12 13:34:00+00:00", "updated_at": "2026-07-12 16:16:12.475048+00:00", "lang": "en", "topics": ["ai-agents", "developer-tools", "artificial-intelligence"], "entities": ["Skillscript"], "alternates": {"html": "https://wpnews.pro/news/show-hn-skillscript-a-declarative-sandboxed-language-for-tool-orchestration", "markdown": "https://wpnews.pro/news/show-hn-skillscript-a-declarative-sandboxed-language-for-tool-orchestration.md", "text": "https://wpnews.pro/news/show-hn-skillscript-a-declarative-sandboxed-language-for-tool-orchestration.txt", "jsonld": "https://wpnews.pro/news/show-hn-skillscript-a-declarative-sandboxed-language-for-tool-orchestration.jsonld"}}