aided-dev 1.0.5 → 1.0.7
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/dist/cli.js +10 -0
- package/dist/orchestrator/orchestrator.d.ts +3 -0
- package/dist/orchestrator/orchestrator.js +206 -11
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -15,6 +15,7 @@ program
|
|
|
15
15
|
.option('-m, --model <model>', 'Claude model to use', 'claude-sonnet-4-20250514')
|
|
16
16
|
.option('-v, --verbose', 'Show detailed output')
|
|
17
17
|
.option('-d, --dir <path>', 'Project directory', process.cwd())
|
|
18
|
+
.option('-a, --apply', 'Apply code changes directly to the repository')
|
|
18
19
|
.action(async (prompt, options) => {
|
|
19
20
|
if (!prompt) {
|
|
20
21
|
program.help();
|
|
@@ -48,6 +49,7 @@ program
|
|
|
48
49
|
repoPath: options.dir,
|
|
49
50
|
model: options.model,
|
|
50
51
|
verbose: options.verbose,
|
|
52
|
+
applyChanges: options.apply,
|
|
51
53
|
});
|
|
52
54
|
await orchestrator.run(prompt);
|
|
53
55
|
}
|
|
@@ -256,10 +258,18 @@ program
|
|
|
256
258
|
console.log(' aided "Document all REST API endpoints"');
|
|
257
259
|
console.log('');
|
|
258
260
|
console.log(chalk.cyan('Options:'));
|
|
261
|
+
console.log(' -a, --apply Apply code changes directly to repo');
|
|
259
262
|
console.log(' -v, --verbose Show detailed agent output');
|
|
260
263
|
console.log(' -d, --dir <path> Run in specific directory');
|
|
261
264
|
console.log(' -m, --model <model> Use different Claude model');
|
|
262
265
|
console.log('');
|
|
266
|
+
console.log(chalk.cyan('Examples with --apply:'));
|
|
267
|
+
console.log(chalk.dim(' # Create new feature and write files'));
|
|
268
|
+
console.log(' aided "Add user dashboard" --apply');
|
|
269
|
+
console.log('');
|
|
270
|
+
console.log(chalk.dim(' # Fix a bug in existing code'));
|
|
271
|
+
console.log(' aided "Fix: null check in auth middleware" --apply');
|
|
272
|
+
console.log('');
|
|
263
273
|
console.log(chalk.cyan('Other Commands:'));
|
|
264
274
|
console.log(' aided analyze Analyze project context');
|
|
265
275
|
console.log(' aided agents List available AI agents');
|
|
@@ -6,6 +6,7 @@ export interface OrchestratorOptions {
|
|
|
6
6
|
verbose?: boolean;
|
|
7
7
|
skipPlanning?: boolean;
|
|
8
8
|
outputDir?: string;
|
|
9
|
+
applyChanges?: boolean;
|
|
9
10
|
}
|
|
10
11
|
export interface WorkflowState {
|
|
11
12
|
task: string;
|
|
@@ -24,6 +25,7 @@ export declare class Orchestrator {
|
|
|
24
25
|
private verbose;
|
|
25
26
|
private skipPlanning;
|
|
26
27
|
private outputDir;
|
|
28
|
+
private applyChanges;
|
|
27
29
|
private bmadLoader;
|
|
28
30
|
private agents;
|
|
29
31
|
private planner;
|
|
@@ -42,5 +44,6 @@ export declare class Orchestrator {
|
|
|
42
44
|
private buildStepPrompt;
|
|
43
45
|
private extractTextContent;
|
|
44
46
|
private saveOutputs;
|
|
47
|
+
private applyCodeToRepo;
|
|
45
48
|
private printSummary;
|
|
46
49
|
}
|
|
@@ -14,6 +14,7 @@ export class Orchestrator {
|
|
|
14
14
|
verbose;
|
|
15
15
|
skipPlanning;
|
|
16
16
|
outputDir;
|
|
17
|
+
applyChanges;
|
|
17
18
|
bmadLoader;
|
|
18
19
|
agents = new Map();
|
|
19
20
|
planner = null;
|
|
@@ -28,6 +29,7 @@ export class Orchestrator {
|
|
|
28
29
|
this.verbose = options.verbose || false;
|
|
29
30
|
this.skipPlanning = options.skipPlanning || false;
|
|
30
31
|
this.outputDir = options.outputDir || path.join(options.repoPath, '.aided-output');
|
|
32
|
+
this.applyChanges = options.applyChanges || false;
|
|
31
33
|
this.bmadLoader = getLoader();
|
|
32
34
|
}
|
|
33
35
|
async loadAgents() {
|
|
@@ -255,7 +257,7 @@ export class Orchestrator {
|
|
|
255
257
|
return files;
|
|
256
258
|
}
|
|
257
259
|
async scanDirectory(dirPath, files, relativePath, maxDepth) {
|
|
258
|
-
if (maxDepth <= 0 || files.size >
|
|
260
|
+
if (maxDepth <= 0 || files.size > 20)
|
|
259
261
|
return;
|
|
260
262
|
const sourceExtensions = ['.ts', '.js', '.py', '.go', '.rs', '.java', '.rb', '.php'];
|
|
261
263
|
try {
|
|
@@ -274,11 +276,11 @@ export class Orchestrator {
|
|
|
274
276
|
if (sourceExtensions.includes(ext)) {
|
|
275
277
|
try {
|
|
276
278
|
const content = fs.readFileSync(fullPath, 'utf-8');
|
|
277
|
-
if (content.length <
|
|
279
|
+
if (content.length < 4000) {
|
|
278
280
|
files.set(relPath, content);
|
|
279
281
|
}
|
|
280
282
|
else {
|
|
281
|
-
files.set(relPath, content.substring(0,
|
|
283
|
+
files.set(relPath, content.substring(0, 4000) + '\n\n... (truncated)');
|
|
282
284
|
}
|
|
283
285
|
}
|
|
284
286
|
catch {
|
|
@@ -361,10 +363,37 @@ Implement the code according to the specifications provided.
|
|
|
361
363
|
- Follow the architecture/design if provided
|
|
362
364
|
- Implement all required functionality
|
|
363
365
|
- Include proper error handling
|
|
364
|
-
-
|
|
365
|
-
- Follow framework conventions
|
|
366
|
+
- Follow framework conventions and existing patterns
|
|
366
367
|
|
|
367
|
-
|
|
368
|
+
## CRITICAL: Output Format
|
|
369
|
+
You MUST output changes in this EXACT format for each file:
|
|
370
|
+
|
|
371
|
+
For NEW files:
|
|
372
|
+
\`\`\`
|
|
373
|
+
===FILE: path/to/newfile.js
|
|
374
|
+
===ACTION: CREATE
|
|
375
|
+
\`\`\`javascript
|
|
376
|
+
// complete file content here
|
|
377
|
+
\`\`\`
|
|
378
|
+
===END_FILE
|
|
379
|
+
\`\`\`
|
|
380
|
+
|
|
381
|
+
For MODIFYING existing files:
|
|
382
|
+
\`\`\`
|
|
383
|
+
===FILE: path/to/existing.js
|
|
384
|
+
===ACTION: MODIFY
|
|
385
|
+
===FIND:
|
|
386
|
+
\`\`\`javascript
|
|
387
|
+
// exact code to find and replace
|
|
388
|
+
\`\`\`
|
|
389
|
+
===REPLACE:
|
|
390
|
+
\`\`\`javascript
|
|
391
|
+
// new code to replace with
|
|
392
|
+
\`\`\`
|
|
393
|
+
===END_FILE
|
|
394
|
+
\`\`\`
|
|
395
|
+
|
|
396
|
+
Stay focused ONLY on the task. Do not add unnecessary features or refactor unrelated code.`,
|
|
368
397
|
tests: `## Task
|
|
369
398
|
${task}
|
|
370
399
|
|
|
@@ -375,18 +404,45 @@ Generate comprehensive tests for the implementation:
|
|
|
375
404
|
- Edge cases and error scenarios
|
|
376
405
|
- Use appropriate test framework for the stack
|
|
377
406
|
|
|
378
|
-
|
|
407
|
+
## CRITICAL: Output Format
|
|
408
|
+
Output test files using this EXACT format:
|
|
409
|
+
|
|
410
|
+
\`\`\`
|
|
411
|
+
===FILE: path/to/test.spec.js
|
|
412
|
+
===ACTION: CREATE
|
|
413
|
+
\`\`\`javascript
|
|
414
|
+
// test file content
|
|
415
|
+
\`\`\`
|
|
416
|
+
===END_FILE
|
|
417
|
+
\`\`\``,
|
|
379
418
|
fix: `## Task
|
|
380
419
|
${task}
|
|
381
420
|
|
|
382
421
|
## Instructions
|
|
383
422
|
Analyze and fix the reported issue:
|
|
384
423
|
1. Identify the root cause
|
|
385
|
-
2. Implement the fix
|
|
386
|
-
3.
|
|
387
|
-
4.
|
|
424
|
+
2. Implement the MINIMAL fix required
|
|
425
|
+
3. Do NOT refactor unrelated code
|
|
426
|
+
4. Stay focused on the specific bug
|
|
427
|
+
|
|
428
|
+
## CRITICAL: Output Format
|
|
429
|
+
Output fixes using this EXACT format:
|
|
430
|
+
|
|
431
|
+
\`\`\`
|
|
432
|
+
===FILE: path/to/file.js
|
|
433
|
+
===ACTION: MODIFY
|
|
434
|
+
===FIND:
|
|
435
|
+
\`\`\`javascript
|
|
436
|
+
// exact buggy code
|
|
437
|
+
\`\`\`
|
|
438
|
+
===REPLACE:
|
|
439
|
+
\`\`\`javascript
|
|
440
|
+
// fixed code
|
|
441
|
+
\`\`\`
|
|
442
|
+
===END_FILE
|
|
443
|
+
\`\`\`
|
|
388
444
|
|
|
389
|
-
|
|
445
|
+
ONLY output changes needed for the fix. Do not touch unrelated code.`,
|
|
390
446
|
design: `## Task
|
|
391
447
|
${task}
|
|
392
448
|
|
|
@@ -480,6 +536,119 @@ ${content}
|
|
|
480
536
|
}
|
|
481
537
|
return runDir;
|
|
482
538
|
}
|
|
539
|
+
async applyCodeToRepo(state) {
|
|
540
|
+
const result = { created: [], modified: [], errors: [] };
|
|
541
|
+
for (const [key, content] of state.outputs) {
|
|
542
|
+
if (!key.includes('implementation') && !key.includes('code') && !key.includes('fix') && !key.includes('test')) {
|
|
543
|
+
continue;
|
|
544
|
+
}
|
|
545
|
+
const fileBlocks = content.split('===FILE:').slice(1);
|
|
546
|
+
for (const block of fileBlocks) {
|
|
547
|
+
try {
|
|
548
|
+
const endIndex = block.indexOf('===END_FILE');
|
|
549
|
+
const fileBlock = endIndex > -1 ? block.substring(0, endIndex) : block;
|
|
550
|
+
const filePathMatch = fileBlock.match(/^([^\n]+)/);
|
|
551
|
+
if (!filePathMatch)
|
|
552
|
+
continue;
|
|
553
|
+
const filePath = filePathMatch[1].trim();
|
|
554
|
+
if (filePath.includes('...') || filePath.includes('example') || !filePath.includes('.')) {
|
|
555
|
+
continue;
|
|
556
|
+
}
|
|
557
|
+
const normalizedPath = filePath.replace(/^\/+/, '').replace(/^\.\//, '');
|
|
558
|
+
const fullPath = path.join(this.repoPath, normalizedPath);
|
|
559
|
+
const actionMatch = fileBlock.match(/===ACTION:\s*(\w+)/);
|
|
560
|
+
const action = actionMatch ? actionMatch[1].toUpperCase() : 'CREATE';
|
|
561
|
+
if (action === 'CREATE') {
|
|
562
|
+
const codeMatch = fileBlock.match(/```(?:\w+)?\n([\s\S]*?)```/);
|
|
563
|
+
if (!codeMatch)
|
|
564
|
+
continue;
|
|
565
|
+
const code = codeMatch[1];
|
|
566
|
+
const dirPath = path.dirname(fullPath);
|
|
567
|
+
if (!fs.existsSync(dirPath)) {
|
|
568
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
569
|
+
}
|
|
570
|
+
fs.writeFileSync(fullPath, code.trim() + '\n');
|
|
571
|
+
result.created.push(normalizedPath);
|
|
572
|
+
}
|
|
573
|
+
else if (action === 'MODIFY') {
|
|
574
|
+
const findMatch = fileBlock.match(/===FIND:\s*```(?:\w+)?\n([\s\S]*?)```/);
|
|
575
|
+
const replaceMatch = fileBlock.match(/===REPLACE:\s*```(?:\w+)?\n([\s\S]*?)```/);
|
|
576
|
+
if (!findMatch || !replaceMatch) {
|
|
577
|
+
result.errors.push(`${normalizedPath}: Missing FIND or REPLACE block`);
|
|
578
|
+
continue;
|
|
579
|
+
}
|
|
580
|
+
const findCode = findMatch[1].trim();
|
|
581
|
+
const replaceCode = replaceMatch[1].trim();
|
|
582
|
+
if (!fs.existsSync(fullPath)) {
|
|
583
|
+
result.errors.push(`${normalizedPath}: File not found`);
|
|
584
|
+
continue;
|
|
585
|
+
}
|
|
586
|
+
let fileContent = fs.readFileSync(fullPath, 'utf-8');
|
|
587
|
+
if (fileContent.includes(findCode)) {
|
|
588
|
+
fileContent = fileContent.replace(findCode, replaceCode);
|
|
589
|
+
fs.writeFileSync(fullPath, fileContent);
|
|
590
|
+
result.modified.push(normalizedPath);
|
|
591
|
+
}
|
|
592
|
+
else {
|
|
593
|
+
const normalizedFind = findCode.replace(/\s+/g, ' ').trim();
|
|
594
|
+
const normalizedContent = fileContent.replace(/\s+/g, ' ');
|
|
595
|
+
if (normalizedContent.includes(normalizedFind)) {
|
|
596
|
+
const lines = fileContent.split('\n');
|
|
597
|
+
const findLines = findCode.split('\n');
|
|
598
|
+
let startLine = -1;
|
|
599
|
+
for (let i = 0; i <= lines.length - findLines.length; i++) {
|
|
600
|
+
const segment = lines.slice(i, i + findLines.length).join('\n');
|
|
601
|
+
if (segment.replace(/\s+/g, ' ').trim() === normalizedFind) {
|
|
602
|
+
startLine = i;
|
|
603
|
+
break;
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
if (startLine >= 0) {
|
|
607
|
+
lines.splice(startLine, findLines.length, ...replaceCode.split('\n'));
|
|
608
|
+
fs.writeFileSync(fullPath, lines.join('\n'));
|
|
609
|
+
result.modified.push(normalizedPath);
|
|
610
|
+
}
|
|
611
|
+
else {
|
|
612
|
+
result.errors.push(`${normalizedPath}: Could not locate code to replace`);
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
else {
|
|
616
|
+
result.errors.push(`${normalizedPath}: Code to replace not found`);
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
catch (error) {
|
|
622
|
+
result.errors.push(`Error processing block: ${error}`);
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
if (result.created.length === 0 && result.modified.length === 0) {
|
|
626
|
+
const legacyPattern = /###\s+([^\n]+\.[a-zA-Z]+)\s*\n```(?:\w+)?\n([\s\S]*?)```/g;
|
|
627
|
+
let match;
|
|
628
|
+
while ((match = legacyPattern.exec(content)) !== null) {
|
|
629
|
+
const filePath = match[1].trim();
|
|
630
|
+
const code = match[2];
|
|
631
|
+
if (filePath.includes('...') || filePath.includes('example') || filePath.startsWith('//')) {
|
|
632
|
+
continue;
|
|
633
|
+
}
|
|
634
|
+
const normalizedPath = filePath.replace(/^\/+/, '').replace(/^\.\//, '');
|
|
635
|
+
const fullPath = path.join(this.repoPath, normalizedPath);
|
|
636
|
+
const dirPath = path.dirname(fullPath);
|
|
637
|
+
try {
|
|
638
|
+
if (!fs.existsSync(dirPath)) {
|
|
639
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
640
|
+
}
|
|
641
|
+
fs.writeFileSync(fullPath, code.trim() + '\n');
|
|
642
|
+
result.created.push(normalizedPath);
|
|
643
|
+
}
|
|
644
|
+
catch (error) {
|
|
645
|
+
result.errors.push(`${normalizedPath}: ${error}`);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
return result;
|
|
651
|
+
}
|
|
483
652
|
async printSummary(state) {
|
|
484
653
|
console.log('');
|
|
485
654
|
if (state.errors.length > 0) {
|
|
@@ -491,6 +660,32 @@ ${content}
|
|
|
491
660
|
else {
|
|
492
661
|
console.log(chalk.green('✓ Workflow complete!'));
|
|
493
662
|
}
|
|
663
|
+
if (this.applyChanges && state.outputs.size > 0) {
|
|
664
|
+
console.log('');
|
|
665
|
+
console.log(chalk.cyan('Applying code changes to repository...'));
|
|
666
|
+
const result = await this.applyCodeToRepo(state);
|
|
667
|
+
if (result.created.length > 0) {
|
|
668
|
+
console.log(chalk.green(`✓ Created ${result.created.length} files:`));
|
|
669
|
+
for (const file of result.created) {
|
|
670
|
+
console.log(chalk.green(` + ${file}`));
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
if (result.modified.length > 0) {
|
|
674
|
+
console.log(chalk.blue(`✓ Modified ${result.modified.length} files:`));
|
|
675
|
+
for (const file of result.modified) {
|
|
676
|
+
console.log(chalk.blue(` ~ ${file}`));
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
if (result.errors.length > 0) {
|
|
680
|
+
console.log(chalk.yellow(`⚠ ${result.errors.length} issues:`));
|
|
681
|
+
for (const error of result.errors) {
|
|
682
|
+
console.log(chalk.yellow(` ! ${error}`));
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
if (result.created.length === 0 && result.modified.length === 0) {
|
|
686
|
+
console.log(chalk.yellow(' No code changes to apply'));
|
|
687
|
+
}
|
|
688
|
+
}
|
|
494
689
|
if (state.outputs.size > 0) {
|
|
495
690
|
const outputPath = await this.saveOutputs(state);
|
|
496
691
|
console.log('');
|