Skip to content

CLI Tools Reference

Complete reference for ll- command-line tools and related utilities (including mcp-call). Install from PyPI:

pip install little-loops

See COMMANDS.md for /ll: slash commands and README for overview.

Common Flags

These flags appear across multiple tools:

Flag Short Behavior Used by
--dry-run -n Show what would happen without making changes ll-auto, ll-parallel, ll-sprint run, ll-deps fix, ll-sync
--resume -r Resume from previous checkpoint ll-auto, ll-parallel, ll-sprint run
--max-issues -m Limit number of issues to process (0 = unlimited) ll-auto, ll-parallel
--quiet -q Suppress non-essential output ll-auto, ll-parallel, ll-sprint run, ll-sync
--only Comma-separated issue IDs to process exclusively ll-auto, ll-parallel, ll-sprint run
--skip Comma-separated issue IDs to exclude ll-auto, ll-parallel, ll-sprint
--type Comma-separated issue types: BUG, FEAT, ENH, EPIC ll-auto, ll-parallel, ll-sprint
--config Path to project root (default: current directory) ll-auto, ll-parallel, ll-sprint, ll-sync
--timeout -t Timeout in seconds per issue ll-parallel, ll-sprint run
--handoff-threshold Override auto-handoff context threshold (1-100, default: from config) ll-auto, ll-parallel, ll-sprint run, ll-loop run, ll-loop resume
--context-limit Override context window token estimate (default: from config or model-detected) ll-auto, ll-parallel, ll-sprint run, ll-loop run, ll-loop resume
--json -j Output as JSON (structured, machine-readable) Most ll-* CLIs — see individual tool sections
--format -f Output format: text, json, markdown ll-history, ll-deps, ll-verify-docs, ll-check-links, ll-issues epic-progress

Project Setup

ll-init

Initialize little-loops for a project. Detects the project root, selects host adapters, generates a .ll/ll-config.json, and optionally installs hook adapters for supported host CLIs (claude-code, codex, opencode, pi).

When run on a project that already has a .ll/ll-config.json, the interactive wizard pre-populates every field with the existing values so you can review and update without losing previous settings. The headless --yes path preserves existing feature toggles and project fields, applying only the overrides supplied via --enable/--disable.

Flags:

Flag Short Description
--yes -y Accept all defaults; run non-interactively. Merges existing config values when a config is present. Loop run defaults: clear: true, show_diagrams: "clean".
--force -f Reset to template defaults rather than pre-populating from existing config
--dry-run -n Preview actions without writing files
--plan Emit a JSON plan {detected, proposed_config, host_options, warnings} without writing anything
--hosts HOST [HOST ...] Host harnesses to install adapters for (claude-code, codex, opencode, pi). Defaults to auto-detected hosts. Unknown values produce a warning and are skipped.
--enable FEATURE Enable a feature in the headless config (repeatable). Requires --yes/--dry-run/--plan. Valid: decisions, scratch_pad, session_capture, product, analytics, context_monitor, learning_tests, session_digest, prompt_optimization.
--disable FEATURE Disable a feature in the headless config (repeatable). Same valid names as --enable. Use --disable prompt_optimization to opt out of the default-on prompt optimizer.
--upgrade Act on version drift automatically, then run a host-parameterized surface refresh for every active host: upgrade the pip package, force-regenerate adapter files (e.g. .codex/hooks.json, re-stamping the embedded gen-version), and scope-aware-update the claude-code plugin (auto for project-scoped installs, advise-only for user-scoped). Without this flag, headless mode only warns — including a hint when a generated adapter's gen-version stamp diverges from the installed package.
--root ROOT -C Project root directory (default: current directory)

Richer features (parallel, sync, documents, design_tokens, confidence_gate, tdd) carry sub-config and remain interactive-only; they are not accepted by --enable/--disable. Unknown feature names exit 2.

Subcommands:

Subcommand Description
apply --config PLAN Apply writes from a --plan JSON output. --config accepts a file path or raw JSON string. Produces the same artifacts as --yes (config, issue dirs, design tokens, issue templates, CLAUDE.md, host adapters, etc.). Accepts --force to overwrite existing configuration keys and overwrite existing host adapter files (e.g. .codex/hooks.json).

Exit codes: 0 = success, 1 = error (template missing, stdin not a TTY, etc.), 2 = usage error

Interactive TUI screens (omitted when --yes is passed):

The detected project type is shown as a banner line (not a questionary prompt) before Screen 1 starts.

Screen Prompt Notes
1 / 6 Project Basics Project name, src dir, test/lint/format/type-check commands. Pre-filled from existing config when present, otherwise from project-type detection; command fields offer curated-menu select with "Custom…" fallthrough
2 / 6 Scan focus_dirs text entry; confirm/override exclude patterns
3 / 6 Features Opt-in checkboxes including github_sync, confidence_gate, tdd, decisions (rules log), scratch_pad (automation context masking), session_capture (PreCompact handoff); profile picker for design_tokens; worktree copy-files toggle; session-digest confirm; prompt-optimization opt-out confirm (default on); loop defaults: "Enable --clear by default?" (default Yes) and "Default diagram mode for ll-loop run?" (default clean)
4 / 6 Hosts Defaults to detected hosts
5 / 6 Settings target Third "Skip" option skips merge_settings entirely
6 / 6 CLAUDE.md update Offers to create .claude/CLAUDE.md (or append to an existing one) with ll CLI command stubs; skipped if a ## little-loops section is already present (ENH-2043, ENH-2092)

Examples:

ll-init --yes                      # Non-interactive full init with defaults
ll-init --yes --dry-run            # Preview without writing files
ll-init --yes --force              # Overwrite existing configuration
ll-init --yes --upgrade            # Upgrade stale package/plugin automatically
ll-init --plan                     # Emit JSON plan without writing
ll-init --hosts claude-code codex  # Install adapters for specific hosts
ll-init --yes --enable decisions --enable session_capture  # Opt in to extra features
ll-init --yes --disable prompt_optimization                # Opt out of prompt optimizer
ll-init apply --config plan.json   # Apply writes from a --plan output

Defaults source (ENH-2434): All ll-init defaults — TUI field values, --enable/--disable valid feature names, host detection heuristics, default project commands — are read from config-schema.json at the package root, which is also the validation schema for .ll/ll-config.json. Edit defaults there; they're not duplicated between the wizard and the schema. To audit a default, search config-schema.json for the field name; the matching default value is what ll-init --yes will produce.


Skill Invocation

ll-action

Thin CLI wrapper for invoking ll skills as one-shot commands with JSON-structured output. Useful for dashboard integrations, shell scripts, and cron jobs that need a single skill result without running a full FSM loop.

Subcommands:

invoke

Invokes a skill and streams output as newline-delimited JSON (NDJSON) events by default.

Flag Description
skill Skill name (e.g. refine-issue, confidence-check)
--args ARG [ARG ...] Arguments to pass to the skill
--timeout SECONDS Timeout in seconds (default: 300)
--output FORMAT stream-json (default) or json

stream-json event shapes:

{"event":"action_start","ts":"...","skill":"refine-issue","args":["ENH-353"]}
{"event":"action_output","ts":"...","line":"Analyzing ENH-353..."}
{"event":"action_complete","ts":"...","exit_code":0,"duration_ms":45230}

json output shape (--output json):

{"exit_code":0,"duration_ms":45230,"output":"...","error":null}

capabilities

Returns the full CapabilityReport for the configured host. Does not invoke Claude.

{
  "host": "claude-code",
  "binary": "claude",
  "version": "1.0.3",
  "capabilities": [
    {"name": "streaming", "status": "full", "note": ""},
    {"name": "permission_skip", "status": "full", "note": ""},
    {"name": "agent_select", "status": "full", "note": ""},
    {"name": "tool_allowlist", "status": "full", "note": ""},
    {"name": "json_schema", "status": "unsupported", "note": "..."},
    {"name": "structured_output", "status": "full", "note": "..."}
  ],
  "hooks": [
    {"name": "session_start", "status": "installed", "note": ""}
  ]
}

list

Returns all skills with names, descriptions, and argument hints from the plugin manifest. Does not invoke Claude.

[
  {"name":"refine-issue","description":"...","args":"ISSUE_ID [--auto] [--dry-run]"},
  {"name":"old-skill","description":"...","args":null}
]

The args field is sourced from the args: frontmatter field in skills/<name>/SKILL.md (with argument-hint: as a fallback alias). It is null when neither field is present.

Exit codes: 0 = success, 1 = error, 124 = timeout

Examples:

ll-action invoke refine-issue --args P2-ENH-1229
ll-action invoke confidence-check --args FEAT-042 --timeout 120
ll-action invoke refine-issue --args P2-ENH-1229 --output json
ll-action invoke link-epics --args --auto      # Link all HIGH-confidence orphans to epics non-interactively
ll-action capabilities
ll-action list

ll-harness

One-shot runner evaluation CLI that invokes a skill, shell command, MCP tool, or raw Claude prompt, captures its output, and exits 0 (PASS) / 1 (FAIL) / 2 (error/timeout) based on optional criteria.

Runners:

Runner Description
skill Invoke a little-loops skill via the active host CLI
cmd Run a shell command and capture its output
mcp Call an MCP tool via JSON-RPC
prompt Send a raw prompt to Claude via the active host CLI
dsl Run a DSL task set and report pass rates with Wilson CI

Shared evaluator flags:

Flag Description
--exit-code INT Expected exit code (FAIL if mismatch)
--semantic TEXT Natural-language criterion evaluated against captured output via LLM
--timeout SECONDS Runner timeout (default: 120)
--output FORMAT text (default) or json
--verbose Show full captured output even on PASS

mcp-specific flag: --args JSON — JSON arguments forwarded to the MCP tool (default: {}).

prompt-specific flag: --model MODEL — Override the Claude model used for the prompt (e.g. claude-haiku-4-5-20251001). Omit to use the host session default.

dsl-specific flag: --model MODEL — Override the Claude model for all task invocations. Run ll-harness dsl once per model to compare pass rates across models.

Exit codes: 0 = PASS, 1 = FAIL, 2 = internal error / timeout

Examples:

ll-harness skill check-code
ll-harness cmd "echo hello" --exit-code 0
ll-harness mcp my-server:my-tool --args '{"key": "val"}' --semantic "tool returned results"
ll-harness prompt "What is 2+2?" --semantic "response contains a number"
ll-harness skill refine-issue P2-ENH-1229 --semantic "has implementation plan" --output json
ll-harness dsl evals/dsl/my-loop/
ll-harness dsl evals/dsl/my-loop/ --model claude-haiku-4-5-20251001


Diagnostics

ll-doctor

Probes the active host CLI and reports which little-loops features are supported. Produces a CapabilityReport with one CapabilityEntry per capability (streaming, permission skip, agent selection, tool allowlist, structured output) and one HookEntry per registered hook event.

Flags: - -j, --json — emit the CapabilityReport as JSON instead of the human-readable table.

Exit codes: 0 = all capabilities supported, 1 = one or more capabilities unsupported.

Example output:

Host:    claude  (1.2.3)
Binary:  /usr/local/bin/claude

Capabilities
────────────────────────────────────────
  ✓  streaming
  ✓  permission_skip
  ✓  agent_select
  ○  tool_allowlist  (flag accepted but not validated)

Hooks
────────────────────────────────────────
  ✓  pre_tool_use
  ○  post_tool_use

Analytics Capture
────────────────────────────────────────
  ✓  skills:        ['*']
  ✓  cli_commands:  ['*']
  ✓  corrections:   enabled
  ✗  file_events:   disabled

Examples:

ll-doctor
ll-doctor --json


ll-ctx-stats

Show context-window analytics for the current project (FEAT-1160). Reads per-tool byte metrics that the post_tool_use hook persists into .ll/history.db (FEAT-1623) and renders a compact summary of how much data was processed by tools vs. how much actually entered the conversation context. Also surfaces skill-health signals (per-skill invocation frequency and correction rate) from ll-logs stats (ENH-1921). Falls back to .ll/ll-context-state.json (token estimates) when the SQLite store is absent so first-time users still get useful output.

When learning_tests.enabled is true, the report also includes a Learning Test Coverage section (ENH-2218) showing total record count, breakdown by status (proven / stale / refuted), and the number of orphaned records (targets with no matching import in the project). Use this section to spot stale coverage before a release.

Flags: - --db PATH — Use a non-default session database (default .ll/history.db). - --json — Emit the report as JSON instead of the human-readable summary. The JSON payload includes a skill_health array ([{skill, invocations, corrections, correction_rate}]) when skill events are present, or null when not. When learning tests are enabled it also includes a learning_tests key with {total, proven, stale, refuted, orphans}.

Exit codes: 0 = report rendered (data present or fallback used), 1 = no data found in either the SQLite store or the fallback file.

Examples:

ll-ctx-stats
ll-ctx-stats --db custom/history.db
ll-ctx-stats --json

To enable per-tool byte tracking, set "analytics": {"enabled": true} in .ll/ll-config.json.


Issue Processing

ll-auto

Process all backlog issues sequentially in priority order. On startup, ll-auto prints a header showing the active LLM model name (detected from the Claude CLI stream-json init event).

Context handoff and stale-inflight re-queue: When the agent's context window nears capacity, ll-auto emits a CONTEXT_HANDOFF signal and hands off to a fresh session. On resume (--resume), any issue that was in-flight at handoff time is re-queued at the front of the remaining work list so it is not silently dropped. (1693649e)

Flags:

Flag Short Description
--resume -r Resume from previous checkpoint
--dry-run -n Show what would be processed without running
--max-issues -m Limit number of issues (0 = unlimited)
--quiet -q Suppress non-essential output
--only Process only these issue IDs (comma-separated)
--skip Skip these issue IDs (comma-separated)
--type Process only these types: BUG, FEAT, ENH, EPIC
--config Path to project root
--category -c Filter to category: bugs, features, enhancements, epics
--priority -p Comma-separated priority levels to process (e.g., P1,P2)
--label Comma-separated labels to process (e.g., fsm,cli,quick-win); matches issues with labels: frontmatter containing any of the specified values
--verbose -v Show full prompt text; default shows abbreviated 5-line preview
--idle-timeout Kill worker if no output for N seconds (0 to disable)
--handoff-threshold Override auto-handoff context threshold (1-100)
--context-limit Override context window token estimate
--skip-learning-gate Bypass the per-issue learning-test pre-flight gate (for emergency runs when learning_tests.enabled is true)

Examples:

ll-auto                          # Process all issues in priority order
ll-auto --max-issues 5           # Process at most 5 issues
ll-auto --resume                 # Resume from previous state
ll-auto --dry-run                # Preview what would be processed
ll-auto --category bugs          # Only process bugs
ll-auto --only BUG-001,BUG-002   # Process only specific issues
ll-auto --skip BUG-003           # Skip a specific issue
ll-auto --type BUG               # Process only bugs
ll-auto --type BUG,ENH           # Process bugs and enhancements
ll-auto --priority P1,P2         # Only process P1 and P2 issues
ll-auto --label quick-win        # Only process issues tagged quick-win
ll-auto --handoff-threshold 90   # Trigger handoff at 90% context usage


ll-parallel

Process issues concurrently using isolated git worktrees.

Flags:

Flag Short Description
--workers -w Number of parallel workers (default: from config or 2)
--priority -p Comma-separated priorities to process (e.g., P1,P2)
--worktree-base Base directory for git worktrees
--cleanup -c Clean up all worktrees and exit
--prune-merged-branches Delete local feature/* branches already merged into the base branch; use with --dry-run to preview. Squash/rebase-merged branches require the gh CLI for detection.
--merge-pending Attempt to merge pending work from interrupted runs
--clean-start Remove all worktrees and start fresh
--ignore-pending Report pending work but continue without merging
--stream-output Stream Claude CLI subprocess output to console
--show-model Verify and display model on worktree setup
--feature-branches Enable/disable feature-branch mode (--feature-branches / --no-feature-branches); overrides parallel.use_feature_branches in config for this run
--epic-branches Enable/disable per-EPIC integration-branch mode (--epic-branches / --no-epic-branches); overrides parallel.epic_branches.enabled in config for this run
--overlap-detection Enable pre-flight overlap detection to reduce merge conflicts
--warn-only With --overlap-detection, warn instead of serializing
--dry-run -n Show what would be processed
--resume -r Resume from previous checkpoint
--timeout -t Timeout in seconds per issue
--quiet -q Suppress non-essential output
--only Process only these issue IDs
--skip Skip these issue IDs
--type Process only these types: BUG, FEAT, ENH, EPIC
--label Comma-separated labels to process (e.g., fsm,quick-win)
--max-issues -m Limit total issues processed
--config Path to project root
--idle-timeout Kill worker if no output for N seconds (0 to disable)
--handoff-threshold Override auto-handoff context threshold (1-100)
--context-limit Override context window token estimate
--verbose -v Enable verbose output
--skip-learning-gate Bypass per-worktree proof-first-task gate (emergency runs when learning_tests.enabled is true)

Per-worktree proof-first gate (ENH-2219): When learning_tests.enabled is true, each worktree runs a proof-first-task gate before handing off to the implementation loop. The gate reads learning_tests_required from the issue file; when that field is absent (an unrefined issue), it resolves targets just-in-time by extracting external-API dependencies from the issue text (BUG-2320), so the firewall still fires on the capture-issue → ll-parallel path. A populated field is proven directly — forwarded as targets_csv so proof-first-task proves exactly the registered list rather than re-extracting an independent one (ENH-2405); a JIT-resolved list still goes through the assumption-firewall extraction/classification path. Either way, the gate verifies that every resolved API assumption has a proven (non-stale) record in the registry. If resolution yields no targets, the gate logs "no external dependencies detected" and proceeds (an auditable decision, not a silent skip). Issues that fail the gate are retried once after /ll:explore-api completes; if the retry also fails the issue is skipped and marked blocked. Use --skip-learning-gate for emergency runs when the registry is unavailable.

Config tip: Branch naming and merge behavior are controlled by parallel.use_feature_branches in ll-config.json. When true, branches are named feature/<id>-<slug> and auto-merge is skipped, leaving PR-ready branches for review. Set parallel.push_feature_branches: true to also push branches to remote after success, and parallel.open_pr_for_feature_branches: true to open a draft PR via gh and record pr_url: on the issue. See Configuration reference and the Feature-Branch / PR-Based Workflow guide.

Config tip (epic branches): Per-EPIC integration branches are controlled by parallel.epic_branches.enabled in ll-config.json (or --epic-branches for one run). When true, children of a single EPIC coalesce their work onto a shared epic/<EPIC-ID>-<slug> integration branch (parallel.epic_branches.prefix, default epic/) instead of per-worker branches. On the EPIC's last child, the integration branch merges back to base_branch when parallel.epic_branches.merge_to_base_on_complete is true (default), and opens a PR via gh when parallel.epic_branches.open_pr is true. Set parallel.epic_branches.verify_before_merge: true to run test_cmd/lint_cmd against the branch tip before that merge/PR-open — a failure blocks it and leaves the branch open for retry, surfaced in the run summary rather than silently logged. (On the auto-refine-and-implement FSM loop, this check is skipped as redundant when the loop's verify state already produced a fresh passed verdict for the same tip — ENH-2630.) See Configuration reference and the Per-EPIC integration branch guide.

Examples:

ll-parallel                         # Process with default workers
ll-parallel --workers 3             # Use 3 parallel workers
ll-parallel --dry-run               # Preview what would be processed
ll-parallel --priority P1,P2        # Only process P1 and P2 issues
ll-parallel --cleanup               # Clean up worktrees and exit
ll-parallel --stream-output         # Stream Claude output in real-time
ll-parallel --only BUG-001,BUG-002  # Process only specific issues
ll-parallel --type BUG,ENH          # Process bugs and enhancements
ll-parallel --overlap-detection     # Reduce merge conflicts
ll-parallel --handoff-threshold 85  # Override handoff threshold for this run


ll-sprint

Define and execute curated issue sets with dependency-aware ordering.

Subcommands:

ll-sprint create <name>

Create a new sprint.

Argument/Flag Short Description
name Sprint name (used as filename)
--issues Required. Comma-separated issue IDs
--description -d Sprint description
--max-workers -w Max parallel workers (default: 2)
--timeout -t Timeout per issue in seconds (default: 3600)
--skip Issue IDs to exclude
--type Filter by type: BUG, FEAT, ENH, EPIC

ll-sprint run <sprint|EPIC-NNN> / ll-sprint r <sprint|EPIC-NNN>

Execute a sprint or resolve an EPIC's active children as a sprint.

Argument/Flag Short Description
sprint Sprint name or EPIC ID (e.g. EPIC-1234) to resolve and execute
--dry-run -n Show plan without running
--feature-branches Enable/disable feature-branch mode (--feature-branches / --no-feature-branches); overrides parallel.use_feature_branches in config for this run
--epic-branches Enable/disable per-EPIC integration-branch mode (--epic-branches / --no-epic-branches); overrides parallel.epic_branches.enabled in config for this run
--max-workers -w Max parallel workers
--timeout -t Timeout per issue in seconds
--config Path to project root
--resume -r Resume interrupted sprint
--quiet -q Suppress non-essential output
--only Issue IDs to process exclusively during execution
--skip Issue IDs to skip during execution
--skip-analysis Skip dependency analysis
--type Filter by type
--save Write the resolved sprint YAML to .ll/sprints/epic-NNN.yaml before executing (useful for inspect/edit workflows)
--handoff-threshold Override auto-handoff context threshold (1-100)
--context-limit Override context window token estimate
--skip-learning-gate Bypass the pre-flight learning-test batch gate (see below)

When an EPIC ID is passed, resolution is the union of the EPIC's relates_to: field (forward) and any issue with parent: EPIC-NNN (backward), deduplicated and filtered to active statuses (open, in_progress, blocked). Resume works using the normalized epic-NNN name stored in .sprint-state.json.

Pre-flight learning-test gate (ENH-2210): When learning_tests.enabled is true, ll-sprint run aggregates all learning_tests_required targets across every issue in the sprint before the first wave runs, checks each target via ll-learning-tests check --stale-aware, and blocks execution if any are missing or stale. This catches assumption gaps for the entire sprint in a single pre-flight pass rather than discovering them mid-wave. Use --skip-learning-gate to bypass when the registry is unavailable.

Milestone write-back: When ll-sprint run starts, it writes milestone: <sprint-name> to the frontmatter of every issue in the sprint. This makes the sprint assignment visible on each issue file and enables ll-issues list --milestone filtering and ll-sync milestone assignment.

ll-sprint list / ll-sprint l

List all sprints.

Flag Short Description
--verbose -v Show detailed information
--json -j Output as JSON array

ll-sprint show <sprint|EPIC-NNN> / ll-sprint s <sprint|EPIC-NNN>

Show sprint details, dependency graph, and health summary. Accepts either a sprint name or an EPIC ID — when passed an EPIC ID, SprintManager.load_or_resolve() resolves the EPIC's active children into a virtual sprint and renders them in dependency wave order.

Argument/Flag Short Description
sprint Sprint name or EPIC ID (e.g., EPIC-1773)
--json -j Output as JSON (includes all fields)
--config Path to project root
--skip-analysis Skip dependency analysis

ll-sprint edit <sprint> / ll-sprint e <sprint>

Edit a sprint's issue list.

Argument/Flag Description
sprint Sprint name
--add Comma-separated issue IDs to add
--remove Comma-separated issue IDs to remove
--prune Remove invalid/completed issue references
--revalidate Re-run dependency analysis after edits
--config Path to project root

ll-sprint delete <sprint> / ll-sprint del <sprint>

Delete a sprint definition.

ll-sprint analyze <sprint> / ll-sprint a <sprint>

Analyze sprint for file conflicts between issues.

Argument/Flag Short Description
sprint Sprint name
--format -f Output format: text (default), json
--config Path to project root

Examples:

ll-sprint create sprint-1 --issues BUG-001,FEAT-010 --description "Q1 fixes"
ll-sprint run sprint-1
ll-sprint run sprint-1 --dry-run
ll-sprint list
ll-sprint list --json                         # JSON array of all sprints
ll-sprint show sprint-1
ll-sprint edit sprint-1 --add BUG-045,ENH-050
ll-sprint edit sprint-1 --remove BUG-001
ll-sprint edit sprint-1 --prune
ll-sprint delete sprint-1
ll-sprint analyze sprint-1 --format json


Loop Automation

ll-loop

Execute FSM-based automation loops. If the first argument is a loop name (not a subcommand), run is inferred automatically.

Subcommands:

ll-loop run <loop> / ll-loop r <loop>

Run a loop.

Argument/Flag Short Description
loop Loop name or path
input (Optional positional) If valid JSON object with keys matching defined context variables, unpacks into those keys; otherwise stored as a string in context[input_key]
--max-steps -n Override step cap (individual state transitions)
--max-iterations Override full-pass cap (complete loop cycles)
--delay Sleep N seconds between iterations (useful for recording and to relieve host memory pressure between subprocess spawns). Config-defaultable via loops.run_defaults.delay (ENH-2556); an explicit --delay always overrides the configured default.
--no-llm Disable LLM evaluation
--no-host-guard Disable the adaptive host memory-pressure guard (host_guard: block, ENH-2452). By default the guard samples host memory before each prompt-mode state and adds an extra cooldown / routes / aborts per the loop's host_guard: config.
--host-guard-budget-mb N Override host_guard.max_cumulative_subproc_mb: cap on summed peak subprocess RSS (MB) across the run (ENH-2453). 0 disables the budget.
--model Default model for host-CLI action states (prompt/slash_command). Per-state model: key overrides this.
--llm-model Override model for FSM evaluator/judge states (distinct from --model)
--dry-run Show execution plan without running. Diagram rendering is not suppressed — combine with --show-diagrams to preview both the FSM diagram and the execution plan.
--background -b Run as background daemon
--follow -f Stream FSM state transitions to stdout as they fire, in ll-loop history format. Cannot be combined with --background — passing both exits with an error; use ll-logs tail to watch a background loop instead.
--quiet / --qt Suppress progress output
--verbose -v Stream all action output live; default shows a short response head preview
--queue -q Wait for conflicting loops to finish; writes a queue entry to <loops_dir>/.queue/<uuid>.json while waiting (see Queue entries)
--show-diagrams[=MODE] Display FSM diagram after each step. MODE is a topology (layered|neighborhood|inline|window) or preset (detailed|summary|clean|local|slim|oneline). Bare flag selects summary (layered, main-path scope). Override individual facets with --diagram-edge-labels=on\|off, --diagram-state-detail=title\|full, --diagram-scope=main\|full. Breaking (ENH-1672): mainsummary, fulldetailed, miniclean; old values error with migration hints. Viewport auto-degrades layered→window→neighborhood→inline for preset/default sources (the window rung crops the real layered diagram to ±K layers around the active state with ▲ N layers above/▼ M layers below banners — ENH-2410); explicit topology values disable degradation (window is also selectable explicitly).
--clear Clear terminal before each iteration (combine with --show-diagrams for live in-place rendering; suppressed when stdout is not a tty). When combined with --show-diagrams on a tty, the screen splits into a pinned FSM diagram on top and a scrolling action-output region below; on terminals too short for the full diagram the pinned pane falls back first to a windowed view (the real layered diagram cropped to ±K layers around the active state, with ▲ N layers above/▼ M layers below overflow banners — ENH-2410), then to a 1-hop neighborhood view (predecessors → [active] → successors), then to a single-line fsm: ... → [...] → ... status. The pane redraws on SIGWINCH (terminal resize). When a parent loop spawns child loops, the pinned pane shows only the deepest active child loop rather than all nesting levels simultaneously — keeping the pane readable regardless of loop depth.
--builtin Load loop from built-ins directory (bypasses project .loops/ lookup)
--context KEY=VALUE Override a context variable (repeatable)
--program-md PATH Load steering directive from a Markdown file (default: .ll/program.md when present); parsed fields injected into context before --context overrides. See program-md reference.
--worktree Run loop in an isolated git worktree on a new branch named TIMESTAMP-LOOP-NAME. On exit, if the worktree is clean (no uncommitted changes, no commits ahead of base), the branch is deleted. If pending work is detected (uncommitted changes or commits ahead), the branch is retained and a warning is printed with the branch name for recovery (git checkout BRANCH-NAME). Cannot be combined with --background — passing both exits with an error.
--baseline Run a blind A/B comparison: executes primary skill with full evaluation gates (harness arm) and creates a matching ungated invocation (baseline arm) in parallel, then feeds both outputs into a blind LLM judge. Writes ab.json to the run directory and prints a terminal summary with pass-rate delta, Wilson 95% CI bounds [lo, hi] for each arm, and token/duration ratios. Cannot be combined with --worktree — passing both exits with an error.
--baseline-skill Override the baseline arm skill (default: extracted from the execute state action). Accepts a full slash command such as /ll:some-skill.
--cross-host Re-run the loop on a second available host CLI and append a cross-host comparison table to the baseline report. Requires --baseline. The comparison runs the execute state on the alternate host, then feeds both outputs into the same blind LLM judge. (ENH-2086)
--items Number of compare cycles to run (default: iterate with MIMO packing heuristics)
--cost-output-json PATH Also write the per-state cost report to PATH as machine-readable JSON (same shape as CostReport.write_json — see Per-State Token/Cost Summary). The human-readable table is unaffected. Forwarded through ll-loop run --background re-exec so detached runs honor the flag (BUG-1414).
--handoff-threshold Override auto-handoff context threshold (1-100)
--context-limit Override context window token estimate
--no-lock Run without acquiring the scope lock, bypassing the conflict check. Caution: this allows concurrent runs that may interfere with each other on shared resources. Use when you need parallel runs that operate on disjoint paths or when testing a loop that would otherwise be blocked by a stale lock you cannot clear.
Model Header Display (ENH-1805)

ll-loop run and ll-loop monitor print a header line showing the active LLM model name on startup, detected from the Claude CLI stream-json init event (same mechanism as ll-auto). The model name appears in the first output line after the logo banner:

ll-loop run general-task "fix the lint warnings"
  model: claude-sonnet-4-6
  [state transitions follow]

When --llm-model is passed, the header reflects the override model. When the detection fails (e.g., non-Claude host), the field shows unknown.

Per-State Token/Cost Summary (ENH-1797)

After a loop run completes, ll-loop run prints a per-state token and cost summary table immediately before the final completion line. The table is produced whenever at least one LLM action (prompt or slash_command) executed during the run.

state                    invoc    input   output    cache     est_cost
────────────────────────────────────────────────────────────────────
execute                      3   12 400    2 100    8 500      $0.042
check_semantic               3    3 200      480    2 900      $0.011
────────────────────────────────────────────────────────────────────
TOTAL                        6   15 600    2 580   11 400      $0.053

Columns:

Column Description
state FSM state name
invoc Number of times the state ran an LLM action
input Total input tokens (prompt + cached)
output Total output tokens
cache Cache read tokens (cache_read_tokens)
est_cost Estimated USD cost (using pricing.py MODEL_PRICING constants; shown as ~$X.XXX (model unknown) when the model is not in the pricing table)

Shell (action_type: shell) and MCP tool (action_type: mcp_tool) states are omitted from the table — they produce no token usage row in usage.jsonl.

The raw per-iteration data lives at .loops/runs/<run-id>/usage.jsonl (not archived to .loops/.history/). See Output Artifacts for the usage.jsonl schema.

Machine-Readable JSON Output (--cost-output-json, ENH-2477)

Pass --cost-output-json PATH to also write the same per-state aggregates as a stable JSON document (built by CostReport.from_usage_jsonl().write_json() at fsm/cost_graph.py). The shape is locked so downstream dashboards can parse without depending on the human-readable table layout:

{
  "states": [
    {
      "state": "execute",
      "iterations": 3,
      "input_tokens": 12400,
      "output_tokens": 2100,
      "cache_read_tokens": 8500,
      "cache_creation_tokens": 0,
      "cost_usd": 0.0421,
      "wallclock_ms": 18500
    }
  ],
  "totals": {
    "iterations": 6,
    "input_tokens": 15600,
    "output_tokens": 2580,
    "cache_read_tokens": 11400,
    "cost_usd": 0.0533,
    "wallclock_ms": 24300
  }
}

State rows are sorted by name. Totals mirror the same metric keys. cost_usd is 0.0 for any state where at least one row used an unknown model (the has_unknown_model flag is surfaced only in the Python API, not the JSON). The flag is forwarded through ll-loop run --background re-exec so detached runs honor the same destination (BUG-1414 prevention).

Note: agent:, tools:, and model: are per-state YAML fields, not CLI flags. See Subprocess Agent and Tool Scoping in the Loops Guide for per-state agent, tool, and model scoping options.

Failure Reason Display

When a loop exits a non-success terminal state, ll-loop run prints the failing state's output as a Failure reason block immediately before the completion line. This surfaces failure context that would otherwise be invisible: the alt-screen wipes on teardown, and non-verbose runs never echo per-state stdout inline.

Failure reason:
│ harness exit code 1: assertion failed on line 42
│ expected "done", got "failed"

The block is shown when the run was in alt-screen mode or in non-verbose (--quiet) mode. It is suppressed in --verbose mode, where the live renderer already echoed the output. Output is capped at 40 lines to bound scrollback.

Queue entries (.loops/.queue/)

When ll-loop run --queue encounters a scope conflict with a running loop, it creates <loops_dir>/.queue/<uuid>.json before entering the wait and removes it on lock acquisition, timeout, error, or process exit (via atexit). The file lets external observers (e.g. a dashboard) enumerate loops that are waiting on a scope lock without scanning process state.

Ordering: When multiple loops are waiting on the same lock, they acquire it in FIFO (arrival) order — the first loop to enqueue is the first to run after the current holder exits.

Entry schema:

{
  "id": "<uuid>",
  "loopName": "<loop name>",
  "enqueuedAt": "<ISO 8601 UTC timestamp>",
  "context": {
    "waitingFor": "<name of conflicting running loop>",
    "scope": ["<scope path>", ...],
    "pid": <integer PID of the waiting process>
  }
}

Entries are short-lived and ephemeral — treat the directory as a live view, not a history log. Stale entries are possible if a process exits abnormally without running atexit handlers; ll-loop queue list (below) is that cleanup tooling — reading the queue prunes entries whose pid is no longer alive.

ll-loop queue list

List pending entries in the process-backed run queue (the .loops/.queue/*.json files documented under Queue entries above). This is the observability surface for loops waiting on a scope lock via ll-loop run --queue.

Pruning side effect: listing the queue calls read_queue_entries(), which unlinks dead-PID entries as a side effect — every rendered entry is alive by construction. Running ll-loop queue list is therefore also the sanctioned way to garbage-collect stale queue files left behind by a process that exited without running its atexit handlers.

Human-readable output prints a Pending queue entries (N): header followed by one line per entry, sorted ascending by enqueue time:

Pending queue entries (2):

  a1b2c3d4  my-loop  pid=12345  alive  2026-07-13 19:40:00
  e5f6a7b8  other-loop  pid=12346  alive  2026-07-13 19:41:12

Each line is <short id (first 8 chars)> <loopName> pid=<pid> alive <YYYY-MM-DD HH:MM:SS>. When the queue is empty, it prints Queue is empty.

Flag Short Description
--json -j Emit the queue as a JSON array (one object per entry, using the entry schema); an empty queue emits []. Exit code is 0 in all cases.

ll-loop queue remove <id>

Cancel a queued waiter: signal its process (SIGTERM) and delete its .loops/.queue/<uuid>.json entry. This is the way to abandon a loop that is blocked waiting on a scope lock via ll-loop run --queue — the counterpart to the read-only ll-loop queue list.

The <id> argument accepts either a full uuid or an 8+-character prefix (the short id shown by ll-loop queue list). Before signaling, remove runs a psutil identity check confirming the entry's context.pid is really a live ll-loop waiter; if the check fails, the entry file is still deleted but no signal is sent (pass --force to signal anyway). The entry file is always deleted whether or not the signal landed, because the waiter's atexit cleanup does not fire on SIGTERM. remove never touches the running lock-holder — that PID lives in a separate .running/ namespace.

Exit codes: 0 on success (entry deleted); 1 when the <id> matches no entry or is an ambiguous prefix matching more than one entry.

Flag Short Description
--force Bypass the psutil identity check and signal the tracked pid unconditionally.
--json -j Emit the result as a JSON object: {"id", "removed", "signaled", "identityVerified", "pid"}.

Bare ll-loop queue (no subcommand): invoking ll-loop queue without a subcommand prints the queue subparser help and exits with code 1.

ll-loop validate <loop> / ll-loop val <loop>

Validate a loop definition file.

In addition to structural checks (reachability, evaluator fields, routing consistency), validation applies meta-loop lint rules when a loop is classified as a meta-loop (writes harness artifacts, imports lib/benchmark.yaml, or references yaml_state_editor/replace_action):

  • MR-1 (ERROR): A meta-loop must have at least one non-LLM evaluator (exit_code, output_numeric, output_json, output_contains, convergence, diff_stall, score_stall, action_stall, harbor_scorer, mcp_result). LLM self-grades on harness updates are unreliable (SHOR Table 1: 33–55% accuracy). Triggers a ValueError (exit code 1) that blocks the loop from running.
  • MR-2 (WARNING): A meta-loop should reference a captured baseline value in a later evaluator (evaluate.previous, evaluate.target, or evaluate.source). This ensures a measure→propose→apply→re-measure spine is present. Does not block validation.
  • MR-3 (WARNING): A loop writes intermediate artifacts to shared .loops/tmp/ instead of the runner-injected ${context.run_dir}/. Concurrent runs (e.g., under ll-parallel) will corrupt each other's state. Does not block validation. Suppressed by shared_state_ok: true.
  • MR-4 (WARNING): An LLM-judged state (action_type: prompt/slash_command, or an explicit llm_structured/check_semantic evaluator) maps on_yes but has no route for no or partial verdicts — with no next: or route: table with a default. The loop silently dead-ends when the judge returns no/partial; a parent loop reads this as failed. Does not block validation. Suppressed by partial_route_ok: true.
  • MR-5 (WARNING): A harness-category loop writes artifact files to a flat path in an iterative generate→evaluate→generate cycle without per-iteration versioning. Intermediate versions are lost; only the final output survives. Add per-iteration snapshots (see oracle generator-evaluator for the snapshot-state pattern) and declare artifact_versioning: true. Does not block validation. Suppressed by artifact_versioning: true (loop snapshots artifacts) or artifact_versioning_ok: true (intentional overwrite). (ENH-1957)
  • MR-6 (WARNING): A meta-loop has a shell-type state that writes to the same file path as an LLM-generator state (prompt/slash_command with yaml_state_editor or replace_action markers). Hand-patching creates fragile output that diverges from the generator on the next run; fix the generator action so every run produces correct output automatically. Does not block validation. Suppressed by generator_fix_ok: true for intentional post-processing. (ENH-2079)
  • MR-7 (ERROR): A FSM action string contains an unescaped ${namespace.path:-default} (bash :- parameter-expansion default syntax). The FSM interpolation engine does not support this form and will crash at runtime with Path 'ns.path:-default' not found in context. Use ${ns.path:default=value} (engine-native) or $${VAR:-value} (shell-escaped) instead. Blocks the loop from running. Suppressed by bash_default_ok: true. (ENH-2348)
  • MR-8 (WARNING): A check_semantic/llm_structured state whose evaluate.prompt does not contain evidence-contract keywords (verbatim, quote, evidence). Verdicts without verbatim citation requirements default to optimism (SHOR Table 1: 33–55% accuracy; Sonnet 4.6 = 33.4%). States with evaluate.prompt: null inherit DEFAULT_LLM_PROMPT which includes the contract automatically and are not flagged. Does not block validation. Suppressed by evidence_contract_ok: true. (ENH-2342)
  • MR-9 (ERROR): A shell action string contains $$( or $$VAR (over-escaped bash). The FSM interpolator only rewrites the brace form $${...}${...}; bare $(...) and $VAR are passed to bash -c untouched. Doubling them causes the leading $$ to expand to the runner's PID, silently corrupting every downstream ${captured.*} reference (e.g. echo "$$(pwd)" captures <pid>(pwd) instead of a path). Use single $ for command substitution and variables; reserve $$ exclusively for the $${VAR} brace form that collides with ${ns.path} interpolation. Blocks the loop from running. Suppressed by shell_pid_ok: true. (BUG-2368)
  • MR-10 (WARNING): A shell-type state whose inline Python calls json.loads/json.load, catches JSONDecodeError/ValueError/bare Exception, and explicitly exits 0 (sys.exit(0) or exit(0)) — without an on_error: route — silently discards parse failures. The FSM receives exit 0 and treats the state as successful, producing zero results with no log, no stderr, and no non-zero exit code. Add on_error: to the state to route parse failures explicitly. Does not block validation. Suppressed by parse_swallow_ok: true when treating a parse failure as an empty result is intentional. (BUG-2383)
  • MR-11 (WARNING): A shell-type state pastes a user-controlled ${context.input|goal|description|task|prompt|query|topic} value raw into the action body, outside a safe position (single-quoted string, quoted heredoc <<'EOF', or the :shell suffix). interpolate() substitutes with a bare str(value) and no shell escaping; a value containing ", $, `, \, or ! breaks bash tokenizing (misrouting the loop to on_error/on_no) or, from an untrusted source, injects commands. Fix by wrapping the placeholder in single quotes, writing it through a quoted heredoc, or using ${context.input:shell} to shlex-quote it at interpolation time. Does not block validation. Suppressed by unsafe_context_interpolation_ok: true. (BUG-2622)

MR-1, MR-2, and the multimodal evaluator blind-spot rule are suppressed by setting meta_self_eval_ok: true at the loop top-level (with a justifying comment). MR-3 is suppressed by shared_state_ok: true. MR-4 is suppressed by partial_route_ok: true. MR-5 is suppressed by artifact_versioning: true or artifact_versioning_ok: true. MR-6 is suppressed by generator_fix_ok: true. MR-7 is suppressed by bash_default_ok: true. MR-8 is suppressed by evidence_contract_ok: true. MR-9 is suppressed by shell_pid_ok: true. MR-10 is suppressed by parse_swallow_ok: true. MR-11 is suppressed by unsafe_context_interpolation_ok: true.

  • Zero-retry counter pattern (WARNING): Detects states whose retry config sets max_retries: 0 alongside a non-zero retry_count counter variable, or retry_count that is never incremented in any on-error transition. A zero-retry counter pattern means the state will never actually retry despite having retry infrastructure wired — this is almost always a configuration mistake. Does not block validation.
  • Multimodal evaluator blind-spot (WARNING): Detects harness-loop states that use an LLM multimodal prompt (screenshot/image) evaluated via output_contains as the sole gate routing directly to a terminal state. LLMs can silently fall back to text-only analysis when reading images, producing verdicts from incomplete information without the output_contains evaluator detecting the gap. Consider adding a shell-action verification state (e.g., functional smoke test) between scoring and the terminal. Does not block validation. Suppressed by meta_self_eval_ok: true.
  • Unresolvable static loop: references (ERROR): A state whose loop: key contains a non-interpolated (static) target name that cannot be resolved to a .yaml file at definition time will fail identically at runtime (FileNotFoundError in resolve_loop_path). Originally a WARNING (BUG-2305) on the theory that some references are intentionally optional, but this theory does not hold — dynamic names containing ${...} are already skipped, so any remaining static name either resolves or fails. The validator now emits an ERROR and ll-loop validate exits 1, blocking the loop from loading via load_and_validate. Fix: correct the target name (sub-loops under oracles/ require the full relative path, e.g. loop: oracles/verify-confidence-scores, not loop: verify-confidence-scores). (BUG-2400)
  • Capture reachability (WARNING/ERROR): Detects states that reference ${captured.<var>.*} in their action or evaluator source where the capturing state may not execute on all code paths to the referencing state. Uses dominance analysis (reverse BFS) to check whether every path from initial to the referencing state passes through at least one of the capturing states. When a variable is produced by more than one state on mutually-exclusive branches (e.g. fifo_pop and select_next both capture input), the validator accepts the reference as safe if the set of capturing states collectively dominates the referencing state — every path must pass through at least one member of the set. Emits an ERROR when the referenced capture variable has no capturing state at all in the current FSM (likely a missing capture: declaration). Emits a WARNING when a bypassing path exists — the variable may be undefined at runtime if the bypass path is taken. Sub-loop exception: when the loop contains sub-loop states and the variable has no capturing state in the parent FSM, the validator emits a WARNING rather than an ERROR — the capture may legitimately live in a child namespace; the WARNING ensures typos still surface rather than going completely dark. Does not block validation for warnings; errors block validation. (ENH-1961, BUG-1997, ENH-1998)

Flags:

Flag Short Description
--json -j Output validation result as JSON. On success: {"valid": true, "loop": "<name>", "warnings": [...]}. On failure: {"valid": false, "loop": "<name>", "error": "<message>", "warnings": [...]}. Exit code is unchanged (1 for ERROR, 0 for clean/warnings-only). (ENH-2090)

ll-loop list / ll-loop l

List available loops. Discovery is recursive: runnable loops nested under subdirectories of loops/ (e.g. oracles/oracle-capture-issue) are included, while library fragments under loops/lib/ are filtered out via is_runnable_loop(). Output is grouped by category, each category using its own header color via the CATEGORY_COLOR map. Each header carries an inline rollup badge (e.g. 2 built-in, 1 project) and dimensions of the kind/label/description columns are computed once per render to fit terminal_width(default=120). Categories with a dominant name-prefix cluster (≥3 members sharing the apo- prefix, etc.) get a bold subgroup subhead in the parent's category color, with leaves indented one level deeper. Visibility (built-in / project / internal / example) is a first-class column rather than a trailing marker; known label classes (hitl, comparison, generated, meta) get distinct ANSI colors. Output ends with a bold TOTAL: summary line that surfaces loop/category counts plus a dim hidden-tier hint when applicable. All-caps section markers (category headers, subgroup subheads, summary lines) use the _all_caps helper, while body content (name, kind, labels, description) stays mixed case. No body text is rendered with dim/faint ANSI; only the hidden-tier hint keeps dim. CATEGORY_COLOR no longer duplicates the FEAT green ("32") across code-quality and quality — both pick distinct 256-color codes. (ENH-2539, refined in v2 polish)

For nested loops, the displayed identifier is the relative path without the .yaml suffix (e.g. oracles/oracle-capture-issue) — the same string ll-loop run and ll-loop validate accept. Top-level loops continue to display as their bare stem. Override suppression (a project loop hiding a built-in of the same name) keys on the full relative path, not the bare stem — so a project oracles/foo.yaml does not suppress a built-in top-level foo.yaml.

Flag Short Description
--running Only show currently running loops
--status STATUS With --running, filter to loops with the given status (e.g. interrupted, awaiting_continuation)
--builtin Only show built-in loops (exclude project .loops/)
--category <cat> -c Filter to loops with the given category (e.g. apo, issue-management, code-quality)
--label <tag> -l Filter to loops that carry the given label tag; repeat for multiple tags (OR match)
--all / -a -a Show all loops including internal sub-loops and examples (hidden by default)
--internal Show only internal (delegated-only) sub-loops
--examples Show only example/template loops
--visibility {public,internal,example,all} Filter loops by visibility tier: public (routable, default view), internal, example, or all. Composes with --label and --json.
--json / -j Output as JSON array. Without --running: each entry includes name (relative-path identifier — e.g. oracles/oracle-capture-issue for nested loops, fix-quality-and-tests for top-level), path, category, labels, visibility ("public" | "internal" | "example"), description, and built_in. With --running: each entry is a LoopState object (loop_name, status, current_state, iteration, updated_at, etc.); instance_id is absent from this output — use ll-loop status <loop> --json to resolve per-instance details

ll-loop status <loop> / ll-loop st <loop>

Show current status of a loop. Aggregates across all running instances of <loop>.

Flag Short Description
--json -j Output loop state as JSON. Returns a single object when one instance is running; returns a JSON array of objects (each including instance_id, pid, pid_source, log_file, events_file) when two or more instances are running. The pid field is populated from the .pid file if present, otherwise falls back to the .lock file. The pid_source field is "pid_file", "lock_file", or null. log_file is a path for both foreground and background runs; null only for background-spawned child processes (--foreground-internal) or pre-ENH-1703 state files. events_file points to the <instance-id>.events.jsonl file, which exists for all run modes

The human-readable Log: line uses one of three labels: - Log: <path> — background run with its .log file present (normal case) - Log: (foreground run — output went to terminal) — legacy fallback only (pre-ENH-1703 instances or instance_id=None runs); foreground runs after ENH-1703 always produce a .log file and display Log: <path> instead - Log: (expected <path>, missing).pid file exists but .log was deleted (something went wrong)

An Events: line follows whenever an <instance-id>.events.jsonl file is found, showing the event count and age of the most recent event regardless of run mode.

Note: ll-loop status is not a pure read — it may transparently rewrite orphaned state files. If a state file claims status: running but its PID (resolved via .pid.lock → embedded state.pid) is provably dead, the file is updated in-place to status: interrupted with a reconciled_at timestamp. This is a no-op for live processes and is idempotent.

ll-loop stop <loop>

Stop a running loop. Terminates all running instances of the named loop (no --instance-id selector).

Termination is process-cohort aware (ENH BUG-2147): before sending any signal, ll-loop stop walks the full descendant tree of the root PID via pgrep -P recursion, collecting every child and grandchild process. Children are terminated first (so the root cannot respawn them), then the root receives SIGTERM. If any member of the cohort is still alive after 10 s, the entire group is escalated to SIGKILL. This ensures that loops spawning child CLIs (e.g. the claude binary launched with start_new_session=True) are reliably cleaned up rather than left as orphans.

Also handles loops in interrupted state that hold an orphaned lock-file PID: if .loops/.running/<loop>.lock exists and its PID is alive, ll-loop stop terminates the full process cohort as above and removes the lock file. This resolves scope conflicts that block subsequent ll-loop run invocations without requiring manual kill + rm. If the lock-file PID is already dead, the stale lock is cleaned up and reported.

ll-loop resume <loop> / ll-loop res <loop>

Resume a loop. Resumable statuses are "running", "awaiting_continuation", "interrupted" (Ctrl-C, the runner caught the signal itself), and "user_stopped" (clean ll-loop stop — ENH-2522 wrote a user-stop.marker so the runner can distinguish this from a kernel kill). Loops that died from a kernel/SIGKILL/OOM kill terminate with terminated_by="system_signal" and are not resumable — the runner died mid-state and there is no clean recovery point. When no --instance-id is given, the most recent resumable instance is auto-selected. Use --instance-id to disambiguate when you need a specific instance.

Flag Short Description
--instance-id <id> Select a specific instance to resume (auto-detected if omitted)
--background -b Resume as a detached background process
--context KEY=VALUE Override a context variable (repeatable)
--show-diagrams[=MODE] Display FSM diagram after each step. MODE is a topology (layered|neighborhood|inline|window) or preset (detailed|summary|clean|local|slim|oneline). Bare flag selects summary (layered, main-path scope). Override individual facets with --diagram-edge-labels=on\|off, --diagram-state-detail=title\|full, --diagram-scope=main\|full. Breaking (ENH-1672): mainsummary, fulldetailed, miniclean; old values error with migration hints. Viewport auto-degrades layered→window→neighborhood→inline for preset/default sources (the window rung crops the real layered diagram to ±K layers around the active state with ▲ N layers above/▼ M layers below banners — ENH-2410); explicit topology values disable degradation (window is also selectable explicitly).
--clear Clear terminal before each iteration (combine with --show-diagrams for live in-place rendering; suppressed when stdout is not a tty). When combined with --show-diagrams on a tty, the screen splits into a pinned FSM diagram on top and a scrolling action-output region below; on terminals too short for the full diagram the pinned pane falls back first to a windowed view (the real layered diagram cropped to ±K layers around the active state, with ▲ N layers above/▼ M layers below overflow banners — ENH-2410), then to a 1-hop neighborhood view (predecessors → [active] → successors), then to a single-line fsm: ... → [...] → ... status. The pane redraws on SIGWINCH (terminal resize). When a parent loop spawns child loops, the pinned pane shows only the deepest active child loop rather than all nesting levels simultaneously — keeping the pane readable regardless of loop depth.
--delay Sleep N seconds between iterations (useful for recording and to relieve host memory pressure between subprocess spawns)
--no-host-guard Disable the adaptive host memory-pressure guard (host_guard: block, ENH-2452)
--handoff-threshold Override auto-handoff context threshold (1-100)
--context-limit Override context window token estimate

ll-loop monitor <loop>

Attach to a running loop and render its FSM state in realtime. Read-only: tails <instance-id>.events.jsonl and the loop's .log file from disk and forwards events to the same StateFeedRenderer used by ll-loop run. Ctrl-C detaches from the rendered stream without sending any signal to the loop process (use ll-loop stop to terminate the loop). When no instance is running (no live PID), prints the last-known state of the most recent instance and exits 0.

ll-loop monitor fix-types               # tail events and log
ll-loop monitor fix-types --show-diagrams --clear    # pinned FSM diagram + scrolling log
ll-loop monitor fix-types --clear              # with clear-screen on redraw
Flag Short Description
--show-diagrams[=MODE] Display FSM diagram alongside events (same semantics as ll-loop run).
--diagram-edge-labels Override edge-label visibility (on|off).
--diagram-state-detail Override state-detail level (title|full).
--diagram-scope Override diagram scope (main|full).
--clear Pin the FSM diagram and stream events below (TTY only).
--no-clear Disable terminal clearing between iterations (scroll output instead).
--quiet / --qt Suppress progress output.
--verbose -v Show full prompt at action start.

ll-loop history <loop> / ll-loop h <loop>

Show execution history for a loop.

Flag Short Description
run_id (Optional positional) Archived run ID to inspect; omit to list all archived runs
--tail -n Last N events to show (default: 50)
--event -e Filter by event type (e.g. evaluate, route, state_enter)
--state -s Filter by state name (matches state, from, or to fields)
--since Filter to events within a time window (e.g. 1h, 30m, 2d)
--verbose -v Show action output preview and LLM call details (model, latency, prompt, response)
--full Show untruncated prompts and output (implies --verbose)
--json -j Output events as JSON array

ll-loop test <loop> / ll-loop t <loop>

Run a single test iteration to verify loop configuration.

ll-loop simulate <loop> / ll-loop sim <loop>

Trace loop execution interactively without running commands.

Flag Short Description
--scenario Auto-select results: all-pass, all-fail, first-fail, alternating
--max-steps -n Override step cap (default: min of loop config or 20)
--max-iterations Override full-pass cap for simulation

Runner-injected context variables (run_dir, input, run_timestamp) are populated before simulation begins, matching the behaviour of ll-loop run. Loops that reference ${context.run_dir} in early states can be tested with simulate without errors (BUG-2118).

ll-loop install <loop>

Copy a built-in loop to .loops/ for customization.

ll-loop show <loop> / ll-loop s <loop>

Show loop details and FSM structure. The header line displays active per-loop config overrides (e.g., config: handoff_threshold=60) when a config: block is present in the loop YAML.

The Commands section at the bottom of the output can be overridden by adding a top-level commands: list to the loop YAML. Each entry is a {cmd, comment} pair; when present, this list replaces the five generic default commands so that loops requiring --param or --context flags can surface copy-paste-ready examples. See docs/generalized-fsm-loop.md for the full commands: schema.

State overview table columns. When --show-diagrams is not in --json mode, the main body of ll-loop show prints a compact state overview table with four columns:

Column Meaning
State State name; the initial state is prefixed with .
Type Action shape: sub-loop (state with a loop: field — renders magenta), shell, agent, llm_structured, check_semantic, etc.; (terminal) states show . Lets you read at a glance which states delegate to another loop vs run inline logic.
Action Preview First non-blank line of the state's action: source (or [sub-loop: <name>] for delegating states), truncated to fit terminal width.
Transitions Compact routing summary: each on_yes / on_no / on_error / on_partial / next / route.<verdict> label grouped by target (e.g., yes→done, no→retry); emits when the state has no explicit routing.

This state-overview table is separate from the --show-diagrams ASCII diagram — the table is always rendered in --clean / --summary / default output as a quick reference; the diagram shows the graph topology when --show-diagrams is enabled.

Flag Short Description
--json -j Output FSM config as JSON
--resolved Expand sub-loop states inline under _subloop key (requires --json)
--show-diagrams[=MODE] Display FSM diagram inline. MODE is a topology (layered|neighborhood|inline|window) or preset (detailed|summary|clean|local|slim|oneline). Bare flag selects summary. Override facets with --diagram-edge-labels=on\|off, --diagram-state-detail=title\|full, --diagram-scope=main\|full. Mutually exclusive with --json.
--diagram-edge-labels Override edge-label visibility for the diagram (on|off).
--diagram-state-detail Override state-detail level for the diagram (title|full).
--diagram-scope Override diagram scope (main|full).

ll-loop fragments <lib>

List fragments defined in a library file, showing each fragment's name and description. Resolves the library path relative to .loops/, then falls back to the built-in library directory.

ll-loop fragments lib/common.yaml         # list built-in common fragments
ll-loop fragments lib/cli.yaml            # list built-in CLI tool fragments
ll-loop fragments lib/benchmark.yaml      # list built-in benchmark runner fragment
ll-loop fragments lib/prompt-fragments.yaml  # list built-in prompt fragment library
ll-loop fragments lib/harness.yaml        # list built-in Playwright screenshot fragment
ll-loop fragments .loops/my-lib.yaml      # list project-local fragment library

ll-loop next-loop

Inspect .loops/.history/ and suggest the next loop(s) to run, with resolved input parameters where available. Useful for unattended chaining or scheduled follow-up work.

Flag Short Description
--count N -n Return top N suggestions instead of just one (default: 1)
--json -j Output suggestions as a JSON array
--execute Run the top suggestion immediately via the same code path as ll-loop run
--exclude NAME Skip the named loop from suggestions (repeatable; useful from on-completion hooks to avoid trivial self-loops)

Each suggestion includes a scored rationale (run frequency, recency, success rate) and a ready-to-paste shell command. For autodev, the suggested input is automatically resolved to the current set of status: open issue IDs.

JSON output keys: loop, input, context, score, rationale, command

Examples:

ll-loop next-loop                          # Top suggestion with human-readable output
ll-loop next-loop --count 3                # Top 3 ranked candidates
ll-loop next-loop --json                   # Machine-readable suggestion
ll-loop next-loop --execute                # Run the top suggestion immediately
ll-loop next-loop --exclude autodev        # Skip autodev (e.g. from its own on-completion hook)
ll-loop next-loop --count 3 --json        # Top 3 as JSON for downstream tooling

ll-loop audit-meta

Read meta-eval.jsonl from archived runs and print a summary table of LLM vs. external-evaluator agreement statistics. Useful for diagnosing meta-loops where the LLM judge may be too lenient or agreeing on no-op iterations.

Flag Short Description
--json -j Output stats as a JSON object

Exit codes: 0 = no divergence flags triggered; 1 = at least one threshold crossed (agreed: false streak ≥ 3, or trivial-agreement streak ≥ 3).

Examples:

ll-loop audit-meta harness-optimize        # Human-readable summary table
ll-loop audit-meta harness-optimize --json # JSON output for scripting

ll-loop diagnose-evaluators

Scan .loops/.history/*-<loop>/events.jsonl to detect non-discriminating evaluator states whose verdict has near-zero variance across runs. Flags states below --threshold (default 0.05) with pattern-matched recommendations for improving discriminating power.

Flag Short Description
--threshold Variance floor below which a state is flagged (default: 0.05)
--min-runs Minimum runs required for meaningful variance (default: 10)
--json -j Output results as a JSON object; each evaluator entry includes ci_lower and ci_upper (Wilson 95% CI bounds on the pass-rate)

Exit codes: 0 = no low-variance states found or insufficient data; 1 = at least one non-discriminating evaluator flagged.

Examples:

ll-loop diagnose-evaluators harness-refine-issue              # Human-readable report
ll-loop diagnose-evaluators harness-refine-issue --json        # JSON output for scripting
ll-loop diagnose-evaluators harness-refine-issue --threshold 0.1 --min-runs 5

ll-loop calibrate-budget

Report per-evaluator Bernoulli variance p*(1-p) to decide whether increasing max_steps will improve outcomes. Calls the same analytics engine as diagnose-evaluators but frames output around retry-budget ROI: evaluators below the variance threshold waste iterations and should be fixed before raising max_steps.

Flag Short Description
--threshold Variance floor below which a state is flagged (default: 0.05)
--min-runs Minimum runs required for meaningful variance (default: 10)
--json -j Output results as a JSON object; each evaluator entry includes ci_lower and ci_upper (Wilson 95% CI bounds on the pass-rate)

Exit codes: 0 = all evaluators healthy or insufficient data; 1 = at least one evaluator flagged (fix before increasing max_steps).

Examples:

ll-loop calibrate-budget rn-refine                    # Human-readable variance report
ll-loop calibrate-budget rn-refine --json              # JSON output for scripting
ll-loop calibrate-budget rn-refine --threshold 0.1 --min-runs 5

ll-loop promote-baseline

Promote the latest run's action output as the new comparator baseline. Reads action_output events from the most recent .loops/.history/*-<loop>/events.jsonl and writes the concatenated output to .loops/baselines/<loop>/output.txt. Use this to manually set the baseline after inspecting a run, as an alternative to auto_promote: true.

Arguments: | Argument | Description | |----------|-------------| | loop | Loop name |

Exit codes: 0 = baseline promoted successfully; 1 = no history found or no action_output events.

Examples:

ll-loop promote-baseline my-loop    # Promote latest run as new baseline

ll-loop edit-routes

Render a loop's routing logic as an editable decision table. Opens the table in $EDITOR (or prints to stdout with --dry-run). On save, parses the edited table and writes changes back to the loop YAML, preserving all non-route fields, comments, and YAML structure.

Two rendering modes:

  • State × verdict matrix (default) — one row per state, one column per verdict. Used for standard loops.
  • Compound decision table (auto-detected or --decision-table) — used for loops that import lib/policy-router.yaml with a context.policy_rules block. Renders a condition-columns × action grid where each row is one conjunctive rule, columns are scored dimensions, and the final column is the target action state.

Before opening the editor in verdict-matrix mode, prints warnings for: unreachable states, dead-end states (no outbound routes and not terminal), and missing verdict arms (e.g. on_yes without on_no or default). In decision-table mode, warns on shadowed rules, missing catch-all, and unknown action states.

Arguments: | Argument | Default | Description | |----------|---------|-------------| | loop | | Loop name or path | | --format {markdown,csv} | markdown | Output format for the table | | --dry-run | | Print table to stdout without opening editor | | --no-warnings | | Skip gap/conflict detection output | | --allow-delete | | Allow deletion of state blocks that were removed from the edited table (default: removed rows are ignored) | | --decision-table | | Force compound policy-router decision table instead of state × verdict matrix |

State operations via the verdict-matrix table: - Edit routes — change any cell in the table; the corresponding on_<verdict> field is updated on save. - Add a terminal stub — add a new row with a state name that doesn't exist yet and leave all verdict cells empty. On save the state is inserted with terminal: true as a placeholder you can expand later. - Delete a state — remove a row entirely, then re-run with --allow-delete. Without --allow-delete, deleted rows are silently ignored.

Compound decision table format:

| #  | confidence | outcome | security | aggregate | → action     |
|----|------------|---------|----------|-----------|--------------|
| 1  | —          | —       | <65      | —         | escalate     |
| 2  | >=85       | >=75    | —        | —         | implement    |
| 3  | *          | *       | *        | *         | deep_repair  |

Each condition cell is an operator+value (>=85, <65, ==true). Empty cell () means dimension unconstrained in that rule. Catch-all row uses * in all condition columns (first-match-wins, catch-all should be last). Edit cells or reorder rows; save to write changes back to context.policy_rules.

Exit codes: 0 = success or no changes; 1 = parse error or unknown state in edited table (when not a new stub); 2 = loop not found.

Examples:

ll-loop edit-routes rn-implement             # Open routing table in $EDITOR
ll-loop edit-routes rn-implement --dry-run   # Print table to stdout
ll-loop edit-routes rn-implement --format csv --dry-run   # CSV format
ll-loop edit-routes rn-implement --no-warnings            # Skip gap warnings
ll-loop edit-routes rn-implement --allow-delete           # Apply row deletions
ll-loop edit-routes policy-refine --decision-table        # Compound table (explicit)
ll-loop edit-routes policy-refine --dry-run               # Auto-detects decision-table mode

Examples:

ll-loop fix-types                     # Run loop (shorthand for run)
ll-loop run fix-types --worktree      # Run in isolated git worktree
ll-loop run fix-types --dry-run       # Show execution plan
ll-loop run fix-types --dry-run --show-diagrams          # FSM diagram + execution plan
ll-loop run fix-types --dry-run --show-diagrams=detailed # Detailed diagram + plan
ll-loop validate fix-types            # Validate loop definition
ll-loop test fix-types                # Run single test iteration
ll-loop simulate fix-types            # Interactive simulation
ll-loop simulate fix-types --scenario all-pass
ll-loop list                          # List available loops
ll-loop list --running                # List running loops
ll-loop list --json                   # JSON array of available loops
ll-loop status fix-types              # Show loop status
ll-loop status fix-types --json       # Loop state as JSON
ll-loop stop fix-types                # Stop a running loop
ll-loop resume fix-types              # Resume interrupted loop
ll-loop history fix-types             # Show execution history
ll-loop history fix-types --tail 20   # Last 20 events
ll-loop history fix-types --verbose   # Include LLM call details
ll-loop history fix-types --full      # Untruncated output
ll-loop history fix-types --json      # JSON output
ll-loop history fix-types <run_id>    # Inspect a specific archived run
ll-loop install fix-types             # Install built-in loop
ll-loop show fix-types                # Show loop details
ll-loop show fix-types --json         # FSM config as JSON
ll-loop show fix-types --json --resolved  # FSM config with sub-loop states expanded
ll-loop fragments lib/common.yaml         # List built-in common fragments with descriptions
ll-loop fragments lib/cli.yaml            # List built-in CLI tool fragments with descriptions
ll-loop fragments lib/benchmark.yaml      # List built-in benchmark runner fragment
ll-loop fragments lib/prompt-fragments.yaml  # List built-in prompt fragment library
ll-loop fragments lib/harness.yaml        # List built-in Playwright screenshot fragment
ll-loop next-loop                     # Suggest next loop from history
ll-loop next-loop --count 3 --json    # Top 3 suggestions as JSON
ll-loop audit-meta fix-types          # Summarize meta-eval agreement stats
ll-loop audit-meta fix-types --json   # JSON output

See LOOPS_GUIDE for loop configuration details.


Issue Management

ll-issues

Issue management and visualization utilities.

Subcommands:

ll-issues next-id / ll-issues ni

Print the next globally unique issue number across all types.

Flag Description
--count N / -n N Print N consecutive IDs starting at max+1, one per line (default: 1). Must be a positive integer; 0 or negative values exit with code 2.
--config Path to project root

ll-issues list / ll-issues l

List issues with optional filters.

Flag Description
--type Filter by type: BUG, FEAT, ENH, EPIC
--priority Filter by priority: P0P5, or comma-separated e.g. P1,P2
--label Filter by label from labels: frontmatter; repeatable for OR match
--milestone Filter by milestone name from milestone: frontmatter (exact match)
--group-by Group output by type (default, existing four-bucket view) or epic (group child issues under their parent ID, with an "Unparented" bucket for issues without a parent: field; each EPIC bucket header includes a (N/M done · K blocked) progress badge). The (N/M done) denominator is the EPIC's full transitive descendant set, including nested EPICs — a nested EPIC child is rendered as its own row in a Sub-EPICs (k) sub-section beneath the parent heading, each carrying its own (j/m done) rollup, so the badge and the visible list always agree (BUG-2480).
--status Filter by status: open (default), in_progress, blocked, deferred, done, cancelled, all. Note: synonyms in on-disk frontmatter are normalized on read, but --status arguments must use canonical values (argparse validates choices before normalization runs).
--parent EPIC-NNN Filter to the full transitive descendant set of the given EPIC or issue ID (e.g. --parent EPIC-101) — grandchildren nested under an intermediate (often done) FEAT/ENH are included, resolved via the same cycle-safe walker as epic-progress. Still --status-gated (default open), so completed descendants are not re-surfaced.
--flat Output flat list for scripting
--json / -j Output as JSON array; each entry includes id, title, priority, type, status, path, labels, milestone, and parent (the parent EPIC or issue ID when set)
--include-summary When combined with --json, adds a "summary" key to each JSON object containing the plain text of the issue's ## Summary section (empty string if absent). No-op without --json.
--sort / -s Sort by field: priority (default), id, type, title, created, completed, confidence, outcome, refinement
--asc Sort ascending
--desc Sort descending
--limit / -n Cap output at N issues (must be ≥ 1)
--config Path to project root

ll-issues count / ll-issues c

Count issues. Outputs a single integer by default, or a JSON object with breakdowns.

Flag Description
--type Filter by type: BUG, FEAT, ENH, EPIC
--priority Filter by priority: P0P5, or comma-separated e.g. P1,P2
--status Filter by status: open (default), in_progress, blocked, deferred, done, cancelled, all. Note: synonyms in on-disk frontmatter are normalized on read, but --status arguments must use canonical values (argparse validates choices before normalization runs).
--json / -j Output JSON with total, status, by_type, and by_priority breakdowns
--config Path to project root

ll-issues show <issue_id> / ll-issues s <issue_id>

Show summary card for a single issue. Accepts short form (518), type-prefixed (FEAT-518), or full (P3-FEAT-518). Searches all type directories regardless of status.

The card includes: ID, title, priority, status, effort, risk, confidence scores, dimension scores (Cmplx, Tcov, Ambig, Chsrf — when present), source (discovered_by), integration file count, labels, captured_at / completed_at timestamps (when present), session history, and path.

Rendering is scanning-first (ENH-2574): the title is bold, borders/field labels/the Path line are dimmed, status is colored per state, and the inter-field separator is · rather than the border glyph. Several rows are pruned or collapsed for signal:

  • Source is omitted when discovered_by is absent or when its value is manual (the default case).
  • Norm/Fmt collapse into a single Needs: formatting row, shown only when the file is actually missing required sections — nothing renders when formatting is already correct.
  • captured_at / completed_at render date-only (the T00:00:00Z time component is dropped); Captured at is omitted entirely when it's the same calendar date as Discovered.
  • Once the capture/discovery/relationships/history/closure block has 4 or more rows, labels right-pad into a column so every row's value starts at the same position.

The card also surfaces, when present in frontmatter (ENH-2535):

  • Closure contextclosing_note / cancelled_reason / deferred_reason plus closed_by, closed_at, deferred_date (only when status is done, cancelled, or deferred).
  • Relationshipsparent (with epic title when resolvable), relates_to, depends_on, blocked_by, blocks, supersedes, decomposed_into, affects, focus_area.
  • Discoverydiscovered_date (distinct from captured_at), discovered_commit (short-SHA, first 7 chars), discovered_branch, discovered_source, discovered_external_repo.
  • Decision coupling — when decision_needed: true is paired with decision_ref (e.g., ARCHITECTURE-049), the card renders Decision needed → ARCHITECTURE-049; explicit Decision needed: no for decision_needed: false.
Flag Description
--json / -j Output issue fields as JSON (includes source, norm, fmt keys)

ll-issues path <issue_id> / ll-issues p <issue_id>

Print the relative file path for an issue ID. Accepts short form (1009), type-prefixed (FEAT-1009), or full (P3-FEAT-1009). Searches all type directories regardless of status. Exits 0 on match, 1 if not found.

Flag Description
--json / -j Output as JSON object {"path": "..."}

ll-issues search [query] / ll-issues sr [query]

Search issues with filters and sorting.

Argument/Flag Description
query (Optional) Text to match against title and body (case-insensitive)
--type Filter by type: BUG, FEAT, ENH, EPIC (repeatable)
--priority Filter by priority: P0P5 or range e.g. P0-P2 (repeatable)
--status Filter by status: open (default), in_progress, blocked, deferred, done, cancelled, all
--include-completed Include issues of all statuses (alias for --status all)
--label Filter by label tag (repeatable)
--since Only issues on or after DATE (YYYY-MM-DD)
--until Only issues on or before DATE (YYYY-MM-DD)
--date-field Date field to filter on: discovered (default) prefers captured_at frontmatter (sub-day resolution) and falls back to discovered_date; updated uses the last ## Session Log entry timestamp, falling back to file mtime
--sort Sort field: priority (default), id, date, type, title, created, completed, confidence, outcome, refinement
--asc / --desc Sort direction
--format Output format: table (default), list, ids
--limit Cap results at N
--json / -j Output as JSON array; each entry includes id, title, priority, type, status, path, labels, milestone, and parent

ll-issues sequence / ll-issues seq

Suggest a dependency-ordered implementation sequence.

Flag Description
--type Filter by issue type: BUG, FEAT, ENH, EPIC
--limit Maximum issues to show (default: 10)
--json / -j Output sequence as JSON array
--config Path to project root

ll-issues impact-effort / ll-issues ie

Display an impact vs. effort matrix for active issues.

Flag Description
--type Filter by type: BUG, FEAT, ENH, EPIC
--json / -j Output as JSON object with quadrant keys

ll-issues refine-status / ll-issues rs

Show refinement depth table sorted by commands touched. Columns: ID, Pri, size, Title, source, norm, fmt, per-command session indicators (✓/—), Ready (confidence score), conf (outcome confidence), cmplx (complexity score 0–25), tcov (test coverage score 0–25), ambig (ambiguity score 0–25), chsrf (change surface score 0–25), total.

Argument/Flag Description
ISSUE-ID (Optional) Filter to a single issue by ID (e.g. FEAT-873, BUG-525). Ignores --type when set. Exits 1 if the issue is not found.
--type Filter by type: BUG, FEAT, ENH, EPIC (ignored when ISSUE-ID is provided)
--format Output format: table (default), json (NDJSON)
--json / -j Output as JSON array; with ISSUE-ID outputs a single JSON object instead
--no-key Suppress the key/legend section at the bottom of output
--config Path to project root

The Norm column checks filenames against ^P[0-5]-(BUG|FEAT|ENH|EPIC)-[0-9]{3,}-[a-z0-9-]+\.md$. JSON output includes a "normalized": true/false boolean field per record.

Narrow terminal support: When the table exceeds the available terminal width, columns are automatically elided in priority order. The default drop sequence is sourcenormfmtsizechsrfambigtcovcmplxconfidencereadytotal; any remaining command columns are then dropped rightmost-first. id, priority, and title are always pinned. The title column maintains a minimum width of 20 characters. The drop order is configurable via refine_status.elide_order in ll-config.json — see CONFIGURATION.md.

ll-issues next-action / ll-issues na

Print the next refinement action needed across all active issues. Designed for FSM loop integration — exits 1 when work remains, exits 0 when all issues are ready.

Output format: <ACTION> <issue-id> (one line), or ALL_DONE.

Action token Meaning
NEEDS_FORMAT Issue file does not match template v2.0 structure
NEEDS_VERIFY /ll:verify-issues has not been run on this issue
NEEDS_SCORE Confidence/outcome score is missing
NEEDS_REFINE Score is below threshold and refine-cap not reached
Flag Default Description
--refine-cap N 5 Max /ll:refine-issue runs before moving on
--ready-threshold N 85 Minimum readiness score to consider issue ready
--outcome-threshold N 70 Minimum outcome confidence score to consider issue ready
--skip / -s ISSUE_ID[,...] Comma-separated issue IDs to exclude (e.g. ENH-929,BUG-001); absent --skip preserves existing behavior
--config (auto) Override the config file path

Config-driven defaults: next-action reads commands.confidence_gate.readiness_threshold from .ll/ll-config.json before falling back to the CLI default of 85. Set commands.confidence_gate.readiness_threshold: 90 in your project config to raise the bar globally without passing --ready-threshold on every call. The --ready-threshold flag still overrides the config value when provided explicitly.

ll-issues next-issue / ll-issues nx

Print the issue ranked highest by outcome confidence and readiness score. Designed for FSM loop integration — use this to pick the best issue to work on next based on implementation readiness rather than raw priority.

Sort order: Config-driven via issues.next_issue.strategy (default: confidence_firstoutcome_confidence desc, confidence_score desc, priority asc). Issues without scores are ranked below all scored issues.

Dependency filter: By default (ENH-2436), issues whose Blocked By references a non-terminal (done/cancelled) issue are filtered out before ranking, so the returned ID is always actionable. Pass --include-blocked to revert to the legacy behavior (return any active issue, blocked or not).

EPIC exclusion: EPIC-type ids are never returned (BUG-2638). EPICs are umbrella containers meant to be decomposed via scope resolution (SprintManager.load_or_resolve), not implemented as leaves; the exclusion applies to all output modes (--json, --path, --include-blocked).

Exit codes: 0 = issue found, 1 = no active issues OR every active issue is currently blocked. The all-blocked case emits Error: No ready issues (N blocked, 0 ready) on stderr.

Flag Description
--json / -j Output a JSON object: {id, path, outcome_confidence, confidence_score, priority}. With --include-blocked, the row also carries blocked (bool), blocked_by (sorted list of issue IDs), and pending_prerequisites (sorted list of still-open soft depends_on targets). blocked reflects hard blocked_by edges only, so the three states are distinguishable: hard-blocked (blocked: true), soft-deferred (blocked: false with a non-empty pending_prerequisites), and ready (blocked: false, pending_prerequisites: []).
--path Output only the file path (useful for shell scripting: $(ll-issues next-issue --path))
--skip / -s ISSUE_ID[,...] Comma-separated issue IDs to exclude (e.g. FEAT-007,BUG-001); absent --skip preserves existing behavior
--include-blocked Include issues with unresolved blockers in the ranked output. With --json, each row carries blocked, blocked_by, and pending_prerequisites fields.
--config Path to project root

ll-issues next-issues [N] / ll-issues nxs [N]

Print all active issues in ranked order by outcome confidence and readiness score. Designed for FSM loop integration — use this to get a ranked list of all issues, not just the top one.

Sort order: Config-driven via issues.next_issue.strategy (default: confidence_firstoutcome_confidence desc, confidence_score desc, priority asc). Issues without scores are ranked below all scored issues.

Dependency filter: By default (ENH-2436), issues whose Blocked By references a non-terminal (done/cancelled) issue are filtered out before ranking. Pass --include-blocked to revert to the legacy behavior.

EPIC exclusion: EPIC-type ids are never returned (BUG-2638), in any output mode. EPICs are decomposed via scope resolution, not ranked as implementable leaves.

Exit codes: 0 = at least one unblocked issue found, 1 = no active issues OR every active issue is currently blocked. The all-blocked case emits Error: No ready issues (N blocked, 0 ready) on stderr.

Flag/Arg Description
N Optional count — limit output to top N issues
--json / -j Output a JSON array of objects: {id, path, outcome_confidence, confidence_score, priority}. With --include-blocked, each row also carries blocked (bool), blocked_by (sorted list), and pending_prerequisites (sorted list of still-open soft depends_on targets). As with next-issue, blocked reflects hard blocked_by edges only, so hard-blocked, soft-deferred, and ready rows are distinguishable.
--path Output file paths instead of issue IDs
--include-blocked Include issues with unresolved blockers in the ranked list. With --json, each row carries blocked, blocked_by, and pending_prerequisites fields.
--config Path to project root

ll-issues skip <issue_id> / ll-issues sk

Deprioritize an active issue by bumping its priority prefix and appending a ## Skip Log entry. Use this to move refinement failures or blocked issues out of the active queue without completing or deleting them.

Argument / Flag Short Description
<issue_id> Issue to deprioritize. Accepts numeric ID (955), type+ID (FEAT-955), or full prefix (P3-FEAT-955)
--priority -p Target priority P0–P5 (default: P5)
--reason TEXT Reason text appended to the ## Skip Log entry in the issue file

Behavior: - Renames the issue file with the new priority prefix (e.g., P3-FEAT-955P5-FEAT-955) using git mv for tracked files to preserve history, falling back to an atomic rename for untracked files - Appends a ## Skip Log section with ISO timestamp and the provided reason (or "No reason provided" if omitted) - If the issue is already at the target priority, the file is not renamed but the Skip Log entry is still appended - Works on issues in any type directory (bugs/, features/, enhancements/, epics/) - Prints the new file path to stdout on success

Examples:

ll-issues skip FEAT-955                                          # Deprioritize to P5 (default)
ll-issues skip 955 --priority P4                                 # Deprioritize to P4
ll-issues skip BUG-042 --reason "retry after CI fix"             # With reason
ll-issues sk ENH-123 -p P3 --reason "blocked on upstream change"


ll-issues finalize-decomposition <parent> [children...] / ll-issues fd

Close a decomposed parent issue and re-link its children to the parent's EPIC. Sets the parent's status to done, optionally moves it to its type directory, and updates the EPIC's child references.

Argument/Flag Description
parent Decomposed parent issue ID (e.g., ENH-123)
children (Optional) Child issue IDs as positional arguments
--children-file PATH File with one child ID per line (e.g., the children_<id>.txt artifact from rn-decompose)
--issues-dir DIR Issues base directory (default: .issues)
--no-move Do not move the closed parent into the completed directory; update status only
--config Path to project root

Examples:

ll-issues finalize-decomposition ENH-123 ENH-124 ENH-125     # Close ENH-123; re-link children
ll-issues fd ENH-123 --children-file run_dir/children_ENH-123.txt  # Load children from file
ll-issues fd ENH-123 --no-move                                # Status-only close; no file move


ll-issues append-log <issue_path> <log_command> / ll-issues al

Append a session log entry to an issue file.

Argument Description
issue_path Path to the issue markdown file
log_command Command name to record (e.g., /ll:refine-issue)

ll-issues sections <type> / ll-issues sec <type>

Print the JSON content of the per-type section template for <type>. Resolves via 4-tier precedence — explicit issues.templates_dir config → <project_root>/.ll/templates/ (project-deployed copy) → bundled wheel templates — so project-local overrides (deployed via ll-init --deploy-templates) are returned automatically. Skills and commands should call this instead of reading scripts/little_loops/templates/{type}-sections.json directly.

Argument/Flag Description
type Issue type: bug, feat, enh, or epic
--path Output only the resolved file path (useful for shell scripting)

Examples:

ll-issues sections bug                # Print bug-sections.json content
ll-issues sections feat --path        # Print resolved path (for shell scripting)
ll-issues sec enh                     # Alias: sec
ll-issues sections epic --path        # Resolved path to epic-sections.json

Examples:

ll-issues next-id
ll-issues list --type FEAT --priority P2
ll-issues list --priority P1,P2              # Filter by multiple priorities
ll-issues list --json                         # JSON array of all active issues
ll-issues list --type BUG --json             # JSON filtered by type
ll-issues count                              # Total active issue count
ll-issues count --json                       # JSON with breakdowns
ll-issues count --type BUG                   # Count bugs only
ll-issues count --status done                # Count done issues
ll-issues count --status all                 # Total across all statuses
ll-issues show FEAT-518
ll-issues show 518
ll-issues show FEAT-518 --json        # Issue fields as JSON
ll-issues path 1009                   # Resolve numeric ID to file path
ll-issues path FEAT-1009              # Resolve TYPE-NNN to file path
ll-issues path P3-FEAT-1009           # Resolve full ID to file path
ll-issues path FEAT-1009 --json       # Output as {"path": "..."}
ll-issues search "caching"                   # Search by keyword
ll-issues search --type BUG --priority P0-P2  # Filter bugs by priority range
ll-issues search --since 2026-01-01 --json   # Issues since date as JSON
ll-issues sequence --limit 10
ll-issues sequence --json             # Ordered sequence as JSON
ll-issues impact-effort
ll-issues impact-effort --type BUG    # Only bugs
ll-issues impact-effort --json        # JSON object with quadrant arrays
ll-issues impact-effort --json --type BUG  # Filtered JSON output
ll-issues refine-status
ll-issues refine-status FEAT-873              # Single-issue view
ll-issues refine-status FEAT-873 --json       # Single issue as JSON object
ll-issues refine-status --type BUG --format json
ll-issues next-action                            # Next refinement action needed (exits 1 if work remains)
ll-issues next-action --refine-cap 3             # Lower the refine-cap
ll-issues next-action --ready-threshold 90       # Stricter readiness threshold
ll-issues next-action --skip ENH-929,BUG-001     # Exclude specific issues from consideration
ll-issues next-issue                             # Highest-confidence issue ID
ll-issues next-issue --json                      # As JSON: {id, path, outcome_confidence, confidence_score, priority}
ll-issues next-issue --path                      # File path only (for shell scripting)
ll-issues next-issue --skip FEAT-007,BUG-001     # Exclude specific issues from consideration
ll-issues next-issues                            # All active issues in ranked order
ll-issues next-issues 5                          # Top 5 ranked issues
ll-issues nxs --json                             # Ranked list as JSON array
ll-issues nxs --path                             # Ranked list as file paths
ll-issues skip FEAT-955                          # Deprioritize to P5
ll-issues skip BUG-042 --priority P4 --reason "retry after CI fix"
ll-issues append-log .issues/bugs/P2-BUG-123-foo.md /ll:refine-issue
ll-issues anchor-sweep --dry-run                 # Preview file:line rewrites
ll-issues anchor-sweep                           # Rewrite file:line refs in active issues
ll-issues asw --dry-run                          # Alias: asw
ll-issues set-status ENH-1725 in_progress        # Transition status
ll-issues sst BUG-042 done                       # Alias: sst
ll-issues epic-progress EPIC-1773                # EPIC progress summary (text)
ll-issues ep EPIC-1773 --format json             # EPIC progress as JSON
ll-issues ep EPIC-1773 --format markdown         # EPIC progress as markdown


ll-issues clusters / ll-issues cl

Visualize issue dependency clusters. Walks all relationship types across active issues by default and renders each connected component. The default tree layout draws an indented, multi-root dependency tree (├──/└── connectors) in which every edge is shown — hub/parent hierarchies (e.g. one EPIC with many parent: children) render with the hub at the root and depth shown naturally, and DAG cross-edges or cycle back-edges appear as cross-references rather than being demoted to a trailing skip-edge list.

Flags:

Flag Default Description
--layout {tree,list,boxes} tree Diagram layout. tree (default): indented multi-root dependency tree with every edge shown inline. list: one line per issue with edge annotations (compact). boxes: legacy vertical box-stack with arrows between consecutive boxes and a trailing skip-edge list. An explicit --layout overrides --compact.
--compact / --summary off Alias for --layout list.
--edges SET all Relationship types to follow. Aliases: all (all types), blocking (blocked_by+blocks only — legacy behaviour), hard (blocked_by+blocks+depends_on). Or a comma-separated list of: blocked_by,blocks,depends_on,relates_to,parent.
--status SET active Issue statuses to include. Aliases: active (open/in_progress/blocked), +deferred (active + deferred), all (everything except cancelled). Or a comma-separated list of canonical status values.
--cluster N Render only the Nth cluster (1-indexed).
--limit N Render at most N clusters; the footer reports how many were suppressed.
--include-orphans off Include 1-issue clusters (isolated issues with no relationships).
--min-connections N 0 Only show clusters where at least one issue has N or more connections.
--json / -j off Output as JSON array. Each element has cluster_index, issue_count, issues, and edges (with relationship values: blocked_by, blocks, depends_on, relates_to, parent). Output is identical across all --layout values.

Examples:

ll-issues clusters                          # Indented dependency tree (default), active issues
ll-issues clusters --layout list            # Compact one-line-per-issue view
ll-issues clusters --layout boxes           # Legacy vertical box-stack
ll-issues clusters --edges=blocking         # Legacy view: blocked_by/blocks only
ll-issues clusters --status=+deferred       # Include deferred issues
ll-issues clusters --status=all             # All statuses except cancelled
ll-issues clusters --json | jq '[.[] | {n: .issue_count, types: [.edges[].relationship] | unique}]'
ll-issues cl --include-orphans              # Show isolated issues too

ll-issues anchor-sweep / ll-issues asw

Scan all active issue files (bugs/, features/, enhancements/, epics/) for bare file:line references outside code fences and rewrite them to enclosing function/class/section anchors. Uses a language-agnostic regex backwards-scan (no AST) covering Python, TypeScript, JavaScript, Go, Rust, Ruby, Java, C#, and Markdown.

Flag Description
--dry-run Print what would change without modifying files
--issues-dir DIR Issues base directory (default: .issues)

Behavior: - Scans backwards from the cited line number to find the nearest enclosing def/func/fn/function/class/struct/# heading. - Replaces file.py:42 with `file.py` (near function `foo`). - References inside code fences are skipped. - References with no resolvable anchor are left unchanged with a warning. - Always run --dry-run before the first production sweep.

Examples:

ll-issues anchor-sweep --dry-run
ll-issues anchor-sweep
ll-issues anchor-sweep --issues-dir custom/issues
ll-issues asw --dry-run


ll-issues fingerprint / ll-issues fp

Extract a structured fingerprint from an issue file for cross-theme conflict detection. Returns JSON with the issue id, files_to_modify (file paths from the Integration Map), and key_terms (significant words after stop-word filtering). Used by /ll:audit-issue-conflicts --cross-theme Phase 2b to identify cross-batch overlap pairs without an LLM call.

Argument Description
issue_path Path to the issue file (absolute or relative to project root)

Output (JSON):

{"id": "ENH-1801", "files_to_modify": ["scripts/config.py"], "key_terms": ["authentication", "conflict"]}

Examples:

ll-issues fingerprint .issues/enhancements/P3-ENH-1801-example.md
ll-issues fp .issues/bugs/P2-BUG-042-example.md


ll-issues check-flag / ll-issues cf

Exit 0 if a named boolean frontmatter field in the issue equals true. Designed for use as a shell gate in FSM loop states.

Argument Description
issue_id Issue ID (e.g., 518, FEAT-518, P3-FEAT-518)
field Frontmatter field name (e.g., decision_needed)

Examples:

ll-issues check-flag 518 decision_needed   # Exit 0 if decision_needed: true
ll-issues cf FEAT-518 implementation_ready # Exit 0 if implementation_ready: true

FSM loop use: Use as a shell action with evaluate: {type: exit_code} to branch on a single frontmatter boolean without an LLM call.


ll-issues check-decidable

Exit 0 if an issue has >=1 enumerable option to decide between (ENH-2443). Deterministic (no-LLM) companion to /ll:decide-issue --validate-only — re-implements the same option-extraction patterns in pure Python (count_enumerable_options), so FSM shell states can pre-check decidability without dispatching an LLM call. Mirrors the ll-issues format-check / ensure_formatted precedent (ENH-2426).

Argument Description
issue_id Issue ID (e.g., 518, FEAT-518, P3-FEAT-518)

Examples:

ll-issues check-decidable FEAT-398   # Exit 1 (OPTIONS_MISSING) — no enumerable options
ll-issues check-decidable ENH-277    # Exit 0 — 2+ options found

FSM loop use: The check_decision_decidable gate in rn-remediate.yaml (and its parity insertion in autodev.yaml) calls this as a shell action with evaluate: {type: exit_code}, routing to a bounded /ll:refine-issue --auto deposit-options retry on exit 1 rather than letting decide run with nothing to score.


ll-issues check-open-questions

Coverage-aware decidability probe (ENH-2446). Companion to check-decidable — exits 0 only when both (a) every option block in ## Proposed Solution carries a > **Selected:** or ### Decision Rationale marker AND (b) no bullet items in ## Edge Cases / ## Confidence Check Notes / ## Open Questions carry an open-question signal (Q:, ?, open question, needs decision, decision needed, open decision, unresolved decision, decision point) without a ✅ RESOLVED / ✔ RESOLVED / **RESOLVED** / > **RESOLVED** marker. Exits 1 with OPEN_QUESTIONS_REMAIN: <ID> — N open question(s) and M unresolved option(s); run /ll:refine-issue <ID> --auto otherwise.

Closes the mixed-issue gap that the count-based check-decidable misses: an issue with already-resolved options PLUS unresolved free-form questions previously routed straight to decide (an idempotent no-op) and bypassed deposit_options entirely.

Argument Description
issue_id Issue ID (e.g., 2446, ENH-2446, P2-ENH-2446)

Examples:

ll-issues check-open-questions FEAT-2339  # Exit 1 (mixed: 0 unresolved options + N open questions)
ll-issues check-open-questions ENH-2446   # Exit 0 — no unresolved decision surface

FSM loop use: The check_decision_decidable gate in rn-remediate.yaml:263 (and its parity insertion in autodev.yaml:211) chains this probe BEFORE check-decidable (check-open-questions || check-decidable) so the coverage gap is caught before the count-based fallback runs. Pair with the open_question_stall evaluator (open_question_stall_gate fragment in lib/common.yaml) for progress-gated re-fire.


ll-issues format-check

Deterministic (no-LLM) structural linter for issue formatting (ENH-2426). Grades an issue against its type template and reports gaps in four classes: missing (a required section header absent entirely), renamed (a present section header is deprecated with an extractable canonical replacement, e.g. Proposed FixProposed Solution), empty (a required header present with a whitespace-only body), and boilerplate (a required section's body still equals its creation_template). Fails open — an unresolved template or unreadable issue file reports no gaps (exit 0) rather than blocking.

Argument/Flag Default Description
issue_id (required) Issue ID (e.g., 2426, ENH-2426, P3-ENH-2426)
--format {text,json} text Output format

Examples:

ll-issues format-check ENH-2426               # text report, exit 0/1
ll-issues format-check ENH-2426 --format json # {"missing": [...], "renamed": [...], "empty": [...], "boilerplate": [...]}

FSM loop use: The ensure_formatted gate in rn-remediate.yaml calls this as a shell action with evaluate: {type: exit_code}, routing to /ll:format-issue only when a gap is found — replacing the older missing-headers-only inline check.


ll-issues check-readiness / ll-issues cr

Exit 0 if an issue's confidence_score and outcome_confidence frontmatter fields both meet the configured thresholds. Reads thresholds from commands.confidence_gate in ll-config.json, falling back to --readiness / --outcome CLI args.

Argument/Flag Default Description
issue_id (required) Issue ID (e.g., 518, FEAT-518, P3-FEAT-518)
--readiness N 90 Fallback readiness threshold when not set in ll-config.json
--outcome N 75 Fallback outcome confidence threshold when not set in ll-config.json

Examples:

ll-issues check-readiness 518             # Use thresholds from ll-config.json
ll-issues cr FEAT-518 --readiness 85      # Override readiness threshold
ll-issues check-readiness 518 --readiness 80 --outcome 70

FSM loop use: Use as a shell gate in refine-to-ready-issue-style loops to branch without an LLM call. Pair with ll-issues show --json when you need the raw scores.

ll-issues set-scores / ll-issues ss

Write confidence_score, outcome_confidence, and the four per-dimension scores into an issue's YAML frontmatter. Writes idempotently: existing fields are overwritten, unrelated keys are preserved, and missing frontmatter is created from scratch. If no score flags are provided, returns 0 with a warning and writes nothing.

Argument/Flag Default Description
issue_id (required) Issue ID (e.g., 518, FEAT-518, P3-FEAT-518)
--confidence N None Overall readiness score (0–100)
--outcome N None Outcome confidence score (0–100)
--score-complexity N None Complexity dimension score (0–25)
--score-test-coverage N None Test-coverage dimension score (0–25)
--score-ambiguity N None Ambiguity dimension score (0–25)
--score-change-surface N None Change-surface dimension score (0–25)

Examples:

ll-issues set-scores BUG-1307 --confidence 95 --outcome 80
ll-issues ss FEAT-518 --confidence 88 --outcome 72 --score-complexity 22 --score-test-coverage 20 --score-ambiguity 25 --score-change-surface 15

Used by: /ll:confidence-check Phase 4 to persist scores deterministically instead of a free-form Edit call.


ll-issues set-status / ll-issues sst

Transition an issue to a new status value. Validates the target status against the canonical enum, updates the status: frontmatter field in-place, and prints the before→after transition to stdout.

Argument Description
issue_id Issue ID (e.g., 518, ENH-518, P3-ENH-518)
status New status value: open, in_progress, blocked, deferred, done, cancelled
--cascade Propagate status to issues with parent: <EPIC-ID> (EPIC closure only; only valid with done/cancelled). Only follows parent: edges — relates_to:, blocked_by:, and other relationship types are not traversed.
--cascade-to <status> Status to apply to cascaded children (default: deferred)

Examples:

ll-issues set-status ENH-1725 in_progress   # ENH-1725: open → in_progress
ll-issues sst BUG-042 done                  # BUG-042: in_progress → done
ll-issues set-status FEAT-100 blocked
ll-issues set-status EPIC-042 cancelled --cascade              # Close EPIC + defer children
ll-issues set-status EPIC-042 done --cascade --cascade-to done # Close EPIC + all children


ll-issues epic-progress <epic_id> / ll-issues ep <epic_id>

Show a progress summary for an EPIC and all its child issues. Aggregates child statuses into a completion bar, counts by status, surfaces the oldest open child, and lists any blocked children with their blocked_by links.

Argument/Flag Short Default Description
epic_id (required) EPIC ID to summarize (e.g., EPIC-1773)
--format -f text Output format: text, json, or markdown
--config Path to project root

Sample text output:

EPIC-1773: Audit & simplify built-in FSM loops
  Progress:     ████████░░░░░░░░  8/12 resolved (67%)
  Status:       2 in_progress  •  1 blocked  •  1 open  •  8 done
  Oldest open:  ENH-1641 (24 days)
  Blocked:      ENH-1820 → blocked_by BUG-1701

The "resolved" count on the Progress line is done + cancelled (terminal states). When cancelled issues are present, a breakdown is appended: e.g., 8/12 resolved (67%) (7 done, 1 cancelled). The Status line always shows individual status buckets including the raw done and cancelled counts separately.

Rollup semantics (BUG-2441): The child set is collected by walking the parent: chain transitively, so grandchildren (and any deeper descendants) of the EPIC roll up into the Progress and Status counts — not just direct children. This matches the bucketing behavior of ll-issues list --bucket epic. Note: ll-sprint's EPIC resolution still counts direct children only; for a sprint-aware breakdown use ll-sprint show.

Nested EPIC visibility (BUG-2480): a nested EPIC (a type: EPIC child of another EPIC) counts toward its parent's (N/M done) denominator and is rendered as its own row in a Sub-EPICs (k) sub-section under ll-issues list --group-by epic, carrying its own (j/m done) rollup from a separate compute_epic_progress call. The nested EPIC's own descendants (grandchildren of the outer EPIC) are bucketed under the nested EPIC, not the outer one — so they appear once, either under the outer EPIC's leaf rows or under the nested EPIC's own heading, never duplicated.

Examples:

ll-issues epic-progress EPIC-1773              # Text summary (default)
ll-issues ep EPIC-1773 --format json           # JSON object with counts and child list
ll-issues ep EPIC-1773 --format markdown       # Markdown-formatted summary


ll-issues decisions

Manage rules, decisions, and exceptions log.

Sub-sub-commands:

Sub-command Description
list List decisions log entries (with optional filters)
add Add a new rule, decision, or exception entry
outcome <ID> Record the outcome of a decision entry
generate Generate entries from completed issues
sync Sync active rules to .ll/ll.local.md
suggest-rules Analyze decision entries and surface candidates ready for promotion to rules
promote <ID> Convert a decision entry into an enforced rule (rewrites entry in-place; auto-syncs when --enforcement required)
extract-from-completed Extract rules from completed issues via LLM; appends RuleEntry records to the decisions log as .ll/decisions.d/*.json fragments with deduplication

list flags:

Flag Description
--type Filter by entry type: rule, decision, exception
--category Filter by category string
--label Filter by label
--no-outcome Show only DecisionEntry records with no outcome
--before <ISO-8601> Show entries with timestamp before this date
--scope <scope> Filter DecisionEntry records by scope
--active-only Exclude entries superseded by a newer entry
--enforcement Filter rule/coupling entries by enforcement level: required or advisory
--format / -f Output format: text (default), json

add flags:

Flag Applies to Description
--type all Entry type: rule, decision, exception (required)
--category all Category string (required)
--rule rule, decision Rule or decision text (required for these types)
--rationale all Why this entry applies (required)
--issue all Related issue ID
--label all Comma-separated labels
--enforcement rule required or advisory (default: advisory)
--rule-ref exception Rule being excepted (required for exception)
--alternatives-rejected decision, exception Alternatives considered
--supersedes rule ID of the rule this supersedes
--scope decision issue (default) or project
--id all Explicit entry ID (auto-generated if omitted)

outcome flags:

Flag Description
<ID> Entry ID to record outcome for (positional, required)
--result Outcome: worked, did_not_work, mixed, reversed (required)
--notes Free-text notes about the outcome
--measured-at <ISO-8601> When the outcome was measured (default: now)
--force Overwrite an existing outcome

generate flags:

Flag Description
--from Source to generate from: completed (default). Scans issue type directories (.issues/bugs/, .issues/features/, .issues/enhancements/, .issues/epics/) for files with status: done frontmatter, skips entries already present in the decisions log (both .ll/decisions.yaml and .ll/decisions.d/*.json fragments), and appends new decision entries (as fragments) for each issue not yet logged.

sync flags:

No additional flags. Reads active required rules from the decisions log (both .ll/decisions.yaml and .ll/decisions.d/*.json fragments) and writes them to the ## Active Rules section in .ll/ll.local.md. Creates .ll/ll.local.md if absent. Silently skips when the decisions log is absent (neither tier present).

suggest-rules flags:

No additional flags. Analyzes decision entries and clusters them by category and shared token overlap to surface candidates with recurring patterns. Requires at least 3 decision entries to produce output (exits 1 with a message if fewer).

promote flags:

Flag Description
<ID> ID of the decision entry to promote (positional, required)
--enforcement Enforcement level for the new rule: required (default) or advisory. When required, auto-runs sync to push the rule into .ll/ll.local.md immediately.

extract-from-completed flags:

Flag Description
--since YYYY-MM-DD Only process issues completed on or after this date
--issue ID Only extract from this specific issue (e.g. ENH-2151)
--dry-run Print candidates without writing to decisions.yaml
--min-confidence FLOAT Minimum LLM confidence to accept a candidate (default: 0.7)
ll-issues decisions list
ll-issues decisions list --type rule --active-only
ll-issues decisions list --type rule --enforcement required --active-only  # only enforced rules
ll-issues decisions list --no-outcome
ll-issues decisions add --type=decision --category=architecture --rule="Use atomic_write" --rationale="Prevents partial state"
ll-issues decisions outcome dec-001 --result=worked --notes="No incidents in 30 days"
ll-issues decisions generate                     # Generate from completed issues (default)
ll-issues decisions generate --from completed    # Explicit source
ll-issues decisions sync                         # Sync active rules → .ll/ll.local.md
ll-issues decisions suggest-rules                # Surface decision candidates for promotion
ll-issues decisions promote dec-007              # Promote dec-007 → required rule (auto-sync)
ll-issues decisions promote dec-007 --enforcement advisory  # Promote as advisory rule
ll-issues decisions extract-from-completed       # Extract rules from all completed issues via LLM
ll-issues decisions extract-from-completed --since 2026-01-01  # Only issues completed since date
ll-issues decisions extract-from-completed --issue ENH-2151    # Only one issue
ll-issues decisions extract-from-completed --dry-run           # Preview candidates without writing

ll-deps

Cross-issue dependency discovery and validation.

Global flags:

Flag Short Description
--issues-dir -d Path to issues directory (default: .issues)
--intent QUERY Intent query for output filtering (no-op until FTS5 ranking lands)
--intent-limit N Max lines for intent-filtered output (default: 50)

Subcommands:

ll-deps analyze

Full dependency analysis combining file overlaps and validation.

Flag Short Description
--format -f Output format: text (default), json
--graph Include ASCII dependency graph
--sprint Restrict analysis to issues in named sprint

ll-deps validate

Validate existing dependency references only (broken refs, cycles, stale completed refs).

Flag Short Description
--json -j Output as JSON (serializes ValidationResult fields)
--sprint Restrict validation to named sprint

ll-deps fix

Auto-fix broken refs, stale refs, and missing backlinks.

Flag Short Description
--dry-run -n Preview fixes without modifying files
--sprint Restrict fixes to named sprint

ll-deps apply

Write proposed dependency relationships to issue files. Re-runs analysis internally and writes accepted proposals (above a confidence threshold) to ## Blocked By sections. Backlinks are intentionally not written — run ll-deps fix afterward to add missing ## Blocks entries.

Flag Short Description
--min-confidence Minimum confidence to apply (default: 0.7)
--dry-run -n Preview without writing
--sprint Restrict to issues in named sprint
<source> <relation> <target> Explicit pair: FEAT-001 blocks FEAT-002 or FEAT-001 blocked-by FEAT-002

ll-deps tree

Render an EPIC's child issue hierarchy as a Unicode box-drawing tree with dependency edges.

Flag Short Description
--epic EPIC issue ID to render (required, e.g. EPIC-1773)
--format -f Output format: text (default), json

JSON output (--format json) emits {"root": "EPIC-NNN", "nodes": [...], "edges": [...]}. Exits 0 on success; exits non-zero if the EPIC is not found.

Examples:

ll-deps analyze                       # Full analysis with markdown output
ll-deps analyze --format json         # JSON output
ll-deps analyze --graph               # Include ASCII dependency graph
ll-deps analyze --sprint my-sprint    # Analyze only sprint issues
ll-deps validate                      # Validation only
ll-deps validate --json               # JSON output
ll-deps validate --sprint my-sprint   # Validate sprint issue deps
ll-deps fix                           # Auto-fix broken refs and backlinks
ll-deps fix --dry-run                 # Preview fixes
ll-deps apply                         # Apply proposals >= 0.7 confidence
ll-deps apply --min-confidence 0.5    # Lower threshold
ll-deps apply --dry-run               # Preview only (no writes)
ll-deps apply --sprint my-sprint      # Sprint-scoped apply
ll-deps apply FEAT-001 blocks FEAT-002       # Manual explicit pair
ll-deps apply FEAT-001 blocked-by FEAT-002   # Manual explicit pair (inverse)
ll-deps tree --epic EPIC-1773        # Text tree with ├──/└── connectors
ll-deps tree --epic EPIC-1773 -f json  # Structured JSON (root, nodes, edges)


ll-code

Structural code queries (callers, callees, imports, impact) via a pluggable CodeQueryProvider protocol (FEAT-2576). Ships with a grep/AST fallback provider that requires no external index — every subcommand works out of the box. Graph-backed providers (e.g. ENH-2613's codegraph, a read-only reader over a .codegraph/codegraph.db index) register in the same resolver and take priority under --provider auto.

Global flags:

Flag Short Description
--provider Provider name (fallback, codegraph), or auto (default) to pick the first available
--json -j Output as JSON: {provider, freshness, query, results: [CodeRef...]}

Subcommands:

Subcommand Description
status Provider name, availability, freshness, capabilities
callers-of <symbol> Who calls this symbol (heuristic on fallback, exact on codegraph)
callees-of <symbol> What this symbol calls (exact)
importers-of <module> Who imports this module/file (heuristic on fallback, exact on codegraph)
defines <path> Symbols defined in a file (exact)
references <symbol> All reference sites — defs + uses (heuristic on fallback, exact on codegraph)
impact-of <paths...> [--depth N] Reverse transitive closure of files impacted by changes to paths (default depth: 2; fallback only — codegraph has no edge kind mapping to this verb)

Every result carries a confidence (exact or heuristic) and provider field. Exit codes: 0 = hits, 1 = no hits, 2 = provider error.

status freshness fields (codegraph provider): the detail string reports indexed_at (index build time, ISO 8601), head_moved (commits landed since the index was built), dirty_files (uncommitted/untracked file count), and the active policy (code_query.staleness, ENH-2612). Enforcement: strict makes a stale index report available: false (the resolver falls through to fallback automatically); warn (default) serves stale results with freshness: stale; off always reports freshness: fresh.

Examples:

ll-code status                                     # provider name, availability, freshness
ll-code callers-of little_loops.issue_manager.IssueManager.load
ll-code --json callers-of <symbol>                 # machine-readable output for skills/loops
ll-code --provider fallback callers-of <symbol>    # force a specific provider
ll-code --provider codegraph status                # inspect the codegraph index's freshness
ll-code impact-of little_loops/state.py --depth 3


History & Analysis

ll-history

Display summary statistics and analysis for completed issues.

When present in issue frontmatter, captured_at and completed_at are preferred over the legacy discovered_date field and Resolution body regex / git-log fallbacks; the JSON serialization of CompletedIssue includes both fields at sub-day ISO 8601 resolution.

Global flags:

Flag Short Description
--intent QUERY Intent query for output filtering (no-op until FTS5 ranking lands)
--intent-limit N Max lines for intent-filtered output (default: 50)

Subcommands:

ll-history summary

Show issue statistics for completed issues.

Flag Short Description
--json -j Output as JSON
--directory -d Path to issues directory (default: .issues)

When the unified session DB (.ll/history.db, FEAT-1112) contains issue_events rows, summary reads from the DB instead of re-parsing every completed-issue file. An empty/absent DB falls back to file parsing — no behavior change for projects without recorded events (ENH-1621). As of ENH-1691, ll-auto writes issue lifecycle events live during each run via AutoManager's internal SQLiteTransport; ll-session backfill is retained for importing historical data captured before ENH-1691. Only the summary subcommand is DB-backed; analyze and export still scan the files because they need bodies and git history.

ll-history analyze

Full analysis with trends, subsystems, and debt metrics.

Flag Short Description
--format -f Output format: text (default), json, markdown, yaml
--directory -d Path to issues directory
--period -p Trend grouping: weekly, monthly (default), quarterly
--compare -c Compare last N days to previous N days
--since Only analyze issues completed on or after DATE (YYYY-MM-DD)
--until Only analyze issues completed on or before DATE (YYYY-MM-DD)

ll-history export <topic>

Export topic-filtered excerpts from completed issue history.

Argument/Flag Short Description
topic Topic, area, or system to export
--output Write to file instead of stdout
--format -f Format: narrative (default), structured
--directory -d Path to issues directory
--since Only include issues completed after DATE (YYYY-MM-DD)
--min-relevance Minimum relevance score (default: 0.5)
--type Filter by type: BUG, FEAT, ENH, EPIC
--scoring Relevance method: intersection (default), bm25, hybrid

ll-history sessions <ISSUE_ID>

List sessions that co-occurred with the given issue's active period. Queries the issue_sessions VIEW (v5 schema, ENH-1711) which joins issue_events to message_events via overlapping timestamps. Issues processed after ENH-1839 populate captured_at immediately on live events; a prior ll-session backfill pass (or ENH-1830 auto-backfill) is only needed for older issues.

Flag Description
--limit N Maximum results (default: 20)
--json / -j Output as JSON array

Examples:

ll-history sessions ENH-1710              # Sessions that touched ENH-1710
ll-history sessions ENH-1710 --json       # JSON output

Examples (all subcommands):

ll-history summary                         # Summary statistics
ll-history summary --json                  # JSON output
ll-history analyze                         # Full analysis report
ll-history analyze --format markdown       # Markdown report
ll-history analyze --compare 30            # Compare last 30 days to previous
ll-history export "session log"            # Export excerpts for topic
ll-history export "sprint CLI" --output docs/arch/sprint.md
ll-history sessions ENH-1710              # Sessions that touched ENH-1710
ll-history sessions ENH-1710 --json       # JSON output

ll-history root

Show the project-root summary node — the top-level condensed node that covers all sessions in the project (ENH-1955). Requires ll-session backfill with compaction and cross-session condensation enabled.

Flag Description
--expand Expand and display all messages under the root node
--limit N Maximum messages to show with --expand (default: 20)
--json / -j Output root node metadata + message count as JSON

Examples:

ll-history root                  # Show root node metadata
ll-history root --expand         # Show metadata + first 20 messages
ll-history root --expand --limit 5  # Show metadata + first 5 messages
ll-history root --json           # JSON output


ll-workflows

Identify multi-step workflow patterns from user message history. Steps 2 and 3 of the /ll:analyze-workflows pipeline.

Subcommands:

ll-workflows analyze

Analyze workflows from messages and Step 1 patterns.

Flag Short Description
--input -i Input JSONL file with user messages (default: .ll/workflow-analysis/step1-patterns.jsonl)
--patterns -p Required. Input YAML from Step 1 (workflow-pattern-analyzer)
--output -o Output YAML file (default: .ll/workflow-analysis/step2-workflows.yaml)
--verbose -v Show verbose analysis output

The Python API (analyze_workflows()) accepts an optional db_path argument that prefers the unified session store's message_events table over the JSONL input when populated (ENH-1621); an empty/missing DB transparently falls back to the JSONL file. The --patterns YAML is a generated analysis artifact and stays a file input.

Examples:

# Use conventional path (no --input needed if ll-messages wrote to the default location)
ll-messages --output .ll/workflow-analysis/step1-patterns.jsonl
ll-workflows analyze --patterns .ll/workflow-analysis/step1-patterns.yaml

# Explicit input
ll-workflows analyze -i messages.jsonl -p patterns.yaml -o output.yaml
ll-workflows analyze --input .ll/user-messages.jsonl \
                     --patterns .ll/workflow-analysis/step1-patterns.yaml

ll-workflows propose

Run Step 3 automation proposals from workflow analysis output. Invokes the workflow-automation-proposer skill and writes the proposals to a file. Use this as a CLI-native fallback when the interactive skill invocation is unavailable (e.g., disable-model-invocation breakage).

Flag Short Description
--patterns -p Required. Step 1 patterns YAML (from ll-messages or workflow-pattern-analyzer)
--workflows -w Required. Step 2 workflows YAML (from ll-workflows analyze)
--output -o Output path (default: .ll/workflow-analysis/step3-proposals.yaml or .json)
--format -f Output format: yaml (default) or json

Examples:

# Full pipeline — Steps 1, 2, 3 non-interactively
ll-messages --output .ll/workflow-analysis/step1-patterns.jsonl
ll-workflows analyze --patterns .ll/workflow-analysis/step1-patterns.yaml
ll-workflows propose \
  --patterns .ll/workflow-analysis/step1-patterns.yaml \
  --workflows .ll/workflow-analysis/step2-workflows.yaml

# JSON output at a custom path
ll-workflows propose \
  --patterns step1.yaml \
  --workflows step2.yaml \
  --output out.json \
  --format json


Synchronization

ll-sync

Sync local .issues/ files with GitHub Issues.

Global flags:

Flag Description
--config Path to project root
--quiet Suppress non-essential output
--dry-run Show what would happen without making changes

Subcommands:

ll-sync status

Show sync status between local issues and GitHub.

Flag Short Description
--json -j Output as JSON (serializes SyncStatus.to_dict())

ll-sync push [issue_ids...]

Push local issues to GitHub. If no IDs given, pushes all. When an issue has a milestone: frontmatter field, ll-sync push passes it to gh issue create/edit --milestone <name> to assign the issue to the matching GitHub milestone (by title).

ll-sync pull

Pull GitHub Issues to local.

Flag Short Description
--labels -l Filter by labels (comma-separated)

ll-sync diff [issue_id]

Show differences between local and GitHub issues. Omit issue_id for a summary of all synced issues.

Flag Short Description
--json -j Output as JSON (serializes SyncResult.to_dict())

ll-sync close [issue_ids...]

Close GitHub issues for completed local issues.

Flag Description
--all-completed Close all GitHub issues whose local counterparts have status: done or status: cancelled

ll-sync reopen [issue_ids...]

Reopen GitHub issues for locally-active issues. After a successful reopen, the issue's status frontmatter is updated to open; the file stays in its type directory (bugs/, features/, etc.).

Flag Description
--all-reopened Reopen all GitHub issues whose local counterparts are not closed on GitHub

Examples:

ll-sync status                    # Show sync status
ll-sync status --json             # Sync status as JSON
ll-sync push                      # Push all local issues to GitHub
ll-sync push BUG-123              # Push specific issue
ll-sync pull                      # Pull GitHub Issues to local
ll-sync diff BUG-123              # Show diff for specific issue
ll-sync diff                      # Diff summary for all synced issues
ll-sync diff --json               # Diff summary as JSON
ll-sync close ENH-123             # Close GitHub issue for ENH-123
ll-sync close --all-completed     # Close all completed issues on GitHub
ll-sync reopen BUG-042            # Reopen GitHub issue for BUG-042
ll-sync reopen --all-reopened     # Reopen all issues moved back to active locally

Requires "sync": { "enabled": true } in .ll/ll-config.json.


Utilities

ll-messages

Extract user messages from Claude Code session logs.

Flags:

Flag Short Description
--limit -n Maximum messages to extract (default: 100)
--since Include only messages after this date (YYYY-MM-DD or ISO)
--output -o Output file path (default: .ll/user-messages-{timestamp}.jsonl)
--cwd Working directory to use (default: current directory)
--exclude-agents Exclude agent session files (agent-*.jsonl)
--stdout Print to stdout instead of writing to file
--verbose -v Print verbose progress information
--include-response-context Include metadata from assistant responses
--skip-cli Exclude CLI commands from output
--commands-only Extract only CLI commands, no user messages
--tools Comma-separated tools to extract commands from (default: Bash)
--skill Filter to sessions where this skill was invoked (e.g. capture-issue)
--examples-format Output (input, output) training pairs instead of raw messages (requires --skill); mutually exclusive with --sft-format
--sft-format Output conversation turns in SFT training format as JSON-lines (chatml, alpaca, sharegpt); mutually exclusive with --examples-format
--context-window Number of context turn-pairs per window in --examples-format or --sft-format (default: 3)

Examples:

ll-messages                               # Last 100 messages to file
ll-messages -n 50                         # Last 50 messages
ll-messages --since 2026-01-01            # Messages since date
ll-messages -o output.jsonl               # Custom output path
ll-messages --stdout                      # Print to terminal
ll-messages --include-response-context    # Include response metadata
ll-messages --skip-cli                    # Exclude CLI commands
ll-messages --commands-only               # Extract only CLI commands
ll-messages --skill capture-issue         # Filter to sessions where /ll:capture-issue was invoked
ll-messages --skill capture-issue --examples-format --since 2026-01-01 -o examples.jsonl
ll-messages --skill refine-issue --examples-format --context-window 5 --stdout
ll-messages --sft-format chatml --stdout
ll-messages --sft-format sharegpt --context-window 3 --since 2026-05-01 --stdout
ll-messages --sft-format alpaca --output data/sft/raw.jsonl


ll-logs

Discover and extract ll-relevant JSONL entries from Claude Code session logs. Also generates logs/index.md after extraction. The sequences subcommand mines tool-chain n-grams for workflow analysis. The stats subcommand aggregates per-skill invocation frequency and correction rate from .ll/history.db. The dead-skills subcommand cross-references the skill catalog against the log corpus to flag never-invoked and rarely-invoked skills. The scan-failures subcommand mines failed ll-* Bash calls to propose bug issue files.

Subcommands:

Subcommand Description
discover List all Claude projects with ll activity (one path per line, sorted)
tail Stream live events from an active loop session
extract Extract ll-relevant JSONL records to logs/<slug>/<session-id>.jsonl
sequences Extract tool-chain n-grams of ll invocations from JSONL logs
stats Aggregate skill invocation frequency and correction rate from history.db
dead-skills Cross-reference skill catalog against log corpus to flag never/rarely-invoked skills
scan-failures Mine failed ll-* Bash calls from session logs; cluster by error signature; optionally create bug issues
diff Compare two sessions' ll-invocation behavior: skills added/removed, per-skill count deltas, and unified sequence diff
eval-export Export EvalFixture v1 records reconstructed from session logs for use with ll-harness
loop-fleet Aggregate cross-project loop-run outcomes for built-in loop improvement

discover flags:

Flag Short Description
--json -j Output as JSON: {"paths": [...]}

tail flags:

Flag Description
--loop NAME Loop name to tail (required)
--project DIR Project root to tail loops from (default: CWD)

extract flags:

Flag Description
--all Extract all projects with ll activity
--project DIR Working directory of the target project
--cmd TOOL Filter to records containing this ll- tool name (e.g. ll-history)

sequences flags:

Flag Short Description
--all Analyze all projects with ll activity
--project DIR Working directory of the target project
--min-len N Minimum n-gram length (default: 2)
--min-count M Minimum occurrence count to include (default: 1)
--top N Limit output to top N chains by frequency
--window-days D Only consider records within D days of latest record
--json -j Output as JSON: [{"chain": [...], "count": N, "edges": [{"from": "...", "to": "...", "freq": f, "pmi": 1.23, "lift": 3.4}], "pmi": 1.23, "lift": 3.4}]; pmi/lift are optional additive fields; lift < 1.0 signals a frequency-prior-equivalent pair

--all and --project are mutually exclusive for extract, sequences, stats, dead-skills, scan-failures, and eval-export.

stats flags:

Flag Short Description
--all Aggregate across all projects with ll activity
--project DIR Working directory of the target project
--window-days D Only consider records within D days of latest record
--sort {freq,corrections} Sort by invocation frequency or correction count (default: freq)
--json -j Output as JSON: [{"skill": str, "invocations": int, "corrections": int, "correction_rate": float}]

dead-skills flags:

Flag Short Description
--all Aggregate across all projects; catalog loaded from current directory
--project DIR Working directory of the target project (also used as catalog root)
--window-days D Only consider records within D days of latest record
--threshold N Skills with invocations ≤ N are "rarely" invoked (default: 3)
--json -j Output as JSON: [{"skill": str, "invocations": int, "tier": "never"\|"rarely"}]

scan-failures flags:

Flag Short Description
--all Scan all projects with ll activity
--project DIR Working directory of the target project
--window-days D Only consider records within D days of latest record
--capture Create BUG issue files for each failure cluster. When combined with --all, scopes capture to Path.cwd() by default — foreign-project clusters are reported but not filed. Use --capture-foreign to also create issues for clusters from other projects
--capture-foreign When --capture --all is active, also create BUG issues for failure clusters from projects outside the current working directory
--json -j Output as JSON: [{"tool": str, "count": int, "normalized_sig": str, "sample_error": str, "session_ids": [...]}]

diff flags:

Flag Short Description
SESSION_A First session ID or JSONL file path (positional)
SESSION_B Second session ID or JSONL file path (positional)
--json -j Output diff as JSON object

eval-export flags:

Flag Short Description
--project DIR Project working directory (default: cwd)
--skill NAME Filter exported fixtures to this skill name
--issue ID Filter to fixtures where this issue ID appears in session context
--limit N Cap output record count (0 = unlimited)
--out PATH Write output to file (default: stdout)
--json -j JSON output instead of default YAML

Examples:

ll-logs discover                          # List all projects with ll activity
ll-logs discover --json                   # Output paths as JSON array
ll-logs tail --loop my-loop              # Stream live events from an active loop session
ll-logs extract --all                    # Extract all projects to logs/
ll-logs extract --project /path/to/proj  # Extract one project to logs/<slug>/
ll-logs extract --all --cmd ll-history   # Filter to ll-history invocations
ll-logs sequences --all                  # Find all tool-chain bigrams (default min-len=2)
ll-logs sequences --project /path -j     # Output n-grams as JSON for one project
ll-logs sequences --all --top 10         # Top 10 most frequent chains
ll-logs sequences --all --min-len 3 --min-count 3  # Trigrams appearing ≥3 times
ll-logs stats --all                      # Skill frequency/correction table across all projects
ll-logs stats --project /path --json     # JSON stats for one project
ll-logs stats --all --sort corrections   # Sort by correction count (highest first)
ll-logs stats --all --window-days 30     # Limit to last 30 days of data
ll-logs dead-skills --project /path/to/proj --json  # List never/rarely-invoked skills as JSON
ll-logs dead-skills --project . --threshold 5       # Skills with <=5 invocations
ll-logs dead-skills --all --window-days 90          # Dead skills across all projects, last 90 days
ll-logs scan-failures --all                         # Report all failed ll-* calls across all projects
ll-logs scan-failures --project /path --json        # JSON failure clusters for one project
ll-logs scan-failures --all --window-days 30        # Only failures from last 30 days
ll-logs scan-failures --all --capture               # Create BUG issues for each failure cluster
ll-logs diff SESSION_A SESSION_B                    # Compare behavioral diff between two sessions
ll-logs diff SESSION_A SESSION_B --json             # Diff output as JSON
ll-logs eval-export --project .                     # Export all fixtures from current project (YAML)
ll-logs eval-export --skill refine-issue --json     # JSON fixtures for refine-issue only
ll-logs eval-export --issue ENH-1710 --limit 10     # Up to 10 fixtures touching ENH-1710

Companion loop — ll-logs-telemetry-digest (FEAT-1925)

A project-local FSM loop that orchestrates the full ll-logs analysis pipeline in a single unattended run: 1. Refreshes the log corpus (ll-logs extract) 2. Runs stats, sequences, scan-failures, and dead-skills subcommands 3. Triages findings into issue files 4. Writes a digest artifact to .loops/runs/ll-logs-telemetry-digest-<timestamp>/digest.md

Subcommands not yet available are skipped gracefully via capability detection — the loop gains depth as new subcommands land.

ll-loop run ll-logs-telemetry-digest    # Full telemetry digest pass

ll-session

Query the unified session store (SQLite + FTS5) — the per-project .ll/history.db populated by SQLiteTransport, AutoManager (live-writes issue lifecycle events during ll-auto runs), and ll-session backfill (for historical data captured before ENH-1691). Lets operators search and inspect session activity without re-parsing the scattered JSON/markdown sources the analyze-* skills read.

Global flags:

Flag Description
--db PATH Path to the session database (default: .ll/history.db)

Subcommands:

Subcommand Description
search FTS5 full-text query with BM25-ranked results
recent Most recent rows for an event kind; optionally filtered by issue
skill-stats Per-skill invocation/completion/success-rate rollup from skill_events completion columns; --since DATE bounds the window (ENH-2460)
backfill Seed the database from existing on-disk sources; --since DATE uses incremental JSONL-only mode (ENH-1830); --snapshots seeds the issue_snapshots table from .issues/ files (ENH-2151); --extract-decisions runs extract-from-completed after backfill (ENH-2152); --max-sessions N caps how many sessions are compacted in this run (newest first, useful for large DBs) (ENH-2252)
export Dump selected history tables as JSONL to stdout or a file — for visualization, external tooling, or backup (ENH-2252)
related Issue events for a given issue ID
path Resolve the JSONL file path for a session ID
grep Regex search over message_events with optional summary-node scope filtering; condensed nodes use recursive CTE for N-level DAG traversal (ENH-1955)
expand Return all message_events covered by a given summary node ID; condensed nodes use recursive CTE for N-level DAG traversal (ENH-1955)
describe Show metadata (level, block span, parent) for a summary node
rebuild Re-derive the JSONL-cache tables (tool_events, message_events, …) from raw_events; idempotent (ENH-2581)
compact Sweep raw_events past the retention max-age into per-session retention summary nodes; --and-prune also deletes and VACUUMs (ENH-2581)
recompress Rewrite legacy uncompressed raw_events payloads (raw_line/parsed_json) as zlib BLOBs and VACUUM; idempotent, off-hot-path maintenance (ENH-2624)
prune Delete raw event rows older than configured analytics.retention max-age and VACUUM the DB

grep flags:

Flag Description
PATTERN Regex pattern (required, positional; case-insensitive)
--summary-id ID Restrict search to messages covered by this summary node ID
--limit N Maximum results (default: 50)
--json Output results as a JSON array

expand flags:

Flag Description
SUMMARY_ID Summary node ID to expand (required, positional)
--json Output message events as a JSON array

describe flags:

Flag Description
NODE_ID Summary node ID to describe (required, positional)
--json Output metadata as a JSON object

search flags:

Flag Description
--fts QUERY FTS5 match query (required)
--kind {tool,file,issue,loop,correction,message,skill,cli,commit,test_run} Filter results by event kind (optional)
--limit N Maximum results (default: 20)
--json / -j Output results as a JSON array

recent flags:

Flag Description
--kind {tool,file,issue,loop,correction,message,skill,cli,snapshot,commit,test_run} Event kind to list (required unless --issue is given). skill rows include exit_code/success/duration_ms when a completion-side host recorded them (ENH-2460). snapshot and the full choice list are now sourced from VALID_KINDS, the single source of truth (ENH-2581)
--issue ID Filter to sessions that co-occurred with this issue (e.g. ENH-1710). Without --kind, lists sessions directly from the issue_sessions view. Issues processed after ENH-1839 populate captured_at immediately; a prior backfill pass is only needed for older issues.
--limit N Maximum rows (default: 20)
--json Output as a JSON array

backfill flags:

backfill ingests session JSONL lines into raw_events only (ENH-2581) — issue/loop-state/commit data is still written directly. The JSONL-derived cache tables (tool_events, message_events, assistant_messages, skill_events, sessions) are populated by a separate rebuild (or backfill --rebuild in the same call).

Flag Description
--since DATE Incremental mode: only process JSONL files modified on or after DATE (ENH-1830)
--host HOST Filter to a single host source: claude-code, codex, opencode, or pi
--rebuild Also materialize the JSONL-derived cache tables from raw_events in this call (ENH-2581)
--snapshots Also seed the issue_snapshots table from .issues/ files (ENH-2151)
--extract-decisions Run extract-from-completed on issue history after backfill (ENH-2152)
--max-sessions N Cap the number of sessions compacted in this run (newest first); useful for large DBs that would otherwise time out (ENH-2252)

rebuild flags:

Flag Description
--config PATH Path to ll-config.json (default: auto-resolve from cwd)
--json Output row counts as JSON

Wipes and re-derives tool_events, message_events, assistant_messages, skill_events, sessions, user_corrections, summary_nodes/ summary_spans, and their search_index rows from raw_events. Idempotent. Issue/loop/commit/cli/file/test_run tables are outside raw_events's scope and are untouched (ENH-2581).

compact flags:

Flag Description
--and-prune Also delete the newly-compacted raw_events rows and VACUUM afterward
--config PATH Path to ll-config.json (default: auto-resolve from cwd)
--json Output result summary as JSON

Sweeps raw_events rows past analytics.retention.raw_event_max_age_days into per-session kind='retention' summary_nodes rows (a deterministic one-liner, not an LLM summary) and marks them compacted=1 so prune can delete them safely later (ENH-2581).

recompress flags:

Flag Description
--batch N Rows to rewrite per transaction (default: 2000)
--json Output result summary as JSON

New raw_events rows written by backfill already store raw_line/parsed_json as zlib-compressed BLOBs (~2.9× smaller per row). recompress is a one-time maintenance sweep that converts pre-existing uncompressed TEXT rows (written before ENH-2624) to the compressed form and VACUUMs afterward. The read path (rebuild) transparently decompresses either representation, so the command is idempotent and byte-lossless — running it twice is a no-op on the second pass.

export flags:

Flag Description
--tables TYPE [TYPE…] Tables to include (default: all non-message tables). Choices: session, issue_event, issue_snapshot, skill_event, loop_event, correction, summary_node, message_event, commit_event, test_run_event
--since DATE Only rows at or after this ISO 8601 date/datetime
--include-messages Also include message_events (potentially large); ignored when --tables is given explicitly
-o / --output FILE Write output to FILE instead of stdout

Examples:

ll-session search --fts "rate limit"            # Full-text search, BM25-ranked
ll-session recent --kind loop                   # Recent loop events
ll-session recent --kind commit                 # Recent git commits (ENH-2458)
ll-session recent --kind test_run               # Recent pytest runs (ENH-2459)
ll-session skill-stats --since 2026-06-01       # Per-skill success rates (ENH-2460)
ll-session recent --issue ENH-1710              # Sessions that touched ENH-1710
ll-session recent --kind message --issue ENH-1710  # Messages from those sessions
ll-session backfill                             # Ingest on-disk sources (raw_events + issues/loops/commits)
ll-session backfill --rebuild                   # Ingest, then materialize cache tables in one call
ll-session backfill --since 2026-01-01          # Incremental JSONL backfill since date
ll-session backfill --max-sessions 50           # Compact at most 50 sessions this run
ll-session rebuild                              # Re-derive cache tables from raw_events (ENH-2581)
ll-session compact --and-prune                  # Sweep+summarize old raw_events, then delete (ENH-2581)
ll-session path <session_id>                    # Resolve JSONL file path for a session ID
ll-session grep "error"                         # Regex search over messages
ll-session grep "traceback" --summary-id 5      # Search within a summary node's span
ll-session expand 5                             # List messages covered by summary node 5
ll-session describe 5                           # Show metadata for summary node 5
ll-session export                               # Dump all non-message tables to stdout as JSONL
ll-session export --tables issue_event loop_event -o history.jsonl  # Selective export to file
ll-session export --since 2026-01-01 --include-messages             # Full dump since date
ll-session prune --dry-run                      # Preview what would be pruned without deleting
ll-session prune                                # Delete old raw events and VACUUM
ll-session prune --json                         # Prune result as JSON
ll-session recompress                           # Compress legacy raw_events payloads and VACUUM (ENH-2624)
ll-session recompress --batch 5000              # Rewrite 5000 rows per transaction

prune flags:

Flag Description
--dry-run Report rows that would be deleted without actually deleting them
--json Output result summary as JSON

Pruning is dual-gated by analytics.retention config: both min_project_age_days and min_db_size_mb must be exceeded before any rows are deleted (defaults: 365 days, 800 MB). Only raw_events rows already marked compacted=1 (by compact) past raw_event_max_age_days are deleted (ENH-2581) — issue/loop/commit/cli/file/test_run tables and uncompacted raw_events rows are never pruned. See analytics.retention in CONFIGURATION.md.


ll-history-context

Query .ll/history.db for user corrections and FTS5 matches related to an issue ID and print a ready-to-inject ## Historical Context markdown block. Returns empty output (exit 0) when the DB is missing, has no matches, or all rows are stale (>30 days old).

Pass --project instead of an issue ID to print the project-wide context digest that session_start would inject (dry-run / config-tuning mode, ENH-1907).

Flags:

Flag Description
ISSUE_ID Issue ID to query (optional, e.g. ENH-1708). Mutually exclusive with --project.
--project Print the project-wide context digest (dry-run of session-start injection).
--file PATH Also include recent file events for this path (issue-mode only)
--db PATH Path to the session database (default: .ll/history.db)
--effort Output a ## Effort Context block with per-issue session count and cycle time (ENH-1905)
--for-skill NAME Exit 0 with no output if NAME is not in history.planning_skills (ENH-1909)

When learning_tests.enabled is true and the queried issue has learning_tests_required declared, an additional ## Learning Test Evidence block is appended (ENH-2217). The block lists each declared target with its current registry status (proven / stale / refuted / missing), so planning skills that call ll-history-context automatically surface assumption coverage without an extra ll-learning-tests check call.

Output cap: At most 5 rows are rendered in issue mode. Project mode respects history.session_digest.char_cap (default 1200 chars).

Effort Context block: When --effort is passed, a ## Effort Context section is appended after the ## Historical Context block (or emitted alone when no corrections/FTS matches exist). It includes the session count and cycle time (first-to-last session span in days) for the queried issue, plus a velocity table of recently completed issues drawn from recent_issue_velocity(). Returns empty output when the DB is absent or the issue has no recorded sessions.

Examples:

ll-history-context ENH-1708                        # Corrections matching the issue ID
ll-history-context ENH-1708 --file src/foo.py      # Also include recent file events
ll-history-context ENH-9999                        # Returns empty output when no matches
ll-history-context --project                        # Print project-wide digest (dry-run)
ll-history-context --project --db .ll/history.db   # Use a specific DB path


ll-gitignore

Suggest and apply .gitignore patterns based on untracked files.

Flags:

Flag Short Description
--dry-run -n Preview suggestions without modifying .gitignore
--json -j Output as JSON (serializes GitignoreSuggestion fields)
--quiet -q Suppress non-essential output
--config Path to project root (default: current directory)

Examples:

ll-gitignore                  # Show suggestions and apply approved patterns
ll-gitignore --json           # Output suggestions as JSON
ll-gitignore --dry-run        # Preview suggestions without modifying .gitignore
ll-gitignore --quiet          # Suppress non-essential output


ll-migrate

One-time migration script that moves all issues from completed/ and deferred/ directories into their type-based directories, backfills completed_at: for older completed files, and sets correct status: frontmatter. Part of the ENH-1390 status-decoupling migration.

Flags:

Flag Short Description
--dry-run -n Preview all planned moves without modifying files
--config -C Path to project root (default: current directory)

Examples:

ll-migrate --dry-run   # Preview all planned moves (strongly advised before running)
ll-migrate             # Execute migration
ll-migrate --config /path/to/project  # Run for a specific project


ll-migrate-relationships

One-time migration script that renames deprecated relationship frontmatter keys in all .md files under .issues/: parent_issue:parent: and related:relates_to:. Part of the ENH-1434 relationship field standardization.

Flags:

Flag Short Description
--dry-run -n Preview all planned renames without modifying files
--config -C Path to project root (default: current directory)

Examples:

ll-migrate-relationships --dry-run   # Preview all planned renames
ll-migrate-relationships             # Execute migration
ll-migrate-relationships --config /path/to/project  # Run for a specific project


ll-migrate-labels

One-time migration script that reads the freeform ## Labels body section from all .md files under .issues/ and writes the labels as a labels: YAML list in frontmatter. Part of the ENH-1392 labels field addition.

Flags:

Flag Short Description
--dry-run -n Preview all planned migrations without modifying files
--config -C Path to project root (default: current directory)

Examples:

ll-migrate-labels --dry-run   # Preview all planned migrations
ll-migrate-labels             # Execute migration
ll-migrate-labels --config /path/to/project  # Run for a specific project


ll-migrate-status

One-time migration script that reads the status: frontmatter field from all .md files under .issues/ and rewrites any non-canonical synonyms (e.g. completed, wip) to their canonical equivalents. Uses the authoritative STATUS_SYNONYMS map from frontmatter.py. Part of the ENH-1551 cleanup pass.

Flags:

Flag Short Description
--dry-run -n Preview all planned normalizations without modifying files
--config -C Path to project root (default: current directory)

Examples:

ll-migrate-status --dry-run   # Preview all planned normalizations
ll-migrate-status             # Execute migration
ll-migrate-status --config /path/to/project  # Run for a specific project


ll-verify-docs

Verify that documented counts (commands, agents, skills, loops) match actual file counts.

Flags:

Flag Short Description
--json -j Output as JSON
--format -f Output format: text (default), json, markdown
--fix Auto-fix count mismatches in documentation files
--directory -C Base directory (default: current directory)

Exit codes: 0 = all counts match, 1 = mismatches found, 2 = error

Examples:

ll-verify-docs                    # Check and show results
ll-verify-docs --json             # Output as JSON
ll-verify-docs --format markdown  # Markdown report
ll-verify-docs --fix              # Auto-fix mismatches


ll-verify-skill-budget

Check that the total skill description token footprint stays within the Claude Code listing budget.

Scans all skills/*/SKILL.md frontmatter description fields. Skips skills with disable-model-invocation: true. Token estimate: len(description) // 4. Exits 1 if total exceeds the threshold.

Flags:

Flag Short Description
--threshold Token budget threshold (default: 2000; overrides ll-config.json)
--json -j Output as JSON
--directory -C Base directory (default: current directory)

Exit codes: 0 = under budget, 1 = over budget

Examples:

ll-verify-skill-budget                # Check against default 2000-token budget
ll-verify-skill-budget --json          # Output as JSON
ll-verify-skill-budget --threshold 1500  # Custom threshold


ll-verify-skills

Check that no SKILL.md file exceeds the 500-line limit.

Scans all skills/*/SKILL.md files. Skips skills with disable-model-invocation: true. Exits 1 if any file exceeds the limit.

Flags:

Flag Short Description
--limit Maximum lines per SKILL.md (default: 500)
--json -j Output as JSON
--directory -C Base directory (default: current directory)

Exit codes: 0 = all within limit, 1 = violations found

Examples:

ll-verify-skills                    # Check against default 500-line limit
ll-verify-skills --limit 400        # Custom limit
ll-verify-skills --json             # Output as JSON


ll-verify-triggers

Validate skill description trigger accuracy against should-fire and should-NOT-fire phrasings. Reports per-skill precision/recall and a cross-skill collision matrix.

Flags:

Flag Short Description
--json Output as JSON
--directory -C Base directory (default: current directory)
--precision-threshold Minimum precision required (default: 0.5)
--recall-threshold Minimum recall required (default: 0.5)

Exit codes: 0 = all skills meet thresholds, no collisions; 1 = threshold miss or collision detected.

Examples:

ll-verify-triggers                         # Validate all skills against default thresholds
ll-verify-triggers --json                  # Machine-readable JSON output
ll-verify-triggers --precision-threshold 0.8 --recall-threshold 0.6


ll-verify-package-data

Lint the little_loops package source for __file__-escape patterns that break non-editable installs, and verify every declared asset is accessible via importlib.resources in the current installation. Both gates must pass for exit 0.

Two checks run by default:

  1. __file__-escape lint — regex-scans every .py file under little_loops/ for Path(__file__).parents[N] or .parent.parent... chains whose traversal depth exits the package directory. Reports violations with file and line number.
  2. Manifest completeness check — verifies every asset in PACKAGE_DATA_ASSETS (package_data.py) is reachable via importlib.resources. Catches assets missing from the wheel (e.g., omitted from MANIFEST.in or pyproject.toml).

Flags:

Flag Short Description
--json Output as JSON
--directory -C Project root containing the little_loops package (default: cwd)
--lint-only Run only the __file__-escape lint (skip manifest check)
--manifest-only Run only the manifest completeness check (skip lint)

Exit codes: 0 = no escape violations and all assets accessible; 1 = one or more violations or missing assets.

Examples:

ll-verify-package-data                     # Run both checks from cwd
ll-verify-package-data --json              # Machine-readable JSON output
ll-verify-package-data --lint-only         # Lint only (no manifest check)
ll-verify-package-data --manifest-only     # Manifest check only
ll-verify-package-data -C /path/to/root    # Run from a specific project root


ll-verify-design-tokens

Structural lint for half-flipped design-token theme profiles. A profile's themes/dark.json (or any theme) that inverts the foreground/background pair — overriding both surface and text to move onto a near-black surface — but leaves border/action falling through to the light-tuned semantic.json defaults produces harsh gridlines, muddy accents, and a danger == action.primary collision. This lint catches that class at authoring time.

For every profile under the profiles directory, each theme that performs a full inversion (overrides both surface and text) must override every semantic color group semantic.json defines (border, action). A theme that does not invert (e.g. a light.json restating only surface) is exempt.

Flags:

Flag Short Description
--json Output as JSON
--directory -C Project root to discover the profiles directory under (default: cwd)
--profiles-dir Explicit path to a design-token profiles/ directory (overrides -C discovery)

Exit codes: 0 = every inverting theme overrides all semantic color groups; 1 = one or more half-flipped themes (or no profiles directory found).

Note: Run against the bundled little-loops templates, this currently flags editorial-mono, a known-incomplete profile pending a follow-on. Point --profiles-dir at a complete profile set, or complete editorial-mono, to gate CI on exit 0.

Examples:

ll-verify-design-tokens                          # Auto-discover profiles dir from cwd
ll-verify-design-tokens --json                   # Machine-readable JSON output
ll-verify-design-tokens --profiles-dir DIR       # Lint a specific profiles directory
ll-verify-design-tokens -C /path/to/root         # Discover under a specific project root


ll-verify-decisions

Validate the decisions log by loading .ll/decisions.yaml through load_decisions() and re-globbing the derived .ll/decisions.d/*.json fragment directory in a strict second pass (bypassing the read path's silent skip of malformed fragments), asserting no YAML/JSON syntax errors, missing required fields, or unknown entry-type discriminators in either tier. Gates the three transport-layer corruption checks (ENH-2589): the pre-commit hook (ENH-2590), the pytest CI gate (ENH-2591), and the Claude Code PreToolUse hook (ENH-2592) all delegate to this binary and rely on its exit-code contract.

The validator catches three corruption classes:

  1. YAML syntax corruption (e.g. an unescaped "" inside a double-quoted rationale: scalar → yaml.YAMLError).
  2. Schema drift — entries missing required fields (id, result/measured_at for outcomes, etc.) → KeyError.
  3. Unknown type discriminatorValueError("Unknown entry type").

Flags:

Flag Description
--config-root Project root whose decisions log (.ll/decisions.yaml + .ll/decisions.d/) to validate (default: cwd). Equivalent to BRConfig.project_root.

Exit codes: 0 = loadable via load_decisions() and schema-clean; 1 = any caught yaml.YAMLError/KeyError/ValueError, with a single-line ERROR: <path>: <ExcType>: <msg> on stderr.

Examples:

ll-verify-decisions                       # Validate .ll/decisions.yaml + .ll/decisions.d/ from cwd
ll-verify-decisions --config-root /repo   # Validate under a specific project root

See CONTRIBUTING.md § Decisions YAML Validation for the pre-commit wiring and docs/guides/DECISIONS_LOG_GUIDE.md § Validation for the full three-layer defense model.


ll-verify-kinds

Assert every CREATE TABLE in session_store._MIGRATIONS is either registered in _KIND_TABLE (so recent()/search --kind can query it) or explicitly listed in _KINDLESS_TABLES (support tables with no "recent by kind" concept — meta, search_index, sessions, assistant_messages, summary_nodes, summary_spans, raw_events, correction_retirements). Catches the case a new *_events table is added without registering its kind — the gap this issue fixed for snapshot (ENH-2581).

Exit codes: 0 = every table is registered or explicitly kindless; 1 = one or more tables are neither, listed on stderr.

Examples:

ll-verify-kinds    # Check the current tree's session_store._MIGRATIONS


ll-verify-des-audit

Walk the source tree and verify every event-emit site maps to a registered DES variant — the F5 adoption gate (ENH-2475). The audit reads every emit-call string literal in scripts/little_loops/, then checks each against the canonical DES_VARIANTS registry (defined in little_loops.observability.schema). Exit 0 means every currently-emitted event type has a registered variant; exit 1 means a new event was emitted without being registered — block F5's gen_ai.usage.* adoption until the variant is added.

Flags:

Flag Short Description
--json Output as JSON
--directory -C Project root to discover the source directory under (default: cwd)
--source-dir Explicit path to a little_loops source/ directory (overrides -C discovery)

Exit codes: 0 = every emit site maps to a registered DES variant; 1 = one or more uncovered event types (or source dir not found).

Examples:

ll-verify-des-audit                          # Auto-discover source dir from cwd
ll-verify-des-audit --json                   # Machine-readable JSON output
ll-verify-des-audit --source-dir DIR         # Walk a specific source directory
ll-verify-des-audit -C /path/to/root         # Discover under a specific project root


Check markdown documentation for broken links.

Flags:

Flag Short Description
--json -j Output as JSON
--format -f Output format: text (default), json, markdown
--directory -C Base directory (default: current directory)
--ignore Ignore URL patterns — repeatable
--timeout HTTP request timeout in seconds (default: 10)
--workers -w Maximum concurrent HTTP requests (default: 10)
--verbose -v Show verbose output

Exit codes: 0 = all links valid, 1 = broken links found, 2 = error

Examples:

ll-check-links                            # Check all markdown files
ll-check-links --json                     # Output as JSON
ll-check-links --format markdown          # Markdown report
ll-check-links -C docs/                   # Check specific directory
ll-check-links --ignore 'http://localhost.*'  # Ignore pattern
ll-check-links --timeout 30 --workers 5   # Custom timeout and concurrency


ll-create-extension

Scaffold a new little-loops extension project directory. Generates a ready-to-install Python package with an LLExtension implementation, a pyproject.toml registered under the little_loops.extensions entry point, and a starter test using LLTestBus.

Arguments:

Argument Description
name Extension name in kebab-case (e.g. my-dashboard-ext)

The name is automatically converted: hyphens become underscores for the package directory (my_dashboard_ext) and each word is capitalized for the class name (MyDashboardExt).

Flags:

Flag Short Description
--dry-run -n Preview files that would be created without writing them

Generated layout:

<name>/
├── pyproject.toml          # Package metadata + little_loops.extensions entry point
├── <pkg_name>/
│   ├── __init__.py
│   └── extension.py        # LLExtension implementation stub
└── tests/
    └── test_extension.py   # Starter test using LLTestBus

Generated file contents:

pyproject.toml — wires automatic extension discovery via the little_loops.extensions entry point group:

[project.entry-points."little_loops.extensions"]
my-dashboard-ext = "my_dashboard_ext.extension:MyDashboardExt"

<pkg_name>/extension.py — skeleton implementing the LLExtension protocol:

class MyDashboardExt:
    """MyDashboardExt extension.

    Implement on_event to handle little-loops lifecycle events.
    Optional mixin Protocols (InterceptorExtension, ActionProviderExtension,
    EvaluatorProviderExtension, LLHookIntentExtension) are opt-in — implement
    their methods to activate.
    """

    def on_event(self, event: LLEvent) -> None:
        """Handle an incoming event."""
        # See docs/reference/EVENT-SCHEMA.md for all available event types and payload fields
        pass

tests/test_extension.py — starter test using LLTestBus:

class TestMyDashboardExt:
    def test_receives_events(self) -> None:
        """Extension receives events via LLTestBus replay."""
        bus = LLTestBus([])
        ext = MyDashboardExt()
        bus.register(ext)
        bus.replay()
        assert bus.delivered_events == []

Dry-run output:

[DRY RUN] Would create: my-dashboard-ext/
  pyproject.toml
  my_dashboard_ext/__init__.py
  my_dashboard_ext/extension.py
  tests/test_extension.py

Exit codes: 0 = scaffold created successfully, 1 = directory already exists or error

Examples:

ll-create-extension my-dashboard-ext              # Scaffold extension
ll-create-extension my-dashboard-ext --dry-run    # Preview without writing files

After scaffolding:

cd my-dashboard-ext
pip install -e .          # Install with entry point registration
python -m pytest tests/   # Run starter tests

See also: Write a little-loops hook — full authoring walkthrough for the LLHookIntentExtension Protocol, including the adapter flow and pure-function + subprocess testing patterns.


ll-artifact

Generate self-contained, human-facing HTML artifacts from project data. All project-derived inputs are stamped into the page at generation time, so the output works over file:// with no runtime fetch.

Subcommands:

Subcommand Description
policy-builder Emit a visual builder for policy-router / rubric FSM loop YAML

Exit codes: 0 = artifact generated successfully, 1 = error

ll-artifact policy-builder

Emit policy-router-builder.html — a single self-contained page for visually authoring Decision Table and Rubric loop YAML. The page inlines three project-derived blobs: design-token CSS variables (light + dark, from load_design_tokens / render_as_css_vars_themed), the canonical predicate grammar (policy_rules.grammar_spec()), and the skill/command catalog (from skills/*/SKILL.md + commands/*.md). It renders a live YAML preview, shadow / zero-condition / unknown-action validation hints, Copy/Download, and a light/dark theme toggle.

Flags:

Flag Short Description
--output -o Output directory (default: config.artifacts.default_output_dir, normally .)

Examples:

ll-artifact policy-builder                   # Write policy-router-builder.html to the default output dir
ll-artifact policy-builder -o build/         # Write to a custom directory

Note: Generated YAML can be validated with ll-loop validate <name> after downloading. Decision Table output imports lib/rubric-router.yaml then lib/policy-router.yaml; Rubric output imports only lib/rubric-router.yaml.


ll-generate-schemas

Internal: Maintainer/developer tool. End users do not need to run this directly.

Generate JSON Schema (draft-07) files for all 38 LLEvent types and write them to docs/reference/schemas/.

Flags:

Flag Short Description
--output -o Output directory (default: docs/reference/schemas/ relative to cwd)

Exit codes: 0 = schemas generated successfully, 1 = error

Examples:

ll-generate-schemas                          # Write to docs/reference/schemas/
ll-generate-schemas -o path/to/schemas/      # Custom output directory

Note: Run this after modifying SCHEMA_DEFINITIONS in scripts/little_loops/generate_schemas.py or adding a new LLEvent type. See CONTRIBUTING.md for the full schema maintenance workflow.


ll-generate-skill-descriptions

Release utility: Run before tagging a release to batch-refresh skill descriptions.

Auto-generate concise (≤100 character) descriptions for LLM-discoverable skills using the Claude CLI. For each skills/*/SKILL.md that does not have disable-model-invocation: true, it extracts trigger keywords and a body excerpt, prompts Claude to produce a single-line description, and optionally writes it back to the frontmatter.

Dry-run by default (previews proposed descriptions without modifying files).

Flags:

Flag Description
--apply Write generated descriptions back to SKILL.md frontmatter
--quiet Suppress per-skill output; only print final summary

Exit codes: 0 = success (no errors), 1 = one or more skills failed or skills directory not found

Examples:

ll-generate-skill-descriptions               # Dry-run: preview proposed descriptions
ll-generate-skill-descriptions --apply       # Write descriptions back to SKILL.md files
ll-generate-skill-descriptions --quiet       # Suppress per-skill output

See also: CONTRIBUTING.md New Skill Checklist for the classification policy and when to run this tool.


ll-adapt

Unified host-parameterized adapter. Dispatches to a host-specific emitter via --host <host> and generates all skill, command, and agent artefacts for that host in one pass.

Flags:

Flag Description
--host HOST Target host (e.g. codex, omp) — required
--apply Write changes (default: dry-run)
--dry-run Explicit dry-run alias
--only NAME Restrict agent processing to a single agent stem
--quiet Suppress per-entry output; only print final summary

Exit codes: 0 = success (no errors), 1 = unknown host or one or more entries failed

Examples:

ll-adapt --host codex                # Dry-run: preview all Codex artefacts
ll-adapt --host codex --apply        # Write Codex artefacts
ll-adapt --host codex --only codebase-analyzer --apply  # Single agent


ll-adapt-skills-for-codex

Deprecated alias — use ll-adapt --host codex instead. This entry point remains available as a convenience alias but is no longer the documented path.

Adapt ll's skills/*/SKILL.md files for the Codex Skills API and bridge every commands/*.md slash command into a Codex-discoverable skills/ll-<name>/ entry.

Skills adaptation (in-place). For each skills/<name>/SKILL.md, inserts name: (the directory slug) and metadata.short-description: (first line of the existing description: field, ≤80 chars) into the SKILL.md frontmatter, and creates agents/openai.yaml with display_name and short_description under an interface: block. Uses targeted string manipulation — no YAML roundtrip — to preserve existing frontmatter formatting.

Commands bridge (synthesized). For each commands/<name>.md, synthesizes a wrapper skills/ll-<name>/SKILL.md (with name: ll-<name>, the source command's description: copied verbatim, and a derived metadata.short-description:) plus a matching agents/openai.yaml. The ll- namespace prefix prevents collisions with skills sharing a base name (e.g. commit). Commands whose frontmatter declares disable-model-invocation: true are skipped, mirroring the skills-adapter contract. Multi-line descriptions are emitted as YAML block scalars so the synthesized frontmatter parses cleanly. Bridged ll-<name>/ entries are committed in-repo and discovered by Codex via the same Skills API path as adapted real skills.

Dry-run by default (previews proposed changes without modifying files).

Flags:

Flag Description
--apply Write skill frontmatter updates and create bridged skills/ll-<name>/ directories on disk
--quiet Suppress per-entry output; only print final summary

Exit codes: 0 = success (no errors), 1 = one or more entries failed or skills/ directory not found

Examples:

ll-adapt-skills-for-codex            # Dry-run: preview proposed skill + command changes
ll-adapt-skills-for-codex --apply    # Write frontmatter, bridge commands → skills/ll-<name>/
ll-adapt-skills-for-codex --quiet    # Suppress per-entry output


ll-adapt-agents-for-codex

Deprecated alias — use ll-adapt --host codex instead. This entry point remains available as a convenience alias but is no longer the documented path.

Generate .codex/agents/*.toml files from agents/*.md so Codex CLI can select ll subagents via --agent <name>.

For each agents/<name>.md, reads the agent's name and description from its frontmatter (falling back to the H1 heading), then writes a TOML file to .codex/agents/<name>.toml with name, description, model, and developer_instructions fields. Uses an idempotent marker comment (# generated by ll-adapt) to detect and skip previously generated files unless --force is passed. User-edited TOML files (files lacking the marker) are never overwritten.

Dry-run by default (previews proposed changes without writing files).

Flags:

Flag Description
--apply Write .codex/agents/*.toml files to disk
--force Overwrite previously generated files even if already up-to-date
--quiet Suppress per-entry output; only print final summary

Exit codes: 0 = success (no errors), 1 = one or more entries failed or agents/ directory not found

Examples:

ll-adapt-agents-for-codex            # Dry-run: preview proposed agent TOML changes
ll-adapt-agents-for-codex --apply    # Write .codex/agents/*.toml files
ll-adapt-agents-for-codex --force --apply  # Regenerate all files (including up-to-date)


mcp-call

Thin CLI wrapper for direct MCP tool invocation via JSON-RPC. Reads .mcp.json from the current directory, spawns the MCP server subprocess, performs the JSON-RPC initialize handshake, calls tools/call, and writes the MCP response envelope to stdout.

Arguments:

Argument Description
server/tool-name MCP server name and tool name joined by / (e.g., pencil/batch_get)
params_json Tool parameters as a JSON object string
--timeout SECONDS Request timeout in seconds (default: 30). Exit code 124 on timeout.

Exit codes: 0 = success, 1 = tool error, 2 = usage/config error, 124 = timeout, 127 = server or tool not found in .mcp.json

Examples:

mcp-call pencil/batch_get '{"patterns": ["**/*.pen"]}'
mcp-call my-server/my-tool '{"key": "value"}'
mcp-call pencil/batch_design '{"nodes": [...]}' --timeout 120


ll-learning-tests

Query and manage the learning test registry. Skills and loops call this via Bash to check coverage before proceeding.

Subcommands:

Subcommand Description
check <target> [--stale-aware] Print record JSON; exit 1 if not found or (with --stale-aware) if the record is stale
list Print all records as a JSON array
mark-stale <target> Set status=stale; exit 1 if not found
orphans [--mark-stale] List records whose target package is not imported by any project file; optionally mark them all stale
prove <target> Trigger proving via ready-to-implement-gate (retry-then-/ll:explore-api); print the refreshed record; exit 0 if proven, 1 otherwise (ENH-2430)

Examples:

ll-learning-tests check "Anthropic SDK streaming"
ll-learning-tests check "Anthropic SDK streaming" --stale-aware   # exit 1 if stale
ll-learning-tests list
ll-learning-tests list | jq -r '.[] | "\(.status)\t\(.target)"'
ll-learning-tests mark-stale "Anthropic SDK streaming"
ll-learning-tests orphans                # list orphaned records
ll-learning-tests orphans --mark-stale   # atomically mark all orphans stale
ll-learning-tests prove "Anthropic SDK streaming"   # trigger proving directly, no issue file required
ll-learning-tests --help

Exit codes: 0 = success, 1 = target not found (or stale with --stale-aware)


See Also