bit-cli-ai 1.0.0
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/.env.example +15 -0
- package/LICENSE +21 -0
- package/README.md +178 -0
- package/package.json +73 -0
- package/src/commands/analyze.js +230 -0
- package/src/commands/branch.js +202 -0
- package/src/commands/commit.js +211 -0
- package/src/commands/hotzone.js +235 -0
- package/src/commands/init.js +233 -0
- package/src/commands/merge.js +191 -0
- package/src/index.js +104 -0
- package/src/utils/ai.js +238 -0
- package/src/utils/config.js +178 -0
- package/src/utils/errors.js +170 -0
- package/src/utils/git.js +241 -0
- package/src/utils/logger.js +94 -0
- package/src/utils/validation.js +108 -0
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import { execSync } from 'child_process';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
import ora from 'ora';
|
|
6
|
+
|
|
7
|
+
// Gitignore templates for different project types
|
|
8
|
+
const gitignoreTemplates = {
|
|
9
|
+
node: `# Dependencies
|
|
10
|
+
node_modules/
|
|
11
|
+
package-lock.json
|
|
12
|
+
|
|
13
|
+
# Environment
|
|
14
|
+
.env
|
|
15
|
+
.env.local
|
|
16
|
+
.env.*.local
|
|
17
|
+
|
|
18
|
+
# Build
|
|
19
|
+
dist/
|
|
20
|
+
build/
|
|
21
|
+
.next/
|
|
22
|
+
|
|
23
|
+
# IDE
|
|
24
|
+
.vscode/
|
|
25
|
+
.idea/
|
|
26
|
+
|
|
27
|
+
# OS
|
|
28
|
+
.DS_Store
|
|
29
|
+
Thumbs.db
|
|
30
|
+
|
|
31
|
+
# Logs
|
|
32
|
+
*.log
|
|
33
|
+
npm-debug.log*
|
|
34
|
+
`,
|
|
35
|
+
python: `# Byte-compiled
|
|
36
|
+
__pycache__/
|
|
37
|
+
*.py[cod]
|
|
38
|
+
*$py.class
|
|
39
|
+
|
|
40
|
+
# Virtual environments
|
|
41
|
+
.venv/
|
|
42
|
+
venv/
|
|
43
|
+
ENV/
|
|
44
|
+
|
|
45
|
+
# Environment
|
|
46
|
+
.env
|
|
47
|
+
|
|
48
|
+
# IDE
|
|
49
|
+
.vscode/
|
|
50
|
+
.idea/
|
|
51
|
+
|
|
52
|
+
# Distribution
|
|
53
|
+
dist/
|
|
54
|
+
build/
|
|
55
|
+
*.egg-info/
|
|
56
|
+
`,
|
|
57
|
+
rust: `# Build
|
|
58
|
+
/target/
|
|
59
|
+
Cargo.lock
|
|
60
|
+
|
|
61
|
+
# IDE
|
|
62
|
+
.vscode/
|
|
63
|
+
.idea/
|
|
64
|
+
|
|
65
|
+
# Environment
|
|
66
|
+
.env
|
|
67
|
+
`,
|
|
68
|
+
go: `# Binaries
|
|
69
|
+
*.exe
|
|
70
|
+
*.exe~
|
|
71
|
+
*.dll
|
|
72
|
+
*.so
|
|
73
|
+
*.dylib
|
|
74
|
+
|
|
75
|
+
# Build
|
|
76
|
+
/bin/
|
|
77
|
+
/pkg/
|
|
78
|
+
|
|
79
|
+
# IDE
|
|
80
|
+
.vscode/
|
|
81
|
+
.idea/
|
|
82
|
+
|
|
83
|
+
# Environment
|
|
84
|
+
.env
|
|
85
|
+
`,
|
|
86
|
+
generic: `# Environment
|
|
87
|
+
.env
|
|
88
|
+
.env.local
|
|
89
|
+
|
|
90
|
+
# IDE
|
|
91
|
+
.vscode/
|
|
92
|
+
.idea/
|
|
93
|
+
|
|
94
|
+
# OS
|
|
95
|
+
.DS_Store
|
|
96
|
+
Thumbs.db
|
|
97
|
+
|
|
98
|
+
# Logs
|
|
99
|
+
*.log
|
|
100
|
+
`
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
// Detect project type based on files present
|
|
104
|
+
function detectProjectType() {
|
|
105
|
+
const detectors = [
|
|
106
|
+
{ file: 'package.json', type: 'node' },
|
|
107
|
+
{ file: 'requirements.txt', type: 'python' },
|
|
108
|
+
{ file: 'Pipfile', type: 'python' },
|
|
109
|
+
{ file: 'pyproject.toml', type: 'python' },
|
|
110
|
+
{ file: 'Cargo.toml', type: 'rust' },
|
|
111
|
+
{ file: 'go.mod', type: 'go' },
|
|
112
|
+
];
|
|
113
|
+
|
|
114
|
+
for (const detector of detectors) {
|
|
115
|
+
if (fs.existsSync(detector.file)) {
|
|
116
|
+
return detector.type;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return 'generic';
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Initialize .bit directory for Bit's intelligence layer
|
|
124
|
+
function initBitDirectory() {
|
|
125
|
+
const bitDir = '.bit';
|
|
126
|
+
|
|
127
|
+
if (!fs.existsSync(bitDir)) {
|
|
128
|
+
fs.mkdirSync(bitDir);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Create metadata files
|
|
132
|
+
const metadata = {
|
|
133
|
+
version: '1.0.0',
|
|
134
|
+
createdAt: new Date().toISOString(),
|
|
135
|
+
ghostBranches: [],
|
|
136
|
+
hotZones: [],
|
|
137
|
+
symbolHistory: []
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
fs.writeFileSync(
|
|
141
|
+
path.join(bitDir, 'metadata.json'),
|
|
142
|
+
JSON.stringify(metadata, null, 2)
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
// Create symbol tracking database
|
|
146
|
+
fs.writeFileSync(
|
|
147
|
+
path.join(bitDir, 'symbols.json'),
|
|
148
|
+
JSON.stringify({ functions: {}, classes: {}, lastAnalyzed: null }, null, 2)
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
// Create hot zones tracking
|
|
152
|
+
fs.writeFileSync(
|
|
153
|
+
path.join(bitDir, 'hotzones.json'),
|
|
154
|
+
JSON.stringify({ zones: [], lastChecked: null }, null, 2)
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
// Add .bit to .gitignore if not already there
|
|
158
|
+
const gitignorePath = '.gitignore';
|
|
159
|
+
if (fs.existsSync(gitignorePath)) {
|
|
160
|
+
const content = fs.readFileSync(gitignorePath, 'utf-8');
|
|
161
|
+
if (!content.includes('.bit/')) {
|
|
162
|
+
fs.appendFileSync(gitignorePath, '\n# Bit intelligence layer\n.bit/\n');
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return bitDir;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export async function smartInit() {
|
|
170
|
+
const spinner = ora('Initializing repository...').start();
|
|
171
|
+
|
|
172
|
+
try {
|
|
173
|
+
// Check if already a git repo
|
|
174
|
+
const isGitRepo = fs.existsSync('.git');
|
|
175
|
+
|
|
176
|
+
if (!isGitRepo) {
|
|
177
|
+
// Initialize git repository
|
|
178
|
+
execSync('git init', { stdio: 'pipe' });
|
|
179
|
+
spinner.succeed('Git repository initialized');
|
|
180
|
+
} else {
|
|
181
|
+
spinner.info('Git repository already exists');
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Detect project type
|
|
185
|
+
spinner.start('Detecting project type...');
|
|
186
|
+
const projectType = detectProjectType();
|
|
187
|
+
spinner.succeed(`Detected project type: ${chalk.green(projectType)}`);
|
|
188
|
+
|
|
189
|
+
// Create or update .gitignore
|
|
190
|
+
spinner.start('Setting up .gitignore...');
|
|
191
|
+
const gitignoreContent = gitignoreTemplates[projectType];
|
|
192
|
+
|
|
193
|
+
if (fs.existsSync('.gitignore')) {
|
|
194
|
+
// Append to existing .gitignore
|
|
195
|
+
const existing = fs.readFileSync('.gitignore', 'utf-8');
|
|
196
|
+
const newEntries = gitignoreContent
|
|
197
|
+
.split('\n')
|
|
198
|
+
.filter(line => line.trim() && !line.startsWith('#') && !existing.includes(line.trim()));
|
|
199
|
+
|
|
200
|
+
if (newEntries.length > 0) {
|
|
201
|
+
fs.appendFileSync('.gitignore', '\n# Added by Bit\n' + newEntries.join('\n') + '\n');
|
|
202
|
+
spinner.succeed(`Updated .gitignore with ${newEntries.length} new entries`);
|
|
203
|
+
} else {
|
|
204
|
+
spinner.info('.gitignore already up to date');
|
|
205
|
+
}
|
|
206
|
+
} else {
|
|
207
|
+
fs.writeFileSync('.gitignore', gitignoreContent);
|
|
208
|
+
spinner.succeed('Created .gitignore');
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Initialize Bit directory
|
|
212
|
+
spinner.start('Initializing Bit intelligence layer...');
|
|
213
|
+
const bitDir = initBitDirectory();
|
|
214
|
+
spinner.succeed(`Bit intelligence layer created at ${chalk.cyan(bitDir)}/`);
|
|
215
|
+
|
|
216
|
+
// Summary
|
|
217
|
+
console.log('\n' + chalk.green('Bit initialized successfully!'));
|
|
218
|
+
console.log(chalk.gray('─'.repeat(40)));
|
|
219
|
+
console.log(` Project type: ${chalk.cyan(projectType)}`);
|
|
220
|
+
console.log(` Git repo: ${chalk.cyan('.git/')}`);
|
|
221
|
+
console.log(` Bit brain: ${chalk.cyan('.bit/')}`);
|
|
222
|
+
console.log(chalk.gray('─'.repeat(40)));
|
|
223
|
+
console.log('\nNext steps:');
|
|
224
|
+
console.log(` ${chalk.yellow('bit commit')} - AI-powered commits`);
|
|
225
|
+
console.log(` ${chalk.yellow('bit branch --ghost <name>')} - Create ghost branch`);
|
|
226
|
+
console.log(` ${chalk.yellow('bit merge --preview <branch>')} - Preview merge`);
|
|
227
|
+
|
|
228
|
+
} catch (error) {
|
|
229
|
+
spinner.fail('Initialization failed');
|
|
230
|
+
console.error(chalk.red(error.message));
|
|
231
|
+
process.exit(1);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { execSync } from 'child_process';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import ora from 'ora';
|
|
4
|
+
|
|
5
|
+
// Parse merge-tree output to find conflicts
|
|
6
|
+
function parseConflicts(output) {
|
|
7
|
+
const conflicts = [];
|
|
8
|
+
const lines = output.split('\n');
|
|
9
|
+
|
|
10
|
+
let currentFile = null;
|
|
11
|
+
let inConflict = false;
|
|
12
|
+
|
|
13
|
+
for (const line of lines) {
|
|
14
|
+
// Detect file markers
|
|
15
|
+
if (line.includes('CONFLICT')) {
|
|
16
|
+
const match = line.match(/CONFLICT.*?: (.+)/);
|
|
17
|
+
if (match) {
|
|
18
|
+
conflicts.push({
|
|
19
|
+
file: match[1].trim(),
|
|
20
|
+
type: 'content conflict'
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Detect merge conflicts in content
|
|
26
|
+
if (line.startsWith('<<<<<<< ')) {
|
|
27
|
+
inConflict = true;
|
|
28
|
+
}
|
|
29
|
+
if (line.startsWith('>>>>>>> ')) {
|
|
30
|
+
inConflict = false;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return conflicts;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Get files that will be modified in merge
|
|
38
|
+
function getAffectedFiles(branch) {
|
|
39
|
+
try {
|
|
40
|
+
const currentBranch = execSync('git branch --show-current', { encoding: 'utf-8' }).trim();
|
|
41
|
+
|
|
42
|
+
// Get diff between current and target branch
|
|
43
|
+
const diff = execSync(`git diff --name-only ${currentBranch}...${branch}`, { encoding: 'utf-8' });
|
|
44
|
+
|
|
45
|
+
return diff.split('\n').filter(f => f.trim());
|
|
46
|
+
} catch {
|
|
47
|
+
return [];
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Perform virtual merge preview
|
|
52
|
+
function virtualMerge(branch) {
|
|
53
|
+
const spinner = ora('Analyzing merge...').start();
|
|
54
|
+
|
|
55
|
+
try {
|
|
56
|
+
const currentBranch = execSync('git branch --show-current', { encoding: 'utf-8' }).trim();
|
|
57
|
+
|
|
58
|
+
// Use git merge-tree for conflict detection
|
|
59
|
+
let mergeResult;
|
|
60
|
+
let hasConflicts = false;
|
|
61
|
+
|
|
62
|
+
try {
|
|
63
|
+
// Get merge base
|
|
64
|
+
const mergeBase = execSync(`git merge-base ${currentBranch} ${branch}`, { encoding: 'utf-8' }).trim();
|
|
65
|
+
|
|
66
|
+
// Perform merge-tree (virtual merge)
|
|
67
|
+
mergeResult = execSync(`git merge-tree ${mergeBase} ${currentBranch} ${branch}`, { encoding: 'utf-8' });
|
|
68
|
+
|
|
69
|
+
// Check for conflict markers
|
|
70
|
+
hasConflicts = mergeResult.includes('<<<<<<<') || mergeResult.includes('>>>>>>>');
|
|
71
|
+
|
|
72
|
+
} catch (error) {
|
|
73
|
+
// merge-tree might fail or indicate conflicts
|
|
74
|
+
mergeResult = error.stdout || '';
|
|
75
|
+
hasConflicts = true;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Get affected files
|
|
79
|
+
const affectedFiles = getAffectedFiles(branch);
|
|
80
|
+
|
|
81
|
+
spinner.stop();
|
|
82
|
+
|
|
83
|
+
// Display results
|
|
84
|
+
console.log(chalk.cyan('\n--- Merge Preview ---\n'));
|
|
85
|
+
console.log(`Current branch: ${chalk.green(currentBranch)}`);
|
|
86
|
+
console.log(`Target branch: ${chalk.yellow(branch)}`);
|
|
87
|
+
console.log(chalk.gray('─'.repeat(50)));
|
|
88
|
+
|
|
89
|
+
// Show affected files
|
|
90
|
+
console.log(chalk.yellow(`\nFiles to be modified (${affectedFiles.length}):`));
|
|
91
|
+
if (affectedFiles.length === 0) {
|
|
92
|
+
console.log(chalk.gray(' No files will be modified'));
|
|
93
|
+
} else {
|
|
94
|
+
affectedFiles.forEach(file => {
|
|
95
|
+
console.log(chalk.gray(` ${file}`));
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Show conflict analysis
|
|
100
|
+
console.log(chalk.gray('\n' + '─'.repeat(50)));
|
|
101
|
+
|
|
102
|
+
if (hasConflicts) {
|
|
103
|
+
console.log(chalk.red('\nPotential Conflicts Detected:'));
|
|
104
|
+
|
|
105
|
+
const conflicts = parseConflicts(mergeResult);
|
|
106
|
+
if (conflicts.length > 0) {
|
|
107
|
+
conflicts.forEach(c => {
|
|
108
|
+
console.log(chalk.red(` - ${c.file}: ${c.type}`));
|
|
109
|
+
});
|
|
110
|
+
} else {
|
|
111
|
+
console.log(chalk.yellow(' Content conflicts detected in merge'));
|
|
112
|
+
console.log(chalk.gray(' Review changes carefully before merging'));
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
console.log(chalk.yellow('\nRecommendation: Review conflicts before merging'));
|
|
116
|
+
console.log(chalk.gray(' To proceed: git merge ' + branch));
|
|
117
|
+
|
|
118
|
+
} else {
|
|
119
|
+
console.log(chalk.green('\nNo conflicts detected!'));
|
|
120
|
+
console.log(chalk.green('This merge should be clean.'));
|
|
121
|
+
console.log(chalk.gray('\n To merge: bit merge ' + branch));
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
console.log('');
|
|
125
|
+
|
|
126
|
+
return { hasConflicts, affectedFiles };
|
|
127
|
+
|
|
128
|
+
} catch (error) {
|
|
129
|
+
spinner.fail('Merge preview failed');
|
|
130
|
+
console.error(chalk.red(error.message));
|
|
131
|
+
return { hasConflicts: true, affectedFiles: [] };
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Perform actual merge
|
|
136
|
+
function performMerge(branch) {
|
|
137
|
+
const spinner = ora(`Merging ${branch}...`).start();
|
|
138
|
+
|
|
139
|
+
try {
|
|
140
|
+
execSync(`git merge ${branch}`, { stdio: 'pipe' });
|
|
141
|
+
spinner.succeed(chalk.green(`Successfully merged ${branch}`));
|
|
142
|
+
} catch (error) {
|
|
143
|
+
spinner.fail('Merge failed');
|
|
144
|
+
|
|
145
|
+
// Check if it's a conflict
|
|
146
|
+
const status = execSync('git status', { encoding: 'utf-8' });
|
|
147
|
+
|
|
148
|
+
if (status.includes('Unmerged')) {
|
|
149
|
+
console.log(chalk.yellow('\nMerge conflicts detected. Resolve them manually:'));
|
|
150
|
+
console.log(chalk.gray(' 1. Edit conflicting files'));
|
|
151
|
+
console.log(chalk.gray(' 2. git add <resolved-files>'));
|
|
152
|
+
console.log(chalk.gray(' 3. git commit'));
|
|
153
|
+
} else {
|
|
154
|
+
console.error(chalk.red(error.message));
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export function mergePreview(branch, options) {
|
|
160
|
+
// Check if branch exists
|
|
161
|
+
try {
|
|
162
|
+
execSync(`git rev-parse --verify ${branch}`, { stdio: 'pipe' });
|
|
163
|
+
} catch {
|
|
164
|
+
// Try with ghost prefix
|
|
165
|
+
try {
|
|
166
|
+
execSync(`git rev-parse --verify ghost/${branch}`, { stdio: 'pipe' });
|
|
167
|
+
branch = `ghost/${branch}`;
|
|
168
|
+
} catch {
|
|
169
|
+
console.error(chalk.red(`Branch not found: ${branch}`));
|
|
170
|
+
console.log(chalk.gray('\nAvailable branches:'));
|
|
171
|
+
execSync('git branch', { stdio: 'inherit' });
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (options.preview) {
|
|
177
|
+
// Just preview, don't merge
|
|
178
|
+
virtualMerge(branch);
|
|
179
|
+
} else {
|
|
180
|
+
// First show preview, then merge
|
|
181
|
+
const { hasConflicts } = virtualMerge(branch);
|
|
182
|
+
|
|
183
|
+
if (!hasConflicts) {
|
|
184
|
+
console.log(chalk.gray('Proceeding with merge...\n'));
|
|
185
|
+
performMerge(branch);
|
|
186
|
+
} else {
|
|
187
|
+
console.log(chalk.yellow('Use --preview to see details without merging'));
|
|
188
|
+
console.log(chalk.gray('Or use: git merge ' + branch + ' to proceed anyway'));
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Command } from 'commander';
|
|
4
|
+
import { execSync } from 'child_process';
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
|
|
7
|
+
import { smartInit } from './commands/init.js';
|
|
8
|
+
import { aiCommit } from './commands/commit.js';
|
|
9
|
+
import { ghostBranch, listBranches, checkoutGhost } from './commands/branch.js';
|
|
10
|
+
import { mergePreview } from './commands/merge.js';
|
|
11
|
+
import { checkHotZones } from './commands/hotzone.js';
|
|
12
|
+
import { analyzeSymbols } from './commands/analyze.js';
|
|
13
|
+
|
|
14
|
+
const program = new Command();
|
|
15
|
+
|
|
16
|
+
program
|
|
17
|
+
.name('bit')
|
|
18
|
+
.description(chalk.cyan('Bit — Git with a brain'))
|
|
19
|
+
.version('1.0.0');
|
|
20
|
+
|
|
21
|
+
// ============ SMART INIT ============
|
|
22
|
+
program
|
|
23
|
+
.command('init')
|
|
24
|
+
.description('Initialize repo with smart .gitignore and .bit directory')
|
|
25
|
+
.action(smartInit);
|
|
26
|
+
|
|
27
|
+
// ============ AI COMMIT ============
|
|
28
|
+
program
|
|
29
|
+
.command('commit')
|
|
30
|
+
.description('AI-powered commit message generation')
|
|
31
|
+
.option('-m, --message <msg>', 'Manual commit message (skip AI)')
|
|
32
|
+
.action(aiCommit);
|
|
33
|
+
|
|
34
|
+
// ============ GHOST BRANCHES ============
|
|
35
|
+
program
|
|
36
|
+
.command('branch')
|
|
37
|
+
.description('List all branches including ghost branches')
|
|
38
|
+
.option('--ghost <name>', 'Create a ghost branch')
|
|
39
|
+
.option('--list-ghost', 'List only ghost branches')
|
|
40
|
+
.action(ghostBranch);
|
|
41
|
+
|
|
42
|
+
program
|
|
43
|
+
.command('checkout')
|
|
44
|
+
.description('Checkout a branch')
|
|
45
|
+
.argument('<branch>', 'Branch name to checkout')
|
|
46
|
+
.option('--ghost', 'Checkout a ghost branch')
|
|
47
|
+
.action(checkoutGhost);
|
|
48
|
+
|
|
49
|
+
// ============ MERGE PREVIEW ============
|
|
50
|
+
program
|
|
51
|
+
.command('merge')
|
|
52
|
+
.description('Merge branches with optional preview')
|
|
53
|
+
.argument('<branch>', 'Branch to merge')
|
|
54
|
+
.option('--preview', 'Preview merge conflicts without merging')
|
|
55
|
+
.action(mergePreview);
|
|
56
|
+
|
|
57
|
+
// ============ HOT ZONE DETECTION ============
|
|
58
|
+
program
|
|
59
|
+
.command('hotzone')
|
|
60
|
+
.description('Detect overlapping work with other developers')
|
|
61
|
+
.action(checkHotZones);
|
|
62
|
+
|
|
63
|
+
// ============ SYMBOL ANALYSIS ============
|
|
64
|
+
program
|
|
65
|
+
.command('analyze')
|
|
66
|
+
.description('Analyze code changes at function/symbol level')
|
|
67
|
+
.action(analyzeSymbols);
|
|
68
|
+
|
|
69
|
+
// ============ STATUS WITH INTELLIGENCE ============
|
|
70
|
+
program
|
|
71
|
+
.command('status')
|
|
72
|
+
.description('Enhanced git status with Bit intelligence')
|
|
73
|
+
.action(() => {
|
|
74
|
+
try {
|
|
75
|
+
console.log(chalk.cyan('\n--- Bit Status ---\n'));
|
|
76
|
+
execSync('git status', { stdio: 'inherit' });
|
|
77
|
+
|
|
78
|
+
// Add Bit intelligence
|
|
79
|
+
checkHotZones(true); // silent mode
|
|
80
|
+
} catch (error) {
|
|
81
|
+
console.error(chalk.red('Not a git repository'));
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// ============ GIT PASSTHROUGH ============
|
|
86
|
+
// Any unknown command goes directly to git
|
|
87
|
+
program
|
|
88
|
+
.arguments('<command> [args...]')
|
|
89
|
+
.action((command, args) => {
|
|
90
|
+
try {
|
|
91
|
+
const gitCmd = `git ${command} ${args.join(' ')}`;
|
|
92
|
+
execSync(gitCmd, { stdio: 'inherit' });
|
|
93
|
+
} catch (error) {
|
|
94
|
+
// Git will print its own error message
|
|
95
|
+
process.exit(1);
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// Handle no arguments
|
|
100
|
+
if (process.argv.length === 2) {
|
|
101
|
+
program.outputHelp();
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
program.parse();
|