laststance / laststance/git-gpt-commit
ποΈ Refactor architecture to improve maintainability
Nobody has claimed this yet.
- Dominant language
- JavaScript
- Stars
- 36
- Forks
- 3
- PR merge metrics
- No merged PRs in 30d
Description
Problem
The current architecture has all logic in a single 389-line index.js file with multiple responsibilities, making it difficult to maintain and test.
Current issues:
- Single file handles: CLI routing, OpenAI API, config I/O, git operations, user prompts
- Global mutable state (
openai,model,language,apiKey) gitExtension()function is 202 lines (lines 184-386)gptCommit()mixes business logic with I/O operations- Hard to test components in isolation
Current Structure
index.js (389 lines)
ββ Global variables (state)
ββ saveConfig() / loadConfig()
ββ maskApiKey()
ββ getGitSummary()
ββ gptCommit()
ββ gitExtension() (CLI setup)
utils/
ββ sanitizeCommitMessage.js
Proposed Modular Structure
src/
ββ index.js # Entry point (~50 lines)
ββ cli/
β ββ commands/
β β ββ commit.js # Commit command handler
β β ββ model.js # Model selection command
β β ββ language.js # Language selection command
β β ββ prefix.js # Prefix toggle command
β β ββ apiKey.js # API key management command
β β ββ config.js # Config display command
β ββ index.js # CLI setup with Commander
ββ services/
β ββ OpenAIService.js # OpenAI API interactions
β ββ GitService.js # Git operations
β ββ ConfigService.js # Configuration management
ββ prompts/
β ββ modelSelection.js # Model selection prompt
β ββ languageSelection.js # Language selection prompt
β ββ confirmCommit.js # Commit confirmation prompt
β ββ apiKeyPrompts.js # API key prompts
ββ utils/
ββ sanitizeCommitMessage.js
ββ validators.js # Input validation utilities
Example Refactoring
Before (index.js - mixed concerns):
const gptCommit = async () => {
const gitSummary = await getGitSummary()
if (!gitSummary) {
console.log('No changes to commit. Commit canceled.')
process.exit(0)
}
const messages = [ /* ... */ ]
const parameters = { /* ... */ }
const response = await openai.chat.completions.create(parameters)
const message = response.choices[0].message.content.trim()
const sanitizedMessage = sanitizeCommitMessage(message)
const confirm = await prompts({ /* ... */ })
if (confirm.value) {
execSync(`git commit -m "${sanitizedMessage}"`)
console.log('Committed with the suggested message.')
} else {
console.log('Commit canceled.')
}
}
After (separation of concerns):
// src/services/OpenAIService.js
export class OpenAIService {
constructor(apiKey, model, language) {
this.openai = new OpenAI({ apiKey })
this.model = model
this.language = language
}
async generateCommitMessage(gitDiff, usePrefix) {
const messages = this.buildMessages(gitDiff, usePrefix)
const response = await this.callWithRetry({
model: this.model,
messages
})
return response.choices[0].message.content.trim()
}
buildMessages(gitDiff, usePrefix) { /* ... */ }
async callWithRetry(params, retries = 3) { /* ... */ }
}
// src/services/GitService.js
export class GitService {
async getStagedDiff() {
const { stdout } = await exec(
"git diff --cached -- . ':(exclude)*lock.json' ':(exclude)*lock.yaml'"
)
return stdout.trim() || null
}
async commit(message) {
execSync(`git commit -m "${message}"`)
}
}
// src/cli/commands/commit.js
export async function commitCommand(config) {
const gitService = new GitService()
const openaiService = new OpenAIService(
config.apiKey,
config.model,
config.language
)
const gitDiff = await gitService.getStagedDiff()
if (!gitDiff) {
console.log('No changes to commit.')
return
}
const message = await openaiService.generateCommitMessage(
gitDiff,
config.prefixEnabled
)
const sanitized = sanitizeCommitMessage(message)
if (await confirmCommitPrompt(sanitized)) {
await gitService.commit(sanitized)
console.log('β
Committed successfully.')
} else {
console.log('Commit canceled.')
}
}
Benefits
- β Single Responsibility: Each module has one clear purpose
- β Testability: Easy to test components in isolation
- β Maintainability: Easier to locate and modify functionality
- β Scalability: Simple to add new commands and features
- β Reusability: Services can be reused across commands
- β State Management: Eliminate global mutable state
Implementation Plan
Phase 1: Extract Services (Week 1)
- Create
src/services/OpenAIService.js - Create
src/services/GitService.js - Create
src/services/ConfigService.js - Add tests for each service
- Update index.js to use services
Phase 2: Extract Commands (Week 2)
- Create
src/cli/commands/directory - Extract each command to separate file
- Add tests for command handlers
- Update CLI setup to use command files
Phase 3: Extract Prompts (Week 3)
- Create
src/prompts/directory - Extract prompt logic from commands
- Add tests for prompts
- Update commands to use prompt modules
Phase 4: Cleanup (Week 4)
- Remove global state from index.js
- Update all imports and exports
- Run full test suite
- Update documentation
- Verify backward compatibility
Acceptance Criteria
- index.js reduced to < 100 lines (entry point only)
- Each service/command file < 200 lines
- No global mutable state
- 100% test coverage for new services
- All existing tests pass
- No breaking changes to CLI interface
Priority
High - Enables future development and maintenance
Related
Quality analysis report: claudedocs/quality-analysis-report.md section 1
Migration Notes
- Maintain backward compatibility during refactoring
- Use feature flags if needed for gradual rollout
- Document breaking changes clearly
- Update CLAUDE.md with new architecture
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up β it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reading index.js, utils/sanitizeCommitMessage.js, and section 1 of claudedocs/quality-analysis-report.md to map the current responsibilities. Compare that structure with the proposed src/ layout and implementation phases before making changes. Done means the stated acceptance criteria are met, existing tests pass, and the CLI remains backward compatible.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- git, javascript
- Domain
- cli, tooling
- Issue type
- Refactor
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 30/100