Code · Extensions
Extension API v1
The versioned package contract for declarative tools, agents, and Agent Skills plus explicitly trusted compiled runtime entrypoints.
Two execution layers
The declarative safe layer loads reviewed data contracts for tools, agents, and Agent Skills without importing package code. Add a trusted runtime only when the extension needs native slash commands, Ink UI, status or help content, keybindings, CLI flags, hooks, providers, or permission policy. Runtime installation requires --trust and executes compiled JavaScript inside the Autohand process.
Package layout
company.release-helper/
autohand.extension.json
README.md
src/
extension.ts
dist/
extension.mjs
tools/
release-range.json
agents/
release-planner.md
skills/
release-workflow/
SKILL.mdOnly files declared in autohand.extension.json contribute capabilities. Source, README, license, tests, fixtures, and bundled dependencies may live beside them, but Autohand neither discovers undeclared capabilities nor compiles TypeScript or installs dependencies during extension installation.
Manifest
{
"$schema": "https://raw.githubusercontent.com/autohandai/code-extensions/main/schema/autohand.extension.schema.json",
"schemaVersion": 1,
"extensionApi": 1,
"id": "company.release-helper",
"name": "Release Helper",
"version": "1.0.0",
"description": "Prepare and inspect releases.",
"license": "Apache-2.0",
"repository": "https://github.com/company/release-helper",
"contributes": {
"tools": ["tools/release-range.json"],
"agents": ["agents/release-planner.md"],
"skills": ["skills/release-workflow/SKILL.md"],
"runtime": ["dist/extension.mjs"]
}
}| Field | Contract |
|---|---|
schemaVersion | Required and exactly 1 |
extensionApi | Required and exactly 1 |
id | Lowercase qualified id such as company.extension-name, 3–100 characters |
version | Strict major.minor.patch semver |
name / description | Required human-readable metadata |
license / repository | Optional publishing metadata |
contributes | At least one non-empty tools, agents, skills, or runtime list; at most 100 contained paths per list |
The manifest is strict. Unknown fields and duplicate JSON keys are rejected so a typo cannot silently change package behavior.
Tool contribution
{
"name": "find_todos",
"description": "Find TODO comments under a tracked path",
"parameters": {
"type": "object",
"properties": {
"path": {
"type": "string",
"description": "Repository-relative file or directory"
}
},
"required": ["path"]
},
"handler": "git grep -n TODO -- {{path}}",
"source": "user"
}Tool names use lower snake case. parameters must be a JSON Schema object. Every handler placeholder must be declared and required; values are shell escaped when rendered. The handler is screened for unsafe patterns at validation and still requires normal authorization when invoked.
Never embed credentials. Extension packages can be inspected and shared. Let commands read required secrets from the user's environment, and document the variable without including its value.
Agent contribution
JSON agents use the existing fields description, systemPrompt, tools, and optional model. Markdown agents use the file name as the agent name and may declare frontmatter:
---
description: Review maintainability risks
tools: read_file, fff_grep, find_todos
---
Review the requested code and return evidence-backed findings.
Preserve working contracts and propose the smallest safe remediation.If frontmatter omits tools, the definition requests all available tools. That does not grant access: Autohand resolves names against the final runtime registry, applies context filtering, and enforces permissions on every call.
Agent Skill contribution
Declare the entrypoint SKILL.md for each portable skill. The file uses the normal Agent Skills contract and may reference files inside its own skill directory. Enabled extension skills appear in $ suggestions and /skills; an exact mention activates the instructions for that turn.
---
name: release-workflow
description: Prepare an evidence-backed release checklist.
---
Use `release_range` to establish the exact revision boundary.
Do not claim readiness without current validation evidence.Pi and other Agent Skills that already use SKILL.md are directly portable when their instructions and referenced resources remain valid in Autohand.
Trusted runtime entrypoints
A runtime path must resolve to compiled .js, .mjs, or .cjs. Validation checks the file and package contract without importing it. Installation requires --trust; trusted code then runs inside the Autohand process with the same operating-system access and is not sandboxed.
export async function activate(api) {
api.commands.register({
command: '/deploy',
description: 'Open the deployment workflow',
execute(context) {
return `Preparing ${context.args[0] || 'staging'}`;
},
});
return async () => {
// Release extension-owned resources.
};
}
export async function deactivate() {
// Optional cleanup on reload, disable, or removal.
}The module may export activate(api), a default activation function, or a default object with activate. api.version is 1. TypeScript authors can import ExtensionRuntimeAPI from autohand-cli for source checking, then ship compiled JavaScript.
--trust is a code-execution decision. It is neither a sandbox nor a permission shortcut. Review source, emitted code, and bundled dependencies before trusting a package.
Runtime registration surfaces
| API | Contribution | Key constraints |
|---|---|---|
api.commands.register | Slash commands | Lowercase command such as /deploy; built-ins cannot be replaced. |
api.ui.registerView | Ink menus, dialogs, renderers, and editor-like views | Use host api.ui.React and api.ui.Ink; Autohand owns modal pause/resume and terminal cleanup. |
api.ui.setStatusLine, setHelpLine | Status/help segments | Stable extension-specific ids and semantic colors. |
api.keybindings.register | Keyboard shortcuts | Routes to a registered command; Escape, Enter, Ctrl+C, Ctrl+D, and Shift+Tab are reserved. |
api.cli.registerFlag | Startup options | Must include a unique long --kebab-case flag and register before Commander parses input. |
api.hooks.on | Lifecycle handlers | Uses the normal hook event and response contract. |
api.providers.register | LLM providers | Provider id uses extension:; settings live under extensionProviders. |
api.permissions.registerPolicy | Permission policy | May contribute lists, rules, tool patterns, and path/URL policy; cannot replace mode, decision cache, or immutable security. |
Registration is transactional per extension. One malformed, duplicate, reserved, or conflicting registration rejects that extension's activation instead of leaving a partial command, UI, provider, or policy surface.
Runtime provider configuration
{
"provider": "extension:company-release",
"extensionProviders": {
"extension:company-release": {
"model": "release-model",
"apiKey": "replace-locally",
"baseUrl": "https://models.example.com"
}
}
}Keep credentials in user configuration or environment variables, never in the package. Provider implementations receive their named extension settings and the complete root config and must implement Autohand's provider contract.
Path and input constraints
- Contribution paths use
/, are relative to the package root, unique in their list, and no longer than 240 characters. - Absolute paths, drive-letter paths, backslashes, empty segments,
.,.., NUL bytes, and path traversal are rejected. - Declared contributions must resolve to regular files inside the real package root. Contribution symlinks are rejected.
- Manifests are limited to 64 KiB; each contribution is limited to 256 KiB.
- Manifest and contribution text must be valid UTF-8.
- A package cannot reuse a built-in, standalone, or already-active declarative or runtime identity.
Compatibility and publishing contract
Package against extensionApi: 1, validate with the oldest Autohand Code version your team supports, and test both linked and copied installation. Keep each package independently installable and include purpose, validation, installation, trust, permission behavior, daily use, and removal instructions in its README.
Extension API v1 installs only from a local directory. For distribution, publish an immutable tag or release, have users check out that pinned version, and install the local directory. Installing directly from an unpinned remote URL or Git branch is intentionally unsupported.
autohand extensions validate ./company.release-helper
autohand extensions install ./company.release-helper --link --trust
autohand extensions show company.release-helper
autohand extensions doctor
autohand extensions disable company.release-helper
autohand extensions enable company.release-helper
autohand extensions remove company.release-helper --yes
autohand extensions install ./company.release-helper --trust
autohand extensions show company.release-helperRelease checklist
- Validate the source package and save machine-readable output in CI.
- Install with
--linkduring development; add--trustonly after reviewing every runtime file and bundled dependency. - Exercise every tool, agent, skill, command, view, line segment, shortcut, flag, hook, provider, and permission contribution the package declares.
- Confirm expected approval prompts and denial behavior; never test only unrestricted mode.
- Run
doctorwith both user and project extensions present. - Test disable, enable, and removal, including current-session refresh.
- Install a copied package and start a fresh Autohand process.
- Test on each supported operating system when handlers or paths are platform-sensitive.
- Run component tests plus a real PTY/Tuistory acceptance test for terminal UI and shortcut behavior.
- Publish an immutable version and keep the manifest version aligned with the release.
Ready to build? Follow Authoring your first declarative extension or Build a trusted runtime extension.