/** * Team Capability Advertisement (#1608) * * Generates the `Team Capabilities` section that is spliced into * `.github/agents/squad.agent.md` so an *outer* coordinator (Agentic * Workflows, another Squad, a human) can tell — without spawning anything — * which specialists this squad actually has, which task types it supports, * where to route a given domain, and what the team is *not* able to do. * * Design rules (all enforced by tests): * * 1. **Grounded only in real data.** Everything is derived from `team.md`, * `routing.md`, `casting/registry.json`, and agent charters. Nothing is * invented, and no default cast is ever hardcoded. * 2. **No duplicate routing model.** Roster/routing parsing is delegated to * the existing parsers in `ralph/triage.ts`; charter parsing is delegated * to `config/agent-doc.ts`. * 3. **Deterministic.** Same inputs → byte-identical output. Ordering is * either source order (roster, routing table) or a fixed vocabulary order. * 4. **Untrusted metadata is data, not instructions.** Every value that comes * from a charter or a markdown table is passed through * {@link sanitizeMetadataText} before it reaches the rendered block. * 5. **Marker-delimited.** Only the region between the BEGIN/END markers is * ever rewritten, so user-authored content around it is preserved. * * @module config/team-capabilities */ import type { StorageProvider } from '../storage/index.js'; /** Opening marker of the generated region. */ export declare const TEAM_CAPABILITIES_BEGIN = ""; /** Closing marker of the generated region. */ export declare const TEAM_CAPABILITIES_END = ""; /** Format version embedded in the generated block. Bump on breaking changes. */ export declare const TEAM_CAPABILITIES_SCHEMA = 1; /** * What an agent is actually permitted to do, derived from charter evidence. * * `edit` and `review` are only claimed when charter/role text supports them; * `advisory` is the conservative fallback so the block never over-claims. */ export type AgentAuthority = 'review' | 'edit' | 'advisory'; /** One advertised specialist. */ export interface SpecialistEntry { /** Sanitized display name exactly as it appears in the roster. */ readonly name: string; /** Sanitized role string from the roster. */ readonly role: string; /** Short, sanitized focus line grounded in the charter (may be empty). */ readonly focus: string; /** Evidence-backed authority tokens, in {@link AUTHORITY_ORDER}. */ readonly authority: readonly AgentAuthority[]; } /** One `domain → agent` routing hint. */ export interface RoutingHintEntry { /** Sanitized domain (work type or module path). */ readonly domain: string; /** Sanitized roster name(s) this domain routes to. */ readonly routeTo: string; /** Where the hint came from. */ readonly source: 'work-type' | 'module'; } /** A team-level capability the squad can demonstrably perform. */ export interface TeamCapabilityEntry { /** Stable identifier (kebab-case). */ readonly id: string; /** Human label used in the rendered block. */ readonly label: string; /** Roster names providing it, in roster order. */ readonly agents: readonly string[]; } /** The machine-readable form of the generated block. */ export interface TeamCapabilityProfile { readonly schema: number; readonly specialists: readonly SpecialistEntry[]; readonly taskTypes: readonly string[]; readonly routingHints: readonly RoutingHintEntry[]; /** Capabilities with at least one agent behind them. */ readonly capabilities: readonly TeamCapabilityEntry[]; /** Vocabulary entries with zero evidence — the "cannot" list. */ readonly absentCapabilities: readonly string[]; /** True when there is no roster at all (uncast squad). */ readonly empty: boolean; } /** Raw inputs for {@link buildTeamCapabilityProfile}. All optional. */ export interface TeamCapabilityInput { /** Contents of `.squad/team.md`. */ readonly teamMarkdown?: string; /** Contents of `.squad/routing.md`. */ readonly routingMarkdown?: string; /** Charter markdown keyed by agent directory slug or roster name. */ readonly charters?: Readonly>; /** Parsed `.squad/casting/registry.json`, used to drop retired agents. */ readonly registry?: unknown; } /** * Make an untrusted metadata string safe to embed inside a markdown table * cell of the coordinator prompt. * * Neutralizes, in order: invisible/bidi and control characters, HTML comments * and tags, line breaks, code fences, table pipes, leading markdown * structure characters, and known prompt-injection phrasings. Finally clamps * the length so a hostile charter cannot blow the prompt budget. * * @param raw - Any value; non-strings collapse to an empty string. * @param maxLength - Maximum rendered length (ellipsis added when clamped). * @returns A single-line, structure-safe string. */ export declare function sanitizeMetadataText(raw: unknown, maxLength?: number): string; /** * Build the machine-readable capability profile from raw squad state. * * Handles missing, empty and malformed inputs by degrading to an explicitly * empty profile rather than throwing. * * @param input - Raw markdown/registry inputs. * @returns Deterministic {@link TeamCapabilityProfile}. */ export declare function buildTeamCapabilityProfile(input?: TeamCapabilityInput): TeamCapabilityProfile; /** * Render the marker-delimited block for a profile. * * The format is stable and part of the contract: heading text, table headers * and column order may only change with a {@link TEAM_CAPABILITIES_SCHEMA} bump. * * @param profile - Profile from {@link buildTeamCapabilityProfile}. * @returns The full block including BEGIN/END markers, no trailing newline. */ export declare function renderTeamCapabilitiesBlock(profile: TeamCapabilityProfile): string; /** * Build and render in one step. * * @param input - Raw markdown/registry inputs. * @returns The rendered block. */ export declare function generateTeamCapabilitiesBlock(input?: TeamCapabilityInput): string; /** * Replace the generated region with a neutral placeholder-free gap. * * Used by the upgrade path so a regenerated block is not mistaken for a user * customization when the installed file is diffed against the template. * * @param markdown - Agent-file contents. * @returns The contents with the generated region removed. */ export declare function stripTeamCapabilitiesBlock(markdown: string): string; /** * Splice a generated block into an agent file, preserving everything outside * the markers. Idempotent: applying the same block twice is a no-op. * * Insertion order when the markers are absent: * 1. before the first `## Init Mode` heading (where the placeholder lives), * 2. before the EOF canary comment (so canary golden checks keep passing), * 3. appended at the end. * * @param markdown - Existing agent-file contents. * @param block - Block produced by {@link renderTeamCapabilitiesBlock}. * @returns Updated contents. */ export declare function applyTeamCapabilitiesBlock(markdown: string, block: string): string; /** Options for {@link syncTeamCapabilities}. */ export interface SyncTeamCapabilitiesOptions { /** Absolute path to the `.squad` directory holding team state. */ readonly squadDir: string; /** Absolute path to `.github/agents/squad.agent.md`. */ readonly agentFile: string; /** Storage provider (defaults to the real filesystem). */ readonly storage?: StorageProvider; } /** Result of {@link syncTeamCapabilities}. */ export interface SyncTeamCapabilitiesResult { /** True when the agent file content changed on disk. */ readonly updated: boolean; /** The profile that was rendered (useful for diagnostics and tests). */ readonly profile: TeamCapabilityProfile; /** Reason the sync was skipped, when it was. */ readonly skipped?: 'missing-agent-file'; } /** * Regenerate the Team Capabilities block inside an installed * `.github/agents/squad.agent.md`. * * Safe to call on every init / upgrade / cast-composition change: it is a * no-op when the agent file is missing, and it only rewrites the file when * the rendered block actually differs. * * @param options - Paths and storage provider. * @returns Whether the file changed, plus the rendered profile. */ export declare function syncTeamCapabilities(options: SyncTeamCapabilitiesOptions): SyncTeamCapabilitiesResult; //# sourceMappingURL=team-capabilities.d.ts.map