@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 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:support@snapcommit.dev)
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
- (0, ui_1.displaySuccess)(`${workflow.name} workflow completed.`, [
379
- 'Re-run with --plan to preview or --auto to run without prompts.',
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 {
@@ -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 {
@@ -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!'));
@@ -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'));
@@ -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: feedback@snapcommit.dev'));
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()) {
@@ -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
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@snapcommit/cli",
3
- "version": "3.11.0",
3
+ "version": "3.11.1",
4
4
  "description": "Instant AI commits. Beautiful progress tracking. Never write commit messages again.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {