feat: implement AI-assisted rename prompting feature
- Added data model for AI-assisted renaming including structures for prompts, responses, and policies. - Created implementation plan detailing the integration of Google Genkit into the CLI for renaming tasks. - Developed quickstart guide for setting up and using the new AI rename functionality. - Documented research decisions regarding Genkit orchestration and prompt composition. - Established tasks for phased implementation, including setup, foundational work, and user stories. - Implemented contract tests to ensure AI rename policies and ledger metadata are correctly applied. - Developed integration tests for validating AI rename flows, including preview, apply, and undo functionalities. - Added tooling to pin Genkit dependency for consistent builds.
This commit is contained in:
@@ -7,6 +7,8 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/rogeecn/renamer/internal/ai/prompt"
|
||||
)
|
||||
|
||||
const ledgerFileName = ".renamer"
|
||||
@@ -26,6 +28,65 @@ type Entry struct {
|
||||
Metadata map[string]any `json:"metadata,omitempty"`
|
||||
}
|
||||
|
||||
const aiMetadataKey = "ai"
|
||||
|
||||
// AIMetadata captures AI-specific ledger metadata for rename batches.
|
||||
type AIMetadata struct {
|
||||
PromptHash string `json:"promptHash"`
|
||||
ResponseHash string `json:"responseHash"`
|
||||
Model string `json:"model"`
|
||||
Policies prompt.NamingPolicyConfig `json:"policies"`
|
||||
BatchSize int `json:"batchSize"`
|
||||
AppliedAt time.Time `json:"appliedAt"`
|
||||
}
|
||||
|
||||
// AttachAIMetadata records AI metadata alongside the ledger entry.
|
||||
func (e *Entry) AttachAIMetadata(meta AIMetadata) {
|
||||
if e.Metadata == nil {
|
||||
e.Metadata = make(map[string]any)
|
||||
}
|
||||
if meta.AppliedAt.IsZero() {
|
||||
meta.AppliedAt = time.Now().UTC()
|
||||
}
|
||||
e.Metadata[aiMetadataKey] = meta
|
||||
}
|
||||
|
||||
// AIMetadata extracts AI metadata from the ledger entry if present.
|
||||
func (e Entry) AIMetadata() (AIMetadata, bool) {
|
||||
if e.Metadata == nil {
|
||||
return AIMetadata{}, false
|
||||
}
|
||||
raw, ok := e.Metadata[aiMetadataKey]
|
||||
if !ok {
|
||||
return AIMetadata{}, false
|
||||
}
|
||||
|
||||
switch value := raw.(type) {
|
||||
case AIMetadata:
|
||||
return value, true
|
||||
case map[string]any:
|
||||
var meta AIMetadata
|
||||
if err := remarshal(value, &meta); err != nil {
|
||||
return AIMetadata{}, false
|
||||
}
|
||||
return meta, true
|
||||
default:
|
||||
var meta AIMetadata
|
||||
if err := remarshal(value, &meta); err != nil {
|
||||
return AIMetadata{}, false
|
||||
}
|
||||
return meta, true
|
||||
}
|
||||
}
|
||||
|
||||
func remarshal(value any, target any) error {
|
||||
data, err := json.Marshal(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return json.Unmarshal(data, target)
|
||||
}
|
||||
|
||||
// Append writes a new entry to the ledger in newline-delimited JSON format.
|
||||
func Append(workingDir string, entry Entry) error {
|
||||
entry.Timestamp = time.Now().UTC()
|
||||
|
||||
Reference in New Issue
Block a user