build/skills/pull/SKILL.md
Fetch an image from its registry into local container storage so deploy-mode commands can read its OCI labels. MUST be invoked before any work involving: charly box pull command, the ErrImageNotLocal error, fetching images by short name / fully-qualified ref / @github.com/... remote ref, or recovering deploy-mode commands that fail with "image X is not available locally".
npx skillsauth add overthinkos/overthink-plugins pullInstall 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.
charly box pull fetches an image from its registry into the local container
engine's storage so deploy-mode commands (charly shell, charly start, charly config,
charly alias add, etc.) can read its OCI labels via ExtractMetadata. It is a
pull-only operation — it does not start, configure, or restart any
service. That's charly update's job (see /charly-core:charly-update).
This command is the prerequisite for every deploy-mode operation on a fresh
host. Since the charly box refactor, deploy-mode commands no longer read
charly.yml — they read OCI labels + charly.yml only. If an image isn't
in local storage, the label read fails and the CLI surfaces a friendly
recommendation pointing here.
| Action | Command | Description |
|--------|---------|-------------|
| Pull short name | charly box pull jupyter | Resolves registry + tag via charly.yml (requires project directory) |
| Pull fully-qualified ref | charly box pull ghcr.io/overthinkos/jupyter:2026.108.56 | Pulls as-is, works from anywhere |
| Pull remote project ref | charly box pull @github.com/org/repo/image:v1 | Downloads repo, reads its charly.yml, pulls registry ref |
| Override tag (short name) | charly box pull jupyter --tag 2026.108.1 | Pull a specific CalVer tag |
| Override platform | charly box pull jupyter --platform linux/arm64 | Pull a specific platform |
cd ~/opencharly && charly box pull jupyter
Resolves <registry>/jupyter:<tag> via charly.yml. Equivalent to the
two-step:
charly box inspect jupyter --format registry # ghcr.io/overthinkos
charly box inspect jupyter --format tag # ghcr.io/overthinkos/jupyter:2026.108.56
# …then podman pull <that-ref>
…but packaged as a single command.
charly box pull ghcr.io/overthinkos/jupyter:2026.108.56
Runs podman pull <ref> directly. Useful from any directory, including
/tmp. Supports any registry that the underlying engine (podman/docker)
can authenticate against.
@github.com/...)charly box pull @github.com/overthinkos/overthink/jupyter:2026.108.56
charly box pull @github.com/overthinkos/overthink/jupyter # latest git tag
Downloads and caches the repo, reads its charly.yml, then pulls the
registry ref declared there. This is the only place @github.com/...
refs are accepted in charly. Deploy-mode commands (charly shell, charly start,
charly config, etc.) reject them with a message pointing users here.
charly box pull jupyter twice with the image
already local is a no-op (the engine's pull is layer-aware).charly update, this command does
not restart, reconfigure, or touch any running container.ResolveRuntime (podman by default; docker if configured).ErrImageNotLocal (the sentinel pattern)The CLI has a centralized design for "image not in local storage" errors. Every deploy-mode command inherits the same friendly recommendation without per-call-site code:
ExtractMetadata(engine, imageRef) in charly/labels.go returns
ErrImageNotLocal (wrapped with the image ref) when the image is absent
from local storage.EnsureImage(imageRef, rt) in charly/transfer.go returns the same sentinel
when the image is absent from both run and build engines.FormatCLIError in charly/image.go unwraps the sentinel at the top-level
error boundary in main() and renders:Error: image "X" is not available locally.
Run 'charly box pull X' to fetch it first
Any deploy-mode command that calls ExtractMetadata or EnsureImage
(shell, start, stop, config, deploy, update, remove, alias, vm, service,
cdp, wl, vnc, tmux, record, dbus, logs — essentially everything outside
charly box) automatically participates. If you're authoring a new command
that reads image metadata, just call ExtractMetadata and the
recommendation falls out for free.
charly update| Aspect | charly box pull | charly update |
|---|---|---|
| Side effects | None. Pulls bytes, prints digest. | Pulls, seeds data into volumes, restarts active service. |
| Required before deploy | Yes (first time only). | No — it calls pull internally if needed. |
| Use when | You want labels available but aren't ready to deploy. Or just installed charly on a fresh host. | You have a running service and want to roll to a new image version. |
Rule of thumb: pull is the prerequisite; update is the refresh.
Use charly box pull <image> once to seed local storage; use charly update <image> every time you bump the version afterward.
--tag <tag> (default: latest) — Image tag when resolving a short
name. Ignored for fully-qualified or remote-project refs (the tag is
embedded in the ref).--platform <platform> — Target platform (default: host). Passed to
podman pull --platform.# Install charly (see README Install section)
charly box pull jupyter # pull once; labels now readable
charly config jupyter # generate quadlet from labels + charly.yml
charly start jupyter # systemctl --user start
charly box pull @github.com/overthinkos/overthink/hermes:latest
charly config hermes
charly start hermes
charly shell jupyter
# Error: image "jupyter:latest" is not available locally.
# Run 'charly box pull jupyter:latest' to fetch it first
charly box pull jupyter:latest # follow the recommendation
charly shell jupyter # now works
Before the charly box refactor, deploy-mode commands had dual-mode logic:
try charly.yml first, fall back to OCI labels. That produced drift (the
two paths could diverge) and silently pulled-and-built remote refs as a
side effect of what looked like a simple charly shell @github.com/... call.
The refactor drew a hard line: deploy-mode commands read labels only;
build-mode commands (charly box …) read charly.yml only. charly box pull
is the bridge — it takes a build-mode identifier (short name or remote
repo ref) and produces a deploy-mode-consumable artifact (labels in local
storage).
charly box pull resolves charly.yml via os.Getwd() when given a short name (to resolve registry + tag). Override with -C <dir> / --dir <dir> / CHARLY_PROJECT_DIR=<dir>. Fully-qualified refs and @github.com/... remote refs don't need a project dir. See /charly-image:image "Project directory resolution".
/charly-image:image — family overview; charly box pull is one of 8 subcommands./charly-build:build — pulls and builds are orthogonal; build creates images,
pull fetches existing ones./charly-core:charly-update — rolls deployed services to a new image version
(pulls + data-seeds + restarts)./charly-build:inspect — print resolved ref from charly.yml without pulling./charly-core:shell, /charly-core:start, /charly-core:charly-config, /charly-automation:alias, /charly-vm:vm —
deploy-mode commands that require a pulled image./charly-core:deploy — charly.yml overlay semantics applied on top of the
labels pull materializes./charly-internals:go — ErrImageNotLocal / EnsureImage / ExtractMetadata
source locations (charly/labels.go, charly/transfer.go, charly/image.go).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).