Files
2025-10-30 10:31:53 +08:00

112 lines
7.8 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Feature Specification: Extension Command for Multi-Extension Normalization
**Feature Branch**: `004-extension-rename`
**Created**: 2025-10-29
**Status**: Draft
**Input**: User description: "实现扩展名修改Extension命令类似于 replace 命令,可以支持把多个扩展名更改为一个指定的扩展名"
## Clarifications
### Session 2025-10-30
- Q: Should extension comparisons treat casing uniformly or follow the host filesystem? → A: Always case-insensitive
- Q: How should hidden files be handled when `--hidden` is omitted? → A: Exclude hidden entries
- Q: What exit behavior should occur when no files match the given extensions? → A: Exit 0 with notice
- Q: How should files that already have the target extension be represented? → A: Preview as no-change; skip on apply
## User Scenarios & Testing _(mandatory)_
### User Story 1 - Normalize Legacy Extensions in Bulk (Priority: P1)
As a power user cleaning project assets, I want a command to replace multiple file extensions with a single standardized extension so that I can align legacy files (e.g., `.jpeg`, `.JPG`) without hand-editing each one.
**Why this priority**: Delivers the core business value—fast extension normalization across large folders.
**Independent Test**: In a sample directory containing `.jpeg`, `.JPG`, and `.png`, run `renamer extension .jpeg .JPG .png .jpg --dry-run`, verify preview shows the new `.jpg` extension for each, then apply with `--yes` and confirm filesystem updates.
**Acceptance Scenarios**:
1. **Given** files with extensions `.jpeg` and `.JPG`, **When** the user runs `renamer extension .jpeg .JPG .jpg`, **Then** the preview lists each file with the `.jpg` extension and apply renames successfully.
2. **Given** nested directories, **When** the user adds `--recursive`, **Then** all matching extensions in subdirectories are normalized while unrelated files remain untouched.
---
### User Story 2 - Automation-Friendly Extension Updates (Priority: P2)
As an operator integrating renamer into CI scripts, I want deterministic exit codes, ledger entries, and undo support when changing extensions so automated jobs remain auditable and recoverable.
**Why this priority**: Ensures enterprise workflows can rely on extension updates without risking data loss.
**Independent Test**: Run `renamer extension .yaml .yml .yml --yes --path ./fixtures`, verify exit code `0`, inspect `.renamer` ledger for recorded operations, and confirm `renamer undo` restores originals.
**Acceptance Scenarios**:
1. **Given** a non-interactive environment with `--yes`, **When** the command completes without conflicts, **Then** exit code is `0` and ledger metadata captures original extension list and target extension.
2. **Given** a ledger entry exists, **When** `renamer undo` runs, **Then** all files revert to their prior extensions even if the command was executed by automation.
---
### User Story 3 - Validate Extension Inputs and Conflicts (Priority: P3)
As a user preparing an extension migration, I want validation and preview warnings for invalid tokens, duplicate target names, and no-op operations so I can adjust before committing changes.
**Why this priority**: Reduces support load from misconfigured commands and protects against accidental overwrites.
**Independent Test**: Run `renamer extension .mp3 .MP3 mp3 --dry-run`, confirm validation fails because tokens must include leading `.`, and run a scenario where resulting filenames collide to ensure conflicts abort the apply step.
**Acceptance Scenarios**:
1. **Given** invalid input (e.g., missing leading `.` or fewer than two arguments), **When** the command executes, **Then** it exits with non-zero status and prints actionable guidance.
2. **Given** two files that would become the same path after extension normalization, **When** the preview runs, **Then** conflicts are listed and the apply step refuses to proceed until resolved.
---
### Edge Cases
- How does the rename plan surface conflicts when multiple files map to the same normalized extension?
- When the target extension already matches some files, they appear in preview with a “no change” indicator and are skipped during apply without raising errors.
- Hidden files and directories remain excluded unless the user supplies `--hidden`.
- When no files match in preview or apply, the command must surface a “no candidates found” notice while completing successfully.
## Requirements _(mandatory)_
### Functional Requirements
- **FR-001**: CLI MUST provide a dedicated `extension` subcommand that accepts one or more source extensions and a single target extension as positional arguments.
- **FR-002**: Preview → confirm workflow MUST mirror existing commands: list original paths, proposed paths, and highlight extension changes before apply.
- **FR-003**: Executions MUST append ledger entries capturing original extension list, target extension, affected files, and timestamps to support undo.
- **FR-004**: Users MUST be able to undo the most recent extension batch via existing undo mechanics without leaving orphaned files.
- **FR-005**: Command MUST respect global scope flags (`--path`, `--recursive`, `--include-dirs`, `--hidden`, `--extensions`, `--dry-run`, `--yes`) consistent with `list` and `replace`, excluding hidden files and directories unless `--hidden` is explicitly supplied.
- **FR-006**: Extension parsing MUST require leading `.` tokens, deduplicate case-insensitively, warn when duplicates or no-op tokens are supplied, and compare file extensions case-insensitively across all platforms.
- **FR-007**: Preview MUST detect target conflicts (two files mapping to the same new path) and block apply until conflicts are resolved.
- **FR-008**: Invalid invocations (e.g., fewer than two arguments, empty tokens after trimming) MUST exit with non-zero status and provide remediation tips.
- **FR-009**: Help output MUST clearly explain argument order, sequential evaluation rules, and interaction with scope flags.
- **FR-010**: When no files match the provided extensions, preview and apply runs MUST emit a clear “no candidates found” message and exit with status `0`.
- **FR-011**: Preview MUST surface already-targeted files with a “no change” marker, and apply MUST skip them while returning success.
### Key Entities
- **ExtensionRequest**: Working directory, ordered source extension list, target extension, scope flags, dry-run/apply settings.
- **ExtensionSummary**: Totals for candidates, changed files, per-extension match counts, conflicts, and warning messages used for preview and ledger metadata.
## Success Criteria _(mandatory)_
### Measurable Outcomes
- **SC-001**: Users normalize 500 files extensions (preview + apply) in under 2 minutes end-to-end.
- **SC-002**: 95% of beta testers correctly supply arguments (source extensions + target) after reading `renamer extension --help` without additional guidance.
- **SC-003**: Automated regression tests confirm extension change + undo leave the filesystem unchanged in 100% of scripted scenarios.
- **SC-004**: Support tickets related to inconsistent file extensions drop by 30% within the first release cycle after launch.
## Assumptions
- Command name will be `renamer extension` to align with existing verb-noun conventions.
- Source extensions are literal matches with leading dots; wildcard or regex patterns remain out of scope.
- Target extension must include a leading dot and is applied case-sensitively as provided.
- Existing traversal, summary, and ledger infrastructure can be extended from the replace/remove commands.
## Dependencies & Risks
- Requires new extension-specific packages analogous to replace/remove for parser, engine, summary, and CLI wiring.
- Help/quickstart documentation must be updated to explain argument order and extension validation.
- Potential filename conflicts after normalization must be detected pre-apply to avoid overwriting files.