---
phase: 05-cli-scaffolding
plan: 02
type: execute
wave: 2
depends_on: ["05-01"]
files_modified:
  - cli/src/Main.scala
  - cli/src/commands/ThemeCommands.scala
  - cli/src/commands/ComponentCommands.scala
  - cli/src/scaffold/templates/ThemeTemplates.scala
  - cli/src/scaffold/templates/ComponentTemplates.scala
  - cli/src/output/Progress.scala
autonomous: true

must_haves:
  truths:
    - "Developer can run `summer theme create Author.Theme` and get a blank theme scaffold"
    - "Developer can run `summer theme create Author.Theme --template=starter` and get a starter theme with Vite/Tailwind"
    - "Developer can run `summer component create Author.Plugin MyComponent` and get a component scaffold"
    - "Component scaffolding fails with clear error if plugin doesn't exist"
    - "Theme scaffolding shows progress bar during file creation"
  artifacts:
    - path: "cli/src/commands/ThemeCommands.scala"
      provides: "Theme scaffolding commands"
      exports: ["command"]
    - path: "cli/src/commands/ComponentCommands.scala"
      provides: "Component scaffolding commands"
      exports: ["command"]
    - path: "cli/src/scaffold/templates/ThemeTemplates.scala"
      provides: "Theme file templates (blank and starter)"
      exports: ["blankThemeFiles", "starterThemeFiles"]
    - path: "cli/src/scaffold/templates/ComponentTemplates.scala"
      provides: "Component file templates"
      exports: ["componentClass", "componentPartial", "componentYaml"]
  key_links:
    - from: "cli/src/Main.scala"
      to: "cli/src/commands/ThemeCommands.scala"
      via: "subcommands composition"
      pattern: "ThemeCommands\\.command"
    - from: "cli/src/commands/ComponentCommands.scala"
      to: "cli/src/scaffold/ScaffoldService.scala"
      via: "plugin existence check"
      pattern: "exists.*Plugin\\.scala"
---

<objective>
Implement theme and component scaffolding commands for the `summer` CLI.

Purpose: Complete the core scaffolding capabilities for developer productivity (CORE-08, CORE-09).
Output: Working `summer theme create` and `summer component create` commands with templates.
</objective>

<execution_context>
@/home/jin/.claude/get-shit-done/workflows/execute-plan.md
@/home/jin/.claude/get-shit-done/templates/summary.md
</execution_context>

<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/05-cli-scaffolding/05-CONTEXT.md
@.planning/phases/05-cli-scaffolding/05-RESEARCH.md
@.planning/phases/05-cli-scaffolding/05-01-SUMMARY.md
@build.mill
@cli/src/Main.scala
@cli/src/scaffold/ScaffoldService.scala
</context>

<tasks>

<task type="auto">
  <name>Task 1: Theme Scaffolding Command</name>
  <files>
    cli/src/Main.scala
    cli/src/commands/ThemeCommands.scala
    cli/src/scaffold/templates/ThemeTemplates.scala
    cli/src/output/Progress.scala
  </files>
  <action>
    Implement `summer theme create Author.Theme [--template=blank|starter]` command.

    1. **Create cli/src/output/Progress.scala:**
       - ZIO service trait with methods:
         - `start(total: Int, description: String): UIO[ProgressHandle]`
         - `update(handle: ProgressHandle, current: Int): UIO[Unit]`
         - `complete(handle: ProgressHandle): UIO[Unit]`
       - Case class `ProgressHandle(id: Long, total: Int, description: String)`
       - Live layer implementation:
         - Use `print` with `\r` for in-place updates
         - Progress bar format: `[====      ] 4/10`
         - Width: 30 characters for the bar
         - On complete: print newline to preserve final state

    2. **Create cli/src/scaffold/templates/ThemeTemplates.scala:**
       - Object with template string vals for BLANK theme:
         - `themeYaml`: theme.yaml manifest (name, author, description, version)
         - `defaultLayout`: layouts/default.htm with HTML5 boilerplate, {{page}} placeholder
         - `homePage`: pages/home.htm with basic content, url="/", title="Home"
         - `notFoundPage`: pages/404.htm with 404 content, url="/404"
       - Additional templates for STARTER theme:
         - `packageJson`: package.json with Vite, Tailwind dependencies
         - `viteConfig`: vite.config.js with build output to assets/
         - `tailwindCss`: assets/css/app.css with @tailwind directives
         - `starterJs`: assets/js/app.js with basic imports
         - `gitignore`: .gitignore for node_modules, dist
       - Helper function `blankThemeFiles(dir: os.Path, vars: TemplateVars): List[(os.Path, String)]`
       - Helper function `starterThemeFiles(dir: os.Path, vars: TemplateVars): List[(os.Path, String)]`
         - Starter includes all blank files PLUS the npm/Vite files

    3. **Create cli/src/commands/ThemeCommands.scala:**
       - Sealed trait `ThemeCommand` with case class `Create(name: String, template: String, dryRun: Boolean)`
       - Define options:
         - `--template`: String with default "blank", help "Theme template: blank or starter"
         - `--dry-run` / `-n`: Show what would be created
       - Define args:
         - `name`: Theme name in Author.Name format
       - Validate template option: must be "blank" or "starter"
       - Create `createCommand: Command[ThemeCommand.Create]`
       - Create `command: Command[ThemeCommand]` with "theme" parent, "create" subcommand
       - Implement handler:
         1. Validate name format (same regex as plugin: `^[A-Z][a-zA-Z0-9]*\\.[A-Z][a-zA-Z0-9]*$`)
         2. Parse into (author, theme) tuple
         3. Build theme directory path: `themes/{lower_author}/{lower_theme}/`
         4. Check if directory exists -> fail with `AlreadyExists`
         5. Get file list based on template choice (blank or starter)
         6. If --dry-run: print file list
         7. Else:
            - Start progress bar
            - Create each file, update progress
            - Complete progress bar
         8. Print next steps:
            - Blank: "Add layouts, pages, and partials to your theme"
            - Starter: "Run 'npm install' in theme directory, then 'npm run build'"

    4. **Update cli/src/Main.scala:**
       - Add ThemeCommands.command to subcommands
       - Wire Progress.live layer

    **Template placeholder format (use {{var}} consistently):**
    - `{{name}}`, `{{lower_name}}`, `{{studly_name}}`
    - `{{author}}`, `{{lower_author}}`, `{{studly_author}}`
  </action>
  <verify>
    ```bash
    # Test blank theme
    ./mill cli.run theme create Acme.Corporate --dry-run
    # Should list: theme.yaml, layouts/default.htm, pages/home.htm, pages/404.htm

    ./mill cli.run theme create Acme.Corporate
    # Should create themes/acme/corporate/ with blank structure

    ls themes/acme/corporate/
    # theme.yaml, layouts/, pages/, partials/, assets/

    cat themes/acme/corporate/theme.yaml
    # Valid YAML with name, author, version

    # Test starter theme
    ./mill cli.run theme create Acme.Modern --template=starter --dry-run
    # Should list additional: package.json, vite.config.js, etc.

    ./mill cli.run theme create Acme.Modern --template=starter
    # Should create with npm files

    ls themes/acme/modern/
    # Includes package.json, vite.config.js, etc.

    # Test invalid template
    ./mill cli.run theme create Test.Theme --template=invalid
    # Should error with valid options

    # Test already exists
    ./mill cli.run theme create Acme.Corporate
    # Should error
    ```
  </verify>
  <done>
    `summer theme create Author.Theme` creates blank theme. `--template=starter` adds Vite/Tailwind setup. Progress bar shows during creation. Invalid templates rejected.
  </done>
</task>

<task type="auto">
  <name>Task 2: Component Scaffolding Command</name>
  <files>
    cli/src/Main.scala
    cli/src/commands/ComponentCommands.scala
    cli/src/scaffold/templates/ComponentTemplates.scala
  </files>
  <action>
    Implement `summer component create Author.Plugin ComponentName` command.

    1. **Create cli/src/scaffold/templates/ComponentTemplates.scala:**
       - Object with template string vals:
         - `componentScala`: Component class extending SummerComponent
           - `componentDetails`: name and description
           - `defineProperties`: returns List.empty (placeholder)
           - `onRun`: returns Map.empty (placeholder)
           - Include TODO comments for each section
         - `componentPartial`: default.htm partial with basic HTML
           - Include example of accessing component properties
         - `componentYaml`: component.yaml with name, description, properties section
       - Template variables used: `{{name}}`, `{{studly_name}}`, `{{lower_name}}`, `{{plugin_namespace}}`

    2. **Create cli/src/commands/ComponentCommands.scala:**
       - Sealed trait `ComponentCommand` with case class `Create(plugin: String, name: String, dryRun: Boolean)`
       - Define args:
         - `plugin`: Plugin identifier in Author.Name format (required)
         - `name`: Component name (single word, PascalCase)
       - Define options:
         - `--dry-run` / `-n`: Show what would be created
       - Create `createCommand: Command[ComponentCommand.Create]`
       - Create `command: Command[ComponentCommand]` with "component" parent, "create" subcommand
       - Implement handler:
         1. Validate plugin name format (Author.Name)
         2. Parse plugin into (author, pluginName) tuple
         3. Validate component name (PascalCase: `^[A-Z][a-zA-Z0-9]*$`)
         4. Build plugin directory path: `plugins/{lower_author}/{lower_plugin}/`
         5. **Check if plugin exists** by verifying `Plugin.scala` exists in plugin dir
            - If not: fail with `PluginNotFound` error (add to ScaffoldError if needed)
         6. Build component paths:
            - `components/{StudlyName}.scala` - component class
            - `components/{lower_name}/default.htm` - default partial
            - `components/{lower_name}/component.yaml` - component config
         7. Check if component already exists -> fail with `AlreadyExists`
         8. If --dry-run: print file list
         9. Else: create files via ScaffoldService
         10. Print next steps:
             - "Register component in Plugin.scala register() method"
             - "Edit {StudlyName}.scala to add component logic"
             - "Edit default.htm partial for component output"

    3. **Update cli/src/Main.scala:**
       - Add ComponentCommands.command to subcommands

    4. **Add PluginNotFound to ScaffoldError if not present:**
       - `case class PluginNotFound(pluginId: String) extends ScaffoldError`
       - Message: "Plugin '{pluginId}' not found. Create it first with: summer plugin create {pluginId}"

    **Component name validation pattern:**
    ```scala
    private val componentNamePattern = "^[A-Z][a-zA-Z0-9]*$".r

    def validateComponentName(name: String): Either[String, String] =
      if componentNamePattern.matches(name) then Right(name)
      else Left(s"Invalid component name '$name'. Use PascalCase (e.g., BlogPost, FeaturedItems)")
    ```
  </action>
  <verify>
    ```bash
    # Ensure test plugin exists first
    ./mill cli.run plugin create Test.Blog 2>/dev/null || true

    # Test component creation
    ./mill cli.run component create Test.Blog FeaturedPosts --dry-run
    # Should list: FeaturedPosts.scala, featuredposts/default.htm, featuredposts/component.yaml

    ./mill cli.run component create Test.Blog FeaturedPosts
    # Should create component files

    ls plugins/test/blog/components/
    # FeaturedPosts.scala, featuredposts/

    ls plugins/test/blog/components/featuredposts/
    # default.htm, component.yaml

    cat plugins/test/blog/components/FeaturedPosts.scala
    # Valid Scala extending SummerComponent

    # Test plugin not found
    ./mill cli.run component create Nonexistent.Plugin MyComponent
    # Error: Plugin 'Nonexistent.Plugin' not found...

    # Test invalid component name
    ./mill cli.run component create Test.Blog invalid_name
    # Error: Invalid component name...

    # Test component already exists
    ./mill cli.run component create Test.Blog FeaturedPosts
    # Error: Already exists...
    ```
  </verify>
  <done>
    `summer component create Author.Plugin ComponentName` creates component scaffold inside existing plugin. Plugin existence is verified. Invalid names produce clear errors. Generated files include TODO comments.
  </done>
</task>

</tasks>

<verification>
Full plan verification:

1. Theme scaffolding works:
   ```bash
   rm -rf themes/verify  # Cleanup
   ./mill cli.run theme create Verify.Blank
   ls themes/verify/blank/
   # theme.yaml, layouts/, pages/, partials/, assets/
   ```

2. Starter theme includes npm files:
   ```bash
   rm -rf themes/verify/starter
   ./mill cli.run theme create Verify.Starter --template=starter
   ls themes/verify/starter/
   # Includes package.json, vite.config.js
   cat themes/verify/starter/package.json
   # Valid JSON with Vite, Tailwind deps
   ```

3. Component scaffolding requires existing plugin:
   ```bash
   ./mill cli.run component create Missing.Plugin Test 2>&1 | grep -q "not found"
   echo "Plugin check works: $?"
   ```

4. Component creates correct structure:
   ```bash
   # Use existing test plugin from above
   ./mill cli.run component create Test.Blog LatestPosts
   test -f plugins/test/blog/components/LatestPosts.scala && echo "Component class created"
   test -f plugins/test/blog/components/latestposts/default.htm && echo "Partial created"
   test -f plugins/test/blog/components/latestposts/component.yaml && echo "Config created"
   ```

5. All help commands work:
   ```bash
   ./mill cli.run theme --help
   ./mill cli.run theme create --help
   ./mill cli.run component --help
   ./mill cli.run component create --help
   ```

6. Progress bar displays during theme creation:
   ```bash
   ./mill cli.run theme create Verify.Progress --template=starter 2>&1 | head -5
   # Should show progress output
   ```
</verification>

<success_criteria>
- [ ] `summer theme create Author.Theme` creates valid blank theme
- [ ] `--template=starter` adds Vite/Tailwind configuration files
- [ ] `--template=invalid` produces error listing valid options
- [ ] Progress bar displays during theme file creation
- [ ] `summer component create Plugin.Name ComponentName` creates component scaffold
- [ ] Component command validates plugin exists before creating
- [ ] Component command validates component name is PascalCase
- [ ] Generated component includes .scala, .htm partial, .yaml config
- [ ] All generated files contain appropriate TODO comments
- [ ] Next steps printed after each successful scaffold
- [ ] Dry-run mode works for both commands
</success_criteria>

<output>
After completion, create `.planning/phases/05-cli-scaffolding/05-02-SUMMARY.md`
</output>
