aided-dev 1.0.0
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/README.md +615 -0
- package/bin/aidev.js +11 -0
- package/dist/agent-creator/creator.d.ts +22 -0
- package/dist/agent-creator/creator.js +393 -0
- package/dist/agent-creator/index.d.ts +1 -0
- package/dist/agent-creator/index.js +1 -0
- package/dist/bmad/index.d.ts +1 -0
- package/dist/bmad/index.js +1 -0
- package/dist/bmad/loader.d.ts +68 -0
- package/dist/bmad/loader.js +482 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +257 -0
- package/dist/config.d.ts +23 -0
- package/dist/config.js +48 -0
- package/dist/discovery/detector.d.ts +12 -0
- package/dist/discovery/detector.js +272 -0
- package/dist/discovery/documents.d.ts +25 -0
- package/dist/discovery/documents.js +154 -0
- package/dist/discovery/index.d.ts +2 -0
- package/dist/discovery/index.js +2 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/orchestrator/index.d.ts +3 -0
- package/dist/orchestrator/index.js +3 -0
- package/dist/orchestrator/orchestrator.d.ts +40 -0
- package/dist/orchestrator/orchestrator.js +386 -0
- package/dist/orchestrator/planner.d.ts +44 -0
- package/dist/orchestrator/planner.js +355 -0
- package/dist/postinstall.d.ts +1 -0
- package/dist/postinstall.js +49 -0
- package/package.json +70 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import * as p from '@clack/prompts';
|
|
4
|
+
import { Orchestrator } from './orchestrator/index.js';
|
|
5
|
+
import { setApiKey, hasApiKey, getConfigPath, clearConfig, } from './config.js';
|
|
6
|
+
import { discoverProjectDocuments, detectStack, formatStack, } from './discovery/index.js';
|
|
7
|
+
import { AgentCreator, quickCreateAgent } from './agent-creator/index.js';
|
|
8
|
+
const program = new Command();
|
|
9
|
+
program
|
|
10
|
+
.name('aidev')
|
|
11
|
+
.description('AI Development Team Orchestrator - BMAD-METHOD integration')
|
|
12
|
+
.version('1.0.0');
|
|
13
|
+
program
|
|
14
|
+
.argument('[prompt]', 'Development task to execute')
|
|
15
|
+
.option('-m, --model <model>', 'Claude model to use', 'claude-sonnet-4-20250514')
|
|
16
|
+
.option('-v, --verbose', 'Show detailed output')
|
|
17
|
+
.option('-d, --dir <path>', 'Project directory', process.cwd())
|
|
18
|
+
.action(async (prompt, options) => {
|
|
19
|
+
if (!prompt) {
|
|
20
|
+
program.help();
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
if (!hasApiKey()) {
|
|
24
|
+
console.log('');
|
|
25
|
+
console.log(chalk.yellow('No API key configured.'));
|
|
26
|
+
console.log('');
|
|
27
|
+
const apiKey = await p.text({
|
|
28
|
+
message: 'Enter your Anthropic API key:',
|
|
29
|
+
placeholder: 'sk-ant-...',
|
|
30
|
+
validate: (value) => {
|
|
31
|
+
if (!value)
|
|
32
|
+
return 'API key is required';
|
|
33
|
+
if (!value.startsWith('sk-ant-'))
|
|
34
|
+
return 'Invalid API key format';
|
|
35
|
+
return undefined;
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
if (p.isCancel(apiKey)) {
|
|
39
|
+
console.log(chalk.red('Cancelled'));
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
setApiKey(apiKey);
|
|
43
|
+
console.log(chalk.green('API key saved!'));
|
|
44
|
+
console.log('');
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
const orchestrator = new Orchestrator({
|
|
48
|
+
repoPath: options.dir,
|
|
49
|
+
model: options.model,
|
|
50
|
+
verbose: options.verbose,
|
|
51
|
+
});
|
|
52
|
+
await orchestrator.run(prompt);
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
console.error(chalk.red('Error:'), error);
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
program
|
|
60
|
+
.command('config')
|
|
61
|
+
.description('Configure aidev settings')
|
|
62
|
+
.option('--api-key <key>', 'Set Anthropic API key')
|
|
63
|
+
.option('--show', 'Show current configuration')
|
|
64
|
+
.option('--clear', 'Clear all configuration')
|
|
65
|
+
.action(async (options) => {
|
|
66
|
+
if (options.apiKey) {
|
|
67
|
+
setApiKey(options.apiKey);
|
|
68
|
+
console.log(chalk.green('API key saved!'));
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
if (options.clear) {
|
|
72
|
+
clearConfig();
|
|
73
|
+
console.log(chalk.green('Configuration cleared.'));
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (options.show) {
|
|
77
|
+
console.log('');
|
|
78
|
+
console.log(chalk.bold('Configuration:'));
|
|
79
|
+
console.log(` Path: ${getConfigPath()}`);
|
|
80
|
+
console.log(` API Key: ${hasApiKey() ? chalk.green('configured') : chalk.yellow('not set')}`);
|
|
81
|
+
console.log('');
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
console.log('');
|
|
85
|
+
p.intro(chalk.bgCyan.black(' aidev Configuration '));
|
|
86
|
+
const apiKey = await p.text({
|
|
87
|
+
message: 'Anthropic API key:',
|
|
88
|
+
placeholder: hasApiKey() ? '(already configured)' : 'sk-ant-...',
|
|
89
|
+
validate: (value) => {
|
|
90
|
+
if (!value && !hasApiKey())
|
|
91
|
+
return 'API key is required';
|
|
92
|
+
if (value && !value.startsWith('sk-ant-'))
|
|
93
|
+
return 'Invalid format';
|
|
94
|
+
return undefined;
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
if (!p.isCancel(apiKey) && apiKey) {
|
|
98
|
+
setApiKey(apiKey);
|
|
99
|
+
console.log(chalk.green('API key saved!'));
|
|
100
|
+
}
|
|
101
|
+
p.outro('Configuration complete!');
|
|
102
|
+
});
|
|
103
|
+
program
|
|
104
|
+
.command('analyze')
|
|
105
|
+
.description('Analyze a repository and show detected context')
|
|
106
|
+
.argument('[path]', 'Repository path', process.cwd())
|
|
107
|
+
.action(async (repoPath) => {
|
|
108
|
+
console.log('');
|
|
109
|
+
console.log(chalk.bold('Analyzing repository:'), repoPath);
|
|
110
|
+
console.log('');
|
|
111
|
+
const stack = await detectStack(repoPath);
|
|
112
|
+
console.log(chalk.cyan('Technology Stack:'));
|
|
113
|
+
console.log(formatStack(stack).split('\n').map(l => ` ${l}`).join('\n'));
|
|
114
|
+
console.log('');
|
|
115
|
+
const docs = await discoverProjectDocuments(repoPath);
|
|
116
|
+
if (docs) {
|
|
117
|
+
console.log(chalk.cyan('Source of Truth Documents:'));
|
|
118
|
+
console.log(` Project: ${docs.projectName}`);
|
|
119
|
+
if (docs.brownfieldArchitecture) {
|
|
120
|
+
console.log(chalk.green(` ✓ ${docs.projectName}-brownfield-architecture.md`));
|
|
121
|
+
}
|
|
122
|
+
if (docs.integrationMap) {
|
|
123
|
+
console.log(chalk.green(` ✓ ${docs.projectName}-integration-map.md`));
|
|
124
|
+
}
|
|
125
|
+
if (docs.systemAnalysis) {
|
|
126
|
+
console.log(chalk.green(` ✓ ${docs.projectName}-system-analysis.md`));
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
console.log(chalk.yellow('No source-of-truth documents found.'));
|
|
131
|
+
console.log(chalk.dim('Create these files for better context:'));
|
|
132
|
+
console.log(chalk.dim(' - <project>-brownfield-architecture.md'));
|
|
133
|
+
console.log(chalk.dim(' - <project>-integration-map.md'));
|
|
134
|
+
console.log(chalk.dim(' - <project>-system-analysis.md'));
|
|
135
|
+
}
|
|
136
|
+
console.log('');
|
|
137
|
+
});
|
|
138
|
+
program
|
|
139
|
+
.command('agents')
|
|
140
|
+
.description('Show information about AI agents (loaded from BMAD-METHOD)')
|
|
141
|
+
.action(async () => {
|
|
142
|
+
const { getLoader } = await import('./bmad/index.js');
|
|
143
|
+
const loader = getLoader();
|
|
144
|
+
console.log('');
|
|
145
|
+
console.log(chalk.bold('AI Development Team (BMAD Agents)'));
|
|
146
|
+
if (loader.isAvailable()) {
|
|
147
|
+
console.log(chalk.dim(`Loaded from: ${loader.getPath()}`));
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
console.log(chalk.yellow('BMAD-METHOD not found, using fallback agents'));
|
|
151
|
+
}
|
|
152
|
+
console.log('');
|
|
153
|
+
const agentNames = ['analyst', 'architect', 'dev', 'tea'];
|
|
154
|
+
const agents = await loader.loadAgents(agentNames);
|
|
155
|
+
for (const [name, agent] of agents) {
|
|
156
|
+
console.log(`${agent.icon} ${chalk.cyan(agent.name)} - ${agent.title}`);
|
|
157
|
+
console.log(chalk.dim(` Role: ${agent.persona.role}`));
|
|
158
|
+
console.log('');
|
|
159
|
+
}
|
|
160
|
+
const allAgents = await loader.listAgents();
|
|
161
|
+
if (allAgents.length > agentNames.length) {
|
|
162
|
+
console.log(chalk.dim(`Other available agents: ${allAgents.filter(a => !agentNames.includes(a)).join(', ')}`));
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
program
|
|
166
|
+
.command('create-agent')
|
|
167
|
+
.description('Create a new BMAD agent using the agent-builder (Bond)')
|
|
168
|
+
.option('-q, --quick', 'Quick mode - guided prompts without AI conversation')
|
|
169
|
+
.option('-o, --output <path>', 'Output directory for the agent file', process.cwd())
|
|
170
|
+
.option('-m, --model <model>', 'Claude model to use', 'claude-sonnet-4-20250514')
|
|
171
|
+
.option('-v, --verbose', 'Show detailed output')
|
|
172
|
+
.action(async (options) => {
|
|
173
|
+
if (!hasApiKey()) {
|
|
174
|
+
console.log('');
|
|
175
|
+
console.log(chalk.yellow('No API key configured.'));
|
|
176
|
+
console.log('');
|
|
177
|
+
const apiKey = await p.text({
|
|
178
|
+
message: 'Enter your Anthropic API key:',
|
|
179
|
+
placeholder: 'sk-ant-...',
|
|
180
|
+
validate: (value) => {
|
|
181
|
+
if (!value)
|
|
182
|
+
return 'API key is required';
|
|
183
|
+
if (!value.startsWith('sk-ant-'))
|
|
184
|
+
return 'Invalid API key format';
|
|
185
|
+
return undefined;
|
|
186
|
+
},
|
|
187
|
+
});
|
|
188
|
+
if (p.isCancel(apiKey)) {
|
|
189
|
+
console.log(chalk.red('Cancelled'));
|
|
190
|
+
process.exit(1);
|
|
191
|
+
}
|
|
192
|
+
setApiKey(apiKey);
|
|
193
|
+
console.log(chalk.green('API key saved!'));
|
|
194
|
+
console.log('');
|
|
195
|
+
}
|
|
196
|
+
try {
|
|
197
|
+
if (options.quick) {
|
|
198
|
+
await quickCreateAgent({
|
|
199
|
+
outputDir: options.output,
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
const creator = new AgentCreator({
|
|
204
|
+
outputDir: options.output,
|
|
205
|
+
model: options.model,
|
|
206
|
+
verbose: options.verbose,
|
|
207
|
+
});
|
|
208
|
+
await creator.create();
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
catch (error) {
|
|
212
|
+
console.error(chalk.red('Error:'), error);
|
|
213
|
+
process.exit(1);
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
program
|
|
217
|
+
.command('help-examples')
|
|
218
|
+
.description('Show usage examples')
|
|
219
|
+
.action(() => {
|
|
220
|
+
console.log('');
|
|
221
|
+
console.log(chalk.bold('aidev - AI Development Team Orchestrator'));
|
|
222
|
+
console.log('');
|
|
223
|
+
console.log(chalk.cyan('Quick Start:'));
|
|
224
|
+
console.log(' aidev config --api-key sk-ant-xxx Set your API key');
|
|
225
|
+
console.log(' aidev "Add user authentication" Run a task');
|
|
226
|
+
console.log('');
|
|
227
|
+
console.log(chalk.cyan('Common Tasks:'));
|
|
228
|
+
console.log(chalk.dim(' # New feature'));
|
|
229
|
+
console.log(' aidev "Add password reset with email verification"');
|
|
230
|
+
console.log('');
|
|
231
|
+
console.log(chalk.dim(' # Bug fix'));
|
|
232
|
+
console.log(' aidev "Fix: login fails after password change"');
|
|
233
|
+
console.log('');
|
|
234
|
+
console.log(chalk.dim(' # Refactoring'));
|
|
235
|
+
console.log(' aidev "Refactor auth module to use dependency injection"');
|
|
236
|
+
console.log('');
|
|
237
|
+
console.log(chalk.dim(' # Testing'));
|
|
238
|
+
console.log(' aidev "Add unit tests for the user service"');
|
|
239
|
+
console.log('');
|
|
240
|
+
console.log(chalk.dim(' # Documentation'));
|
|
241
|
+
console.log(' aidev "Document all REST API endpoints"');
|
|
242
|
+
console.log('');
|
|
243
|
+
console.log(chalk.cyan('Options:'));
|
|
244
|
+
console.log(' -v, --verbose Show detailed agent output');
|
|
245
|
+
console.log(' -d, --dir <path> Run in specific directory');
|
|
246
|
+
console.log(' -m, --model <model> Use different Claude model');
|
|
247
|
+
console.log('');
|
|
248
|
+
console.log(chalk.cyan('Other Commands:'));
|
|
249
|
+
console.log(' aidev analyze Analyze project context');
|
|
250
|
+
console.log(' aidev agents List available AI agents');
|
|
251
|
+
console.log(' aidev create-agent Create a custom agent');
|
|
252
|
+
console.log(' aidev config --show View configuration');
|
|
253
|
+
console.log('');
|
|
254
|
+
console.log(chalk.dim('Full documentation: https://github.com/shikharsworks/aided#readme'));
|
|
255
|
+
console.log('');
|
|
256
|
+
});
|
|
257
|
+
program.parse();
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
declare const ConfigSchema: z.ZodObject<{
|
|
3
|
+
anthropicApiKey: z.ZodOptional<z.ZodString>;
|
|
4
|
+
defaultModel: z.ZodDefault<z.ZodEnum<["claude-sonnet-4-20250514", "claude-opus-4-20250514"]>>;
|
|
5
|
+
autoCommit: z.ZodDefault<z.ZodBoolean>;
|
|
6
|
+
}, "strip", z.ZodTypeAny, {
|
|
7
|
+
defaultModel: "claude-sonnet-4-20250514" | "claude-opus-4-20250514";
|
|
8
|
+
autoCommit: boolean;
|
|
9
|
+
anthropicApiKey?: string | undefined;
|
|
10
|
+
}, {
|
|
11
|
+
anthropicApiKey?: string | undefined;
|
|
12
|
+
defaultModel?: "claude-sonnet-4-20250514" | "claude-opus-4-20250514" | undefined;
|
|
13
|
+
autoCommit?: boolean | undefined;
|
|
14
|
+
}>;
|
|
15
|
+
export type Config = z.infer<typeof ConfigSchema>;
|
|
16
|
+
export declare function getConfig(): Config;
|
|
17
|
+
export declare function setApiKey(apiKey: string): void;
|
|
18
|
+
export declare function getApiKey(): string | undefined;
|
|
19
|
+
export declare function hasApiKey(): boolean;
|
|
20
|
+
export declare function setDefaultModel(model: string): void;
|
|
21
|
+
export declare function clearConfig(): void;
|
|
22
|
+
export declare function getConfigPath(): string;
|
|
23
|
+
export {};
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import Conf from 'conf';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
const ConfigSchema = z.object({
|
|
4
|
+
anthropicApiKey: z.string().optional(),
|
|
5
|
+
defaultModel: z.enum(['claude-sonnet-4-20250514', 'claude-opus-4-20250514']).default('claude-sonnet-4-20250514'),
|
|
6
|
+
autoCommit: z.boolean().default(true),
|
|
7
|
+
});
|
|
8
|
+
const config = new Conf({
|
|
9
|
+
projectName: 'aidev',
|
|
10
|
+
schema: {
|
|
11
|
+
anthropicApiKey: {
|
|
12
|
+
type: 'string',
|
|
13
|
+
},
|
|
14
|
+
defaultModel: {
|
|
15
|
+
type: 'string',
|
|
16
|
+
default: 'claude-sonnet-4-20250514',
|
|
17
|
+
},
|
|
18
|
+
autoCommit: {
|
|
19
|
+
type: 'boolean',
|
|
20
|
+
default: true,
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
export function getConfig() {
|
|
25
|
+
return {
|
|
26
|
+
anthropicApiKey: config.get('anthropicApiKey'),
|
|
27
|
+
defaultModel: config.get('defaultModel') || 'claude-sonnet-4-20250514',
|
|
28
|
+
autoCommit: config.get('autoCommit') ?? true,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
export function setApiKey(apiKey) {
|
|
32
|
+
config.set('anthropicApiKey', apiKey);
|
|
33
|
+
}
|
|
34
|
+
export function getApiKey() {
|
|
35
|
+
return process.env.ANTHROPIC_API_KEY || config.get('anthropicApiKey');
|
|
36
|
+
}
|
|
37
|
+
export function hasApiKey() {
|
|
38
|
+
return !!getApiKey();
|
|
39
|
+
}
|
|
40
|
+
export function setDefaultModel(model) {
|
|
41
|
+
config.set('defaultModel', model);
|
|
42
|
+
}
|
|
43
|
+
export function clearConfig() {
|
|
44
|
+
config.clear();
|
|
45
|
+
}
|
|
46
|
+
export function getConfigPath() {
|
|
47
|
+
return config.path;
|
|
48
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface ProjectStack {
|
|
2
|
+
language: string;
|
|
3
|
+
languageVersion?: string;
|
|
4
|
+
framework?: string;
|
|
5
|
+
frameworkVersion?: string;
|
|
6
|
+
packageManager?: string;
|
|
7
|
+
testFramework?: string;
|
|
8
|
+
database?: string;
|
|
9
|
+
dependencies: string[];
|
|
10
|
+
}
|
|
11
|
+
export declare function detectStack(repoPath: string): Promise<ProjectStack>;
|
|
12
|
+
export declare function formatStack(stack: ProjectStack): string;
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
const LANGUAGE_MARKERS = {
|
|
4
|
+
python: {
|
|
5
|
+
files: ['pyproject.toml', 'setup.py', 'requirements.txt', 'Pipfile'],
|
|
6
|
+
language: 'python',
|
|
7
|
+
},
|
|
8
|
+
javascript: {
|
|
9
|
+
files: ['package.json'],
|
|
10
|
+
language: 'javascript',
|
|
11
|
+
},
|
|
12
|
+
typescript: {
|
|
13
|
+
files: ['tsconfig.json'],
|
|
14
|
+
language: 'typescript',
|
|
15
|
+
},
|
|
16
|
+
go: {
|
|
17
|
+
files: ['go.mod', 'go.sum'],
|
|
18
|
+
language: 'go',
|
|
19
|
+
},
|
|
20
|
+
rust: {
|
|
21
|
+
files: ['Cargo.toml'],
|
|
22
|
+
language: 'rust',
|
|
23
|
+
},
|
|
24
|
+
java: {
|
|
25
|
+
files: ['pom.xml', 'build.gradle', 'build.gradle.kts'],
|
|
26
|
+
language: 'java',
|
|
27
|
+
},
|
|
28
|
+
ruby: {
|
|
29
|
+
files: ['Gemfile', 'Rakefile'],
|
|
30
|
+
language: 'ruby',
|
|
31
|
+
},
|
|
32
|
+
php: {
|
|
33
|
+
files: ['composer.json'],
|
|
34
|
+
language: 'php',
|
|
35
|
+
},
|
|
36
|
+
csharp: {
|
|
37
|
+
files: ['*.csproj', '*.sln'],
|
|
38
|
+
language: 'csharp',
|
|
39
|
+
},
|
|
40
|
+
kotlin: {
|
|
41
|
+
files: ['build.gradle.kts'],
|
|
42
|
+
language: 'kotlin',
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
const FRAMEWORK_DETECTION = {
|
|
46
|
+
python: {
|
|
47
|
+
fastapi: 'fastapi',
|
|
48
|
+
flask: 'flask',
|
|
49
|
+
django: 'django',
|
|
50
|
+
starlette: 'starlette',
|
|
51
|
+
tornado: 'tornado',
|
|
52
|
+
aiohttp: 'aiohttp',
|
|
53
|
+
},
|
|
54
|
+
javascript: {
|
|
55
|
+
express: 'express',
|
|
56
|
+
next: 'next.js',
|
|
57
|
+
react: 'react',
|
|
58
|
+
vue: 'vue',
|
|
59
|
+
angular: 'angular',
|
|
60
|
+
nestjs: 'nestjs',
|
|
61
|
+
koa: 'koa',
|
|
62
|
+
hapi: 'hapi',
|
|
63
|
+
},
|
|
64
|
+
typescript: {
|
|
65
|
+
express: 'express',
|
|
66
|
+
next: 'next.js',
|
|
67
|
+
react: 'react',
|
|
68
|
+
vue: 'vue',
|
|
69
|
+
angular: 'angular',
|
|
70
|
+
nestjs: 'nestjs',
|
|
71
|
+
},
|
|
72
|
+
go: {
|
|
73
|
+
gin: 'gin',
|
|
74
|
+
echo: 'echo',
|
|
75
|
+
fiber: 'fiber',
|
|
76
|
+
chi: 'chi',
|
|
77
|
+
mux: 'gorilla/mux',
|
|
78
|
+
},
|
|
79
|
+
rust: {
|
|
80
|
+
actix: 'actix-web',
|
|
81
|
+
axum: 'axum',
|
|
82
|
+
rocket: 'rocket',
|
|
83
|
+
warp: 'warp',
|
|
84
|
+
},
|
|
85
|
+
java: {
|
|
86
|
+
spring: 'spring-boot',
|
|
87
|
+
quarkus: 'quarkus',
|
|
88
|
+
micronaut: 'micronaut',
|
|
89
|
+
},
|
|
90
|
+
ruby: {
|
|
91
|
+
rails: 'rails',
|
|
92
|
+
sinatra: 'sinatra',
|
|
93
|
+
hanami: 'hanami',
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
export async function detectStack(repoPath) {
|
|
97
|
+
const stack = {
|
|
98
|
+
language: 'unknown',
|
|
99
|
+
dependencies: [],
|
|
100
|
+
};
|
|
101
|
+
for (const [, config] of Object.entries(LANGUAGE_MARKERS)) {
|
|
102
|
+
for (const file of config.files) {
|
|
103
|
+
const filePath = path.join(repoPath, file);
|
|
104
|
+
if (await fs.pathExists(filePath)) {
|
|
105
|
+
stack.language = config.language;
|
|
106
|
+
break;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (stack.language !== 'unknown')
|
|
110
|
+
break;
|
|
111
|
+
}
|
|
112
|
+
switch (stack.language) {
|
|
113
|
+
case 'python':
|
|
114
|
+
await detectPythonStack(repoPath, stack);
|
|
115
|
+
break;
|
|
116
|
+
case 'javascript':
|
|
117
|
+
case 'typescript':
|
|
118
|
+
await detectNodeStack(repoPath, stack);
|
|
119
|
+
break;
|
|
120
|
+
case 'go':
|
|
121
|
+
await detectGoStack(repoPath, stack);
|
|
122
|
+
break;
|
|
123
|
+
case 'rust':
|
|
124
|
+
await detectRustStack(repoPath, stack);
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
127
|
+
return stack;
|
|
128
|
+
}
|
|
129
|
+
async function detectPythonStack(repoPath, stack) {
|
|
130
|
+
const pyprojectPath = path.join(repoPath, 'pyproject.toml');
|
|
131
|
+
if (await fs.pathExists(pyprojectPath)) {
|
|
132
|
+
try {
|
|
133
|
+
const content = await fs.readFile(pyprojectPath, 'utf-8');
|
|
134
|
+
const versionMatch = content.match(/python\s*=\s*["']([^"']+)["']/);
|
|
135
|
+
if (versionMatch) {
|
|
136
|
+
stack.languageVersion = versionMatch[1];
|
|
137
|
+
}
|
|
138
|
+
const depsMatch = content.match(/dependencies\s*=\s*\[([\s\S]*?)\]/);
|
|
139
|
+
if (depsMatch) {
|
|
140
|
+
const deps = depsMatch[1].match(/["']([^"']+)["']/g);
|
|
141
|
+
if (deps) {
|
|
142
|
+
stack.dependencies = deps.map((d) => d.replace(/["']/g, '').split(/[<>=]/)[0]);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
const frameworks = FRAMEWORK_DETECTION.python;
|
|
146
|
+
for (const [dep, framework] of Object.entries(frameworks)) {
|
|
147
|
+
if (stack.dependencies.some((d) => d.toLowerCase().includes(dep))) {
|
|
148
|
+
stack.framework = framework;
|
|
149
|
+
break;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
if (stack.dependencies.includes('pytest')) {
|
|
153
|
+
stack.testFramework = 'pytest';
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
stack.packageManager = 'pip';
|
|
160
|
+
}
|
|
161
|
+
async function detectNodeStack(repoPath, stack) {
|
|
162
|
+
const packageJsonPath = path.join(repoPath, 'package.json');
|
|
163
|
+
if (await fs.pathExists(packageJsonPath)) {
|
|
164
|
+
try {
|
|
165
|
+
const pkg = await fs.readJson(packageJsonPath);
|
|
166
|
+
const allDeps = {
|
|
167
|
+
...pkg.dependencies,
|
|
168
|
+
...pkg.devDependencies,
|
|
169
|
+
};
|
|
170
|
+
stack.dependencies = Object.keys(allDeps);
|
|
171
|
+
if (allDeps.typescript || (await fs.pathExists(path.join(repoPath, 'tsconfig.json')))) {
|
|
172
|
+
stack.language = 'typescript';
|
|
173
|
+
}
|
|
174
|
+
const frameworks = FRAMEWORK_DETECTION[stack.language];
|
|
175
|
+
for (const [dep, framework] of Object.entries(frameworks)) {
|
|
176
|
+
if (allDeps[dep]) {
|
|
177
|
+
stack.framework = framework;
|
|
178
|
+
stack.frameworkVersion = allDeps[dep];
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
if (allDeps.jest)
|
|
183
|
+
stack.testFramework = 'jest';
|
|
184
|
+
else if (allDeps.vitest)
|
|
185
|
+
stack.testFramework = 'vitest';
|
|
186
|
+
else if (allDeps.mocha)
|
|
187
|
+
stack.testFramework = 'mocha';
|
|
188
|
+
if (await fs.pathExists(path.join(repoPath, 'pnpm-lock.yaml'))) {
|
|
189
|
+
stack.packageManager = 'pnpm';
|
|
190
|
+
}
|
|
191
|
+
else if (await fs.pathExists(path.join(repoPath, 'yarn.lock'))) {
|
|
192
|
+
stack.packageManager = 'yarn';
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
stack.packageManager = 'npm';
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
catch {
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
async function detectGoStack(repoPath, stack) {
|
|
203
|
+
const goModPath = path.join(repoPath, 'go.mod');
|
|
204
|
+
if (await fs.pathExists(goModPath)) {
|
|
205
|
+
try {
|
|
206
|
+
const content = await fs.readFile(goModPath, 'utf-8');
|
|
207
|
+
const versionMatch = content.match(/go\s+(\d+\.\d+)/);
|
|
208
|
+
if (versionMatch) {
|
|
209
|
+
stack.languageVersion = versionMatch[1];
|
|
210
|
+
}
|
|
211
|
+
const requireMatch = content.match(/require\s*\(([\s\S]*?)\)/);
|
|
212
|
+
if (requireMatch) {
|
|
213
|
+
const deps = requireMatch[1].match(/(\S+)\s+v/g);
|
|
214
|
+
if (deps) {
|
|
215
|
+
stack.dependencies = deps.map((d) => d.replace(/\s+v$/, ''));
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
const frameworks = FRAMEWORK_DETECTION.go;
|
|
219
|
+
for (const [dep, framework] of Object.entries(frameworks)) {
|
|
220
|
+
if (stack.dependencies.some((d) => d.includes(dep))) {
|
|
221
|
+
stack.framework = framework;
|
|
222
|
+
break;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
catch {
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
stack.packageManager = 'go mod';
|
|
230
|
+
}
|
|
231
|
+
async function detectRustStack(repoPath, stack) {
|
|
232
|
+
const cargoPath = path.join(repoPath, 'Cargo.toml');
|
|
233
|
+
if (await fs.pathExists(cargoPath)) {
|
|
234
|
+
try {
|
|
235
|
+
const content = await fs.readFile(cargoPath, 'utf-8');
|
|
236
|
+
const depsMatch = content.match(/\[dependencies\]([\s\S]*?)(?:\[|$)/);
|
|
237
|
+
if (depsMatch) {
|
|
238
|
+
const deps = depsMatch[1].match(/^(\w[\w-]*)\s*=/gm);
|
|
239
|
+
if (deps) {
|
|
240
|
+
stack.dependencies = deps.map((d) => d.replace(/\s*=$/, ''));
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
const frameworks = FRAMEWORK_DETECTION.rust;
|
|
244
|
+
for (const [dep, framework] of Object.entries(frameworks)) {
|
|
245
|
+
if (stack.dependencies.some((d) => d.includes(dep))) {
|
|
246
|
+
stack.framework = framework;
|
|
247
|
+
break;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
catch {
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
stack.packageManager = 'cargo';
|
|
255
|
+
}
|
|
256
|
+
export function formatStack(stack) {
|
|
257
|
+
const parts = [];
|
|
258
|
+
parts.push(`Language: ${stack.language}`);
|
|
259
|
+
if (stack.languageVersion) {
|
|
260
|
+
parts.push(`Version: ${stack.languageVersion}`);
|
|
261
|
+
}
|
|
262
|
+
if (stack.framework) {
|
|
263
|
+
parts.push(`Framework: ${stack.framework}`);
|
|
264
|
+
}
|
|
265
|
+
if (stack.packageManager) {
|
|
266
|
+
parts.push(`Package Manager: ${stack.packageManager}`);
|
|
267
|
+
}
|
|
268
|
+
if (stack.testFramework) {
|
|
269
|
+
parts.push(`Test Framework: ${stack.testFramework}`);
|
|
270
|
+
}
|
|
271
|
+
return parts.join('\n');
|
|
272
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export interface ProjectDocuments {
|
|
2
|
+
projectName: string;
|
|
3
|
+
brownfieldArchitecture?: {
|
|
4
|
+
path: string;
|
|
5
|
+
content: string;
|
|
6
|
+
};
|
|
7
|
+
integrationMap?: {
|
|
8
|
+
path: string;
|
|
9
|
+
content: string;
|
|
10
|
+
};
|
|
11
|
+
systemAnalysis?: {
|
|
12
|
+
path: string;
|
|
13
|
+
content: string;
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
export declare const PROJECT_DOC_PATTERNS: {
|
|
17
|
+
readonly brownfieldArchitecture: "{project}-brownfield-architecture.md";
|
|
18
|
+
readonly integrationMap: "{project}-integration-map.md";
|
|
19
|
+
readonly systemAnalysis: "{project}-system-analysis.md";
|
|
20
|
+
};
|
|
21
|
+
export declare function detectProjectName(repoPath: string): Promise<string | undefined>;
|
|
22
|
+
export declare function findProjectNameFromDocs(repoPath: string): Promise<string | undefined>;
|
|
23
|
+
export declare function discoverProjectDocuments(repoPath: string): Promise<ProjectDocuments | undefined>;
|
|
24
|
+
export declare function hasDocuments(docs: ProjectDocuments | undefined): boolean;
|
|
25
|
+
export declare function formatDocumentsForPrompt(docs: ProjectDocuments): string;
|