feat(#2096): migrate Antigravity onto EoS declarative adapter + permission-writer + MCP companion (ADR-1239)
Fold all antigravity literal branches into descriptor-driven reads: getConfigDirFromHome (→ configHome.kind 'dot-home-nested'), projectLocalHookPrefix (→ hostBehaviors.hookPathStyle 'raw'), applyAgentPathRewrites (→ noPathRewrite), getProjectInstructionFile (→ projectInstructionFile 'GEMINI.md'); removed the dead inline convertClaudeAgentToAntigravityAgent branch + dead isAntigravity destructures (antigravity is already on the descriptor-agents path). subagentToolkit flipped undocumented→full (Context7: antigravity.google/docs/cli/features); namedDispatch/nested/maxDepth/backgroundDispatch stay undocumented. Byte-identical golden parity for all 16 runtimes. UPGRADE 1 (permission-writer): permissionWriter 'antigravity' + configureAntigravityPermissions merges a scoped permissions.allow block (GSD's own tree + hooks) into Antigravity's settings.json — non-destructive, idempotent, symmetric uninstall. Added to VALID_PERMISSION_WRITERS + the FinishPermissionWriter union. UPGRADE 2 (MCP companion): configureAntigravityMcpConfig writes mcp_config.json registering the gsd-core companion MCP server (Gemini-successor mcpServers schema, best-effort — raw schema unpublished). Both writers dispatch from finishInstall. settings.json is golden-excluded (HOOK_CONFIG_FILES); mcp_config.json (portable, no absolute paths) is golden-tracked → only antigravity.json changes. Tests: declarative-reference-antigravity extended (source-grep guard across 4 modules, fail-closed for the 4 undocumented sub-axes, validator acceptance) + antigravity-upgrades (permission-writer + mcp_config live-install, idempotency, user-preservation). Matrix + ADR-1016 + capability-manifest + CONTEXT.md + connect-gsd-mcp-server docs updated; changeset (Changed). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -2743,9 +2743,11 @@ function normalizeAgentBodyForRuntime(content: string, runtime: string, cmdNames
|
||||
* ~/\.claude\b → normalizedPathPrefix
|
||||
* $HOME/\.claude\b → normalizedPathPrefix
|
||||
*
|
||||
* Skipped for copilot and antigravity (which do NOT do path rewrites in the
|
||||
* inline loop). NO stamp (_stampNonClaudeRuntimeDefaults) — agents are NOT
|
||||
* stamped in the inline loop.
|
||||
* Skipped for copilot (hardcoded — #2099 will fold it) and for any runtime
|
||||
* that declares `hostBehaviors.noPathRewrite` (descriptor-driven, ADR-1239 /
|
||||
* #2096 — folds the prior hardcoded `runtime === 'antigravity'` literal;
|
||||
* Antigravity does NOT do path rewrites in the inline loop). NO stamp
|
||||
* (_stampNonClaudeRuntimeDefaults) — agents are NOT stamped in the inline loop.
|
||||
*
|
||||
* ADR-1235 §1: pre-converter cross-cutting for descriptor-driven agent pipeline.
|
||||
* Exported as `applyAgentPathRewrites` for testing and for injection into
|
||||
@@ -2754,10 +2756,10 @@ function normalizeAgentBodyForRuntime(content: string, runtime: string, cmdNames
|
||||
* @param content raw agent file content
|
||||
* @param runtime canonical runtime ID
|
||||
* @param pathPrefix trailing-slash path prefix (e.g. '$HOME/.cursor/')
|
||||
* @returns content with path-prefix rewrites applied (or unchanged for copilot/antigravity)
|
||||
* @returns content with path-prefix rewrites applied (or unchanged for copilot / noPathRewrite runtimes)
|
||||
*/
|
||||
function applyAgentPathRewrites(content: string, runtime: string, pathPrefix: string): string {
|
||||
if (runtime === 'copilot' || runtime === 'antigravity') return content;
|
||||
if (runtime === 'copilot' || _hostBehaviors(runtime).noPathRewrite === true) return content;
|
||||
const normalizedPathPrefix = pathPrefix.replace(/\/$/, '');
|
||||
content = content.replace(/~\/\.claude\//g, pathPrefix);
|
||||
content = content.replace(/\$HOME\/\.claude\//g, pathPrefix);
|
||||
|
||||
@@ -22,9 +22,12 @@
|
||||
* false for codex / copilot / kilo / cursor / windsurf / trae / cline / kimi (legacy exclusion list).
|
||||
* true for all other runtimes.
|
||||
* - `finishPermissionWriter` names the finishInstall-phase dedicated config writer:
|
||||
* 'opencode' → writes BOTH shared settings AND its own permissions file.
|
||||
* 'kilo' → writes only its own permissions file.
|
||||
* null → no dedicated permission writer.
|
||||
* 'opencode' → writes BOTH shared settings AND its own permissions file.
|
||||
* 'kilo' → writes only its own permissions file.
|
||||
* 'antigravity' → writes BOTH shared settings.json permissions.allow AND a
|
||||
* standalone mcp_config.json MCP companion profile (#2096
|
||||
* Phase B Upgrades 1+2).
|
||||
* null → no dedicated permission writer.
|
||||
*/
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||
@@ -45,7 +48,7 @@ type ConfigInstallSurface =
|
||||
| 'cursor-hooks-json'
|
||||
| 'profile-marker-only';
|
||||
|
||||
type FinishPermissionWriter = 'opencode' | 'kilo' | null;
|
||||
type FinishPermissionWriter = 'opencode' | 'kilo' | 'antigravity' | null;
|
||||
|
||||
type HooksSurface =
|
||||
| 'settings-json'
|
||||
|
||||
@@ -120,13 +120,26 @@ export function resolveRuntimeNameFromCandidates(...candidates: unknown[]): stri
|
||||
* Aliases are normalized via `canonicalizeRuntimeName` first, so inputs like
|
||||
* `codex-cli` resolve to `codex` → `AGENTS.md`. Replaces the prior codex-only
|
||||
* override in profile-output.cjs (#3163) which left AGENTS-native runtimes
|
||||
* (opencode/kilo/kimi) incorrectly emitting `.claude/CLAUDE.md`. Pure: no I/O.
|
||||
* (opencode/kilo/kimi) incorrectly emitting `.claude/CLAUDE.md`. Pure: no I/O
|
||||
* (the lazy `require` below reads a static generated module, not the disk).
|
||||
*
|
||||
* Descriptor-driven (ADR-1239 / #2096): antigravity's `GEMINI.md` is folded
|
||||
* from a hardcoded `canonical === 'antigravity'` literal into a read of
|
||||
* `runtime.hostBehaviors.projectInstructionFile`. claude/copilot stay
|
||||
* hardcoded (out of scope here) mirroring `getDirName` below, which already
|
||||
* lazy-`require`s `capability-registry.cjs` inside the function body to
|
||||
* avoid a circular dependency at module load.
|
||||
*/
|
||||
export function getProjectInstructionFile(runtime: unknown): string {
|
||||
const canonical = canonicalizeRuntimeName(runtime);
|
||||
if (canonical === 'claude') return '.claude/CLAUDE.md';
|
||||
if (canonical === 'copilot') return '.github/copilot-instructions.md';
|
||||
if (canonical === 'antigravity') return 'GEMINI.md';
|
||||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||
const { runtimes } = require('./capability-registry.cjs') as {
|
||||
runtimes: Record<string, { runtime?: { hostBehaviors?: { projectInstructionFile?: string } } } | undefined>;
|
||||
};
|
||||
const declared = canonical ? runtimes[canonical]?.runtime?.hostBehaviors?.projectInstructionFile : undefined;
|
||||
if (typeof declared === 'string' && declared.length > 0) return declared;
|
||||
// codex, opencode, kilo, kimi, AND unknown/future runtimes all default to
|
||||
// root AGENTS.md (the safe cross-agent instruction file).
|
||||
return 'AGENTS.md';
|
||||
|
||||
@@ -98,9 +98,17 @@ export function formatManagedHookScriptToken(scriptPath: string, opts: { platfor
|
||||
return JSON.stringify(scriptPath.replace(/\\/g, '/'));
|
||||
}
|
||||
|
||||
export function projectLocalHookPrefix({ runtime = 'claude', dirName }: { runtime?: string; dirName?: string | null }): string | undefined | null {
|
||||
export function projectLocalHookPrefix({ runtime: _runtime = 'claude', dirName, hookPathStyle }: { runtime?: string; dirName?: string | null; hookPathStyle?: string | null }): string | undefined | null {
|
||||
if (!dirName) return dirName;
|
||||
return (runtime === 'antigravity')
|
||||
// Descriptor-driven (ADR-1239 / #2096): folded from a hardcoded
|
||||
// `runtime === 'antigravity'` literal into the runtime's declared
|
||||
// `hostBehaviors.hookPathStyle`. Runtimes that always run project hooks
|
||||
// with the project dir as cwd (Antigravity today) declare 'raw' and get
|
||||
// the bare dirName; every other runtime keeps the $CLAUDE_PROJECT_DIR-
|
||||
// anchored prefix. `runtime` itself is now unused here but stays in the
|
||||
// signature for call-site/back-compat parity (kept `_`-prefixed to
|
||||
// satisfy no-unused-vars).
|
||||
return (hookPathStyle === 'raw')
|
||||
? dirName
|
||||
: `"$CLAUDE_PROJECT_DIR"/${dirName}`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user