Skip to content

plugin-task

Placement compiled-in (in-process)
Source github.com/opencharly/plugin-task/candy/plugin-task
Candy plugin-task

This plugin is listed in charly/charly.yml’s compiled_plugins:, so its providers are compiled into the charly binary and register in-process.

The reserved words this plugin serves:

  • task — command class
  • task — kind class
  • file-parity — verb class
  • git-submodules — verb class
  • module-pins — verb class
  • prune — verb class
  • splice-region — verb class
  • task — verb class

The generic declarative TASK runner for charly — the replacement for a Taskfile. Serves three task capabilities plus five generic maintenance verbs. Task capabilities: kind:task (a named, host-native, REUSABLE plan authored as a task: node in charly.yml, using the SAME #Step/#Op grammar a candy’s plan uses), command:task (charly task [list] [<name>] [--dry-run] [--json] [--param k=v] [--all]), and verb:task (compose a declared task into a candy/box/task plan). The engine reuses kit.RunPlan + a checkkit.VerbResolver over kit.ShellExecutor{} — the SAME engine candy/plugin-check drives — so there is no second execution engine. Any task a repository needs (build, test, lint, release, verify, notify) is a task entity; the task body is the base-schema #Task, validated host-side against #TaskValue (a task’s plan: [...#Step] references the base #Step grammar, which a self-contained plugin schema cannot carry).

Five GENERIC, domain-neutral MAINTENANCE verbs replace repository shell scripts, each parameterized by the repo’s own data (paths/pairs/pins) so the plugin stays reusable by any repository: verb:git-submodules (status/bump/ verify .gitmodules pins, incl. the policy-B comparison of a repo’s pins against a pinned checkout’s gitlinks), verb:file-parity (check/sync that paired files are byte-identical), verb:splice-region (splice a marked region from a fragment into a target), verb:module-pins (adopt a source go.mod’s require pins across every module a glob matches, then tidy), and verb:prune (garbage-collect MERGED-UPSTREAM session worktrees + branches across the project root and every submodule — the mechanical reaper the close-out contract’s per-session prose never provided; a branch is pruned only when its merge is PROVEN, and a CLOSED/abandoned PR is pruned only under an explicit opt-in).

COMPILED-IN by default (the command needs the host’s loaded project over the reverse channel; the maintenance verbs are host-native and act on the checkout); the kind/verb capabilities also serve out-of-process.

The CUE schema below is the authoritative grammar for this plugin’s input. It is the same single source that generates the plugin’s Go parameter types and answers the runtime Describe RPC, so this page cannot disagree with either.

// schema/task.cue — the SELF-CONTAINED CUE schema for plugin-task's authored
// plugin_input. It defines ONLY the plugin's own input defs (command-level and
// verb-level); the `kind:task` ENTITY body is the base-schema #Task, validated
// host-side against #TaskValue (a task's `plan: [...#Step]` references the base
// #Step grammar, which a self-contained plugin schema cannot carry — see
// /charly-internals:plugin "Why self-contained schemas").
//
// SELF-CONTAINED: package-less and references NO base def (the load gate
// compiles it standalone AND splices it onto the base), so it may use only
// plain primitives.
// #TaskInput is the typed input for a `task:` verb step composed into
// a candy/box/task plan (`- run: ... \n task: {task: build}`), so a plan can
// invoke another declared task.
#TaskInput: {
// task is the declared task entity name to run.
task: string & !=""
// params are NAME=VALUE overrides for the task's declared params.
param?: [...string]
}
// ---------------------------------------------------------------------------
// The four GENERIC, domain-neutral maintenance verbs. Each is parameterized by
// the REPO's own data (paths, pairs, keys) carried in its authored input, so the
// plugin stays reusable by any repository (R3/boundary law) — no org name, no
// repo name, no distro list is baked in.
// ---------------------------------------------------------------------------
// #GitSubmodulesInput drives `.gitmodules` pin maintenance.
#GitSubmodulesInput: {
// mode: status lists; bump stages new gitlinks; verify asserts.
mode: "status" | "bump" | "verify"
// pinned_from is a checkout path whose gitlinks are authoritative for the
// entries named in pin_map (e.g. "charly"). Empty => no pinned set.
pinned_from?: string @go(PinnedFrom)
// pin_map maps a submodule PATH in this repo to its twin PATH in pinned_from.
// On bump, every submodule NOT in pin_map rolls to its own default-branch HEAD.
pin_map?: {[string]: string} @go(PinMap)
// skip lists submodule paths the verb must not touch.
skip?: [...string]
}
// #FileParityInput asserts (or syncs) that paired files are byte-identical.
#FileParityInput: {
// mode: check fails on any drift; sync copies left -> right.
mode?: "check" | "sync"
// pairs are the compared file pairs, left = source of truth.
pairs: [...#FileParityPair]
}
#FileParityPair: {
left: string
right: string
}
// #SpliceRegionInput splices a marked region from a fragment into a target file.
#SpliceRegionInput: {
// mode: sync writes; check exits non-zero on a stale target.
mode?: "sync" | "check"
// target is the file whose marked region is replaced.
target: string
// fragment is the file carrying the source region (must carry the markers).
fragment: string
// begin / end are the region marker strings (a line containing begin starts the
// region; a line containing end closes it), matched with the fragment's own copy.
begin: string
end: string
}
// #ModulePinsInput adopts a set of module pins from a source go.mod into every
// module a glob matches, then tidies.
#ModulePinsInput: {
// mode: sync edits+tidies; check only asserts.
mode?: "sync" | "check"
// source_go_mod is the go.mod whose require pins are the source of truth.
source_go_mod: string @go(SourceGoMod)
// glob is the directory glob (relative to the working dir) selecting modules.
glob: string
// keys are the module paths whose pins are adopted (e.g. sdk, spec).
keys: [...string]
}
// #PruneInput garbage-collects merged-upstream session worktrees + branches: the
// session-scoped `.worktrees/<slug>/<repo>/` linked worktrees, the umbrella
// root's own worktrees, and the local `feat/` branches they pinned — across the
// umbrella root AND every submodule. A branch is prunable when its work is
// already on the merge target: its tip is an ancestor of `base`, OR its PR is
// MERGED and its tip is contained in the merged PR head (the squash-merge case
// git ancestry alone cannot see). A branch with commits BEYOND a merged head
// (unmerged local work) is NEVER pruned. Domain-neutral: the repo and merge
// target come from the authored input, never baked in (R3 / boundary law).
#PruneInput: {
// mode: report lists what would be pruned (a dry run); prune performs it.
mode?: "report" | "prune"
// base is the merge target a branch must be merged into (default origin/main).
base?: string
// include_closed also prunes worktrees/branches whose PR is CLOSED (abandoned).
// Off by default: a closed branch may hold unmerged work.
include_closed?: bool @go(IncludeClosed)
// local_only skips the GitHub PR-state lookup. It then CANNOT see a
// squash-merged branch whose remote ref still survives (git ancestry alone
// misses it), so it prunes strictly less — never more.
local_only?: bool @go(LocalOnly)
}

See also the candy reference for this candy’s install surface.