UNPKG

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

139 lines (138 loc) • 6.71 kB
// 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/. /** * Amazon Q Developer 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 `q` CLI expects. Per the capability matrix the headless * invocation is: * * q chat --no-interactive --trust-all-tools "<prompt>" * * with `--model <m>` for model selection and `--resume` for resume — NOTE: * unlike every other harness's resume, Q's `--resume` is a bare flag that * replays the previous conversation *of the working directory*; it takes no * session id. There is nothing to thread from `workflow_run_units` — akm's own * unit rows remain the durable resume source of truth regardless (plan * §"Session, MCP, and identity across harnesses"). * * Platform-specific mapping decisions (all localized here, per the adapter * contract): * * - **subcommand** — headless dispatch is the `chat` subcommand. The builder * prepends `chat` itself (mirroring the codex builder's `exec` handling); a * user profile that already pins `chat` as its first arg is not doubled. * - **prompt** — the trailing positional `[INPUT]` argument of `q chat`, * preceded by the `--` end-of-options separator (mirroring the * claude/codex/pi builders) so a prompt whose text begins with `-`/`--` can * never be parsed as flags. `--no-interactive` makes Q print the response * and exit instead of opening the REPL. * - **systemPrompt** — `q chat` has no system-prompt flag (persona/context * comes from Q's own agent config files), so the system prompt is folded * into the positional payload ahead of the task prompt, separated by a * blank line. * - **schema** — the matrix places Q in the NO-structured-output tier * ("via prompt+validate": *(none documented)* — there is no `--json` or * `--output-format` to ask for). The JSON Schema therefore reaches it only * as the instruction the shared request lowering appends to the prompt. * Stdout stays plain text; `./result-extractor.ts` strips terminal framing * and the engine's shared embedded-JSON parse + retry-until-valid loop does * the rest. No schema temp file is written — that seam is codex-only * (`--output-schema`); inventing a flag here would produce a silently * broken command. * - **tools** — a string/array tool policy maps to Q's documented * `--trust-tools=<t1,t2>` allowlist flag (equals-joined, per `q chat * --help`). With no policy at all, headless runs need autonomy, so * `--trust-all-tools` is emitted per the matrix. A *structured* policy * object is NOT expressible as Q flags; it is deliberately dropped without * falling back to `--trust-all-tools` (never silently widen a restriction) * — Q then refuses untrusted tool actions in non-interactive mode, which is * the conservative failure mode. * - **inference** — not translated: the shared lowering reports each field of * the request's inference as untranslated. * * Registered: `amazonqBuilder` is `AmazonqHarness.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 `"amazonq"` 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 AMAZONQ_PLATFORM = "amazonq"; /** * Split a tool policy into individual tool names for `--trust-tools`. * Strings are comma-separated lists; arrays are taken as-is. Structured * policy objects return `undefined` (not expressible as Q flags — see * module doc). */ function toolPolicyEntries(tools) { if (typeof tools === "string") { return tools .split(",") .map((t) => t.trim()) .filter(Boolean); } if (Array.isArray(tools)) { return tools.map((t) => t.trim()).filter(Boolean); } return undefined; } /** Assemble the positional prompt payload: optional system prompt, then the task prompt. */ function buildPromptPayload(req) { const sections = []; if (req.systemPrompt) sections.push(req.systemPrompt); sections.push(req.prompt); return sections.join("\n\n"); } /** * Amazon Q Developer CLI builder. * Command shape: * q chat --no-interactive (--trust-all-tools | --trust-tools=<t1,t2>) * [--model <m>] -- "<systemPrompt?\n\nprompt>" */ export const amazonqBuilder = { platform: AMAZONQ_PLATFORM, personaChannel: "prompt", lower: createAgentRequestLowerer({ adapter: AMAZONQ_PLATFORM, personaChannel: "prompt", tools: "flat", }), build(profile, req) { // Built-in q profiles would ship `args: []`; headless dispatch is the // `chat` subcommand. Don't double it when a user profile already pins it. const extra = profile.args[0] === "chat" ? profile.args.slice(1) : [...profile.args]; const args = ["chat", ...extra]; // Print the response and exit — required for captured dispatch. args.push("--no-interactive"); if (req.tools) { // Structured policy objects (entries === undefined) emit NO trust // flags: dropping a restriction must never widen to --trust-all-tools. const entries = toolPolicyEntries(req.tools); if (entries !== undefined) { // Q's documented allowlist form is equals-joined and comma-separated // (`--trust-tools=fs_read,fs_write`); an empty list trusts no tools. args.push(`--trust-tools=${entries.join(",")}`); } } else { // Headless default per the capability matrix: units must run without // interactive tool-approval prompts. args.push("--trust-all-tools"); } if (req.model) { const resolved = resolveDispatchModel(req, profile, AMAZONQ_PLATFORM); args.push("--model", resolved); } // No system-prompt flag exists on `q chat` — it travels in the positional // payload, after the end-of-options separator. args.push("--"); args.push(buildPromptPayload(req)); return { argv: [profile.bin, ...args] }; }, };