@snapcommit/cli 1.0.5 → 1.0.7

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/index.js CHANGED
@@ -28,8 +28,11 @@ const program = new commander_1.Command();
28
28
  // Check for updates (async, non-blocking)
29
29
  (0, version_1.checkForUpdates)().then((result) => {
30
30
  if (result && result.hasUpdate) {
31
- console.log(chalk_1.default.yellow(`\n⚠️ Update available: ${result.currentVersion} → ${result.latestVersion}`));
32
- console.log(chalk_1.default.gray(' Run: npm install -g builderos\n'));
31
+ console.log(chalk_1.default.yellow.bold('\n╔════════════════════════════════════════╗'));
32
+ console.log(chalk_1.default.yellow.bold('║ 🚀 UPDATE AVAILABLE! ║'));
33
+ console.log(chalk_1.default.yellow.bold('╚════════════════════════════════════════╝'));
34
+ console.log(chalk_1.default.white(` Current: ${chalk_1.default.red(result.currentVersion)} → Latest: ${chalk_1.default.green(result.latestVersion)}`));
35
+ console.log(chalk_1.default.cyan(' Update now: ') + chalk_1.default.white('npm install -g @snapcommit/cli@latest\n'));
33
36
  }
34
37
  }).catch(() => {
35
38
  // Silent fail
package/dist/repl.js CHANGED
@@ -11,6 +11,8 @@ const natural_1 = require("./commands/natural");
11
11
  const quick_1 = require("./commands/quick");
12
12
  const stats_1 = require("./commands/stats");
13
13
  const github_connect_1 = require("./commands/github-connect");
14
+ const repo_manager_1 = require("./utils/repo-manager");
15
+ const version_1 = require("./utils/version");
14
16
  /**
15
17
  * Start SnapCommit REPL (Read-Eval-Print-Loop)
16
18
  * Interactive mode for natural language Git commands
@@ -28,6 +30,18 @@ async function startREPL() {
28
30
  process.exit(1);
29
31
  }
30
32
  console.log(chalk_1.default.green(`✅ Logged in as ${chalk_1.default.bold(authConfig.email)}\n`));
33
+ // Check for updates (non-blocking)
34
+ (0, version_1.checkForUpdates)().then(updateInfo => {
35
+ if (updateInfo && updateInfo.hasUpdate) {
36
+ console.log(chalk_1.default.yellow.bold('╔════════════════════════════════════════╗'));
37
+ console.log(chalk_1.default.yellow.bold('║ 🚀 UPDATE AVAILABLE! ║'));
38
+ console.log(chalk_1.default.yellow.bold('╚════════════════════════════════════════╝'));
39
+ console.log(chalk_1.default.white(` Current: ${chalk_1.default.red(updateInfo.currentVersion)} → Latest: ${chalk_1.default.green(updateInfo.latestVersion)}\n`));
40
+ console.log(chalk_1.default.cyan(' Update now: ') + chalk_1.default.white('npm install -g @snapcommit/cli@latest\n'));
41
+ }
42
+ }).catch(() => {
43
+ // Silent fail - don't interrupt user experience
44
+ });
31
45
  // Check GitHub connection status
32
46
  const githubConnected = (0, github_connect_1.isGitHubConnected)();
33
47
  if (githubConnected) {
@@ -38,6 +52,7 @@ async function startREPL() {
38
52
  console.log(chalk_1.default.bold('💡 What can SnapCommit do?\n'));
39
53
  console.log(chalk_1.default.gray(' • ') + chalk_1.default.white('Natural language Git: ') + chalk_1.default.cyan('"undo my last commit"'));
40
54
  console.log(chalk_1.default.gray(' • ') + chalk_1.default.white('Quick AI commits: ') + chalk_1.default.cyan('quick'));
55
+ 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'));
41
56
  if (githubConnected) {
42
57
  console.log(chalk_1.default.gray(' • ') + chalk_1.default.white('GitHub operations: ') + chalk_1.default.cyan('"create a PR", "check CI"'));
43
58
  }
@@ -82,8 +97,45 @@ async function startREPL() {
82
97
  rl.prompt();
83
98
  return;
84
99
  }
100
+ // cd command - navigate to different repo
101
+ if (line.startsWith('cd ')) {
102
+ const targetPath = line.substring(3).trim();
103
+ const result = (0, repo_manager_1.changeToRepo)(targetPath);
104
+ if (result.success) {
105
+ console.log(chalk_1.default.green(`\n✅ Switched to: ${chalk_1.default.bold(result.repoName)}`));
106
+ console.log(chalk_1.default.gray(` Path: ${result.repoPath}\n`));
107
+ }
108
+ else {
109
+ console.log(chalk_1.default.red(`\n❌ ${result.error}`));
110
+ console.log(chalk_1.default.gray(' Navigate to a valid git repository\n'));
111
+ }
112
+ rl.prompt();
113
+ return;
114
+ }
115
+ // repos command - show recent repos
116
+ if (line === 'repos' || line === 'repo') {
117
+ const repos = (0, repo_manager_1.getRecentRepos)();
118
+ if (repos.length === 0) {
119
+ console.log(chalk_1.default.yellow('\n📁 No recent repositories\n'));
120
+ 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'));
121
+ }
122
+ else {
123
+ console.log(chalk_1.default.bold('\n📁 Recent Repositories:\n'));
124
+ repos.slice(0, 10).forEach((repo, index) => {
125
+ const isCurrent = repo.path === process.cwd();
126
+ const prefix = isCurrent ? chalk_1.default.green('→ ') : ' ';
127
+ console.log(prefix + chalk_1.default.cyan(`${index + 1}. ${repo.name}`) + chalk_1.default.gray(` (${repo.useCount} uses)`));
128
+ console.log(' ' + chalk_1.default.gray(repo.path));
129
+ });
130
+ console.log(chalk_1.default.gray('\n Use ') + chalk_1.default.cyan('cd <path>') + chalk_1.default.gray(' to switch repos\n'));
131
+ }
132
+ rl.prompt();
133
+ return;
134
+ }
85
135
  if (line === 'help' || line === 'h') {
86
136
  console.log(chalk_1.default.bold('\n💡 SnapCommit Commands:\n'));
137
+ console.log(chalk_1.default.gray(' • ') + chalk_1.default.cyan('cd /path/to/repo') + chalk_1.default.gray(' - Switch to a different repository'));
138
+ console.log(chalk_1.default.gray(' • ') + chalk_1.default.cyan('repos') + chalk_1.default.gray(' - Show recent repositories'));
87
139
  console.log(chalk_1.default.gray(' • ') + chalk_1.default.cyan('quick') + chalk_1.default.gray(' - Quick AI commit (stage all + commit)'));
88
140
  console.log(chalk_1.default.gray(' • ') + chalk_1.default.cyan('stats') + chalk_1.default.gray(' - Show your coding stats'));
89
141
  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
+ }
@@ -33,7 +33,7 @@ async function checkForUpdates() {
33
33
  }
34
34
  }
35
35
  // Fetch latest version from npm
36
- const latestVersion = await getLatestVersionFromNpm('snapcommit');
36
+ const latestVersion = await getLatestVersionFromNpm('@snapcommit/cli');
37
37
  // Update cache
38
38
  const newCache = {
39
39
  lastCheck: Date.now(),
@@ -53,7 +53,9 @@ async function checkForUpdates() {
53
53
  }
54
54
  function getLatestVersionFromNpm(packageName) {
55
55
  return new Promise((resolve, reject) => {
56
- https_1.default.get(`https://registry.npmjs.org/${packageName}/latest`, (res) => {
56
+ // Encode package name for URL (handles scoped packages like @snapcommit/cli)
57
+ const encodedName = packageName.replace('/', '%2F');
58
+ https_1.default.get(`https://registry.npmjs.org/${encodedName}/latest`, (res) => {
57
59
  let data = '';
58
60
  res.on('data', (chunk) => data += chunk);
59
61
  res.on('end', () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@snapcommit/cli",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "description": "Instant AI commits. Beautiful progress tracking. Never write commit messages again.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {