aidx 1.0.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/LICENSE +21 -0
- package/dist/index.js +333 -0
- package/package.json +64 -0
- package/readme.md +115 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 rx76d
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import { checkbox, confirm } from '@inquirer/prompts';
|
|
4
|
+
import glob from 'fast-glob';
|
|
5
|
+
import fs from 'fs/promises';
|
|
6
|
+
import { statSync } from 'fs';
|
|
7
|
+
import path from 'path';
|
|
8
|
+
import clipboardy from 'clipboardy';
|
|
9
|
+
import chalk from 'chalk';
|
|
10
|
+
import { encode } from 'gpt-tokenizer';
|
|
11
|
+
import * as Diff from 'diff';
|
|
12
|
+
import { isBinaryFile } from 'isbinaryfile';
|
|
13
|
+
// --- CONFIGURATION ---
|
|
14
|
+
const METADATA = {
|
|
15
|
+
name: "aidx",
|
|
16
|
+
description: "A CLI bridge between local code and LLMs.",
|
|
17
|
+
author: "rx76d",
|
|
18
|
+
version: "",
|
|
19
|
+
license: "MIT",
|
|
20
|
+
github: "https://github.com/rx76d/aidx"
|
|
21
|
+
};
|
|
22
|
+
const CONFIG_FILE = '.aidxrc.json';
|
|
23
|
+
const MAX_FILE_SIZE = 1.5 * 1024 * 1024; // 1.5MB Limit
|
|
24
|
+
const SECRET_REGEX = /(?:sk-[a-zA-Z0-9]{32,})|(?:AKIA[0-9A-Z]{16})|(?:[a-zA-Z0-9+/]{40,}=)/;
|
|
25
|
+
const SYSTEM_HEADER = `
|
|
26
|
+
================================================================
|
|
27
|
+
SYSTEM PROMPT: STRICT CODE MODE
|
|
28
|
+
You are an automated coding agent. You are NOT a chatbot.
|
|
29
|
+
You do NOT converse. You do NOT use Markdown formatting (like \`\`\`).
|
|
30
|
+
You ONLY output executable XML code changes.
|
|
31
|
+
================================================================
|
|
32
|
+
`;
|
|
33
|
+
const XML_SCHEMA_INSTRUCTION = `
|
|
34
|
+
\n\n
|
|
35
|
+
================================================================
|
|
36
|
+
CRITICAL OUTPUT INSTRUCTIONS:
|
|
37
|
+
1. You must output file changes inside <file> tags.
|
|
38
|
+
2. The "path" attribute must match the file path exactly.
|
|
39
|
+
3. **PROVIDE THE FULL FILE CONTENT.** Do not use placeholders like "// ... rest of code ...".
|
|
40
|
+
4. Do NOT wrap the output in \`\`\`xml or \`\`\` blocks.
|
|
41
|
+
|
|
42
|
+
FORMAT EXAMPLE:
|
|
43
|
+
<file path="src/index.ts">
|
|
44
|
+
import fs from 'fs';
|
|
45
|
+
console.log("Full code here...");
|
|
46
|
+
</file>
|
|
47
|
+
================================================================
|
|
48
|
+
`;
|
|
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;
|
|
61
|
+
}
|
|
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
|
+
}
|
|
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 () => {
|
|
77
|
+
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('----------------------------------------'));
|
|
85
|
+
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('--------------------------------------------------'));
|
|
100
|
+
const models = [
|
|
101
|
+
{ name: "Gemini 3 Pro", limit: "2,000,000+", type: "Huge" },
|
|
102
|
+
{ name: "Gemini 2.5 Flash", limit: "1,000,000+", type: "Huge" },
|
|
103
|
+
{ name: "ChatGPT-5", limit: " 200,000", type: "Large" },
|
|
104
|
+
{ name: "Claude 4.5 Sonnet", limit: " 200,000", type: "Large" },
|
|
105
|
+
{ name: "GPT-4o", limit: " 128,000", type: "Medium" },
|
|
106
|
+
{ name: "Llama 4 405B", limit: " 128,000", type: "Medium" },
|
|
107
|
+
{ name: "DeepSeek V3", limit: " 128,000", type: "Medium" },
|
|
108
|
+
{ name: "ChatGPT (Free)", limit: " ~8,000", type: "Small" },
|
|
109
|
+
];
|
|
110
|
+
console.log(chalk.cyan('Model Name'.padEnd(20)) + chalk.yellow('Max Tokens'.padEnd(15)) + chalk.white('Category'));
|
|
111
|
+
console.log(chalk.dim('--------------------------------------------------'));
|
|
112
|
+
models.forEach(m => {
|
|
113
|
+
const color = m.type === "Huge" ? chalk.green : m.type === "Large" ? chalk.blue : chalk.gray;
|
|
114
|
+
console.log(m.name.padEnd(20) + color(m.limit.padEnd(15)) + chalk.dim(m.type));
|
|
115
|
+
});
|
|
116
|
+
console.log(chalk.dim('--------------------------------------------------'));
|
|
117
|
+
console.log(chalk.dim('* 1,000 tokens ≈ 750 words of code/text.'));
|
|
118
|
+
console.log(chalk.dim('* Estimates based on latest model specs.\n'));
|
|
119
|
+
});
|
|
120
|
+
// --- COMMAND: BACKUP ---
|
|
121
|
+
program
|
|
122
|
+
.command('backup')
|
|
123
|
+
.description('Configure automatic backups')
|
|
124
|
+
.option('--on', 'Enable backups')
|
|
125
|
+
.option('--off', 'Disable backups')
|
|
126
|
+
.action(async (options) => {
|
|
127
|
+
if (options.on) {
|
|
128
|
+
await setBackupStatus(true);
|
|
129
|
+
console.log(chalk.green(`\n✔ Backups enabled. Settings saved to ${CONFIG_FILE}`));
|
|
130
|
+
}
|
|
131
|
+
else if (options.off) {
|
|
132
|
+
await setBackupStatus(false);
|
|
133
|
+
console.log(chalk.yellow(`\nBackups disabled.`));
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
const status = await getBackupStatus();
|
|
137
|
+
console.log(`\nCurrent Backup Status: ${status ? chalk.green('ENABLED') : chalk.red('DISABLED')}`);
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
// --- COMMAND: COPY (ROBUST) ---
|
|
141
|
+
program
|
|
142
|
+
.command('copy')
|
|
143
|
+
.description('Select files and copy to clipboard')
|
|
144
|
+
.action(async () => {
|
|
145
|
+
console.log(chalk.blue('Scanning directory...'));
|
|
146
|
+
const files = await glob(['**/*'], {
|
|
147
|
+
ignore: [
|
|
148
|
+
// Windows System Junk (CRITICAL FOR STABILITY)
|
|
149
|
+
'**/Application Data/**', '**/Cookies/**', '**/Local Settings/**', '**/Recent/**', '**/Start Menu/**',
|
|
150
|
+
// Dev Junk
|
|
151
|
+
'**/node_modules/**', '**/dist/**', '**/build/**', '**/.git/**', '**/.vscode/**',
|
|
152
|
+
'**/__pycache__/**', '**/venv/**', '**/target/**', '**/bin/**', '**/obj/**',
|
|
153
|
+
'**/vendor/**',
|
|
154
|
+
// File Types
|
|
155
|
+
'**/*.lock', '**/*.log', '**/*.png', '**/*.exe', '**/*.dll', '**/*.zip', '**/*.tar', '**/*.gz'
|
|
156
|
+
],
|
|
157
|
+
onlyFiles: true,
|
|
158
|
+
dot: true,
|
|
159
|
+
suppressErrors: true, // Fixes Windows EPERM crashes
|
|
160
|
+
followSymbolicLinks: false // Fixes Infinite Loops
|
|
161
|
+
});
|
|
162
|
+
if (files.length === 0)
|
|
163
|
+
return console.log(chalk.red('Error: No files found.'));
|
|
164
|
+
let selectedFiles = [];
|
|
165
|
+
try {
|
|
166
|
+
selectedFiles = await checkbox({
|
|
167
|
+
message: 'Select files to send to AI:',
|
|
168
|
+
choices: files.map(f => ({ name: f, value: f })),
|
|
169
|
+
pageSize: 15, loop: false,
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
catch (e) {
|
|
173
|
+
return console.log(chalk.yellow('\nSelection cancelled.'));
|
|
174
|
+
}
|
|
175
|
+
if (selectedFiles.length === 0)
|
|
176
|
+
return console.log(chalk.yellow('No files selected.'));
|
|
177
|
+
let output = SYSTEM_HEADER + "\n";
|
|
178
|
+
let skippedCount = 0;
|
|
179
|
+
console.log(chalk.dim('Reading files...'));
|
|
180
|
+
for (const file of selectedFiles) {
|
|
181
|
+
try {
|
|
182
|
+
// 1. Check Size
|
|
183
|
+
const stats = statSync(file);
|
|
184
|
+
if (stats.size > MAX_FILE_SIZE) {
|
|
185
|
+
console.log(chalk.yellow(`⚠ Skipped large file (>1.5MB): ${file}`));
|
|
186
|
+
skippedCount++;
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
// 2. Check Binary
|
|
190
|
+
if (await isBinaryFile(file)) {
|
|
191
|
+
console.log(chalk.yellow(`⚠ Skipped binary file: ${file}`));
|
|
192
|
+
skippedCount++;
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
const content = await fs.readFile(file, 'utf-8');
|
|
196
|
+
// 3. Check Secrets
|
|
197
|
+
if (file.includes('.env') || SECRET_REGEX.test(content)) {
|
|
198
|
+
console.log(chalk.red(`\nSECURITY ALERT: Secrets detected in ${file}`));
|
|
199
|
+
skippedCount++;
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
output += `File: ${file}\n\`\`\`\n${content}\n\`\`\`\n\n`;
|
|
203
|
+
}
|
|
204
|
+
catch (e) {
|
|
205
|
+
console.log(chalk.red(`Error reading ${file}`));
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
output += XML_SCHEMA_INSTRUCTION;
|
|
209
|
+
try {
|
|
210
|
+
await clipboardy.write(output);
|
|
211
|
+
const tokens = encode(output).length;
|
|
212
|
+
const finalCount = selectedFiles.length - skippedCount;
|
|
213
|
+
const tokenColor = tokens > 100000 ? chalk.red : tokens > 30000 ? chalk.yellow : chalk.green;
|
|
214
|
+
console.log(chalk.green(`\n✔ Copied ${finalCount} files to clipboard`));
|
|
215
|
+
console.log(`Estimated Tokens: ${tokenColor(tokens.toLocaleString())}`);
|
|
216
|
+
}
|
|
217
|
+
catch (e) {
|
|
218
|
+
console.log(chalk.red('Clipboard write failed (File too large for OS).'));
|
|
219
|
+
console.log(chalk.dim('Try selecting fewer files.'));
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
// --- COMMAND: APPLY ---
|
|
223
|
+
program
|
|
224
|
+
.command('apply')
|
|
225
|
+
.description('Apply AI changes from clipboard')
|
|
226
|
+
.action(async () => {
|
|
227
|
+
const backupsEnabled = await getBackupStatus();
|
|
228
|
+
console.log(chalk.dim('Reading clipboard...'));
|
|
229
|
+
let content;
|
|
230
|
+
try {
|
|
231
|
+
content = await clipboardy.read();
|
|
232
|
+
}
|
|
233
|
+
catch (e) {
|
|
234
|
+
return console.log(chalk.red('Error: Could not read clipboard.'));
|
|
235
|
+
}
|
|
236
|
+
const cleanedContent = content.replace(/```xml/g, '').replace(/```/g, '');
|
|
237
|
+
const fileRegex = /<file\s+path=["'](.*?)["']\s*>([\s\S]*?)<\/file>/gi;
|
|
238
|
+
const deleteRegex = /<delete\s+path=["'](.*?)["']\s*\/>/gi;
|
|
239
|
+
const updates = [];
|
|
240
|
+
let match;
|
|
241
|
+
while ((match = fileRegex.exec(cleanedContent)) !== null)
|
|
242
|
+
updates.push({ type: 'write', path: match[1], content: match[2].trim() });
|
|
243
|
+
while ((match = deleteRegex.exec(cleanedContent)) !== null)
|
|
244
|
+
updates.push({ type: 'delete', path: match[1] });
|
|
245
|
+
if (updates.length === 0)
|
|
246
|
+
return console.log(chalk.red('\nNo valid XML tags found.'));
|
|
247
|
+
console.log(chalk.bold(`\nFound ${updates.length} pending change(s):\n`));
|
|
248
|
+
for (const update of updates) {
|
|
249
|
+
const targetPath = path.resolve(process.cwd(), update.path);
|
|
250
|
+
console.log(chalk.bgBlue.white(` ${update.path} `));
|
|
251
|
+
if (update.type === 'delete') {
|
|
252
|
+
console.log(chalk.bgRed.white(' [DELETE] '));
|
|
253
|
+
}
|
|
254
|
+
else {
|
|
255
|
+
let originalContent = '';
|
|
256
|
+
try {
|
|
257
|
+
originalContent = await fs.readFile(targetPath, 'utf-8');
|
|
258
|
+
}
|
|
259
|
+
catch (e) {
|
|
260
|
+
console.log(chalk.bgGreen.black(' [NEW FILE] '));
|
|
261
|
+
}
|
|
262
|
+
if (originalContent && update.content !== undefined) {
|
|
263
|
+
const changes = Diff.diffLines(originalContent, update.content);
|
|
264
|
+
let count = 0;
|
|
265
|
+
changes.forEach((part) => {
|
|
266
|
+
if (count > 50)
|
|
267
|
+
return;
|
|
268
|
+
if (part.added) {
|
|
269
|
+
process.stdout.write(chalk.green(part.value.replace(/^/gm, '+ ')));
|
|
270
|
+
count++;
|
|
271
|
+
}
|
|
272
|
+
else if (part.removed) {
|
|
273
|
+
process.stdout.write(chalk.red(part.value.replace(/^/gm, '- ')));
|
|
274
|
+
count++;
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
if (count > 50)
|
|
278
|
+
console.log(chalk.dim('...'));
|
|
279
|
+
console.log('');
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
console.log(chalk.dim('--------------------------------------------------\n'));
|
|
283
|
+
}
|
|
284
|
+
let proceed = false;
|
|
285
|
+
try {
|
|
286
|
+
proceed = await confirm({ message: 'Apply these changes to disk?' });
|
|
287
|
+
}
|
|
288
|
+
catch (e) {
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
if (!proceed)
|
|
292
|
+
return console.log(chalk.yellow('Aborted.'));
|
|
293
|
+
console.log('');
|
|
294
|
+
for (const update of updates) {
|
|
295
|
+
const targetPath = path.resolve(process.cwd(), update.path);
|
|
296
|
+
try {
|
|
297
|
+
if (update.type === 'delete') {
|
|
298
|
+
if (backupsEnabled) {
|
|
299
|
+
try {
|
|
300
|
+
await fs.copyFile(targetPath, `${targetPath}.bak`);
|
|
301
|
+
}
|
|
302
|
+
catch (e) { }
|
|
303
|
+
}
|
|
304
|
+
try {
|
|
305
|
+
await fs.unlink(targetPath);
|
|
306
|
+
console.log(chalk.gray(`Deleted ${update.path}`));
|
|
307
|
+
}
|
|
308
|
+
catch (e) { }
|
|
309
|
+
}
|
|
310
|
+
else {
|
|
311
|
+
await fs.mkdir(path.dirname(targetPath), { recursive: true });
|
|
312
|
+
if (backupsEnabled) {
|
|
313
|
+
try {
|
|
314
|
+
await fs.copyFile(targetPath, `${targetPath}.bak`);
|
|
315
|
+
console.log(chalk.gray(`(Backup saved)`));
|
|
316
|
+
}
|
|
317
|
+
catch (e) { }
|
|
318
|
+
}
|
|
319
|
+
await fs.writeFile(targetPath, update.content || '');
|
|
320
|
+
console.log(chalk.green(`✔ Wrote ${update.path}`));
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
catch (e) {
|
|
324
|
+
console.log(chalk.bgRed.white(` ERROR `) + ` ${update.path}: ${e.message}`);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
console.log(chalk.cyan('\nDone.'));
|
|
328
|
+
});
|
|
329
|
+
program.on('command:*', (operands) => {
|
|
330
|
+
console.error(chalk.red(`\nError: Unknown command '${operands[0]}'`));
|
|
331
|
+
process.exit(1);
|
|
332
|
+
});
|
|
333
|
+
program.parse();
|
package/package.json
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "aidx",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "A CLI bridge between local code and LLMs. Copy context to clipboard and apply AI changes safely with diffs.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"aidx": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"README.md",
|
|
13
|
+
"LICENSE"
|
|
14
|
+
],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsc",
|
|
17
|
+
"start": "tsx src/index.ts",
|
|
18
|
+
"dev": "tsx src/index.ts",
|
|
19
|
+
"prepublishOnly": "npm run build"
|
|
20
|
+
},
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "git+https://github.com/rx76d/aidx.git"
|
|
24
|
+
},
|
|
25
|
+
"homepage": "https://github.com/rx76d/aidx#readme",
|
|
26
|
+
"bugs": {
|
|
27
|
+
"url": "https://github.com/rx76d/aidx/issues"
|
|
28
|
+
},
|
|
29
|
+
"keywords": [
|
|
30
|
+
"aidx",
|
|
31
|
+
"rx76d",
|
|
32
|
+
"ai",
|
|
33
|
+
"cli",
|
|
34
|
+
"clipboard",
|
|
35
|
+
"workflow",
|
|
36
|
+
"automation",
|
|
37
|
+
"llm",
|
|
38
|
+
"chatgpt",
|
|
39
|
+
"claude",
|
|
40
|
+
"context",
|
|
41
|
+
"diff"
|
|
42
|
+
],
|
|
43
|
+
"author": "rx76d",
|
|
44
|
+
"license": "MIT",
|
|
45
|
+
"dependencies": {
|
|
46
|
+
"@inquirer/prompts": "^7.0.0",
|
|
47
|
+
"chalk": "^5.3.0",
|
|
48
|
+
"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"
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"@types/diff": "^5.0.9",
|
|
57
|
+
"@types/node": "^20.10.0",
|
|
58
|
+
"tsx": "^4.7.0",
|
|
59
|
+
"typescript": "^5.3.0"
|
|
60
|
+
},
|
|
61
|
+
"engines": {
|
|
62
|
+
"node": ">=18.0.0"
|
|
63
|
+
}
|
|
64
|
+
}
|
package/readme.md
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
<h1>aidx</h1>
|
|
4
|
+
|
|
5
|
+
<p>
|
|
6
|
+
<strong>The CLI bridge between your local codebase and LLMs.</strong>
|
|
7
|
+
</p>
|
|
8
|
+
|
|
9
|
+

|
|
10
|
+

|
|
11
|
+

|
|
12
|
+

|
|
13
|
+
|
|
14
|
+
<br />
|
|
15
|
+
|
|
16
|
+
<p>
|
|
17
|
+
<a href="#-quick-start">Quick Start</a> •
|
|
18
|
+
<a href="#-how-it-works">How It Works</a> •
|
|
19
|
+
<a href="#-commands">Commands</a> •
|
|
20
|
+
<a href="#-features">Features</a>
|
|
21
|
+
</p>
|
|
22
|
+
</div>
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## 📖 About
|
|
27
|
+
|
|
28
|
+
**aidx** is a zero-config tool that turns your terminal into a context manager for ChatGPT, Claude, and Gemini.
|
|
29
|
+
|
|
30
|
+
It solves two problems:
|
|
31
|
+
1. **Getting code TO the AI:** It scans your project, filters out junk (binaries, secrets, `node_modules`), and copies code to your clipboard with a strict XML protocol.
|
|
32
|
+
2. **Getting code FROM the AI:** It reads the AI's response, shows you a visual diff, and applies changes to your disk safely.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## 🚀 Quick Start
|
|
37
|
+
|
|
38
|
+
No installation required. Run it instantly:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npx aidx
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Or install globally for repeated use:
|
|
45
|
+
```bash
|
|
46
|
+
npm install -g aidx
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## 🔄 How It Works
|
|
50
|
+
1. **The Outbound Loop (Copying)**
|
|
51
|
+
|
|
52
|
+
Select the files you want the AI to understand.
|
|
53
|
+
```
|
|
54
|
+
npx aidx copy
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
**Action:** Opens an interactive file picker.
|
|
58
|
+
Result: Copies files + hidden system instructions to your clipboard.
|
|
59
|
+
Next: Paste into ChatGPT/Claude.
|
|
60
|
+
|
|
61
|
+
2. **The Inbound Loop (Applying):**
|
|
62
|
+
When the AI replies with code, copy its entire response to your clipboard.
|
|
63
|
+
```
|
|
64
|
+
npx aidx apply
|
|
65
|
+
```
|
|
66
|
+
**Action:** Scans your clipboard for the XML tags.
|
|
67
|
+
**Safety:** Shows a Git-style diff (Green + / Red -).
|
|
68
|
+
**Result:** Updates your files only after you confirm Yes.
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
# 🎮 Commands
|
|
72
|
+
|
|
73
|
+
Command Description
|
|
74
|
+
|
|
75
|
+
```npx aidx```: Shows the main menu and status info.
|
|
76
|
+
|
|
77
|
+
```npx aidx copy```: Interactive file scanner & clipboard copier.
|
|
78
|
+
|
|
79
|
+
```npx aidx apply```: Reads clipboard, shows diffs, and writes changes.
|
|
80
|
+
|
|
81
|
+
```npx aidx backup --on```: Enables automatic .bak files before overwriting.
|
|
82
|
+
|
|
83
|
+
```npx aidx backup --off```: Disables automatic backups.
|
|
84
|
+
|
|
85
|
+
```npx aidx stl```: Shows token limits of AI model.
|
|
86
|
+
|
|
87
|
+
```npx aidx --help```: Displays help information.
|
|
88
|
+
|
|
89
|
+
```npx aidx -version```: Displays current version.
|
|
90
|
+
|
|
91
|
+
# ✨ Features
|
|
92
|
+
|
|
93
|
+
🛡️ **Security Guard**
|
|
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
|
+
|
|
96
|
+
💾 **Automatic Backups**
|
|
97
|
+
Don't trust the AI completely? Turn on backups.
|
|
98
|
+
```npx aidx backup --on```
|
|
99
|
+
Before src/App.tsx is updated, aidx will save a copy to src/App.tsx.bak.
|
|
100
|
+
|
|
101
|
+
🌍 **Universal Support**
|
|
102
|
+
Works with almost any text-based language:
|
|
103
|
+
Web: TS, JS, HTML, CSS, Svelte, Vue, JSX
|
|
104
|
+
Backend: Python, Go, Rust, Java, C#, PHP
|
|
105
|
+
Config: JSON, YAML, TOML, SQL, Markdown
|
|
106
|
+
Smart Ignores: Automatically ignores node_modules, .git, __pycache__, venv, target, bin, and binary files (.png, .exe).
|
|
107
|
+
|
|
108
|
+
📊 **Token Awareness**
|
|
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
|
+
|
|
111
|
+
🛡️ **License**
|
|
112
|
+
This project is open source and available under the MIT License.
|
|
113
|
+
<div align="center">
|
|
114
|
+
<sub>Developed by rx76d</sub>
|
|
115
|
+
</div>
|