From 936a3453810470182421051301f6a0a83552bf89 Mon Sep 17 00:00:00 2001 From: Tom Boucher Date: Wed, 22 Jul 2026 01:25:41 -0400 Subject: [PATCH] =?UTF-8?q?feat(#2505):=20Phase=203=20=E2=80=94=20agent-sk?= =?UTF-8?q?ills=20fallback=20for=20non-dispatchable=20runtimes=20(#2521)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(#2454): PR 2 — cmdAgentSkills fallback reads installed agent prompt When no agent_skills config entry exists for a given agent type (the common case on AGENTS-native runtimes), cmdAgentSkills previously returned empty output. Workflows that inject ${AGENT_SKILLS_*} into subagent dispatch prompts then carried nothing — the persona was lost. The fallback: resolve the runtime's agents directory via checkAgentsInstalled and read /.md. The installed agent prompt content (now present for kimi-code via the flat-skills install layout) flows into the dispatch prompt so the persona survives even without explicit config opt-in. This is the reporter's suggested fix #2 from #2454. The fallback triggers for ALL runtimes (not just kimi-code) when no config entry exists — it is strictly additive (returns content the previous empty path could not). If the agent file is not found on disk, the block stays empty (same as before). * docs(changeset): Phase 3 agent-skills fallback Added (#2510) * docs(changeset): backfill PR #2521 for Phase 3 (#2510) --- .changeset/2505-3-agent-skills-fallback.md | 6 ++++ src/init.cts | 33 +++++++++++++++++++++- 2 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 .changeset/2505-3-agent-skills-fallback.md diff --git a/.changeset/2505-3-agent-skills-fallback.md b/.changeset/2505-3-agent-skills-fallback.md new file mode 100644 index 000000000..7c33cd8d5 --- /dev/null +++ b/.changeset/2505-3-agent-skills-fallback.md @@ -0,0 +1,6 @@ +--- +type: Added +pr: 2521 +--- + +**`gsd-tools query agent-skills ` returns the installed agent's prompt content on non-Claude runtimes** — previously, when a non-Claude runtime (kimi, kimi-code, opencode, kilo, etc.) had no explicit `agent_skills` config entry, `buildAgentSkillsBlock` returned empty and the `${AGENT_SKILLS_*}` workflow injection carried no persona. Phase 3 adds a fallback in `cmdAgentSkills`: on non-Claude runtimes, when the configured block is empty, resolve the runtime's agents directory via `checkAgentsInstalled(runtime)` and read `/.md` as the block. Gated to `runtime !== 'claude'` (Claude supports named dispatch and its `${AGENT_SKILLS_*}` contract is a skills-injection path, not a persona fallback). (#2510) diff --git a/src/init.cts b/src/init.cts index 395b6b0e8..6d0a0d6b7 100644 --- a/src/init.cts +++ b/src/init.cts @@ -2258,13 +2258,44 @@ function cmdAgentSkills( const projectRoot = findProjectRoot(cwd); const { config, source, degraded } = loadConfigResolved(projectRoot); const diagnostics = { warnings: [] as string[] }; - const block = buildAgentSkillsBlock( + let block = buildAgentSkillsBlock( config, agentType, projectRoot, diagnostics, ); + // #2454: Agent prompt fallback for AGENTS-native runtimes where named + // subagents are NOT dispatchable (kimi-code, kimi, opencode, kilo, etc.). + // On these runtimes, workflows inject ${AGENT_SKILLS_*} into the dispatch + // prompt of a built-in subagent (coder/explore/plan). If no + // model_profile_overrides or agent_skills config entry exists, the block + // is empty — but the agent's prompt CONTENT is installed on disk at the + // runtime's agents directory. Read it as a fallback so the persona survives + // the dispatch even without explicit config opt-in. + // + // GATED to non-claude runtimes: Claude Code supports named subagent dispatch + // and its ${AGENT_SKILLS_*} contract is a skills-injection path, not a + // persona fallback. Triggering the fallback for claude would change the + // documented "unconfigured → empty block" contract that agent-skills tests + // pin. + if (!block) { + const runtime = (config && (config['runtime'] as string)) || process.env['GSD_RUNTIME'] || 'claude'; + if (runtime !== 'claude') { + const agentCheck = checkAgentsInstalled(runtime) as unknown as { agents_dir?: string } | null; + const agentsDir = agentCheck?.agents_dir; + if (typeof agentsDir === 'string' && agentsDir.length > 0) { + const agentFile = path.join(agentsDir, `${agentType}.md`); + try { + const content = platformReadSync(agentFile); + if (content && content.length > 0) { + block = content; + } + } catch { /* agent file not found — fall through to empty block */ } + } + } + } + // Compute configured + reason for diagnostic output. const agentSkillsMap = (config && config['agent_skills'] && typeof config['agent_skills'] === 'object') ? config['agent_skills'] as Record