Documentation
¶
Overview ¶
Package cli wires the `clu` command-line interface on top of internal/store.
Index ¶
- Variables
- func Run(ctx context.Context, stdout, stderr io.Writer, args []string) int
- type AgentCmd
- type AgentGcCmd
- type AgentLsCmd
- type AgentShowCmd
- type AgentStartCmd
- type ApproveCmd
- type AssignCmd
- type BatchCmd
- type BlockedCmd
- type BriefCmd
- type CLI
- type CancelCmd
- type CheckpointCmd
- type CheckpointFailCmd
- type CheckpointPassCmd
- type ClaimCmd
- type CloseCmd
- type CommentAddCmd
- type CommentCmd
- type CommentEditCmd
- type CommentLsCmd
- type CommentRmCmd
- type CompletionCmd
- type CountCmd
- type CreateCmd
- type CronAddCmd
- type CronCmd
- type CronDisableCmd
- type CronEnableCmd
- type CronListCmd
- type CronRmCmd
- type CronRunCmd
- type DeferCmd
- type DepAddCmd
- type DepCmd
- type DepLsCmd
- type DepRmCmd
- type DescribeCmd
- type DoctorCmd
- type ExportCmd
- type HTTPCmd
- type HistoryCmd
- type ImportCmd
- type InboxCmd
- type InfoCmd
- type InitCmd
- type KVClearCmd
- type KVCmd
- type KVGetCmd
- type KVListCmd
- type KVSetCmd
- type LabelAddCmd
- type LabelCmd
- type LabelLsCmd
- type LabelPropagateCmd
- type LabelRmCmd
- type LinkCmd
- type ListCmd
- type LockCmd
- type LocksCmd
- type LogCmd
- type NoteAppendCmd
- type NoteClearCmd
- type NoteCmd
- type NoteSetCmd
- type NoteShowCmd
- type PingCmd
- type PriorityCmd
- type ReadyCmd
- type ReopenCmd
- type RunCmd
- type ShowCmd
- type SqlCmd
- type StatsCmd
- type StatusesCmd
- type SyncCmd
- type SyncFlushCmd
- type SyncPullCmd
- type SyncPushCmd
- type SyncStatusCmd
- type TagCmd
- type TemplateCmd
- type TemplateLsCmd
- type TemplateShowCmd
- type TemplateValidateCmd
- type TypesCmd
- type UndeferCmd
- type UnlockCmd
- type UpdateCmd
- type VersionCmd
- type WebCmd
- type WorktreeAddCmd
- type WorktreeBootstrapCmd
- type WorktreeCmd
- type WorktreeRemoveCmd
Constants ¶
This section is empty.
Variables ¶
var Version = ""
Version is set via -ldflags "-X github.com/arjia-labs/clu/internal/cli.Version=…" at release time. Falls back to module/VCS info from runtime/debug when empty.
Functions ¶
Types ¶
type AgentCmd ¶
type AgentCmd struct {
Start AgentStartCmd `cmd:"" help:"Launch a declared agent (config.yaml command + prompts), heartbeating while it runs."`
Ls AgentLsCmd `cmd:"" aliases:"list" help:"List declared agents (config) with an active indicator."`
Show AgentShowCmd `cmd:"" help:"Show one agent's full record."`
Gc AgentGcCmd `cmd:"" help:"Forcibly drop stale active_agents rows (older than --stale-seconds)."`
}
type AgentGcCmd ¶
type AgentGcCmd struct {
StaleSec int64 `name:"stale-seconds" default:"30" help:"Delete rows older than this."`
}
func (*AgentGcCmd) Run ¶
func (c *AgentGcCmd) Run(r *runCtx) error
type AgentLsCmd ¶
type AgentLsCmd struct {
StaleSec int64 `name:"stale-seconds" default:"30" help:"Consider rows older than this dead."`
}
func (*AgentLsCmd) Run ¶
func (c *AgentLsCmd) Run(r *runCtx) error
type AgentShowCmd ¶
type AgentShowCmd struct {
Name string `arg:"" help:"Agent name."`
}
func (*AgentShowCmd) Run ¶
func (c *AgentShowCmd) Run(r *runCtx) error
type AgentStartCmd ¶
type AgentStartCmd struct {
Name string `arg:"" help:"Agent name (declared in config.yaml)."`
Print bool `name:"print" aliases:"dry-run" help:"Print the assembled command and exit without launching."`
Rest []string `arg:"" optional:"" name:"args" passthrough:"" help:"Extra arguments forwarded to the agent command."`
}
AgentStartCmd launches a declared agent: it assembles a command from the agent's config.yaml launch spec and execs it in the foreground, inheriting the terminal. While the child runs, a heartbeat keeps the agent visible as live in `clu agent ls`; the row is cleared on exit.
Prompt files are layered: the shared base (<dir>/agents/_shared/*.md, sorted) comes first, then the agent's own prompts, so a persona refines the shared contract rather than restating it. An optional startup_prompt is passed as the command's trailing positional — the agent's first message.
This is a thin launcher, not a supervisor — no daemonizing, no restart-on-crash. Use --print to see the assembled command without running it (handy for wiring clu into your own launcher).
func (*AgentStartCmd) Run ¶
func (c *AgentStartCmd) Run(r *runCtx) error
type ApproveCmd ¶
type ApproveCmd struct {
ID string `arg:"" help:"Checkpoint issue ID."`
As string `` /* 198-byte string literal not displayed */
Reason string `name:"reason" help:"Optional note appended to the issue when approving."`
}
ApproveCmd is sugar for `clu checkpoint pass`. Mirrors the pattern of assign/tag/link — a short verb that wraps a longer command.
func (*ApproveCmd) Run ¶
func (c *ApproveCmd) Run(r *runCtx) error
type AssignCmd ¶
type AssignCmd struct {
ID string `arg:"" help:"Issue ID."`
To string `arg:"" optional:"" help:"Assignee name (empty clears)."`
}
AssignCmd is sugar for `clu update <id> --assignee <who>`. Pass an empty <who> to clear the assignee.
type BatchCmd ¶
type BatchCmd struct {
File string `arg:"" optional:"" help:"JSON file (default: stdin)."`
Docs bool `name:"docs" help:"Print the full batch JSON format reference (fields, semantics, example) and exit."`
DryRun bool `name:"dry-run" help:"Validate and report stats without writing anything."`
Group string `` /* 180-byte string literal not displayed */
OnExisting string `` /* 178-byte string literal not displayed */
Agent string `short:"a" name:"agent" default:"${user}" help:"Identity recorded as the actor on the created issues' audit events."`
}
BatchCmd instantiates a whole issue graph from a single JSON document in one transaction. The producer (any script / "codemode" / generator) emits the graph; clu validates it (acyclic, all refs resolve, fields valid), allocates real IDs, and writes it atomically. Built for scale — a thousand+ issues with complex deps in one shot.
node gen.js | clu batch --dry-run # validate + stats, write nothing
node gen.js | clu batch --json # commit; returns {alias: real-id}
type BlockedCmd ¶
type BlockedCmd struct {
Limit int `short:"n" name:"limit" default:"20" help:"Limit results (0 = unlimited)."`
Agent string `short:"a" help:"Lane to query (default: unassigned)."`
}
func (*BlockedCmd) Run ¶
func (c *BlockedCmd) Run(r *runCtx) error
type BriefCmd ¶
type BriefCmd struct{}
BriefCmd prints the workflow context for an agent: the agent-facing manual (AGENTS.md, embedded at build time), the agents declared in the project's config.yaml, who's currently live (heartbeat), and any persisted memories.
Intended for `clu brief | $YOUR_AGENT_RUNTIME` at session start so a fresh agent loads the right context in one shot.
type CLI ¶
type CLI struct {
Dir string `` /* 197-byte string literal not displayed */
JSON bool `name:"json" help:"Emit machine-readable JSON instead of human output."`
Quiet bool `short:"q" name:"quiet" help:"Suppress non-essential output (errors still go to stderr)."`
// --- issues: the core CRUD + state loop ---
Init InitCmd `cmd:"" group:"issues" help:"Initialize the database in the current directory."`
Create CreateCmd `cmd:"" group:"issues" help:"Create a new issue."`
List ListCmd `cmd:"" group:"issues" aliases:"ls" help:"List issues."`
Ready ReadyCmd `cmd:"" group:"issues" help:"List issues that are ready to work on."`
Blocked BlockedCmd `cmd:"" group:"issues" help:"List issues that have at least one open dependency."`
Show ShowCmd `cmd:"" group:"issues" help:"Show details for one issue."`
Claim ClaimCmd `cmd:"" group:"issues" help:"Claim the next ready issue (or a specific one)."`
Close CloseCmd `cmd:"" group:"issues" help:"Close an issue."`
Cancel CancelCmd `cmd:"" group:"issues" help:"Cancel an issue and all its transitive dependents."`
Reopen ReopenCmd `cmd:"" group:"issues" help:"Reopen a closed issue."`
Update UpdateCmd `cmd:"" group:"issues" help:"Update fields on an issue."`
// --- edits: sugar + relations ---
Assign AssignCmd `cmd:"" group:"edits" help:"Assign an issue (sugar for 'update --assignee')."`
Priority PriorityCmd `cmd:"" group:"edits" help:"Set an issue's priority (sugar for 'update -p N')."`
Describe DescribeCmd `cmd:"" group:"edits" help:"Set or clear an issue's description (sugar for 'update --description')."`
Note NoteCmd `cmd:"" group:"edits" help:"Manage an issue's freeform notes."`
Comment CommentCmd `cmd:"" group:"edits" help:"Manage comments on an issue."`
Label LabelCmd `cmd:"" group:"edits" help:"Manage labels on an issue."`
Tag TagCmd `cmd:"" group:"edits" help:"Add labels to an issue (alias for 'label add')."`
Dep DepCmd `cmd:"" group:"edits" help:"Manage dependency edges."`
Link LinkCmd `cmd:"" group:"edits" help:"Add a dependency edge (alias for 'dep add')."`
Defer DeferCmd `cmd:"" group:"edits" help:"Defer an issue until a later time."`
Undefer UndeferCmd `cmd:"" group:"edits" help:"Clear an issue's deferral."`
// --- coordination: agents, brief, locks, mailbox ---
Agent AgentCmd `cmd:"" group:"coord" help:"Manage agents — list declared (config.yaml) and live (heartbeat) state."`
Brief BriefCmd `cmd:"" group:"coord" help:"Print agent workflow context: AGENTS.md, declared agents, who's live, persisted memories."`
Lock LockCmd `cmd:"" group:"coord" help:"Acquire a named lock for ad-hoc coordination (deploy slots, build dirs, shared resources)."`
Unlock UnlockCmd `cmd:"" group:"coord" help:"Release a named lock."`
Locks LocksCmd `cmd:"" group:"coord" help:"List current locks (live and stale)."`
Ping PingCmd `cmd:"" group:"coord" help:"Send a fire-and-forget message to another agent's mailbox (TTL'd, doesn't pollute the work log)."`
Inbox InboxCmd `cmd:"" group:"coord" help:"Read pings addressed to you (the mailbox). Marks read on consume unless --peek."`
// --- workflows ---
Run RunCmd `cmd:"" group:"workflow" help:"Instantiate a workflow template into issues + deps."`
Template TemplateCmd `cmd:"" group:"workflow" help:"Inspect and validate workflow templates."`
Checkpoint CheckpointCmd `cmd:"" group:"workflow" help:"Pass or fail a checkpoint step."`
Approve ApproveCmd `cmd:"" group:"workflow" help:"Approve a checkpoint (sugar for 'checkpoint pass')."`
// --- inspection: read-only diagnostics ---
History HistoryCmd `cmd:"" group:"inspect" help:"Show the audit trail for one issue (oldest first)."`
Log LogCmd `cmd:"" group:"inspect" help:"Show the global event stream, newest first (filter by --actor/--kind/--issue/--since)."`
Count CountCmd `cmd:"" group:"inspect" help:"Count issues matching the same filters as 'list'."`
Stats StatsCmd `cmd:"" group:"inspect" help:"Show issue counts grouped by status, agent, and type."`
Info InfoCmd `cmd:"" group:"inspect" help:"Show database path, schema version, and a summary of issues."`
Doctor DoctorCmd `cmd:"" group:"inspect" help:"Run integrity and health checks against the database."`
Statuses StatusesCmd `cmd:"" group:"inspect" help:"List valid issue statuses."`
Types TypesCmd `cmd:"" group:"inspect" help:"List valid issue types."`
// --- data + servers ---
KV KVCmd `cmd:"" group:"data" help:"Manage a generic key-value store (feature flags, env, scratch data)."`
Export ExportCmd `cmd:"" group:"data" help:"Export all issues + deps + labels as JSONL."`
Import ImportCmd `cmd:"" group:"data" help:"Import JSONL produced by 'clu export'."`
Sync SyncCmd `cmd:"" group:"data" help:"Sync issue state to a branch-independent git ref (refs/clu/store)."`
Batch BatchCmd `cmd:"" group:"data" help:"Instantiate a whole issue graph from one JSON doc, atomically (validated, alias-resolved)."`
SQL SqlCmd `cmd:"" group:"data" name:"sql" help:"Run an ad-hoc SQL query (read-only by default; pass --write for DML/DDL)."`
Cron CronCmd `cmd:"" group:"data" help:"Schedule recurring clu invocations (drive from OS cron / launchd)."`
HTTP HTTPCmd `cmd:"" group:"data" name:"http" help:"Start a REST API server backed by the project's store."`
Web WebCmd `cmd:"" group:"data" name:"web" help:"Launch the web UI (REST API in-process + TanStack Start server)."`
// --- environments: git worktrees and their bootstrap ---
Worktree WorktreeCmd `cmd:"" group:"coord" help:"Manage git worktrees with project-defined bootstrap (copy files, run setup commands)."`
// --- meta ---
Version VersionCmd `cmd:"" group:"meta" help:"Print version information."`
Completion CompletionCmd `cmd:"" group:"meta" help:"Generate a shell completion script."`
}
CLI is the kong-defined command structure.
type CancelCmd ¶
type CancelCmd struct {
IDs []string `arg:"" required:"" name:"id" help:"Issue IDs to cancel (cascades to dependents)."`
Reason string `name:"reason" short:"r" help:"Optional comment posted on each root issue before cancel."`
Agent string `short:"a" name:"agent" default:"${user}" help:"Author for --reason comments. Defaults to $USER."`
}
CancelCmd cancels one or more issues *and all their transitive dependents*. "Cancelled" is a distinct terminal status from "closed": closed = done successfully (downstream unblocks); cancelled = abandoned (downstream stays blocked or is also cancelled here).
To cancel only the target without cascade, use `clu update --status cancelled <id>` instead.
type CheckpointCmd ¶
type CheckpointCmd struct {
Pass CheckpointPassCmd `cmd:"" help:"Mark a checkpoint as passed (clears the wait, closes the issue, unblocks downstream)."`
Fail CheckpointFailCmd `cmd:"" help:"Mark a checkpoint as failed (closes the issue with checkpoint:failed; downstream stays blocked)."`
}
CheckpointCmd is the parent for `clu checkpoint pass|fail`.
type CheckpointFailCmd ¶
type CheckpointFailCmd struct {
ID string `arg:"" help:"Checkpoint issue ID."`
As string `short:"a" name:"agent" default:"${user}" help:"Caller identity (defaults to $USER)."`
Reason string `name:"reason" help:"Optional note appended to the issue when failing."`
}
func (*CheckpointFailCmd) Run ¶
func (c *CheckpointFailCmd) Run(r *runCtx) error
type CheckpointPassCmd ¶
type CheckpointPassCmd struct {
ID string `arg:"" help:"Checkpoint issue ID."`
As string `` /* 198-byte string literal not displayed */
Reason string `name:"reason" help:"Optional note appended to the issue when passing."`
}
func (*CheckpointPassCmd) Run ¶
func (c *CheckpointPassCmd) Run(r *runCtx) error
type ClaimCmd ¶
type ClaimCmd struct {
Agent string `` /* 140-byte string literal not displayed */
Wait bool `help:"Block until something is claimable."`
Interval time.Duration `default:"250ms" help:"Poll interval when --wait is set."`
Heartbeat bool `` /* 127-byte string literal not displayed */
Context bool `` /* 136-byte string literal not displayed */
Depth int `name:"context-depth" help:"Cap how far up the dependency chain --context walks (0 = unlimited)."`
ID string `arg:"" optional:"" help:"Specific issue to claim; omit for next ready."`
}
ClaimCmd: one identity flag does everything.
clu claim → assignee = $USER, claims from the unassigned lane
clu claim -a code-reviewer → claims as code-reviewer; lane filter = code-reviewer's
(+ cap-routed unassigned work if declared in config)
clu claim --wait --heartbeat → register as live in 'agent ls' while waiting
type CloseCmd ¶
type CloseCmd struct {
IDs []string `arg:"" required:"" name:"id" help:"One or more issue IDs to close."`
Reason string `name:"reason" short:"r" help:"Optional comment posted on each issue before close. Avoids the comment-then-close two-step."`
Agent string `short:"a" name:"agent" default:"${user}" help:"Author for --reason comments. Defaults to $USER."`
}
type CommentAddCmd ¶
type CommentAddCmd struct {
ID string `arg:"" help:"Issue ID."`
Body []string `arg:"" optional:"" help:"Comment body. Use '-' or omit (with piped stdin) to read from stdin."`
Author string `short:"a" name:"agent" default:"${user}" help:"Agent identity used as the comment author. Defaults to $USER."`
}
CommentAddCmd appends a comment to an issue. The body can come from positional args, the literal "-" (read stdin), or — if no body args are given and stdin isn't a TTY — implicit stdin. The implicit form keeps heredoc/here-string usage tidy without needing an explicit "-".
func (*CommentAddCmd) Run ¶
func (c *CommentAddCmd) Run(r *runCtx) error
type CommentCmd ¶
type CommentCmd struct {
Add CommentAddCmd `cmd:"" help:"Add a comment to an issue. Pass body as args, '-', or via stdin (heredoc)."`
Edit CommentEditCmd `cmd:"" help:"Replace the body of an existing comment."`
Ls CommentLsCmd `cmd:"" aliases:"list" help:"List comments on an issue, chronological."`
Rm CommentRmCmd `cmd:"" aliases:"remove" help:"Remove a comment by its numeric ID."`
}
type CommentEditCmd ¶
type CommentEditCmd struct {
ID int64 `arg:"" help:"Comment ID (numeric, as shown in 'clu comment ls')."`
Body []string `arg:"" optional:"" help:"New body. Same input modes as 'comment add' (positional, '-', or stdin)."`
Agent string `short:"a" name:"agent" default:"${user}" help:"Agent identity recorded as the actor for this edit. Defaults to $USER."`
}
CommentEditCmd replaces the body of an existing comment. The author + created timestamp are preserved so references to the comment ID stay stable.
func (*CommentEditCmd) Run ¶
func (c *CommentEditCmd) Run(r *runCtx) error
type CommentLsCmd ¶
type CommentLsCmd struct {
ID string `arg:"" help:"Issue ID."`
}
func (*CommentLsCmd) Run ¶
func (c *CommentLsCmd) Run(r *runCtx) error
type CommentRmCmd ¶
type CommentRmCmd struct {
ID int64 `arg:"" help:"Comment ID (numeric, as shown in 'cli comment ls')."`
Agent string `short:"a" name:"agent" default:"${user}" help:"Agent identity recorded as the actor for this removal. Defaults to $USER."`
}
func (*CommentRmCmd) Run ¶
func (c *CommentRmCmd) Run(r *runCtx) error
type CompletionCmd ¶
type CompletionCmd struct {
Shell string `arg:"" enum:"bash,zsh,fish" help:"Target shell (bash, zsh, or fish)."`
}
func (*CompletionCmd) Run ¶
func (c *CompletionCmd) Run(r *runCtx) error
type CreateCmd ¶
type CreateCmd struct {
Priority int `short:"p" default:"2" help:"Priority (0=highest)."`
Type string `short:"t" default:"task" help:"Issue type."`
Agent string `short:"a" help:"Assign directly to an agent lane (e.g. code-reviewer)."`
Capability []string `` /* 139-byte string literal not displayed */
Dep []string `` /* 170-byte string literal not displayed */
Description string `name:"description" help:"Long description. Set atomically with the issue (avoids the race between create + 'clu describe')."`
Notes string `name:"notes" help:"Initial working notes. Set atomically with the issue."`
Title []string `arg:"" required:"" help:"Issue title."`
}
type CronAddCmd ¶
type CronAddCmd struct {
Name string `arg:"" help:"Unique job name."`
Schedule string `name:"schedule" required:"" help:"+Nh/+Nd/+Nw or @hourly/@daily/@weekly/@monthly."`
Args []string `arg:"" passthrough:"" help:"After '--': the cli args to invoke (e.g. -- create -a infra-agent 'Check CI')."`
StartNow bool `name:"start-now" help:"Treat next_run as now; the first execution happens on the next 'cron run'."`
}
func (*CronAddCmd) Run ¶
func (c *CronAddCmd) Run(r *runCtx) error
type CronCmd ¶
type CronCmd struct {
Add CronAddCmd `cmd:"" help:"Add a scheduled job."`
List CronListCmd `cmd:"" aliases:"ls" help:"List all scheduled jobs."`
Rm CronRmCmd `cmd:"" aliases:"delete,remove" help:"Delete a scheduled job."`
Enable CronEnableCmd `cmd:"" help:"Enable a scheduled job."`
Disable CronDisableCmd `cmd:"" help:"Disable a scheduled job (keeps the row, skips it on run)."`
Run CronRunCmd `cmd:"" help:"Run every job whose next_run has elapsed. Invoke periodically from OS cron / launchd."`
}
CronCmd dispatches the `clu cron …` subtree.
type CronDisableCmd ¶
type CronDisableCmd struct {
Name string `arg:"" help:"Job name."`
}
func (*CronDisableCmd) Run ¶
func (c *CronDisableCmd) Run(r *runCtx) error
type CronEnableCmd ¶
type CronEnableCmd struct {
Name string `arg:"" help:"Job name."`
}
func (*CronEnableCmd) Run ¶
func (c *CronEnableCmd) Run(r *runCtx) error
type CronListCmd ¶
type CronListCmd struct{}
func (*CronListCmd) Run ¶
func (c *CronListCmd) Run(r *runCtx) error
type CronRunCmd ¶
type CronRunCmd struct {
Force string `name:"force" help:"Run this one job regardless of schedule (ignores enabled bit too)."`
DryRun bool `name:"dry-run" help:"Print what would run; don't execute or advance schedules."`
}
func (*CronRunCmd) Run ¶
func (c *CronRunCmd) Run(r *runCtx) error
type DeferCmd ¶
type DepAddCmd ¶
type DepLsCmd ¶
type DepLsCmd struct {
ID string `arg:"" help:"Issue ID."`
}
DepLsCmd prints the dep edges for an issue, both directions: what it depends on (parents) and what depends on it (children/blocks). Mirrors the `Depends:` / `Blocks:` lines in `clu show` but standalone + scriptable via --json.
type DepRmCmd ¶
type DescribeCmd ¶
type DescribeCmd struct {
ID string `arg:"" help:"Issue ID."`
Text string `arg:"" optional:"" help:"Description text (empty clears)."`
}
DescribeCmd is sugar for `clu update <id> --description <text>`. Pass an empty <text> to clear the description.
func (*DescribeCmd) Run ¶
func (c *DescribeCmd) Run(r *runCtx) error
type DoctorCmd ¶
type DoctorCmd struct {
StuckHours int `name:"stuck-hours" default:"24" help:"Flag in-progress issues not updated in this many hours (0 disables)."`
}
type ExportCmd ¶
type ExportCmd struct {
Out string `` /* 157-byte string literal not displayed */
}
type HTTPCmd ¶
type HTTPCmd struct {
Bind string `name:"bind" default:"127.0.0.1" help:"Interface to bind. Defaults to loopback; use 0.0.0.0 to share across the LAN."`
Port int `name:"port" default:"7777" help:"TCP port. 0 picks a free port and prints it on startup."`
}
HTTPCmd starts a REST API server backed by the project's store. Used by `clu web` (which spawns it as a child process) and is also usable on its own for scripting or for pointing a separate frontend dev server at a live tracker.
type HistoryCmd ¶
type HistoryCmd struct {
ID string `arg:"" help:"Issue ID."`
}
HistoryCmd prints the audit trail for a single issue, oldest first — the timeline of who did what to it.
func (*HistoryCmd) Run ¶
func (c *HistoryCmd) Run(r *runCtx) error
type ImportCmd ¶
type InboxCmd ¶
type InboxCmd struct {
Agent string `short:"a" name:"agent" default:"${user}" help:"Whose inbox to read (defaults to $USER). Ignored when --global is set."`
Global bool `short:"A" name:"global" help:"Tail/list every recipient's pings (system-wide). Never marks as read; --peek is implied."`
All bool `name:"all" help:"Include already-read pings (default: unread only)."`
Since string `name:"since" help:"Only pings newer than this (e.g. 1h, 2d, 1w). Empty = no floor."`
Peek bool `name:"peek" help:"Read without marking as read. Default: listing marks them."`
Clear bool `name:"clear" help:"Mark every unread ping as read without listing them. Mutually exclusive with --peek and --watch."`
Watch bool `short:"w" name:"watch" help:"Keep emitting as new pings arrive; redraws the screen on each tick. Ctrl+C to exit."`
Tail bool `` /* 142-byte string literal not displayed */
Interval time.Duration `name:"interval" default:"1s" help:"Poll interval when --watch or --tail is set."`
Limit int `short:"n" default:"50" help:"Max rows to list (0 = unlimited)."`
Topic []string `` /* 198-byte string literal not displayed */
NoTopic []string `name:"no-topic" help:"Unsubscribe from a topic pattern. Repeatable."`
TopicTTL time.Duration `name:"topic-ttl" default:"15m" help:"How long a subscription lives between refreshes. Capped at 168h."`
Topics bool `` /* 127-byte string literal not displayed */
}
InboxCmd reads the mailbox addressed to one agent. By default lists unread pings and marks them read on the way out. Also manages topic subscriptions — when --topic is set with --watch/--tail, the subscription is refreshed every tick and lives for the duration of the loop. Persistent subscriptions can be created by passing --topic without --watch/--tail (uses --topic-ttl).
type InitCmd ¶
type InitCmd struct {
Prefix string `name:"prefix" help:"ID prefix for newly-created issues (only used on first init; ignored if config.yaml already exists)."`
Stealth bool `` /* 227-byte string literal not displayed */
}
InitCmd lays down the project skeleton:
.clu/
config.yaml # project config (id_prefix, future knobs)
data.sqlite # the database (created by the schema migrations)
templates/
example.yaml # a starter workflow you can copy
Each piece is created only if missing — re-running `clu init` is a safe no-op for an already-initialised project. The command also surfaces the local-state gitignore recipe so the user knows what to add to their repo's .gitignore.
type KVClearCmd ¶
type KVClearCmd struct {
Key string `arg:"" help:"Key to delete."`
}
func (*KVClearCmd) Run ¶
func (c *KVClearCmd) Run(r *runCtx) error
type KVCmd ¶
type KVCmd struct {
Set KVSetCmd `cmd:"" help:"Set (or overwrite) a key."`
Get KVGetCmd `cmd:"" help:"Print the value of a key."`
Clear KVClearCmd `cmd:"" aliases:"rm,delete,unset" help:"Delete a key."`
List KVListCmd `cmd:"" aliases:"ls" help:"List every key=value pair."`
}
type KVSetCmd ¶
type LabelAddCmd ¶
type LabelAddCmd struct {
ID string `arg:"" help:"Issue ID."`
Labels []string `arg:"" required:"" help:"Label(s) to add."`
}
func (*LabelAddCmd) Run ¶
func (c *LabelAddCmd) Run(r *runCtx) error
type LabelCmd ¶
type LabelCmd struct {
Add LabelAddCmd `cmd:"" help:"Add labels to an issue."`
Rm LabelRmCmd `cmd:"" aliases:"remove" help:"Remove labels from an issue."`
Ls LabelLsCmd `cmd:"" aliases:"list" help:"List labels on an issue."`
Propagate LabelPropagateCmd `` /* 139-byte string literal not displayed */
}
type LabelLsCmd ¶
type LabelLsCmd struct {
ID string `arg:"" help:"Issue ID."`
}
func (*LabelLsCmd) Run ¶
func (c *LabelLsCmd) Run(r *runCtx) error
type LabelPropagateCmd ¶
type LabelPropagateCmd struct {
Parent string `arg:"" name:"parent" help:"Parent issue ID."`
Labels []string `arg:"" required:"" name:"label" help:"Label(s) to propagate."`
Deep bool `name:"deep" aliases:"recursive" help:"Propagate to every transitive descendant, not just direct children."`
}
func (*LabelPropagateCmd) Run ¶
func (c *LabelPropagateCmd) Run(r *runCtx) error
type LabelRmCmd ¶
type LabelRmCmd struct {
ID string `arg:"" help:"Issue ID."`
Labels []string `arg:"" required:"" help:"Label(s) to remove."`
}
func (*LabelRmCmd) Run ¶
func (c *LabelRmCmd) Run(r *runCtx) error
type LinkCmd ¶
type LinkCmd struct {
Child string `arg:"" help:"Child issue (the one that depends)."`
Parent string `arg:"" help:"Parent issue (the blocker)."`
}
LinkCmd is sugar for `clu dep add <child> <parent>`.
type ListCmd ¶
type ListCmd struct {
Limit int `short:"n" default:"50" help:"Limit results (0 = unlimited)."`
Watch bool `short:"w" name:"watch" help:"Keep updating the list when matching issues change. Ctrl+C to exit."`
WatchInterval time.Duration `name:"interval" default:"1s" help:"Poll interval when --watch is set."`
Heartbeat bool `name:"heartbeat" help:"While watching, register -a <name> as a live agent in 'clu agent ls'. Requires -a. Off by default."`
// contains filtered or unexported fields
}
type LockCmd ¶
type LockCmd struct {
Name string `arg:"" required:"" help:"Lock name (e.g. deploy, build, test-db)."`
Cmd []string `arg:"" optional:"" passthrough:"" help:"Optional command to run while holding the lock. Pass after '--'."`
Agent string `short:"a" name:"agent" default:"${user}" help:"Holder identity (defaults to $USER)."`
TTL time.Duration `name:"ttl" default:"5m" help:"How long the lock lives if not explicitly released. Crashed holders auto-expire after this."`
Wait bool `name:"wait" help:"Block until the lock becomes free, then acquire."`
WaitTimeout time.Duration `name:"wait-timeout" default:"0" help:"Give up waiting after this (0 = forever). Only meaningful with --wait."`
Interval time.Duration `name:"interval" default:"500ms" help:"Poll interval when --wait is set."`
}
LockCmd implements `clu lock <name> [-- cmd args...]`: acquire a named lock, optionally run a command while holding it.
Every acquire requires a finite --ttl so a crashed holder can't wedge the lock forever. The trailing-command form is the preferred shape — clu owns acquire+release so leakage is impossible:
clu lock deploy --ttl 1h -- ./deploy.sh production
The bare form exists for cases where the work spans multiple shell invocations; pair it with `clu unlock`.
type LocksCmd ¶
type LocksCmd struct{}
LocksCmd implements `clu locks`: list current locks with live/stale annotations.
type LogCmd ¶
type LogCmd struct {
Actor string `name:"actor" help:"Only events by this actor."`
Kind string `name:"kind" help:"Only events of this kind (created, claimed, closed, …)."`
Issue string `name:"issue" help:"Only events for this issue ID."`
Since string `name:"since" help:"Only events newer than this ago (e.g. 24h, 7d, 2w)."`
Limit int `name:"limit" default:"50" help:"Max events to show."`
}
LogCmd prints the global event stream, newest first, with optional filters. The cross-issue companion to `history`.
type NoteAppendCmd ¶
type NoteAppendCmd struct {
ID string `arg:"" help:"Issue ID."`
Text []string `arg:"" required:"" help:"Note text to append."`
}
func (*NoteAppendCmd) Run ¶
func (c *NoteAppendCmd) Run(r *runCtx) error
type NoteClearCmd ¶
type NoteClearCmd struct {
ID string `arg:"" help:"Issue ID."`
}
func (*NoteClearCmd) Run ¶
func (c *NoteClearCmd) Run(r *runCtx) error
type NoteCmd ¶
type NoteCmd struct {
Set NoteSetCmd `cmd:"" help:"Replace an issue's notes."`
Append NoteAppendCmd `cmd:"" aliases:"add" help:"Append a note to the existing notes."`
Clear NoteClearCmd `cmd:"" help:"Clear an issue's notes."`
Show NoteShowCmd `cmd:"" aliases:"get" help:"Print the current notes."`
}
type NoteSetCmd ¶
type NoteSetCmd struct {
ID string `arg:"" help:"Issue ID."`
Text []string `arg:"" required:"" help:"Note text."`
}
func (*NoteSetCmd) Run ¶
func (c *NoteSetCmd) Run(r *runCtx) error
type NoteShowCmd ¶
type NoteShowCmd struct {
ID string `arg:"" help:"Issue ID."`
}
func (*NoteShowCmd) Run ¶
func (c *NoteShowCmd) Run(r *runCtx) error
type PingCmd ¶
type PingCmd struct {
Recipient string `arg:"" name:"recipient" help:"Agent name or topic. Direct delivery + fan-out to matching --topic subscribers."`
Body []string `arg:"" optional:"" help:"Message body. Use '-' or omit (with piped stdin) to read from stdin."`
Agent string `short:"a" name:"agent" default:"${user}" help:"Sender identity (defaults to $USER)."`
TTL time.Duration `` /* 138-byte string literal not displayed */
}
PingCmd writes a fire-and-forget mailbox row to `Recipient`. Direct delivery always lands in `Recipient`'s mailbox; the store also fans out to every active subscription whose pattern matches the recipient name (see `clu inbox --topic`). The body can come from positional args, '-', or piped stdin (same convention as comment add).
Topic-style names are conventional: `release.urgent`, `frontend.build-broken`. They're just strings — a listener subscribed to `release.*` picks them up, otherwise the row sits in the literal-name inbox until TTL.
Distinct from `clu comment add`: doesn't attach to an issue, auto-expires, doesn't pollute the work log.
type PriorityCmd ¶
type PriorityCmd struct {
ID string `arg:"" help:"Issue ID."`
Level int `arg:"" help:"New priority (0=highest)."`
}
PriorityCmd is sugar for `clu update <id> -p <N>`.
func (*PriorityCmd) Run ¶
func (c *PriorityCmd) Run(r *runCtx) error
type ReadyCmd ¶
type ReadyCmd struct {
Limit int `short:"n" name:"limit" default:"20" help:"Limit results (0 = unlimited)."`
Agent string `short:"a" help:"Lane to query (default: unassigned)."`
Wait bool `help:"Block until at least one issue is ready, then print and exit."`
Watch bool `short:"w" name:"watch" help:"Keep emitting the ready list as it changes. Ctrl+C to exit."`
Interval time.Duration `default:"250ms" help:"Poll interval when --wait or --watch is set."`
Heartbeat bool `name:"heartbeat" help:"While waiting/watching, register -a's name as a live agent."`
}
type ReopenCmd ¶
type ReopenCmd struct {
IDs []string `arg:"" required:"" name:"id" help:"One or more issue IDs to reopen."`
}
type RunCmd ¶
type RunCmd struct {
Template string `arg:"" help:"Template name (in .clu/templates/) or path to a .yaml file."`
Var []string `short:"v" name:"var" placeholder:"KEY=VALUE" help:"Variable bindings (repeatable)."`
DryRun bool `name:"dry-run" help:"Validate and print the plan without writing to the DB."`
NoPrompt bool `name:"no-prompt" help:"Don't prompt for missing required vars; fail instead. Default: prompt when stdin is a TTY."`
}
RunCmd instantiates a workflow template into issues + deps.
type ShowCmd ¶
type ShowCmd struct {
ID string `arg:"" help:"Issue ID."`
Context bool `name:"context" help:"Also print the upstream dependency chain (descriptions, notes, comments) leading up to this issue."`
Depth int `name:"context-depth" help:"Cap how far up the dependency chain --context walks (0 = unlimited)."`
}
type SqlCmd ¶
type SqlCmd struct {
Query string `arg:"" optional:"" help:"SQL query. Use '-' to read from stdin, or pass --file."`
Write bool `name:"write" help:"Allow non-SELECT statements (UPDATE/DELETE/INSERT/DDL). Default refuses anything other than read queries."`
File string `name:"file" short:"f" help:"Read the query from this file instead of the positional arg."`
CSV bool `name:"csv" help:"Emit CSV (with header) instead of a pretty table. Mutually exclusive with --json."`
}
SqlCmd runs an ad-hoc SQL query against the project's data.sqlite.
Read-only by default — first keyword must be SELECT, WITH, EXPLAIN, or PRAGMA. Pass --write to allow arbitrary statements. There's no stricter sandbox; schema is internal and queries can break across migration versions.
type StatusesCmd ¶
type StatusesCmd struct{}
func (*StatusesCmd) Run ¶
func (c *StatusesCmd) Run(r *runCtx) error
type SyncCmd ¶
type SyncCmd struct {
Push SyncPushCmd `cmd:"" help:"Serialize the DB onto refs/clu/store (and optionally push it)."`
Pull SyncPullCmd `cmd:"" help:"Reconcile refs/clu/store back into the DB (LWW + tombstones)."`
Status SyncStatusCmd `cmd:"" help:"Show the synced ref and how it compares to the local DB."`
Flush SyncFlushCmd `cmd:"" help:"pull then push — the full round-trip (beads-rs 'sync')."`
}
type SyncFlushCmd ¶
type SyncFlushCmd struct {
Remote string `name:"remote" help:"Fetch before / push after against this remote."`
}
func (*SyncFlushCmd) Run ¶
func (c *SyncFlushCmd) Run(r *runCtx) error
type SyncPullCmd ¶
type SyncPullCmd struct {
Remote string `name:"remote" help:"Fetch refs/clu/store from this remote first (force; the DB is the real local state)."`
}
func (*SyncPullCmd) Run ¶
func (c *SyncPullCmd) Run(r *runCtx) error
type SyncPushCmd ¶
type SyncPushCmd struct {
Remote string `name:"remote" help:"Also push refs/clu/store to this git remote (e.g. origin)."`
Message string `name:"message" short:"m" help:"Commit subject for the ref commit." default:"clu sync"`
}
func (*SyncPushCmd) Run ¶
func (c *SyncPushCmd) Run(r *runCtx) error
type SyncStatusCmd ¶
type SyncStatusCmd struct{}
func (*SyncStatusCmd) Run ¶
func (c *SyncStatusCmd) Run(r *runCtx) error
type TagCmd ¶
type TagCmd struct {
ID string `arg:"" help:"Issue ID."`
Labels []string `arg:"" required:"" help:"Label(s) to add."`
}
TagCmd is sugar for `clu label add <id> <labels...>`.
type TemplateCmd ¶
type TemplateCmd struct {
Ls TemplateLsCmd `cmd:"" aliases:"list" help:"List available templates."`
Show TemplateShowCmd `cmd:"" help:"Print one template as parsed YAML."`
Validate TemplateValidateCmd `cmd:"" help:"Validate a template's structure."`
}
TemplateCmd is the parent for `clu template ls|show|validate`.
type TemplateLsCmd ¶
type TemplateLsCmd struct{}
func (*TemplateLsCmd) Run ¶
func (c *TemplateLsCmd) Run(r *runCtx) error
type TemplateShowCmd ¶
type TemplateShowCmd struct {
Name string `arg:"" help:"Template name (in .clu/templates/) or path to a .yaml file."`
}
func (*TemplateShowCmd) Run ¶
func (c *TemplateShowCmd) Run(r *runCtx) error
type TemplateValidateCmd ¶
type TemplateValidateCmd struct {
Name string `arg:"" help:"Template name (in .clu/templates/) or path to a .yaml file."`
}
func (*TemplateValidateCmd) Run ¶
func (c *TemplateValidateCmd) Run(r *runCtx) error
type UndeferCmd ¶
type UndeferCmd struct {
IDs []string `arg:"" required:"" name:"id" help:"One or more issue IDs to undefer."`
}
func (*UndeferCmd) Run ¶
func (c *UndeferCmd) Run(r *runCtx) error
type UnlockCmd ¶
type UnlockCmd struct {
Name string `arg:"" required:"" help:"Lock name to release."`
Agent string `` /* 140-byte string literal not displayed */
Force bool `name:"force" help:"Release the lock even if held by another holder. Use to clear a stuck lock after a crash."`
}
UnlockCmd implements `clu unlock <name>`.
type UpdateCmd ¶
type UpdateCmd struct {
ID string `arg:"" help:"Issue ID."`
Priority *int `short:"p" help:"New priority (0=highest)."`
Status *string `help:"New status."`
Assignee *string `short:"a" name:"assignee" help:"Set assignee. The 'agent lane' is just the assignee on an open issue."`
Unassign bool `help:"Clear assignee."`
Title *string `help:"New title."`
Description *string `help:"Set description."`
NoDescription bool `name:"no-description" help:"Clear description."`
}
type VersionCmd ¶
type VersionCmd struct{}
func (*VersionCmd) Run ¶
func (c *VersionCmd) Run(r *runCtx) error
type WebCmd ¶
type WebCmd struct {
APIPort int `name:"api-port" default:"7777" help:"Port for the REST API. 0 picks a free port."`
WebPort int `` /* 156-byte string literal not displayed */
Bind string `name:"bind" default:"127.0.0.1" help:"Interface to bind both servers."`
Dev bool `name:"dev" help:"Use 'pnpm dev' (HMR) instead of the built output. Default tries built output first, falls back to dev."`
NoBrowser bool `name:"no-browser" help:"Don't try to open a browser window."`
WebDir string `` /* 135-byte string literal not displayed */
Install bool `` /* 193-byte string literal not displayed */
}
WebCmd boots the local web UI: starts the REST API in-process and spawns the TanStack Start server as a child. The two together give the user a clickable kanban / list / detail UI at a localhost URL.
In-process API rather than spawning `clu http` separately so we only manage one external child (Node) and don't need a port-discovery dance.
type WorktreeAddCmd ¶
type WorktreeAddCmd struct {
Path string `arg:"" help:"Path to create the worktree at (relative or absolute)."`
Ref string `arg:"" optional:"" help:"Branch or commit-ish to check out (default: current HEAD)."`
Branch string `name:"branch" short:"b" help:"Create a new branch at <ref> for this worktree (passes -b to git worktree add)."`
Bootstrap bool `name:"bootstrap" help:"After creating the worktree, run config.yaml's worktree.copy + worktree.commands."`
}
WorktreeAddCmd wraps `git worktree add`. With --bootstrap it then runs the project's `worktree:` recipe so a single command goes from "I want a worktree for feat/foo" to "ready to work."
func (*WorktreeAddCmd) Run ¶
func (c *WorktreeAddCmd) Run(r *runCtx) error
type WorktreeBootstrapCmd ¶
type WorktreeBootstrapCmd struct {
Path string `arg:"" help:"Path of the worktree to bootstrap. Must be a git worktree."`
}
WorktreeBootstrapCmd applies the config.yaml `worktree:` recipe to an already-created worktree. Idempotent enough to re-run after editing the recipe — copies overwrite, commands run again.
func (*WorktreeBootstrapCmd) Run ¶
func (c *WorktreeBootstrapCmd) Run(r *runCtx) error
type WorktreeCmd ¶
type WorktreeCmd struct {
Add WorktreeAddCmd `cmd:"" help:"Create a new git worktree (and optionally run the bootstrap recipe)."`
Bootstrap WorktreeBootstrapCmd `cmd:"" help:"Run the worktree.copy + worktree.commands recipe against an existing worktree."`
Remove WorktreeRemoveCmd `cmd:"" aliases:"rm" help:"Remove a git worktree after checking for uncommitted/unpushed work."`
}
WorktreeCmd groups worktree lifecycle subcommands. clu wraps `git worktree` so the project's bootstrap recipe (config.yaml's `worktree:` section) runs in lock-step with the git operation — otherwise every fresh worktree starts as a half-broken filesystem tree missing .env, install state, and any per-checkout init.
type WorktreeRemoveCmd ¶
type WorktreeRemoveCmd struct {
Path string `arg:"" help:"Path of the worktree to remove."`
Force bool `name:"force" short:"f" help:"Skip the safety checks and pass --force to git worktree remove."`
}
WorktreeRemoveCmd wraps `git worktree remove` with safety checks git itself doesn't perform: unpushed commits (the load-bearing one) plus a stash warning. Git already refuses to remove a worktree with uncommitted changes, but we run that check explicitly so the error message is uniform with the others.
func (*WorktreeRemoveCmd) Run ¶
func (c *WorktreeRemoveCmd) Run(r *runCtx) error
Source Files
¶
- agent.go
- agent_start.go
- approve.go
- assign.go
- batch.go
- blocked.go
- brief.go
- cancel.go
- checkpoint.go
- claim.go
- cli.go
- close.go
- comment.go
- completion.go
- context.go
- count.go
- create.go
- cron.go
- defer.go
- dep.go
- describe.go
- doctor.go
- export.go
- history.go
- http.go
- import.go
- info.go
- init.go
- kv.go
- label.go
- link.go
- list.go
- lock.go
- note.go
- ping.go
- priority.go
- ready.go
- reopen.go
- run.go
- show.go
- sql.go
- stats.go
- statuses.go
- sync.go
- tag.go
- template.go
- update.go
- version.go
- web.go
- worktree.go