@snapcommit/cli 3.11.1 → 3.11.3
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/autopilot.js +307 -0
- package/dist/commands/cursor-style.js +41 -2
- package/dist/commands/git-advanced.js +19 -0
- package/dist/commands/stats.js +1 -0
- package/dist/repl/interpreter.js +20 -5
- package/dist/utils/git.js +9 -1
- package/dist/utils/metrics.js +1 -0
- package/package.json +1 -1
|
@@ -1,4 +1,37 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
37
|
};
|
|
@@ -9,6 +42,7 @@ const child_process_1 = require("child_process");
|
|
|
9
42
|
const fs_1 = require("fs");
|
|
10
43
|
const path_1 = __importDefault(require("path"));
|
|
11
44
|
const auth_1 = require("../lib/auth");
|
|
45
|
+
const github = __importStar(require("../lib/github"));
|
|
12
46
|
const git_1 = require("../utils/git");
|
|
13
47
|
const ui_1 = require("../utils/ui");
|
|
14
48
|
const prompt_1 = require("../utils/prompt");
|
|
@@ -211,14 +245,182 @@ const WORKFLOWS = [
|
|
|
211
245
|
},
|
|
212
246
|
],
|
|
213
247
|
},
|
|
248
|
+
{
|
|
249
|
+
id: 'pr-polish',
|
|
250
|
+
name: 'PR Polish',
|
|
251
|
+
headline: 'Push, summarize, and open a reviewer-ready pull request.',
|
|
252
|
+
description: 'Ensure your working tree is clean, run optional tests, capture reviewer notes, push your branch, and open a GitHub PR with consistent copy.',
|
|
253
|
+
idealFor: ['feature branches', 'handoffs to reviewers', 'Product Hunt launches'],
|
|
254
|
+
prerequisites: [
|
|
255
|
+
'Branch has commits ready for review',
|
|
256
|
+
'GitHub authentication configured (`snap github connect`)',
|
|
257
|
+
],
|
|
258
|
+
steps: [
|
|
259
|
+
{
|
|
260
|
+
id: 'review-status',
|
|
261
|
+
title: 'Confirm branch status',
|
|
262
|
+
description: 'Check branch tracking info and ensure the working tree is ready.',
|
|
263
|
+
action: async (ctx) => {
|
|
264
|
+
const status = (0, git_1.getGitStatus)();
|
|
265
|
+
const branch = (0, git_1.getCurrentBranch)();
|
|
266
|
+
const tracking = getBranchTrackingStatus();
|
|
267
|
+
const dirty = status.unstaged > 0 || status.untracked > 0;
|
|
268
|
+
ctx.recordInsight('branch', branch && branch !== 'unknown' ? branch : 'unknown');
|
|
269
|
+
if (tracking) {
|
|
270
|
+
ctx.recordInsight('tracking', tracking);
|
|
271
|
+
(0, ui_1.displayInfo)('Branch tracking', [tracking]);
|
|
272
|
+
}
|
|
273
|
+
if (dirty) {
|
|
274
|
+
const warnings = [];
|
|
275
|
+
if (status.unstaged > 0)
|
|
276
|
+
warnings.push(`${status.unstaged} unstaged file(s)`);
|
|
277
|
+
if (status.untracked > 0)
|
|
278
|
+
warnings.push(`${status.untracked} untracked file(s)`);
|
|
279
|
+
(0, ui_1.displayWarning)('Working tree has pending changes.', warnings);
|
|
280
|
+
const continueDirty = ctx.autoContinue || (await (0, prompt_1.promptConfirm)('Continue with dirty working tree?', false));
|
|
281
|
+
if (!continueDirty) {
|
|
282
|
+
throw new Error('Please commit, stash, or clean your working tree before continuing.');
|
|
283
|
+
}
|
|
284
|
+
ctx.recordInsight('workingTree', 'Dirty (user accepted)');
|
|
285
|
+
}
|
|
286
|
+
else {
|
|
287
|
+
ctx.recordInsight('workingTree', 'Clean');
|
|
288
|
+
(0, ui_1.displaySuccess)('Working tree clean.');
|
|
289
|
+
}
|
|
290
|
+
},
|
|
291
|
+
},
|
|
292
|
+
{
|
|
293
|
+
id: 'pr-tests',
|
|
294
|
+
title: 'Optional: run tests',
|
|
295
|
+
description: 'Run your preferred test command so reviewers trust the PR.',
|
|
296
|
+
skippable: true,
|
|
297
|
+
action: async (ctx) => {
|
|
298
|
+
const defaultCommand = getPreferredTestCommand();
|
|
299
|
+
const shouldRun = ctx.autoContinue ||
|
|
300
|
+
(await (0, prompt_1.promptConfirm)('Run tests before pushing?', Boolean(defaultCommand)));
|
|
301
|
+
if (!shouldRun) {
|
|
302
|
+
ctx.recordInsight('tests', 'Skipped');
|
|
303
|
+
ctx.prTestResult = 'Tests not run';
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
const command = ctx.autoContinue
|
|
307
|
+
? defaultCommand || ''
|
|
308
|
+
: await (0, prompt_1.promptInput)('Test command', defaultCommand || 'npm test');
|
|
309
|
+
if (!command) {
|
|
310
|
+
ctx.recordInsight('tests', 'Skipped (no command)');
|
|
311
|
+
ctx.prTestResult = 'Tests not run';
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
try {
|
|
315
|
+
await runShellCommand(command);
|
|
316
|
+
(0, memory_1.rememberPreference)('autopilot', 'testCommand', command);
|
|
317
|
+
ctx.recordInsight('tests', `Passed (${command})`);
|
|
318
|
+
ctx.prTestResult = `✅ ${command}`;
|
|
319
|
+
(0, ui_1.displaySuccess)('Tests passed successfully.');
|
|
320
|
+
}
|
|
321
|
+
catch (error) {
|
|
322
|
+
ctx.recordInsight('tests', `Failed (${command})`);
|
|
323
|
+
ctx.prTestResult = `⚠️ Failed (${command})`;
|
|
324
|
+
(0, ui_1.displayError)('Test command failed.', [
|
|
325
|
+
error.message,
|
|
326
|
+
'Fix the failures or continue at your own risk.',
|
|
327
|
+
]);
|
|
328
|
+
const continueAnyway = ctx.autoContinue || (await (0, prompt_1.promptConfirm)('Continue despite failing tests?', false));
|
|
329
|
+
if (!continueAnyway) {
|
|
330
|
+
throw new Error('Aborted because tests failed.');
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
},
|
|
334
|
+
},
|
|
335
|
+
{
|
|
336
|
+
id: 'sync',
|
|
337
|
+
title: 'Push branch to origin',
|
|
338
|
+
description: 'Make sure your branch is on GitHub before opening a PR.',
|
|
339
|
+
action: async (ctx) => {
|
|
340
|
+
const branch = (0, git_1.getCurrentBranch)();
|
|
341
|
+
const hasUpstream = branchHasUpstream();
|
|
342
|
+
const command = hasUpstream ? 'git push' : `git push --set-upstream origin ${branch}`;
|
|
343
|
+
try {
|
|
344
|
+
await runShellCommand(command);
|
|
345
|
+
ctx.recordInsight('push', `Pushed via "${command}"`);
|
|
346
|
+
(0, ui_1.displaySuccess)(`Branch ${chalk_1.default.cyan(branch)} pushed to origin.`);
|
|
347
|
+
}
|
|
348
|
+
catch (error) {
|
|
349
|
+
ctx.recordInsight('push', 'Failed');
|
|
350
|
+
throw new Error(error.message || 'Failed to push branch.');
|
|
351
|
+
}
|
|
352
|
+
},
|
|
353
|
+
},
|
|
354
|
+
{
|
|
355
|
+
id: 'capture-summary',
|
|
356
|
+
title: 'Capture reviewer summary',
|
|
357
|
+
description: 'Gather highlights so reviewers instantly understand the change.',
|
|
358
|
+
action: async (ctx) => {
|
|
359
|
+
const defaultSummary = getRecentCommitSummary();
|
|
360
|
+
let summary = defaultSummary;
|
|
361
|
+
if (!ctx.autoContinue) {
|
|
362
|
+
summary =
|
|
363
|
+
(await (0, prompt_1.promptInput)('Describe what reviewers should know (markdown ok)', defaultSummary || 'Add a quick summary of your changes')) || defaultSummary;
|
|
364
|
+
}
|
|
365
|
+
summary = (summary || defaultSummary || '').trim();
|
|
366
|
+
if (!summary) {
|
|
367
|
+
summary = '- Updated files\n- Ready for review';
|
|
368
|
+
}
|
|
369
|
+
ctx.prSummary = summary;
|
|
370
|
+
ctx.recordInsight('prSummary', summary);
|
|
371
|
+
(0, ui_1.displayInfo)('PR summary captured', [summary]);
|
|
372
|
+
},
|
|
373
|
+
},
|
|
374
|
+
{
|
|
375
|
+
id: 'open-pr',
|
|
376
|
+
title: 'Create pull request',
|
|
377
|
+
description: 'Generate title + body and open a GitHub PR with one approval.',
|
|
378
|
+
action: async (ctx) => {
|
|
379
|
+
const branch = (0, git_1.getCurrentBranch)();
|
|
380
|
+
const defaultTitle = getDefaultPRTitle();
|
|
381
|
+
const storedSummary = ctx.prSummary || getRecentCommitSummary();
|
|
382
|
+
const testResult = ctx.prTestResult;
|
|
383
|
+
const defaultBody = buildPRBody(storedSummary, testResult);
|
|
384
|
+
const title = ctx.autoContinue
|
|
385
|
+
? defaultTitle
|
|
386
|
+
: await (0, prompt_1.promptInput)('PR title', defaultTitle || `Updates from ${branch}`);
|
|
387
|
+
const body = ctx.autoContinue
|
|
388
|
+
? defaultBody
|
|
389
|
+
: await (0, prompt_1.promptInput)('PR body (markdown supported)', defaultBody);
|
|
390
|
+
(0, ui_1.displayInfo)('Opening pull request on GitHub...', [
|
|
391
|
+
`Title: ${title}`,
|
|
392
|
+
testResult ? `Tests: ${testResult}` : 'Tests: not provided',
|
|
393
|
+
]);
|
|
394
|
+
try {
|
|
395
|
+
const pr = await github.createPullRequest({
|
|
396
|
+
title: title || defaultTitle,
|
|
397
|
+
body: body || defaultBody,
|
|
398
|
+
});
|
|
399
|
+
ctx.recordInsight('pullRequest', `#${pr.number} ${pr.title}`);
|
|
400
|
+
ctx.recordInsight('prUrl', pr.html_url);
|
|
401
|
+
(0, ui_1.displaySuccess)('Pull request created.', [
|
|
402
|
+
`#${pr.number} ${pr.title}`,
|
|
403
|
+
pr.html_url,
|
|
404
|
+
]);
|
|
405
|
+
}
|
|
406
|
+
catch (error) {
|
|
407
|
+
ctx.recordInsight('pullRequest', 'Failed to open PR');
|
|
408
|
+
throw new Error(formatGitHubError(error));
|
|
409
|
+
}
|
|
410
|
+
},
|
|
411
|
+
},
|
|
412
|
+
],
|
|
413
|
+
},
|
|
214
414
|
];
|
|
215
415
|
const WORKFLOW_TIME_SAVINGS = {
|
|
216
416
|
'conflict-crusher': 25,
|
|
217
417
|
'release-ready': 18,
|
|
418
|
+
'pr-polish': 15,
|
|
218
419
|
};
|
|
219
420
|
const WORKFLOW_EVENT_IDS = {
|
|
220
421
|
'conflict-crusher': 'autopilot:conflict-crusher',
|
|
221
422
|
'release-ready': 'autopilot:release-ready',
|
|
423
|
+
'pr-polish': 'autopilot:pr-polish',
|
|
222
424
|
};
|
|
223
425
|
async function autopilotCommand(workflowId, rawOptions) {
|
|
224
426
|
const options = {
|
|
@@ -462,3 +664,108 @@ function inferProjectTestCommand() {
|
|
|
462
664
|
}
|
|
463
665
|
return null;
|
|
464
666
|
}
|
|
667
|
+
function branchHasUpstream() {
|
|
668
|
+
try {
|
|
669
|
+
(0, child_process_1.execSync)('git rev-parse --abbrev-ref --symbolic-full-name @{u}', {
|
|
670
|
+
stdio: ['ignore', 'ignore', 'ignore'],
|
|
671
|
+
});
|
|
672
|
+
return true;
|
|
673
|
+
}
|
|
674
|
+
catch {
|
|
675
|
+
return false;
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
function getBranchTrackingStatus() {
|
|
679
|
+
try {
|
|
680
|
+
const firstLine = (0, child_process_1.execSync)('git status -sb', {
|
|
681
|
+
encoding: 'utf-8',
|
|
682
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
683
|
+
})
|
|
684
|
+
.split('\n')[0]
|
|
685
|
+
.trim();
|
|
686
|
+
const match = firstLine.match(/\[(.+)\]/);
|
|
687
|
+
if (match && match[1]) {
|
|
688
|
+
return match[1].replace(',', ' • ');
|
|
689
|
+
}
|
|
690
|
+
return 'Up to date with upstream';
|
|
691
|
+
}
|
|
692
|
+
catch {
|
|
693
|
+
return null;
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
function getRecentCommitSummary(limit = 3) {
|
|
697
|
+
try {
|
|
698
|
+
const log = (0, child_process_1.execSync)(`git log -${limit} --oneline`, {
|
|
699
|
+
encoding: 'utf-8',
|
|
700
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
701
|
+
})
|
|
702
|
+
.trim()
|
|
703
|
+
.split('\n')
|
|
704
|
+
.filter(Boolean)
|
|
705
|
+
.map((line) => `- ${line.trim()}`)
|
|
706
|
+
.join('\n');
|
|
707
|
+
return log;
|
|
708
|
+
}
|
|
709
|
+
catch {
|
|
710
|
+
return '';
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
function getDefaultPRTitle() {
|
|
714
|
+
try {
|
|
715
|
+
const title = (0, child_process_1.execSync)('git log -1 --pretty=%s', {
|
|
716
|
+
encoding: 'utf-8',
|
|
717
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
718
|
+
}).trim();
|
|
719
|
+
if (title) {
|
|
720
|
+
return title;
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
catch {
|
|
724
|
+
// no-op
|
|
725
|
+
}
|
|
726
|
+
const branch = (0, git_1.getCurrentBranch)();
|
|
727
|
+
return branch && branch !== 'unknown' ? `Updates from ${branch}` : 'Updates from SnapCommit';
|
|
728
|
+
}
|
|
729
|
+
function buildPRBody(summary, tests) {
|
|
730
|
+
const lines = [];
|
|
731
|
+
lines.push('## Summary');
|
|
732
|
+
if (summary && summary.trim()) {
|
|
733
|
+
lines.push(summary.trim());
|
|
734
|
+
}
|
|
735
|
+
else {
|
|
736
|
+
lines.push('- Describe the key changes');
|
|
737
|
+
}
|
|
738
|
+
lines.push('');
|
|
739
|
+
lines.push('## Testing');
|
|
740
|
+
if (tests && tests.trim()) {
|
|
741
|
+
lines.push(`- ${tests.trim()}`);
|
|
742
|
+
}
|
|
743
|
+
else {
|
|
744
|
+
lines.push('- [ ] Tests not run');
|
|
745
|
+
}
|
|
746
|
+
lines.push('');
|
|
747
|
+
lines.push('## Checklist');
|
|
748
|
+
lines.push('- [ ] Linked issue / ticket');
|
|
749
|
+
lines.push('- [ ] Added or updated tests');
|
|
750
|
+
lines.push('- [ ] Updated documentation (if needed)');
|
|
751
|
+
return lines.join('\n');
|
|
752
|
+
}
|
|
753
|
+
function formatGitHubError(error) {
|
|
754
|
+
const raw = (error?.message || String(error) || '').trim();
|
|
755
|
+
if (!raw) {
|
|
756
|
+
return 'GitHub request failed. Verify your connection with `snap github status`.';
|
|
757
|
+
}
|
|
758
|
+
if (/not connected/i.test(raw) || /github connect/i.test(raw)) {
|
|
759
|
+
return 'GitHub not connected. Run `snap github connect` and try again.';
|
|
760
|
+
}
|
|
761
|
+
if (/bad credentials/i.test(raw)) {
|
|
762
|
+
return 'GitHub authentication failed. Re-authorize with `snap github connect`.';
|
|
763
|
+
}
|
|
764
|
+
if (/not a github repository/i.test(raw)) {
|
|
765
|
+
return 'Origin remote is missing or not a GitHub repo. Add a GitHub remote and retry.';
|
|
766
|
+
}
|
|
767
|
+
if (/resource not accessible/i.test(raw)) {
|
|
768
|
+
return `${raw} — check that your GitHub user has access to this repository.`;
|
|
769
|
+
}
|
|
770
|
+
return raw;
|
|
771
|
+
}
|
|
@@ -41,6 +41,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
41
41
|
};
|
|
42
42
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
43
43
|
exports.executeCursorStyle = executeCursorStyle;
|
|
44
|
+
exports.showStatus = showStatus;
|
|
44
45
|
const chalk_1 = __importDefault(require("chalk"));
|
|
45
46
|
const fs_1 = __importDefault(require("fs"));
|
|
46
47
|
const child_process_1 = require("child_process");
|
|
@@ -113,7 +114,7 @@ async function handleAICommand(userInput) {
|
|
|
113
114
|
async function showStatus() {
|
|
114
115
|
const status = (0, git_1.getGitStatus)();
|
|
115
116
|
const branch = (0, git_1.getCurrentBranch)();
|
|
116
|
-
const hasChanges = status.
|
|
117
|
+
const hasChanges = status.entries.length > 0;
|
|
117
118
|
console.log(chalk_1.default.blue(`\nBranch: ${branch}`));
|
|
118
119
|
if (!hasChanges) {
|
|
119
120
|
console.log(chalk_1.default.gray('✓ Branch clean - no changes\n'));
|
|
@@ -123,10 +124,48 @@ async function showStatus() {
|
|
|
123
124
|
if (status.unstaged > 0)
|
|
124
125
|
console.log(chalk_1.default.yellow(` • ${status.unstaged} modified`));
|
|
125
126
|
if (status.untracked > 0)
|
|
126
|
-
console.log(chalk_1.default.
|
|
127
|
+
console.log(chalk_1.default.cyan(` • ${status.untracked} new`));
|
|
127
128
|
if (status.staged > 0)
|
|
128
129
|
console.log(chalk_1.default.green(` • ${status.staged} staged`));
|
|
129
130
|
console.log();
|
|
131
|
+
const stagedFiles = [];
|
|
132
|
+
const unstagedFiles = [];
|
|
133
|
+
const untrackedFiles = [];
|
|
134
|
+
const pushUnique = (list, value) => {
|
|
135
|
+
if (!list.includes(value)) {
|
|
136
|
+
list.push(value);
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
status.entries.forEach((entry) => {
|
|
140
|
+
const file = entry.file;
|
|
141
|
+
const stageCode = entry.stagedCode;
|
|
142
|
+
const worktreeCode = entry.worktreeCode;
|
|
143
|
+
if (stageCode === '?' && worktreeCode === '?') {
|
|
144
|
+
pushUnique(untrackedFiles, file);
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
if (stageCode !== ' ' && stageCode !== '?') {
|
|
148
|
+
pushUnique(stagedFiles, file);
|
|
149
|
+
}
|
|
150
|
+
if (worktreeCode !== ' ' && worktreeCode !== '?') {
|
|
151
|
+
pushUnique(unstagedFiles, file);
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
const printSection = (label, files, formatter) => {
|
|
155
|
+
if (!files.length) {
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
console.log(label);
|
|
159
|
+
files.forEach((file) => {
|
|
160
|
+
console.log(formatter(file));
|
|
161
|
+
});
|
|
162
|
+
console.log();
|
|
163
|
+
};
|
|
164
|
+
printSection(chalk_1.default.green('Staged:'), stagedFiles, (file) => chalk_1.default.green(` ✓ ${file}`));
|
|
165
|
+
printSection(chalk_1.default.yellow('Modified (unstaged):'), unstagedFiles, (file) => chalk_1.default.yellow(` ✎ ${file}`));
|
|
166
|
+
printSection(chalk_1.default.cyan('Untracked:'), untrackedFiles, (file) => chalk_1.default.cyan(` + ${file}`));
|
|
167
|
+
console.log(chalk_1.default.gray('Tip: say "show diff <file>", "stage <file>", or "commit these changes" next.'));
|
|
168
|
+
console.log();
|
|
130
169
|
}
|
|
131
170
|
/**
|
|
132
171
|
* Execute commit - EXACTLY like Cursor!
|
|
@@ -19,6 +19,7 @@ exports.switchBranch = switchBranch;
|
|
|
19
19
|
exports.mergeBranch = mergeBranch;
|
|
20
20
|
exports.showLog = showLog;
|
|
21
21
|
exports.showDiff = showDiff;
|
|
22
|
+
exports.showDiffForFile = showDiffForFile;
|
|
22
23
|
const child_process_1 = require("child_process");
|
|
23
24
|
const chalk_1 = __importDefault(require("chalk"));
|
|
24
25
|
const readline_1 = __importDefault(require("readline"));
|
|
@@ -294,6 +295,21 @@ function showDiff(cached = false) {
|
|
|
294
295
|
console.log(chalk_1.default.red(`\n❌ Failed to show diff: ${error.message}\n`));
|
|
295
296
|
}
|
|
296
297
|
}
|
|
298
|
+
/**
|
|
299
|
+
* Show diff for a specific file
|
|
300
|
+
*/
|
|
301
|
+
function showDiffForFile(filePath, cached = false) {
|
|
302
|
+
try {
|
|
303
|
+
const flag = cached ? '--cached' : '';
|
|
304
|
+
const escapedPath = escapeFilePath(filePath.trim());
|
|
305
|
+
console.log(chalk_1.default.bold(`\n📄 Changes in ${filePath.trim()}:\n`));
|
|
306
|
+
(0, child_process_1.execSync)(`git diff ${flag} -- "${escapedPath}"`, { stdio: 'inherit' });
|
|
307
|
+
console.log('');
|
|
308
|
+
}
|
|
309
|
+
catch (error) {
|
|
310
|
+
console.log(chalk_1.default.red(`\n❌ Failed to show diff for ${filePath.trim()}: ${error.message}\n`));
|
|
311
|
+
}
|
|
312
|
+
}
|
|
297
313
|
/**
|
|
298
314
|
* Helper: Confirm action
|
|
299
315
|
*/
|
|
@@ -309,3 +325,6 @@ async function confirmAction(message) {
|
|
|
309
325
|
});
|
|
310
326
|
});
|
|
311
327
|
}
|
|
328
|
+
function escapeFilePath(path) {
|
|
329
|
+
return path.replace(/(["\\$`])/g, '\\$1');
|
|
330
|
+
}
|
package/dist/commands/stats.js
CHANGED
|
@@ -88,6 +88,7 @@ function formatEventLabel(eventId) {
|
|
|
88
88
|
const table = {
|
|
89
89
|
'autopilot:conflict-crusher': 'Autopilot – Conflict Crusher',
|
|
90
90
|
'autopilot:release-ready': 'Autopilot – Release Ready',
|
|
91
|
+
'autopilot:pr-polish': 'Autopilot – PR Polish',
|
|
91
92
|
'commit:interactive': 'Interactive AI commits',
|
|
92
93
|
'commit:quick': 'Quick commits',
|
|
93
94
|
'conflict:auto': 'AI conflict resolution',
|
package/dist/repl/interpreter.js
CHANGED
|
@@ -210,13 +210,12 @@ async function tryQuickCommands(input, context) {
|
|
|
210
210
|
// Status commands
|
|
211
211
|
if (lower === 'status' || lower === 'show status' || lower === 'what changed') {
|
|
212
212
|
try {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
console.log('');
|
|
213
|
+
const { showStatus } = await Promise.resolve().then(() => __importStar(require('../commands/cursor-style')));
|
|
214
|
+
await showStatus();
|
|
216
215
|
return true;
|
|
217
216
|
}
|
|
218
217
|
catch (error) {
|
|
219
|
-
console.log(chalk_1.default.red(
|
|
218
|
+
console.log(chalk_1.default.red(`\n❌ Failed to get status: ${error.message}\n`));
|
|
220
219
|
return true;
|
|
221
220
|
}
|
|
222
221
|
}
|
|
@@ -345,7 +344,23 @@ async function tryQuickCommands(input, context) {
|
|
|
345
344
|
gitAdvanced.showLog();
|
|
346
345
|
return true;
|
|
347
346
|
}
|
|
348
|
-
// Git Advanced: Show diff
|
|
347
|
+
// Git Advanced: Show diff for specific file
|
|
348
|
+
const diffFileMatch = input.match(/(?:show|what).*(?:diff|changed).*(?:in|for|of)\s+(.+)/i) ||
|
|
349
|
+
input.match(/show diff (?:for|of|on)\s+(.+)/i) ||
|
|
350
|
+
input.match(/diff (?:for|of|on)\s+(.+)/i);
|
|
351
|
+
if (diffFileMatch) {
|
|
352
|
+
const raw = diffFileMatch[1]
|
|
353
|
+
.trim()
|
|
354
|
+
.replace(/^the\s+/i, '')
|
|
355
|
+
.replace(/^(file|files)\s+/i, '')
|
|
356
|
+
.replace(/\s+please$/i, '')
|
|
357
|
+
.replace(/[?]+$/g, '');
|
|
358
|
+
if (raw.length > 0) {
|
|
359
|
+
gitAdvanced.showDiffForFile(raw);
|
|
360
|
+
return true;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
// Git Advanced: Show diff (general)
|
|
349
364
|
if (lower === 'diff' || lower === 'show diff' || lower === 'what changed' || lower === 'changes') {
|
|
350
365
|
gitAdvanced.showDiff();
|
|
351
366
|
return true;
|
package/dist/utils/git.js
CHANGED
|
@@ -38,8 +38,16 @@ function getGitStatus() {
|
|
|
38
38
|
let staged = 0;
|
|
39
39
|
let unstaged = 0;
|
|
40
40
|
let untracked = 0;
|
|
41
|
+
const entries = [];
|
|
41
42
|
lines.forEach((line) => {
|
|
42
43
|
const statusCode = line.substring(0, 2);
|
|
44
|
+
const file = line.substring(3).trim();
|
|
45
|
+
entries.push({
|
|
46
|
+
code: statusCode,
|
|
47
|
+
file,
|
|
48
|
+
stagedCode: statusCode[0],
|
|
49
|
+
worktreeCode: statusCode[1],
|
|
50
|
+
});
|
|
43
51
|
if (statusCode[0] !== ' ' && statusCode[0] !== '?')
|
|
44
52
|
staged++;
|
|
45
53
|
if (statusCode[1] !== ' ')
|
|
@@ -47,7 +55,7 @@ function getGitStatus() {
|
|
|
47
55
|
if (statusCode[0] === '?' && statusCode[1] === '?')
|
|
48
56
|
untracked++;
|
|
49
57
|
});
|
|
50
|
-
return { staged, unstaged, untracked };
|
|
58
|
+
return { staged, unstaged, untracked, entries };
|
|
51
59
|
}
|
|
52
60
|
catch (error) {
|
|
53
61
|
throw new Error(`Git error: ${error.message}`);
|
package/dist/utils/metrics.js
CHANGED