Load an agent directory by convention. `instructions.md` is required.
(dir: impl AsRef<Path>)
| 135 | impl AgentDir { |
| 136 | /// Load an agent directory by convention. `instructions.md` is required. |
| 137 | pub fn load(dir: impl AsRef<Path>) -> Result<Self> { |
| 138 | let dir = dir.as_ref().to_path_buf(); |
| 139 | if !dir.is_dir() { |
| 140 | return Err(CodeError::Context(format!( |
| 141 | "agent directory not found: {}", |
| 142 | dir.display() |
| 143 | ))); |
| 144 | } |
| 145 | |
| 146 | // instructions.md (required) → role SLOT. Using a slot (not a raw system |
| 147 | // prompt) keeps the harness's BOUNDARIES/response-format/verification. |
| 148 | let instructions = std::fs::read_to_string(dir.join("instructions.md")).map_err(|e| { |
| 149 | CodeError::Context(format!( |
| 150 | "agent dir {} is missing required instructions.md: {e}", |
| 151 | dir.display() |
| 152 | )) |
| 153 | })?; |
| 154 | let prompt_slots = SystemPromptSlots { |
| 155 | role: Some(instructions.trim().to_string()), |
| 156 | ..Default::default() |
| 157 | }; |
| 158 | |
| 159 | // agent.acl (optional) → CodeConfig, else default. |
| 160 | let acl_path = dir.join("agent.acl"); |
| 161 | let mut config = if acl_path.is_file() { |
| 162 | CodeConfig::from_file(&acl_path)? |
| 163 | } else { |
| 164 | CodeConfig::default() |
| 165 | }; |
| 166 | |
| 167 | // skills/ → appended to skill_dirs (existing *.md format, zero adaptation). |
| 168 | let skills_dir = dir.join("skills"); |
| 169 | if skills_dir.is_dir() { |
| 170 | config.skill_dirs.push(skills_dir); |
| 171 | } |
| 172 | |
| 173 | let schedules = load_schedules(&dir.join("schedules"))?; |
| 174 | let tools = load_tools(&dir.join("tools"))?; |
| 175 | |
| 176 | Ok(Self { |
| 177 | dir, |
| 178 | config, |
| 179 | prompt_slots, |
| 180 | schedules, |
| 181 | tools, |
| 182 | }) |
| 183 | } |
| 184 | } |
| 185 | |
| 186 | /// Markdown files with a `<ext>` extension in `dir`, sorted by path. Returns an |