|
1 | 1 | import * as os from "os"; |
2 | 2 | import * as path from "path"; |
3 | 3 | import type { WorkspaceMetadata } from "@/types/workspace"; |
4 | | -import { gatherInstructionSets, readInstructionSet, INSTRUCTION_FILE_NAMES } from "@/utils/main/instructionFiles"; |
| 4 | +import { readInstructionSet, INSTRUCTION_FILE_NAMES } from "@/utils/main/instructionFiles"; |
5 | 5 | import { extractModeSection } from "@/utils/main/markdown"; |
6 | 6 | import type { Runtime } from "@/runtime/Runtime"; |
7 | 7 | import { readFileString } from "@/utils/runtime/helpers"; |
@@ -96,13 +96,13 @@ async function readInstructionSetFromRuntime( |
96 | 96 | /** |
97 | 97 | * Builds a system message for the AI model by combining multiple instruction sources. |
98 | 98 | * |
99 | | - * Instruction sources are layered in this order: |
100 | | - * 1. Global instructions: ~/.cmux/AGENTS.md (+ AGENTS.local.md) |
101 | | - * 2. Workspace instructions: <workspacePath>/AGENTS.md (+ AGENTS.local.md) - if exists |
102 | | - * 3. Project instructions: <projectPath>/AGENTS.md (+ AGENTS.local.md) - fallback if workspace doesn't have one |
103 | | - * 4. Mode-specific context (if mode provided): Extract a section titled "Mode: <mode>" |
104 | | - * (case-insensitive) from the instruction file. We search at most one section in |
105 | | - * precedence order: workspace instructions first, then project, then global instructions. |
| 99 | + * Instruction sources are layered as follows: |
| 100 | + * 1. Global instructions: ~/.cmux/AGENTS.md (+ AGENTS.local.md) - always included |
| 101 | + * 2. Context instructions: EITHER workspace OR project AGENTS.md (not both) |
| 102 | + * - Workspace: <workspacePath>/AGENTS.md (+ AGENTS.local.md) - if exists (read via runtime) |
| 103 | + * - Project: <projectPath>/AGENTS.md (+ AGENTS.local.md) - fallback if workspace doesn't exist |
| 104 | + * 3. Mode-specific context (if mode provided): Extract a section titled "Mode: <mode>" |
| 105 | + * (case-insensitive) from the instruction file. Priority: context instructions, then global. |
106 | 106 | * |
107 | 107 | * Each instruction file location is searched for in priority order: |
108 | 108 | * - AGENTS.md |
@@ -138,43 +138,34 @@ export async function buildSystemMessage( |
138 | 138 | const systemDir = getSystemDirectory(); |
139 | 139 | const projectDir = metadata.projectPath; |
140 | 140 |
|
141 | | - // Read workspace instructions using runtime (may be remote for SSH) |
142 | | - // Try to read AGENTS.md from workspace directory first |
| 141 | + // Layer 1: Global instructions (always included) |
| 142 | + const globalInstructions = await readInstructionSet(systemDir); |
| 143 | + |
| 144 | + // Layer 2: Workspace OR Project instructions (not both) |
| 145 | + // Try workspace first (via runtime, may be remote for SSH) |
| 146 | + // Fall back to project if workspace doesn't have AGENTS.md |
143 | 147 | const workspaceInstructions = await readInstructionSetFromRuntime(runtime, workspacePath); |
| 148 | + const projectInstructions = workspaceInstructions ? null : await readInstructionSet(projectDir); |
144 | 149 |
|
145 | | - // Gather instruction sets from global and project directories (always local) |
146 | | - // Note: We gather from both systemDir and projectDir, but workspace is handled separately |
147 | | - const localInstructionDirs = [systemDir, projectDir]; |
148 | | - const localInstructionSegments = await gatherInstructionSets(localInstructionDirs); |
149 | | - |
150 | | - // Combine all instruction sources |
151 | | - // Priority: global, workspace (if found), project (as fallback) |
152 | | - const allSegments = [...localInstructionSegments]; |
153 | | - if (workspaceInstructions) { |
154 | | - // Insert workspace instructions after global (index 0) but before project |
155 | | - allSegments.splice(1, 0, workspaceInstructions); |
156 | | - } |
157 | | - const customInstructions = allSegments.join("\n\n"); |
| 150 | + // Combine instruction sources |
| 151 | + // Result: global + (workspace OR project) |
| 152 | + const instructionSegments = [globalInstructions, workspaceInstructions ?? projectInstructions].filter( |
| 153 | + Boolean |
| 154 | + ); |
| 155 | + const customInstructions = instructionSegments.join("\n\n"); |
158 | 156 |
|
159 | 157 | // Look for a "Mode: <mode>" section inside instruction sets |
160 | | - // Priority: workspace instructions, then project, then global |
| 158 | + // Priority: workspace (or project fallback), then global |
| 159 | + // We only check the workspace OR project instructions, not both |
161 | 160 | // This behavior is documented in docs/instruction-files.md - keep both in sync when changing. |
162 | 161 | let modeContent: string | null = null; |
163 | 162 | if (mode) { |
164 | | - if (workspaceInstructions) { |
165 | | - modeContent = extractModeSection(workspaceInstructions, mode); |
166 | | - } |
167 | | - if (!modeContent) { |
168 | | - const projectInstructions = await readInstructionSet(projectDir); |
169 | | - if (projectInstructions) { |
170 | | - modeContent = extractModeSection(projectInstructions, mode); |
171 | | - } |
| 163 | + const contextInstructions = workspaceInstructions ?? projectInstructions; |
| 164 | + if (contextInstructions) { |
| 165 | + modeContent = extractModeSection(contextInstructions, mode); |
172 | 166 | } |
173 | | - if (!modeContent) { |
174 | | - const globalInstructions = await readInstructionSet(systemDir); |
175 | | - if (globalInstructions) { |
176 | | - modeContent = extractModeSection(globalInstructions, mode); |
177 | | - } |
| 167 | + if (!modeContent && globalInstructions) { |
| 168 | + modeContent = extractModeSection(globalInstructions, mode); |
178 | 169 | } |
179 | 170 | } |
180 | 171 |
|
|
0 commit comments