CLI/Template Domain Knowledge
Domain-specific knowledge for moai-adk-go's CLI and template system. Supplements expert-backend with project-specific patterns.
Quick Reference
Architecture Overview
moai binary (Go)
├── internal/cli/ ~50 cobra command files
├── internal/template/
│ ├── templates/ Source of truth for all templates
│ ├── embedded.go Auto-generated (go:embed)
│ ├── context.go TemplateContext with GoBinPath, HomeDir
│ └── renderer.go Template rendering engine
└── internal/config/ Configuration loading and defaults
Key Patterns
- Template-First Rule: New files under
.claude/or.moai/must be added tointernal/template/templates/first, thenmake build - Embedded System:
//go:embed templates/*inembedded.go-- auto-generated, never edit - Template Variables:
{{.GoBinPath}}(init-time absolute),{{.HomeDir}}(init-time absolute) - Fallback Paths: Use
$HOME(not.HomeDir) in.sh.tmplfiles for runtime flexibility - 16-Language Neutrality: Templates treat all 16 supported languages equally -- no "PRIMARY" language
Build Cycle
# 1. Edit templates
vim internal/template/templates/.claude/skills/...
# 2. Regenerate embedded files
make build
# 3. Run tests
go test ./internal/template/...
# 4. Verify
ls -la internal/template/embedded.go
Command Structure
Each cobra command lives in internal/cli/<command>.go with corresponding tests in <command>_test.go. Commands follow the pattern:
rootCmdinroot.gowith subcommands registered viainit()- Each command file:
var <cmd>Cmd = &cobra.Command{...}+func init() { rootCmd.AddCommand(...) }
Configuration System
- Main config:
.moai/config/config.yaml - Sections:
.moai/config/sections/*.yaml(quality, language, user, workflow, harness, design) - Priority: env vars > user config > template defaults
- Env var keys:
internal/config/envkeys.go(constants, never hardcode)
Implementation Guide
Adding a New Cobra Command
- Create
internal/cli/<command>.gowith cobra command struct - Register in
init()function - Create
internal/cli/<command>_test.gousingt.TempDir() - If command modifies templates, add template file to
internal/template/templates/ - Run
make buildif templates changed - Run
go test ./internal/cli/...to verify
Adding a New Template File
- Add file to
internal/template/templates/<path> - If file needs variable substitution, use
.tmplextension - Register passthrough tokens in
renderer.goif needed (e.g.,$HOME) - Run
make buildto regenerateembedded.go - Run
go test ./internal/template/...
Template Rendering Pipeline
TemplateContext{GoBinPath, HomeDir}
-> renderer.Render(templateContent, context)
-> Output file (variable substitution applied)
Reserved passthrough tokens (not substituted, passed through verbatim):
$HOME,$PATH,$CLAUDE_PROJECT_DIR- Environment variable references in shell scripts
Error Wrapping Convention
// Correct: use fmt.Errorf with %w
if err != nil {
return fmt.Errorf("deploy template: %w", err)
}
// Wrong: string concatenation
if err != nil {
return fmt.Errorf("deploy template: " + err.Error())
}
Cross-References
- CLAUDE.local.md Section 2: Template-First Rule and file synchronization
- CLAUDE.local.md Section 8: Template Variable Strategy
.claude/rules/moai/development/coding-standards.md: Coding conventionsmoai-foundation-ccskill: Claude Code authoring patternsmoai-foundation-coreskill: SPEC system and TRUST 5