aidx 1.0.3 → 1.0.4

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.
Files changed (2) hide show
  1. package/dist/index.js +188 -168
  2. package/package.json +4 -12
package/dist/index.js CHANGED
@@ -1,25 +1,97 @@
1
1
  #!/usr/bin/env node
2
- import { Command } from 'commander';
3
2
  import { checkbox, confirm } from '@inquirer/prompts';
4
- import glob from 'fast-glob';
5
- import fs from 'fs/promises';
6
- import { statSync } from 'fs';
3
+ import fs from 'fs';
4
+ import fsPromises from 'fs/promises';
7
5
  import path from 'path';
8
6
  import clipboardy from 'clipboardy';
9
- import chalk from 'chalk';
10
7
  import * as Diff from 'diff';
11
8
  // --- CONFIGURATION ---
12
9
  const METADATA = {
13
10
  name: "aidx",
14
11
  description: "A CLI bridge between local code and LLMs.",
15
12
  author: "rx76d",
16
- version: "1.0.3",
13
+ version: "1.0.4",
17
14
  license: "MIT",
18
15
  github: "https://github.com/rx76d/aidx"
19
16
  };
20
17
  const CONFIG_FILE = '.aidxrc.json';
21
18
  const MAX_FILE_SIZE = 1.5 * 1024 * 1024; // 1.5MB Limit
22
19
  const SECRET_REGEX = /(?:sk-[a-zA-Z0-9]{32,})|(?:AKIA[0-9A-Z]{16})|(?:[a-zA-Z0-9+/]{40,}=)/;
20
+ // --- UTILS: NATIVE COLORS ---
21
+ const colors = {
22
+ reset: "\x1b[0m",
23
+ red: (t) => `\x1b[31m${t}\x1b[0m`,
24
+ green: (t) => `\x1b[32m${t}\x1b[0m`,
25
+ yellow: (t) => `\x1b[33m${t}\x1b[0m`,
26
+ blue: (t) => `\x1b[34m${t}\x1b[0m`,
27
+ cyan: (t) => `\x1b[36m${t}\x1b[0m`,
28
+ dim: (t) => `\x1b[2m${t}\x1b[0m`,
29
+ bold: (t) => `\x1b[1m${t}\x1b[0m`,
30
+ bgBlue: (t) => `\x1b[44m${t}\x1b[0m`,
31
+ bgRed: (t) => `\x1b[41m${t}\x1b[0m`,
32
+ bgGreen: (t) => `\x1b[42m\x1b[30m${t}\x1b[0m`
33
+ };
34
+ // --- UTILS: NATIVE FILE SCANNER ---
35
+ async function scanFiles(startDir) {
36
+ const ignoredFolders = new Set([
37
+ 'node_modules', '.git', '.vscode', '.idea', 'dist', 'build', '.next',
38
+ '__pycache__', 'venv', 'env', 'target', 'bin', 'obj', 'vendor',
39
+ 'Application Data', 'Cookies', 'Local Settings', 'Recent', 'Start Menu'
40
+ ]);
41
+ const ignoredExts = new Set([
42
+ '.lock', '.log', '.png', '.jpg', '.jpeg', '.gif', '.svg', '.ico',
43
+ '.pdf', '.zip', '.tar', '.gz', '.exe', '.dll', '.iso', '.class', '.pyc'
44
+ ]);
45
+ const results = [];
46
+ async function walk(dir) {
47
+ try {
48
+ const entries = await fsPromises.readdir(dir, { withFileTypes: true });
49
+ for (const entry of entries) {
50
+ const fullPath = path.join(dir, entry.name);
51
+ if (entry.isDirectory()) {
52
+ if (!ignoredFolders.has(entry.name))
53
+ await walk(fullPath);
54
+ }
55
+ else {
56
+ const ext = path.extname(entry.name).toLowerCase();
57
+ if (!ignoredExts.has(ext)) {
58
+ results.push(path.relative(startDir, fullPath));
59
+ }
60
+ }
61
+ }
62
+ }
63
+ catch (e) { /* Suppress EPERM */ }
64
+ }
65
+ await walk(startDir);
66
+ return results;
67
+ }
68
+ // --- UTILS: HELPERS ---
69
+ function estimateTokens(text) {
70
+ return Math.ceil(text.length / 4);
71
+ }
72
+ function isBinary(buffer) {
73
+ if (buffer.length === 0)
74
+ return false;
75
+ const len = Math.min(buffer.length, 1000);
76
+ for (let i = 0; i < len; i++)
77
+ if (buffer[i] === 0x00)
78
+ return true;
79
+ return false;
80
+ }
81
+ async function getBackupStatus() {
82
+ try {
83
+ const configPath = path.resolve(process.cwd(), CONFIG_FILE);
84
+ const data = await fsPromises.readFile(configPath, 'utf-8');
85
+ return !!JSON.parse(data).backup;
86
+ }
87
+ catch {
88
+ return false;
89
+ }
90
+ }
91
+ async function setBackupStatus(enabled) {
92
+ await fsPromises.writeFile(path.resolve(process.cwd(), CONFIG_FILE), JSON.stringify({ backup: enabled }, null, 2));
93
+ }
94
+ // --- PROTOCOLS ---
23
95
  const SYSTEM_HEADER = `
24
96
  ================================================================
25
97
  SYSTEM PROMPT: STRICT CODE MODE
@@ -44,76 +116,59 @@ console.log("Full code here...");
44
116
  </file>
45
117
  ================================================================
46
118
  `;
47
- // --- UTILS: ZERO-DEPENDENCY HELPERS ---
48
- // 1. Token Estimator (1 token ~= 4 chars in Code)
49
- function estimateTokens(text) {
50
- return Math.ceil(text.length / 4);
51
- }
52
- // 2. Binary Detector (Checks for null bytes in first 1KB)
53
- function isBinary(buffer) {
54
- // If file is empty, it's text safe
55
- if (buffer.length === 0)
56
- return false;
57
- // Check first 1000 bytes for a null byte (common in images/binaries)
58
- const len = Math.min(buffer.length, 1000);
59
- for (let i = 0; i < len; i++) {
60
- if (buffer[i] === 0x00)
61
- return true;
62
- }
63
- return false;
64
- }
65
- // --- GLOBAL HANDLERS ---
66
- process.on('SIGINT', () => {
67
- console.log(chalk.yellow('\n\nOperation cancelled by user.'));
68
- process.exit(0);
69
- });
70
- // --- HELPER: CONFIG MANAGEMENT ---
71
- async function getBackupStatus() {
72
- try {
73
- const configPath = path.resolve(process.cwd(), CONFIG_FILE);
74
- const data = await fs.readFile(configPath, 'utf-8');
75
- const config = JSON.parse(data);
76
- return !!config.backup;
77
- }
78
- catch (e) {
79
- return false;
119
+ // --- MAIN CLI LOGIC ---
120
+ async function main() {
121
+ const args = process.argv.slice(2);
122
+ const command = args[0] || 'menu';
123
+ switch (command) {
124
+ case 'copy':
125
+ await runCopy();
126
+ break;
127
+ case 'apply':
128
+ await runApply();
129
+ break;
130
+ case 'backup':
131
+ await runBackup(args[1]);
132
+ break;
133
+ case 'stl':
134
+ runSTL();
135
+ break;
136
+ case 'menu':
137
+ case '--help':
138
+ case '-h':
139
+ await showMenu();
140
+ break;
141
+ case '-v':
142
+ case '--version':
143
+ console.log(METADATA.version);
144
+ break;
145
+ default:
146
+ console.log(colors.red(`\nError: Unknown command '${command}'`));
147
+ console.log(`Run ${colors.cyan('npx aidx')} for help.\n`);
148
+ process.exit(1);
80
149
  }
81
150
  }
82
- async function setBackupStatus(enabled) {
83
- const configPath = path.resolve(process.cwd(), CONFIG_FILE);
84
- await fs.writeFile(configPath, JSON.stringify({ backup: enabled }, null, 2));
85
- }
86
- const program = new Command();
87
- program
88
- .name(METADATA.name)
89
- .description(METADATA.description)
90
- .version(METADATA.version);
91
- // --- ROOT COMMAND ---
92
- program.action(async () => {
151
+ // --- ACTIONS ---
152
+ async function showMenu() {
93
153
  const backupEnabled = await getBackupStatus();
94
- console.log('\n' + chalk.bgBlue.bold(` ${METADATA.name.toUpperCase()} `) + chalk.dim(` v${METADATA.version}`));
95
- console.log(chalk.dim('----------------------------------------'));
96
- console.log(`${chalk.bold('Description:')} ${METADATA.description}`);
97
- console.log(`${chalk.bold('Author:')} ${METADATA.author}`);
98
- console.log(`${chalk.bold('Backups:')} ${backupEnabled ? chalk.green('ENABLED') : chalk.dim('DISABLED')}`);
99
- console.log(chalk.dim('----------------------------------------'));
154
+ console.log('\n' + colors.bgBlue(` ${METADATA.name.toUpperCase()} `) + colors.dim(` v${METADATA.version}`));
155
+ console.log(colors.dim('----------------------------------------'));
156
+ console.log(`${colors.bold('Description:')} ${METADATA.description}`);
157
+ console.log(`${colors.bold('Author:')} ${METADATA.author}`);
158
+ console.log(`${colors.bold('Backups:')} ${backupEnabled ? colors.green('ENABLED') : colors.dim('DISABLED')}`);
159
+ console.log(colors.dim('----------------------------------------'));
100
160
  console.log('\nAvailable Commands:');
101
- console.log(` ${chalk.cyan('npx aidx copy')} Select files and copy context`);
102
- console.log(` ${chalk.cyan('npx aidx apply')} Apply AI changes to disk`);
103
- console.log(` ${chalk.cyan('npx aidx backup --on')} Enable auto-backups`);
104
- console.log(` ${chalk.cyan('npx aidx backup --off')} Disable auto-backups`);
105
- console.log(` ${chalk.cyan('npx aidx stl')} Show AI token limits`);
106
- console.log(`\nRun ${chalk.gray('npx aidx --help')} for details.\n`);
107
- });
108
- // --- COMMAND: STL (Safe Token Limits) ---
109
- program
110
- .command('stl')
111
- .description('Show safe token limits for AI models')
112
- .action(() => {
113
- console.log('\n' + chalk.bold('AI Model Context Limits (2025 Reference)'));
114
- console.log(chalk.dim('--------------------------------------------------'));
161
+ console.log(` ${colors.cyan('npx aidx copy')} Select files and copy context`);
162
+ console.log(` ${colors.cyan('npx aidx apply')} Apply AI changes to disk`);
163
+ console.log(` ${colors.cyan('npx aidx backup --on')} Enable auto-backups`);
164
+ console.log(` ${colors.cyan('npx aidx backup --off')} Disable auto-backups`);
165
+ console.log(` ${colors.cyan('npx aidx stl')} Show AI token limits`);
166
+ console.log(`\nRun ${colors.dim('npx aidx copy')} to start.\n`);
167
+ }
168
+ function runSTL() {
169
+ console.log('\n' + colors.bold('AI Model Context Limits (2025 Reference)'));
170
+ console.log(colors.dim('--------------------------------------------------'));
115
171
  const models = [
116
- // HUGE (≈ 1M+ tokens)
117
172
  { name: "Gemini 3 Pro", limit: "2,000,000+", type: "Huge" },
118
173
  { name: "Gemini 2.5 Pro", limit: "1,000,000+", type: "Huge" },
119
174
  { name: "Gemini 2.5 Flash", limit: "1,000,000+", type: "Huge" },
@@ -121,73 +176,48 @@ program
121
176
  { name: "Llama 4 Maverick", limit: "1,000,000+", type: "Huge" },
122
177
  { name: "Qwen 2.5 1M", limit: "1,000,000+", type: "Huge" },
123
178
  { name: "GPT-4.1", limit: "1,000,000+", type: "Huge" },
124
- // LARGE (≈ 200K–500K tokens)
125
179
  { name: "ChatGPT-5", limit: "200,000+", type: "Large" },
126
180
  { name: "Claude 4.5 Sonnet", limit: "200,000+", type: "Large" },
127
181
  { name: "Claude 4.5 Opus", limit: "200,000+", type: "Large" },
128
182
  { name: "Grok 4", limit: "256,000", type: "Large" },
129
183
  { name: "Cohere Command A", limit: "256,000", type: "Large" },
130
- // MEDIUM (≈ 100K–150K tokens)
131
184
  { name: "GPT-4o", limit: "128,000", type: "Medium" },
132
185
  { name: "Llama 4 405B", limit: "128,000", type: "Medium" },
133
186
  { name: "DeepSeek V3", limit: "128,000", type: "Medium" },
134
187
  { name: "Grok 3", limit: "128,000", type: "Medium" },
135
188
  { name: "GPT-5 Mini", limit: "128,000", type: "Medium" },
136
- // SMALL (< 50K tokens)
137
189
  { name: "ChatGPT (Free)", limit: "~8,000", type: "Small" },
138
190
  { name: "Claude Haiku", limit: "~16,000", type: "Small" },
139
191
  ];
140
- console.log(chalk.cyan('Model Name'.padEnd(20)) + chalk.yellow('Max Tokens'.padEnd(15)) + chalk.white('Category'));
141
- console.log(chalk.dim('--------------------------------------------------'));
192
+ console.log(colors.cyan('Model Name'.padEnd(20)) + colors.yellow('Max Tokens'.padEnd(15)) + colors.dim('Category'));
193
+ console.log(colors.dim('--------------------------------------------------'));
142
194
  models.forEach(m => {
143
- const color = m.type === "Huge" ? chalk.green : m.type === "Large" ? chalk.blue : chalk.gray;
144
- console.log(m.name.padEnd(20) + color(m.limit.padEnd(15)) + chalk.dim(m.type));
195
+ const color = m.type === "Huge" ? colors.green : m.type === "Large" ? colors.blue : colors.dim;
196
+ console.log(m.name.padEnd(20) + color(m.limit.padEnd(15)) + colors.dim(m.type));
145
197
  });
146
- console.log(chalk.dim('--------------------------------------------------'));
147
- console.log(chalk.dim('* 1,000 tokens ≈ 750 words of code/text.'));
148
- console.log(chalk.dim('* Estimates based on latest model specs.\n'));
149
- });
150
- // --- COMMAND: BACKUP ---
151
- program
152
- .command('backup')
153
- .description('Configure automatic backups')
154
- .option('--on', 'Enable backups')
155
- .option('--off', 'Disable backups')
156
- .action(async (options) => {
157
- if (options.on) {
198
+ console.log(colors.dim('--------------------------------------------------'));
199
+ console.log(colors.dim('* 1,000 tokens ≈ 750 words of code/text.'));
200
+ console.log(colors.dim('* Estimates based on latest model specs.\n'));
201
+ }
202
+ async function runBackup(flag) {
203
+ if (flag === '--on') {
158
204
  await setBackupStatus(true);
159
- console.log(chalk.green(`\n✔ Backups enabled. Settings saved to ${CONFIG_FILE}`));
205
+ console.log(colors.green(`\n✔ Backups enabled. Settings saved to ${CONFIG_FILE}`));
160
206
  }
161
- else if (options.off) {
207
+ else if (flag === '--off') {
162
208
  await setBackupStatus(false);
163
- console.log(chalk.yellow(`\nBackups disabled.`));
209
+ console.log(colors.yellow(`\nBackups disabled.`));
164
210
  }
165
211
  else {
166
- const status = await getBackupStatus();
167
- console.log(`\nCurrent Backup Status: ${status ? chalk.green('ENABLED') : chalk.red('DISABLED')}`);
212
+ console.log(colors.red('Error: Use --on or --off'));
168
213
  }
169
- });
170
- // --- COMMAND: COPY (FAST) ---
171
- program
172
- .command('copy')
173
- .description('Select files and copy to clipboard')
174
- .action(async () => {
175
- console.log(chalk.blue('Scanning directory...'));
176
- const files = await glob(['**/*'], {
177
- ignore: [
178
- '**/Application Data/**', '**/Cookies/**', '**/Local Settings/**', '**/Recent/**', '**/Start Menu/**',
179
- '**/node_modules/**', '**/dist/**', '**/build/**', '**/.git/**', '**/.vscode/**',
180
- '**/__pycache__/**', '**/venv/**', '**/target/**', '**/bin/**', '**/obj/**',
181
- '**/vendor/**', '**/*.lock', '**/*.log', '**/*.png', '**/*.exe', '**/*.dll', '**/*.zip', '**/*.tar', '**/*.gz'
182
- ],
183
- onlyFiles: true,
184
- dot: true,
185
- suppressErrors: true,
186
- followSymbolicLinks: false
187
- });
214
+ }
215
+ async function runCopy() {
216
+ console.log(colors.blue('Scanning directory...'));
217
+ const files = await scanFiles(process.cwd());
188
218
  if (files.length === 0)
189
- return console.log(chalk.red('Error: No files found.'));
190
- let selectedFiles = [];
219
+ return console.log(colors.red('Error: No files found.'));
220
+ let selectedFiles;
191
221
  try {
192
222
  selectedFiles = await checkbox({
193
223
  message: 'Select files to send to AI:',
@@ -196,68 +226,62 @@ program
196
226
  });
197
227
  }
198
228
  catch (e) {
199
- return console.log(chalk.yellow('\nSelection cancelled.'));
229
+ return console.log(colors.yellow('\nSelection cancelled.'));
200
230
  }
201
231
  if (selectedFiles.length === 0)
202
- return console.log(chalk.yellow('No files selected.'));
232
+ return console.log(colors.yellow('No files selected.'));
203
233
  let output = SYSTEM_HEADER + "\n";
204
234
  let skippedCount = 0;
205
- console.log(chalk.dim('Reading files...'));
235
+ console.log(colors.dim('Reading files...'));
206
236
  for (const file of selectedFiles) {
207
237
  try {
208
- const stats = statSync(file);
238
+ const stats = fs.statSync(file);
209
239
  if (stats.size > MAX_FILE_SIZE) {
210
- console.log(chalk.yellow(`⚠ Skipped large file (>1.5MB): ${file}`));
240
+ console.log(colors.yellow(`⚠ Skipped large file (>1.5MB): ${file}`));
211
241
  skippedCount++;
212
242
  continue;
213
243
  }
214
- // Optimized Read: Read buffer first to check binary, then convert to string
215
- const buffer = await fs.readFile(file);
244
+ const buffer = await fsPromises.readFile(file);
216
245
  if (isBinary(buffer)) {
217
- console.log(chalk.yellow(`⚠ Skipped binary file: ${file}`));
246
+ console.log(colors.yellow(`⚠ Skipped binary file: ${file}`));
218
247
  skippedCount++;
219
248
  continue;
220
249
  }
221
250
  const content = buffer.toString('utf-8');
222
251
  if (file.includes('.env') || SECRET_REGEX.test(content)) {
223
- console.log(chalk.red(`\n🛑 SECURITY ALERT: Secrets detected in ${file}`));
252
+ console.log(colors.red(`\n🛑 SECURITY ALERT: Secrets detected in ${file}`));
224
253
  skippedCount++;
225
254
  continue;
226
255
  }
227
256
  output += `File: ${file}\n\`\`\`\n${content}\n\`\`\`\n\n`;
228
257
  }
229
258
  catch (e) {
230
- console.log(chalk.red(`Error reading ${file}`));
259
+ console.log(colors.red(`Error reading ${file}`));
231
260
  }
232
261
  }
233
262
  output += XML_SCHEMA_INSTRUCTION;
234
263
  try {
235
- await clipboardy.write(output);
236
- // LIGHTWEIGHT TOKENIZER USAGE
264
+ await clipboardy.write(output); // Uses robust library
237
265
  const tokens = estimateTokens(output);
238
266
  const finalCount = selectedFiles.length - skippedCount;
239
- const tokenColor = tokens > 100000 ? chalk.red : tokens > 30000 ? chalk.yellow : chalk.green;
240
- console.log(chalk.green(`\n✔ Copied ${finalCount} files to clipboard`));
267
+ const tokenColor = tokens > 100000 ? colors.red : tokens > 30000 ? colors.yellow : colors.green;
268
+ console.log(colors.green(`\n✔ Copied ${finalCount} files to clipboard`));
241
269
  console.log(`Estimated Tokens: ${tokenColor(tokens.toLocaleString())}`);
242
270
  }
243
271
  catch (e) {
244
- console.log(chalk.red('❌ Clipboard write failed (File too large for OS).'));
245
- console.log(chalk.dim('Try selecting fewer files.'));
272
+ console.log(colors.red(`❌ Clipboard write failed: ${e instanceof Error ? e.message : 'Unknown'}`));
246
273
  }
247
- });
248
- // --- COMMAND: APPLY ---
249
- program
250
- .command('apply')
251
- .description('Apply AI changes from clipboard')
252
- .action(async () => {
274
+ }
275
+ async function runApply() {
253
276
  const backupsEnabled = await getBackupStatus();
254
- console.log(chalk.dim('Reading clipboard...'));
277
+ console.log(colors.dim('Reading clipboard...'));
255
278
  let content;
256
279
  try {
257
280
  content = await clipboardy.read();
258
281
  }
259
- catch (e) {
260
- return console.log(chalk.red('Error: Could not read clipboard.'));
282
+ catch (e) { // Uses robust library
283
+ console.log(colors.red(`Error: Could not read clipboard.`));
284
+ return;
261
285
  }
262
286
  const cleanedContent = content.replace(/```xml/g, '').replace(/```/g, '');
263
287
  const fileRegex = /<file\s+path=["'](.*?)["']\s*>([\s\S]*?)<\/file>/gi;
@@ -269,21 +293,21 @@ program
269
293
  while ((match = deleteRegex.exec(cleanedContent)) !== null)
270
294
  updates.push({ type: 'delete', path: match[1] });
271
295
  if (updates.length === 0)
272
- return console.log(chalk.red('\nNo valid XML tags found.'));
273
- console.log(chalk.bold(`\nFound ${updates.length} pending change(s):\n`));
296
+ return console.log(colors.red('\nNo valid XML tags found.'));
297
+ console.log(colors.bold(`\nFound ${updates.length} pending change(s):\n`));
274
298
  for (const update of updates) {
275
299
  const targetPath = path.resolve(process.cwd(), update.path);
276
- console.log(chalk.bgBlue.white(` ${update.path} `));
300
+ console.log(colors.bgBlue(` ${update.path} `));
277
301
  if (update.type === 'delete') {
278
- console.log(chalk.bgRed.white(' [DELETE] '));
302
+ console.log(colors.bgRed(' [DELETE] '));
279
303
  }
280
304
  else {
281
305
  let originalContent = '';
282
306
  try {
283
- originalContent = await fs.readFile(targetPath, 'utf-8');
307
+ originalContent = await fsPromises.readFile(targetPath, 'utf-8');
284
308
  }
285
309
  catch (e) {
286
- console.log(chalk.bgGreen.black(' [NEW FILE] '));
310
+ console.log(colors.bgGreen(' [NEW FILE] '));
287
311
  }
288
312
  if (originalContent && update.content !== undefined) {
289
313
  const changes = Diff.diffLines(originalContent, update.content);
@@ -292,20 +316,20 @@ program
292
316
  if (count > 50)
293
317
  return;
294
318
  if (part.added) {
295
- process.stdout.write(chalk.green(part.value.replace(/^/gm, '+ ')));
319
+ process.stdout.write(colors.green(part.value.replace(/^/gm, '+ ')));
296
320
  count++;
297
321
  }
298
322
  else if (part.removed) {
299
- process.stdout.write(chalk.red(part.value.replace(/^/gm, '- ')));
323
+ process.stdout.write(colors.red(part.value.replace(/^/gm, '- ')));
300
324
  count++;
301
325
  }
302
326
  });
303
327
  if (count > 50)
304
- console.log(chalk.dim('...'));
328
+ console.log(colors.dim('...'));
305
329
  console.log('');
306
330
  }
307
331
  }
308
- console.log(chalk.dim('--------------------------------------------------\n'));
332
+ console.log(colors.dim('--------------------------------------------------\n'));
309
333
  }
310
334
  let proceed = false;
311
335
  try {
@@ -315,7 +339,7 @@ program
315
339
  return;
316
340
  }
317
341
  if (!proceed)
318
- return console.log(chalk.yellow('Aborted.'));
342
+ return console.log(colors.yellow('Aborted.'));
319
343
  console.log('');
320
344
  for (const update of updates) {
321
345
  const targetPath = path.resolve(process.cwd(), update.path);
@@ -323,37 +347,33 @@ program
323
347
  if (update.type === 'delete') {
324
348
  if (backupsEnabled) {
325
349
  try {
326
- await fs.copyFile(targetPath, `${targetPath}.bak`);
350
+ await fsPromises.copyFile(targetPath, `${targetPath}.bak`);
327
351
  }
328
352
  catch (e) { }
329
353
  }
330
354
  try {
331
- await fs.unlink(targetPath);
332
- console.log(chalk.gray(`Deleted ${update.path}`));
355
+ await fsPromises.unlink(targetPath);
356
+ console.log(colors.dim(`Deleted ${update.path}`));
333
357
  }
334
358
  catch (e) { }
335
359
  }
336
360
  else {
337
- await fs.mkdir(path.dirname(targetPath), { recursive: true });
361
+ await fsPromises.mkdir(path.dirname(targetPath), { recursive: true });
338
362
  if (backupsEnabled) {
339
363
  try {
340
- await fs.copyFile(targetPath, `${targetPath}.bak`);
341
- console.log(chalk.gray(`(Backup saved)`));
364
+ await fsPromises.copyFile(targetPath, `${targetPath}.bak`);
365
+ console.log(colors.dim(`(Backup saved)`));
342
366
  }
343
367
  catch (e) { }
344
368
  }
345
- await fs.writeFile(targetPath, update.content || '');
346
- console.log(chalk.green(`✔ Wrote ${update.path}`));
369
+ await fsPromises.writeFile(targetPath, update.content || '');
370
+ console.log(colors.green(`✔ Wrote ${update.path}`));
347
371
  }
348
372
  }
349
373
  catch (e) {
350
- console.log(chalk.bgRed.white(` ERROR `) + ` ${update.path}: ${e.message}`);
374
+ console.log(colors.bgRed(` ERROR `) + ` ${update.path}: ${e.message}`);
351
375
  }
352
376
  }
353
- console.log(chalk.cyan('\nDone.'));
354
- });
355
- program.on('command:*', (operands) => {
356
- console.error(chalk.red(`\nError: Unknown command '${operands[0]}'`));
357
- process.exit(1);
358
- });
359
- program.parse();
377
+ console.log(colors.cyan('\nDone.'));
378
+ }
379
+ main().catch(() => { process.exit(1); });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aidx",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
4
4
  "description": "A CLI bridge between local code and LLMs. Copy context to clipboard and apply AI changes safely with diffs.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -15,7 +15,6 @@
15
15
  "scripts": {
16
16
  "build": "tsc",
17
17
  "start": "tsx src/index.ts",
18
- "dev": "tsx src/index.ts",
19
18
  "prepublishOnly": "npm run build"
20
19
  },
21
20
  "repository": {
@@ -23,13 +22,10 @@
23
22
  "url": "git+https://github.com/rx76d/aidx.git"
24
23
  },
25
24
  "homepage": "https://github.com/rx76d/aidx#readme",
26
- "bugs": {
27
- "url": "https://github.com/rx76d/aidx/issues"
28
- },
29
25
  "keywords": [
26
+ "ai",
30
27
  "aidx",
31
28
  "rx76d",
32
- "ai",
33
29
  "cli",
34
30
  "clipboard",
35
31
  "workflow",
@@ -37,18 +33,14 @@
37
33
  "llm",
38
34
  "chatgpt",
39
35
  "claude",
40
- "context",
41
- "diff"
36
+ "context"
42
37
  ],
43
38
  "author": "rx76d",
44
39
  "license": "MIT",
45
40
  "dependencies": {
46
41
  "@inquirer/prompts": "^7.0.0",
47
- "chalk": "^5.3.0",
48
42
  "clipboardy": "^4.0.0",
49
- "commander": "^12.0.0",
50
- "diff": "^5.2.0",
51
- "fast-glob": "^3.3.2"
43
+ "diff": "^5.2.0"
52
44
  },
53
45
  "devDependencies": {
54
46
  "@types/diff": "^5.0.9",