feat(#1173): wire agent converters into descriptor-driven install path (#1227)

Extends `dispatchKindEntry` in `runtime-artifact-layout.cts` to route
agents-kind entries through a converter when the descriptor carries a
non-null `converter` field. Adds `stageAgentsForRuntimeWithConverter`
to `install-profiles.cts`, expands `VALID_CONVERTER_NAMES` with the 9
agent converter names, and adds a fail-first behavioral test suite
(9 tests) proving the new wiring end-to-end.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Tom Boucher
2026-06-14 14:20:21 -04:00
committed by GitHub
parent bf634b95c3
commit 73b7f45140
5 changed files with 420 additions and 4 deletions

View File

@@ -21,6 +21,7 @@ import installProfiles = require('./install-profiles.cjs');
const {
stageSkillsForProfile,
stageAgentsForProfile,
stageAgentsForRuntimeWithConverter,
stageSkillsForRuntimeAsSkills,
stageCommandsForRuntimeFlat,
} = installProfiles;
@@ -192,6 +193,41 @@ function agentsKind(destSubpath: string, prefix: string, configDir: string): Art
};
}
/**
* Build a converted-agents kind descriptor for runtimes whose agent `.md` files
* need runtime-specific frontmatter/body conversion (e.g. Copilot, Cursor, Codex).
*
* Unlike `agentsKind` (which raw-copies source files), this kind applies
* `converterName` from Runtime Artifact Conversion exports to each agent file
* during staging, writing flat `${name}.md` files to the staged directory.
*
* Agent filenames are preserved verbatim (the prefix is already embedded in the
* agent stem — e.g. `gsd-planner.md`).
*
* Mirrors the `convertedCommandsKind` pattern (#785).
*
* @param destSubpath destination subpath within configDir (e.g. 'agents')
* @param prefix filename prefix (informational; not applied here)
* @param converterName name of converter function in Runtime Artifact Conversion exports
* @param configDir runtime config dir (for .gsd-source marker resolution)
*/
function convertedAgentsKind(
destSubpath: string,
prefix: string,
converterName: string,
configDir: string,
): ArtifactKind {
return {
kind: 'agents',
destSubpath,
prefix,
stage: (resolved) => {
const converter = conversionExports[converterName] as (content: string) => string;
return stageAgentsForRuntimeWithConverter(findAgentsSourceRoot(configDir), resolved, converter);
},
};
}
function kimiAgentsKind(destSubpath: string, prefix: string, configDir: string): ArtifactKind {
return {
kind: 'kimi-agents',
@@ -401,7 +437,10 @@ function dispatchKindEntry(entry: ArtifactKindDescriptor, runtime: string, confi
return convertedCommandsKind(destSubpath, prefix, converter, configDir);
case 'agents':
return agentsKind(destSubpath, prefix, configDir);
if (converter == null) {
return agentsKind(destSubpath, prefix, configDir);
}
return convertedAgentsKind(destSubpath, prefix, converter, configDir);
case 'skills':
if (converter == null) {