@snapcommit/cli 1.0.4 → 1.0.6

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/lib/auth.js CHANGED
@@ -133,6 +133,14 @@ async function promptAuth() {
133
133
  console.log(chalk_1.default.gray(' • ') + chalk_1.default.cyan('snap quick') + chalk_1.default.gray(' - AI commit in any repo'));
134
134
  console.log(chalk_1.default.gray(' • ') + chalk_1.default.cyan('snap') + chalk_1.default.gray(' - Natural language Git commands'));
135
135
  console.log(chalk_1.default.gray(' • Type ') + chalk_1.default.cyan('exit') + chalk_1.default.gray(' to leave SnapCommit\n'));
136
+ console.log(chalk_1.default.yellow('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'));
137
+ console.log(chalk_1.default.bold('💡 Want GitHub features too?\n'));
138
+ console.log(chalk_1.default.gray(' Create PRs, check CI, and more with natural language!'));
139
+ console.log(chalk_1.default.gray(' Run: ') + chalk_1.default.cyan('snap github connect'));
140
+ console.log(chalk_1.default.gray(' (You\'ll need a GitHub Personal Access Token - Classic)\n'));
141
+ console.log(chalk_1.default.gray(' Get one at: ') + chalk_1.default.cyan('https://github.com/settings/tokens'));
142
+ console.log(chalk_1.default.gray(' Scopes needed: ') + chalk_1.default.white('repo, workflow, read:user'));
143
+ console.log(chalk_1.default.gray(' 💡 Tip: Set no expiration or remember to renew!\n'));
136
144
  return true;
137
145
  }
138
146
  else {
package/dist/repl.js CHANGED
@@ -10,6 +10,8 @@ const auth_1 = require("./lib/auth");
10
10
  const natural_1 = require("./commands/natural");
11
11
  const quick_1 = require("./commands/quick");
12
12
  const stats_1 = require("./commands/stats");
13
+ const github_connect_1 = require("./commands/github-connect");
14
+ const repo_manager_1 = require("./utils/repo-manager");
13
15
  /**
14
16
  * Start SnapCommit REPL (Read-Eval-Print-Loop)
15
17
  * Interactive mode for natural language Git commands
@@ -27,12 +29,30 @@ async function startREPL() {
27
29
  process.exit(1);
28
30
  }
29
31
  console.log(chalk_1.default.green(`✅ Logged in as ${chalk_1.default.bold(authConfig.email)}\n`));
32
+ // Check GitHub connection status
33
+ const githubConnected = (0, github_connect_1.isGitHubConnected)();
34
+ if (githubConnected) {
35
+ const githubConfig = (0, github_connect_1.getGitHubConfig)();
36
+ console.log(chalk_1.default.green(`✅ GitHub connected as ${chalk_1.default.bold('@' + githubConfig?.username)}\n`));
37
+ }
30
38
  console.log(chalk_1.default.gray('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'));
31
39
  console.log(chalk_1.default.bold('💡 What can SnapCommit do?\n'));
32
40
  console.log(chalk_1.default.gray(' • ') + chalk_1.default.white('Natural language Git: ') + chalk_1.default.cyan('"undo my last commit"'));
33
41
  console.log(chalk_1.default.gray(' • ') + chalk_1.default.white('Quick AI commits: ') + chalk_1.default.cyan('quick'));
34
- console.log(chalk_1.default.gray(' • ') + chalk_1.default.white('Show your stats: ') + chalk_1.default.cyan('stats'));
42
+ console.log(chalk_1.default.gray(' • ') + chalk_1.default.white('Switch repos: ') + chalk_1.default.cyan('cd /path/to/repo') + chalk_1.default.gray(' or ') + chalk_1.default.cyan('repos'));
43
+ if (githubConnected) {
44
+ console.log(chalk_1.default.gray(' • ') + chalk_1.default.white('GitHub operations: ') + chalk_1.default.cyan('"create a PR", "check CI"'));
45
+ }
35
46
  console.log(chalk_1.default.gray(' • ') + chalk_1.default.white('Exit SnapCommit: ') + chalk_1.default.cyan('exit') + chalk_1.default.gray(' or ') + chalk_1.default.cyan('quit\n'));
47
+ // Show GitHub setup reminder if not connected
48
+ if (!githubConnected) {
49
+ console.log(chalk_1.default.yellow('💡 Want GitHub features? (Create PRs, check CI, etc.)\n'));
50
+ console.log(chalk_1.default.gray(' Run: ') + chalk_1.default.cyan('snap github connect'));
51
+ console.log(chalk_1.default.gray(' You\'ll need a GitHub Personal Access Token (Classic)'));
52
+ console.log(chalk_1.default.gray(' Get one at: ') + chalk_1.default.cyan('https://github.com/settings/tokens'));
53
+ console.log(chalk_1.default.gray(' Scopes: ') + chalk_1.default.white('repo, workflow, read:user'));
54
+ console.log(chalk_1.default.gray(' 💡 Set no expiration or remember to renew!\n'));
55
+ }
36
56
  console.log(chalk_1.default.gray('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'));
37
57
  console.log(chalk_1.default.bold.yellow('🎯 Try it now! Type what you want to do:\n'));
38
58
  const rl = readline_1.default.createInterface({
@@ -64,8 +84,45 @@ async function startREPL() {
64
84
  rl.prompt();
65
85
  return;
66
86
  }
87
+ // cd command - navigate to different repo
88
+ if (line.startsWith('cd ')) {
89
+ const targetPath = line.substring(3).trim();
90
+ const result = (0, repo_manager_1.changeToRepo)(targetPath);
91
+ if (result.success) {
92
+ console.log(chalk_1.default.green(`\n✅ Switched to: ${chalk_1.default.bold(result.repoName)}`));
93
+ console.log(chalk_1.default.gray(` Path: ${result.repoPath}\n`));
94
+ }
95
+ else {
96
+ console.log(chalk_1.default.red(`\n❌ ${result.error}`));
97
+ console.log(chalk_1.default.gray(' Navigate to a valid git repository\n'));
98
+ }
99
+ rl.prompt();
100
+ return;
101
+ }
102
+ // repos command - show recent repos
103
+ if (line === 'repos' || line === 'repo') {
104
+ const repos = (0, repo_manager_1.getRecentRepos)();
105
+ if (repos.length === 0) {
106
+ console.log(chalk_1.default.yellow('\n📁 No recent repositories\n'));
107
+ console.log(chalk_1.default.gray(' Use ') + chalk_1.default.cyan('cd /path/to/repo') + chalk_1.default.gray(' to navigate to a repo\n'));
108
+ }
109
+ else {
110
+ console.log(chalk_1.default.bold('\n📁 Recent Repositories:\n'));
111
+ repos.slice(0, 10).forEach((repo, index) => {
112
+ const isCurrent = repo.path === process.cwd();
113
+ const prefix = isCurrent ? chalk_1.default.green('→ ') : ' ';
114
+ console.log(prefix + chalk_1.default.cyan(`${index + 1}. ${repo.name}`) + chalk_1.default.gray(` (${repo.useCount} uses)`));
115
+ console.log(' ' + chalk_1.default.gray(repo.path));
116
+ });
117
+ console.log(chalk_1.default.gray('\n Use ') + chalk_1.default.cyan('cd <path>') + chalk_1.default.gray(' to switch repos\n'));
118
+ }
119
+ rl.prompt();
120
+ return;
121
+ }
67
122
  if (line === 'help' || line === 'h') {
68
123
  console.log(chalk_1.default.bold('\n💡 SnapCommit Commands:\n'));
124
+ console.log(chalk_1.default.gray(' • ') + chalk_1.default.cyan('cd /path/to/repo') + chalk_1.default.gray(' - Switch to a different repository'));
125
+ console.log(chalk_1.default.gray(' • ') + chalk_1.default.cyan('repos') + chalk_1.default.gray(' - Show recent repositories'));
69
126
  console.log(chalk_1.default.gray(' • ') + chalk_1.default.cyan('quick') + chalk_1.default.gray(' - Quick AI commit (stage all + commit)'));
70
127
  console.log(chalk_1.default.gray(' • ') + chalk_1.default.cyan('stats') + chalk_1.default.gray(' - Show your coding stats'));
71
128
  console.log(chalk_1.default.gray(' • ') + chalk_1.default.cyan('exit/quit') + chalk_1.default.gray(' - Exit SnapCommit'));
@@ -0,0 +1,187 @@
1
+ "use strict";
2
+ /**
3
+ * Multi-Repo Manager
4
+ * Handles navigation and tracking of multiple git repositories
5
+ */
6
+ var __importDefault = (this && this.__importDefault) || function (mod) {
7
+ return (mod && mod.__esModule) ? mod : { "default": mod };
8
+ };
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.isGitRepository = isGitRepository;
11
+ exports.changeToRepo = changeToRepo;
12
+ exports.getRecentRepos = getRecentRepos;
13
+ exports.getCurrentRepo = getCurrentRepo;
14
+ exports.findRepo = findRepo;
15
+ const fs_1 = __importDefault(require("fs"));
16
+ const path_1 = __importDefault(require("path"));
17
+ const os_1 = __importDefault(require("os"));
18
+ const child_process_1 = require("child_process");
19
+ const CONFIG_DIR = path_1.default.join(os_1.default.homedir(), '.snapcommit');
20
+ const REPOS_FILE = path_1.default.join(CONFIG_DIR, 'repos.json');
21
+ /**
22
+ * Ensure config directory exists
23
+ */
24
+ function ensureConfigDir() {
25
+ if (!fs_1.default.existsSync(CONFIG_DIR)) {
26
+ fs_1.default.mkdirSync(CONFIG_DIR, { recursive: true });
27
+ }
28
+ }
29
+ /**
30
+ * Load repos configuration
31
+ */
32
+ function loadReposConfig() {
33
+ ensureConfigDir();
34
+ if (!fs_1.default.existsSync(REPOS_FILE)) {
35
+ return { current: null, history: [] };
36
+ }
37
+ try {
38
+ const data = fs_1.default.readFileSync(REPOS_FILE, 'utf-8');
39
+ return JSON.parse(data);
40
+ }
41
+ catch {
42
+ return { current: null, history: [] };
43
+ }
44
+ }
45
+ /**
46
+ * Save repos configuration
47
+ */
48
+ function saveReposConfig(config) {
49
+ ensureConfigDir();
50
+ fs_1.default.writeFileSync(REPOS_FILE, JSON.stringify(config, null, 2));
51
+ }
52
+ /**
53
+ * Check if path is a git repository
54
+ */
55
+ function isGitRepository(dirPath) {
56
+ try {
57
+ const gitDir = path_1.default.join(dirPath, '.git');
58
+ return fs_1.default.existsSync(gitDir) && fs_1.default.statSync(gitDir).isDirectory();
59
+ }
60
+ catch {
61
+ return false;
62
+ }
63
+ }
64
+ /**
65
+ * Get repository name from path
66
+ */
67
+ function getRepoName(repoPath) {
68
+ try {
69
+ // Try to get repo name from git config
70
+ const remoteName = (0, child_process_1.execSync)('git config --get remote.origin.url', {
71
+ cwd: repoPath,
72
+ encoding: 'utf-8',
73
+ stdio: ['pipe', 'pipe', 'ignore']
74
+ }).trim();
75
+ // Extract repo name from URL
76
+ const match = remoteName.match(/\/([^\/]+?)(\.git)?$/);
77
+ if (match) {
78
+ return match[1];
79
+ }
80
+ }
81
+ catch {
82
+ // Fallback to directory name
83
+ }
84
+ return path_1.default.basename(repoPath);
85
+ }
86
+ /**
87
+ * Change to a repository directory
88
+ */
89
+ function changeToRepo(targetPath) {
90
+ // Resolve path (handle ~ and relative paths)
91
+ let resolvedPath = targetPath;
92
+ if (targetPath.startsWith('~')) {
93
+ resolvedPath = targetPath.replace('~', os_1.default.homedir());
94
+ }
95
+ else if (!path_1.default.isAbsolute(targetPath)) {
96
+ resolvedPath = path_1.default.resolve(process.cwd(), targetPath);
97
+ }
98
+ // Check if path exists
99
+ if (!fs_1.default.existsSync(resolvedPath)) {
100
+ return { success: false, error: `Path does not exist: ${resolvedPath}` };
101
+ }
102
+ // Check if it's a directory
103
+ if (!fs_1.default.statSync(resolvedPath).isDirectory()) {
104
+ return { success: false, error: 'Path is not a directory' };
105
+ }
106
+ // Check if it's a git repository
107
+ if (!isGitRepository(resolvedPath)) {
108
+ return { success: false, error: 'Not a git repository' };
109
+ }
110
+ // Change process directory
111
+ try {
112
+ process.chdir(resolvedPath);
113
+ }
114
+ catch (error) {
115
+ return { success: false, error: `Failed to change directory: ${error.message}` };
116
+ }
117
+ // Get repo name
118
+ const repoName = getRepoName(resolvedPath);
119
+ // Track in history
120
+ trackRepo(resolvedPath, repoName);
121
+ return { success: true, repoPath: resolvedPath, repoName };
122
+ }
123
+ /**
124
+ * Track a repository in history
125
+ */
126
+ function trackRepo(repoPath, repoName) {
127
+ const config = loadReposConfig();
128
+ // Update current
129
+ config.current = repoPath;
130
+ // Update or add to history
131
+ const existingIndex = config.history.findIndex(r => r.path === repoPath);
132
+ if (existingIndex >= 0) {
133
+ // Update existing entry
134
+ config.history[existingIndex].lastUsed = Date.now();
135
+ config.history[existingIndex].useCount++;
136
+ }
137
+ else {
138
+ // Add new entry
139
+ config.history.push({
140
+ path: repoPath,
141
+ name: repoName,
142
+ lastUsed: Date.now(),
143
+ useCount: 1
144
+ });
145
+ }
146
+ // Keep only last 20 repos
147
+ config.history = config.history
148
+ .sort((a, b) => b.lastUsed - a.lastUsed)
149
+ .slice(0, 20);
150
+ saveReposConfig(config);
151
+ }
152
+ /**
153
+ * Get recent repositories
154
+ */
155
+ function getRecentRepos() {
156
+ const config = loadReposConfig();
157
+ return config.history.filter(repo => fs_1.default.existsSync(repo.path));
158
+ }
159
+ /**
160
+ * Get current repository path
161
+ */
162
+ function getCurrentRepo() {
163
+ const config = loadReposConfig();
164
+ return config.current;
165
+ }
166
+ /**
167
+ * Find a repo by name or partial path
168
+ */
169
+ function findRepo(query) {
170
+ const config = loadReposConfig();
171
+ // Exact name match
172
+ const exactMatch = config.history.find(r => r.name.toLowerCase() === query.toLowerCase());
173
+ if (exactMatch && fs_1.default.existsSync(exactMatch.path)) {
174
+ return exactMatch;
175
+ }
176
+ // Partial name match
177
+ const partialMatch = config.history.find(r => r.name.toLowerCase().includes(query.toLowerCase()));
178
+ if (partialMatch && fs_1.default.existsSync(partialMatch.path)) {
179
+ return partialMatch;
180
+ }
181
+ // Path match
182
+ const pathMatch = config.history.find(r => r.path.includes(query));
183
+ if (pathMatch && fs_1.default.existsSync(pathMatch.path)) {
184
+ return pathMatch;
185
+ }
186
+ return null;
187
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@snapcommit/cli",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "description": "Instant AI commits. Beautiful progress tracking. Never write commit messages again.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {