-
Notifications
You must be signed in to change notification settings - Fork 0
Description
Part of: #648
Part of: #EPIC_TBD
[Conversation Reference: "Story 8: Advanced Operations Parity - User Story: As a CIDX power user, I want to manage SSH keys and trigger advanced indexing operations from CLI remote mode, so that I have complete feature parity with REST/MCP interfaces."]
Story Overview
Objective: Enable advanced operations from CLI remote mode including SSH key management for repository access and advanced indexing control, providing complete parity with REST/MCP interfaces.
User Value: Power users can fully manage their CIDX environment from the command line, including setting up SSH keys for private repository access and controlling indexing operations at a granular level.
Acceptance Criteria Summary: SSH key management (create/list/delete/show-public/assign), Advanced indexing control (trigger/status/add-type).
Acceptance Criteria
AC1: Create SSH Key
Scenario: Generate new SSH key for repository access
Given the user is connected to a remote CIDX server
When the user runs "cidx keys create github-key --email [email protected]"
Then a new SSH key pair is generated on the server
And the CLI displays the public key for copying to GitHub/GitLab
And supports --key-type flag (ed25519 default, rsa)
And supports --description flag for key metadataTechnical Requirements:
- Create keys command group
- Implement create-key command
- Support --email flag for key comment
- Support --key-type flag (ed25519, rsa)
- Support --description flag
- Display public key in copyable format
- Return key name for reference
AC2: List SSH Keys
Scenario: View all SSH keys configured on server
Given the user is connected to a remote CIDX server
When the user runs "cidx keys list"
Then all SSH keys are listed
And shows: name, type, fingerprint, created_at, assigned_hosts
And supports --json output formatTechnical Requirements:
- Implement list-keys command
- Display Rich table by default
- Show key fingerprints (not full keys)
- Show assigned hosts for each key
- Support --json output format
AC3: Delete SSH Key
Scenario: Remove an SSH key from server
Given the user has SSH keys configured
When the user runs "cidx keys delete github-key"
Then confirmation prompt is displayed
And upon confirmation, key is deleted
And removes key from SSH config for assigned hosts
And supports --yes flag to skip confirmationTechnical Requirements:
- Implement delete-key command
- Require confirmation (--yes to skip)
- Remove from SSH config on deletion
- Handle key not found gracefully
- Warn about repositories using this key
AC4: Show Public Key
Scenario: Display public key for copying
Given the user has an SSH key configured
When the user runs "cidx keys show-public github-key"
Then the full public key is displayed
And formatted for direct copy/paste
And suitable for adding to GitHub/GitLab/etcTechnical Requirements:
- Implement show-public command
- Display complete public key
- Format appropriately for copying
- Handle key not found gracefully
AC5: Assign Key to Host
Scenario: Configure SSH key for specific host
Given the user has an SSH key
When the user runs "cidx keys assign github-key github.com"
Then the key is configured for that host in SSH config
And subsequent git operations to github.com use this key
And supports --force flag to replace existing assignmentTechnical Requirements:
- Implement assign-key command
- Update SSH config with Host entry
- Support --force flag for override
- Validate hostname format
- Show confirmation of assignment
AC6: Trigger Re-indexing
Scenario: Manually trigger indexing for repository
Given the user has an activated repository
When the user runs "cidx index trigger my-repo"
Then re-indexing is triggered for the repository
And job_id is returned for monitoring
And supports --clear flag to rebuild from scratch
And supports --types flag to specify index typesTechnical Requirements:
- Create index command group (if not exists)
- Implement trigger command
- Support --clear flag for full rebuild
- Support --types flag (semantic, fts, temporal, scip)
- Return job_id for async monitoring
AC7: Check Index Status
Scenario: View indexing status for repository
Given the user has a repository
When the user runs "cidx index status my-repo"
Then index status is displayed for all types:
- semantic: indexed (12,345 files)
- fts: indexed (12,345 files)
- temporal: not indexed
- scip: indexed (5 projects)
And shows last indexed timestamp
And shows health indicatorsTechnical Requirements:
- Implement index status command
- Show status for all index types
- Show file counts per type
- Show last indexed timestamps
- Support --json output format
AC8: Add Index Type
Scenario: Add new index type to existing repository
Given the user has a repository with basic indexing
When the user runs "cidx index add-type my-repo scip"
Then SCIP indexing is triggered for the repository
And job_id is returned for monitoring
And validates index type is validTechnical Requirements:
- Implement add-type command
- Validate index type parameter
- Return job_id for async monitoring
- Handle already-exists case gracefully
- Show estimated time if available
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/
ssh_api_client.py # SSHAPIClient class
- create_key()
- list_keys()
- delete_key()
- show_public()
- assign_host()
index_api_client.py # IndexAPIClient class
- trigger_reindex()
- get_index_status()
- add_index_type()
src/code_indexer/cli/commands/
keys.py # SSH key management commands
index.py # Indexing control commands (extend if exists)
Command Group Structure
@cli.group()
@requires_mode(remote=True)
def keys():
"""SSH key management for repository access"""
pass
@keys.command('create')
@click.argument('name')
@click.option('--email', help='Email for key comment')
@click.option('--key-type', type=click.Choice(['ed25519', 'rsa']), default='ed25519')
@click.option('--description', help='Key description/purpose')
@click.option('--json', 'output_json', is_flag=True)
def create_key(name, email, key_type, description, output_json):
"""Create a new SSH key pair"""
client = get_ssh_api_client()
result = client.create_key(name, email, key_type, description)
# Display public key for copying
@keys.command('list')
@click.option('--json', 'output_json', is_flag=True)
def list_keys(output_json):
"""List all SSH keys"""
pass
@cli.group()
@requires_mode(remote=True)
def index():
"""Index management commands"""
pass
@index.command('trigger')
@click.argument('repository')
@click.option('--clear', is_flag=True, help='Rebuild from scratch')
@click.option('--types', multiple=True, type=click.Choice(['semantic', 'fts', 'temporal', 'scip']))
@click.option('--json', 'output_json', is_flag=True)
def trigger_index(repository, clear, types, output_json):
"""Trigger re-indexing for repository"""
passAPI Client Pattern
class SSHAPIClient:
def __init__(self, remote_client: CIDXRemoteAPIClient):
self.client = remote_client
async def create_key(
self,
name: str,
email: Optional[str] = None,
key_type: str = 'ed25519',
description: Optional[str] = None
) -> SSHKeyResult:
response = await self.client.post(
"/api/v1/ssh/keys",
json={
"name": name,
"email": email,
"key_type": key_type,
"description": description
}
)
return SSHKeyResult(**response)
class IndexAPIClient:
async def trigger_reindex(
self,
repository: str,
clear: bool = False,
index_types: Optional[List[str]] = None
) -> TriggerResult:
response = await self.client.post(
f"/api/v1/index/{repository}/trigger",
json={
"clear": clear,
"index_types": index_types or []
}
)
return TriggerResult(**response)Testing Requirements
Unit Test Coverage
- SSH key creation sends correct payload
- Key listing parses response correctly
- Key deletion handles confirmation
- Index trigger sends correct parameters
- Index status parsing works correctly
Integration Test Coverage
- Create key generates valid key pair
- List keys shows all keys
- Assign key updates SSH config
- Trigger index creates background job
- Index status reflects actual state
E2E Test Coverage
- Full SSH workflow: create -> assign -> use
- Full index workflow: trigger -> monitor -> verify
- Add index type triggers correct indexer
- Key deletion removes from config
Performance Requirements
Response Time Targets
- Key operations: <2 seconds
- Index trigger (response): <2 seconds (job is async)
- Index status: <2 seconds
Resource Requirements
- Memory: Minimal
- Network: Standard REST calls
- CPU: Key generation on server
Error Handling Specifications
User-Friendly Error Messages
Error: SSH key 'github-key' already exists
Suggestion: Use different name or delete existing key first
Error: Host 'github.com' already has assigned key: other-key
Suggestion: Use --force to replace existing assignment
Error: Invalid index type: 'invalid'
Valid types: semantic, fts, temporal, scip
Error: SCIP indexing not supported for this repository
Details: No supported languages detected
Supported: Python, TypeScript, Java, C#, Go, Kotlin
Recovery Guidance
- Key exists: Suggest different name or delete
- Host assigned: Suggest --force flag
- Invalid index type: Show valid options
- Indexing not supported: Explain requirements
Definition of Done
Functional Completion
- SSH key create/list/delete/show works
- Key assignment to hosts works
- Index trigger with options works
- Index status shows all types
- Add index type 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 (8 operations)
Priority: Low (power user features)
Dependencies: Story 1 (Mode Detection)
Success Metric: Complete feature parity with REST/MCP