@snapcommit/cli 3.2.0 ā 3.3.0
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.
- package/dist/commands/cursor-style.js +103 -24
- package/dist/repl.js +52 -1
- package/package.json +1 -1
|
@@ -140,38 +140,100 @@ async function executeCommitWithAI(intent) {
|
|
|
140
140
|
}
|
|
141
141
|
// Count files
|
|
142
142
|
let fileCount = status.staged + status.unstaged + status.untracked;
|
|
143
|
-
//
|
|
144
|
-
|
|
143
|
+
// Get all changed files with details
|
|
144
|
+
let changedFiles = [];
|
|
145
145
|
try {
|
|
146
146
|
const output = (0, child_process_1.execSync)('git status --short', { encoding: 'utf-8' });
|
|
147
|
-
const lines = output.split('\n').filter(line => line.trim())
|
|
148
|
-
lines.
|
|
149
|
-
const
|
|
147
|
+
const lines = output.split('\n').filter(line => line.trim());
|
|
148
|
+
changedFiles = lines.map(line => {
|
|
149
|
+
const fileStatus = line.substring(0, 2);
|
|
150
150
|
const file = line.substring(3);
|
|
151
|
-
|
|
152
|
-
|
|
151
|
+
let symbol = 'ā';
|
|
152
|
+
let color = chalk_1.default.yellow;
|
|
153
|
+
if (fileStatus.includes('M')) {
|
|
154
|
+
symbol = 'ā';
|
|
155
|
+
color = chalk_1.default.yellow;
|
|
153
156
|
}
|
|
154
|
-
else if (
|
|
155
|
-
|
|
157
|
+
else if (fileStatus.includes('?')) {
|
|
158
|
+
symbol = '+';
|
|
159
|
+
color = chalk_1.default.green;
|
|
156
160
|
}
|
|
157
|
-
else if (
|
|
158
|
-
|
|
161
|
+
else if (fileStatus.includes('D')) {
|
|
162
|
+
symbol = '-';
|
|
163
|
+
color = chalk_1.default.red;
|
|
159
164
|
}
|
|
165
|
+
return { file, status: fileStatus, symbol, color };
|
|
160
166
|
});
|
|
161
|
-
if (fileCount > 10) {
|
|
162
|
-
console.log(chalk_1.default.gray(` ... and ${fileCount - 10} more`));
|
|
163
|
-
}
|
|
164
167
|
}
|
|
165
168
|
catch {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
169
|
+
// Fallback to staging all
|
|
170
|
+
try {
|
|
171
|
+
(0, git_1.stageAllChanges)();
|
|
172
|
+
}
|
|
173
|
+
catch (error) {
|
|
174
|
+
console.log(chalk_1.default.red(`ā ${error.message}\n`));
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
170
177
|
}
|
|
178
|
+
// Show interactive file selection
|
|
179
|
+
console.log(chalk_1.default.blue(`\nš¦ Changes (${fileCount} ${fileCount === 1 ? 'file' : 'files'}):\n`));
|
|
180
|
+
changedFiles.forEach((item, index) => {
|
|
181
|
+
console.log(chalk_1.default.gray(` ${index + 1}. `) + item.color(`${item.symbol} ${item.file}`));
|
|
182
|
+
});
|
|
171
183
|
console.log();
|
|
172
|
-
//
|
|
184
|
+
// Interactive file selection
|
|
185
|
+
const rlModule = await Promise.resolve().then(() => __importStar(require('readline')));
|
|
186
|
+
const rlFileSelect = rlModule.createInterface({
|
|
187
|
+
input: process.stdin,
|
|
188
|
+
output: process.stdout,
|
|
189
|
+
});
|
|
190
|
+
const fileSelection = await new Promise((resolve) => {
|
|
191
|
+
rlFileSelect.question(chalk_1.default.cyan('Select files: ') +
|
|
192
|
+
chalk_1.default.gray('(Enter=all, 1-3=specific, 1,3,5=multiple): '), (ans) => {
|
|
193
|
+
rlFileSelect.close();
|
|
194
|
+
resolve(ans.trim());
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
// Process selection
|
|
198
|
+
let filesToStage = [];
|
|
199
|
+
if (!fileSelection || fileSelection.toLowerCase() === 'all' || fileSelection.toLowerCase() === 'a') {
|
|
200
|
+
// Stage all files
|
|
201
|
+
filesToStage = changedFiles.map(f => f.file);
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
// Parse selection (e.g., "1,3,5" or "1-3")
|
|
205
|
+
const selections = fileSelection.split(',').map(s => s.trim());
|
|
206
|
+
const selectedIndices = new Set();
|
|
207
|
+
for (const sel of selections) {
|
|
208
|
+
if (sel.includes('-')) {
|
|
209
|
+
// Range (e.g., "1-3")
|
|
210
|
+
const [start, end] = sel.split('-').map(n => parseInt(n.trim()));
|
|
211
|
+
for (let i = start; i <= end; i++) {
|
|
212
|
+
if (i >= 1 && i <= changedFiles.length) {
|
|
213
|
+
selectedIndices.add(i - 1);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
// Single number
|
|
219
|
+
const index = parseInt(sel);
|
|
220
|
+
if (index >= 1 && index <= changedFiles.length) {
|
|
221
|
+
selectedIndices.add(index - 1);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
filesToStage = Array.from(selectedIndices).map(i => changedFiles[i].file);
|
|
226
|
+
}
|
|
227
|
+
// Stage selected files
|
|
228
|
+
if (filesToStage.length === 0) {
|
|
229
|
+
console.log(chalk_1.default.gray('\nā No files selected\n'));
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
173
232
|
try {
|
|
174
|
-
(
|
|
233
|
+
for (const file of filesToStage) {
|
|
234
|
+
(0, child_process_1.execSync)(`git add "${file}"`, { encoding: 'utf-8', stdio: 'pipe' });
|
|
235
|
+
}
|
|
236
|
+
console.log(chalk_1.default.gray(`\nā Staged ${filesToStage.length} ${filesToStage.length === 1 ? 'file' : 'files'}\n`));
|
|
175
237
|
}
|
|
176
238
|
catch (error) {
|
|
177
239
|
console.log(chalk_1.default.red(`ā ${error.message}\n`));
|
|
@@ -184,14 +246,14 @@ async function executeCommitWithAI(intent) {
|
|
|
184
246
|
console.log(chalk_1.default.cyan('š¤ ') + chalk_1.default.white.bold(commitMessage.split('\n')[0]));
|
|
185
247
|
console.log();
|
|
186
248
|
// ONE PROMPT - like Cursor's commit button
|
|
187
|
-
const
|
|
188
|
-
const
|
|
249
|
+
const rlModule2 = await Promise.resolve().then(() => __importStar(require('readline')));
|
|
250
|
+
const rlCommit = rlModule2.createInterface({
|
|
189
251
|
input: process.stdin,
|
|
190
252
|
output: process.stdout,
|
|
191
253
|
});
|
|
192
254
|
const response = await new Promise((resolve) => {
|
|
193
|
-
|
|
194
|
-
|
|
255
|
+
rlCommit.question(chalk_1.default.gray('ā Press Enter to commit, or edit message: '), (ans) => {
|
|
256
|
+
rlCommit.close();
|
|
195
257
|
resolve(ans.trim());
|
|
196
258
|
});
|
|
197
259
|
});
|
|
@@ -203,6 +265,23 @@ async function executeCommitWithAI(intent) {
|
|
|
203
265
|
try {
|
|
204
266
|
(0, child_process_1.execSync)(`git commit -m "${commitMessage.replace(/"/g, '\\"')}"`, { encoding: 'utf-8', stdio: 'pipe' });
|
|
205
267
|
console.log(chalk_1.default.green(`\nā Committed`));
|
|
268
|
+
// Log commit stats
|
|
269
|
+
try {
|
|
270
|
+
const { logCommit } = await Promise.resolve().then(() => __importStar(require('../db/database')));
|
|
271
|
+
const commitHash = (0, child_process_1.execSync)('git rev-parse HEAD', { encoding: 'utf-8' }).trim();
|
|
272
|
+
const stats = (0, child_process_1.execSync)(`git diff HEAD~1 HEAD --numstat | awk '{inserted+=$1; deleted+=$2} END {print inserted" "deleted}'`, { encoding: 'utf-8' }).trim().split(' ');
|
|
273
|
+
logCommit({
|
|
274
|
+
message: commitMessage,
|
|
275
|
+
hash: commitHash,
|
|
276
|
+
files_changed: filesToStage.length,
|
|
277
|
+
insertions: parseInt(stats[0]) || 0,
|
|
278
|
+
deletions: parseInt(stats[1]) || 0,
|
|
279
|
+
timestamp: Date.now(),
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
catch {
|
|
283
|
+
// Silent fail - don't break user experience
|
|
284
|
+
}
|
|
206
285
|
}
|
|
207
286
|
catch (error) {
|
|
208
287
|
console.log(chalk_1.default.red(`\nā Commit failed: ${error.message}\n`));
|
package/dist/repl.js
CHANGED
|
@@ -89,7 +89,7 @@ async function startREPL() {
|
|
|
89
89
|
console.log(chalk_1.default.gray(' GitHub: ') + chalk_1.default.cyan('"create a PR to main"'));
|
|
90
90
|
console.log(chalk_1.default.gray(' ') + chalk_1.default.cyan('"check CI status"'));
|
|
91
91
|
}
|
|
92
|
-
console.log(chalk_1.default.gray(' Other: ') + chalk_1.default.cyan('cd <path>') + chalk_1.default.gray(' ⢠') + chalk_1.default.cyan('exit') + chalk_1.default.gray(' ⢠') + chalk_1.default.cyan('help') + chalk_1.default.gray(' ⢠') + chalk_1.default.cyan('update\n'));
|
|
92
|
+
console.log(chalk_1.default.gray(' Other: ') + chalk_1.default.cyan('stats') + chalk_1.default.gray(' ⢠') + chalk_1.default.cyan('cd <path>') + chalk_1.default.gray(' ⢠') + chalk_1.default.cyan('exit') + chalk_1.default.gray(' ⢠') + chalk_1.default.cyan('help') + chalk_1.default.gray(' ⢠') + chalk_1.default.cyan('update\n'));
|
|
93
93
|
// Show GitHub setup reminder if not connected
|
|
94
94
|
if (!githubConnected) {
|
|
95
95
|
console.log(chalk_1.default.yellow.bold('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā'));
|
|
@@ -123,6 +123,56 @@ async function startREPL() {
|
|
|
123
123
|
rl.close();
|
|
124
124
|
process.exit(0);
|
|
125
125
|
}
|
|
126
|
+
// Stats command - show commit stats
|
|
127
|
+
if (line === 'stats' || line.startsWith('stats ')) {
|
|
128
|
+
const parts = line.split(' ');
|
|
129
|
+
const period = parts[1] || 'week';
|
|
130
|
+
let daysBack = 7;
|
|
131
|
+
if (period === 'today')
|
|
132
|
+
daysBack = 1;
|
|
133
|
+
else if (period === 'week')
|
|
134
|
+
daysBack = 7;
|
|
135
|
+
else if (period === 'month')
|
|
136
|
+
daysBack = 30;
|
|
137
|
+
else if (period === 'year')
|
|
138
|
+
daysBack = 365;
|
|
139
|
+
try {
|
|
140
|
+
const { getStats } = await Promise.resolve().then(() => __importStar(require('./db/database')));
|
|
141
|
+
const stats = getStats(daysBack);
|
|
142
|
+
console.log(chalk_1.default.bold.cyan(`\nš Stats (${period}):\n`));
|
|
143
|
+
console.log(chalk_1.default.white(` ⢠${chalk_1.default.bold.green(stats.totalCommits)} commits`));
|
|
144
|
+
console.log(chalk_1.default.white(` ⢠${chalk_1.default.bold.green(stats.totalInsertions.toLocaleString())} lines added`));
|
|
145
|
+
console.log(chalk_1.default.white(` ⢠${chalk_1.default.bold.red(stats.totalDeletions.toLocaleString())} lines removed`));
|
|
146
|
+
// Calculate busiest day
|
|
147
|
+
if (stats.totalCommits > 0) {
|
|
148
|
+
const commitsByDay = {};
|
|
149
|
+
const { execSync } = await Promise.resolve().then(() => __importStar(require('child_process')));
|
|
150
|
+
try {
|
|
151
|
+
const since = Date.now() - daysBack * 24 * 60 * 60 * 1000;
|
|
152
|
+
const sinceDate = new Date(since).toISOString().split('T')[0];
|
|
153
|
+
const logOutput = execSync(`git log --since="${sinceDate}" --format="%ai"`, { encoding: 'utf-8' });
|
|
154
|
+
logOutput.split('\n').filter(line => line.trim()).forEach(line => {
|
|
155
|
+
const day = line.split(' ')[0];
|
|
156
|
+
commitsByDay[day] = (commitsByDay[day] || 0) + 1;
|
|
157
|
+
});
|
|
158
|
+
const busiestDay = Object.entries(commitsByDay).sort(([, a], [, b]) => b - a)[0];
|
|
159
|
+
if (busiestDay) {
|
|
160
|
+
const dayName = new Date(busiestDay[0]).toLocaleDateString('en-US', { weekday: 'long' });
|
|
161
|
+
console.log(chalk_1.default.white(` ⢠Busiest day: ${chalk_1.default.bold.cyan(dayName)} (${busiestDay[1]} commits)`));
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
// Silent fail
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
console.log();
|
|
169
|
+
}
|
|
170
|
+
catch (error) {
|
|
171
|
+
console.log(chalk_1.default.red(`\nā Failed to get stats: ${error.message}\n`));
|
|
172
|
+
}
|
|
173
|
+
rl.prompt();
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
126
176
|
// CD command - navigate to different repo
|
|
127
177
|
if (line.startsWith('cd ')) {
|
|
128
178
|
const targetPath = line.substring(3).trim();
|
|
@@ -210,6 +260,7 @@ async function startREPL() {
|
|
|
210
260
|
console.log(chalk_1.default.cyan(' "list my pull requests"'));
|
|
211
261
|
console.log(chalk_1.default.cyan(' "merge PR #123"\n'));
|
|
212
262
|
console.log(chalk_1.default.white.bold('System Commands:\n'));
|
|
263
|
+
console.log(chalk_1.default.gray(' ⢠') + chalk_1.default.cyan('stats') + chalk_1.default.gray(' - Show commit stats (stats today/week/month/year)'));
|
|
213
264
|
console.log(chalk_1.default.gray(' ⢠') + chalk_1.default.cyan('cd <path>') + chalk_1.default.gray(' - Navigate to different repo'));
|
|
214
265
|
console.log(chalk_1.default.gray(' ⢠') + chalk_1.default.cyan('update') + chalk_1.default.gray(' - Update to latest version'));
|
|
215
266
|
console.log(chalk_1.default.gray(' ⢠') + chalk_1.default.cyan('exit/quit') + chalk_1.default.gray(' - Exit SnapCommit\n'));
|