feature-architect-agent 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.
Files changed (49) hide show
  1. package/README.md +704 -0
  2. package/bin/feature-architect.js +2 -0
  3. package/dist/cli/index.d.ts +3 -0
  4. package/dist/cli/index.d.ts.map +1 -0
  5. package/dist/cli/index.js +63 -0
  6. package/dist/commands/init.d.ts +2 -0
  7. package/dist/commands/init.d.ts.map +1 -0
  8. package/dist/commands/init.js +125 -0
  9. package/dist/commands/plan.d.ts +6 -0
  10. package/dist/commands/plan.d.ts.map +1 -0
  11. package/dist/commands/plan.js +147 -0
  12. package/dist/commands/verify.d.ts +2 -0
  13. package/dist/commands/verify.d.ts.map +1 -0
  14. package/dist/commands/verify.js +101 -0
  15. package/dist/config/api.config.d.ts +49 -0
  16. package/dist/config/api.config.d.ts.map +1 -0
  17. package/dist/config/api.config.js +78 -0
  18. package/dist/llm/Claude.d.ts +8 -0
  19. package/dist/llm/Claude.d.ts.map +1 -0
  20. package/dist/llm/Claude.js +44 -0
  21. package/dist/llm/OpenAI.d.ts +8 -0
  22. package/dist/llm/OpenAI.d.ts.map +1 -0
  23. package/dist/llm/OpenAI.js +43 -0
  24. package/dist/llm/factory.d.ts +9 -0
  25. package/dist/llm/factory.d.ts.map +1 -0
  26. package/dist/llm/factory.js +36 -0
  27. package/dist/llm/types.d.ts +8 -0
  28. package/dist/llm/types.d.ts.map +1 -0
  29. package/dist/llm/types.js +2 -0
  30. package/dist/services/Analyzer.d.ts +18 -0
  31. package/dist/services/Analyzer.d.ts.map +1 -0
  32. package/dist/services/Analyzer.js +235 -0
  33. package/dist/services/ContextManager.d.ts +16 -0
  34. package/dist/services/ContextManager.d.ts.map +1 -0
  35. package/dist/services/ContextManager.js +82 -0
  36. package/dist/services/Planner.d.ts +16 -0
  37. package/dist/services/Planner.d.ts.map +1 -0
  38. package/dist/services/Planner.js +181 -0
  39. package/dist/services/Scanner.d.ts +17 -0
  40. package/dist/services/Scanner.d.ts.map +1 -0
  41. package/dist/services/Scanner.js +75 -0
  42. package/dist/types/index.d.ts +90 -0
  43. package/dist/types/index.d.ts.map +1 -0
  44. package/dist/types/index.js +3 -0
  45. package/dist/utils/logger.d.ts +22 -0
  46. package/dist/utils/logger.d.ts.map +1 -0
  47. package/dist/utils/logger.js +56 -0
  48. package/package.json +48 -0
  49. package/src/config/api.config.ts +79 -0
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OpenAIProvider = void 0;
4
+ class OpenAIProvider {
5
+ apiKey;
6
+ model;
7
+ constructor(apiKey, model = 'gpt-4-turbo') {
8
+ this.apiKey = apiKey;
9
+ this.model = model;
10
+ }
11
+ async generate(prompt, options = {}) {
12
+ const temperature = options.temperature ?? 0.7;
13
+ const maxTokens = options.maxTokens ?? 4000;
14
+ const response = await fetch('https://api.openai.com/v1/chat/completions', {
15
+ method: 'POST',
16
+ headers: {
17
+ 'Authorization': `Bearer ${this.apiKey}`,
18
+ 'Content-Type': 'application/json'
19
+ },
20
+ body: JSON.stringify({
21
+ model: this.model,
22
+ messages: [
23
+ {
24
+ role: 'user',
25
+ content: prompt
26
+ }
27
+ ],
28
+ temperature,
29
+ max_tokens: maxTokens
30
+ })
31
+ });
32
+ if (!response.ok) {
33
+ const error = await response.text();
34
+ throw new Error(`OpenAI API error: ${response.status} ${error}`);
35
+ }
36
+ const data = await response.json();
37
+ if (data.error) {
38
+ throw new Error(`OpenAI API error: ${data.error.message}`);
39
+ }
40
+ return data.choices[0].message.content;
41
+ }
42
+ }
43
+ exports.OpenAIProvider = OpenAIProvider;
@@ -0,0 +1,9 @@
1
+ import type { LLMProvider } from './types.js';
2
+ export type ProviderType = 'claude' | 'anthropic' | 'openai' | 'gemini' | 'google' | 'opencode' | 'ollama';
3
+ export interface ProviderConfig {
4
+ provider: ProviderType;
5
+ apiKey?: string;
6
+ model?: string;
7
+ }
8
+ export declare function createProvider(config: ProviderConfig): LLMProvider;
9
+ //# sourceMappingURL=factory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../src/llm/factory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAI9C,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,WAAW,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,UAAU,GAAG,QAAQ,CAAC;AAE3G,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,YAAY,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAWD,wBAAgB,cAAc,CAAC,MAAM,EAAE,cAAc,GAAG,WAAW,CAmClE"}
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createProvider = createProvider;
4
+ const Claude_js_1 = require("./Claude.js");
5
+ const OpenAI_js_1 = require("./OpenAI.js");
6
+ /**
7
+ * Normalize provider name - accepts both 'claude'/'anthropic' and 'gemini'/'google'
8
+ */
9
+ function normalizeProvider(provider) {
10
+ if (provider === 'anthropic')
11
+ return 'claude';
12
+ if (provider === 'google')
13
+ return 'gemini';
14
+ return provider;
15
+ }
16
+ function createProvider(config) {
17
+ const normalizedProvider = normalizeProvider(config.provider);
18
+ const apiKey = config.apiKey || process.env.ANTHROPIC_API_KEY || process.env.OPENAI_API_KEY;
19
+ if (!apiKey && ['claude', 'openai'].includes(normalizedProvider)) {
20
+ throw new Error(`API key required for ${config.provider}. Set ${config.provider.toUpperCase()}_API_KEY environment variable.`);
21
+ }
22
+ switch (normalizedProvider) {
23
+ case 'claude':
24
+ return new Claude_js_1.ClaudeProvider(apiKey, config.model || 'claude-3-5-sonnet-20241022');
25
+ case 'openai':
26
+ return new OpenAI_js_1.OpenAIProvider(apiKey, config.model || 'gpt-4o');
27
+ case 'gemini':
28
+ throw new Error('Gemini provider not yet implemented');
29
+ case 'opencode':
30
+ throw new Error('OpenCode provider not yet implemented');
31
+ case 'ollama':
32
+ throw new Error('Ollama provider not yet implemented');
33
+ default:
34
+ throw new Error(`Unknown provider: ${config.provider}`);
35
+ }
36
+ }
@@ -0,0 +1,8 @@
1
+ export interface LLMProvider {
2
+ generate(prompt: string, options?: GenerateOptions): Promise<string>;
3
+ }
4
+ export interface GenerateOptions {
5
+ temperature?: number;
6
+ maxTokens?: number;
7
+ }
8
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/llm/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CACtE;AAED,MAAM,WAAW,eAAe;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB"}
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,18 @@
1
+ import type { CodebasePatterns } from '../types/index.js';
2
+ export declare class AnalyzerService {
3
+ analyze(files: string[]): Promise<CodebasePatterns>;
4
+ private analyzeTypeScriptFile;
5
+ private analyzeSQLFile;
6
+ private analyzePrismaFile;
7
+ private isReactComponent;
8
+ private isAPIRoute;
9
+ private extractComponentName;
10
+ private getComponentType;
11
+ private extractProps;
12
+ private extractAPIRoutes;
13
+ private extractTypeDefinitions;
14
+ private extractTables;
15
+ private extractPrismaModels;
16
+ private findLine;
17
+ }
18
+ //# sourceMappingURL=Analyzer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Analyzer.d.ts","sourceRoot":"","sources":["../../src/services/Analyzer.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAuD,MAAM,mBAAmB,CAAC;AAE/G,qBAAa,eAAe;IACpB,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,gBAAgB,CAAC;IA+BzD,OAAO,CAAC,qBAAqB;IAkC7B,OAAO,CAAC,cAAc;IAUtB,OAAO,CAAC,iBAAiB;IAUzB,OAAO,CAAC,gBAAgB;IAQxB,OAAO,CAAC,UAAU;IAWlB,OAAO,CAAC,oBAAoB;IAgB5B,OAAO,CAAC,gBAAgB;IAOxB,OAAO,CAAC,YAAY;IAQpB,OAAO,CAAC,gBAAgB;IAwCxB,OAAO,CAAC,sBAAsB;IA2C9B,OAAO,CAAC,aAAa;IAgCrB,OAAO,CAAC,mBAAmB;IAqB3B,OAAO,CAAC,QAAQ;CAQjB"}
@@ -0,0 +1,235 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.AnalyzerService = void 0;
7
+ const promises_1 = __importDefault(require("fs/promises"));
8
+ const path_1 = __importDefault(require("path"));
9
+ class AnalyzerService {
10
+ async analyze(files) {
11
+ const patterns = {
12
+ apiRoutes: [],
13
+ databaseSchemas: [],
14
+ components: [],
15
+ types: [],
16
+ utilities: []
17
+ };
18
+ for (const file of files) {
19
+ const ext = path_1.default.extname(file);
20
+ try {
21
+ const content = await promises_1.default.readFile(file, 'utf-8');
22
+ const lines = content.split('\n');
23
+ if (['.ts', '.tsx', '.js', '.jsx'].includes(ext)) {
24
+ this.analyzeTypeScriptFile(file, content, lines, patterns);
25
+ }
26
+ else if (ext === '.sql') {
27
+ this.analyzeSQLFile(file, content, lines, patterns);
28
+ }
29
+ else if (ext === '.prisma') {
30
+ this.analyzePrismaFile(file, content, lines, patterns);
31
+ }
32
+ }
33
+ catch (error) {
34
+ // Skip files that can't be read
35
+ }
36
+ }
37
+ return patterns;
38
+ }
39
+ analyzeTypeScriptFile(file, content, lines, patterns) {
40
+ const isComponent = this.isReactComponent(file, content);
41
+ const isRoute = this.isAPIRoute(file, content);
42
+ // Extract components
43
+ if (isComponent) {
44
+ const componentName = this.extractComponentName(content);
45
+ if (componentName) {
46
+ patterns.components.push({
47
+ file,
48
+ line: this.findLine(lines, componentName),
49
+ name: componentName,
50
+ type: this.getComponentType(file),
51
+ props: this.extractProps(content)
52
+ });
53
+ }
54
+ }
55
+ // Extract API routes
56
+ if (isRoute) {
57
+ const routes = this.extractAPIRoutes(file, lines);
58
+ patterns.apiRoutes.push(...routes);
59
+ }
60
+ // Extract type definitions
61
+ const types = this.extractTypeDefinitions(file, lines);
62
+ patterns.types.push(...types);
63
+ }
64
+ analyzeSQLFile(file, content, lines, patterns) {
65
+ const tables = this.extractTables(file, lines);
66
+ patterns.databaseSchemas.push(...tables);
67
+ }
68
+ analyzePrismaFile(file, content, lines, patterns) {
69
+ const models = this.extractPrismaModels(file, lines);
70
+ patterns.databaseSchemas.push(...models);
71
+ }
72
+ isReactComponent(file, content) {
73
+ return ((file.endsWith('.tsx') || file.endsWith('.jsx')) ||
74
+ content.includes('React.FC') ||
75
+ content.includes('export function') && content.includes('JSX'));
76
+ }
77
+ isAPIRoute(file, content) {
78
+ return (file.includes('/api/') ||
79
+ file.includes('/routes/') ||
80
+ file.includes('router.') ||
81
+ content.includes('express.Router') ||
82
+ content.includes('.get(') ||
83
+ content.includes('.post('));
84
+ }
85
+ extractComponentName(content) {
86
+ // Match: export function ComponentName
87
+ const funcMatch = content.match(/export\s+function\s+(\w+)/);
88
+ if (funcMatch)
89
+ return funcMatch[1];
90
+ // Match: const ComponentName = () =>
91
+ const arrowMatch = content.match(/(?:export\s+)?const\s+(\w+)\s*=\s*(?:async\s+)?\(/);
92
+ if (arrowMatch)
93
+ return arrowMatch[1];
94
+ // Match: export class ComponentName
95
+ const classMatch = content.match(/export\s+class\s+(\w+)\s+extends/);
96
+ if (classMatch)
97
+ return classMatch[1];
98
+ return null;
99
+ }
100
+ getComponentType(file) {
101
+ if (file.includes('/pages/') || file.includes('/app/'))
102
+ return 'Page';
103
+ if (file.includes('/components/'))
104
+ return 'Component';
105
+ if (file.includes('/layouts/'))
106
+ return 'Layout';
107
+ return 'Component';
108
+ }
109
+ extractProps(content) {
110
+ const interfaceMatch = content.match(/interface\s+(\w+Props)\s*{([^}]+)}/s);
111
+ if (interfaceMatch) {
112
+ return interfaceMatch[2].trim();
113
+ }
114
+ return undefined;
115
+ }
116
+ extractAPIRoutes(file, lines) {
117
+ const routes = [];
118
+ for (let i = 0; i < lines.length; i++) {
119
+ const line = lines[i];
120
+ // Match: router.get('/path', handler)
121
+ const routeMatch = line.match(/\.(get|post|put|patch|delete|options|head)\s*\(\s*['"`]([^'"`]+)['"`]\s*,\s*(\w+)/);
122
+ if (routeMatch) {
123
+ routes.push({
124
+ file,
125
+ line: i + 1,
126
+ method: routeMatch[1].toUpperCase(),
127
+ path: routeMatch[2],
128
+ handler: routeMatch[3]
129
+ });
130
+ }
131
+ // Match: app.get('/path', handler)
132
+ const appMatch = line.match(/\b(app|router)\.(get|post|put|patch|delete)\s*\(\s*['"`]([^'"`]+)['"`]/);
133
+ if (appMatch) {
134
+ routes.push({
135
+ file,
136
+ line: i + 1,
137
+ method: appMatch[2].toUpperCase(),
138
+ path: appMatch[3],
139
+ handler: 'anonymous'
140
+ });
141
+ }
142
+ }
143
+ return routes;
144
+ }
145
+ extractTypeDefinitions(file, lines) {
146
+ const types = [];
147
+ for (let i = 0; i < lines.length; i++) {
148
+ const line = lines[i];
149
+ // Match: interface Name
150
+ const interfaceMatch = line.match(/(?:export\s+)?interface\s+(\w+)/);
151
+ if (interfaceMatch) {
152
+ types.push({
153
+ file,
154
+ line: i + 1,
155
+ name: interfaceMatch[1],
156
+ kind: 'interface'
157
+ });
158
+ }
159
+ // Match: type Name =
160
+ const typeMatch = line.match(/(?:export\s+)?type\s+(\w+)\s*=/);
161
+ if (typeMatch) {
162
+ types.push({
163
+ file,
164
+ line: i + 1,
165
+ name: typeMatch[1],
166
+ kind: 'type'
167
+ });
168
+ }
169
+ // Match: enum Name
170
+ const enumMatch = line.match(/(?:export\s+)?enum\s+(\w+)/);
171
+ if (enumMatch) {
172
+ types.push({
173
+ file,
174
+ line: i + 1,
175
+ name: enumMatch[1],
176
+ kind: 'enum'
177
+ });
178
+ }
179
+ }
180
+ return types;
181
+ }
182
+ extractTables(file, lines) {
183
+ const schemas = [];
184
+ for (let i = 0; i < lines.length; i++) {
185
+ const line = lines[i];
186
+ // Match: CREATE TABLE name
187
+ const tableMatch = line.match(/CREATE\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?(?:`?(\w+)`?)/i);
188
+ if (tableMatch) {
189
+ schemas.push({
190
+ file,
191
+ line: i + 1,
192
+ type: 'table',
193
+ name: tableMatch[1]
194
+ });
195
+ }
196
+ // Match: CREATE VIEW name
197
+ const viewMatch = line.match(/CREATE\s+VIEW\s+(?:`?(\w+)`?)/i);
198
+ if (viewMatch) {
199
+ schemas.push({
200
+ file,
201
+ line: i + 1,
202
+ type: 'view',
203
+ name: viewMatch[1]
204
+ });
205
+ }
206
+ }
207
+ return schemas;
208
+ }
209
+ extractPrismaModels(file, lines) {
210
+ const schemas = [];
211
+ for (let i = 0; i < lines.length; i++) {
212
+ const line = lines[i];
213
+ // Match: model Name {
214
+ const modelMatch = line.match(/model\s+(\w+)\s*{/);
215
+ if (modelMatch) {
216
+ schemas.push({
217
+ file,
218
+ line: i + 1,
219
+ type: 'table',
220
+ name: modelMatch[1]
221
+ });
222
+ }
223
+ }
224
+ return schemas;
225
+ }
226
+ findLine(lines, text) {
227
+ for (let i = 0; i < lines.length; i++) {
228
+ if (lines[i].includes(text)) {
229
+ return i + 1;
230
+ }
231
+ }
232
+ return 1;
233
+ }
234
+ }
235
+ exports.AnalyzerService = AnalyzerService;
@@ -0,0 +1,16 @@
1
+ import type { CodebaseContext } from '../types/index.js';
2
+ export declare class ContextManager {
3
+ private contextDir;
4
+ private contextFile;
5
+ constructor(root?: string);
6
+ saveContext(context: CodebaseContext): Promise<void>;
7
+ loadContext(): Promise<CodebaseContext | null>;
8
+ updateContext(updates: Partial<CodebaseContext>): Promise<void>;
9
+ contextExists(): Promise<boolean>;
10
+ getContextPath(): Promise<string>;
11
+ exportContext(outputPath: string): Promise<void>;
12
+ importContext(inputPath: string): Promise<void>;
13
+ private ensureContextDir;
14
+ private generateHash;
15
+ }
16
+ //# sourceMappingURL=ContextManager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ContextManager.d.ts","sourceRoot":"","sources":["../../src/services/ContextManager.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEzD,qBAAa,cAAc;IACzB,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,WAAW,CAAS;gBAEhB,IAAI,GAAE,MAAsB;IAKlC,WAAW,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAapD,WAAW,IAAI,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC;IAS9C,aAAa,CAAC,OAAO,EAAE,OAAO,CAAC,eAAe,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAU/D,aAAa,IAAI,OAAO,CAAC,OAAO,CAAC;IASjC,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC;IAIjC,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAShD,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YAQvC,gBAAgB;IAQ9B,OAAO,CAAC,YAAY;CAIrB"}
@@ -0,0 +1,82 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.ContextManager = void 0;
7
+ const promises_1 = __importDefault(require("fs/promises"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const crypto_1 = __importDefault(require("crypto"));
10
+ class ContextManager {
11
+ contextDir;
12
+ contextFile;
13
+ constructor(root = process.cwd()) {
14
+ this.contextDir = path_1.default.join(root, '.feature-architect');
15
+ this.contextFile = path_1.default.join(this.contextDir, 'context.json');
16
+ }
17
+ async saveContext(context) {
18
+ await this.ensureContextDir();
19
+ // Update metadata
20
+ context.metadata = {
21
+ version: '1.0.0',
22
+ analyzedAt: new Date().toISOString(),
23
+ hash: this.generateHash(context)
24
+ };
25
+ await promises_1.default.writeFile(this.contextFile, JSON.stringify(context, null, 2), 'utf-8');
26
+ }
27
+ async loadContext() {
28
+ try {
29
+ const content = await promises_1.default.readFile(this.contextFile, 'utf-8');
30
+ return JSON.parse(content);
31
+ }
32
+ catch {
33
+ return null;
34
+ }
35
+ }
36
+ async updateContext(updates) {
37
+ const current = await this.loadContext();
38
+ if (!current) {
39
+ throw new Error('No context found. Run init first.');
40
+ }
41
+ const updated = { ...current, ...updates };
42
+ await this.saveContext(updated);
43
+ }
44
+ async contextExists() {
45
+ try {
46
+ await promises_1.default.access(this.contextFile);
47
+ return true;
48
+ }
49
+ catch {
50
+ return false;
51
+ }
52
+ }
53
+ async getContextPath() {
54
+ return this.contextFile;
55
+ }
56
+ async exportContext(outputPath) {
57
+ const context = await this.loadContext();
58
+ if (!context) {
59
+ throw new Error('No context found. Run init first.');
60
+ }
61
+ await promises_1.default.writeFile(outputPath, JSON.stringify(context, null, 2), 'utf-8');
62
+ }
63
+ async importContext(inputPath) {
64
+ const content = await promises_1.default.readFile(inputPath, 'utf-8');
65
+ const context = JSON.parse(content);
66
+ await this.ensureContextDir();
67
+ await this.saveContext(context);
68
+ }
69
+ async ensureContextDir() {
70
+ try {
71
+ await promises_1.default.mkdir(this.contextDir, { recursive: true });
72
+ }
73
+ catch {
74
+ // Ignore if already exists
75
+ }
76
+ }
77
+ generateHash(context) {
78
+ const content = JSON.stringify(context);
79
+ return crypto_1.default.createHash('sha256').update(content).digest('hex').substring(0, 16);
80
+ }
81
+ }
82
+ exports.ContextManager = ContextManager;
@@ -0,0 +1,16 @@
1
+ import type { LLMProvider } from '../llm/types.js';
2
+ import type { CodebaseContext, FeaturePlan } from '../types/index.js';
3
+ interface PlanOptions {
4
+ output?: string;
5
+ includeDiagrams?: boolean;
6
+ }
7
+ export declare class PlannerService {
8
+ private llm;
9
+ constructor(llm: LLMProvider);
10
+ planFeature(feature: string, context: CodebaseContext, options?: PlanOptions): Promise<FeaturePlan>;
11
+ private buildPrompt;
12
+ private generateId;
13
+ private slugify;
14
+ }
15
+ export {};
16
+ //# sourceMappingURL=Planner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Planner.d.ts","sourceRoot":"","sources":["../../src/services/Planner.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,KAAK,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEtE,UAAU,WAAW;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,qBAAa,cAAc;IACb,OAAO,CAAC,GAAG;gBAAH,GAAG,EAAE,WAAW;IAE9B,WAAW,CACf,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,eAAe,EACxB,OAAO,GAAE,WAAgB,GACxB,OAAO,CAAC,WAAW,CAAC;IAqBvB,OAAO,CAAC,WAAW;IAgJnB,OAAO,CAAC,UAAU;IAIlB,OAAO,CAAC,OAAO;CAUhB"}
@@ -0,0 +1,181 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.PlannerService = void 0;
7
+ const path_1 = __importDefault(require("path"));
8
+ class PlannerService {
9
+ llm;
10
+ constructor(llm) {
11
+ this.llm = llm;
12
+ }
13
+ async planFeature(feature, context, options = {}) {
14
+ const prompt = this.buildPrompt(feature, context);
15
+ const response = await this.llm.generate(prompt, {
16
+ temperature: 0.7,
17
+ maxTokens: 8000
18
+ });
19
+ const slug = this.slugify(feature);
20
+ const outputFile = options.output || path_1.default.join('docs', 'features', `${slug}.md`);
21
+ const plan = {
22
+ id: this.generateId(),
23
+ feature,
24
+ slug,
25
+ createdAt: new Date().toISOString(),
26
+ markdown: response
27
+ };
28
+ return plan;
29
+ }
30
+ buildPrompt(feature, context) {
31
+ const patterns = context.patterns;
32
+ let relevantPatterns = '';
33
+ // API Routes
34
+ if (patterns.apiRoutes.length > 0) {
35
+ relevantPatterns += `\n### Existing API Routes (${patterns.apiRoutes.length}):\n`;
36
+ patterns.apiRoutes.slice(0, 20).forEach(route => {
37
+ relevantPatterns += `- ${route.method} ${route.path} (${path_1.default.basename(route.file)})\n`;
38
+ });
39
+ if (patterns.apiRoutes.length > 20) {
40
+ relevantPatterns += `... and ${patterns.apiRoutes.length - 20} more\n`;
41
+ }
42
+ }
43
+ // Database Tables
44
+ if (patterns.databaseSchemas.length > 0) {
45
+ relevantPatterns += `\n### Existing Database Tables (${patterns.databaseSchemas.length}):\n`;
46
+ patterns.databaseSchemas.slice(0, 20).forEach(schema => {
47
+ relevantPatterns += `- ${schema.name} (${schema.type})\n`;
48
+ });
49
+ if (patterns.databaseSchemas.length > 20) {
50
+ relevantPatterns += `... and ${patterns.databaseSchemas.length - 20} more\n`;
51
+ }
52
+ }
53
+ // Components
54
+ if (patterns.components.length > 0) {
55
+ relevantPatterns += `\n### Existing Components (${patterns.components.length}):\n`;
56
+ patterns.components.slice(0, 20).forEach(component => {
57
+ relevantPatterns += `- ${component.name} (${component.type})\n`;
58
+ });
59
+ if (patterns.components.length > 20) {
60
+ relevantPatterns += `... and ${patterns.components.length - 20} more\n`;
61
+ }
62
+ }
63
+ // Frameworks
64
+ const frameworks = Object.values(context.frameworks).filter(Boolean).join(', ') || 'Unknown';
65
+ return `You are a senior software architect. Plan the following feature based on the existing codebase context.
66
+
67
+ FEATURE TO PLAN: ${feature}
68
+
69
+ ---
70
+
71
+ ## CODEBASE CONTEXT
72
+
73
+ **Project Type:** ${context.project.type}
74
+ **Frameworks:** ${frameworks}
75
+ **Total Files:** ${context.summary.totalFiles}
76
+ **Languages:** ${Object.entries(context.summary.languages)
77
+ .map(([lang, count]) => `${lang} (${count})`)
78
+ .join(', ')}
79
+
80
+ ${relevantPatterns}
81
+
82
+ ---
83
+
84
+ ## YOUR TASK
85
+
86
+ Generate a complete, detailed feature plan that:
87
+
88
+ 1. **Follows existing patterns** in the codebase
89
+ 2. **Uses the same frameworks and conventions** detected above
90
+ 3. **Integrates seamlessly** with existing API routes and database tables
91
+
92
+ ## OUTPUT FORMAT (Markdown)
93
+
94
+ # Feature Plan: ${feature}
95
+
96
+ ## Overview
97
+ [Brief description of what this feature does and why it's needed]
98
+
99
+ ## Use Cases
100
+ | ID | Description | Priority |
101
+ |----|-------------|----------|
102
+ | UC1 | [Specific use case] | Must Have |
103
+ | UC2 | [Specific use case] | Should Have |
104
+ ...
105
+
106
+ ## Database Design
107
+ ### New Tables
108
+ \`\`\`sql
109
+ [SQL CREATE statements for any new tables]
110
+ \`\`\`
111
+
112
+ ### Modified Tables
113
+ \`\`\`sql
114
+ [SQL ALTER statements for any modifications to existing tables]
115
+ \`\`\`
116
+
117
+ ## Backend API
118
+ ### New Endpoints
119
+ | Method | Endpoint | Description | Auth |
120
+ |--------|----------|-------------|------|
121
+ | GET | /api/v1/... | [Description] | Required |
122
+ ...
123
+
124
+ ### Request/Response Examples
125
+ \`\`\`typescript
126
+ [TypeScript interfaces for requests and responses]
127
+ \`\`\`
128
+
129
+ ## Frontend Components
130
+ ### Component Structure
131
+ \`\`\`
132
+ src/features/[feature-name]/
133
+ ├── ComponentName.tsx
134
+ └── ...
135
+ \`\`\`
136
+
137
+ ### Component Specs
138
+ \`\`\`typescript
139
+ [Component interfaces and props]
140
+ \`\`\`
141
+
142
+ ## Architecture Flow
143
+ \`\`\`mermaid
144
+ sequenceDiagram
145
+ [Sequence diagram showing the flow]
146
+ \`\`\`
147
+
148
+ ## Implementation Tasks
149
+ ### Phase 1: Database (X days)
150
+ - [ ] [Task 1]
151
+ - [ ] [Task 2]
152
+
153
+ ### Phase 2: Backend (X days)
154
+ - [ ] [Task 1]
155
+
156
+ ...
157
+
158
+ ## Edge Cases
159
+ | Case | Handling |
160
+ |------|----------|
161
+ | [Edge case] | [How to handle] |
162
+ ...
163
+
164
+ Generate the complete plan now. Be specific and detailed.
165
+ `;
166
+ }
167
+ generateId() {
168
+ return `plan_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
169
+ }
170
+ slugify(text) {
171
+ return text
172
+ .toString()
173
+ .toLowerCase()
174
+ .trim()
175
+ .replace(/\s+/g, '-')
176
+ .replace(/[^\w\-]+/g, '')
177
+ .replace(/\-\-+/g, '-')
178
+ .replace(/[^a-z0-9\-]/g, '');
179
+ }
180
+ }
181
+ exports.PlannerService = PlannerService;