local/skills/local-spec/SKILL.md
MUST be invoked before any work involving: authoring `kind: local` templates, `local.yml` files, inline `kind: local` template nodes in `charly.yml`, or the merge semantics between a `kind: local` template and the deploy that deploys it.
npx skillsauth add overthinkos/overthink-plugins local-specInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
kind: local declares a reusable candy-stack template that gets applied to a Linux filesystem (deployed by a local: deploy whose from: field selects this template). Unlike kind: pod / kind: vm / kind: k8s which wrap an image, a kind: local is defined entirely by its layers + install_opts + env — there's no OCI artifact backing it. The convention file is local.yml; templates may also be authored inline in charly.yml as top-level name-first nodes (<name>: { local: {…} }).
Legacy kind: host projects migrate via charly migrate.
# Name-first: the entity flattens to a top-level <name>: key whose `local:`
# block holds only scalars (description); every non-scalar field becomes a
# child node, and every plan step becomes its own child step node.
dev-workstation:
local:
description: Standard developer workstation profile # optional — plain string
dev-workstation-candy: # required (use [] for a placeholder; see below)
candy:
- ripgrep
- direnv
- uv
dev-workstation-install_opts: # optional — defaults merged 3-tier (CLI > deploy > template)
install_opts:
with_service: false
allow_repo_changes: true
dev-workstation-env: # optional
env:
- EDITOR=vim
dev-workstation-step-1: # optional — one intent keyword + inline Op
check: ripgrep is installed after deploy
command: rg --version
context: [deploy]
version: 2026.144.1443
dev-workstation:
local:
description: Dev workstation
dev-workstation-candy:
candy: [ripgrep, direnv]
dev-workstation-install_opts:
install_opts: {with_service: false, allow_repo_changes: true}
dev-workstation-env:
env: [EDITOR=vim]
ci-runner:
local:
description: CI runner profile
ci-runner-candy:
candy: [ripgrep, pixi, cargo-toolchain]
ci-runner-install_opts:
install_opts: {with_service: true, allow_root_tasks: true}
charly-cachyos-app:
local:
description: CachyOS DX (placeholder)
# Empty placeholder — `candy: []` is a load-time WARNING (allowed
# for staged template name reservation); a missing candy child node
# is a hard error.
charly-cachyos-app-candy:
candy: []
charly-cachyos-app-install_opts:
install_opts: {}
| Field | Required | Description |
|---|---|---|
| layers | Yes | Ordered candy stack. [] permitted as a placeholder (warning, not error). |
| install_opts | No | Default install gates. Deployment overrides merge on top. |
| env | No | Shell-profile env vars (KEY=VALUE). Deployment env wins on key collision. |
| description | No | Plain string — the profile's purpose; first line = the summary. |
| plan steps | No | Each step is its own child step node (<name>-step-N, or named by its id:) — one intent keyword: run: (state-change) / check: (deterministic probe) / agent-run: / agent-check: / include: — carrying prose plus, for run:/check:, an inline Op (verb + matchers + context:). Deploy-scope steps carry context: [deploy]. Merged with the deploy's step nodes. |
There is no status: or info: field; description is the human-facing summary string.
A kind: local deploy emits zero container-image fetch / build
steps. The deploy applies host packages + configs only. There is no
image: field; declaring one in legacy YAML hard-errors at
charly box validate time with a pointer to charly migrate.
Test-bed image preflight is the check entry point's job, not the
deploy's. When charly check run --on-host <name> resolves to a host
target, the runner walks the bed's plan steps, collects every distinct
step pod: value plus the bed's target image, and ensures each
image is present in local podman storage (LocalImageExists →
charly box pull → fall back to charly box build for short names that
resolve via cfg.Images). Operators who never run charly check run
never pay the image-fetch cost. See /charly-check:check "Image
preflight" and charly/check_image_preflight.go.
This invariant — "deploy fetches NOTHING speculative" — is codified
as a CLAUDE.md Key Rule and enforced at the type level: the
LocalSpec Go struct has no Images field, so the surface is
unreachable from any new code. Migration: charly migrate
(idempotent; rewrites legacy image: blocks under local.<name>
to a dated comment fence).
kind: local template + a deployWhen a local: deploy carries from: <template-name>:
| Field | Template provides | Deployment overrides | CLI overrides | Effective value |
|---|---|---|---|---|
| layers | base list | — | — | template.Layers |
| add_layers | — | extra list | --add-candy | deployment.AddCandies ++ CLI.AddCandy |
| effective layer order | — | — | — | template.Layers ++ deployment.AddCandies ++ CLI.AddCandy |
| install_opts.* (bool) | default | wins over template | wins over both | CLI > deployment > template |
| install_opts.builder_image | default "" | wins | wins over both | CLI > deployment > template |
| env | base list | extends + overrides on key | — | template.Env merged with deployment.Env (deployment-wins on collision) |
| plan | base plan | extends list | — | template.Plan ++ deployment.Plan |
The InstallOptsConfig.ApplyTo method is fill-empty — calling it on the deployment's opts first, then the template's, gives the priority chain automatically.
candy: [] is a placeholderA template with candy: [] is permitted as a stub for staged name reservation:
charly-cachyos-app:
local:
description: CachyOS DX (placeholder)
charly-cachyos-app-candy:
candy: []
charly-cachyos-app-install_opts:
install_opts: {}
charly box validate emits a WARNING but does not error. A missing candy child node entirely IS an error — its presence is the signal that the author intended a template.
/charly-local:local-deploy — the target: local deployment surface that consumes this template./charly-internals:local-infra — Go file map (local_spec.go, LocalSpec struct, findLocalSpec lookup)./charly-image:layer — candy authoring (the building blocks composed by templates)./charly-build:migrate — charly migrate migrates legacy kind: host/host.yml projects up to the node-form schema (every entity name-first inline in charly.yml). The charly-cachyos deploy applies the suffixed charly-cachyos-app kind: local template — within one document top-level names are globally unique, so the deploy keeps the user-facing name and the template it deploys is suffixed (see "Globally-unique names within one document" below).Every entity flattens to a top-level <name>: key, so within one document (the project-root charly.yml) top-level names must be globally unique — a deploy and the kind: local template it deploys cannot share a name (they would collide on one YAML key). The convention: keep the user-facing deploy name and suffix the template it deploys. The canonical example is charly-cachyos — the charly-cachyos deploy (local: { from: charly-cachyos-app, host: local }) deploys the charly-cachyos-app kind: local template; charly update charly-cachyos resolves to the deploy, which references the template via its from: charly-cachyos-app field. Cross-kind reuse of the SAME name across SEPARATE discovered files (a candy/redis + a box/redis) remains permitted. See CLAUDE.md "a single document's top-level node names are GLOBALLY UNIQUE".
MUST be invoked when the task involves authoring or editing kind: local templates, local.yml files, inline kind: local template nodes in charly.yml, or the merge between a template and the deploy that deploys it. Invoke this skill BEFORE reading Go source or launching Explore agents.
tools
Use when authoring or modifying a charly PLUGIN — a candy with a `plugin:` block that contributes Providers (verbs/kinds/deploy-targets/steps/builders/commands), its own CUE schema, builtin (compiled-in) or external (out-of-tree git repo). Covers the unified Provider model, the per-plugin CUE-schema contract (single source → Go params for dev + schema-over-Describe RPC for runtime), the SDK, and the loader.
tools
The CUE data-validation / configuration CLI (cue), pinned to v0.16.1. Use when working with the cue candy, installing the cue binary into a box or onto a target:local dev host, or running the offline schema-vendoring pipeline that feeds charly's egress validation.
tools
CUE EGRESS validation — validating (and, where it adds value, generating) the config files charly WRITES to a system BEFORE the bytes hit disk. MUST be invoked before working on charly/egress.go, the vendored schemas under candy/plugin-egress/egress-schemas/vendor/, the ValidateEgress / registerVendoredEgressKind path, the offline `task cue:vendor` pipeline, or adding an egress schema for any written artifact (cloud-init, k8s manifests, traefik routes, runtime config, install ledger, systemd/quadlet units, ssh_config, libvirt XML).
tools
Kubernetes cluster-probe declarative check verb — the `kube:` check verb (nodes, pods, ingress, storage class, addon health, apply/delete, and arbitrary resource GETs) served out-of-process by the candy/plugin-kube plugin (vendored client-go; no external kubectl required).