Add regex command implementation
This commit is contained in:
34
specs/006-add-regex-command/checklists/requirements.md
Normal file
34
specs/006-add-regex-command/checklists/requirements.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# Specification Quality Checklist: Regex Command for Pattern-Based Renaming
|
||||
|
||||
**Purpose**: Validate specification completeness and quality before proceeding to planning
|
||||
**Created**: 2025-10-30
|
||||
**Feature**: specs/001-add-regex-command/spec.md
|
||||
|
||||
## Content Quality
|
||||
|
||||
- [X] No implementation details (languages, frameworks, APIs)
|
||||
- [X] Focused on user value and business needs
|
||||
- [X] Written for non-technical stakeholders
|
||||
- [X] All mandatory sections completed
|
||||
|
||||
## Requirement Completeness
|
||||
|
||||
- [X] No [NEEDS CLARIFICATION] markers remain
|
||||
- [X] Requirements are testable and unambiguous
|
||||
- [X] Success criteria are measurable
|
||||
- [X] Success criteria are technology-agnostic (no implementation details)
|
||||
- [X] All acceptance scenarios are defined
|
||||
- [X] Edge cases are identified
|
||||
- [X] Scope is clearly bounded
|
||||
- [X] Dependencies and assumptions identified
|
||||
|
||||
## Feature Readiness
|
||||
|
||||
- [X] All functional requirements have clear acceptance criteria
|
||||
- [X] User scenarios cover primary flows
|
||||
- [X] Feature meets measurable outcomes defined in Success Criteria
|
||||
- [X] No implementation details leak into specification
|
||||
|
||||
## Notes
|
||||
|
||||
- Items marked incomplete require spec updates before `/speckit.clarify` or `/speckit.plan`
|
||||
248
specs/006-add-regex-command/contracts/regex-command.yaml
Normal file
248
specs/006-add-regex-command/contracts/regex-command.yaml
Normal file
@@ -0,0 +1,248 @@
|
||||
openapi: 3.1.0
|
||||
info:
|
||||
title: Renamer Regex Command API
|
||||
version: 0.1.0
|
||||
description: >
|
||||
Contract representation of the `renamer regex` command workflows (preview/apply/undo)
|
||||
for automation harnesses and documentation parity.
|
||||
servers:
|
||||
- url: cli://renamer
|
||||
description: Command-line invocation surface
|
||||
paths:
|
||||
/regex/preview:
|
||||
post:
|
||||
summary: Preview regex-based rename results
|
||||
description: Mirrors `renamer regex <pattern> <template> --dry-run`
|
||||
operationId: previewRegex
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RegexRequest'
|
||||
responses:
|
||||
'200':
|
||||
description: Successful preview
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RegexPreview'
|
||||
'400':
|
||||
description: Validation error (invalid pattern, undefined placeholders)
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
/regex/apply:
|
||||
post:
|
||||
summary: Apply regex-based renaming
|
||||
description: Mirrors `renamer regex <pattern> <template> --yes`
|
||||
operationId: applyRegex
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/RegexRequest'
|
||||
- type: object
|
||||
properties:
|
||||
dryRun:
|
||||
type: boolean
|
||||
const: false
|
||||
responses:
|
||||
'200':
|
||||
description: Apply succeeded
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RegexApplyResult'
|
||||
'409':
|
||||
description: Conflict detected (duplicate targets, existing files)
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ConflictResponse'
|
||||
'400':
|
||||
description: Validation error (invalid pattern/template)
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
/regex/undo:
|
||||
post:
|
||||
summary: Undo the latest regex rename batch
|
||||
description: Mirrors `renamer undo` when the last ledger entry corresponds to a regex command.
|
||||
operationId: undoRegex
|
||||
responses:
|
||||
'200':
|
||||
description: Undo succeeded
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/UndoResult'
|
||||
'409':
|
||||
description: Ledger inconsistent or no regex entry found
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ErrorResponse'
|
||||
components:
|
||||
schemas:
|
||||
RegexRequest:
|
||||
type: object
|
||||
required:
|
||||
- workingDir
|
||||
- pattern
|
||||
- template
|
||||
properties:
|
||||
workingDir:
|
||||
type: string
|
||||
pattern:
|
||||
type: string
|
||||
description: RE2-compatible regular expression applied to filename stem.
|
||||
template:
|
||||
type: string
|
||||
description: Replacement template supporting placeholders `@0..@n`.
|
||||
includeDirs:
|
||||
type: boolean
|
||||
default: false
|
||||
recursive:
|
||||
type: boolean
|
||||
default: false
|
||||
includeHidden:
|
||||
type: boolean
|
||||
default: false
|
||||
extensionFilter:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
dryRun:
|
||||
type: boolean
|
||||
default: true
|
||||
autoConfirm:
|
||||
type: boolean
|
||||
default: false
|
||||
RegexPreview:
|
||||
type: object
|
||||
required:
|
||||
- totalCandidates
|
||||
- matched
|
||||
- changed
|
||||
- entries
|
||||
properties:
|
||||
totalCandidates:
|
||||
type: integer
|
||||
minimum: 0
|
||||
matched:
|
||||
type: integer
|
||||
minimum: 0
|
||||
changed:
|
||||
type: integer
|
||||
minimum: 0
|
||||
skipped:
|
||||
type: integer
|
||||
minimum: 0
|
||||
conflicts:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Conflict'
|
||||
warnings:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
entries:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/PreviewEntry'
|
||||
RegexApplyResult:
|
||||
type: object
|
||||
required:
|
||||
- totalApplied
|
||||
- skipped
|
||||
- ledgerEntryId
|
||||
properties:
|
||||
totalApplied:
|
||||
type: integer
|
||||
minimum: 0
|
||||
skipped:
|
||||
type: integer
|
||||
minimum: 0
|
||||
ledgerEntryId:
|
||||
type: string
|
||||
warnings:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
UndoResult:
|
||||
type: object
|
||||
required:
|
||||
- restored
|
||||
- ledgerEntryId
|
||||
properties:
|
||||
restored:
|
||||
type: integer
|
||||
ledgerEntryId:
|
||||
type: string
|
||||
message:
|
||||
type: string
|
||||
Conflict:
|
||||
type: object
|
||||
required:
|
||||
- originalPath
|
||||
- proposedPath
|
||||
- reason
|
||||
properties:
|
||||
originalPath:
|
||||
type: string
|
||||
proposedPath:
|
||||
type: string
|
||||
reason:
|
||||
type: string
|
||||
enum:
|
||||
- duplicate_target
|
||||
- existing_file
|
||||
- existing_directory
|
||||
- invalid_template
|
||||
PreviewEntry:
|
||||
type: object
|
||||
required:
|
||||
- originalPath
|
||||
- proposedPath
|
||||
- status
|
||||
properties:
|
||||
originalPath:
|
||||
type: string
|
||||
proposedPath:
|
||||
type: string
|
||||
status:
|
||||
type: string
|
||||
enum:
|
||||
- changed
|
||||
- no_change
|
||||
- skipped
|
||||
matchGroups:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
ErrorResponse:
|
||||
type: object
|
||||
required:
|
||||
- error
|
||||
properties:
|
||||
error:
|
||||
type: string
|
||||
remediation:
|
||||
type: string
|
||||
ConflictResponse:
|
||||
type: object
|
||||
required:
|
||||
- error
|
||||
- conflicts
|
||||
properties:
|
||||
error:
|
||||
type: string
|
||||
conflicts:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Conflict'
|
||||
62
specs/006-add-regex-command/data-model.md
Normal file
62
specs/006-add-regex-command/data-model.md
Normal file
@@ -0,0 +1,62 @@
|
||||
# Data Model – Regex Command
|
||||
|
||||
## Entity: RegexRequest
|
||||
- **Fields**
|
||||
- `WorkingDir string` — Absolute path derived from CLI `--path` or current directory.
|
||||
- `Pattern string` — User-supplied regular expression.
|
||||
- `Template string` — Replacement string with `@n` placeholders.
|
||||
- `IncludeDirs bool` — Mirrors `--include-dirs` flag.
|
||||
- `Recursive bool` — Mirrors `--recursive` flag.
|
||||
- `IncludeHidden bool` — True only when `--hidden` is supplied.
|
||||
- `ExtensionFilter []string` — Filter tokens from `--extensions`.
|
||||
- `DryRun bool` — Preview-only execution state.
|
||||
- `AutoConfirm bool` — Captures `--yes` for non-interactive runs.
|
||||
- `Timestamp time.Time` — Invocation timestamp for ledger correlation.
|
||||
- **Validation Rules**
|
||||
- Regex must compile; invalid patterns produce errors.
|
||||
- Template may reference `@0` (full match) and numbered groups; referencing undefined groups is invalid.
|
||||
- Prohibit control characters and path separators in resulting names.
|
||||
- **Relationships**
|
||||
- Consumed by regex engine to build rename plan.
|
||||
- Serialized into ledger metadata alongside summary output.
|
||||
|
||||
## Entity: RegexSummary
|
||||
- **Fields**
|
||||
- `TotalCandidates int` — Items inspected after scope filtering.
|
||||
- `Matched int` — Files whose names matched the regex.
|
||||
- `Changed int` — Entries that will change after template substitution.
|
||||
- `Skipped int` — Non-matching or invalid-template entries.
|
||||
- `Conflicts []Conflict` — Rename collisions or generated duplicates.
|
||||
- `Warnings []string` — Validation notices (unused groups, truncated templates).
|
||||
- `Entries []PreviewEntry` — Original/proposed mappings with status.
|
||||
- `LedgerMetadata map[string]any` — Snapshot persisted with ledger entry (pattern, template, scope flags).
|
||||
- **Validation Rules**
|
||||
- Conflicts must be empty before apply.
|
||||
- `Matched = Changed + (matched entries with no change)` for consistency.
|
||||
- **Relationships**
|
||||
- Drives preview rendering.
|
||||
- Input for ledger writer and undo verification.
|
||||
|
||||
## Entity: Conflict
|
||||
- **Fields**
|
||||
- `OriginalPath string`
|
||||
- `ProposedPath string`
|
||||
- `Reason string` — (`duplicate_target`, `existing_file`, `invalid_template`).
|
||||
- **Validation Rules**
|
||||
- `ProposedPath` unique among planned operations.
|
||||
- Reason drawn from known enum for consistent messaging.
|
||||
- **Relationships**
|
||||
- Reported in preview output and blocks apply.
|
||||
|
||||
## Entity: PreviewEntry
|
||||
- **Fields**
|
||||
- `OriginalPath string`
|
||||
- `ProposedPath string`
|
||||
- `Status string` — `changed`, `no_change`, `skipped`.
|
||||
- `MatchGroups []string` — Captured groups applied to template.
|
||||
- **Validation Rules**
|
||||
- `ProposedPath` equals `OriginalPath` when `Status == "no_change"`.
|
||||
- `MatchGroups` length must equal number of captured groups.
|
||||
- **Relationships**
|
||||
- Displayed in preview output.
|
||||
- Persisted alongside ledger metadata for undo.
|
||||
96
specs/006-add-regex-command/plan.md
Normal file
96
specs/006-add-regex-command/plan.md
Normal file
@@ -0,0 +1,96 @@
|
||||
# Implementation Plan: Regex Command for Pattern-Based Renaming
|
||||
|
||||
**Branch**: `006-add-regex-command` | **Date**: 2025-10-30 | **Spec**: `specs/006-add-regex-command/spec.md`
|
||||
**Input**: Feature specification from `/specs/006-add-regex-command/spec.md`
|
||||
|
||||
**Note**: This template is filled in by the `/speckit.plan` command. See `.specify/templates/commands/plan.md` for the execution workflow.
|
||||
|
||||
## Summary
|
||||
|
||||
Deliver a `renamer regex` subcommand that compiles a user-supplied pattern, substitutes numbered capture groups into a replacement template, surfaces deterministic previews, and records ledger metadata so undo and automation workflows remain safe and auditable.
|
||||
|
||||
## Technical Context
|
||||
|
||||
**Language/Version**: Go 1.24
|
||||
**Primary Dependencies**: `spf13/cobra`, `spf13/pflag`, Go `regexp` (RE2 engine), internal traversal/history/output packages
|
||||
**Storage**: Local filesystem and `.renamer` ledger files
|
||||
**Testing**: `go test ./...`, contract suites under `tests/contract`, integration flows under `tests/integration`, targeted smoke script
|
||||
**Target Platform**: Cross-platform CLI (Linux, macOS, Windows shells)
|
||||
**Project Type**: Single CLI project (`cmd/`, `internal/`, `tests/`, `scripts/`)
|
||||
**Performance Goals**: Preview + apply 500 regex-driven renames in <2 minutes end-to-end
|
||||
**Constraints**: Preview-first confirmation, reversible ledger entries, Unicode-safe regex evaluation, conflict detection before apply
|
||||
**Scale/Scope**: Expected to operate on thousands of entries per invocation within local directories
|
||||
|
||||
## Constitution Check
|
||||
|
||||
*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*
|
||||
|
||||
- Preview flow MUST show deterministic rename mappings and require explicit confirmation (Preview-First Safety). ✅ Use shared preview renderer to list original → proposed names plus skipped/conflict indicators prior to apply.
|
||||
- Undo strategy MUST describe how the `.renamer` ledger entry is written and reversed (Persistent Undo Ledger). ✅ Append ledger entries containing pattern, template, captured groups per file, enabling `renamer undo` to restore originals.
|
||||
- Planned rename rules MUST document their inputs, validations, and composing order (Composable Rule Engine). ✅ Build a dedicated regex rule that compiles patterns, validates templates, and plugs into traversal pipeline without altering shared state.
|
||||
- Scope handling MUST cover files vs directories (`-d`), recursion (`-r`), and extension filtering via `-e` without escaping the requested path (Scope-Aware Traversal). ✅ Reuse traversal filters so regex respects directory, recursion, hidden, and extension flags identically to other commands.
|
||||
- CLI UX plan MUST confirm Cobra usage, flag naming, help text, and automated tests for preview/undo flows (Ergonomic CLI Stewardship). ✅ Add Cobra subcommand with documented flags, examples, help output, and contract/integration coverage for preview/apply/undo flows.
|
||||
|
||||
*Post-Design Verification (2025-10-30): Research, data model, contracts, and quickstart documents confirm preview coverage, ledger metadata, regex template validation, and CLI UX updates — no gate violations detected.*
|
||||
|
||||
## Project Structure
|
||||
|
||||
### Documentation (this feature)
|
||||
|
||||
```text
|
||||
specs/006-add-regex-command/
|
||||
├── plan.md
|
||||
├── research.md
|
||||
├── data-model.md
|
||||
├── quickstart.md
|
||||
├── contracts/
|
||||
└── tasks.md # Generated via /speckit.tasks
|
||||
```
|
||||
|
||||
### Source Code (repository root)
|
||||
|
||||
```text
|
||||
cmd/
|
||||
├── root.go
|
||||
├── list.go
|
||||
├── replace.go
|
||||
├── remove.go
|
||||
├── extension.go
|
||||
├── insert.go
|
||||
├── regex.go # NEW
|
||||
└── undo.go
|
||||
|
||||
internal/
|
||||
├── filters/
|
||||
├── history/
|
||||
├── listing/
|
||||
├── output/
|
||||
├── remove/
|
||||
├── replace/
|
||||
├── extension/
|
||||
├── insert/
|
||||
└── regex/ # NEW: pattern compilation, template evaluation, engine, ledger metadata
|
||||
|
||||
tests/
|
||||
├── contract/
|
||||
├── integration/
|
||||
├── fixtures/
|
||||
└── unit/
|
||||
|
||||
scripts/
|
||||
├── smoke-test-list.sh
|
||||
├── smoke-test-replace.sh
|
||||
├── smoke-test-remove.sh
|
||||
├── smoke-test-extension.sh
|
||||
├── smoke-test-insert.sh
|
||||
└── smoke-test-regex.sh # NEW
|
||||
```
|
||||
|
||||
**Structure Decision**: Extend the single CLI project by introducing `cmd/regex.go`, a new `internal/regex` package for rule evaluation, and corresponding contract/integration tests plus a smoke script under existing directories.
|
||||
|
||||
## Complexity Tracking
|
||||
|
||||
> **Fill ONLY if Constitution Check has violations that must be justified**
|
||||
|
||||
| Violation | Why Needed | Simpler Alternative Rejected Because |
|
||||
|-----------|------------|-------------------------------------|
|
||||
28
specs/006-add-regex-command/quickstart.md
Normal file
28
specs/006-add-regex-command/quickstart.md
Normal file
@@ -0,0 +1,28 @@
|
||||
# Quickstart – Regex Command
|
||||
|
||||
1. **Preview a capture-group rename before applying.**
|
||||
```bash
|
||||
renamer regex '^(\d{4})-(\d{2})_(.*)$' 'Q@2-@1_@3' --dry-run
|
||||
```
|
||||
- Converts `2025-01_report.txt` into `Q01-2025_report.txt` in preview mode.
|
||||
- Skipped files remain untouched and are labeled in the preview table.
|
||||
|
||||
2. **Limit scope with extension and directory flags.**
|
||||
```bash
|
||||
renamer regex '^(build)_(\d+)_v(.*)$' 'release-@2-@1-v@3' --path ./artifacts --extensions .zip|.tar.gz --include-dirs --dry-run
|
||||
```
|
||||
- Applies only to archives under `./artifacts`, including subdirectories when paired with `-r`.
|
||||
- Hidden files remain excluded unless `--hidden` is added.
|
||||
|
||||
3. **Apply changes non-interactively for automation.**
|
||||
```bash
|
||||
renamer regex '^(feature)-(.*)$' '@2-@1' --yes --path ./staging
|
||||
```
|
||||
- `--yes` confirms using the preview plan and writes a ledger entry containing pattern and template metadata.
|
||||
- Exit code `0` indicates success; non-zero signals validation or conflict issues.
|
||||
|
||||
4. **Undo the last regex batch if results are unexpected.**
|
||||
```bash
|
||||
renamer undo --path ./staging
|
||||
```
|
||||
- Restores original filenames using the `.renamer` ledger captured during apply.
|
||||
21
specs/006-add-regex-command/research.md
Normal file
21
specs/006-add-regex-command/research.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# Phase 0 Research – Regex Command
|
||||
|
||||
## Decision: Reuse Traversal, Preview, and Ledger Pipelines for Regex Rule
|
||||
- **Rationale**: Existing replace/remove/extension commands already walk the filesystem, apply scope filters, and feed preview + ledger writers. Plugging a regex rule into this pipeline guarantees consistent conflict detection, skipped reporting, and undo safety without reimplementing traversal safeguards.
|
||||
- **Alternatives considered**: Building a standalone regex walker was rejected because it would duplicate scope logic and risk violating Scope-Aware Traversal. Embedding regex into replace internals was rejected to keep literal and regex behaviors independent and easier to test.
|
||||
|
||||
## Decision: Compile Patterns with Go `regexp` (RE2) and Cache Group Metadata
|
||||
- **Rationale**: Go’s standard library provides RE2-backed regex compilation with deterministic performance and Unicode safety. Capturing the compiled expression once per invocation lets us pre-count capture groups, validate templates, and apply matches efficiently across many files.
|
||||
- **Alternatives considered**: Using third-party regex engines (PCRE) was rejected due to external dependencies and potential catastrophic backtracking. Recompiling the pattern per file was rejected for performance reasons.
|
||||
|
||||
## Decision: Validate and Render Templates via Placeholder Tokens (`@0`, `@1`, …, `@@`)
|
||||
- **Rationale**: Parsing the template into literal and placeholder segments ensures undefined group references surface as validation errors before preview/apply, while optional groups that fail to match substitute with empty strings. Doubling `@` (i.e., `@@`) yields a literal `@`, aligning with the clarification already captured in the specification.
|
||||
- **Alternatives considered**: Allowing implicit zero-value substitution for undefined groups was rejected because it hides mistakes. Relying on `fmt.Sprintf`-style formatting was rejected since it lacks direct mapping to numbered capture groups and complicates escaping rules.
|
||||
|
||||
## Decision: Ledger Metadata Includes Pattern, Template, and Match Snapshots
|
||||
- **Rationale**: Persisting the regex pattern, replacement template, scope flags, and per-file capture arrays alongside old/new paths enables precise undo and supports automation auditing. This mirrors expectations set for other commands and satisfies the Persistent Undo Ledger principle.
|
||||
- **Alternatives considered**: Logging only before/after filenames was rejected because undo would lose context if filenames changed again outside the tool. Capturing full file contents was rejected as unnecessary and intrusive.
|
||||
|
||||
## Decision: Block Apply When Template Yields Conflicts or Empty Targets
|
||||
- **Rationale**: Conflict detection will reuse existing duplicate/overwrite checks but extend them to treat empty or whitespace-only proposals as invalid. Apply exits non-zero when conflicts remain, protecting against accidental data loss or invalid filenames.
|
||||
- **Alternatives considered**: Auto-resolving conflicts by suffixing counters was rejected because it introduces nondeterministic results and complicates undo. Allowing empty targets was rejected for safety and compatibility reasons.
|
||||
107
specs/006-add-regex-command/spec.md
Normal file
107
specs/006-add-regex-command/spec.md
Normal file
@@ -0,0 +1,107 @@
|
||||
# Feature Specification: Regex Command for Pattern-Based Renaming
|
||||
|
||||
**Feature Branch**: `006-add-regex-command`
|
||||
**Created**: 2025-10-30
|
||||
**Status**: Draft
|
||||
**Input**: User description: "实现 regex 命令,用于使用正则获取指定位置内容后再重新命名,示例 renamer regexp <pattern> @1-@2 实现了获取正则的第一、二位的匹配数据,并进行重新命名"
|
||||
|
||||
## User Scenarios & Testing *(mandatory)*
|
||||
|
||||
### User Story 1 - Rename Files Using Captured Groups (Priority: P1)
|
||||
|
||||
As a power user organizing datasets, I want to rename files by extracting portions of their names via regular expressions so that I can normalize naming schemes without writing custom scripts.
|
||||
|
||||
**Why this priority**: Provides the core value—regex-driven renaming to rearrange captured data quickly across large batches.
|
||||
|
||||
**Independent Test**: In a directory with files named `2025-01_report.txt` and `2025-02_report.txt`, run `renamer regex "^(\\d{4})-(\\d{2})_report" "Q@2-@1" --dry-run` and verify the preview shows `Q01-2025.txt` and `Q02-2025.txt`. Re-run with `--yes` to confirm filesystem updates and ledger entry.
|
||||
|
||||
**Acceptance Scenarios**:
|
||||
|
||||
1. **Given** files `alpha-123.log` and `beta-456.log`, **When** the user runs `renamer regex "^(\\w+)-(\\d+)" "@2_@1" --dry-run`, **Then** the preview lists `123_alpha.log` and `456_beta.log` as proposed names.
|
||||
2. **Given** files that do not match the pattern, **When** the command runs in preview mode, **Then** unmatched files are listed with a "skipped" status and no filesystem changes occur on apply.
|
||||
|
||||
---
|
||||
|
||||
### User Story 2 - Automation-Friendly Regex Renames (Priority: P2)
|
||||
|
||||
As a DevOps engineer automating release artifact naming, I need deterministic exit codes, ledger metadata, and undo support for regex-based renames so CI pipelines remain auditable and reversible.
|
||||
|
||||
**Why this priority**: Ensures the new command can be safely adopted in automation without risking opaque failures.
|
||||
|
||||
**Independent Test**: Execute `renamer regex "^build_(\\d+)_(.*)$" "release-@1-@2" --yes --path ./fixtures`, verify exit code `0`, inspect `.renamer` for recorded pattern, replacement template, and affected files, then run `renamer undo` to restore originals.
|
||||
|
||||
**Acceptance Scenarios**:
|
||||
|
||||
1. **Given** a non-interactive run with `--yes`, **When** all matches succeed without conflicts, **Then** exit code is `0` and the ledger entry records the regex pattern, replacement template, and matching groups per file.
|
||||
2. **Given** a ledger entry produced by `renamer regex`, **When** `renamer undo` executes, **Then** filenames revert to their previous values even if the original files contained Unicode characters or were renamed by automation.
|
||||
|
||||
---
|
||||
|
||||
### User Story 3 - Validate Patterns, Placeholders, and Conflicts (Priority: P3)
|
||||
|
||||
As a user experimenting with regex templates, I want clear validation and preview feedback for invalid patterns, missing capture groups, or resulting conflicts so I can adjust commands before committing changes.
|
||||
|
||||
**Why this priority**: Prevents accidental data loss and reduces trial-and-error when constructing regex commands.
|
||||
|
||||
**Independent Test**: Run `renamer regex "^(.*)$" "@2" --dry-run` and confirm the command exits with a descriptive error because placeholder `@2` is undefined; run a scenario where multiple files would map to the same name and ensure apply is blocked.
|
||||
|
||||
**Acceptance Scenarios**:
|
||||
|
||||
1. **Given** a replacement template referencing an undefined capture group, **When** the command runs, **Then** it exits non-zero with a message explaining the missing group and no files change.
|
||||
2. **Given** two files whose matches produce identical targets, **When** preview executes, **Then** conflicts are listed and apply refuses to proceed until resolved.
|
||||
|
||||
---
|
||||
|
||||
### Edge Cases
|
||||
|
||||
- How does the command behave when the regex pattern is invalid or cannot compile?
|
||||
- What is the outcome when no files match the pattern (preview and apply)?
|
||||
- How are nested or optional groups handled when placeholders reference non-matching groups?
|
||||
- What happens if the replacement template results in empty filenames or removes extensions?
|
||||
- How are directories or hidden files treated when scope flags include/exclude them?
|
||||
- What feedback is provided when resulting names differ only by case on case-insensitive filesystems?
|
||||
|
||||
## Requirements *(mandatory)*
|
||||
|
||||
### Functional Requirements
|
||||
|
||||
- **FR-001**: CLI MUST provide a `regex` subcommand that accepts a required regex pattern and replacement template arguments (e.g., `renamer regex <pattern> <template>`).
|
||||
- **FR-002**: Replacement templates MUST support numbered capture placeholders (`@1`, `@2`, etc.) corresponding to the regex groups; referencing undefined groups MUST produce a validation error.
|
||||
- **FR-003**: Pattern matching MUST operate on the filename stem by default while preserving extensions unless the template explicitly alters them.
|
||||
- **FR-004**: Preview MUST display original names, proposed names, and highlight skipped entries (unmatched, invalid template) prior to apply; apply MUST be blocked when conflicts or validation errors exist.
|
||||
- **FR-005**: Execution MUST respect shared scope flags (`--path`, `--recursive`, `--include-dirs`, `--hidden`, `--extensions`, `--dry-run`, `--yes`) consistent with other commands.
|
||||
- **FR-006**: Ledger entries MUST capture the regex pattern, replacement template, and affected files so undo can restore originals deterministically.
|
||||
- **FR-007**: The command MUST emit deterministic exit codes: `0` for successful apply or no matches, non-zero for validation failures or conflicts.
|
||||
- **FR-008**: Help output MUST document pattern syntax expectations, placeholder usage, escaping rules, and examples for both files and directories.
|
||||
|
||||
### Key Entities
|
||||
|
||||
- **RegexRequest**: Working directory, regex pattern, replacement template, scope flags, dry-run/apply settings.
|
||||
- **RegexSummary**: Counts of matched files, skipped entries, conflicts, warnings, and preview entries with status (`changed`, `skipped`, `no_change`).
|
||||
|
||||
## Success Criteria *(mandatory)*
|
||||
|
||||
### Measurable Outcomes
|
||||
|
||||
- **SC-001**: Users rename 500 files via regex (preview + apply) in under 2 minutes end-to-end.
|
||||
- **SC-002**: 95% of beta testers correctly apply a regex rename after reading `renamer regex --help` without additional guidance.
|
||||
- **SC-003**: Automated regression tests confirm regex rename + undo cycles leave the filesystem unchanged in 100% of scripted scenarios.
|
||||
- **SC-004**: Support tickets related to custom regex renaming scripts drop by 30% within the first release cycle post-launch.
|
||||
|
||||
## Clarifications
|
||||
|
||||
### Session 2025-10-30
|
||||
- Q: How should literal @ characters be escaped in templates? → A: Use @@ to emit a literal @ while keeping numbered placeholders intact.
|
||||
|
||||
## Assumptions
|
||||
|
||||
- Regex evaluation uses the runtime’s built-in engine with RE2-compatible syntax; no backtracking-specific constructs (e.g., look-behind) are supported.
|
||||
- Matching applies to filename stems by default; users can reconstruct extensions via placeholders if required.
|
||||
- Unmatched files are skipped gracefully and reported in preview; apply exits `0` when all files are skipped.
|
||||
- Templates treat `@0` as the entire match if referenced; placeholders are case-sensitive and must be preceded by `@`. Use `@@` to emit a literal `@` character.
|
||||
|
||||
## Dependencies & Risks
|
||||
|
||||
- Requires extending existing traversal, preview, and ledger infrastructure to accommodate regex replacement logic.
|
||||
- Complex regex patterns may produce unexpected duplicates; conflict detection must guard against accidental overwrites.
|
||||
- Users may expect advanced regex features (named groups, non-ASCII classes); documentation must clarify supported syntax to prevent confusion.
|
||||
159
specs/006-add-regex-command/tasks.md
Normal file
159
specs/006-add-regex-command/tasks.md
Normal file
@@ -0,0 +1,159 @@
|
||||
# Tasks: Regex Command for Pattern-Based Renaming
|
||||
|
||||
**Input**: Design documents from `/specs/006-add-regex-command/`
|
||||
**Prerequisites**: plan.md (required), spec.md (required for user stories), research.md, data-model.md, contracts/
|
||||
|
||||
**Tests**: Include targeted contract and integration coverage where scenarios demand automated verification.
|
||||
|
||||
**Organization**: Tasks are grouped by user story to enable independent implementation and testing of each story.
|
||||
|
||||
## Format: `[ID] [P?] [Story] Description`
|
||||
|
||||
- **[P]**: Can run in parallel (different files, no dependencies)
|
||||
- **[Story]**: Which user story this task belongs to (e.g., US1, US2, US3)
|
||||
- Include exact file paths in descriptions
|
||||
|
||||
## Phase 1: Setup (Shared Infrastructure)
|
||||
|
||||
**Purpose**: Prepare fixtures and support tooling required across all stories.
|
||||
|
||||
- [X] T001 Create regex test fixtures (`tests/fixtures/regex/`) with sample filenames covering digits, words, and Unicode cases.
|
||||
- [X] T002 [P] Scaffold `scripts/smoke-test-regex.sh` mirroring quickstart scenarios for preview/apply/undo.
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: Foundational (Blocking Prerequisites)
|
||||
|
||||
**Purpose**: Establish reusable package skeletons and command registration that all stories build upon.
|
||||
|
||||
- [X] T003 Create `internal/regex` package scaffolding (request.go, summary.go, doc.go) matching data-model entities.
|
||||
- [X] T004 [P] Register a stub `regex` Cobra command in `cmd/regex.go` with flag definitions aligned to shared scope options.
|
||||
|
||||
**Checkpoint**: Foundation ready – user story implementation can now begin.
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: User Story 1 - Rename Files Using Captured Groups (Priority: P1) 🎯 MVP
|
||||
|
||||
**Goal**: Allow users to preview regex-based renames that substitute captured groups into templates while preserving extensions.
|
||||
|
||||
**Independent Test**: Run `renamer regex "^(\w+)-(\d+)" "@2_@1" --dry-run` against fixtures and verify preview outputs `123_alpha.log`, `456_beta.log` without modifying the filesystem.
|
||||
|
||||
### Tests for User Story 1
|
||||
|
||||
- [X] T005 [P] [US1] Add preview contract test for capture groups in `tests/contract/regex_command_test.go`.
|
||||
- [X] T006 [P] [US1] Add integration preview flow test covering dry-run confirmation in `tests/integration/regex_flow_test.go`.
|
||||
|
||||
### Implementation for User Story 1
|
||||
|
||||
- [X] T007 [P] [US1] Implement template parser handling `@n` and `@@` tokens in `internal/regex/template.go`.
|
||||
- [X] T008 [P] [US1] Implement regex engine applying capture groups to candidate names in `internal/regex/engine.go`.
|
||||
- [X] T009 [US1] Build preview planner producing `RegexSummary` entries in `internal/regex/preview.go`.
|
||||
- [X] T010 [US1] Wire Cobra command to preview/apply planner with scope options in `cmd/regex.go`.
|
||||
|
||||
**Checkpoint**: User Story 1 preview capability ready for validation.
|
||||
|
||||
---
|
||||
|
||||
## Phase 4: User Story 2 - Automation-Friendly Regex Renames (Priority: P2)
|
||||
|
||||
**Goal**: Deliver deterministic apply flows with ledger metadata and undo support suitable for CI automation.
|
||||
|
||||
**Independent Test**: Execute `renamer regex "^build_(\d+)_(.*)$" "release-@1-@2" --yes --path ./tests/fixtures/regex` and verify exit code `0`, ledger metadata, and successful `renamer undo` restoration.
|
||||
|
||||
### Tests for User Story 2
|
||||
|
||||
- [X] T011 [P] [US2] Add ledger contract test capturing pattern/template metadata in `tests/contract/regex_ledger_test.go`.
|
||||
- [X] T012 [P] [US2] Add integration undo flow test for regex entries in `tests/integration/regex_undo_test.go`.
|
||||
|
||||
### Implementation for User Story 2
|
||||
|
||||
- [X] T013 [P] [US2] Implement apply handler persisting ledger entries in `internal/regex/apply.go`.
|
||||
- [X] T014 [US2] Ensure `cmd/regex.go` honors `--yes` automation semantics and deterministic exit codes.
|
||||
- [X] T015 [US2] Extend undo recognition for regex batches in `internal/history/history.go` and shared output messaging.
|
||||
|
||||
**Checkpoint**: Automation-focused workflows (apply + undo) validated.
|
||||
|
||||
---
|
||||
|
||||
## Phase 5: User Story 3 - Validate Patterns, Placeholders, and Conflicts (Priority: P3)
|
||||
|
||||
**Goal**: Provide clear feedback for invalid patterns or template conflicts to prevent destructive applies.
|
||||
|
||||
**Independent Test**: Run `renamer regex "^(.*)$" "@2" --dry-run` and confirm an error about undefined capture groups; attempt a rename producing duplicate targets and confirm apply is blocked.
|
||||
|
||||
### Tests for User Story 3
|
||||
|
||||
- [X] T016 [P] [US3] Add validation contract tests for invalid patterns/placeholders in `tests/contract/regex_validation_test.go`.
|
||||
- [X] T017 [P] [US3] Add integration conflict test ensuring duplicate targets block apply in `tests/integration/regex_conflict_test.go`.
|
||||
|
||||
### Implementation for User Story 3
|
||||
|
||||
- [X] T018 [P] [US3] Implement validation for undefined groups and empty results in `internal/regex/validate.go`.
|
||||
- [X] T019 [US3] Extend conflict detection to flag duplicate or empty proposals in `internal/regex/preview.go`.
|
||||
- [X] T020 [US3] Enhance CLI error messaging and help examples in `cmd/regex.go`.
|
||||
|
||||
**Checkpoint**: Validation safeguards complete; regex command safe for experimentation.
|
||||
|
||||
---
|
||||
|
||||
## Phase 6: Polish & Cross-Cutting Concerns
|
||||
|
||||
**Purpose**: Final documentation, tooling, and quality passes.
|
||||
|
||||
- [X] T021 [P] Update CLI documentation with regex command details in `docs/cli-flags.md`.
|
||||
- [X] T022 [P] Finalize `scripts/smoke-test-regex.sh` to exercise quickstart scenarios and ledger undo.
|
||||
- [X] T023 Run `gofmt` and `go test ./...` to verify formatting and regression coverage.
|
||||
|
||||
---
|
||||
|
||||
## Dependencies & Execution Order
|
||||
|
||||
### Phase Dependencies
|
||||
|
||||
- **Setup (Phase 1)** → prerequisite for foundational work.
|
||||
- **Foundational (Phase 2)** → must complete before User Stories begin.
|
||||
- **User Stories (Phase 3–5)** → execute sequentially by priority or in parallel once dependencies satisfied.
|
||||
- **Polish (Phase 6)** → runs after desired user stories ship.
|
||||
|
||||
### User Story Dependencies
|
||||
|
||||
- **US1** depends on Foundational package scaffolding (T003–T004).
|
||||
- **US2** depends on US1 preview/apply wiring.
|
||||
- **US3** depends on US1 preview engine and US2 apply infrastructure to validate.
|
||||
|
||||
### Parallel Opportunities
|
||||
|
||||
- Tasks marked `[P]` operate on distinct files and can proceed concurrently once their prerequisites are met.
|
||||
- Different user stories can progress in parallel after their dependencies complete, provided shared files (`cmd/regex.go`, `internal/regex/preview.go`) are coordinated sequentially.
|
||||
|
||||
---
|
||||
|
||||
## Implementation Strategy
|
||||
|
||||
### MVP First (User Story 1 Only)
|
||||
|
||||
1. Complete Phase 1–2 to establish scaffolding.
|
||||
2. Implement US1 preview workflow (T005–T010) and validate independently.
|
||||
3. Ship preview-only capability if automation support can follow later.
|
||||
|
||||
### Incremental Delivery
|
||||
|
||||
1. Deliver US1 preview/apply basics.
|
||||
2. Layer US2 automation + ledger features.
|
||||
3. Add US3 validation/conflict safeguards.
|
||||
4. Conclude with polish tasks for docs, smoke script, and regression suite.
|
||||
|
||||
### Parallel Team Strategy
|
||||
|
||||
- Developer A focuses on template/engine internals (T007–T008) while Developer B builds tests (T005–T006).
|
||||
- After US1, split automation work: ledger implementation (T013) and undo validation tests (T012) run concurrently.
|
||||
- Validation tasks (T016–T020) can be parallelized between CLI messaging and conflict handling once US2 merges.
|
||||
|
||||
---
|
||||
|
||||
## Notes
|
||||
|
||||
- Keep task granularity small enough for independent completion while documenting file paths for each change.
|
||||
- Tests should fail before implementation to confirm coverage.
|
||||
- Mark tasks complete (`[X]`) in this document as work progresses.
|
||||
Reference in New Issue
Block a user