aidx 1.0.2 → 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 (3) hide show
  1. package/dist/index.js +191 -160
  2. package/package.json +6 -16
  3. package/readme.md +5 -5
package/dist/index.js CHANGED
@@ -1,27 +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
- import { encode } from 'gpt-tokenizer';
11
7
  import * as Diff from 'diff';
12
- import { isBinaryFile } from 'isbinaryfile';
13
8
  // --- CONFIGURATION ---
14
9
  const METADATA = {
15
10
  name: "aidx",
16
11
  description: "A CLI bridge between local code and LLMs.",
17
12
  author: "rx76d",
18
- version: "1.0.2",
13
+ version: "1.0.4",
19
14
  license: "MIT",
20
15
  github: "https://github.com/rx76d/aidx"
21
16
  };
22
17
  const CONFIG_FILE = '.aidxrc.json';
23
18
  const MAX_FILE_SIZE = 1.5 * 1024 * 1024; // 1.5MB Limit
24
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 ---
25
95
  const SYSTEM_HEADER = `
26
96
  ================================================================
27
97
  SYSTEM PROMPT: STRICT CODE MODE
@@ -46,59 +116,59 @@ console.log("Full code here...");
46
116
  </file>
47
117
  ================================================================
48
118
  `;
49
- // --- GLOBAL HANDLERS ---
50
- process.on('SIGINT', () => {
51
- console.log(chalk.yellow('\n\nOperation cancelled by user.'));
52
- process.exit(0);
53
- });
54
- // --- HELPER: CONFIG MANAGEMENT ---
55
- async function getBackupStatus() {
56
- try {
57
- const configPath = path.resolve(process.cwd(), CONFIG_FILE);
58
- const data = await fs.readFile(configPath, 'utf-8');
59
- const config = JSON.parse(data);
60
- return !!config.backup;
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);
61
149
  }
62
- catch (e) {
63
- return false;
64
- }
65
- }
66
- async function setBackupStatus(enabled) {
67
- const configPath = path.resolve(process.cwd(), CONFIG_FILE);
68
- await fs.writeFile(configPath, JSON.stringify({ backup: enabled }, null, 2));
69
150
  }
70
- const program = new Command();
71
- program
72
- .name(METADATA.name)
73
- .description(METADATA.description)
74
- .version(METADATA.version);
75
- // --- ROOT COMMAND (DASHBOARD) ---
76
- program.action(async () => {
151
+ // --- ACTIONS ---
152
+ async function showMenu() {
77
153
  const backupEnabled = await getBackupStatus();
78
- console.log('\n' + chalk.bgBlue.bold(` ${METADATA.name.toUpperCase()} `) + chalk.dim(` v${METADATA.version}`));
79
- console.log(chalk.dim('----------------------------------------'));
80
- console.log(`${chalk.bold('Description:')} ${METADATA.description}`);
81
- console.log(`${chalk.bold('Author:')} ${METADATA.author}`);
82
- console.log(`${chalk.bold('Backups:')} ${backupEnabled ? chalk.green('ENABLED') : chalk.dim('DISABLED')}`);
83
- console.log(`${chalk.bold('Limit:')} 1.5MB per file`);
84
- 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('----------------------------------------'));
85
160
  console.log('\nAvailable Commands:');
86
- console.log(` ${chalk.cyan('npx aidx copy')} Select files and copy context`);
87
- console.log(` ${chalk.cyan('npx aidx apply')} Apply AI changes to disk`);
88
- console.log(` ${chalk.cyan('npx aidx backup --on')} Enable auto-backups`);
89
- console.log(` ${chalk.cyan('npx aidx backup --off')} Disable auto-backups`);
90
- console.log(` ${chalk.cyan('npx aidx stl')} Show AI token limits`);
91
- console.log(`\nRun ${chalk.gray('npx aidx --help')} for details.\n`);
92
- });
93
- // --- COMMAND: STL (Safe Token Limits) ---
94
- program
95
- .command('stl')
96
- .description('Show safe token limits for AI models')
97
- .action(() => {
98
- console.log('\n' + chalk.bold('AI Model Context Limits (2025 Reference)'));
99
- 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('--------------------------------------------------'));
100
171
  const models = [
101
- // HUGE (≈ 1M+ tokens)
102
172
  { name: "Gemini 3 Pro", limit: "2,000,000+", type: "Huge" },
103
173
  { name: "Gemini 2.5 Pro", limit: "1,000,000+", type: "Huge" },
104
174
  { name: "Gemini 2.5 Flash", limit: "1,000,000+", type: "Huge" },
@@ -106,77 +176,48 @@ program
106
176
  { name: "Llama 4 Maverick", limit: "1,000,000+", type: "Huge" },
107
177
  { name: "Qwen 2.5 1M", limit: "1,000,000+", type: "Huge" },
108
178
  { name: "GPT-4.1", limit: "1,000,000+", type: "Huge" },
109
- // LARGE (≈ 200K–500K tokens)
110
179
  { name: "ChatGPT-5", limit: "200,000+", type: "Large" },
111
180
  { name: "Claude 4.5 Sonnet", limit: "200,000+", type: "Large" },
112
181
  { name: "Claude 4.5 Opus", limit: "200,000+", type: "Large" },
113
182
  { name: "Grok 4", limit: "256,000", type: "Large" },
114
183
  { name: "Cohere Command A", limit: "256,000", type: "Large" },
115
- // MEDIUM (≈ 100K–150K tokens)
116
184
  { name: "GPT-4o", limit: "128,000", type: "Medium" },
117
185
  { name: "Llama 4 405B", limit: "128,000", type: "Medium" },
118
186
  { name: "DeepSeek V3", limit: "128,000", type: "Medium" },
119
187
  { name: "Grok 3", limit: "128,000", type: "Medium" },
120
188
  { name: "GPT-5 Mini", limit: "128,000", type: "Medium" },
121
- // SMALL (< 50K tokens)
122
189
  { name: "ChatGPT (Free)", limit: "~8,000", type: "Small" },
123
190
  { name: "Claude Haiku", limit: "~16,000", type: "Small" },
124
191
  ];
125
- console.log(chalk.cyan('Model Name'.padEnd(20)) + chalk.yellow('Max Tokens'.padEnd(15)) + chalk.white('Category'));
126
- 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('--------------------------------------------------'));
127
194
  models.forEach(m => {
128
- const color = m.type === "Huge" ? chalk.green : m.type === "Large" ? chalk.blue : chalk.gray;
129
- 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));
130
197
  });
131
- console.log(chalk.dim('--------------------------------------------------'));
132
- console.log(chalk.dim('* 1,000 tokens ≈ 750 words of code/text.'));
133
- console.log(chalk.dim('* Estimates based on latest model specs.\n'));
134
- });
135
- // --- COMMAND: BACKUP ---
136
- program
137
- .command('backup')
138
- .description('Configure automatic backups')
139
- .option('--on', 'Enable backups')
140
- .option('--off', 'Disable backups')
141
- .action(async (options) => {
142
- 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') {
143
204
  await setBackupStatus(true);
144
- 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}`));
145
206
  }
146
- else if (options.off) {
207
+ else if (flag === '--off') {
147
208
  await setBackupStatus(false);
148
- console.log(chalk.yellow(`\nBackups disabled.`));
209
+ console.log(colors.yellow(`\nBackups disabled.`));
149
210
  }
150
211
  else {
151
- const status = await getBackupStatus();
152
- console.log(`\nCurrent Backup Status: ${status ? chalk.green('ENABLED') : chalk.red('DISABLED')}`);
212
+ console.log(colors.red('Error: Use --on or --off'));
153
213
  }
154
- });
155
- // --- COMMAND: COPY (ROBUST) ---
156
- program
157
- .command('copy')
158
- .description('Select files and copy to clipboard')
159
- .action(async () => {
160
- console.log(chalk.blue('Scanning directory...'));
161
- const files = await glob(['**/*'], {
162
- ignore: [
163
- // Windows System Junk (CRITICAL FOR STABILITY)
164
- '**/Application Data/**', '**/Cookies/**', '**/Local Settings/**', '**/Recent/**', '**/Start Menu/**',
165
- // Dev Junk
166
- '**/node_modules/**', '**/dist/**', '**/build/**', '**/.git/**', '**/.vscode/**',
167
- '**/__pycache__/**', '**/venv/**', '**/target/**', '**/bin/**', '**/obj/**',
168
- '**/vendor/**',
169
- // File Types
170
- '**/*.lock', '**/*.log', '**/*.png', '**/*.exe', '**/*.dll', '**/*.zip', '**/*.tar', '**/*.gz'
171
- ],
172
- onlyFiles: true,
173
- dot: true,
174
- suppressErrors: true, // Fixes Windows EPERM crashes
175
- followSymbolicLinks: false // Fixes Infinite Loops
176
- });
214
+ }
215
+ async function runCopy() {
216
+ console.log(colors.blue('Scanning directory...'));
217
+ const files = await scanFiles(process.cwd());
177
218
  if (files.length === 0)
178
- return console.log(chalk.red('Error: No files found.'));
179
- let selectedFiles = [];
219
+ return console.log(colors.red('Error: No files found.'));
220
+ let selectedFiles;
180
221
  try {
181
222
  selectedFiles = await checkbox({
182
223
  message: 'Select files to send to AI:',
@@ -185,68 +226,62 @@ program
185
226
  });
186
227
  }
187
228
  catch (e) {
188
- return console.log(chalk.yellow('\nSelection cancelled.'));
229
+ return console.log(colors.yellow('\nSelection cancelled.'));
189
230
  }
190
231
  if (selectedFiles.length === 0)
191
- return console.log(chalk.yellow('No files selected.'));
232
+ return console.log(colors.yellow('No files selected.'));
192
233
  let output = SYSTEM_HEADER + "\n";
193
234
  let skippedCount = 0;
194
- console.log(chalk.dim('Reading files...'));
235
+ console.log(colors.dim('Reading files...'));
195
236
  for (const file of selectedFiles) {
196
237
  try {
197
- // 1. Check Size
198
- const stats = statSync(file);
238
+ const stats = fs.statSync(file);
199
239
  if (stats.size > MAX_FILE_SIZE) {
200
- console.log(chalk.yellow(`⚠ Skipped large file (>1.5MB): ${file}`));
240
+ console.log(colors.yellow(`⚠ Skipped large file (>1.5MB): ${file}`));
201
241
  skippedCount++;
202
242
  continue;
203
243
  }
204
- // 2. Check Binary
205
- if (await isBinaryFile(file)) {
206
- console.log(chalk.yellow(`⚠ Skipped binary file: ${file}`));
244
+ const buffer = await fsPromises.readFile(file);
245
+ if (isBinary(buffer)) {
246
+ console.log(colors.yellow(`⚠ Skipped binary file: ${file}`));
207
247
  skippedCount++;
208
248
  continue;
209
249
  }
210
- const content = await fs.readFile(file, 'utf-8');
211
- // 3. Check Secrets
250
+ const content = buffer.toString('utf-8');
212
251
  if (file.includes('.env') || SECRET_REGEX.test(content)) {
213
- console.log(chalk.red(`\nSECURITY ALERT: Secrets detected in ${file}`));
252
+ console.log(colors.red(`\n🛑 SECURITY ALERT: Secrets detected in ${file}`));
214
253
  skippedCount++;
215
254
  continue;
216
255
  }
217
256
  output += `File: ${file}\n\`\`\`\n${content}\n\`\`\`\n\n`;
218
257
  }
219
258
  catch (e) {
220
- console.log(chalk.red(`Error reading ${file}`));
259
+ console.log(colors.red(`Error reading ${file}`));
221
260
  }
222
261
  }
223
262
  output += XML_SCHEMA_INSTRUCTION;
224
263
  try {
225
- await clipboardy.write(output);
226
- const tokens = encode(output).length;
264
+ await clipboardy.write(output); // Uses robust library
265
+ const tokens = estimateTokens(output);
227
266
  const finalCount = selectedFiles.length - skippedCount;
228
- const tokenColor = tokens > 100000 ? chalk.red : tokens > 30000 ? chalk.yellow : chalk.green;
229
- 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`));
230
269
  console.log(`Estimated Tokens: ${tokenColor(tokens.toLocaleString())}`);
231
270
  }
232
271
  catch (e) {
233
- console.log(chalk.red('Clipboard write failed (File too large for OS).'));
234
- console.log(chalk.dim('Try selecting fewer files.'));
272
+ console.log(colors.red(`❌ Clipboard write failed: ${e instanceof Error ? e.message : 'Unknown'}`));
235
273
  }
236
- });
237
- // --- COMMAND: APPLY ---
238
- program
239
- .command('apply')
240
- .description('Apply AI changes from clipboard')
241
- .action(async () => {
274
+ }
275
+ async function runApply() {
242
276
  const backupsEnabled = await getBackupStatus();
243
- console.log(chalk.dim('Reading clipboard...'));
277
+ console.log(colors.dim('Reading clipboard...'));
244
278
  let content;
245
279
  try {
246
280
  content = await clipboardy.read();
247
281
  }
248
- catch (e) {
249
- 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;
250
285
  }
251
286
  const cleanedContent = content.replace(/```xml/g, '').replace(/```/g, '');
252
287
  const fileRegex = /<file\s+path=["'](.*?)["']\s*>([\s\S]*?)<\/file>/gi;
@@ -258,21 +293,21 @@ program
258
293
  while ((match = deleteRegex.exec(cleanedContent)) !== null)
259
294
  updates.push({ type: 'delete', path: match[1] });
260
295
  if (updates.length === 0)
261
- return console.log(chalk.red('\nNo valid XML tags found.'));
262
- 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`));
263
298
  for (const update of updates) {
264
299
  const targetPath = path.resolve(process.cwd(), update.path);
265
- console.log(chalk.bgBlue.white(` ${update.path} `));
300
+ console.log(colors.bgBlue(` ${update.path} `));
266
301
  if (update.type === 'delete') {
267
- console.log(chalk.bgRed.white(' [DELETE] '));
302
+ console.log(colors.bgRed(' [DELETE] '));
268
303
  }
269
304
  else {
270
305
  let originalContent = '';
271
306
  try {
272
- originalContent = await fs.readFile(targetPath, 'utf-8');
307
+ originalContent = await fsPromises.readFile(targetPath, 'utf-8');
273
308
  }
274
309
  catch (e) {
275
- console.log(chalk.bgGreen.black(' [NEW FILE] '));
310
+ console.log(colors.bgGreen(' [NEW FILE] '));
276
311
  }
277
312
  if (originalContent && update.content !== undefined) {
278
313
  const changes = Diff.diffLines(originalContent, update.content);
@@ -281,20 +316,20 @@ program
281
316
  if (count > 50)
282
317
  return;
283
318
  if (part.added) {
284
- process.stdout.write(chalk.green(part.value.replace(/^/gm, '+ ')));
319
+ process.stdout.write(colors.green(part.value.replace(/^/gm, '+ ')));
285
320
  count++;
286
321
  }
287
322
  else if (part.removed) {
288
- process.stdout.write(chalk.red(part.value.replace(/^/gm, '- ')));
323
+ process.stdout.write(colors.red(part.value.replace(/^/gm, '- ')));
289
324
  count++;
290
325
  }
291
326
  });
292
327
  if (count > 50)
293
- console.log(chalk.dim('...'));
328
+ console.log(colors.dim('...'));
294
329
  console.log('');
295
330
  }
296
331
  }
297
- console.log(chalk.dim('--------------------------------------------------\n'));
332
+ console.log(colors.dim('--------------------------------------------------\n'));
298
333
  }
299
334
  let proceed = false;
300
335
  try {
@@ -304,7 +339,7 @@ program
304
339
  return;
305
340
  }
306
341
  if (!proceed)
307
- return console.log(chalk.yellow('Aborted.'));
342
+ return console.log(colors.yellow('Aborted.'));
308
343
  console.log('');
309
344
  for (const update of updates) {
310
345
  const targetPath = path.resolve(process.cwd(), update.path);
@@ -312,37 +347,33 @@ program
312
347
  if (update.type === 'delete') {
313
348
  if (backupsEnabled) {
314
349
  try {
315
- await fs.copyFile(targetPath, `${targetPath}.bak`);
350
+ await fsPromises.copyFile(targetPath, `${targetPath}.bak`);
316
351
  }
317
352
  catch (e) { }
318
353
  }
319
354
  try {
320
- await fs.unlink(targetPath);
321
- console.log(chalk.gray(`Deleted ${update.path}`));
355
+ await fsPromises.unlink(targetPath);
356
+ console.log(colors.dim(`Deleted ${update.path}`));
322
357
  }
323
358
  catch (e) { }
324
359
  }
325
360
  else {
326
- await fs.mkdir(path.dirname(targetPath), { recursive: true });
361
+ await fsPromises.mkdir(path.dirname(targetPath), { recursive: true });
327
362
  if (backupsEnabled) {
328
363
  try {
329
- await fs.copyFile(targetPath, `${targetPath}.bak`);
330
- console.log(chalk.gray(`(Backup saved)`));
364
+ await fsPromises.copyFile(targetPath, `${targetPath}.bak`);
365
+ console.log(colors.dim(`(Backup saved)`));
331
366
  }
332
367
  catch (e) { }
333
368
  }
334
- await fs.writeFile(targetPath, update.content || '');
335
- console.log(chalk.green(`✔ Wrote ${update.path}`));
369
+ await fsPromises.writeFile(targetPath, update.content || '');
370
+ console.log(colors.green(`✔ Wrote ${update.path}`));
336
371
  }
337
372
  }
338
373
  catch (e) {
339
- console.log(chalk.bgRed.white(` ERROR `) + ` ${update.path}: ${e.message}`);
374
+ console.log(colors.bgRed(` ERROR `) + ` ${update.path}: ${e.message}`);
340
375
  }
341
376
  }
342
- console.log(chalk.cyan('\nDone.'));
343
- });
344
- program.on('command:*', (operands) => {
345
- console.error(chalk.red(`\nError: Unknown command '${operands[0]}'`));
346
- process.exit(1);
347
- });
348
- program.parse();
377
+ console.log(colors.cyan('\nDone.'));
378
+ }
379
+ main().catch(() => { process.exit(1); });
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "aidx",
3
- "version": "1.0.2",
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",
7
7
  "bin": {
8
- "aidx": "dist/index.js"
8
+ "aidx": "./dist/index.js"
9
9
  },
10
10
  "files": [
11
11
  "dist",
@@ -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,20 +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",
52
- "gpt-tokenizer": "^2.1.2",
53
- "isbinaryfile": "^5.0.2"
43
+ "diff": "^5.2.0"
54
44
  },
55
45
  "devDependencies": {
56
46
  "@types/diff": "^5.0.9",
@@ -61,4 +51,4 @@
61
51
  "engines": {
62
52
  "node": ">=18.0.0"
63
53
  }
64
- }
54
+ }
package/readme.md CHANGED
@@ -90,25 +90,25 @@ Command Description
90
90
 
91
91
  # ✨ Features
92
92
 
93
- 🛡️ **Security Guard**
93
+ 🛡️ **Security Guard**:
94
94
  Automatically detects and blocks API keys (AWS, OpenAI, Stripe) from being copied to the clipboard. If a file looks like a secret, it is skipped.
95
95
 
96
- 💾 **Automatic Backups**
96
+ 💾 **Automatic Backups**:
97
97
  Don't trust the AI completely? Turn on backups.
98
98
  ```npx aidx backup --on```
99
99
  Before src/App.tsx is updated, aidx will save a copy to src/App.tsx.bak.
100
100
 
101
- 🌍 **Universal Support**
101
+ 🌍 **Universal Support**:
102
102
  Works with almost any text-based language:
103
103
  Web: TS, JS, HTML, CSS, Svelte, Vue, JSX
104
104
  Backend: Python, Go, Rust, Java, C#, PHP
105
105
  Config: JSON, YAML, TOML, SQL, Markdown
106
106
  Smart Ignores: Automatically ignores node_modules, .git, __pycache__, venv, target, bin, and binary files (.png, .exe).
107
107
 
108
- 📊 **Token Awareness**
108
+ 📊 **Token Awareness**:
109
109
  Calculates estimated token usage before you paste, so you know if you are about to exceed the limits of GPT-5 or Claude 3.5.
110
110
 
111
- 🛡️ **License**
111
+ 🛡️ **License**:
112
112
  This project is open source and available under the MIT License.
113
113
  <div align="center">
114
114
  <sub>Developed by rx76d</sub>