@snapcommit/cli 3.11.0 ā 3.11.1
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/README.md +1 -1
- package/dist/commands/autopilot.js +24 -3
- package/dist/commands/commit.js +6 -0
- package/dist/commands/conflict.js +6 -0
- package/dist/commands/onboard.js +1 -0
- package/dist/commands/quick.js +6 -0
- package/dist/commands/stats.js +23 -0
- package/dist/commands/uninstall.js +1 -1
- package/dist/utils/memory.js +12 -0
- package/dist/utils/metrics.js +66 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -148,7 +148,7 @@ Full docs at [snapcommit.dev/docs](https://snapcommit.dev/docs)
|
|
|
148
148
|
## Support
|
|
149
149
|
|
|
150
150
|
- š¦ [Follow on X/Twitter](https://x.com/Arjun06061)
|
|
151
|
-
- š§ [Email support](mailto:
|
|
151
|
+
- š§ [Email support](mailto:karjunvarma2001@gmail.com)
|
|
152
152
|
- š” [Suggestions & Feedback](https://x.com/Arjun06061)
|
|
153
153
|
|
|
154
154
|
## License
|
|
@@ -16,6 +16,7 @@ const conflict_1 = require("./conflict");
|
|
|
16
16
|
const commit_1 = require("./commit");
|
|
17
17
|
const quick_1 = require("./quick");
|
|
18
18
|
const memory_1 = require("../utils/memory");
|
|
19
|
+
const metrics_1 = require("../utils/metrics");
|
|
19
20
|
const WORKFLOWS = [
|
|
20
21
|
{
|
|
21
22
|
id: 'conflict-crusher',
|
|
@@ -211,6 +212,14 @@ const WORKFLOWS = [
|
|
|
211
212
|
],
|
|
212
213
|
},
|
|
213
214
|
];
|
|
215
|
+
const WORKFLOW_TIME_SAVINGS = {
|
|
216
|
+
'conflict-crusher': 25,
|
|
217
|
+
'release-ready': 18,
|
|
218
|
+
};
|
|
219
|
+
const WORKFLOW_EVENT_IDS = {
|
|
220
|
+
'conflict-crusher': 'autopilot:conflict-crusher',
|
|
221
|
+
'release-ready': 'autopilot:release-ready',
|
|
222
|
+
};
|
|
214
223
|
async function autopilotCommand(workflowId, rawOptions) {
|
|
215
224
|
const options = {
|
|
216
225
|
workflowId,
|
|
@@ -375,9 +384,21 @@ function presentSummary(workflow, result) {
|
|
|
375
384
|
});
|
|
376
385
|
}
|
|
377
386
|
console.log();
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
]
|
|
387
|
+
const estimatedMinutes = WORKFLOW_TIME_SAVINGS[workflow.id];
|
|
388
|
+
const completedSteps = workflow.steps.filter((step) => result.statusByStep[step.id] === 'completed').length;
|
|
389
|
+
const eventId = WORKFLOW_EVENT_IDS[workflow.id];
|
|
390
|
+
if (estimatedMinutes && completedSteps > 0 && eventId) {
|
|
391
|
+
(0, metrics_1.logTimeSaved)(eventId, estimatedMinutes);
|
|
392
|
+
(0, ui_1.displaySuccess)(`${workflow.name} workflow completed.`, [
|
|
393
|
+
`Time saved: ~${(0, metrics_1.formatMinutes)(estimatedMinutes)} compared to manual steps.`,
|
|
394
|
+
'Re-run with --plan to preview or --auto to run without prompts.',
|
|
395
|
+
]);
|
|
396
|
+
}
|
|
397
|
+
else {
|
|
398
|
+
(0, ui_1.displaySuccess)(`${workflow.name} workflow completed.`, [
|
|
399
|
+
'Re-run with --plan to preview or --auto to run without prompts.',
|
|
400
|
+
]);
|
|
401
|
+
}
|
|
381
402
|
}
|
|
382
403
|
function getConflictedFiles() {
|
|
383
404
|
try {
|
package/dist/commands/commit.js
CHANGED
|
@@ -44,6 +44,7 @@ const manager_1 = require("../license/manager");
|
|
|
44
44
|
const rate_limit_1 = require("../utils/rate-limit");
|
|
45
45
|
const analytics_1 = require("../utils/analytics");
|
|
46
46
|
const readline_1 = __importDefault(require("readline"));
|
|
47
|
+
const metrics_1 = require("../utils/metrics");
|
|
47
48
|
async function commitCommand() {
|
|
48
49
|
// Check license before proceeding
|
|
49
50
|
const { allowed, reason, usage } = (0, manager_1.canUseCommit)();
|
|
@@ -226,6 +227,11 @@ async function commitCommand() {
|
|
|
226
227
|
catch (error) {
|
|
227
228
|
// Silent fail - cloud sync is optional
|
|
228
229
|
}
|
|
230
|
+
const minutesSaved = (0, metrics_1.logTimeSaved)('commit:interactive');
|
|
231
|
+
if (minutesSaved > 0) {
|
|
232
|
+
console.log(chalk_1.default.gray(` ā± Saved about ${(0, metrics_1.formatMinutes)(minutesSaved)} compared to drafting this commit by hand.`));
|
|
233
|
+
console.log();
|
|
234
|
+
}
|
|
229
235
|
// Show dopamine stats
|
|
230
236
|
const { displayQuickDopamine } = await Promise.resolve().then(() => __importStar(require('../utils/dopamine')));
|
|
231
237
|
displayQuickDopamine();
|
|
@@ -14,6 +14,7 @@ const fs_1 = __importDefault(require("fs"));
|
|
|
14
14
|
const readline_1 = __importDefault(require("readline"));
|
|
15
15
|
const sdk_1 = __importDefault(require("@anthropic-ai/sdk"));
|
|
16
16
|
const analytics_1 = require("../utils/analytics");
|
|
17
|
+
const metrics_1 = require("../utils/metrics");
|
|
17
18
|
const anthropic = new sdk_1.default({
|
|
18
19
|
apiKey: process.env.ANTHROPIC_API_KEY || '',
|
|
19
20
|
});
|
|
@@ -74,6 +75,11 @@ async function conflictCommand() {
|
|
|
74
75
|
}
|
|
75
76
|
// Final commit
|
|
76
77
|
console.log(chalk_1.default.blue('\nā
All conflicts resolved!\n'));
|
|
78
|
+
const minutesSaved = (0, metrics_1.logTimeSaved)('conflict:auto');
|
|
79
|
+
if (minutesSaved > 0) {
|
|
80
|
+
console.log(chalk_1.default.gray(`ā± Saved roughly ${(0, metrics_1.formatMinutes)(minutesSaved)} by letting SnapCommit handle the merge.`));
|
|
81
|
+
console.log();
|
|
82
|
+
}
|
|
77
83
|
const shouldCommit = await askQuestion(chalk_1.default.yellow('Commit the merge? (Y/n): '));
|
|
78
84
|
if (shouldCommit.toLowerCase() !== 'n') {
|
|
79
85
|
try {
|
package/dist/commands/onboard.js
CHANGED
|
@@ -76,6 +76,7 @@ async function onboardCommand() {
|
|
|
76
76
|
console.log(chalk_1.default.cyan(' snapcommit doctor ') + chalk_1.default.gray('ā Check setup'));
|
|
77
77
|
console.log(chalk_1.default.cyan(' snapcommit stats ') + chalk_1.default.gray('ā See your progress'));
|
|
78
78
|
console.log(chalk_1.default.cyan(' snap autopilot ') + chalk_1.default.gray('ā AI workflows for conflicts & releases'));
|
|
79
|
+
console.log(chalk_1.default.cyan(' snap stats ') + chalk_1.default.gray('ā Track hours saved by AI'));
|
|
79
80
|
console.log(chalk_1.default.cyan(' snapcommit --help ') + chalk_1.default.gray('ā All commands'));
|
|
80
81
|
console.log();
|
|
81
82
|
console.log(chalk_1.default.yellow.bold('š” Pro tip: ') + chalk_1.default.white('Use') + chalk_1.default.cyan(' bq ') + chalk_1.default.white('for instant commits!'));
|
package/dist/commands/quick.js
CHANGED
|
@@ -9,6 +9,7 @@ const git_1 = require("../utils/git");
|
|
|
9
9
|
const database_1 = require("../db/database");
|
|
10
10
|
const auth_1 = require("../lib/auth");
|
|
11
11
|
const analytics_1 = require("../utils/analytics");
|
|
12
|
+
const metrics_1 = require("../utils/metrics");
|
|
12
13
|
// API URL - defaults to production, can be overridden for development
|
|
13
14
|
const API_BASE_URL = process.env.SNAPCOMMIT_API_URL || 'https://www.snapcommit.dev';
|
|
14
15
|
/**
|
|
@@ -123,6 +124,11 @@ async function quickCommand() {
|
|
|
123
124
|
deletions: stats.deletions,
|
|
124
125
|
timestamp: Date.now(),
|
|
125
126
|
});
|
|
127
|
+
const minutesSaved = (0, metrics_1.logTimeSaved)('commit:quick');
|
|
128
|
+
if (minutesSaved > 0) {
|
|
129
|
+
console.log(chalk_1.default.gray(` ā± Saved ~${(0, metrics_1.formatMinutes)(minutesSaved)} with quick commit automation.`));
|
|
130
|
+
console.log();
|
|
131
|
+
}
|
|
126
132
|
}
|
|
127
133
|
catch (error) {
|
|
128
134
|
console.log(chalk_1.default.red('\nā Commit failed\n'));
|
package/dist/commands/stats.js
CHANGED
|
@@ -8,6 +8,7 @@ const chalk_1 = __importDefault(require("chalk"));
|
|
|
8
8
|
const database_1 = require("../db/database");
|
|
9
9
|
const dopamine_1 = require("../utils/dopamine");
|
|
10
10
|
const heatmap_1 = require("../utils/heatmap");
|
|
11
|
+
const metrics_1 = require("../utils/metrics");
|
|
11
12
|
function statsCommand() {
|
|
12
13
|
console.log(chalk_1.default.blue.bold('\nš Your SnapCommit Stats\n'));
|
|
13
14
|
// Show dopamine stats first (most engaging)
|
|
@@ -30,6 +31,18 @@ function statsCommand() {
|
|
|
30
31
|
{ label: 'Commits', value: statsAll.totalCommits, color: 'green' },
|
|
31
32
|
{ label: 'Commands', value: statsAll.totalCommands, color: 'cyan' },
|
|
32
33
|
]));
|
|
34
|
+
const timeSaved = (0, metrics_1.getTimeSavedSummary)();
|
|
35
|
+
if (timeSaved.totalMinutes > 0) {
|
|
36
|
+
console.log(chalk_1.default.white.bold('\nTime Saved With SnapCommit:'));
|
|
37
|
+
console.log(chalk_1.default.green(` ā ${(0, metrics_1.formatMinutes)(Math.round(timeSaved.totalMinutes))} saved by AI assistance so far`));
|
|
38
|
+
const topEvents = Object.entries(timeSaved.breakdown)
|
|
39
|
+
.sort(([, a], [, b]) => (b.minutes ?? 0) - (a.minutes ?? 0))
|
|
40
|
+
.slice(0, 3);
|
|
41
|
+
topEvents.forEach(([eventId, data]) => {
|
|
42
|
+
console.log(chalk_1.default.gray(` ⢠${formatEventLabel(eventId)} ā ${(0, metrics_1.formatMinutes)(Math.round(data.minutes))} (${data.count} run${data.count === 1 ? '' : 's'})`));
|
|
43
|
+
});
|
|
44
|
+
console.log();
|
|
45
|
+
}
|
|
33
46
|
// Commit streak
|
|
34
47
|
const streak = calculateStreak(statsAll.recentCommits);
|
|
35
48
|
if (streak > 0) {
|
|
@@ -71,6 +84,16 @@ function statsCommand() {
|
|
|
71
84
|
console.log(chalk_1.default.gray('š” Keep building with SnapCommit!'));
|
|
72
85
|
console.log();
|
|
73
86
|
}
|
|
87
|
+
function formatEventLabel(eventId) {
|
|
88
|
+
const table = {
|
|
89
|
+
'autopilot:conflict-crusher': 'Autopilot ā Conflict Crusher',
|
|
90
|
+
'autopilot:release-ready': 'Autopilot ā Release Ready',
|
|
91
|
+
'commit:interactive': 'Interactive AI commits',
|
|
92
|
+
'commit:quick': 'Quick commits',
|
|
93
|
+
'conflict:auto': 'AI conflict resolution',
|
|
94
|
+
};
|
|
95
|
+
return table[eventId] ?? eventId;
|
|
96
|
+
}
|
|
74
97
|
function createStatCard(title, stats) {
|
|
75
98
|
const width = 32;
|
|
76
99
|
const border = 'ā'.repeat(width);
|
|
@@ -91,7 +91,7 @@ async function uninstallCommand() {
|
|
|
91
91
|
}
|
|
92
92
|
// Feedback request
|
|
93
93
|
console.log(chalk_1.default.yellow('š¬ We\'d love to know why you\'re leaving:'));
|
|
94
|
-
console.log(chalk_1.default.gray(' Email:
|
|
94
|
+
console.log(chalk_1.default.gray(' Email: karjunvarma2001@gmail.com'));
|
|
95
95
|
console.log(chalk_1.default.gray(' Or just reply to this prompt:\n'));
|
|
96
96
|
const feedback = await askQuestion(chalk_1.default.yellow('Why are you uninstalling? (optional): '));
|
|
97
97
|
if (feedback.trim()) {
|
package/dist/utils/memory.js
CHANGED
|
@@ -17,6 +17,10 @@ const DEFAULT_MEMORY = {
|
|
|
17
17
|
version: 1,
|
|
18
18
|
workflows: {},
|
|
19
19
|
preferences: {},
|
|
20
|
+
metrics: {
|
|
21
|
+
minutesSavedTotal: 0,
|
|
22
|
+
events: {},
|
|
23
|
+
},
|
|
20
24
|
};
|
|
21
25
|
function loadMemory() {
|
|
22
26
|
try {
|
|
@@ -31,6 +35,14 @@ function loadMemory() {
|
|
|
31
35
|
...parsed,
|
|
32
36
|
workflows: { ...DEFAULT_MEMORY.workflows, ...parsed.workflows },
|
|
33
37
|
preferences: { ...DEFAULT_MEMORY.preferences, ...parsed.preferences },
|
|
38
|
+
metrics: {
|
|
39
|
+
...DEFAULT_MEMORY.metrics,
|
|
40
|
+
...parsed.metrics,
|
|
41
|
+
events: {
|
|
42
|
+
...(DEFAULT_MEMORY.metrics?.events ?? {}),
|
|
43
|
+
...(parsed.metrics?.events ?? {}),
|
|
44
|
+
},
|
|
45
|
+
},
|
|
34
46
|
};
|
|
35
47
|
}
|
|
36
48
|
catch {
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.logTimeSaved = logTimeSaved;
|
|
4
|
+
exports.getTimeSavedSummary = getTimeSavedSummary;
|
|
5
|
+
exports.formatMinutes = formatMinutes;
|
|
6
|
+
const memory_1 = require("./memory");
|
|
7
|
+
const telemetry_1 = require("./telemetry");
|
|
8
|
+
const DEFAULT_TIME_SAVED_MINUTES = {
|
|
9
|
+
'autopilot:conflict-crusher': 25,
|
|
10
|
+
'autopilot:release-ready': 18,
|
|
11
|
+
'commit:interactive': 4,
|
|
12
|
+
'commit:quick': 2,
|
|
13
|
+
'conflict:auto': 20,
|
|
14
|
+
};
|
|
15
|
+
function logTimeSaved(eventId, minutesOverride) {
|
|
16
|
+
const minutes = minutesOverride ?? DEFAULT_TIME_SAVED_MINUTES[eventId] ?? 0;
|
|
17
|
+
if (!minutes || minutes <= 0) {
|
|
18
|
+
return 0;
|
|
19
|
+
}
|
|
20
|
+
let recordedMinutes = minutes;
|
|
21
|
+
(0, memory_1.updateMemory)((memory) => {
|
|
22
|
+
const metrics = memory.metrics || { minutesSavedTotal: 0, events: {} };
|
|
23
|
+
const current = metrics.events?.[eventId] ?? {
|
|
24
|
+
minutes: 0,
|
|
25
|
+
count: 0,
|
|
26
|
+
lastUpdated: Date.now(),
|
|
27
|
+
};
|
|
28
|
+
const events = {
|
|
29
|
+
...(metrics.events || {}),
|
|
30
|
+
[eventId]: {
|
|
31
|
+
minutes: current.minutes + minutes,
|
|
32
|
+
count: current.count + 1,
|
|
33
|
+
lastUpdated: Date.now(),
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
const minutesSavedTotal = (metrics.minutesSavedTotal ?? 0) + minutes;
|
|
37
|
+
recordedMinutes = minutes;
|
|
38
|
+
return {
|
|
39
|
+
...memory,
|
|
40
|
+
metrics: {
|
|
41
|
+
minutesSavedTotal,
|
|
42
|
+
events,
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
});
|
|
46
|
+
(0, telemetry_1.recordTelemetry)('time_saved', { eventId, minutes: recordedMinutes });
|
|
47
|
+
return recordedMinutes;
|
|
48
|
+
}
|
|
49
|
+
function getTimeSavedSummary() {
|
|
50
|
+
const memory = (0, memory_1.loadMemory)();
|
|
51
|
+
return {
|
|
52
|
+
totalMinutes: memory.metrics?.minutesSavedTotal ?? 0,
|
|
53
|
+
breakdown: memory.metrics?.events ?? {},
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function formatMinutes(minutes) {
|
|
57
|
+
if (minutes < 60) {
|
|
58
|
+
return `${minutes} minute${minutes === 1 ? '' : 's'}`;
|
|
59
|
+
}
|
|
60
|
+
const hours = Math.floor(minutes / 60);
|
|
61
|
+
const remaining = minutes % 60;
|
|
62
|
+
if (remaining === 0) {
|
|
63
|
+
return `${hours} hour${hours === 1 ? '' : 's'}`;
|
|
64
|
+
}
|
|
65
|
+
return `${hours}h ${remaining}m`;
|
|
66
|
+
}
|