diff --git a/.changeset/proud-sloths-glide.md b/.changeset/proud-sloths-glide.md index f96829ab8..5ab590c3d 100644 --- a/.changeset/proud-sloths-glide.md +++ b/.changeset/proud-sloths-glide.md @@ -2,4 +2,4 @@ type: Fixed pr: 1574 --- -**OpenCode and other AGENTS-native runtimes now get a root `AGENTS.md` from `/gsd:new-project`** — the workflow hardcoded a codex-only branch that sent every other runtime to `.claude/CLAUDE.md`, a location OpenCode never loads. A shared `getProjectInstructionFile(runtime)` policy (claude→`.claude/CLAUDE.md`, codex/opencode/kilo/kimi→`AGENTS.md`, copilot→`copilot-instructions.md`, antigravity/gemini→`GEMINI.md`) is now the single source of truth consumed by both the new-project workflow and the generate-claude-md path, with a parity test guarding drift. +**OpenCode and other AGENTS-native runtimes now get a root `AGENTS.md` from `/gsd:new-project`** — the workflow hardcoded a codex-only branch that sent every other runtime to `.claude/CLAUDE.md`, a location OpenCode never loads. A shared `getProjectInstructionFile(runtime)` policy (claude→`.claude/CLAUDE.md`, codex/opencode/kilo/kimi→`AGENTS.md`, copilot→`.github/copilot-instructions.md`, antigravity/gemini→`GEMINI.md`) is now the single source of truth consumed by both the new-project workflow and the generate-claude-md path, with a parity test guarding drift. diff --git a/gsd-core/workflows/new-project.md b/gsd-core/workflows/new-project.md index c887ec11c..b9042e331 100644 --- a/gsd-core/workflows/new-project.md +++ b/gsd-core/workflows/new-project.md @@ -1533,7 +1533,7 @@ PHASE1_HAS_UI=$(echo "$PHASE1_SECTION" | grep -qi "UI hint.*yes" && echo "true" - `.planning/REQUIREMENTS.md` - `.planning/ROADMAP.md` - `.planning/STATE.md` -- `$INSTRUCTION_FILE` (runtime-derived via the shared `getProjectInstructionFile` policy: `AGENTS.md` for codex/opencode/kilo/kimi, `copilot-instructions.md` for copilot, `GEMINI.md` for gemini/antigravity, `.claude/CLAUDE.md` for claude) +- `$INSTRUCTION_FILE` (runtime-derived via the shared `getProjectInstructionFile` policy: `AGENTS.md` for codex/opencode/kilo/kimi, `.github/copilot-instructions.md` for copilot, `GEMINI.md` for gemini/antigravity, `.claude/CLAUDE.md` for claude) @@ -1555,7 +1555,7 @@ PHASE1_HAS_UI=$(echo "$PHASE1_SECTION" | grep -qi "UI hint.*yes" && echo "true" - [ ] ROADMAP.md created with phases, requirement mappings, success criteria - [ ] STATE.md initialized - [ ] REQUIREMENTS.md traceability updated -- [ ] `$INSTRUCTION_FILE` generated with GSD workflow guidance (runtime-derived via the shared `getProjectInstructionFile` policy — `AGENTS.md` for codex/opencode/kilo/kimi, `copilot-instructions.md` for copilot, `GEMINI.md` for gemini/antigravity, `.claude/CLAUDE.md` for claude; an existing hand-crafted file without GSD markers is left untouched unless `--force`) +- [ ] `$INSTRUCTION_FILE` generated with GSD workflow guidance (runtime-derived via the shared `getProjectInstructionFile` policy — `AGENTS.md` for codex/opencode/kilo/kimi, `.github/copilot-instructions.md` for copilot, `GEMINI.md` for gemini/antigravity, `.claude/CLAUDE.md` for claude; an existing hand-crafted file without GSD markers is left untouched unless `--force`) - [ ] User knows next step is `/gsd:discuss-phase 1` **Atomic commits:** Each phase commits its artifacts immediately. If context is lost, artifacts persist. diff --git a/src/profile-output.cts b/src/profile-output.cts index 21fbac519..f0520d577 100644 --- a/src/profile-output.cts +++ b/src/profile-output.cts @@ -1131,7 +1131,7 @@ function cmdGenerateClaudeMd(cwd: string, options: CmdGenerateClaudeMdOptions, r // new-project.md bash workflow via `gsd-tools query // project-instruction-file`). Previously this was a codex-only override // (#3163) that left AGENTS-native runtimes (opencode/kilo/kimi) emitting - // CLAUDE.md; copilot now resolves to copilot-instructions.md, and + // CLAUDE.md; copilot now resolves to .github/copilot-instructions.md, and // antigravity/gemini to GEMINI.md. GSD_RUNTIME env var takes precedence // over config.runtime, mirroring detectRuntime(). // diff --git a/src/runtime-name-policy.cts b/src/runtime-name-policy.cts index 3f6e21ee4..84d45233b 100644 --- a/src/runtime-name-policy.cts +++ b/src/runtime-name-policy.cts @@ -101,10 +101,21 @@ export function resolveRuntimeNameFromCandidates(...candidates: unknown[]): stri * * claude → .claude/CLAUDE.md * codex, opencode, kilo, kimi → AGENTS.md - * copilot → copilot-instructions.md + * copilot → .github/copilot-instructions.md * antigravity, gemini → GEMINI.md * unknown / future runtimes → AGENTS.md (safe cross-agent default) * + * Source-of-truth references for each runtime's read path: + * - copilot: GitHub Docs — repository-wide custom instructions are read ONLY + * from `.github/copilot-instructions.md`; a root `copilot-instructions.md` + * is not a read path. `AGENTS.md` is also read (agent instructions). + * https://docs.github.com/en/copilot/how-tos/configure-custom-instructions/add-repository-instructions + * (Installer parity: runtime-config-adapter-registry.cts installSurface + * 'copilot-instructions' writes the same `.github/copilot-instructions.md`.) + * - codex/opencode/kilo/kimi: AGENTS.md is the documented cross-agent + * instruction file (agentsmd/agents.md convention). + * - antigravity/gemini: GEMINI.md is Gemini CLI's contextFileName. + * * 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 @@ -113,7 +124,7 @@ export function resolveRuntimeNameFromCandidates(...candidates: unknown[]): stri export function getProjectInstructionFile(runtime: unknown): string { const canonical = canonicalizeRuntimeName(runtime); if (canonical === 'claude') return '.claude/CLAUDE.md'; - if (canonical === 'copilot') return 'copilot-instructions.md'; + if (canonical === 'copilot') return '.github/copilot-instructions.md'; if (canonical === 'antigravity' || canonical === 'gemini') return 'GEMINI.md'; // codex, opencode, kilo, kimi, AND unknown/future runtimes all default to // root AGENTS.md (the safe cross-agent instruction file). diff --git a/tests/runtime-name-policy.test.cjs b/tests/runtime-name-policy.test.cjs index 67cfefe2b..43a9adacb 100644 --- a/tests/runtime-name-policy.test.cjs +++ b/tests/runtime-name-policy.test.cjs @@ -94,8 +94,8 @@ describe('runtime-name-policy getProjectInstructionFile (#1529)', () => { assert.strictEqual(getProjectInstructionFile('kimi'), 'AGENTS.md'); }); - test('copilot maps to copilot-instructions.md', () => { - assert.strictEqual(getProjectInstructionFile('copilot'), 'copilot-instructions.md'); + test('copilot maps to .github/copilot-instructions.md (GitHub docs read path)', () => { + assert.strictEqual(getProjectInstructionFile('copilot'), '.github/copilot-instructions.md'); }); test('gemini maps to GEMINI.md', () => { @@ -121,6 +121,6 @@ describe('runtime-name-policy getProjectInstructionFile (#1529)', () => { // gemini-cli is an alias for gemini. assert.strictEqual(getProjectInstructionFile('gemini-cli'), 'GEMINI.md'); // github-copilot is an alias for copilot. - assert.strictEqual(getProjectInstructionFile('github-copilot'), 'copilot-instructions.md'); + assert.strictEqual(getProjectInstructionFile('github-copilot'), '.github/copilot-instructions.md'); }); });