Skip to content

Implement --update and --update-all flags for rafters add #775

@ssilvius

Description

@ssilvius

Goal

Add --update and --update-all flags to rafters add so users can pull the latest registry versions of installed components without manually specifying --overwrite for each one.

Exact Implementation Requirements

Required Interface/Class Structure

// Updated AddOptions
interface AddOptions {
  overwrite?: boolean;
  update?: boolean;     // re-fetch named components from registry
  updateAll?: boolean;  // re-fetch ALL installed components from registry
  list?: boolean;
  registryUrl?: string;
  agent?: boolean;
}

// New field in RaftersConfig (init.ts)
interface RaftersConfig {
  framework: Framework;
  componentsPath: string;
  primitivesPath: string;
  cssPath: string | null;
  shadcn: boolean;
  exports: ExportConfig;
  installedComponents?: string[];  // NEW: tracks what was installed via `add`
}

Behavior Requirements

Track installed components:

  • When add successfully installs a component, append its name to installedComponents in config.rafters.json
  • De-duplicate the list (Set-based)
  • Include primitive dependencies in the list (they are installed too)

rafters add button --update:

  • Equivalent to rafters add button --overwrite but reads more clearly as intent
  • Fetches latest from registry, overwrites existing files
  • Updates installedComponents list

rafters add --update-all:

  • Reads installedComponents from config.rafters.json
  • Re-fetches and overwrites every listed component from the registry
  • No component names required on the CLI when using --update-all
  • If installedComponents is empty or missing, error with: "No installed components found. Use 'rafters add <component>' to install first."
  • Reports what was updated and what was unchanged (content-identical)

No versioning:

  • Components are always latest from registry (shadcn model: source code you own)
  • No version tracking, no pinning, no diffing -- just re-fetch and overwrite
  • The installedComponents list is purely "what names were installed", not "what version"

Error Handling

  • --update-all with no config file: "No rafters config found. Run 'rafters init' first."
  • --update-all with empty installedComponents: "No installed components found. Use 'rafters add <component>' to install first."
  • --update without component names: same error as current add with no args
  • --update and --update-all together: --update-all takes precedence, ignore component args
  • Registry fetch failure: existing error handling applies

Acceptance Criteria

Functional Tests Required

// Track installed components
test('add records component in installedComponents', async () => {
  await add(['button'], { overwrite: false });
  const config = JSON.parse(await readFile(configPath, 'utf-8'));
  expect(config.installedComponents).toContain('button');
});

// --update overwrites existing
test('--update re-fetches and overwrites existing component', async () => {
  await add(['button'], {});
  await add(['button'], { update: true });
  // Should succeed without error (no "already exists" error)
});

// --update-all refreshes all installed
test('--update-all refreshes all installed components', async () => {
  await add(['button', 'input'], {});
  await add([], { updateAll: true });
  // Should re-fetch both button and input
});

// --update-all with nothing installed
test('--update-all errors when no components installed', async () => {
  await add([], { updateAll: true });
  expect(process.exitCode).toBe(1);
});

TypeScript Requirements

  • TypeScript 5.9.3 strict mode enabled
  • No any types anywhere (Biome enforced)
  • No .forEach() - use for...of loops
  • No .then() chains - use async/await only
  • All external data validated with Zod schemas

Build Requirements

  • All existing Biome rules enforced
  • pnpm preflight must pass

What NOT to Include

  • Component versioning or pinning (not needed -- always latest)
  • Diffing installed vs registry (future consideration)
  • Interactive component selection for update (separate issue)
  • Migration or changelog between component versions (out of scope)

File Locations

  • Implementation: packages/cli/src/commands/add.ts
  • Config type update: packages/cli/src/commands/init.ts (RaftersConfig interface)
  • Unit Tests: packages/cli/test/add.test.ts
  • Types: packages/cli/src/registry/types.ts (if schema changes needed)

Integration Requirements

Dependencies

  • Reads/writes config.rafters.json (already used by init)
  • Uses existing RegistryClient for fetching
  • No new package dependencies

Usage Examples

# Install a component (now also tracks it)
rafters add button

# Update a specific component to latest
rafters add button --update

# Update all installed components to latest
rafters add --update-all

# Agent mode works too
rafters add --update-all --agent

Success Criteria

  • rafters add <name> records installed components in config
  • rafters add <name> --update re-fetches and overwrites
  • rafters add --update-all refreshes all installed components
  • Existing --overwrite behavior unchanged
  • All functional tests pass
  • TypeScript compiles without errors
  • pnpm preflight passes

This issue is complete when: rafters add --update-all successfully re-fetches and overwrites all previously installed components from the registry, and the installed component list persists across CLI invocations.

Context & References

  • Current --overwrite behavior: always re-fetches from registry, but requires flag to write over existing files
  • shadcn model: no versioning, components are source code you own
  • RaftersConfig in packages/cli/src/commands/init.ts (line 78)
  • AddOptions in packages/cli/src/commands/add.ts

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions