Conversation
Phase 1 - Simplify kits: - Remove helpers_bulma.go and helpers_pico.go - Update loader.go to remove Bulma/Pico cases - Update simple kit to use 'none' CSS - Remove Bulma/Pico cases from css_helpers.go Phase 9 - CLI commands: - Add 'lvt component list' - list available components - Add 'lvt component eject' - eject full component source - Add 'lvt component eject-template' - eject template only - Add 'lvt new component' - scaffold new component - Add internal/eject package with eject logic - Update CLI help text Part of the components library implementation (Phases 1 & 9). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Pull Request Review - PR #9: feat: add components library CLI supportOverviewThis PR implements Phase 1 (simplify kits) and Phase 9 (CLI commands) of the components library plan. It removes Bulma/Pico CSS framework support to simplify to Tailwind-only, and adds comprehensive CLI commands for component management. Summary: ✅ Approved - This is a well-implemented, thoroughly tested PR that follows Go best practices. A few minor suggestions for improvement below. Code Quality & Best Practices✅ Strengths
Potential Issues & Security Concerns
|
There was a problem hiding this comment.
Pull request overview
This pull request implements two significant changes to the LiveTemplate CLI:
Phase 1 - CSS Framework Simplification: Removes support for Bulma and Pico CSS frameworks, standardizing on Tailwind CSS as the primary framework with "none" as the semantic HTML fallback. This simplifies the multi-framework abstraction layer that previously supported four CSS options. The changes include:
- Deletion of
helpers_bulma.goandhelpers_pico.go - Removal of Bulma/Pico cases from
loader.goandcss_helpers.go - Update of the simple kit to use "none" CSS instead of Pico
Phase 9 - Component Library CLI Support: Adds comprehensive CLI commands for managing UI components from the components library:
lvt component list- Lists 20 available UI components with their templateslvt component eject <name>- Ejects full component source code to the user's projectlvt component eject-template <name> <template>- Ejects only template files while keeping Go logic in the librarylvt new component <name>- Scaffolds new custom components with proper structure
The implementation includes a new internal/eject package for component ejection logic, comprehensive test coverage (26 tests across eject and scaffold commands), and updates to documentation and help text.
Reviewed changes
Copilot reviewed 22 out of 22 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| main.go | Added component command routing and updated help text |
| internal/kits/system/simple/kit.yaml | Changed CSS framework from "pico" to "none" |
| internal/kits/loader.go | Removed Bulma/Pico cases, added explanatory comment |
| internal/kits/helpers_pico.go | Deleted file (289 lines) |
| internal/kits/helpers_bulma.go | Deleted file (338 lines) |
| internal/generator/css_helpers.go | Removed all Bulma/Pico template helper functions |
| internal/eject/eject_test.go | Added 11 comprehensive tests for eject functionality |
| internal/eject/eject.go | New package implementing component ejection with 327 lines |
| commands/new_component_test.go | Added 15 tests for component scaffolding |
| commands/new_component.go | New command implementing component scaffolding (304 lines) |
| commands/new.go | Added component subcommand routing before help check |
| commands/mcp_server.go | Updated MCP schema to reflect tailwind/none only |
| commands/help.go | Added 106 lines of help text for component commands |
| commands/component_eject_template.go | New command for ejecting templates only |
| commands/component_eject.go | New command for ejecting full components |
| commands/component.go | New command group handler with component list |
| docs/MCP_TOOLS.md | Updated CSS options from "tailwind|bulma|pico|none" to "tailwind|none" |
| docs/AGENT_USAGE_GUIDE.md | Updated CSS framework documentation |
| commands/claude_resources/skills/new-app/SKILL.md | Updated kit description (simple now uses "no CSS") |
| commands/claude_resources/skills/customize/SKILL.md | Removed Bulma/Pico examples |
| commands/claude_resources/skills/TESTING.md | Updated kit descriptions |
| README.md | Updated generated template comments from Bulma to Tailwind |
| // Simple string replacement for imports | ||
| updated := strings.ReplaceAll(string(content), oldPkg, newPkg) |
There was a problem hiding this comment.
The simple string replacement approach for updating imports is risky and could cause false positives. For example, if the old package path appears in a comment, string literal, or as part of a different package name, it will be incorrectly replaced. Consider using Go's AST parsing (golang.org/x/tools/go/ast/astutil.RewriteImport) for more accurate import path updates, or at minimum add word boundary checks to only replace imports within import statements.
| components := []struct { | ||
| name string | ||
| description string | ||
| templates []string | ||
| }{ | ||
| {"accordion", "Collapsible content sections", []string{"lvt:accordion:default:v1", "lvt:accordion:single:v1"}}, | ||
| {"autocomplete", "Search with suggestions", []string{"lvt:autocomplete:default:v1"}}, | ||
| {"breadcrumbs", "Navigation breadcrumb trail", []string{"lvt:breadcrumbs:default:v1"}}, | ||
| {"datatable", "Data tables with sorting/pagination", []string{"lvt:datatable:default:v1"}}, | ||
| {"datepicker", "Date selection", []string{"lvt:datepicker:single:v1", "lvt:datepicker:range:v1", "lvt:datepicker:inline:v1"}}, | ||
| {"drawer", "Slide-out panels", []string{"lvt:drawer:default:v1"}}, | ||
| {"dropdown", "Dropdown menus", []string{"lvt:dropdown:default:v1", "lvt:dropdown:searchable:v1", "lvt:dropdown:multi:v1"}}, | ||
| {"menu", "Navigation menus", []string{"lvt:menu:default:v1", "lvt:menu:nested:v1"}}, | ||
| {"modal", "Modal dialogs", []string{"lvt:modal:default:v1", "lvt:modal:confirm:v1", "lvt:modal:sheet:v1"}}, | ||
| {"popover", "Rich content popovers", []string{"lvt:popover:default:v1"}}, | ||
| {"progress", "Progress indicators", []string{"lvt:progress:default:v1", "lvt:progress:circular:v1", "lvt:progress:spinner:v1"}}, | ||
| {"rating", "Star ratings", []string{"lvt:rating:default:v1"}}, | ||
| {"skeleton", "Loading placeholders", []string{"lvt:skeleton:default:v1", "lvt:skeleton:avatar:v1", "lvt:skeleton:card:v1"}}, | ||
| {"tabs", "Tab navigation", []string{"lvt:tabs:horizontal:v1", "lvt:tabs:vertical:v1", "lvt:tabs:pills:v1"}}, | ||
| {"tagsinput", "Tag/chip input", []string{"lvt:tagsinput:default:v1"}}, | ||
| {"timeline", "Event timelines", []string{"lvt:timeline:default:v1"}}, | ||
| {"timepicker", "Time selection", []string{"lvt:timepicker:default:v1"}}, | ||
| {"toast", "Toast notifications", []string{"lvt:toast:default:v1", "lvt:toast:container:v1"}}, | ||
| {"toggle", "Toggle switches", []string{"lvt:toggle:default:v1", "lvt:toggle:checkbox:v1"}}, | ||
| {"tooltip", "Tooltips", []string{"lvt:tooltip:default:v1"}}, | ||
| } |
There was a problem hiding this comment.
The component list is duplicated between commands/component.go and internal/eject/eject.go. The list in component.go uses fully qualified template names (e.g., "lvt:accordion:default:v1") while eject.go uses just the variant names (e.g., "default", "single"). This duplication creates a maintenance burden - any new component must be added to both lists, and the template names are formatted differently.
Consider using eject.AvailableComponents() directly in the ComponentList function and formatting the template names there. This would ensure a single source of truth and reduce the risk of the lists becoming out of sync.
| defer dstFile.Close() | ||
|
|
||
| _, err = io.Copy(dstFile, srcFile) | ||
| return err |
There was a problem hiding this comment.
The copyFile function doesn't preserve file permissions from the source file. It uses os.Create which creates files with default permissions (0666 minus umask). For executable files or files with specific permission requirements, this could be problematic.
Consider using os.OpenFile with the source file's permissions, or call os.Chmod after creating the file to match the source permissions. Example:
srcInfo, _ := os.Stat(src)
// ... after creating dstFile ...
dstFile.Chmod(srcInfo.Mode())| return err | |
| if err != nil { | |
| return err | |
| } | |
| // Preserve file permissions from source | |
| srcInfo, err := os.Stat(src) | |
| if err != nil { | |
| return err | |
| } | |
| if err := os.Chmod(dst, srcInfo.Mode()); err != nil { | |
| return err | |
| } | |
| return nil |
| func isValidComponentName(name string) bool { | ||
| if name == "" { | ||
| return false | ||
| } | ||
| for i, r := range name { | ||
| if r == '-' { | ||
| if i == 0 || i == len(name)-1 { | ||
| return false | ||
| } | ||
| continue | ||
| } | ||
| if !unicode.IsLetter(r) && !unicode.IsDigit(r) { | ||
| return false | ||
| } | ||
| if unicode.IsUpper(r) { | ||
| return false | ||
| } | ||
| } | ||
| return true | ||
| } |
There was a problem hiding this comment.
[nitpick] The validation allows consecutive hyphens (e.g., "date--picker" is considered valid), which could lead to awkward package/type names. While this is syntactically valid, it creates inconsistent naming. Consider adding a check to reject consecutive hyphens for better naming consistency:
if i > 0 && name[i-1] == '-' {
return false // Reject consecutive hyphens
}This would prevent component names like "button--group" which would become package "buttongroup" and type "ButtonGroup", making the double hyphen in the original name confusing.
| if len(word) > 0 { | ||
| words[i] = strings.ToUpper(word[:1]) + word[1:] | ||
| } |
There was a problem hiding this comment.
If a component name contains consecutive hyphens (e.g., "date--picker"), the toPascalCase function will produce incorrect results. When split by "-", this creates an empty string element, and word[:1] will panic with "index out of range" when word is empty.
While the validation currently allows consecutive hyphens (see line 31 in new_component_test.go), this creates a crash risk. Either:
- Add validation to reject consecutive hyphens in
isValidComponentName, or - Add a check in toPascalCase:
if len(word) == 0 { continue }
The test on line 31 of new_component_test.go notes this is "technically valid but ugly", suggesting it should probably be rejected in validation.
| if len(word) > 0 { | |
| words[i] = strings.ToUpper(word[:1]) + word[1:] | |
| } | |
| if len(word) == 0 { | |
| continue | |
| } | |
| words[i] = strings.ToUpper(word[:1]) + word[1:] |
- Use eject.AvailableComponents() as single source of truth for component list - Improve updateImports() to only replace within import statements (avoids false positives in comments/strings) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
PR Review: Components Library CLI SupportThis PR implements Phase 1 (CSS simplification) and Phase 9 (component CLI commands). Overall: Well-implemented, thoroughly tested, production-ready code. ✅ StrengthsCode Organization: Clean separation with internal/eject package, well-structured commands Test Coverage: 26 total tests (11 eject + 15 scaffold) covering edge cases and errors User Experience: Clear error messages, helpful output, --help flags, --force protection Documentation: Updated README, comprehensive help text, good inline comments 🔍 Key FindingsGood Practices Found:
Minor Issues (Medium Priority):1. File Close Error Handling (internal/eject/eject.go:295-303) 2. Consecutive Hyphen Validation (commands/new_component.go:114-133) 3. Command Safety (internal/eject/eject.go:226, 353) 🔒 Security: No Critical Issues
Minor: exec.Command safe now, harden if sources become user-configurable 📊 Performance: Efficient Design
Consider: Progress indicators for large component copies 🧪 Test Coverage: ExcellentCovers: component lookup, validation, file ops, error conditions, name validation (15 cases), case conversions Missing (acceptable edge cases): module cache integration, network failures, disk full 🎯 RecommendationsHigh Priority: None - no critical issues Medium Priority:
Low Priority:
🎉 ConclusionProduction-ready PR:
Recommendation: ✅ APPROVE Great work! Solid foundation for the components ecosystem. Review focus: code quality, best practices, security, performance, test coverage, maintainability |
🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
PR Review: Components Library CLI SupportOverviewThis is a solid implementation that adds CLI commands for component management and simplifies the CSS framework strategy. The code quality is high with good test coverage. ✅ Strengths1. Well-Structured Code
2. Excellent Test Coverage
3. User Experience
4. Documentation
🔍 Code Quality ObservationsGood Practices
Minor Issues1. Missing Validation for
|
The function now correctly replaces both: - Exact matches: "oldPkg" -> "newPkg" - Subpackage imports: "oldPkg/internal" -> "newPkg/internal" 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
PR Review: Components Library CLI SupportSummaryThis PR successfully implements Phase 1 (simplifying kits) and Phase 9 (CLI commands) from the components library plan. The implementation is well-structured, thoroughly tested, and follows Go best practices. ✅ StrengthsCode Quality
Architecture
User Experience
🔍 Observations & Minor Issues1. Import Update Logic (eject.go:306-354)The Potential issue: Line-by-line parsing might miss imports in multiline import blocks with comments interspersed. import (
// This is a comment
"github.com/livetemplate/components/dropdown"
/* multiline comment */ "other/package"
)Recommendation: Consider using 2. Module Cache Discovery (eject.go:223-243)The Current behavior: Improvement suggestion: Consider automatically running 3. Test File Skipping (eject.go:267-268)Test files are skipped during ejection, which makes sense for production code but might be undesirable if users want to see example tests. Recommendation: Consider adding a 4. Component Name Validation (new_component.go:114-133)The validation is good but if r == '-' {
if i == 0 || i == len(name)-1 {
return false
}
continue // Doesn't check for consecutive hyphens
}Recommendation: Add check for consecutive hyphens. 5. Error Messages Could Include More ContextIn Current: Suggestion: 🔒 Security Considerations✅ Good Security Practices
|
| Category | Score | Notes |
|---|---|---|
| Code Quality | 5/5 | Clean, well-organized, follows best practices |
| Test Coverage | 5/5 | Comprehensive unit tests, good edge case coverage |
| Documentation | 4/5 | Great help text, could use more docs |
| Error Handling | 5/5 | Clear messages with helpful guidance |
| Security | 5/5 | No security concerns identified |
| Performance | 5/5 | Efficient implementation |
| Architecture | 5/5 | Well-designed, maintainable |
| User Experience | 5/5 | Intuitive commands with helpful output |
Overall: 4.9/5 ⭐⭐⭐⭐⭐
✅ Recommendation
APPROVE - This is production-ready code. The minor suggestions above are enhancements, not blockers.
Suggested Action Items (Optional, Post-Merge)
- Add integration test for actual component ejection
- Consider adding
--include-testsflag for ejection - Improve component name validation to reject consecutive hyphens
- Add migration guide for Bulma/Pico removal
- Move long template strings to embed.FS for better maintainability
🎉 Conclusion
Excellent work on this PR! The implementation is solid, well-tested, and provides a great user experience. The components library support will be a valuable addition to the lvt CLI.
Review generated by Claude Code
- Document why kit is set both in inputs map and via SetKit() - Merge Apply_NoArgs and Apply_ReturnsNotImplemented into single Apply_NotImplemented test since the stub ignores args Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: Milestone 3 — Telemetry & Evolution (Issues #61–#67) Add self-improving evolution system that captures generation events, matches errors to known patterns, proposes fixes, and tests them in isolation. This creates a feedback loop: capture → match → propose → test. New packages: - internal/telemetry: SQLite-backed event store with noop-safe Collector/Capture API - internal/evolution/knowledge: Markdown parser for evolution/patterns.md (13 patterns) - internal/evolution: Fix proposer, tester, and upstream support Integration: - commands/gen.go, commands/auth.go: Telemetry capture around all generators - commands/evolution.go: CLI commands (status, metrics, failures, patterns, propose, apply) - main.go: Route "evolution"/"evo" command Closes #61, #62, #63, #64, #65, #66, #67 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * style: fix gofmt formatting in evolution.go and events.go Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: address bot review comments on PR #130 - Change telemetry to opt-in (LVT_TELEMETRY=true) instead of opt-out - Fix double validation: refactor runPostGenValidation to return result and reuse marshalValidationResult instead of re-running validation - Parameterize LIMIT query in SQLite store - Fix scanInto to return errors on parse/unmarshal failures - Add getLvtVersion() using debug.ReadBuildInfo() - Remove unused started field from Capture - Fix tester glob to match relative paths, not just filenames - Fix proposer dedup key to include FindPattern+Replace - Add kit index to schema.sql - Fix zero-total percentage display in evolution status - Gate apply command (return not-implemented error) - Fix truncate() to use rune count instead of byte length - Sort metrics output by command name - Remove runtime.Caller(0) fallback from FindPatternsFile - Walk up directory tree from cwd to find patterns.md - Document evo alias in main.go usage - Fix TestEvolution_Patterns to use t.Skipf - Add upstream proposer unit tests - Add NewFromPatterns constructor for testing Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: address second round of bot review comments - Simplify NewCollector() to return *Collector (always non-nil, no error) - Remove startCapture helper and nil guards at all call sites - Remove dead filepath.Match call in tester.go Walk fallback - Mark 'apply' command as [coming soon] in help text - Make patterns.md integration tests structural (not exact counts/order) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: defer capture.Complete in auth.go to after resource-protection Move telemetry capture completion to after all post-gen work so the event correctly reflects the full outcome of the command. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: use deferred closure for capture.Complete in auth.go Track success with a local bool so the deferred capture.Complete correctly reflects late failures. Also relax upstream pattern count assertion to >= 1 instead of exact count. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: call capture.Complete directly at end of Auth() Replace deferred closure with direct call before return nil, matching the pattern used in gen.go. Also document the */filename glob constraint in FixTemplate.File. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * style: fix gofmt alignment in types.go Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: address Claude review #7 — proposer loop, glob/walk, upstream test - Move ListPatterns() call before error loop in proposer (single lock acquire) - Skip Glob for "*/"-prefixed targets, go straight to Walk for consistency - Use strings.CutPrefix instead of HasPrefix+TrimPrefix - Fix TestUpstreamProposer_NoUpstreamRepo to set MessageRe so it tests the UpstreamRepo="" path, not the MessageRe==nil guard - Add TODO comment in upstream.go for first-fix-only limitation - Simplify evolutionApply to use _ for unused args parameter - Extract defaultLookbackDays const for 30-day hardcoding Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: address Claude review #8 — minor polish items - Add TODO for unwired RecordFileGenerated method - Align null-guard for inputsJSON to match errorsJSON/filesJSON - Document context regex message fallback rationale in matcher.go Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: address Claude review #9 — kit comment, apply test naming - Document why kit is set both in inputs map and via SetKit() - Merge Apply_NoArgs and Apply_ReturnsNotImplemented into single Apply_NotImplemented test since the stub ignores args Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: address Claude review #10 — surface read errors, prevent dup inserts - Return error instead of silently skipping unreadable files in applyFix - Use INSERT OR IGNORE to handle unlikely ID collisions gracefully Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: preserve file permissions in applyFix, document INSERT OR IGNORE - Use os.Stat to capture original file mode before rewriting - Add comment explaining why INSERT OR IGNORE is used Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: XDG_CONFIG_HOME isolation in tests, consistent propose behavior - Clear XDG_CONFIG_HOME in all evolution tests that set HOME for isolation - Make evolutionPropose print-and-return-nil when disabled, matching other subcommands Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: mark unused args parameters with _ in evolution subcommands Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Summary
Phase 1 - Simplify kits
helpers_bulma.goandhelpers_pico.goloader.goto remove Bulma/Pico casescss_helpers.goPhase 9 - CLI commands
lvt component list- list available componentslvt component eject <name>- eject full component sourcelvt component eject-template <name> <template>- eject template onlylvt new component <name>- scaffold new componentinternal/ejectpackage with eject logicContext
Part of the components library implementation (Phases 1 & 9 from COMPONENTS_LIBRARY_PLAN.md).
The components library simplifies the CSS strategy to Tailwind-only, removing the need for the multi-framework abstraction layer.
Test plan
🤖 Generated with Claude Code