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
116 lines (115 loc) • 7.53 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/.
/**
* `akm show` agent-directive emission.
*
* Extracted from `formatShowPlain` (formerly the largest function in
* `helpers.ts`): once the base show content is assembled, this module
* appends the REC-01/REC-09 APPLY / show-loop / workflow-active directive
* blocks that tell the calling agent how to act on the rendered asset.
* Directive strings are byte-identical to the pre-extraction version —
* only the seam moved.
*/
import { conceptIdFromTypeName } from "../../core/asset/resolve-ref.js";
/**
* Append the type-specific agent directive block to an in-progress
* `formatShowPlain` render. Mutates `lines` in place (matches the original
* inline behavior — push/unshift/splice all operate on the same array the
* caller continues to join afterward).
*/
export function appendShowDirectives(lines, r) {
// REC-01 / REC-09: Append a type-specific directive so agents apply the
// content rather than substituting training-data approximations.
const assetType = typeof r.type === "string" ? r.type : null;
// 0.9.0 (Q-02 / R-002): the retired `type:name` colon grammar is gone —
// build the slash conceptId the same way the workflow branch below does
// (`workflows/${name}`), via the entry's own already-computed `ref` when
// present (it carries correct bundle-qualification), falling back to a
// type-derived conceptId for callers that don't set `ref`.
const assetRef = typeof r.ref === "string"
? r.ref
: typeof r.name === "string" && assetType
? conceptIdFromTypeName(assetType, r.name)
: null;
// Show-loop detection: if the agent has shown this asset 3+ times without
// writing anything, surface a warning so it stops cycling and acts.
const showLoopCount = typeof r.showLoopWarning === "number" ? r.showLoopWarning : 0;
if (showLoopCount >= 3) {
lines.push("");
lines.push(`WARNING: You have shown this asset ${showLoopCount} times without completing the task.`);
lines.push("Stop re-reading — you have the information you need. Act on it now:");
lines.push(" - Write your output file using the content above.");
lines.push(` - If this asset does not contain what you need, run \`akm feedback '${assetRef ?? "<ref>"}' --negative\` and search for a different asset.`);
}
if (assetType === "skill" || assetType === "knowledge") {
const activeRun = r.activeRun;
if (activeRun) {
lines.unshift("Read this reference, follow the active unit's instructions, and return the result to the workflow runner. Step completion is automatic.");
lines.unshift(`WORKFLOW ACTIVE — schema shown as reference (run: ${activeRun.runId})`);
lines.unshift("---");
lines.unshift("");
// Still show feedback line at the end but skip the APPLY directive
lines.push("");
lines.push(`Run \`akm feedback ${assetRef ? `'${assetRef}'` : "<ref>"} --positive\` if the step succeeds, or \`--negative\` if this schema did not help.`);
}
else {
// No active workflow: show the APPLY directive. Branch on whether this
// skill primarily teaches CLI commands (shell output) vs YAML schema.
const preApplyLines = [...lines];
lines.push("");
lines.push("---");
if (isCommandOutputSkill(preApplyLines)) {
lines.push("APPLY (only if no workflow step is required for this task):");
lines.push(" 1. Identify the output file from README.md (typically commands.txt).");
lines.push(" 2. Write the exact command syntax from the code blocks above — replace every placeholder (`<name>`, `<value>`) with a real, concrete value from your task context. Do not write placeholder text.");
lines.push(" 3. Each command should be on a single line (no backslash line continuation unless the verifier expects it).");
lines.push(`Run \`akm feedback ${assetRef ? `'${assetRef}'` : "<ref>"} --positive\` after the task succeeds, or \`--negative\` if this reference did not contain the needed command syntax.`);
}
else {
lines.push("APPLY (only if no workflow step is required for this task):");
lines.push(" 1. Identify the target file from README.md — write or edit it. If the file does not yet exist, CREATE it with the full structure from this schema.");
lines.push(" 2. Add/edit the fields shown above using the exact field names from this schema.");
lines.push(" 3. COPY the exact YAML structure and field names from the code blocks above — do not substitute synonyms or invent nesting. Replace every placeholder value with a real, concrete value from your task context. Do not leave any field as null, empty, or a placeholder.");
lines.push(`Run \`akm feedback ${assetRef ? `'${assetRef}'` : "<ref>"} --positive\` after the task succeeds, or \`--negative\` if the task fails after following this guidance.`);
}
}
}
else if (assetType === "workflow") {
const workflowName = typeof r.name === "string" ? r.name : null;
const workflowRef = workflowName ? `workflows/${workflowName}` : "<ref>";
// Insert action directive BEFORE the workflow content by prepending to lines at the
// separator position. We find where the header ends and insert after the first `---`.
// Since lines already contain the full content at this point, we locate the insertion
// index: right after the first `---` separator if present, otherwise after the header.
const separatorIdx = lines.indexOf("---");
const insertIdx = separatorIdx >= 0 ? separatorIdx + 1 : r.type || r.name ? 1 : 0;
const actionDirective = [
`ACTION REQUIRED: Do not execute steps manually from this output.`,
`Run \`akm workflow run '${workflowRef}'\` to execute the workflow through its declared gates.`,
"---",
];
lines.splice(insertIdx, 0, "", ...actionDirective);
lines.push("");
lines.push("---");
lines.push(`NEXT STEP: Run \`akm workflow run '${workflowRef}'\` to execute the workflow.`);
lines.push("Inspect the workflow first; the runner dispatches and completes each step automatically.");
}
}
/**
* Detect whether a skill's rendered content primarily teaches CLI commands
* rather than YAML schema. Used to select the right APPLY directive variant.
*
* Heuristic: count code-block lines that start with known shell command
* prefixes vs lines that look like YAML key-value pairs. If CLI lines
* outnumber YAML lines (and there is at least one CLI line), treat the
* skill as command-output.
*/
function isCommandOutputSkill(lines) {
const codeLines = lines.filter((l) => l.startsWith(" ") || l.startsWith("\t") || /^`/.test(l));
const cliPattern = /^(az |kubectl |docker |git |helm |terraform |aws |gcloud )/;
const yamlPattern = /^\s+\w+:/;
const cliCount = codeLines.filter((l) => cliPattern.test(l.trim())).length;
const yamlCount = codeLines.filter((l) => yamlPattern.test(l)).length;
return cliCount > yamlCount && cliCount > 0;
}