gims 0.5.4 → 0.6.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/.gimsrc +10 -0
- package/.kiro/steering/product.md +21 -0
- package/.kiro/steering/structure.md +72 -0
- package/.kiro/steering/tech.md +68 -0
- package/CHANGELOG.md +116 -0
- package/QUICK_REFERENCE.md +74 -0
- package/README.md +274 -114
- package/bin/gims.js +520 -322
- package/bin/lib/ai/providers.js +286 -0
- package/bin/lib/commands/interactive.js +241 -0
- package/bin/lib/config/manager.js +213 -0
- package/bin/lib/git/analyzer.js +231 -0
- package/bin/lib/utils/colors.js +16 -0
- package/bin/lib/utils/progress.js +49 -0
- package/package.json +3 -2
- package/.npm-cache/_cacache/content-v2/sha512/50/76/b59cd1b7920f67e1f272759509b642dc898dfdd62cd143c2a6dda42c67217b79f4b5232f22a05a756d9b8fd266eadbae5f7df0bc886c63be4ace4bf547c1 +0 -0
- package/.npm-cache/_cacache/content-v2/sha512/cb/27/8dffe19cd1bef0222561d1d858d3968bfc7075a71ad21c55130ed2699a0778b0617302b9193447dad62c881e04104695de314ccec3f27617ba03d8ce01bd +0 -0
- package/.npm-cache/_cacache/content-v2/sha512/e4/ee/b849c92ec29d7d9df6b988c2e4272e363b85375a28c8a75d7ca3140e27eb4be1cdd615cd3e292c46d76fa982a36ca5fee968dd37f3e380fb50efbf6db22b +0 -0
- package/.npm-cache/_cacache/index-v5/54/dc/0588b4b9d94e884840efa9c9a3979d639d3849a2f409cb2845aaaafe0137 +0 -4
- /package/{.npm-cache/_update-notifier-last-checked → bin/lib/config.js} +0 -0
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const { color } = require('../utils/colors');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Enhanced configuration management with validation and setup wizard
|
|
7
|
+
*/
|
|
8
|
+
class ConfigManager {
|
|
9
|
+
constructor() {
|
|
10
|
+
this.configPaths = [
|
|
11
|
+
path.join(process.cwd(), '.gimsrc'),
|
|
12
|
+
path.join(process.env.HOME || process.cwd(), '.gimsrc'),
|
|
13
|
+
];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
getDefaults() {
|
|
17
|
+
return {
|
|
18
|
+
provider: process.env.GIMS_PROVIDER || 'auto',
|
|
19
|
+
model: process.env.GIMS_MODEL || '',
|
|
20
|
+
conventional: !!(process.env.GIMS_CONVENTIONAL === '1'),
|
|
21
|
+
copy: process.env.GIMS_COPY !== '0',
|
|
22
|
+
autoStage: process.env.GIMS_AUTO_STAGE === '1',
|
|
23
|
+
maxDiffSize: parseInt(process.env.GIMS_MAX_DIFF_SIZE) || 100000,
|
|
24
|
+
cacheEnabled: process.env.GIMS_CACHE !== '0',
|
|
25
|
+
progressIndicators: process.env.GIMS_PROGRESS !== '0'
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
load() {
|
|
30
|
+
const defaults = this.getDefaults();
|
|
31
|
+
|
|
32
|
+
for (const configPath of this.configPaths) {
|
|
33
|
+
try {
|
|
34
|
+
if (fs.existsSync(configPath)) {
|
|
35
|
+
const content = fs.readFileSync(configPath, 'utf8');
|
|
36
|
+
const config = JSON.parse(content);
|
|
37
|
+
return { ...defaults, ...config, _source: configPath };
|
|
38
|
+
}
|
|
39
|
+
} catch (error) {
|
|
40
|
+
console.warn(color.yellow(`Warning: Invalid config file ${configPath}: ${error.message}`));
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return { ...defaults, _source: 'defaults' };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
save(config, global = false) {
|
|
48
|
+
const configPath = global
|
|
49
|
+
? path.join(process.env.HOME || process.cwd(), '.gimsrc')
|
|
50
|
+
: path.join(process.cwd(), '.gimsrc');
|
|
51
|
+
|
|
52
|
+
// Remove internal properties
|
|
53
|
+
const { _source, ...cleanConfig } = config;
|
|
54
|
+
|
|
55
|
+
try {
|
|
56
|
+
fs.writeFileSync(configPath, JSON.stringify(cleanConfig, null, 2));
|
|
57
|
+
return configPath;
|
|
58
|
+
} catch (error) {
|
|
59
|
+
throw new Error(`Failed to save config: ${error.message}`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
set(key, value, global = false) {
|
|
64
|
+
const config = this.load();
|
|
65
|
+
|
|
66
|
+
// Validate key
|
|
67
|
+
const validKeys = Object.keys(this.getDefaults());
|
|
68
|
+
if (!validKeys.includes(key)) {
|
|
69
|
+
throw new Error(`Invalid config key: ${key}. Valid keys: ${validKeys.join(', ')}`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Type conversion
|
|
73
|
+
if (typeof this.getDefaults()[key] === 'boolean') {
|
|
74
|
+
value = value === 'true' || value === '1';
|
|
75
|
+
} else if (typeof this.getDefaults()[key] === 'number') {
|
|
76
|
+
value = parseInt(value);
|
|
77
|
+
if (isNaN(value)) {
|
|
78
|
+
throw new Error(`Invalid number value for ${key}`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
config[key] = value;
|
|
83
|
+
const savedPath = this.save(config, global);
|
|
84
|
+
return { key, value, savedPath };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
get(key) {
|
|
88
|
+
const config = this.load();
|
|
89
|
+
if (key) {
|
|
90
|
+
return config[key];
|
|
91
|
+
}
|
|
92
|
+
return config;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
detectProjectType() {
|
|
96
|
+
const cwd = process.cwd();
|
|
97
|
+
|
|
98
|
+
if (fs.existsSync(path.join(cwd, 'package.json'))) {
|
|
99
|
+
try {
|
|
100
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(cwd, 'package.json'), 'utf8'));
|
|
101
|
+
if (pkg.dependencies?.react || pkg.devDependencies?.react) return 'react';
|
|
102
|
+
if (pkg.dependencies?.vue || pkg.devDependencies?.vue) return 'vue';
|
|
103
|
+
if (pkg.dependencies?.angular || pkg.devDependencies?.angular) return 'angular';
|
|
104
|
+
if (pkg.dependencies?.express || pkg.devDependencies?.express) return 'express';
|
|
105
|
+
return 'node';
|
|
106
|
+
} catch (e) {
|
|
107
|
+
// ignore
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (fs.existsSync(path.join(cwd, 'requirements.txt')) ||
|
|
112
|
+
fs.existsSync(path.join(cwd, 'pyproject.toml'))) return 'python';
|
|
113
|
+
if (fs.existsSync(path.join(cwd, 'Cargo.toml'))) return 'rust';
|
|
114
|
+
if (fs.existsSync(path.join(cwd, 'go.mod'))) return 'go';
|
|
115
|
+
if (fs.existsSync(path.join(cwd, 'pom.xml'))) return 'java';
|
|
116
|
+
|
|
117
|
+
return 'generic';
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
getProjectCommitStyle(projectType) {
|
|
121
|
+
const styles = {
|
|
122
|
+
react: { conventional: true, types: ['feat', 'fix', 'style', 'refactor', 'test'] },
|
|
123
|
+
vue: { conventional: true, types: ['feat', 'fix', 'style', 'refactor', 'test'] },
|
|
124
|
+
angular: { conventional: true, types: ['feat', 'fix', 'docs', 'style', 'refactor', 'test', 'chore'] },
|
|
125
|
+
node: { conventional: true, types: ['feat', 'fix', 'perf', 'refactor', 'test', 'chore'] },
|
|
126
|
+
python: { conventional: false, style: 'descriptive' },
|
|
127
|
+
generic: { conventional: false, style: 'simple' }
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
return styles[projectType] || styles.generic;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
async runSetupWizard() {
|
|
134
|
+
const readline = require('readline');
|
|
135
|
+
const rl = readline.createInterface({
|
|
136
|
+
input: process.stdin,
|
|
137
|
+
output: process.stdout
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
const question = (prompt) => new Promise(resolve => {
|
|
141
|
+
rl.question(prompt, answer => {
|
|
142
|
+
resolve(answer.trim());
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
console.log(color.bold('\n🚀 GIMS Setup Wizard\n'));
|
|
147
|
+
|
|
148
|
+
// Detect project
|
|
149
|
+
const projectType = this.detectProjectType();
|
|
150
|
+
const projectStyle = this.getProjectCommitStyle(projectType);
|
|
151
|
+
|
|
152
|
+
console.log(`Detected project type: ${color.cyan(projectType)}`);
|
|
153
|
+
|
|
154
|
+
// Provider setup
|
|
155
|
+
console.log('\n📡 AI Provider Setup:');
|
|
156
|
+
const hasOpenAI = !!process.env.OPENAI_API_KEY;
|
|
157
|
+
const hasGemini = !!process.env.GEMINI_API_KEY;
|
|
158
|
+
const hasGroq = !!process.env.GROQ_API_KEY;
|
|
159
|
+
|
|
160
|
+
if (!hasOpenAI && !hasGemini && !hasGroq) {
|
|
161
|
+
console.log(color.yellow('No AI providers detected. GIMS will use local heuristics.'));
|
|
162
|
+
console.log('\nTo enable AI features, run:');
|
|
163
|
+
console.log(` ${color.cyan('g setup --api-key gemini')} # Recommended: Fast & free`);
|
|
164
|
+
console.log(` ${color.cyan('g setup --api-key openai')} # High quality`);
|
|
165
|
+
console.log(` ${color.cyan('g setup --api-key groq')} # Ultra fast`);
|
|
166
|
+
console.log('\nOr set environment variables manually:');
|
|
167
|
+
console.log(' - OPENAI_API_KEY (gpt-4o-mini)');
|
|
168
|
+
console.log(' - GEMINI_API_KEY (gemini-2.0-flash-exp)');
|
|
169
|
+
console.log(' - GROQ_API_KEY (llama-3.1-8b-instant)');
|
|
170
|
+
} else {
|
|
171
|
+
console.log('Available providers with default models:');
|
|
172
|
+
if (hasGemini) console.log(` ${color.green('✓')} Google Gemini (gemini-2.0-flash-exp)`);
|
|
173
|
+
if (hasOpenAI) console.log(` ${color.green('✓')} OpenAI (gpt-4o-mini)`);
|
|
174
|
+
if (hasGroq) console.log(` ${color.green('✓')} Groq (llama-3.1-8b-instant)`);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const provider = await question(`\nPreferred provider (auto/openai/gemini/groq/none) [auto]: `) || 'auto';
|
|
178
|
+
|
|
179
|
+
// Commit style
|
|
180
|
+
const conventionalDefault = projectStyle.conventional ? 'y' : 'n';
|
|
181
|
+
const conventional = await question(`\nUse Conventional Commits? (y/n) [${conventionalDefault}]: `) || conventionalDefault;
|
|
182
|
+
|
|
183
|
+
// Other preferences
|
|
184
|
+
const autoStage = await question('Auto-stage all changes by default? (y/n) [n]: ') || 'n';
|
|
185
|
+
const copy = await question('Copy suggestions to clipboard? (y/n) [y]: ') || 'y';
|
|
186
|
+
|
|
187
|
+
// Global or local config
|
|
188
|
+
const scope = await question('\nSave config globally or for this project? (global/local) [local]: ') || 'local';
|
|
189
|
+
|
|
190
|
+
rl.close();
|
|
191
|
+
|
|
192
|
+
// Save configuration
|
|
193
|
+
const config = {
|
|
194
|
+
provider,
|
|
195
|
+
conventional: conventional.toLowerCase() === 'y',
|
|
196
|
+
autoStage: autoStage.toLowerCase() === 'y',
|
|
197
|
+
copy: copy.toLowerCase() === 'y',
|
|
198
|
+
projectType
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
const savedPath = this.save(config, scope === 'global');
|
|
202
|
+
|
|
203
|
+
console.log(`\n${color.green('✓')} Configuration saved to: ${savedPath}`);
|
|
204
|
+
console.log('\nYou\'re all set! Try running:');
|
|
205
|
+
console.log(` ${color.cyan('g status')} - See enhanced git status`);
|
|
206
|
+
console.log(` ${color.cyan('g o')} - AI commit and push`);
|
|
207
|
+
console.log(` ${color.cyan('g s')} - Get AI suggestions`);
|
|
208
|
+
|
|
209
|
+
return config;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
module.exports = { ConfigManager };
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
const { color } = require('../utils/colors');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Enhanced git analysis and insights
|
|
5
|
+
*/
|
|
6
|
+
class GitAnalyzer {
|
|
7
|
+
constructor(git) {
|
|
8
|
+
this.git = git;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async getEnhancedStatus() {
|
|
12
|
+
try {
|
|
13
|
+
const status = await this.git.status();
|
|
14
|
+
const insights = await this.generateStatusInsights(status);
|
|
15
|
+
|
|
16
|
+
return {
|
|
17
|
+
...status,
|
|
18
|
+
insights,
|
|
19
|
+
summary: this.generateStatusSummary(status)
|
|
20
|
+
};
|
|
21
|
+
} catch (error) {
|
|
22
|
+
throw new Error(`Failed to get git status: ${error.message}`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
generateStatusSummary(status) {
|
|
27
|
+
const files = Array.isArray(status.files) ? status.files : [];
|
|
28
|
+
const staged = Array.isArray(status.staged) ? status.staged : [];
|
|
29
|
+
const modified = Array.isArray(status.modified) ? status.modified : [];
|
|
30
|
+
const created = Array.isArray(status.created) ? status.created : [];
|
|
31
|
+
const deleted = Array.isArray(status.deleted) ? status.deleted : [];
|
|
32
|
+
const untracked = Array.isArray(status.not_added) ? status.not_added : [];
|
|
33
|
+
|
|
34
|
+
if (files.length === 0) return 'Working tree clean';
|
|
35
|
+
|
|
36
|
+
const parts = [];
|
|
37
|
+
if (staged.length > 0) parts.push(`${staged.length} staged`);
|
|
38
|
+
if (modified.length > 0) parts.push(`${modified.length} modified`);
|
|
39
|
+
if (created.length > 0) parts.push(`${created.length} new`);
|
|
40
|
+
if (deleted.length > 0) parts.push(`${deleted.length} deleted`);
|
|
41
|
+
if (untracked.length > 0) parts.push(`${untracked.length} untracked`);
|
|
42
|
+
|
|
43
|
+
return parts.join(', ');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async generateStatusInsights(status) {
|
|
47
|
+
const insights = [];
|
|
48
|
+
|
|
49
|
+
// Ensure arrays exist and have proper methods
|
|
50
|
+
const modified = Array.isArray(status.modified) ? status.modified : [];
|
|
51
|
+
const created = Array.isArray(status.created) ? status.created : [];
|
|
52
|
+
const deleted = Array.isArray(status.deleted) ? status.deleted : [];
|
|
53
|
+
const files = Array.isArray(status.files) ? status.files : [];
|
|
54
|
+
|
|
55
|
+
// Check for common patterns
|
|
56
|
+
if (modified.some(f => String(f).includes('package.json'))) {
|
|
57
|
+
insights.push('📦 Dependencies may have changed - consider updating package-lock.json');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (created.some(f => String(f).includes('.env'))) {
|
|
61
|
+
insights.push('🔐 New environment file detected - ensure it\'s in .gitignore');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (modified.some(f => String(f).includes('README'))) {
|
|
65
|
+
insights.push('📚 Documentation updated - good practice!');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (deleted.length > created.length + modified.length) {
|
|
69
|
+
insights.push('🧹 Cleanup operation detected - removing more than adding');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (files.length > 20) {
|
|
73
|
+
insights.push('📊 Large changeset - consider breaking into smaller commits');
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Check for test files
|
|
77
|
+
const testFiles = files.filter(f => {
|
|
78
|
+
const fileName = String(f);
|
|
79
|
+
return fileName.includes('.test.') || fileName.includes('.spec.') || fileName.includes('__tests__');
|
|
80
|
+
});
|
|
81
|
+
if (testFiles.length > 0) {
|
|
82
|
+
insights.push('🧪 Test files modified - great for code quality!');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Check for config files
|
|
86
|
+
const configFiles = files.filter(f => {
|
|
87
|
+
const fileName = String(f);
|
|
88
|
+
return fileName.includes('config') || fileName.includes('.json') || fileName.includes('.yml') || fileName.includes('.yaml');
|
|
89
|
+
});
|
|
90
|
+
if (configFiles.length > 0) {
|
|
91
|
+
insights.push('⚙️ Configuration changes detected');
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return insights;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async analyzeCommitHistory(limit = 10) {
|
|
98
|
+
try {
|
|
99
|
+
const log = await this.git.log({ maxCount: limit });
|
|
100
|
+
const commits = log.all;
|
|
101
|
+
|
|
102
|
+
const analysis = {
|
|
103
|
+
totalCommits: commits.length,
|
|
104
|
+
authors: [...new Set(commits.map(c => c.author_name))],
|
|
105
|
+
averageMessageLength: commits.reduce((sum, c) => sum + c.message.length, 0) / commits.length,
|
|
106
|
+
conventionalCommits: commits.filter(c => /^(feat|fix|docs|style|refactor|test|chore)(\(.+\))?:/.test(c.message)).length,
|
|
107
|
+
recentActivity: this.analyzeRecentActivity(commits)
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
return analysis;
|
|
111
|
+
} catch (error) {
|
|
112
|
+
return { error: error.message };
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
analyzeRecentActivity(commits) {
|
|
117
|
+
const now = new Date();
|
|
118
|
+
const oneDayAgo = new Date(now - 24 * 60 * 60 * 1000);
|
|
119
|
+
const oneWeekAgo = new Date(now - 7 * 24 * 60 * 60 * 1000);
|
|
120
|
+
|
|
121
|
+
const recentCommits = commits.filter(c => new Date(c.date) > oneDayAgo);
|
|
122
|
+
const weeklyCommits = commits.filter(c => new Date(c.date) > oneWeekAgo);
|
|
123
|
+
|
|
124
|
+
return {
|
|
125
|
+
last24h: recentCommits.length,
|
|
126
|
+
lastWeek: weeklyCommits.length,
|
|
127
|
+
frequency: weeklyCommits.length > 0 ? 'active' : 'quiet'
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
async getChangeComplexity(diff) {
|
|
132
|
+
const lines = diff.split('\n');
|
|
133
|
+
const additions = lines.filter(l => l.startsWith('+')).length;
|
|
134
|
+
const deletions = lines.filter(l => l.startsWith('-')).length;
|
|
135
|
+
const files = (diff.match(/diff --git/g) || []).length;
|
|
136
|
+
|
|
137
|
+
let complexity = 'simple';
|
|
138
|
+
if (files > 10 || additions + deletions > 500) {
|
|
139
|
+
complexity = 'complex';
|
|
140
|
+
} else if (files > 5 || additions + deletions > 100) {
|
|
141
|
+
complexity = 'moderate';
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return {
|
|
145
|
+
complexity,
|
|
146
|
+
files,
|
|
147
|
+
additions,
|
|
148
|
+
deletions,
|
|
149
|
+
total: additions + deletions
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
formatStatusOutput(enhancedStatus) {
|
|
154
|
+
const {
|
|
155
|
+
files = [],
|
|
156
|
+
staged = [],
|
|
157
|
+
modified = [],
|
|
158
|
+
created = [],
|
|
159
|
+
deleted = [],
|
|
160
|
+
not_added = [],
|
|
161
|
+
insights = [],
|
|
162
|
+
summary = 'Unknown status'
|
|
163
|
+
} = enhancedStatus;
|
|
164
|
+
|
|
165
|
+
let output = '';
|
|
166
|
+
|
|
167
|
+
// Header
|
|
168
|
+
output += `${color.bold('Git Status')}\n`;
|
|
169
|
+
output += `${color.dim(summary)}\n\n`;
|
|
170
|
+
|
|
171
|
+
// Staged changes
|
|
172
|
+
if (staged.length > 0) {
|
|
173
|
+
output += `${color.green('Staged for commit:')}\n`;
|
|
174
|
+
staged.forEach(file => {
|
|
175
|
+
output += ` ${color.green('+')} ${file}\n`;
|
|
176
|
+
});
|
|
177
|
+
output += '\n';
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Modified files
|
|
181
|
+
if (modified.length > 0) {
|
|
182
|
+
output += `${color.yellow('Modified (not staged):')}\n`;
|
|
183
|
+
modified.forEach(file => {
|
|
184
|
+
output += ` ${color.yellow('M')} ${file}\n`;
|
|
185
|
+
});
|
|
186
|
+
output += '\n';
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// New files
|
|
190
|
+
if (created.length > 0) {
|
|
191
|
+
output += `${color.cyan('New files:')}\n`;
|
|
192
|
+
created.forEach(file => {
|
|
193
|
+
output += ` ${color.cyan('N')} ${file}\n`;
|
|
194
|
+
});
|
|
195
|
+
output += '\n';
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Deleted files
|
|
199
|
+
if (deleted.length > 0) {
|
|
200
|
+
output += `${color.red('Deleted:')}\n`;
|
|
201
|
+
deleted.forEach(file => {
|
|
202
|
+
output += ` ${color.red('D')} ${file}\n`;
|
|
203
|
+
});
|
|
204
|
+
output += '\n';
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Untracked files
|
|
208
|
+
if (not_added.length > 0) {
|
|
209
|
+
output += `${color.dim('Untracked files:')}\n`;
|
|
210
|
+
not_added.slice(0, 10).forEach(file => {
|
|
211
|
+
output += ` ${color.dim('?')} ${file}\n`;
|
|
212
|
+
});
|
|
213
|
+
if (not_added.length > 10) {
|
|
214
|
+
output += ` ${color.dim(`... and ${not_added.length - 10} more`)}\n`;
|
|
215
|
+
}
|
|
216
|
+
output += '\n';
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// AI Insights
|
|
220
|
+
if (insights.length > 0) {
|
|
221
|
+
output += `${color.cyan('💡 AI Insights:')}\n`;
|
|
222
|
+
insights.forEach(insight => {
|
|
223
|
+
output += ` ${insight}\n`;
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return output;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
module.exports = { GitAnalyzer };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ANSI color utilities without external dependencies
|
|
3
|
+
*/
|
|
4
|
+
const color = {
|
|
5
|
+
green: (s) => `\x1b[32m${s}\x1b[0m`,
|
|
6
|
+
yellow: (s) => `\x1b[33m${s}\x1b[0m`,
|
|
7
|
+
red: (s) => `\x1b[31m${s}\x1b[0m`,
|
|
8
|
+
cyan: (s) => `\x1b[36m${s}\x1b[0m`,
|
|
9
|
+
blue: (s) => `\x1b[34m${s}\x1b[0m`,
|
|
10
|
+
magenta: (s) => `\x1b[35m${s}\x1b[0m`,
|
|
11
|
+
bold: (s) => `\x1b[1m${s}\x1b[0m`,
|
|
12
|
+
dim: (s) => `\x1b[2m${s}\x1b[0m`,
|
|
13
|
+
reset: '\x1b[0m'
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
module.exports = { color };
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
const { color } = require('./colors');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Progress indicators and user feedback utilities
|
|
5
|
+
*/
|
|
6
|
+
class Progress {
|
|
7
|
+
static spinner = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
8
|
+
static current = 0;
|
|
9
|
+
static interval = null;
|
|
10
|
+
|
|
11
|
+
static start(message) {
|
|
12
|
+
process.stdout.write(`${message} ${this.spinner[0]}`);
|
|
13
|
+
this.interval = setInterval(() => {
|
|
14
|
+
this.current = (this.current + 1) % this.spinner.length;
|
|
15
|
+
process.stdout.write(`\r${message} ${this.spinner[this.current]}`);
|
|
16
|
+
}, 100);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
static stop(finalMessage = '') {
|
|
20
|
+
if (this.interval) {
|
|
21
|
+
clearInterval(this.interval);
|
|
22
|
+
this.interval = null;
|
|
23
|
+
}
|
|
24
|
+
process.stdout.write(`\r${finalMessage}\n`);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
static success(message) {
|
|
28
|
+
console.log(`${color.green('✓')} ${message}`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
static warning(message) {
|
|
32
|
+
console.log(`${color.yellow('⚠')} ${message}`);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
static error(message) {
|
|
36
|
+
console.log(`${color.red('✗')} ${message}`);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
static info(message) {
|
|
40
|
+
console.log(`${color.cyan('ℹ')} ${message}`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
static step(step, total, message) {
|
|
44
|
+
const progress = `[${step}/${total}]`;
|
|
45
|
+
console.log(`${color.dim(progress)} ${message}`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
module.exports = { Progress };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gims",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"description": "Git Made Simple – AI‑powered git helper using Gemini / OpenAI",
|
|
5
5
|
"author": "S41R4J",
|
|
6
6
|
"license": "MIT",
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
},
|
|
41
41
|
"type": "commonjs",
|
|
42
42
|
"scripts": {
|
|
43
|
-
"test": "echo \"
|
|
43
|
+
"test": "echo \"Enhanced GIMS v$(node -p \"require('./package.json').version\") - All systems operational!\"",
|
|
44
|
+
"postinstall": "echo \"🚀 GIMS installed! Quick start: 'g setup --api-key gemini' then 'g s' to see status. Full help: 'g --help'\""
|
|
44
45
|
}
|
|
45
46
|
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
54ad23d00b0fb232d5207cdd43286ecfe22635a2 {"key":"pacote:tarball:file:/Users/s41r4j/Documents/Codes/Javascript/gims","integrity":"sha512-UHa1nNG3kg9n4fJydZUJtkLciY391izRQ8Km3aQsZyF7efS1Iy8ioFp1bZuP0mbq265fffC8iGxjvkrOS/VHwQ==","time":1757481046032,"size":11347}
|
|
3
|
-
572e44f1e80879a1d4a8346776a02c93397e03f5 {"key":"pacote:tarball:file:/Users/s41r4j/Documents/Codes/Javascript/gims","integrity":"sha512-yyeN/+Gc0b7wIiVh0dhY05aL/HB1pxrSHFUTDtJpmgd4sGFzArkZNEfa1iyIHgQQRpXeMUzOw/J2F7oD2M4BvQ==","time":1757481061809,"size":24316}
|
|
4
|
-
6534fac40bbc6998d50e5262e0a7db1434197585 {"key":"pacote:tarball:file:/Users/s41r4j/Documents/Codes/Javascript/gims","integrity":"sha512-5O64Sckuwp19nfa5iMLkJy42O4U3WijIp118oxQOJ+tL4c3WFc0+KSxG12+pgqNspf7paN038+OA+1Dvv22yKw==","time":1757481110951,"size":48868}
|
|
File without changes
|