akm-cli
Version:
akm (Agent Knowledge Manager) — a portable, local-first capability library for AI agents. Discover, load, share, and improve reusable skills, scripts, workflows, and knowledge across any shell-capable coding agent, including Claude Code, OpenCode, and Cur
108 lines (107 loc) • 5.63 kB
JavaScript
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
/**
* Aider CLI agent command builder (P2, plan §"The adapter contract" step 2 /
* §"Capability matrix").
*
* Translates a platform-agnostic {@link AgentDispatchRequest} into the exact
* headless argv the `aider` CLI expects. Per the capability matrix the
* headless invocation is:
*
* aider -m "<p>" --yes-always
*
* with `--model <m>` for model selection, NO structured-output mode (Aider is
* the "none" tier — prompt-injected schema + embedded-JSON extraction), and
* NO session flag (resume is chat-history files, not an id).
*
* Platform-specific mapping decisions (all localized here, per the adapter
* contract):
*
* - **prompt** — `--message` is Aider's one-shot non-interactive mode (the
* matrix's `-m`; the long form is used for self-documenting argv). Unlike
* claude/codex/pi there is NO positional prompt and NO `--` end-of-options
* separator to hide behind: the prompt is a *flag value*. The glued
* `--message=<payload>` form is therefore used so a payload that begins
* with `-`/`--` binds to the flag lexically and can never be parsed as a
* separate option by Aider's argument parser.
* - **--yes-always** — always emitted: auto-confirms every interactive
* prompt (create file? run command? …), which is what makes a captured,
* unattended run possible. This is the matrix's documented headless shape.
* - **--no-pretty** — always emitted: disables colored/pretty terminal
* rendering so captured stdout is clean text for `./result-extractor.ts`
* (the same "dispatch is the captured path" reasoning as the Claude
* builder's unconditional `--print` and Codex's unconditional `--json`).
* - **systemPrompt** — Aider has no system-prompt flag (its nearest concept
* is conventions files via `--read`, which take a path, not text). Folded
* into the message payload — system text first, blank line, then the task —
* mirroring the codex builder's treatment.
* - **schema** — the matrix places Aider in the "via prompt+validate" tier
* with *no* structured output mode at all (plan §"Structured-output
* normalization", tier "none"): there is no schema flag and no JSON output
* flag, so the JSON Schema reaches it only as the instruction the shared
* request lowering appends to the prompt. Downstream, embedded-JSON
* extraction + the engine's shared retry-until-valid loop supply the
* validation Aider lacks. No temp schema file is written — that is Codex's
* native-schema mechanism (`--output-schema`), which Aider does not have.
* - **tools** — deliberately unconsumed. Aider has no per-tool allowlist
* flag; tool-ish behaviour is governed by its own switches (`--yes-always`,
* git integration, shell-command confirmation). A restrictive policy is
* therefore dropped rather than approximated — never silently widened.
* - **resume/session** — NOT expressible: Aider persists context in
* chat-history files (`.aider.chat.history.md`), not session ids, so there
* is no flag-shaped resume to describe and the extractor
* never yields a `sessionId`. akm's `workflow_run_units` remains the
* durable source of truth; resume works even against a harness with no
* session model (plan §"Session, MCP, and identity across harnesses" —
* Aider is the plan's named example).
* - **inference** — not translated: the shared lowering reports each field of
* the request's inference as untranslated.
*
* Registered: `aiderBuilder` is `AiderHarness.agentBuilder` (`./index.ts`),
* one of the ten harnesses `HARNESS_REGISTRY` constructs
* (`harnesses/index.ts`); `agent/builders.ts` derives `BUILTIN_BUILDERS` from
* that registry, so this builder is reachable under the `"aider"` platform
* name without any further wiring.
*/
import { resolveDispatchModel } from "../../agent/builder-shared.js";
import { createAgentRequestLowerer } from "../../agent/request-lowering.js";
/** Canonical harness/platform id used for model-alias resolution. */
export const AIDER_PLATFORM = "aider";
/** Assemble the `--message` payload: optional system text, then the task prompt. */
function buildMessagePayload(req) {
const sections = [];
if (req.systemPrompt)
sections.push(req.systemPrompt);
sections.push(req.prompt);
return sections.join("\n\n");
}
/**
* Aider builder.
* Command shape:
* aider [--model <m>] --yes-always --no-pretty --message=<[system\n\n]prompt>
*/
export const aiderBuilder = {
platform: AIDER_PLATFORM,
personaChannel: "prompt",
lower: createAgentRequestLowerer({
adapter: AIDER_PLATFORM,
personaChannel: "prompt",
tools: "none",
}),
build(profile, req) {
const args = [...profile.args];
if (req.model) {
const resolved = resolveDispatchModel(req, profile, AIDER_PLATFORM);
args.push("--model", resolved);
}
// Headless essentials (matrix shape): auto-confirm everything, and keep
// captured stdout free of pretty/ANSI rendering for the extractor.
args.push("--yes-always");
args.push("--no-pretty");
// Glued form: the payload is a flag VALUE (no positional prompt exists),
// so `=` binding keeps a dash-leading payload from parsing as an option.
args.push(`--message=${buildMessagePayload(req)}`);
return { argv: [profile.bin, ...args] };
},
};