aiwg
Version:
Deployment tool and support utility for AI context. Copies agents, skills, commands, rules, and behaviors into the paths each AI platform reads (Claude Code, Codex, Copilot, Cursor, Warp, OpenClaw, and 6 more) so one source of truth works across 10 platfo
85 lines • 3.62 kB
JavaScript
/**
* Codex `agents/openai.yaml` UI sidecar emission.
*
* Per ADR-1 sign-off (2026-05-05) and #1129 PUW-028: when AIWG deploys a skill
* to a Codex skills directory, also emit `agents/openai.yaml` inside the skill
* folder so Codex's skill-picker UI surfaces display name, icon, brand color,
* and default prompt.
*
* Sidecar is purely additive — its absence is graceful (Codex falls back to
* SKILL.md frontmatter for picker metadata). Sidecar emission must not block
* the skill deploy path on failure; the deploy succeeds even when the sidecar
* write errors out.
*
* Per ADR-1 sign-off: AIWG-wide brand defaults for icon and brand color
* (single brand identity). Per-category mapping is a follow-up if demand
* surfaces. Sidecar contents are sanitized through the AGENTS.md sanitizer
* to keep the same prompt-injection defense surface.
*/
import { promises as fs } from 'node:fs';
import * as path from 'node:path';
import * as yaml from 'js-yaml';
import { sanitizeDescription } from '../context-pipeline/sanitizer.js';
import { AIWG_BRAND_DEFAULTS } from '../context-pipeline/types.js';
/**
* Build the sidecar payload from skill metadata.
*
* Returns null when the skill has no usable name AND no description — there
* is nothing meaningful to emit. Caller skips sidecar in that case.
*/
export function buildSidecarPayload(metadata, folderName) {
const name = (metadata.name && metadata.name.trim()) || folderName;
if (!name)
return null;
// default_prompt is sanitized through the same surface as AGENTS.md fields
// because the sidecar payload reaches the model context once Codex loads
// the skill picker. Same prompt-injection blast radius applies.
const desc = metadata.description ? sanitizeDescription(metadata.description) : null;
const default_prompt = desc?.ok ? desc.value : `Run the ${name} skill.`;
return {
display_name: name,
icon: AIWG_BRAND_DEFAULTS.icon,
brand_color: AIWG_BRAND_DEFAULTS.brandColor,
default_prompt,
};
}
/**
* Render a sidecar payload as YAML.
*
* Wraps js-yaml.dump with a stable formatting set (no anchor refs, sorted
* keys, double-quoted scalars where needed).
*/
export function renderSidecarYaml(payload) {
return yaml.dump(payload, {
sortKeys: true,
noRefs: true,
quotingType: '"',
forceQuotes: false,
});
}
/**
* Emit `agents/openai.yaml` inside a deployed skill folder.
*
* - skillFolder: absolute path to the deployed skill folder (e.g.
* `~/.codex/skills/address-issues/` or `.codex/skills/address-issues/`).
* - metadata: SkillMetadata extracted from the skill's SKILL.md frontmatter.
*
* Returns the absolute sidecar path on successful write, or null when there
* was nothing to emit. Throws on filesystem error so the caller can decide
* whether to surface the failure as a deploy warning (recommended) or
* abort. The PUW-028 contract requires sidecar absence to be graceful, so
* callers should treat throws as warnings, not errors.
*/
export async function emitCodexSidecar(skillFolder, metadata) {
const folderName = path.basename(skillFolder);
const payload = buildSidecarPayload(metadata, folderName);
if (!payload)
return null;
const sidecarDir = path.join(skillFolder, 'agents');
await fs.mkdir(sidecarDir, { recursive: true });
const sidecarPath = path.join(sidecarDir, 'openai.yaml');
const content = renderSidecarYaml(payload);
await fs.writeFile(sidecarPath, content, 'utf8');
return sidecarPath;
}
//# sourceMappingURL=codex-sidecar.js.map