@@ -78,8 +78,29 @@ impl SectionRegistry {
7878 }
7979
8080 /// Create a registry with all default section builders.
81+ ///
82+ /// Registers sections in order:
83+ /// 1. `env` (Static) — environment info (OS, cwd, date, model)
84+ /// 2. `tools` (Static) — available tool descriptions
85+ /// 3. `crab_md` (Static) — CRAB.md project instructions
86+ /// 4. `memory` (Static) — auto-memory content
87+ /// 5. `git` (Dynamic) — current git status
88+ /// 6. `skills` (Static) — available skill descriptions (placeholder)
89+ /// 7. `tips` (Static) — contextual tips (placeholder)
90+ /// 8. `custom` (Dynamic) — user custom instructions
8191 pub fn default_sections ( ) -> Self {
82- todo ! ( "Register: env, tools, memory, git, crab_md, skills, tips, custom" )
92+ let mut registry = Self :: new ( ) ;
93+
94+ registry. register ( Box :: new ( EnvSection ) ) ;
95+ registry. register ( Box :: new ( ToolsSection ) ) ;
96+ registry. register ( Box :: new ( CrabMdSection ) ) ;
97+ registry. register ( Box :: new ( MemorySection ) ) ;
98+ registry. register ( Box :: new ( GitSection ) ) ;
99+ registry. register ( Box :: new ( SkillsSection ) ) ;
100+ registry. register ( Box :: new ( TipsSection ) ) ;
101+ registry. register ( Box :: new ( CustomSection ) ) ;
102+
103+ registry
83104 }
84105
85106 /// Build all sections and assemble them, inserting the dynamic boundary marker.
@@ -131,6 +152,160 @@ impl Default for SectionRegistry {
131152 }
132153}
133154
155+ // ─── Default section builders ─────────────────────────────────────────
156+
157+ /// Environment section: OS, working directory, date, model info.
158+ struct EnvSection ;
159+
160+ impl SectionBuilder for EnvSection {
161+ fn name ( & self ) -> & ' static str {
162+ "env"
163+ }
164+
165+ fn cache_scope ( & self ) -> CacheScope {
166+ CacheScope :: Static
167+ }
168+
169+ fn build ( & self , ctx : & SectionContext ) -> Option < String > {
170+ let mut s = String :: with_capacity ( 256 ) ;
171+ s. push_str ( "# Environment\n " ) ;
172+ let _ = writeln ! ( s, "- Working directory: {}" , ctx. project_dir. display( ) ) ;
173+ let _ = writeln ! ( s, "- Platform: {}" , std:: env:: consts:: OS ) ;
174+ let _ = writeln ! ( s, "- Architecture: {}" , std:: env:: consts:: ARCH ) ;
175+ Some ( s)
176+ }
177+ }
178+
179+ /// Tools section: available tool descriptions.
180+ struct ToolsSection ;
181+
182+ impl SectionBuilder for ToolsSection {
183+ fn name ( & self ) -> & ' static str {
184+ "tools"
185+ }
186+
187+ fn cache_scope ( & self ) -> CacheScope {
188+ CacheScope :: Static
189+ }
190+
191+ fn build ( & self , ctx : & SectionContext ) -> Option < String > {
192+ if ctx. tool_descriptions . is_empty ( ) {
193+ return None ;
194+ }
195+ Some ( format ! ( "# Available Tools\n {}" , ctx. tool_descriptions) )
196+ }
197+ }
198+
199+ /// CRAB.md section: project-level instructions.
200+ struct CrabMdSection ;
201+
202+ impl SectionBuilder for CrabMdSection {
203+ fn name ( & self ) -> & ' static str {
204+ "crab_md"
205+ }
206+
207+ fn cache_scope ( & self ) -> CacheScope {
208+ CacheScope :: Static
209+ }
210+
211+ fn build ( & self , ctx : & SectionContext ) -> Option < String > {
212+ ctx. crab_md_content
213+ . map ( |content| format ! ( "# Project Instructions (CRAB.md)\n {content}" ) )
214+ }
215+ }
216+
217+ /// Memory section: auto-memory content from MEMORY.md and topic files.
218+ struct MemorySection ;
219+
220+ impl SectionBuilder for MemorySection {
221+ fn name ( & self ) -> & ' static str {
222+ "memory"
223+ }
224+
225+ fn cache_scope ( & self ) -> CacheScope {
226+ CacheScope :: Static
227+ }
228+
229+ fn build ( & self , ctx : & SectionContext ) -> Option < String > {
230+ ctx. memory_content
231+ . map ( |content| format ! ( "# Memory\n {content}" ) )
232+ }
233+ }
234+
235+ /// Git section: current git status (dynamic — changes per turn).
236+ struct GitSection ;
237+
238+ impl SectionBuilder for GitSection {
239+ fn name ( & self ) -> & ' static str {
240+ "git"
241+ }
242+
243+ fn cache_scope ( & self ) -> CacheScope {
244+ CacheScope :: Dynamic
245+ }
246+
247+ fn build ( & self , ctx : & SectionContext ) -> Option < String > {
248+ ctx. git_status
249+ . map ( |status| format ! ( "# Git Status\n {status}" ) )
250+ }
251+ }
252+
253+ /// Skills section: available skills (placeholder until skill system is built).
254+ struct SkillsSection ;
255+
256+ impl SectionBuilder for SkillsSection {
257+ fn name ( & self ) -> & ' static str {
258+ "skills"
259+ }
260+
261+ fn cache_scope ( & self ) -> CacheScope {
262+ CacheScope :: Static
263+ }
264+
265+ fn build ( & self , _ctx : & SectionContext ) -> Option < String > {
266+ // Skills will be populated when the skill system (Phase 8) is built
267+ None
268+ }
269+ }
270+
271+ /// Tips section: contextual tips (placeholder until tips system is built).
272+ struct TipsSection ;
273+
274+ impl SectionBuilder for TipsSection {
275+ fn name ( & self ) -> & ' static str {
276+ "tips"
277+ }
278+
279+ fn cache_scope ( & self ) -> CacheScope {
280+ CacheScope :: Static
281+ }
282+
283+ fn build ( & self , _ctx : & SectionContext ) -> Option < String > {
284+ // Tips will be populated when the tips system (Phase 11) is built
285+ None
286+ }
287+ }
288+
289+ /// Custom section: user-provided custom instructions (dynamic).
290+ struct CustomSection ;
291+
292+ impl SectionBuilder for CustomSection {
293+ fn name ( & self ) -> & ' static str {
294+ "custom"
295+ }
296+
297+ fn cache_scope ( & self ) -> CacheScope {
298+ CacheScope :: Dynamic
299+ }
300+
301+ fn build ( & self , ctx : & SectionContext ) -> Option < String > {
302+ ctx. custom_instructions
303+ . map ( |inst| format ! ( "# Custom Instructions\n {inst}" ) )
304+ }
305+ }
306+
307+ // ─── Tests ─────────────────────────────────────────────────────────────
308+
134309#[ cfg( test) ]
135310mod tests {
136311 use super :: * ;
@@ -230,4 +405,89 @@ mod tests {
230405 assert_eq ! ( CacheScope :: Static , CacheScope :: Static ) ;
231406 assert_ne ! ( CacheScope :: Static , CacheScope :: Dynamic ) ;
232407 }
408+
409+ #[ test]
410+ fn default_sections_creates_all_builders ( ) {
411+ let registry = SectionRegistry :: default_sections ( ) ;
412+ assert_eq ! ( registry. builders. len( ) , 8 ) ;
413+ }
414+
415+ #[ test]
416+ fn default_sections_env_present ( ) {
417+ let registry = SectionRegistry :: default_sections ( ) ;
418+ let ctx = make_ctx ( ) ;
419+ let result = registry. assemble ( & ctx) ;
420+ assert ! ( result. contains( "Environment" ) ) ;
421+ assert ! ( result. contains( "Working directory" ) ) ;
422+ }
423+
424+ #[ test]
425+ fn default_sections_with_git ( ) {
426+ let registry = SectionRegistry :: default_sections ( ) ;
427+ let ctx = SectionContext {
428+ git_status : Some ( "On branch main, clean" ) ,
429+ ..make_ctx ( )
430+ } ;
431+ let result = registry. assemble ( & ctx) ;
432+ assert ! ( result. contains( "Git Status" ) ) ;
433+ assert ! ( result. contains( "On branch main" ) ) ;
434+ // Git is dynamic, so boundary marker should be present
435+ assert ! ( result. contains( DYNAMIC_BOUNDARY_MARKER ) ) ;
436+ }
437+
438+ #[ test]
439+ fn default_sections_with_crab_md ( ) {
440+ let registry = SectionRegistry :: default_sections ( ) ;
441+ let ctx = SectionContext {
442+ crab_md_content : Some ( "Build with cargo build" ) ,
443+ ..make_ctx ( )
444+ } ;
445+ let result = registry. assemble ( & ctx) ;
446+ assert ! ( result. contains( "CRAB.md" ) ) ;
447+ assert ! ( result. contains( "cargo build" ) ) ;
448+ }
449+
450+ #[ test]
451+ fn default_sections_with_memory ( ) {
452+ let registry = SectionRegistry :: default_sections ( ) ;
453+ let ctx = SectionContext {
454+ memory_content : Some ( "User prefers Rust" ) ,
455+ ..make_ctx ( )
456+ } ;
457+ let result = registry. assemble ( & ctx) ;
458+ assert ! ( result. contains( "Memory" ) ) ;
459+ assert ! ( result. contains( "User prefers Rust" ) ) ;
460+ }
461+
462+ #[ test]
463+ fn default_sections_with_custom ( ) {
464+ let registry = SectionRegistry :: default_sections ( ) ;
465+ let ctx = SectionContext {
466+ custom_instructions : Some ( "Always respond in Chinese" ) ,
467+ ..make_ctx ( )
468+ } ;
469+ let result = registry. assemble ( & ctx) ;
470+ assert ! ( result. contains( "Custom Instructions" ) ) ;
471+ assert ! ( result. contains( "Always respond in Chinese" ) ) ;
472+ }
473+
474+ #[ test]
475+ fn default_sections_skips_empty_tools ( ) {
476+ let registry = SectionRegistry :: default_sections ( ) ;
477+ let ctx = make_ctx ( ) ; // tool_descriptions = ""
478+ let result = registry. assemble ( & ctx) ;
479+ assert ! ( !result. contains( "Available Tools" ) ) ;
480+ }
481+
482+ #[ test]
483+ fn default_sections_with_tools ( ) {
484+ let registry = SectionRegistry :: default_sections ( ) ;
485+ let ctx = SectionContext {
486+ tool_descriptions : "- Read: reads files\n - Write: writes files" ,
487+ ..make_ctx ( )
488+ } ;
489+ let result = registry. assemble ( & ctx) ;
490+ assert ! ( result. contains( "Available Tools" ) ) ;
491+ assert ! ( result. contains( "Read: reads files" ) ) ;
492+ }
233493}
0 commit comments