voku/agent-loop

Umbrella package and unified CLI for the governed agentic-coding loop: board (kanban), learning, and recall-compiler.

Maintainers

Package info

github.com/voku/agent-loop

pkg:composer/voku/agent-loop

Statistics

Installs: 12

Dependents: 0

Suggesters: 0

Stars: 3

Open Issues: 0

0.1.1 2026-06-23 08:45 UTC

README

agent-loop is one boring CLI for coding-agent DX. It does not think, decide, or remember anything itself — it routes a stable set of commands to the packages that do.

The problem it solves

A realistic coding-agent workflow needs a board to pick work from, a session to hold working memory while a task is in progress, a compiler that selects relevant context instead of dumping everything into the prompt, and a learning loop that turns findings into durable rules without overreacting to noise. Each of those is its own focused package. Wiring them by hand means a different vendor/bin/... invocation per concern, which nobody — human or agent — keeps straight across a long session.

agent-loop exists only to remove that friction: one binary, one stable command vocabulary, zero shared state of its own.

Package map

                ┌───────────────────────────────── voku/agent-loop ──────────────────────────────────┐
  agent-loop →  │  board         →  voku/agent-kanban           (local Markdown board, Jira optional)│
                │  verify        →  voku/agent-loop             (cross-package consistency)          │
                │  board:verify  →  voku/agent-kanban           (TodoBoardVerifier, board only)      │
                │  session       →  voku/agent-session          (working memory per task)            │
                │  recall        →  voku/agent-recall-compiler  (L2 meta-prompt compilation)         │
                │  learn         →  voku/agent-learning         (findings → proposals → history)     │
                │  memory        →  voku/agent-loop             (MEMORY.md promotion review)         │
                └────────────────────────────────────────────────────────────────────────────────────┘
Namespace Purpose Owning package
board Pick work from local Markdown cards (todo/cards/*.md); Jira sync is optional and host-wired voku/agent-kanban
session Working memory for an in-progress task voku/agent-session
recall Compile task-scoped context (L2 meta-prompt) as review artifacts — not auto-injected into any agent voku/agent-recall-compiler
learn Findings → proposals → reviewed decision history voku/agent-learning
verify Cross-package consistency check (the only thing that looks at all of the above at once) voku/agent-loop
board:verify Narrow check of the kanban board source only voku/agent-kanban
memory MEMORY.md promotion review voku/agent-loop

Board: local Markdown first, Jira sync optional

board reads work items from local Markdown card files under todo/cards/*.md (one file per card), with todo/board.md holding board metadata (project prefix, done count). This works fully standalone — no Jira host, credentials, or network access required. todo/jira/ (the original directory name, from when this format was specific to Jira-derived cards) is also still supported: voku/agent-kanban checks todo/cards/ first and falls back to todo/jira/, so existing boards keep working without migration. If neither directory exists, board falls back to reading a single legacy TODO.md at the project root instead (voku/agent-kanban's own fallback, not something agent-loop adds).

Only board jira-sync talks to Jira, and only once the host application constructs the Dispatcher with its own JiraIssueProvider (see "Programmatic use" below) — the bare bin/agent-loop wires none. Every other board command (summary, render, lane, next-pull, ticket, context, brief) works from the local Markdown cards alone.

Requirements

Requirement Version
PHP 8.3 or newer
Composer required

Installation

composer require voku/agent-loop

This installs voku/agent-kanban, voku/agent-session, voku/agent-recall-compiler, and voku/agent-learning as dependencies and exposes vendor/bin/agent-loop.

Basic workflow

Start with the smallest useful loop — one task, one session, one compiled briefing:

agent-loop session start --task ABC-123 --by lars --base-commit "$(git rev-parse HEAD)"
# -> Started session: 2025-01-15-abc-123

agent-loop recall compile --root infra/doc/agent-learning --task ABC-123 --file src/Foo.php

# ...do the work...

agent-loop session record ABC-123 --kind decision --title "Keep change scoped" --body "..."
agent-loop session checkpoint ABC-123 --title "Validation" --body "PHPStan passed."
agent-loop verify
agent-loop session close ABC-123 --status done

session start prints its own generated session id (date-prefixed, e.g. 2025-01-15-abc-123) on its first line. You don't need to capture it: session record/checkpoint/close/claim/show also accept the task id you started the session with — agent-loop resolves it to the matching session id before delegating. The session id still works directly if you have it (e.g. from a list of multiple sessions for the same task). Likewise, recall compile --task ABC-123 without --output-dir writes to recall/ABC-123/ automatically, where agent-loop verify's recall-coverage check expects to find it; pass --output-dir explicitly only to override that default. See examples/basic-loop for this full sequence run against a tiny fake task with real captured output.

recall compile only writes files (system.md, validation-plan.md, recall-log.draft.json, meta.json) under recall/<task-id>/; it does not inject them into a running coding agent itself. After a successful compile, agent-loop prints a reminder of this:

[NOTE] Recall artifacts were written for review or harness ingestion.
[ACTION REQUIRED] Pass system.md / validation-plan.md into your agent workflow manually unless your harness consumes them automatically.

Whatever drives the agent (a human, an editor integration, or voku/housekeeping) is responsible for reading system.md and validation-plan.md and feeding them into the actual prompt/context — that wiring is host-specific and out of scope for this package. agent-loop verify's recall check only confirms a briefing was compiled and is not stale; it cannot confirm anything actually read it.

Add the board once you have more than one task in flight, and the learning loop once you want findings to survive past a single session:

agent-loop board next-pull
agent-loop learn validate --root infra/doc/agent-learning
agent-loop learn guidance-evaluate --root infra/doc/agent-learning

Exact available commands

Every command below is real and was verified against this repository's installed dependencies (composer require'd versions); none of it is aspirational. Run agent-loop <namespace> help (or --help) for a namespace's own usage.

agent-loop --help                 # top-level namespaces
agent-loop learn --help           # commands for a namespace
agent-loop recall --help
agent-loop session --help
agent-loop board --help

# board: reads cards from todo/cards/<PREFIX>-N.md (one file per ticket;
# optional todo/board.md sets the project prefix and done count). Works
# standalone, no Jira connection needed. todo/jira/ also still works as
# a fallback for boards that already use it. Falls back further to a
# single legacy TODO.md only if neither directory exists. Only
# `board jira-sync` needs a host-wired JiraIssueProvider.
agent-loop board summary
agent-loop board render --lanes=READY,BACKLOG --limit=10
agent-loop board next-pull
agent-loop board ticket ABC-123

# session: working memory for one task
agent-loop session start --task ABC-123 [--by ACTOR] [--base-commit SHA] [--slug S]
agent-loop session claim <id> --by ACTOR [--base-commit SHA] [--force]
agent-loop session checkpoint <id> --title T [--body TEXT]
agent-loop session record <id> --kind decision|assumption --title T [--body TEXT]
agent-loop session close <id> --status done|dropped
agent-loop session list [--status STATUS]
agent-loop session show <id>
agent-loop session prune [--keep-days N] [--status done,dropped] [--dry-run]

# recall: compile a task-scoped briefing
agent-loop recall compile --root infra/doc/agent-learning --task ABC-123 --file lib/foo.php
agent-loop recall log-outcome --root infra/doc/agent-learning --by lars --commit abc1234

# learn: findings, proposals, decision history
agent-loop learn validate --root infra/doc/agent-learning
agent-loop learn guidance-evaluate --root infra/doc/agent-learning
agent-loop learn proposal-validate --proposal proposals/candidate/proposal.001.json
agent-loop learn proposal-approve --by lars proposals/candidate/proposal.001.json
# (also: prepare, proposal-import, proposal-reject, proposal-mark-applied,
#  constraint-export, constraint-activate, constraint-loop, finding-transition)

# verify: the safety net — see below
agent-loop verify

# memory promotion review
agent-loop memory review --file MEMORY.md

agent-loop board jira-sync needs a JiraIssueProvider; it is the only board command that does. The bare binary does not wire one (Jira clients are host-specific) — see "Programmatic use" below. Every other board command works against the local Markdown cards without it.

agent-loop verify: the safety net

Every other namespace delegates outward and stops there. verify is the one command that looks across board, session, recall, and learning state at once and answers: is this repo's agent workflow state internally consistent?

agent-loop verify

Checks, each of which prints [OK], [SKIP], or [FAIL] and skips itself when its inputs are absent (so the command stays meaningful for a repo that only wires up part of the stack):

  • package delegates — board/learn/recall/session classes are installed and resolve
  • tasks — every *.md file under tasks/ parses (non-empty, has a heading)
  • boardTODO.md kanban board projection (delegated to voku/agent-kanban)
  • sessions — every non-closed session under session_plan/ points to a known task id
  • recall — every active session has a compiled briefing, and every recall/<task>/meta.json output hash still matches the file on disk (catches a briefing edited or regenerated out of band)
  • learning root — findings, proposals, and decision/outcome history validate

Run agent-loop verify --help for the override flags (--tasks-root, --sessions-root, --recall-root, --learning-root). agent-loop board:verify remains available as the narrower, board-only check this command used to be.

--strict: turn baseline skips into failures

By default, a missing input is reported as [SKIP] and does not fail the command — useful for a repo that only wires up part of the stack. Pass --strict to fail instead when tasks/ or session_plan/ is missing entirely:

agent-loop verify --strict

tasks/ and session_plan/ are the baseline this command exists to confirm — a task to work on, and a session tracking it. board (TODO.md) and the learning root stay skippable even under --strict: both are documented, opt-in additions on top of that baseline (see "Board: local Markdown first, Jira sync optional" above, and the learning loop in "Basic workflow"), not something every repo using agent-loop is expected to have set up. examples/basic-loop fails --strict before step 2 (session_plan/ doesn't exist yet), then passes it from step 5 onward — the same point where its own verify (without --strict) already passes, since by then a session and its recall briefing both exist.

What agent-loop deliberately does not do

agent-loop is not the learning engine. agent-loop is not the session store. agent-loop is not the recall compiler. agent-loop is the command surface.

Concretely, agent-loop:

  • holds no working memory of its own — sessions live in voku/agent-session's files, not in this package
  • makes no decisions about what counts as a durable lesson — that judgment lives in voku/agent-learning
  • selects no context for a prompt — selection logic lives in voku/agent-recall-compiler
  • owns no board data — board state lives in whatever Markdown/Jira source voku/agent-kanban reads
  • adds no scheduler, hidden state machine, or plugin lifecycle — voku/housekeeping is the runner; this is just the loop

If a feature needs new durable state, it belongs in one of the focused packages, not in agent-loop. The moment this wrapper starts hiding state of its own, it has become the second source of truth this whole stack was built to avoid.

Review boundaries and safety contracts

agent-loop coordinates the loop. It does not approve code, approve learning, or replace human review.

Concretely:

  • it does not auto-commit, auto-merge, or push anything — every command it runs is the one you typed, with arguments resolved or defaulted as documented above, nothing more
  • it does not approve code changes — that remains whatever review process (human or otherwise) already gates changes outside this tool
  • it does not silently promote findings into durable memory. learn proposal-approve --by ACTOR <id>, proposal-reject, and proposal-mark-applied are voku/agent-learning's own human-actor gate (each requires an explicit --by actor) on the candidate → approved → applied lifecycle; agent-loop delegates to that command verbatim and adds no auto-approval path of its own
  • agent-loop memory review is read-only: it reports which MEMORY.md rows look ready for promotion (see src/MemoryPromotionAnalyzer.php); it never edits MEMORY.md itself. Promotion stays a manual edit by whoever owns that file
  • agent-loop verify only reports [OK]/[SKIP]/[FAIL] on existing state; it never repairs drift it finds

If a workflow needs an automated approval or auto-promotion path, that is a deliberate, separately-reviewed change to the owning package (voku/agent-learning for proposals, the host application for MEMORY.md), not something to add to this wrapper.

Programmatic use (host wiring)

Hosts that already have a Jira client wire it once and reuse the whole CLI:

use voku\AgentKanban\JiraIssueProvider;
use voku\AgentLoop\Dispatcher;

$provider = new class implements JiraIssueProvider {
    public function projectKey(): string { /* ... */ }
    public function searchIssues(string $jql): array { /* ... */ }
};

exit((new Dispatcher($rootPath, $provider))->run($argv));

That single wrapper replaces per-library glue scripts: every board/verify/session/recall/learn/memory command flows through it.

Auto-running it on a schedule

voku/agent-loop is the loop; voku/housekeeping is the runner. Install Housekeeping in its own checkout, point it at your target repository, and let it invoke agent-loop commands (board refinement, verification, recall, …) from cron in safe patch mode.

Development

composer install
composer ci    # composer validate --strict + phpunit + phpstan (level 8)

tests/fixtures/basic-loop is a minimal end-to-end fixture (SmokeLoopTest) proving the orchestration shape: a task file exists, a session starts against it, recall compiles a briefing, learn validates the root, and agent-loop verify reports no drift — then fails on purpose once a briefing goes missing or gets edited out of band. examples/basic-loop walks through the same shape by hand, with real command output, for reading or running yourself.

License

MIT — see LICENSE.