Skip to content

[STORY] File Management Operations from CLI Remote Mode #652

@jsbattig

Description

@jsbattig

Part of: #648

Part of: #EPIC_TBD

[Conversation Reference: "Story 4: File Management Operations from CLI Remote Mode - User Story: As a developer using CLI with remote server, I want to create, edit, and delete files in remote repositories, so that I can manage codebase files without leaving the command line."]

Story Overview

Objective: Enable file CRUD operations from CLI when connected to a remote CIDX server, with optimistic locking to prevent concurrent edit conflicts.

User Value: Developers can create, modify, and delete files in remote repositories directly from the CLI, maintaining their workflow without switching to web interfaces or separate tools.

Acceptance Criteria Summary: File create/edit/delete operations work with optimistic locking (content hash), support --dry-run for safety, and provide clear conflict detection.

Acceptance Criteria

AC1: Create File Operation

Scenario: Create a new file in remote repository

Given the user is connected to a remote CIDX server
And has an activated repository
When the user runs "cidx files create src/new_module.py --repository my-repo --content 'def hello(): pass'"
Then the file is created in the repository
And the CLI displays the created file path and content hash
And supports reading content from stdin or --content-file flag

Technical Requirements:

  • Create FileAPIClient.create_file() method
  • Support --content flag for inline content
  • Support --content-file flag to read from local file
  • Support reading from stdin (piped input)
  • Return content_hash for subsequent edits
  • Validate file doesn't already exist

AC2: Edit File Operation with Optimistic Locking

Scenario: Edit an existing file with conflict detection

Given the user is connected to a remote CIDX server
And wants to edit a file in an activated repository
When the user runs "cidx files edit src/auth.py --repository my-repo --old-string 'old_func' --new-string 'new_func' --content-hash abc123"
Then the edit is applied if content_hash matches current file state
And the CLI displays the updated content_hash
And if hash mismatch, displays conflict error with current file state

Technical Requirements:

  • Create FileAPIClient.edit_file() method
  • Require --content-hash for optimistic locking
  • Support --old-string and --new-string for replacement
  • Support --replace-all flag for multiple occurrences
  • Return new content_hash after edit
  • Clear conflict detection and reporting

AC3: Delete File Operation

Scenario: Delete a file from remote repository

Given the user is connected to a remote CIDX server
And has an activated repository
When the user runs "cidx files delete src/old_module.py --repository my-repo"
Then the file is deleted from the repository
And CLI displays confirmation message
And supports optional --content-hash for safety validation

Technical Requirements:

  • Create FileAPIClient.delete_file() method
  • Support optional --content-hash for safety
  • Require confirmation (--yes flag to skip)
  • Handle file not found gracefully
  • Provide undo guidance in confirmation

AC4: Get File Content with Hash

Scenario: Read file content and get current hash

Given the user is connected to a remote CIDX server
When the user runs "cidx files get src/auth.py --repository my-repo"
Then the CLI displays file content
And shows current content_hash for use in edit operations
And supports --hash-only flag to just get hash
And supports pagination with --offset and --limit for large files

Technical Requirements:

  • Create FileAPIClient.get_file() method (may reuse existing)
  • Display content_hash in output
  • Support --hash-only flag
  • Support --offset and --limit for pagination
  • Handle binary files appropriately

AC5: Dry-Run Mode for Safety

Scenario: Preview operations before execution

Given the user wants to verify an operation before executing
When the user runs "cidx files edit ... --dry-run"
Then the CLI displays what would happen without making changes
And shows the diff that would result from the edit
And indicates "DRY RUN - no changes made"

Technical Requirements:

  • Implement --dry-run flag for all write operations
  • Display preview of changes for edit operations
  • Show confirmation prompt summary
  • Exit with success status (0) for dry-run

AC6: Concurrent Edit Conflict Handling

Scenario: Detect and report concurrent edit conflicts

Given user A has read file with hash abc123
And user B has since edited the file (new hash def456)
When user A runs edit with --content-hash abc123
Then the CLI displays "Conflict: File has been modified"
And shows diff between expected and current content
And provides current hash for retry
And suggests workflow to resolve

Technical Requirements:

  • Detect hash mismatch from API response
  • Display informative conflict message
  • Show current file content or diff
  • Provide current hash for retry
  • Suggest resolution workflow

Implementation Status

Progress Tracking:

  • Core implementation complete
  • Unit tests passing (X/Y tests)
  • Integration tests passing (X/Y tests)
  • E2E tests passing (X/Y tests)
  • Code review approved
  • Manual E2E testing completed by Claude Code
  • Documentation updated

Completion: 0/7 tasks complete (0%)

Technical Implementation Details

Component Structure

src/code_indexer/client/
  file_api_client.py        # FileAPIClient class
    - create_file()
    - edit_file()
    - delete_file()
    - get_file()            # May reuse existing

src/code_indexer/cli/commands/
  files.py                  # New files command group

Command Group Structure

@cli.group()
def files():
    """File operations for remote repositories"""
    pass

@files.command()
@requires_mode(remote=True)
@click.option('--repository', '-r', required=True)
@click.option('--content', help='Inline content')
@click.option('--content-file', type=click.Path(exists=True), help='Read content from file')
@click.option('--json', 'output_json', is_flag=True)
def create(file_path, repository, content, content_file, output_json):
    """Create a new file"""
    if content_file:
        content = Path(content_file).read_text()
    elif not content:
        content = sys.stdin.read()

    client = get_file_api_client()
    result = client.create_file(repository, file_path, content)
    # Format and display

@files.command()
@requires_mode(remote=True)
@click.option('--repository', '-r', required=True)
@click.option('--old-string', required=True)
@click.option('--new-string', required=True)
@click.option('--content-hash', required=True)
@click.option('--replace-all', is_flag=True)
@click.option('--dry-run', is_flag=True)
def edit(file_path, repository, old_string, new_string, content_hash, replace_all, dry_run):
    """Edit an existing file using string replacement"""
    pass

API Client Pattern

class FileAPIClient:
    def __init__(self, remote_client: CIDXRemoteAPIClient):
        self.client = remote_client

    async def create_file(
        self,
        repository: str,
        file_path: str,
        content: str
    ) -> FileOperationResult:
        response = await self.client.post(
            f"/api/v1/files/{repository}/create",
            json={"file_path": file_path, "content": content}
        )
        return FileOperationResult(**response)

    async def edit_file(
        self,
        repository: str,
        file_path: str,
        old_string: str,
        new_string: str,
        content_hash: str,
        replace_all: bool = False
    ) -> FileOperationResult:
        response = await self.client.post(
            f"/api/v1/files/{repository}/edit",
            json={
                "file_path": file_path,
                "old_string": old_string,
                "new_string": new_string,
                "content_hash": content_hash,
                "replace_all": replace_all
            }
        )
        return FileOperationResult(**response)

Content Hash Workflow

1. Read file: cidx files get src/auth.py --repository my-repo
   -> Returns content + content_hash: "abc123def..."

2. Edit file: cidx files edit src/auth.py --old-string "old" --new-string "new" --content-hash "abc123def..." --repository my-repo
   -> If hash matches: Edit succeeds, returns new hash
   -> If hash mismatch: Conflict error with current hash

3. Continue with new hash for subsequent edits

Testing Requirements

Unit Test Coverage

  • FileAPIClient.create_file() sends correct payload
  • FileAPIClient.edit_file() includes content_hash
  • FileAPIClient.delete_file() handles confirmation
  • Content hash validation works correctly
  • --dry-run prevents actual changes

Integration Test Coverage

  • Create file appears in repository
  • Edit file changes content correctly
  • Delete file removes from repository
  • Hash mismatch triggers conflict error
  • Concurrent edit scenario detected

E2E Test Coverage

  • Complete workflow: create -> edit -> delete
  • Conflict detection with simulated concurrent edit
  • Dry-run shows preview without changes
  • Large file pagination works

Performance Requirements

Response Time Targets

  • Create/edit small files (<1MB): <2 seconds
  • Delete operations: <1 second
  • Get file content: Proportional to size
  • Hash calculation: Instant (done on server)

Resource Requirements

  • Memory: Proportional to file size
  • Network: File content transferred
  • CPU: Minimal (hash calculation on server)

Error Handling Specifications

User-Friendly Error Messages

Error: File already exists at 'src/new_module.py'
Suggestion: Use 'cidx files edit' to modify existing file

Error: Conflict - File has been modified since your last read
Expected hash: abc123...
Current hash: def456...
Suggestion: Run 'cidx files get src/auth.py' to get current content and hash

Error: File not found: 'src/missing.py'
Repository: my-repo
Suggestion: Check file path or use 'cidx files create' for new files

Error: old_string not found in file
Search string: 'function_name'
Suggestion: Verify the exact string including whitespace

Recovery Guidance

  • File exists: Suggest edit instead of create
  • Hash mismatch: Provide current hash and suggest re-read
  • File not found: Suggest checking path or creating
  • String not found: Show file content for verification

Definition of Done

Functional Completion

  • Create file operation works
  • Edit file operation works with optimistic locking
  • Delete file operation works with confirmation
  • Get file operation provides content hash
  • Dry-run mode works for all write operations
  • Conflict detection and reporting works

Quality Validation

  • >90% test coverage achieved
  • All tests passing (unit, integration, E2E)
  • Code review approved
  • Manual testing validated with evidence
  • Performance benchmarks met

Integration Readiness

  • Story delivers working, deployable software
  • Full vertical slice implemented
  • No broken functionality
  • Documentation complete

Story Points: Medium (4 main operations)
Priority: High (essential for remote development)
Dependencies: Story 1 (Mode Detection)
Success Metric: File CRUD operations work with proper conflict detection

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions