feat(#2097): migrate Augment onto EoS declarative adapter + settings.json MCP companion (ADR-1239)

Fold augment's runtime-literal conversion branches onto descriptor-driven
hostBehaviors and delete dead code:
- Site A (_applyRuntimeRewrites case 'augment'): the 4 ~/.augment dot-dir
  regexes now derive from getDirName('augment') via escapeRegExp (byte-
  identical; getDirName('augment')==='.augment') — no runtime literal.
- Site B (applyRuntimeContentRewritesForCommandsInPlace): the
  `if (runtime==='augment')` markdown-converter branch now reads
  runtime.hostBehaviors.commandBodyConverter and dispatches through a local
  COMMAND_BODY_CONVERTERS map (degrade-closed on unknown/absent name).
- Deleted dead `claudeToAugmentTools` map (zero refs; orphaned by ADR-1508
  single-sourcing) and the unreachable `else if (isAugment)` agent-conversion
  branch (augment ∈ _DESCRIPTOR_AGENTS_RUNTIMES → gated out upstream).
- Incidental orphan cleanup (no-defer): removed the equally-unreachable
  `else if (isTrae)` agent-conversion arm left behind by trae's already-merged
  migration #2094 (trae ∈ _DESCRIPTOR_AGENTS_RUNTIMES, same upstream gate).
  copilot/windsurf/codebuddy arms are removed by their own pending migrations.

UPGRADE 3 (transport:mcp): register the GSD companion MCP server in Augment's
settings.json under mcpServers.gsd (Augment hosts MCP in settings.json, not a
standalone file). mergeGsdMcpServerIntoSettings mutates the in-memory settings
object finishInstall already writes (gated on hostBehaviors.mcpCompanion===
'settings-json'); non-destructive + idempotent; symmetric uninstall removal.
settings.json is golden-excluded, so no golden change. UPGRADE 1 (named/
background dispatch) + UPGRADE 2 (settings-json hook bus, Claude dialect)
were already live in production — this adds tests exercising both.

Golden: byte-identical for all 16 runtimes (folds preserve regex behavior;
MCP lives in golden-excluded settings.json) — verified by a real double-install
tree diff. Tests: declarative-reference-augment (adapter/axes/fail-closed/
undocumented-sub-axes + source-grep guard scoped to conversion-logic branches)
+ augment-upgrades (dispatch negotiation, hook-bus live install, MCP add/
idempotent/preserve/uninstall). Matrix + connect-gsd-mcp-server + a stale
install-on-your-runtime hook-ownership claim corrected; changeset (Changed).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Tom Boucher
2026-07-11 04:04:48 -04:00
parent 41d544248b
commit 17aa35fba7
10 changed files with 564 additions and 21 deletions

View File

@@ -1107,6 +1107,11 @@ function convertClaudeToAugmentMarkdown(content) {
return converted;
}
// #2097 (ADR-1239): command-body converters selected by descriptor
// (runtime.hostBehaviors.commandBodyConverter) instead of a runtime-name
// branch. Degrade-closed: unknown/absent name → no conversion.
const COMMAND_BODY_CONVERTERS = { convertClaudeToAugmentMarkdown };
function getAugmentSkillAdapterHeader(skillName) {
return `<augment_skill_adapter>
## A. Skill Invocation
@@ -2442,19 +2447,24 @@ function _applyRuntimeRewrites(content, runtime, pathPrefix, isGlobal = false, a
break;
}
case 'augment':
case 'augment': {
content = content.replace(/~\/\.claude\//g, pathPrefix);
content = content.replace(/\$HOME\/\.claude\//g, pathPrefix);
content = content.replace(/\.\/\.claude\//g, `./${dirName}/`);
content = content.replace(/~\/\.claude(?![\w-])/g, normalizedPathPrefix);
content = content.replace(/\$HOME\/\.claude(?![\w-])/g, normalizedPathPrefix);
content = content.replace(/\.\/\.claude(?![\w-])/g, `./${dirName}`);
content = content.replace(/~\/\.augment\//g, pathPrefix);
content = content.replace(/\$HOME\/\.augment\//g, pathPrefix);
content = content.replace(/~\/\.augment(?![\w-])/g, normalizedPathPrefix);
content = content.replace(/\$HOME\/\.augment(?![\w-])/g, normalizedPathPrefix);
// #2097: dot-dir self-references (~/.augment/…) → resolved prefix,
// dirName-derived (no runtime literal). getDirName('augment') resolves
// to '.augment', so this is byte-identical to the prior hardcoded regexes.
const _dd = escapeRegExp(dirName);
content = content.replace(new RegExp('~/' + _dd + '/', 'g'), pathPrefix);
content = content.replace(new RegExp('\\$HOME/' + _dd + '/', 'g'), pathPrefix);
content = content.replace(new RegExp('~/' + _dd + '(?![\\w-])', 'g'), normalizedPathPrefix);
content = content.replace(new RegExp('\\$HOME/' + _dd + '(?![\\w-])', 'g'), normalizedPathPrefix);
content = processAttribution(content, attribution);
break;
}
case 'trae':
content = content.replace(/~\/\.claude\//g, pathPrefix);
@@ -2631,8 +2641,11 @@ function applyRuntimeContentRewritesForCommandsInPlace(stagedDir, runtime, pathP
if (!entry.isFile() || !entry.name.endsWith('.md')) continue;
let content = fs.readFileSync(path.join(stagedDir, entry.name), 'utf8');
content = _applyRuntimeRewrites(content, runtime, pathPrefix, isGlobal, attribution);
if (runtime === 'augment') {
content = convertClaudeToAugmentMarkdown(content);
// #2097 (ADR-1239): descriptor-driven — commandBodyConverter name comes
// from runtime.hostBehaviors instead of a hardcoded runtime-name branch.
const _cmdConv = _hostBehaviors(runtime).commandBodyConverter;
if (_cmdConv && COMMAND_BODY_CONVERTERS[_cmdConv]) {
content = COMMAND_BODY_CONVERTERS[_cmdConv](content);
}
fs.writeFileSync(path.join(tempDir, entry.name), content);
}