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
393 lines • 15.6 kB
TypeScript
/**
* Project-Level AIWG Config
*
* Manages `.aiwg/aiwg.config` — the project-level record of:
* - Which AI provider toolchains this project targets
* - Which frameworks/addons are deployed (with uninstall metadata)
* - User-defined scripts callable via `aiwg run`
*
* @implements #621
*/
import type { ProjectLocalType } from '../extensions/manifest.js';
/**
* Artifact counts for one provider deployment
*/
export interface DeployedArtifactCounts {
agents: number;
commands: number;
skills: number;
rules: number;
}
/**
* One entry in the `installed` map
*/
export interface InstalledEntry {
/** Deployed version (CalVer or semver) */
version: string;
/**
* Source of the deployment:
* "bundled" — came from the npm package
* "cache" — came from ~/.cache/aiwg/packages/ (#557)
* "project-local" — came from .aiwg/{extensions,addons,frameworks,plugins}/<id>/ (#1035)
* git URL — direct source URL
*/
source: 'bundled' | 'cache' | 'project-local' | string;
/** ISO-8601 timestamp of last deployment */
installedAt: string;
/** Provider → artifact counts */
deployedTo: Record<string, DeployedArtifactCounts>;
/** SHA-256 of manifest.json at deploy time; used for stale detection */
manifestHash?: string;
/**
* Project-local-only fields (set when `source === 'project-local'`).
*
* Per @.aiwg/architecture/adr-unified-registry-shape.md (ADR companion to
* #1035). These three fields MUST be present together when source is
* `'project-local'` and SHOULD be absent otherwise.
*/
/** Path of the bundle directory relative to project root (e.g., ".aiwg/extensions/foo/"). */
localPath?: string;
/** Bundle type from the manifest. */
localType?: ProjectLocalType;
/** Schema version of the manifest.json this entry was written from (currently `'1'`). */
manifestVersion?: string;
/**
* Hashes of source artifacts at deploy time, keyed by source-relative path
* (e.g., "rules/my-rule.md", "skills/my-skill/SKILL.md"). Used by
* `aiwg remove` to detect pristine vs. mutated vs. replaced deployed
* files per the design at @.aiwg/architecture/design-aiwg-remove-revert.md.
*
* Optional — older entries without this field fall back to "always-prompt"
* remove behavior until the next `aiwg use` re-records them.
*
* @implements #1037
*/
artifactHashes?: Record<string, string>;
}
/**
* One secondary remote: a mirror, fork base, or publishing target.
*/
export interface SecondaryRemote {
/** Must match a name from `git remote` */
name: string;
/** Free-form tag (mirror | upstream | publish | replica | …) */
purpose?: string;
/** Hint to release workflows: push tags here on stable cuts */
push_on_release?: boolean;
}
/**
* Repo origin topology — declares which remote is primary (CI / issues / PRs)
* and which are secondary (mirrors, publishing targets).
*
* @implements #994
*/
export interface RemotesConfig {
/** git remote name driving CI / PRs by default. Defaults to "origin". */
primary?: string;
/** Where issues live. Defaults to `primary`. */
issue_tracker?: string;
/** Where CI runs. Defaults to `primary`. */
ci?: string;
/** Mirrors, fork bases, publishing targets. */
secondary?: SecondaryRemote[];
}
/**
* Resolved remote topology — every field guaranteed to be set.
* Returned by {@link resolveRemotes}.
*/
export interface ResolvedRemotes {
primary: string;
issue_tracker: string;
ci: string;
secondary: SecondaryRemote[];
}
/**
* Top-level shape of .aiwg/aiwg.config
*/
export interface AiwgConfig {
$schema?: string;
version: '1';
/**
* AI provider toolchains this project targets.
* `aiwg use <framework>` with no --provider flag deploys to ALL of these.
*/
providers: string[];
/**
* Frameworks and addons currently deployed.
* Keyed by the name passed to `aiwg use`.
*/
installed: Record<string, InstalledEntry>;
/**
* User-defined scripts, run via `aiwg run <name>`.
* Executed with `sh -c "<command>"` (or `cmd /c` on Windows).
*/
scripts: Record<string, string>;
/**
* Repo origin topology. Optional — when absent, agents treat `origin` as primary.
* @implements #994
*/
remotes?: RemotesConfig;
/**
* Repo control / delivery policy — how AIWG agents are expected to ship code.
* Optional — when absent, agents fall back to the conservative defaults
* applied by `resolveDelivery()`.
* @implements #995
*/
delivery?: DeliveryConfig;
/**
* Provider-scoped parallelism caps — limits how many concurrent subagents,
* Ralph loops, and Mission Control missions agents may spawn. Composes with
* (takes the minimum of) `context-budget` rule caps and `rlm-context-management`
* Rule 8's 7-agent hard cap. Optional — when absent, agents fall back to
* provider-specific defaults applied by `resolveParallelism()`.
* @implements #1359
*/
parallelism?: ParallelismConfig;
}
/**
* How agents should ship code — modes:
* - `direct` : commit & push straight to default_branch
* - `feature-branch` : create a branch and push it, but don't open a PR
* - `pr-required` : feature branch + PR via the resolved primary remote
*/
export type DeliveryMode = 'direct' | 'feature-branch' | 'pr-required';
/**
* Merge style preference; matches the values Gitea/GitHub/GitLab APIs accept.
*/
export type MergeStyle = 'rebase-merge' | 'squash' | 'merge' | 'fast-forward-only';
/**
* Force-push policy:
* - `never` : agents may never force-push
* - `own-branch-only` : OK on the agent's own feature branch, never to main
* - `allowed` : escape hatch for tooling that needs it
*/
export type ForcePushPolicy = 'never' | 'own-branch-only' | 'allowed';
/**
* Branch-naming convention. `{issue}` and `{slug}` are interpolated by skills.
*/
export interface BranchNaming {
prefix_by_type?: Partial<Record<'feat' | 'fix' | 'docs' | 'chore' | 'refactor' | 'test', string>>;
}
/**
* Repo control policy — see DeliveryMode for the high-level shape. Every field
* is optional; sensible defaults applied via {@link resolveDelivery}.
*
* @implements #995
*/
export interface DeliveryConfig {
mode?: DeliveryMode;
default_branch?: string;
branch_naming?: BranchNaming;
merge_style?: MergeStyle;
delete_branch_on_merge?: boolean;
/** When true, agents must wait for CI green before declaring done. */
require_ci_green?: boolean;
require_signed_commits?: boolean;
force_push_policy?: ForcePushPolicy;
/** Include "Closes #N" / "Fixes #N" in PR body when an issue is referenced. */
auto_close_issues?: boolean;
/** Post AL CYCLE status comments to issue threads from address-issues loops. */
issue_comment_on_cycle?: boolean;
}
/**
* Resolved delivery policy with all defaults applied. Returned by
* {@link resolveDelivery}.
*/
export interface ResolvedDelivery {
mode: DeliveryMode;
default_branch: string;
branch_naming: Required<BranchNaming>;
merge_style: MergeStyle;
delete_branch_on_merge: boolean;
require_ci_green: boolean;
require_signed_commits: boolean;
force_push_policy: ForcePushPolicy;
auto_close_issues: boolean;
issue_comment_on_cycle: boolean;
}
/**
* Resolve the delivery policy with defaults applied.
*
* Defaults are intentionally conservative — they match what AIWG agents
* naturally do today (PR-required, rebase-merge, no force pushes, post issue
* comments) so that adding the schema doesn't shift behavior for existing
* projects.
*/
export declare function resolveDelivery(delivery: DeliveryConfig | undefined): ResolvedDelivery;
/**
* Provider-scoped parallelism cap — limits how many concurrent subagents,
* Ralph loops, and Mission Control missions agents may spawn. Designed to
* keep AIWG within the rate-limit envelope of the underlying model provider
* (Anthropic per-key TPM/RPM caps are the most-reported trigger).
*
* Composes with (effective limit = MIN of):
* - `parallelism.max_parallel_subagents` (this config)
* - `context-budget` rule's `AIWG_CONTEXT_WINDOW`-derived cap, if set
* - `rlm-context-management` Rule 8's 7-agent hard cap (RLM dispatches only)
* - The natural task decomposition (no point spawning 4 when only 2 subtasks exist)
*
* Every field is optional. Defaults applied via {@link resolveParallelism}.
*
* @implements #1359
*/
export interface ParallelismConfig {
/** Max concurrent subagents (Task dispatches, rlm-batch fan-outs). */
max_parallel_subagents?: number;
/** Max concurrent Ralph external loops (`aiwg agent-loop-ext`). */
max_parallel_ralph_loops?: number;
/** Max concurrent Mission Control missions (`aiwg mc dispatch`). */
max_parallel_mc_missions?: number;
/** Free-form note explaining why this cap was chosen (e.g., plan tier). */
rationale?: string;
}
/**
* Resolved parallelism caps with all defaults applied. Returned by
* {@link resolveParallelism}.
*/
export interface ResolvedParallelism {
max_parallel_subagents: number;
max_parallel_ralph_loops: number;
max_parallel_mc_missions: number;
rationale?: string;
}
/**
* Per-provider parallelism defaults. Conservative numbers for Anthropic-backed
* providers reflect Pro/Team-plan rate limits — operators on Enterprise tiers
* should bump via `aiwg config set --project parallelism.max_parallel_subagents N`.
*
* Sources for the numbers:
* - claude / claude-code: Anthropic per-key throttling at higher concurrency
* - codex / copilot / etc.: OpenAI / GitHub quotas are generally per-org and
* less aggressive at small fan-outs (10 is a safe middle ground)
* - hermes: MCP sidecar; rate-limit depends on upstream provider, operator
* should tune. Conservative 10 default.
* - unknown: conservative 4 default.
*/
export declare const PROVIDER_PARALLELISM_DEFAULTS: Record<string, ResolvedParallelism>;
/**
* Return the provider's parallelism defaults, or the conservative fallback
* when the provider is unknown.
*/
export declare function getProviderParallelismDefaults(provider: string | undefined): ResolvedParallelism;
/**
* Resolve the parallelism caps with provider-aware defaults applied. The
* primary provider drives the default — typically the first entry in the
* project's `providers` array.
*
* When `parallelism` has explicit values, they override the provider default
* field-by-field. When no provider is supplied (or it's not in the defaults
* map), the conservative 4-subagent fallback applies.
*
* @implements #1359
*/
export declare function resolveParallelism(parallelism: ParallelismConfig | undefined, primaryProvider?: string): ResolvedParallelism;
/**
* Provider tag for a given remote URL. Used by skills (issue-create,
* pr-review, commit-and-push) to pick the right CLI / MCP client when
* the operator didn't pass `--provider` explicitly.
*
* Recognized hosts:
* - github.com → 'github'
* - gitlab.com / gitlab.* → 'gitlab'
* - any host containing 'gitea' (or matching the typical Gitea path shape) → 'gitea'
*
* Returns 'unknown' for self-hosted instances we can't classify by host alone —
* callers should then prompt the operator or fall back to the configured
* AIWG provider list.
*
* @implements #997
*/
export declare function resolveRemoteProvider(remoteUrl: string): 'github' | 'gitlab' | 'gitea' | 'unknown';
/**
* Resolve the repo remote topology with defaults applied.
*
* Defaults:
* - `primary` defaults to "origin"
* - `issue_tracker` defaults to `primary`
* - `ci` defaults to `primary`
* - `secondary` defaults to `[]`
*
* Pass an absent or partial `remotes` block — every field comes back populated.
*/
export declare function resolveRemotes(remotes: RemotesConfig | undefined): ResolvedRemotes;
/**
* Valid provider names (mirrors PROVIDER_PATHS in use.ts)
*/
export declare const VALID_PROVIDERS: readonly ["claude", "factory", "codex", "opencode", "copilot", "cursor", "warp", "windsurf", "hermes", "openclaw"];
export type Provider = typeof VALID_PROVIDERS[number];
/**
* Empty config template.
*
* Includes an explicit `delivery` block defaulting to `pr-required`. The
* runtime default in {@link resolveDelivery} is the same, so this is purely
* for visibility — new projects ship with the policy written down so users
* can see what their agents will do, and switch via `aiwg config set` or
* the AIWG Steward agent without first having to discover the field exists.
*/
export declare function emptyConfig(providers?: string[]): AiwgConfig;
/**
* Resolve path to .aiwg/aiwg.config for a project directory
*/
export declare function getConfigPath(projectDir: string): string;
/**
* Resolve the project directory for a handler invocation.
*
* Precedence:
* 1. `--target <path>` or `--prefix <path>` flag in args
* 2. The HandlerContext `cwd`, if provided
* 3. `process.cwd()`
*
* All three variants existed scattered across handlers (#919 cleanup).
* Use this helper so we have one authoritative resolution.
*/
export declare function getProjectDir(ctx: {
cwd?: string;
} | undefined, args?: readonly string[]): string;
/**
* Read .aiwg/aiwg.config.
* Returns null if the file does not exist.
*/
export declare function readAiwgConfig(projectDir: string): Promise<AiwgConfig | null>;
/**
* Write .aiwg/aiwg.config, creating .aiwg/ if needed.
*/
export declare function writeAiwgConfig(projectDir: string, config: AiwgConfig): Promise<void>;
/**
* Update the `installed` record for a framework after a successful deployment.
* Returns the updated config (does not write to disk — caller must call writeAiwgConfig).
*/
export declare function updateInstalled(config: AiwgConfig, name: string, provider: string, counts: DeployedArtifactCounts, opts: {
version: string;
source: string;
manifestHash?: string;
/** Set when source === 'project-local'. Relative to project root. */
localPath?: string;
/** Set when source === 'project-local'. */
localType?: ProjectLocalType;
/** Set when source === 'project-local'. */
manifestVersion?: string;
/** Optional source-artifact hash map for project-local remove revert (#1037). */
artifactHashes?: Record<string, string>;
}): AiwgConfig;
/**
* Aggregate deployment counts across all installed frameworks for a given provider.
* Returns the totals for agents, commands, skills, and rules.
* If no provider is specified, uses the first configured provider.
*/
export declare function getDeploymentSummary(config: AiwgConfig, provider?: string): DeployedArtifactCounts;
/**
* Compute SHA-256 hash of a manifest.json file.
* Returns undefined if the file cannot be read.
*/
export declare function hashManifest(manifestPath: string): Promise<string | undefined>;
/**
* Scan actual deployment directories and populate `deployedTo` for any
* `installed` entries that have an empty `deployedTo` map.
*
* Called by `aiwg init` when migrating a project that already has frameworks
* deployed but whose config was created before deployment-tracking was added.
*
* @implements #721
*/
export declare function populateDeployedTo(config: AiwgConfig, projectDir: string): Promise<AiwgConfig>;
//# sourceMappingURL=aiwg-config.d.ts.map