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