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
134 lines • 5.45 kB
TypeScript
/**
* Namespace-aware Skill Deployment Adapter
*
* Implements per-platform namespace deployment strategy per ADR:
* .aiwg/architecture/adr-skill-namespace-strategy.md
*
* Platform groups:
* Group A (deep-recursion): Claude Code, Cursor, Codex, OpenCode, OpenClaw
* → Deploy both canonical subdir layout AND prefixed slug fallback.
* Group B (one-level): Factory AI, Warp, Windsurf
* → Deploy only the prefixed slug; no subdir (would break one-level discovery).
* Group C (unknown): GitHub Copilot
* → Deploy only the prefixed slug (safe default until recursion depth confirmed).
* Group D (mcp-skip): Hermes
* → Skip file deployment, emit guidance pointing to hermes-quickstart.md.
*
* @implements #704
* @see collision-detector.ts for pre-deployment collision checking (#697)
*/
import type { Platform } from '../../agents/types.js';
/**
* Namespace deployment group per ADR.
*/
export type DeploymentGroup = 'deep-recursion' | 'one-level' | 'unknown' | 'mcp-skip';
/**
* Whether the skills root resolves relative to the project directory or
* the user's home directory.
*/
export type PathType = 'project' | 'home-dir';
/**
* Per-platform namespace deployment adapter configuration.
*/
export interface NamespaceAdapter {
/** Platform this adapter handles */
platform: Platform;
/**
* Deployment group, determines how many target paths are produced:
* - deep-recursion: subdir layout + prefixed slug
* - one-level / unknown: prefixed slug only
* - mcp-skip: no files written
*/
deploymentGroup: DeploymentGroup;
/** Whether skills root resolves relative to project or home directory */
pathType: PathType;
/** Skills base directory (relative to project root, or relative to homedir) */
skillsBaseDir: string;
/**
* Whether to produce the canonical `{baseDir}/{namespace}/{slug}/` path.
* True for Group A (deep recursion) only.
* False for all other groups — would break one-level discovery.
*/
subdirLayout: boolean;
/** Maximum name field length (SKILL.md frontmatter truncation) */
maxNameLength?: number;
/** Maximum description field length (SKILL.md frontmatter truncation) */
maxDescriptionLength?: number;
/** Text appended to description field (e.g. Factory suffix) */
appendToDescription?: string;
}
/**
* A single file deployment instruction.
*/
export interface DeploymentPlan {
/** Absolute path to write the SKILL.md file */
targetPath: string;
/** File content (may differ from source when frontmatter is mutated) */
content: string;
/** Human-readable description for logging */
label: string;
}
/**
* Result of resolving namespace deployment plans for one skill.
*/
export interface NamespaceDeployResult {
platform: Platform;
skillName: string;
/** Plans to execute (empty when skip=true) */
plans: DeploymentPlan[];
/** When true, no files should be written */
skip: boolean;
/** Guidance message when skip=true (e.g. Hermes manual setup note) */
skipMessage?: string;
}
/**
* Per-platform adapter table.
* Keyed by Platform value; includes a 'generic' fallback.
*/
export declare const NAMESPACE_ADAPTERS: Record<Platform | 'generic', NamespaceAdapter>;
/**
* Resolve absolute skills root for a platform + project combination.
* Home-dir platforms (Codex, OpenClaw, Hermes) use os.homedir().
*/
export declare function resolveSkillsRoot(adapter: NamespaceAdapter, projectPath: string): string;
/**
* Compute canonical namespaced slug.
* Idempotent: if `skillName` already starts with `{namespace}-`, returns it unchanged.
*
* @example
* computePrefixedSlug('sync', 'aiwg') // → 'aiwg-sync'
* computePrefixedSlug('aiwg-sync', 'aiwg') // → 'aiwg-sync'
*/
export declare function computePrefixedSlug(skillName: string, namespace: string): string;
/**
* Apply platform-specific frontmatter mutations to SKILL.md content.
*
* Mutations applied (when configured):
* - Inject `namespace: {namespace}` into frontmatter if not present
* - Truncate `name` to `maxNameLength`
* - Append `appendToDescription` suffix to `description`
* - Truncate `description` to `maxDescriptionLength`
*
* Returns the original content unchanged when no mutations are needed.
*/
export declare function mutateFrontmatter(content: string, adapter: NamespaceAdapter, namespace: string): string;
/**
* Get the namespace adapter for a platform.
* Falls back to 'generic' for unrecognised platforms.
*/
export declare function getAdapter(platform: Platform | string): NamespaceAdapter;
/**
* Resolve deployment plans for a skill on a given platform.
*
* Calls `checkCollisions()` before writing should be done at the call site
* (see collision-detector.ts, #697).
*
* @param platform - Target platform
* @param projectPath - Absolute project root directory
* @param skillName - Skill folder name (e.g. 'sync', 'aiwg-sync')
* @param sourceContent - Raw content of the source SKILL.md file
* @param namespace - Namespace prefix (default: 'aiwg')
* @returns NamespaceDeployResult with plans to execute (or skip=true for Hermes)
*/
export declare function getDeploymentPlans(platform: Platform | string, projectPath: string, skillName: string, sourceContent: string, namespace?: string): NamespaceDeployResult;
//# sourceMappingURL=namespace-adapter.d.ts.map