diff --git a/openspec/changes/add-update-command/tasks.md b/openspec/changes/add-update-command/tasks.md index 412fc5053..c539e03e2 100644 --- a/openspec/changes/add-update-command/tasks.md +++ b/openspec/changes/add-update-command/tasks.md @@ -1,20 +1,20 @@ # Implementation Tasks ## 1. Update Command Implementation -- [ ] 1.1 Create `src/core/update.ts` with `UpdateCommand` class -- [ ] 1.2 Check if `openspec` directory exists (use `FileSystemUtils.directoryExists`) -- [ ] 1.3 Write `readmeTemplate` to `openspec/README.md` using `FileSystemUtils.writeFile` -- [ ] 1.4 Update `CLAUDE.md` using markers via `FileSystemUtils.updateFileWithMarkers` and `TemplateManager.getClaudeTemplate()` -- [ ] 1.5 Display ASCII-safe success message: `Updated OpenSpec instructions` +- [x] 1.1 Create `src/core/update.ts` with `UpdateCommand` class +- [x] 1.2 Check if `openspec` directory exists (use `FileSystemUtils.directoryExists`) +- [x] 1.3 Write `readmeTemplate` to `openspec/README.md` using `FileSystemUtils.writeFile` +- [x] 1.4 Update `CLAUDE.md` using markers via `FileSystemUtils.updateFileWithMarkers` and `TemplateManager.getClaudeTemplate()` +- [x] 1.5 Display ASCII-safe success message: `Updated OpenSpec instructions` ## 2. CLI Integration -- [ ] 2.1 Register `update` command in `src/cli/index.ts` -- [ ] 2.2 Add command description: `Update OpenSpec instruction files` -- [ ] 2.3 Handle errors with `ora().fail(...)` and exit code 1 (missing `openspec` directory, file write errors) +- [x] 2.1 Register `update` command in `src/cli/index.ts` +- [x] 2.2 Add command description: `Update OpenSpec instruction files` +- [x] 2.3 Handle errors with `ora().fail(...)` and exit code 1 (missing `openspec` directory, file write errors) ## 3. Testing -- [ ] 3.1 Verify `openspec/README.md` is fully replaced with latest template -- [ ] 3.2 Verify `CLAUDE.md` OpenSpec block updates without altering user content outside markers -- [ ] 3.3 Verify idempotency (running twice yields identical files, no duplicate markers) -- [ ] 3.4 Verify error when `openspec` directory is missing with friendly message -- [ ] 3.5 Verify success message displays properly in ASCII-only terminals \ No newline at end of file +- [x] 3.1 Verify `openspec/README.md` is fully replaced with latest template +- [x] 3.2 Verify `CLAUDE.md` OpenSpec block updates without altering user content outside markers +- [x] 3.3 Verify idempotency (running twice yields identical files, no duplicate markers) +- [x] 3.4 Verify error when `openspec` directory is missing with friendly message +- [x] 3.5 Verify success message displays properly in ASCII-only terminals \ No newline at end of file diff --git a/src/cli/index.ts b/src/cli/index.ts index d428f098b..8ae9417a0 100644 --- a/src/cli/index.ts +++ b/src/cli/index.ts @@ -3,6 +3,7 @@ import ora from 'ora'; import path from 'path'; import { promises as fs } from 'fs'; import { InitCommand } from '../core/init.js'; +import { UpdateCommand } from '../core/update.js'; const program = new Command(); @@ -44,4 +45,19 @@ program } }); +program + .command('update [path]') + .description('Update OpenSpec instruction files') + .action(async (targetPath = '.') => { + try { + const resolvedPath = path.resolve(targetPath); + const updateCommand = new UpdateCommand(); + await updateCommand.execute(resolvedPath); + } catch (error) { + console.log(); // Empty line for spacing + ora().fail(`Error: ${(error as Error).message}`); + process.exit(1); + } + }); + program.parse(); \ No newline at end of file diff --git a/src/core/update.ts b/src/core/update.ts new file mode 100644 index 000000000..93465a10d --- /dev/null +++ b/src/core/update.ts @@ -0,0 +1,35 @@ +import path from 'path'; +import { FileSystemUtils } from '../utils/file-system.js'; +import { TemplateManager } from './templates/index.js'; +import { OPENSPEC_DIR_NAME, OPENSPEC_MARKERS } from './config.js'; +import { readmeTemplate } from './templates/readme-template.js'; + +export class UpdateCommand { + async execute(projectPath: string): Promise { + const resolvedProjectPath = path.resolve(projectPath); + const openspecDirName = OPENSPEC_DIR_NAME; + const openspecPath = path.join(resolvedProjectPath, openspecDirName); + + // 1. Check openspec directory exists + if (!await FileSystemUtils.directoryExists(openspecPath)) { + throw new Error(`No OpenSpec directory found. Run 'openspec init' first.`); + } + + // 2. Update README.md (full replacement) + const readmePath = path.join(openspecPath, 'README.md'); + await FileSystemUtils.writeFile(readmePath, readmeTemplate); + + // 3. Update CLAUDE.md (marker-based) + const claudePath = path.join(resolvedProjectPath, 'CLAUDE.md'); + const claudeContent = TemplateManager.getClaudeTemplate(); + await FileSystemUtils.updateFileWithMarkers( + claudePath, + claudeContent, + OPENSPEC_MARKERS.start, + OPENSPEC_MARKERS.end + ); + + // 4. Success message (ASCII-safe) + console.log('Updated OpenSpec instructions'); + } +}