As a qctl user, I want qctl qqq list to fetch templates from a mock Voyage API so that the command works like the production flow will, even before Voyage is ready.
Design
Current State
Templates are fetched directly from GitHub:
Clone QRun-IO/templates-hub repository
Read directories as available templates
No registry metadata
Target State
Templates listed from mock API (and later real Voyage):
$ qctl qqq list
Available templates:
qqq-web-api v1.2.0 Web API application with Javalin middleware
qqq-cli-tool v1.0.0 Command-line tool with Picocli
qqq-batch v1.1.0 Batch processing application
qqq-full-stack v0.9.0 Full-stack with React frontend
Mock API Design
Serve from static JSON or embedded response:
{"templates":[{"id":"qqq-web-api","name":"QQQ Web API","version":"1.2.0","description":"Web API application with Javalin middleware","downloadUrl":"https://github.com/QRun-IO/templates-hub/archive/refs/heads/main.zip"}]}
Create TemplateInfo record (id, name, version, description, downloadUrl)
Create MockTemplateRegistry with hardcoded templates
Embed mock data as resource file or constants
Refactor ListCommand to use TemplateRegistry
Refactor InitCommand to use TemplateRegistry.downloadTemplate()
Add configuration for registry URL (for future Voyage) - via QCTL_REGISTRY_TYPE env var
Add caching layer with TTL for template list - 15-minute TTL in GitHubTemplateRegistry
Add --refresh flag to bypass cache
Add unit tests for mock registry - 13 tests (MockTemplateRegistryTest + TemplateRegistryFactoryTest)
Document API contract for future Voyage integration - TemplateRegistry interface
Acceptance Criteria
qctl qqq list shows templates from mock registry
Template metadata includes version and description
qctl qqq init <template> downloads from registry
Registry interface ready for Voyage implementation
Cache prevents repeated API calls
--refresh bypasses cache
Offline fallback to cached data
Part of #12
## User Story
As a **qctl user**, I want `qctl qqq list` to fetch templates from a mock Voyage API so that the command works like the production flow will, even before Voyage is ready.
## Design
### Current State
Templates are fetched directly from GitHub:
- Clone `QRun-IO/templates-hub` repository
- Read directories as available templates
- No registry metadata
### Target State
Templates listed from mock API (and later real Voyage):
```bash
$ qctl qqq list
Available templates:
qqq-web-api v1.2.0 Web API application with Javalin middleware
qqq-cli-tool v1.0.0 Command-line tool with Picocli
qqq-batch v1.1.0 Batch processing application
qqq-full-stack v0.9.0 Full-stack with React frontend
```
### Mock API Design
Serve from static JSON or embedded response:
```json
{
"templates": [
{
"id": "qqq-web-api",
"name": "QQQ Web API",
"version": "1.2.0",
"description": "Web API application with Javalin middleware",
"downloadUrl": "https://github.com/QRun-IO/templates-hub/archive/refs/heads/main.zip"
}
]
}
```
### API Client Interface
```java
public interface TemplateRegistry {
List<TemplateInfo> listTemplates() throws IOException;
TemplateInfo getTemplate(String id) throws IOException;
Path downloadTemplate(String id, String version) throws IOException;
}
public class MockTemplateRegistry implements TemplateRegistry { }
public class VoyageTemplateRegistry implements TemplateRegistry { } // Future
```
## Files to Create/Modify
| File | Action | Description |
|------|--------|-------------|
| `TemplateRegistry.java` | Create | Interface for template listing/download |
| `TemplateInfo.java` | Create | DTO for template metadata |
| `MockTemplateRegistry.java` | Create | Mock implementation with embedded data |
| `ListCommand.java` | Modify | Use TemplateRegistry instead of GitHub |
| `InitCommand.java` | Modify | Use TemplateRegistry for template fetch |
| `mock-templates.json` | Create | Embedded mock template list |
## Implementation Tasks
- [x] Define `TemplateRegistry` interface
- [x] Create `TemplateInfo` record (id, name, version, description, downloadUrl)
- [x] Create `MockTemplateRegistry` with hardcoded templates
- [x] Embed mock data as resource file or constants
- [x] Refactor `ListCommand` to use `TemplateRegistry`
- [x] Refactor `InitCommand` to use `TemplateRegistry.downloadTemplate()`
- [x] Add configuration for registry URL (for future Voyage) - via QCTL_REGISTRY_TYPE env var
- [x] Add caching layer with TTL for template list - 15-minute TTL in GitHubTemplateRegistry
- [x] Add `--refresh` flag to bypass cache
- [x] Add unit tests for mock registry - 13 tests (MockTemplateRegistryTest + TemplateRegistryFactoryTest)
- [x] Document API contract for future Voyage integration - TemplateRegistry interface
## Acceptance Criteria
- [x] `qctl qqq list` shows templates from mock registry
- [x] Template metadata includes version and description
- [x] `qctl qqq init <template>` downloads from registry
- [x] Registry interface ready for Voyage implementation
- [x] Cache prevents repeated API calls
- [x] `--refresh` bypasses cache
- [x] Offline fallback to cached data
TemplateRegistry.java - Interface with list/get/download/refresh methods
TemplateInfo.java - Record for template metadata with toManifest() conversion
MockTemplateRegistry.java - Embedded mock data for 3 templates
GitHubTemplateRegistry.java - Wraps TemplatesHub with 15-minute cache TTL
TemplateRegistryFactory.java - Factory using QCTL_REGISTRY_TYPE env var
Files Modified
ListCommand.java - Uses TemplateRegistry, added --refresh flag
InitCommand.java - Uses TemplateRegistry for template selection/download
reflect-config.json - Added TemplateInfo for native-image
Tests Added
MockTemplateRegistryTest.java - 10 tests
TemplateRegistryFactoryTest.java - 3 tests
Usage
# Default (GitHub registry)
qctl qqq list
# Force cache refresh
qctl qqq list --refresh
# Use mock registry for testingQCTL_REGISTRY_TYPE=mock qctl qqq list
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Part of #12
User Story
As a qctl user, I want
qctl qqq listto fetch templates from a mock Voyage API so that the command works like the production flow will, even before Voyage is ready.Design
Current State
Templates are fetched directly from GitHub:
QRun-IO/templates-hubrepositoryTarget State
Templates listed from mock API (and later real Voyage):
Mock API Design
Serve from static JSON or embedded response:
API Client Interface
Files to Create/Modify
TemplateRegistry.javaTemplateInfo.javaMockTemplateRegistry.javaListCommand.javaInitCommand.javamock-templates.jsonImplementation Tasks
TemplateRegistryinterfaceTemplateInforecord (id, name, version, description, downloadUrl)MockTemplateRegistrywith hardcoded templatesListCommandto useTemplateRegistryInitCommandto useTemplateRegistry.downloadTemplate()--refreshflag to bypass cacheAcceptance Criteria
qctl qqq listshows templates from mock registryqctl qqq init <template>downloads from registry--refreshbypasses cacheImplementation Complete
Files Created
TemplateRegistry.java- Interface with list/get/download/refresh methodsTemplateInfo.java- Record for template metadata with toManifest() conversionMockTemplateRegistry.java- Embedded mock data for 3 templatesGitHubTemplateRegistry.java- Wraps TemplatesHub with 15-minute cache TTLTemplateRegistryFactory.java- Factory using QCTL_REGISTRY_TYPE env varFiles Modified
ListCommand.java- Uses TemplateRegistry, added--refreshflagInitCommand.java- Uses TemplateRegistry for template selection/downloadreflect-config.json- Added TemplateInfo for native-imageTests Added
MockTemplateRegistryTest.java- 10 testsTemplateRegistryFactoryTest.java- 3 testsUsage
Commit:
e14565a