feat(#2092): migrate Qwen Code onto EoS imperative adapter + native subagents + SubagentStart (ADR-1239)

Fold all runtime==='qwen'/isQwen logic branches (skill-priority frontmatter,
branding/path rewrites, legacy commands/gsd cleanup, hyphen-namespace
normalization, RUNTIME_CONTENT_DISPATCH, hooks-surface label) into
descriptor-driven runtime.hostBehaviors on capabilities/qwen/capability.json,
read via _hostBehaviors(). Shared claude/qwen/hermes legacy-migration branches
in install-engine.cts folded to descriptor flags (claude+hermes descriptors
updated; FALLBACK_HOST_BEHAVIORS.claude floored). Byte-identical golden parity
for qwen/hermes/claude(global+local).

UPGRADE 1: native .qwen/agents/*.md subagent projection — new agents
artifact-layout kind + convertClaudeAgentToQwenAgent converter (name +
description + tools YAML block list; color/model dropped). qwen routed onto
the descriptor-driven agents path (_DESCRIPTOR_AGENTS_RUNTIMES).
UPGRADE 2: SubagentStart hook wired into extendedHookEvents + the
descriptor-gated hook-writer loop (activates only for qwen).

Tests: qwen-imperative-reference (adapter/axes/fail-closed/hostBehaviors +
no runtime==='qwen' source-grep across 4 files) + qwen-upgrades (agents file
validity + SubagentStart mirrors SubagentStop, descriptor-gated). Docs matrix
+ how-to updated; changeset added.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Tom Boucher
2026-07-10 14:46:59 -04:00
parent 91a68ae8e6
commit c6ce110efa
24 changed files with 819 additions and 172 deletions

View File

@@ -107,6 +107,31 @@ const CURSOR_MANAGED_EVENTS = CURSOR_HOOK_EVENTS;
const GSD_AGENTS_MD_MARKER = '<!-- GSD Configuration — managed by gsd-core installer -->';
const GSD_AGENTS_MD_CLOSE_MARKER = '<!-- End GSD Configuration -->';
// ---------------------------------------------------------------------------
// Descriptor-driven runtime title lookup (ADR-1239 / #2092)
// ---------------------------------------------------------------------------
/**
* Console-log label for a runtime, sourced from the capability registry's
* `title` field (capabilities/<runtime>/capability.json). Folded from a
* hardcoded `runtime === 'qwen' ? 'Qwen Code' : runtime === 'claude' ?
* 'Claude Code' : runtime` ternary — cosmetic (log text) only, but resolves
* to the same 'Qwen Code' / 'Claude Code' values for those two runtimes.
* Falls back to the raw runtime id if the registry can't be loaded or the
* runtime has no title.
*/
function _capabilityTitle(runtime: string): string {
try {
// eslint-disable-next-line @typescript-eslint/no-require-imports
const reg = require('./capability-registry.cjs') as {
runtimes?: Record<string, { title?: string } | undefined>;
};
return reg?.runtimes?.[runtime]?.title || runtime;
} catch {
return runtime;
}
}
// ---------------------------------------------------------------------------
// atomicWriteFileSync — shared canonical implementation.
//
@@ -1548,12 +1573,18 @@ function applySettingsJsonHooks(settings: any, opts: ApplySettingsJsonHooksOpts)
console.warn(` ${yellow}⚠${reset} Skipped phase boundary hook — Bash executable path unavailable (#3393)`);
}
// ── Extended hook events: SubagentStop / Stop / PreCompact (#788 + #770) ──
// Claude Code (since #770) and Qwen Code (since #788) both support these
// three lifecycle events. Wire gsd-context-monitor so agents get context-
// headroom warnings at subagent completion, model stop, and pre-compaction
// (the most critical moment to surface headroom info).
// ── Extended hook events: SubagentStop / Stop / PreCompact / SubagentStart
// (#788 + #770 + #2092) ────────────────────────────────────────────────
// Claude Code (since #770) and Qwen Code (since #788) both support the
// SubagentStop / Stop / PreCompact lifecycle events. Qwen Code additionally
// supports SubagentStart (#2092 Phase B, Upgrade 2). Wire gsd-context-
// monitor so agents get context-headroom warnings at subagent start,
// subagent completion, model stop, and pre-compaction (the most critical
// moment to surface headroom info).
//
// SubagentStart — subagent lifecycle start (context headroom tracking;
// qwen-only today — no other runtime declares it in
// extendedHookEvents)
// SubagentStop — subagent lifecycle completion (context headroom tracking)
// Stop — model stop / final-response moment (context headroom)
// PreCompact — fires before conversation compaction (most critical
@@ -1563,11 +1594,15 @@ function applySettingsJsonHooks(settings: any, opts: ApplySettingsJsonHooksOpts)
// user prompt text, not a tool invocation, so gsd-prompt-guard (which
// exits unless tool_name is Write/Edit) would be a silent no-op. A
// dedicated handler for UserPromptSubmit is deferred to a follow-on issue.
// SubagentStop, Stop, PreCompact — route through the context monitor.
// Guard is now descriptor-driven: only events present in extendedEvents are wired.
// SubagentStart, SubagentStop, Stop, PreCompact — route through the context monitor.
// Guard is descriptor-driven: only events present in extendedEvents are wired,
// so this loop is a no-op for every runtime that doesn't list SubagentStart.
{
const runtimeLabel = runtime === 'qwen' ? 'Qwen Code' : runtime === 'claude' ? 'Claude Code' : runtime;
for (const event of ['SubagentStop', 'Stop', 'PreCompact']) {
// Descriptor-driven (ADR-1239 / #2092): folded from a hardcoded
// `runtime === 'qwen' ? ... : ...` ternary into a capability-title
// lookup (see _capabilityTitle above).
const runtimeLabel = _capabilityTitle(runtime);
for (const event of ['SubagentStop', 'Stop', 'PreCompact', 'SubagentStart']) {
if (!extendedEvents.includes(event)) continue;
if (!settings.hooks[event]) {
settings.hooks[event] = [];
@@ -1591,7 +1626,7 @@ function applySettingsJsonHooks(settings: any, opts: ApplySettingsJsonHooksOpts)
}
}
}
// ── end SubagentStop / Stop / PreCompact events ────────────────────────────
// ── end SubagentStop / Stop / PreCompact / SubagentStart events ────────────
// ── Extended hook events (#776; Gemini runtime removed #1928) ──────────────
// The Gemini-3-backend dialect exposes several hook events beyond