The CLI layer of the ADLC toolchain for coding agents: skill management
(install skills, generate slash commands, wire lifecycle event hooks),
team directives (configure and maintain a team-ai-directives repo),
workspaces (multi-repo bootstrap), agent execution (run any coding
agent headlessly with a task — the invocation layer the
Agentic Container delegates to),
and a workflow engine (a full JS port of the github/spec-kit workflow
engine — ADR-390 + amendments).
Renamed from
adlc-skills-cli. The deprecatedadlc-skills-clibin still ships (the legacyadd/upgrade/remove/status/agentssurface), along with theshim/adlc-skills-clinpm package forwarding to it.
Works with any skills repo: adlc-team-skills, mattpocock/skills, addyosmani/agent-skills, obra/superpowers, or your own.
# Install skills + generate commands + wire events
npx adlc-cli skills add tikalk/adlc-team-skills -a opencode
# Install skills AND configure team-ai-directives (runs /team-setup headlessly)
npx adlc-cli team setup tikalk/adlc-team-skills -a opencode
# Works with any skills repo — events auto-skip if no .events.json
npx adlc-cli skills add mattpocock/skills -a claude-code --no-events
npx adlc-cli skills add addyosmani/agent-skills -a opencode -a cursor
# Run a coding agent headlessly with a task
npx adlc-cli agent run "Fix the failing auth test" -a opencode
# List supported agents + run profiles
npx adlc-cli agent list
# Print version
npx adlc-cli version| Command | Description |
|---|---|
skills add <source> -a <agent> |
Install skills via npx skills add + generate commands + wire events |
skills update [-a <agent>] |
Re-generate commands from installed skills; --pull re-installs from source |
skills remove [-a <agent>] |
Remove generated commands + event configs; cleans dispatcher + .events.json |
skills [-a <agent>] |
Show what's installed per agent + dispatcher + event status |
| Command | Description |
|---|---|
team setup <source> -a <agent> |
Install skills + write .adlc/init-options.json + run /team-setup headlessly (--skip-skills to skip installation) |
team update |
git pull the team-ai-directives repo + re-install skills from source + team repair --update-confidence |
team repair [--update-confidence|--validate-drafts|--build-to-delete] |
Run /team-repair headlessly (agent-driven), or the deterministic setup-team.sh path for --update-confidence / --validate-drafts |
The team tree reads its targets from .adlc/init-options.json (see below);
-a defaults to the configured agent.
| Command | Description |
|---|---|
workspace setup [file] |
Apply a workspace file: clone git: modules, create dirs:, install skills, generate commands, run /workspace --init, and (first boot only) hand the agent the workspace goal |
workspace init [--link|--ignore-only] |
Run /workspace --init headlessly (.adlc/ structure, discover/link child repos) |
workspace status |
Run /workspace --status headlessly (audit branch, dirty, unpushed, SHA drift) |
Workspace file resolution: explicit argument → ADLC_WORKSPACE_FILE env →
.adlc/workspace.yml. Schema requires schema_version plus any of
workspace.git[] (repo/path/branch/ref), workspace.dirs[], workspace.init,
workspace.link, skills.sources[], commands[], goal. Deterministic
steps (git, dirs) run directly; agent-led steps go through agent run.
--dry-run prints the plan without executing.
| Command | Description |
|---|---|
agent run "<task>" [flags] |
Run a coding agent headlessly with a task |
agent list |
List supported agents, command formats, event support, and run profiles |
run "<task>" [flags] |
Top-level alias for agent run (runtime contract, ADR-368) |
| Command | Description |
|---|---|
workflow run <source> |
Execute a workflow — YAML path, installed ID, or built-in (factory = the outer loop) |
workflow resume <run_id> |
Resume a paused/failed run; gates re-prompt (or bind a verdict_input via --input) |
workflow status [run_id] |
List runs / show one run's step states |
workflow validate <source> |
Validate a definition; --headless requires gates to declare verdict_input |
workflow state … |
LLM-executor helpers: start/advance/pause/fail/show (ADR-395) |
Full JS port of the upstream github/spec-kit workflow engine (zero Python —
ADR-390 + amendments). Dual-executor architecture (ADR-395): the same
workflow.yml + state.json run either in-session (skills via the state
helpers) or headless (the CLI engine) — a run parked at a gate by one
executor resumes on the other. Step types: command, prompt, shell,
gate, if, switch, while, do-while, fan-out (with
max_concurrency), fan-in, slot.
.adlc/workflows/<id>/workflow.yml installed · curated definitions
.adlc/workflows/<run_id>/workflow.yml generated-by-slug definitions (ADR-391-amendment)
.adlc/workflows/runs/<run_id>/ run state: state.json, inputs.json, log.jsonl,
frozen workflow.yml, lease.json + mission
artifacts (brief.md, mission.yml, scratchpads/…)
refs/factory-runs/<run_id> git-refs Tier-3 (ADR-393)
# Run → gate pauses (non-TTY) → resume headlessly
adlc-cli workflow run mission --input spec="Fix auth"
adlc-cli workflow resume <run_id> --input verdict=approve
# The bundled outer loop (stages = command steps + gates)
adlc-cli workflow run factory --input verdict=approve
# CI: JSONL lifecycle events (run_started/step_*/gate_paused/…/run_completed)
adlc-cli workflow run factory --format json
# LLM executor drives state via helpers (set ADLC_WORKFLOW_SESSION per session)
export ADLC_WORKFLOW_SESSION=my-session
adlc-cli workflow state start --workflow mission --input spec="Fix auth"
adlc-cli workflow state advance <run_id> --step specify --status completed
adlc-cli workflow state pause <run_id> --step review # gate → humanEnvironment variables:
| Variable | Meaning |
|---|---|
ADLC_WORKFLOW_SESSION |
Session id — makes the run lease persist across sequential CLI invocations instead of per-command |
ADLC_WORKFLOW_RUN_ID |
Set for step processes so nested commands address their own run |
ADLC_WORKFLOW_LEASE_TTL |
Lease TTL in seconds (default 900) |
ADLC_WORKSPACE_FILE |
Workspace file path override for workspace setup |
SIGINT/SIGTERM trigger a clean pause (resumable) rather than a corrupt partial state.
Known constraint (inherited upstream): a gate nested inside
if/switch/while bodies that pauses will re-run the parent control-flow
step and its nested body on resume. Keep gates at the top level, or bind a
verdict_input on the gate.
| Command | Description |
|---|---|
version |
Print installed version |
help |
Show full help |
Written by team setup (and by the /team-setup skill), read by the team
and workspace trees to resolve defaults:
{
"agent": "opencode",
"skills_source": "tikalk/adlc-team-skills",
"team_ai_directives": "./tikal-team-ai-directives"
}agent and skills_source default every later -a / re-install;
team_ai_directives is where team update pulls and team repair indexes.
| Flag | Description |
|---|---|
-a <agent> |
Run profile: opencode | claude-code | goose | gemini (default: opencode) |
--model <id> |
Model id passed to the agent CLI (optional — agent picks its own if absent) |
--format <fmt> |
Output: text (default, human-readable) | json (normalized JSONL for container/CI) |
--cwd <path> |
Working directory (default: current directory) |
--timeout <s> |
Kill agent after N seconds (CI safety) |
--require-approval <tools> |
Comma-separated tools that pause for human approval (e.g., Bash,Write) |
- |
Read the task from stdin |
# One-shot run (text output)
adlc-cli agent run "Fix the failing auth test" -a opencode
# JSON output (normalized JSONL — what the Agentic Container consumes)
adlc-cli agent run "echo hello" -a opencode --format json
# Read task from stdin
cat brief.md | adlc-cli agent run - --format json
# Run in a specific workspace with a timeout
adlc-cli agent run "refactor utils" -a opencode --cwd ./my-project --timeout 120
# Require approval for dangerous tools (HITL)
adlc-cli agent run "deploy to staging" -a opencode --require-approval Bash,Writeagent run --format json emits one JSON object per line:
| Type | Payload | Meaning |
|---|---|---|
message |
{text} |
Agent text output |
tool |
{phase: "call"|"result", name?, arguments?, result?} |
Tool invocation or result |
permission_request |
{tool, request_id} |
HITL permission gate |
error |
{message} |
Error |
complete |
{} |
Agent finished |
log |
{message?} |
Non-structured log line |
adlc-cli skills add <source> -a <agent>
│
├─ 1. npx skills add <source> -a <npx_agent> ← installs SKILL.md files
│
├─ 2. Discover installed skills ← reads SKILL.md frontmatter
│
├─ 3. Generate command files ← slash commands (/name)
│ .opencode/commands/<name>.md inline: embeds skill body
│ wrapper: references skill
│
└─ 4. Wire events (if .events.json in source) ← lifecycle hooks
.agents/dispatcher.mjs generic dispatcher (shipped)
.opencode/plugins/adlc-skills-events.ts agent-native hook config
| Flag | Description |
|---|---|
-a <agent> |
Target agent (repeatable). Run agent list to list. |
-g, --global |
Install to user directory instead of project |
--no-events |
Skip event config generation |
--prefix <str> |
Namespace command filenames (e.g., adlc.team-setup.md) |
--mode <mode> |
inline (embeds full skill body) or wrapper (references skill by name) |
--skill, -s <name> |
Install/generate for one skill only (use '*' for all). The selection is expanded through the source's .skills-deps.json closure: borrowers auto-pull their canonical homes (e.g. --skill architect-implement also installs architect-clarify) |
--copy |
Copy files instead of symlinking (passthrough to npx skills) |
--pull |
(update) re-install from the locked source |
-y, --yes |
Skip confirmation prompts |
24 command agents across 3 command formats (plus a generic fallback), 9
agents with event hooks, 4 run profiles.
Gemini key split (current behavior): the agent registry key is
gemini-cli(skills/commands/events), but the run profile is keyedgemini. So:skills add -a gemini-cli, butagent run -a gemini— andagent listshows gemini-cli without a run profile. Use the two keys as shown.
| Agent | Commands dir | Format | Events | Run |
|---|---|---|---|---|
| opencode | .opencode/commands/ |
markdown | yes | yes |
| claude-code | .claude/commands/ |
markdown | yes | yes |
| goose | .goose/recipes/ |
yaml | no | yes |
| gemini-cli | .gemini/commands/ |
toml | yes | agent run -a gemini |
| ...and 20 more |
Run adlc-cli agent list for the full table.
Embeds the full SKILL.md body in the command file — self-contained, works on any agent:
---
description: Clone, scaffold, or configure a team AI directives repository
---
<!-- generated by adlc-cli; source: tikalk/adlc-team-skills — do not edit -->
Base directory for this skill: /project/.agents/skills/team-setup
# Team Setup
[full skill body...]
$ARGUMENTSThin command that references the installed skill — requires the agent to have skill support:
---
description: Clone, scaffold, or configure a team AI directives repository
---
<!-- generated by adlc-cli; source: tikalk/adlc-team-skills — do not edit -->
Invoke the `team-setup` skill.
<skill summary>
## User Input
$ARGUMENTSThe ## User Input block is generated for both modes so the args placeholder is framed as workflow input rather than trailing text.
User-invoked skills (disable-model-invocation: true in frontmatter) are meant to be triggered explicitly. The command body strategy depends on the agent:
| Mode | Used by | Why |
|---|---|---|
wrapper |
opencode | Ignores disable-model-invocation — skill stays available, model calls skill({name}) |
execution |
claude-code, cursor, copilot, codex | Respects disable-model-invocation — skill hidden, body must be inlined |
For agents with native hook support (9 agents), the CLI wires event hooks that auto-trigger skills at lifecycle points.
Events are auto-enabled when:
- The agent supports events
- The source repo declares a
.events.jsonmanifest --no-eventsis not set
{
"events": {
"session_start": [
{ "skill": "team-boot", "description": "Bootstrap session with team context", "timeout": 60 }
],
"user_prompt_submit": [
{ "skill": "team-discover", "description": "Fetch relevant context", "timeout": 30 }
]
}
}Repos without .events.json get commands only — events are skipped silently.
A generic dispatcher (.agents/dispatcher.mjs) is shipped to the project. When a native hook fires, it calls the dispatcher:
| Path | When | How |
|---|---|---|
| Script | Skill has scripts: in frontmatter |
Runs the script → stdout |
| Body | No scripts: block |
Outputs the skill's markdown body → stdout |
Both paths feed the stdout → context injection pipeline.
| Event | Fires when | Body path? | Script path? |
|---|---|---|---|
session_start |
Agent session begins | yes | yes |
session_compact |
Harness compacts history | yes | yes |
user_prompt_submit |
User sends prompt | yes | yes |
pre_tool_use |
Before tool call | no | yes |
post_tool_use |
After tool call | no | yes |
file_edited |
A file was edited (e.g. decision drafts) | no | yes |
session_end |
Session ends | no | yes |
stop |
Agent stops | no | yes |
| Agent | Native hook | Delivery |
|---|---|---|
| opencode | file.edited (generic event subscription) |
script stdout stashed → injected into the last user message on the next messages.transform pass (before the session-start dedup guard) |
| claude-code | PostToolUse (+ matcher e.g. Edit|Write) |
hookSpecificOutput.additionalContext — direct context injection |
| codex | PostToolUse |
envelope suppressed (tool-hook output sink unverified — safe no-op) |
| cursor | postToolUse |
envelope suppressed (tool-hook output sink unverified — safe no-op) |
| others | — | null mapping until a native surface is documented |
Skill scripts see ADLC_EVENT in their environment and the event payload on stdin.
| Agent | Config file | Format | Timeout unit |
|---|---|---|---|
| opencode | .opencode/plugins/adlc-skills-events.ts |
TS plugin | seconds |
| claude-code | .claude/settings.json (merged) |
JSON nested | seconds |
| cursor | .cursor/hooks.json (merged) |
JSON nested | seconds |
| github-copilot | .github/hooks/adlc-skills.json |
JSON | seconds |
| codex | .codex/config.toml (merged) |
TOML | seconds |
| gemini-cli | .gemini/settings.json (merged) |
JSON nested | milliseconds |
| qwen-code | .qwen/settings.json (merged) |
JSON nested | milliseconds |
| devin | .devin/hooks.v1.json (merged) |
JSON root-nested | seconds |
| tabnine-cli | .tabnine/agent/settings.json (merged) |
JSON nested | milliseconds |
- Idempotent merge: re-install never duplicates hook entries
- Surgical teardown:
removestrips only our entries, preserves user hooks - JSONC preservation: malformed JSON aborts, never resets user content
- Safe-destination validation: rejects symlink redirects outside project root
- Shell-safe argv:
execFileSyncwith argv arrays, no shell injection - stdin payload forwarding:
user_prompt_submitreceives the user's prompt
Every generated file includes a <!-- generated by adlc-cli --> header. Event hook entries carry a _adlc_skills_cli: true marker (JSON) or adlc_skills_marker = true (TOML). This enables:
removeto safely delete only our filesupgradeto overwrite our files while skipping user-modified ones
No manifest database or state file needed.
# One-off (no install needed)
npx adlc-cli skills add tikalk/adlc-team-skills -a opencode
# Install as global binary
npm install -g adlc-cli
adlc-cli skills add tikalk/adlc-team-skills -a opencode# Run tests (300+ tests via node --test)
npm test
# Run the CLI locally
node bin/adlc-cli.mjs help
node bin/adlc-cli.mjs agent list
node bin/adlc-cli.mjs skills -a opencode
# Run a task locally
node bin/adlc-cli.mjs agent run "say hello" -a opencode --format textZero runtime dependencies. Requires Node.js >= 18.
MIT