gitxplain 0.1.6 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,158 @@
1
+ import { appendFileSync, existsSync, mkdirSync, readFileSync, rmSync } from "node:fs";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+
5
+ function getUsageLogPath() {
6
+ return path.join(os.homedir(), ".gitxplain", "usage.jsonl");
7
+ }
8
+
9
+ export function getUsageLogFile() {
10
+ return getUsageLogPath();
11
+ }
12
+
13
+ function readRecords() {
14
+ const filePath = getUsageLogPath();
15
+ if (!existsSync(filePath)) {
16
+ return [];
17
+ }
18
+
19
+ return readFileSync(filePath, "utf8")
20
+ .split("\n")
21
+ .map((line) => line.trim())
22
+ .filter(Boolean)
23
+ .map((line) => JSON.parse(line));
24
+ }
25
+
26
+ function parseNumeric(value) {
27
+ return typeof value === "number" && Number.isFinite(value) ? value : 0;
28
+ }
29
+
30
+ export function normalizeUsageMetrics(usage) {
31
+ if (!usage || typeof usage !== "object") {
32
+ return {
33
+ inputTokens: 0,
34
+ outputTokens: 0,
35
+ totalTokens: 0
36
+ };
37
+ }
38
+
39
+ const inputTokens =
40
+ parseNumeric(usage.prompt_tokens) ||
41
+ parseNumeric(usage.input_tokens) ||
42
+ parseNumeric(usage.promptTokenCount);
43
+ const outputTokens =
44
+ parseNumeric(usage.completion_tokens) ||
45
+ parseNumeric(usage.output_tokens) ||
46
+ parseNumeric(usage.candidatesTokenCount);
47
+ const totalTokens =
48
+ parseNumeric(usage.total_tokens) ||
49
+ parseNumeric(usage.totalTokenCount) ||
50
+ inputTokens + outputTokens;
51
+
52
+ return {
53
+ inputTokens,
54
+ outputTokens,
55
+ totalTokens
56
+ };
57
+ }
58
+
59
+ function parseEnvPrice(envKey) {
60
+ const raw = process.env[envKey];
61
+ if (raw == null || raw === "") {
62
+ return null;
63
+ }
64
+
65
+ const parsed = Number.parseFloat(raw);
66
+ return Number.isFinite(parsed) && parsed >= 0 ? parsed : null;
67
+ }
68
+
69
+ export function resolvePricing(config) {
70
+ const providerKey = config.provider.toUpperCase().replace(/[^A-Z0-9]+/g, "_");
71
+ const modelKey = String(config.model ?? "default").toUpperCase().replace(/[^A-Z0-9]+/g, "_");
72
+
73
+ const inputPerMillion =
74
+ parseEnvPrice(`${providerKey}_${modelKey}_INPUT_COST_PER_MTOK`) ??
75
+ parseEnvPrice(`${providerKey}_INPUT_COST_PER_MTOK`) ??
76
+ parseEnvPrice("LLM_INPUT_COST_PER_MTOK");
77
+ const outputPerMillion =
78
+ parseEnvPrice(`${providerKey}_${modelKey}_OUTPUT_COST_PER_MTOK`) ??
79
+ parseEnvPrice(`${providerKey}_OUTPUT_COST_PER_MTOK`) ??
80
+ parseEnvPrice("LLM_OUTPUT_COST_PER_MTOK");
81
+
82
+ if (inputPerMillion == null || outputPerMillion == null) {
83
+ return null;
84
+ }
85
+
86
+ return {
87
+ inputPerMillion,
88
+ outputPerMillion
89
+ };
90
+ }
91
+
92
+ export function estimateCostUsd(usage, pricing) {
93
+ if (!pricing) {
94
+ return null;
95
+ }
96
+
97
+ const metrics = normalizeUsageMetrics(usage);
98
+ const costUsd =
99
+ (metrics.inputTokens / 1_000_000) * pricing.inputPerMillion +
100
+ (metrics.outputTokens / 1_000_000) * pricing.outputPerMillion;
101
+
102
+ return Number.isFinite(costUsd) ? costUsd : null;
103
+ }
104
+
105
+ export function appendUsageRecord({ provider, model, usage, latencyMs, estimatedCostUsd }) {
106
+ const metrics = normalizeUsageMetrics(usage);
107
+ if (metrics.totalTokens === 0 && estimatedCostUsd == null) {
108
+ return;
109
+ }
110
+
111
+ const filePath = getUsageLogPath();
112
+ mkdirSync(path.dirname(filePath), { recursive: true });
113
+ appendFileSync(
114
+ filePath,
115
+ `${JSON.stringify({
116
+ timestamp: new Date().toISOString(),
117
+ provider,
118
+ model,
119
+ usage: metrics,
120
+ latencyMs: latencyMs ?? null,
121
+ estimatedCostUsd
122
+ })}\n`,
123
+ "utf8"
124
+ );
125
+ }
126
+
127
+ export function getUsageStats() {
128
+ const records = readRecords();
129
+
130
+ return records.reduce(
131
+ (summary, record) => {
132
+ summary.requestCount += 1;
133
+ summary.inputTokens += parseNumeric(record.usage?.inputTokens);
134
+ summary.outputTokens += parseNumeric(record.usage?.outputTokens);
135
+ summary.totalTokens += parseNumeric(record.usage?.totalTokens);
136
+ summary.estimatedCostUsd += parseNumeric(record.estimatedCostUsd);
137
+ return summary;
138
+ },
139
+ {
140
+ requestCount: 0,
141
+ inputTokens: 0,
142
+ outputTokens: 0,
143
+ totalTokens: 0,
144
+ estimatedCostUsd: 0
145
+ }
146
+ );
147
+ }
148
+
149
+ export function clearUsageLog() {
150
+ const filePath = getUsageLogPath();
151
+ const count = readRecords().length;
152
+
153
+ if (existsSync(filePath)) {
154
+ rmSync(filePath, { force: true });
155
+ }
156
+
157
+ return count;
158
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gitxplain",
3
- "version": "0.1.6",
3
+ "version": "0.1.9",
4
4
  "description": "AI-powered Git commit explainer CLI",
5
5
  "type": "module",
6
6
  "bin": {
@@ -9,7 +9,7 @@
9
9
  },
10
10
  "scripts": {
11
11
  "start": "node ./cli/index.js",
12
- "lint": "node --check ./cli/index.js && node --check ./cli/services/chatService.js && node --check ./cli/services/gitConnectionService.js && node --check ./cli/services/envLoader.js && node --check ./cli/services/pipelineService.js",
12
+ "lint": "node --check ./cli/index.js && node --check ./cli/services/envLoader.js && node --check ./cli/services/pipelineService.js",
13
13
  "test": "node --test"
14
14
  },
15
15
  "keywords": [
@@ -0,0 +1,29 @@
1
+ Analyze this git blame report for a file and explain ownership patterns.
2
+
3
+ Context:
4
+ - The input below is not a diff. It is a compact blame summary built from `git blame --line-porcelain`.
5
+ - Focus on who changed which parts of the file, when the major edits happened, and what kinds of changes appear to have shaped the file.
6
+ - Call out concentrated ownership, legacy areas, and spots that may need extra care during onboarding or refactoring.
7
+
8
+ Commit Message:
9
+ {{commit_message}}
10
+
11
+ Files Changed:
12
+ {{files_changed}}
13
+
14
+ Stats:
15
+ {{stats}}
16
+
17
+ Blame Report:
18
+ {{diff}}
19
+
20
+ Return:
21
+
22
+ 1. Ownership Summary:
23
+ - Summarize the main authors or time periods that shaped the file
24
+
25
+ 2. Likely Change Themes:
26
+ - Explain what kinds of changes the major contributors appear to have made
27
+
28
+ 3. Onboarding Notes:
29
+ - Suggest where a new maintainer should read carefully, who to ask first, or what parts look risky or stable
@@ -0,0 +1,36 @@
1
+ Generate release notes in a conventional-changelog style.
2
+
3
+ Context:
4
+ - This may represent a single commit or a range of commits.
5
+ - Prefer grouping entries under headings like Features, Fixes, Refactors, Docs, Tests, Chores, and Breaking Changes when supported by the evidence.
6
+ - Use commit messages first, then use the diff to sharpen unclear items.
7
+ - Be conservative and do not invent user-facing changes that are not visible in the Git data.
8
+
9
+ Commit Message:
10
+ {{commit_message}}
11
+
12
+ Files Changed:
13
+ {{files_changed}}
14
+
15
+ Stats:
16
+ {{stats}}
17
+
18
+ Change Hints:
19
+ {{change_hints}}
20
+
21
+ Diff:
22
+ {{diff}}
23
+
24
+ Return:
25
+
26
+ 1. Release Summary:
27
+ - One short paragraph describing the overall release/change set
28
+
29
+ 2. Changelog:
30
+ - Group entries under relevant categories
31
+ - Keep bullets concise and user-facing
32
+ - If there are no clear entries for a category, omit it
33
+
34
+ 3. Upgrade Notes:
35
+ - Mention any migration steps, rollout cautions, or compatibility risks
36
+ - If none are apparent, say so clearly
@@ -0,0 +1,33 @@
1
+ You are helping resolve unresolved Git merge conflicts.
2
+
3
+ Context:
4
+ - The input below is a structured report extracted from conflict markers in the working tree.
5
+ - Explain what each side appears to be doing, suggest the most likely safe resolution, and call out any ambiguity that requires human review.
6
+ - Be conservative. Do not pretend a single resolution is certain when the intent is unclear.
7
+
8
+ Conflict Summary:
9
+ {{commit_message}}
10
+
11
+ Files Changed:
12
+ {{files_changed}}
13
+
14
+ Stats:
15
+ {{stats}}
16
+
17
+ Conflict Report:
18
+ {{diff}}
19
+
20
+ Return:
21
+
22
+ 1. Conflict Summary:
23
+ - Explain the overall source of the conflict
24
+
25
+ 2. Suggested Resolution:
26
+ - For each file or conflict block, describe the most likely resolution
27
+ - Be explicit about which lines to keep, merge, or rewrite
28
+
29
+ 3. Why:
30
+ - Explain why the suggested resolution is the safest or most consistent
31
+
32
+ 4. Follow-Up Checks:
33
+ - Suggest tests, manual verification steps, or edge cases to validate after resolving the conflict
@@ -0,0 +1,40 @@
1
+ Write a pull request description for this change set.
2
+
3
+ Context:
4
+ - This may be a single commit, a commit range, or a branch comparison.
5
+ - The result should be ready to paste into GitHub or GitLab.
6
+ - Be accurate and concise. Do not invent testing or screenshots that are not evidenced by the diff.
7
+
8
+ Commit Message:
9
+ {{commit_message}}
10
+
11
+ Files Changed:
12
+ {{files_changed}}
13
+
14
+ Stats:
15
+ {{stats}}
16
+
17
+ Change Hints:
18
+ {{change_hints}}
19
+
20
+ Diff:
21
+ {{diff}}
22
+
23
+ Return:
24
+
25
+ ## Summary
26
+ - Short bullets describing the main changes
27
+
28
+ ## Why
29
+ - Why this PR exists
30
+
31
+ ## Testing
32
+ - Mention visible testing evidence from the diff if any
33
+ - If no testing evidence is visible, say "Not specified in diff"
34
+
35
+ ## Risks
36
+ - Briefly describe rollout, compatibility, or review risks
37
+
38
+ ## Screenshots
39
+ - If the diff suggests UI work, include "Screenshots: Needed"
40
+ - Otherwise include "Screenshots: Not needed"
@@ -0,0 +1,29 @@
1
+ Review this change for refactoring opportunities.
2
+
3
+ Commit Message:
4
+ {{commit_message}}
5
+
6
+ Files Changed:
7
+ {{files_changed}}
8
+
9
+ Stats:
10
+ {{stats}}
11
+
12
+ Change Hints:
13
+ {{change_hints}}
14
+
15
+ Diff:
16
+ {{diff}}
17
+
18
+ Return:
19
+
20
+ 1. Refactor Opportunities:
21
+ - List concrete refactors suggested by the code shown
22
+ - Focus on duplication, naming, cohesion, dead code, control flow, error handling, and API clarity
23
+ - If there are no worthwhile refactors, say so explicitly
24
+
25
+ 2. Why These Matter:
26
+ - Explain the maintenance or correctness benefits of the top opportunities
27
+
28
+ 3. Safe Next Steps:
29
+ - Suggest a small, practical follow-up plan that avoids mixing refactors with risky behavior changes
@@ -0,0 +1,34 @@
1
+ Explain the contents of this Git stash entry.
2
+
3
+ Context:
4
+ - This stash may include staged work, unstaged work, or both.
5
+ - Focus on what work is preserved here, why it may have been stashed, and what to watch for before applying it.
6
+
7
+ Stash Message:
8
+ {{commit_message}}
9
+
10
+ Files Changed:
11
+ {{files_changed}}
12
+
13
+ Stats:
14
+ {{stats}}
15
+
16
+ Change Hints:
17
+ {{change_hints}}
18
+
19
+ Diff:
20
+ {{diff}}
21
+
22
+ Return:
23
+
24
+ 1. Summary:
25
+ - Explain what work is stored in the stash
26
+
27
+ 2. Likely Intent:
28
+ - Explain why a developer might have stashed these changes
29
+
30
+ 3. Key Changes:
31
+ - Summarize the important code or file-level changes
32
+
33
+ 4. Reapply Notes:
34
+ - Mention any cautions, missing context, or likely conflicts to watch for when applying the stash
@@ -0,0 +1,29 @@
1
+ Suggest what tests should be added or updated for this change.
2
+
3
+ Commit Message:
4
+ {{commit_message}}
5
+
6
+ Files Changed:
7
+ {{files_changed}}
8
+
9
+ Stats:
10
+ {{stats}}
11
+
12
+ Change Hints:
13
+ {{change_hints}}
14
+
15
+ Diff:
16
+ {{diff}}
17
+
18
+ Return:
19
+
20
+ 1. Test Coverage Suggestions:
21
+ - List the highest-value tests to add or update
22
+ - Be specific about scenarios, assertions, and edge cases
23
+ - If existing coverage already looks sufficient, say so clearly
24
+
25
+ 2. Risk Gaps:
26
+ - Call out the most important behaviors that could regress without tests
27
+
28
+ 3. Suggested Test Plan:
29
+ - Organize the recommended tests by unit, integration, or end-to-end when appropriate
package/IMPLEMENTATION.md DELETED
@@ -1,225 +0,0 @@
1
- # GitXplain Enhancement - Implementation Summary
2
-
3
- ## Overview
4
- Successfully implemented two major new features for gitxplain:
5
- 1. **GitHub Connection** (`--connect-github`) - Connect GitHub account with Personal Access Token
6
- 2. **Interactive Chat Interface** (`--boot`) - Start a chat session with repository context
7
-
8
- ## New Features
9
-
10
- ### 1. GitHub Connection Feature (`--connect-github`)
11
-
12
- **Command:**
13
- ```bash
14
- gitxplain --connect-github
15
- ```
16
-
17
- **Functionality:**
18
- - Prompts user for GitHub Personal Access Token (PAT)
19
- - Saves connection securely to `~/.gitxplain/git-connection.json`
20
- - Displays user's git configuration (name and email)
21
- - Shows success message upon completion
22
- - Required before using `--boot` feature
23
-
24
- **Files Created:**
25
- - `cli/services/gitConnectionService.js` - Handles connection storage and retrieval
26
-
27
- **Key Functions:**
28
- - `saveGitConnection(token, provider)` - Saves PAT locally
29
- - `loadGitConnection()` - Retrieves saved connection
30
- - `isGitConnected()` - Checks if connection exists
31
- - `getGitUserInfo()` - Gets git user name/email
32
-
33
- ### 2. Interactive Chat Interface (`--boot`)
34
-
35
- **Command:**
36
- ```bash
37
- gitxplain --boot
38
- gitxplain --boot --provider groq --model llama-3.3-70b-versatile
39
- ```
40
-
41
- **Functionality:**
42
- - Requires prior git connection (`gitxplain --connect-github`)
43
- - Initializes repository context (commits, branches, status)
44
- - Launches interactive readline interface
45
- - Maintains conversation history with LLM
46
- - Supports all LLM providers (OpenAI, Groq, OpenRouter, Gemini, Ollama, Chutes)
47
-
48
- **Files Created:**
49
- - `cli/services/chatService.js` - Chat interface implementation
50
-
51
- **Key Features:**
52
- - Repository Context Awareness
53
- - Last 20 commits with abbreviated hash and message
54
- - All git branches (local and remote)
55
- - Current working directory status
56
-
57
- - Chat Commands:
58
- - Type normally to ask questions about the code/commits
59
- - `clear` - Reset conversation history
60
- - `exit` - Close chat session
61
-
62
- - Multi-Provider Support:
63
- - OpenAI Compatible providers: OpenAI, Groq, OpenRouter, Chutes
64
- - Specialized providers: Gemini (with custom formatting)
65
- - Local providers: Ollama
66
-
67
- **Key Classes:**
68
- - `ChatService` - Main chat interface class
69
- - `constructor(cwd, providerOverride, modelOverride)` - Initialize service
70
- - `initializeRepoContext()` - Load repository information
71
- - `buildSystemPrompt()` - Create context-aware system prompt
72
- - `sendMessage(userMessage)` - Handle user input
73
- - `startInteractiveChat()` - Main interactive loop
74
-
75
- ### 3. Updated CLI (`cli/index.js`)
76
-
77
- **New Flags:**
78
- - `--connect-github` - Initialize GitHub connection
79
- - `--boot` - Start interactive chat session
80
-
81
- **Updated Features:**
82
- - `parseArgs()` - Now detects `connectGitHub` and `boot` flags
83
- - `handleConnectGit()` - Manages connection workflow
84
- - `handleBoot()` - Manages chat initialization
85
- - `printHelp()` - Updated documentation
86
-
87
- **Error Handling:**
88
- - Checks for git repository existence
89
- - Validates connection before allowing `--boot`
90
- - Graceful error messages for missing PAT or connection
91
-
92
- ## Updated Files
93
-
94
- ### `cli/services/aiService.js`
95
- - Exported `getProviderConfig()` function (moved from private)
96
- - Exported `validateProviderConfig()` function (moved from private)
97
- - These are now used by `chatService.js`
98
-
99
- ### `package.json`
100
- - Updated lint script to include new service files
101
- - `gitConnectionService.js`
102
- - `chatService.js`
103
-
104
- ### `README.md`
105
- - Added new features documentation
106
- - Added usage examples for `--connect-github` and `--boot`
107
- - Added section explaining chat commands
108
-
109
- ## Connection Storage
110
-
111
- **Location:** `~/.gitxplain/git-connection.json`
112
-
113
- **Format:**
114
- ```json
115
- {
116
- "token": "github_pat_xxx",
117
- "provider": "github",
118
- "connectedAt": "2026-04-08T09:45:18.238Z"
119
- }
120
- ```
121
-
122
- **Security Notes:**
123
- - Token stored locally in user's home directory
124
- - Not included in version control (added to `.gitignore`)
125
- - Can be manually deleted to disconnect
126
-
127
- ## Testing
128
-
129
- ✅ All features tested and working:
130
- 1. `--connect-github` successfully saves PAT
131
- 2. Connection file created at correct location
132
- 3. `--boot` checks for existing connection
133
- 4. Help text updated with new commands
134
- 5. Syntax validation passes for all files
135
-
136
- ## Usage Examples
137
-
138
- ### Connect to GitHub
139
- ```bash
140
- gitxplain --connect-github
141
- # Enter your PAT when prompted
142
- # Output: Git Connected Successfully
143
- ```
144
-
145
- ### Start Interactive Chat with Default Provider
146
- ```bash
147
- gitxplain --boot
148
- # Opens chat interface with repository context
149
- ```
150
-
151
- ### Start Chat with Specific Provider
152
- ```bash
153
- gitxplain --boot --provider groq --model llama-3.3-70b-versatile
154
- ```
155
-
156
- ### Chat Commands
157
- ```
158
- You: What commits were made recently?
159
- [Assistant responds about recent commits]
160
-
161
- You: Explain the last change
162
- [Assistant explains based on repo context]
163
-
164
- You: clear
165
- [Conversation history cleared]
166
-
167
- You: exit
168
- [Chat session ends]
169
- ```
170
-
171
- ## Architecture
172
-
173
- ```
174
- CLI (index.js)
175
- ├── parseArgs() → detects --connect-github, --boot
176
- ├── handleConnectGit()
177
- │ └── gitConnectionService.js
178
- │ ├── saveGitConnection()
179
- │ └── getGitUserInfo()
180
- └── handleBoot()
181
- └── chatService.js
182
- ├── ChatService class
183
- ├── initializeRepoContext()
184
- ├── sendMessage()
185
- └── startInteractiveChat()
186
- └── aiService.js
187
- ├── getProviderConfig()
188
- └── validateProviderConfig()
189
- ```
190
-
191
- ## Backward Compatibility
192
-
193
- ✅ All existing features remain fully functional:
194
- - Commit analysis (`gitxplain <commit-id>`)
195
- - All analysis modes (--summary, --issues, --fix, --impact, --full)
196
- - Provider override (--provider, --model)
197
- - Output formatting (--json)
198
- - Help command
199
-
200
- ## Next Steps (Optional Enhancements)
201
-
202
- 1. Add token validation against GitHub API
203
- 2. Add token expiration checking
204
- 3. Add support for other git platforms (GitLab, Bitbucket)
205
- 4. Add option to save chat history
206
- 5. Add syntax highlighting in chat output
207
- 6. Add keyboard shortcuts for chat commands
208
-
209
- ## Files Modified/Created
210
-
211
- ### Created:
212
- - `cli/services/gitConnectionService.js` (134 lines)
213
- - `cli/services/chatService.js` (216 lines)
214
-
215
- ### Modified:
216
- - `cli/index.js` - Added new features, updated help, added handlers
217
- - `cli/services/aiService.js` - Exported previously private functions
218
- - `package.json` - Updated lint script
219
- - `README.md` - Updated documentation
220
-
221
- ### Unchanged but Supporting:
222
- - `cli/services/gitService.js` - Git integration
223
- - `cli/services/outputFormatter.js` - Output formatting
224
- - `cli/services/promptService.js` - Prompt templates
225
- - Prompts directory - All prompt templates