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.
- package/README.md +704 -0
- package/bin/feature-architect.js +2 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +63 -0
- package/dist/commands/init.d.ts +2 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +125 -0
- package/dist/commands/plan.d.ts +6 -0
- package/dist/commands/plan.d.ts.map +1 -0
- package/dist/commands/plan.js +147 -0
- package/dist/commands/verify.d.ts +2 -0
- package/dist/commands/verify.d.ts.map +1 -0
- package/dist/commands/verify.js +101 -0
- package/dist/config/api.config.d.ts +49 -0
- package/dist/config/api.config.d.ts.map +1 -0
- package/dist/config/api.config.js +78 -0
- package/dist/llm/Claude.d.ts +8 -0
- package/dist/llm/Claude.d.ts.map +1 -0
- package/dist/llm/Claude.js +44 -0
- package/dist/llm/OpenAI.d.ts +8 -0
- package/dist/llm/OpenAI.d.ts.map +1 -0
- package/dist/llm/OpenAI.js +43 -0
- package/dist/llm/factory.d.ts +9 -0
- package/dist/llm/factory.d.ts.map +1 -0
- package/dist/llm/factory.js +36 -0
- package/dist/llm/types.d.ts +8 -0
- package/dist/llm/types.d.ts.map +1 -0
- package/dist/llm/types.js +2 -0
- package/dist/services/Analyzer.d.ts +18 -0
- package/dist/services/Analyzer.d.ts.map +1 -0
- package/dist/services/Analyzer.js +235 -0
- package/dist/services/ContextManager.d.ts +16 -0
- package/dist/services/ContextManager.d.ts.map +1 -0
- package/dist/services/ContextManager.js +82 -0
- package/dist/services/Planner.d.ts +16 -0
- package/dist/services/Planner.d.ts.map +1 -0
- package/dist/services/Planner.js +181 -0
- package/dist/services/Scanner.d.ts +17 -0
- package/dist/services/Scanner.d.ts.map +1 -0
- package/dist/services/Scanner.js +75 -0
- package/dist/types/index.d.ts +90 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +3 -0
- package/dist/utils/logger.d.ts +22 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +56 -0
- package/package.json +48 -0
- package/src/config/api.config.ts +79 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
const commander_1 = require("commander");
|
|
5
|
+
const init_js_1 = require("../commands/init.js");
|
|
6
|
+
const plan_js_1 = require("../commands/plan.js");
|
|
7
|
+
const verify_js_1 = require("../commands/verify.js");
|
|
8
|
+
const logger_js_1 = require("../utils/logger.js");
|
|
9
|
+
const program = new commander_1.Command();
|
|
10
|
+
program
|
|
11
|
+
.name('feature-architect')
|
|
12
|
+
.description('AI-powered feature planning agent - generate complete technical specifications')
|
|
13
|
+
.version('1.0.0');
|
|
14
|
+
program
|
|
15
|
+
.command('init')
|
|
16
|
+
.description('Initialize in project (analyze codebase)')
|
|
17
|
+
.option('-f, --force', 'Re-initialize even if already initialized')
|
|
18
|
+
.action(async (options) => {
|
|
19
|
+
try {
|
|
20
|
+
await (0, init_js_1.initCommand)(options.force);
|
|
21
|
+
}
|
|
22
|
+
catch (err) {
|
|
23
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
program
|
|
28
|
+
.command('plan <feature>')
|
|
29
|
+
.description('Plan a new feature')
|
|
30
|
+
.option('-p, --provider <provider>', 'AI provider (claude, openai)', 'claude')
|
|
31
|
+
.option('-o, --output <path>', 'Output file path')
|
|
32
|
+
.option('--no-diagrams', 'Skip diagram generation')
|
|
33
|
+
.action(async (feature, options) => {
|
|
34
|
+
try {
|
|
35
|
+
await (0, plan_js_1.planCommand)(feature, options);
|
|
36
|
+
}
|
|
37
|
+
catch (err) {
|
|
38
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
program
|
|
43
|
+
.command('verify')
|
|
44
|
+
.description('Verify setup and configuration')
|
|
45
|
+
.action(async () => {
|
|
46
|
+
try {
|
|
47
|
+
await (0, verify_js_1.verifyCommand)();
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
// Default: show help if no command
|
|
54
|
+
program.on('command:*', () => {
|
|
55
|
+
(0, logger_js_1.header)('Feature Architect Agent v1.0.0');
|
|
56
|
+
(0, logger_js_1.info)('AI-powered feature planning for development teams\n');
|
|
57
|
+
(0, logger_js_1.dim)('Quick start:');
|
|
58
|
+
(0, logger_js_1.dim)(' 1. feature-architect init # Analyze codebase');
|
|
59
|
+
(0, logger_js_1.dim)(' 2. export ANTHROPIC_API_KEY=xxx # Set API key');
|
|
60
|
+
(0, logger_js_1.dim)(' 3. feature-architect plan "feature" # Plan feature');
|
|
61
|
+
console.log('');
|
|
62
|
+
});
|
|
63
|
+
program.parse();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAOA,wBAAsB,WAAW,CAAC,KAAK,GAAE,OAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CA+EvE"}
|
|
@@ -0,0 +1,125 @@
|
|
|
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.initCommand = initCommand;
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
const Scanner_js_1 = require("../services/Scanner.js");
|
|
9
|
+
const Analyzer_js_1 = require("../services/Analyzer.js");
|
|
10
|
+
const ContextManager_js_1 = require("../services/ContextManager.js");
|
|
11
|
+
const logger_js_1 = require("../utils/logger.js");
|
|
12
|
+
async function initCommand(force = false) {
|
|
13
|
+
(0, logger_js_1.header)('🔍 Feature Architect Agent - Initialization');
|
|
14
|
+
const contextManager = new ContextManager_js_1.ContextManager();
|
|
15
|
+
// Check if already initialized
|
|
16
|
+
if (!force && await contextManager.contextExists()) {
|
|
17
|
+
const context = await contextManager.loadContext();
|
|
18
|
+
(0, logger_js_1.info)(`Already initialized on ${context?.metadata.analyzedAt}`);
|
|
19
|
+
(0, logger_js_1.info)('Use --force to re-initialize');
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
// Scan codebase
|
|
23
|
+
(0, logger_js_1.info)('Scanning codebase...');
|
|
24
|
+
const scanner = new Scanner_js_1.ScannerService();
|
|
25
|
+
const { files, stats } = await scanner.scanWithStats();
|
|
26
|
+
(0, logger_js_1.bullet)(`Found ${stats.total} files`);
|
|
27
|
+
for (const [lang, count] of Object.entries(stats.byLanguage)) {
|
|
28
|
+
(0, logger_js_1.bullet)(` ${lang}: ${count} files`, 1);
|
|
29
|
+
}
|
|
30
|
+
// Analyze patterns
|
|
31
|
+
(0, logger_js_1.info)('\nAnalyzing patterns...');
|
|
32
|
+
const analyzer = new Analyzer_js_1.AnalyzerService();
|
|
33
|
+
const patterns = await analyzer.analyze(files);
|
|
34
|
+
(0, logger_js_1.bullet)(`API Routes: ${patterns.apiRoutes.length}`);
|
|
35
|
+
(0, logger_js_1.bullet)(`Database Tables: ${patterns.databaseSchemas.length}`);
|
|
36
|
+
(0, logger_js_1.bullet)(`Components: ${patterns.components.length}`);
|
|
37
|
+
(0, logger_js_1.bullet)(`Type Definitions: ${patterns.types.length}`);
|
|
38
|
+
// Detect frameworks
|
|
39
|
+
const frameworks = detectFrameworks(patterns);
|
|
40
|
+
// Build context
|
|
41
|
+
(0, logger_js_1.info)('\nBuilding context...');
|
|
42
|
+
const projectName = path_1.default.basename(process.cwd());
|
|
43
|
+
const projectType = detectProjectType(files, patterns);
|
|
44
|
+
const context = {
|
|
45
|
+
project: {
|
|
46
|
+
name: projectName,
|
|
47
|
+
type: projectType,
|
|
48
|
+
root: process.cwd()
|
|
49
|
+
},
|
|
50
|
+
summary: {
|
|
51
|
+
totalFiles: stats.total,
|
|
52
|
+
languages: stats.byLanguage,
|
|
53
|
+
frameworks: Object.values(frameworks).filter(Boolean),
|
|
54
|
+
patterns: patterns.apiRoutes.length +
|
|
55
|
+
patterns.databaseSchemas.length +
|
|
56
|
+
patterns.components.length +
|
|
57
|
+
patterns.types.length
|
|
58
|
+
},
|
|
59
|
+
frameworks: frameworks,
|
|
60
|
+
patterns: patterns,
|
|
61
|
+
metadata: {
|
|
62
|
+
version: '1.0.0',
|
|
63
|
+
analyzedAt: new Date().toISOString(),
|
|
64
|
+
hash: ''
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
// Save context
|
|
68
|
+
await contextManager.saveContext(context);
|
|
69
|
+
(0, logger_js_1.success)('\n✅ Initialization complete!');
|
|
70
|
+
(0, logger_js_1.info)(`Project: ${projectName}`);
|
|
71
|
+
(0, logger_js_1.info)(`Type: ${projectType}`);
|
|
72
|
+
(0, logger_js_1.info)(`Frameworks: ${context.summary.frameworks.join(', ') || 'Unknown'}`);
|
|
73
|
+
(0, logger_js_1.info)(`Patterns indexed: ${context.summary.patterns}`);
|
|
74
|
+
const contextPath = await contextManager.getContextPath();
|
|
75
|
+
(0, logger_js_1.dim)(`\nContext saved to: ${contextPath}`);
|
|
76
|
+
(0, logger_js_1.info)('\nYou can now plan features:');
|
|
77
|
+
(0, logger_js_1.info)(' feature-architect plan "your feature here"');
|
|
78
|
+
}
|
|
79
|
+
function detectProjectType(files, patterns) {
|
|
80
|
+
const hasTS = files.some(f => f.endsWith('.ts') || f.endsWith('.tsx'));
|
|
81
|
+
const hasReact = patterns.components.some((c) => c.type === 'Component');
|
|
82
|
+
const hasNext = files.some(f => f.includes('next.config.') || f.includes('/pages/') || f.includes('/app/'));
|
|
83
|
+
if (hasNext && hasTS)
|
|
84
|
+
return 'Next.js + TypeScript';
|
|
85
|
+
if (hasReact && hasTS)
|
|
86
|
+
return 'React + TypeScript';
|
|
87
|
+
if (hasTS)
|
|
88
|
+
return 'TypeScript';
|
|
89
|
+
return 'JavaScript';
|
|
90
|
+
}
|
|
91
|
+
function detectFrameworks(patterns) {
|
|
92
|
+
const frameworks = {};
|
|
93
|
+
// Detect API framework
|
|
94
|
+
if (patterns.apiRoutes.length > 0) {
|
|
95
|
+
const hasExpress = patterns.apiRoutes.some((r) => r.path.includes(':'));
|
|
96
|
+
if (hasExpress) {
|
|
97
|
+
frameworks.backend = 'Express.js';
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
frameworks.backend = 'REST API';
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// Detect frontend framework
|
|
104
|
+
if (patterns.components.length > 0) {
|
|
105
|
+
const hasPages = patterns.components.some((c) => c.file.includes('/pages/'));
|
|
106
|
+
if (hasPages) {
|
|
107
|
+
frameworks.frontend = 'Next.js';
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
frameworks.frontend = 'React';
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// Detect database
|
|
114
|
+
if (patterns.databaseSchemas.length > 0) {
|
|
115
|
+
const hasPrisma = patterns.databaseSchemas.some((s) => s.file.includes('.prisma'));
|
|
116
|
+
if (hasPrisma) {
|
|
117
|
+
frameworks.orm = 'Prisma';
|
|
118
|
+
frameworks.database = 'PostgreSQL';
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
frameworks.database = 'SQL';
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return frameworks;
|
|
125
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan.d.ts","sourceRoot":"","sources":["../../src/commands/plan.ts"],"names":[],"mappings":"AAQA,wBAAsB,WAAW,CAC/B,OAAO,EAAE,MAAM,EACf,OAAO,GAAE;IACP,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,OAAO,CAAC;CACjB,GACL,OAAO,CAAC,IAAI,CAAC,CAiJf"}
|
|
@@ -0,0 +1,147 @@
|
|
|
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.planCommand = planCommand;
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
|
9
|
+
const ContextManager_js_1 = require("../services/ContextManager.js");
|
|
10
|
+
const Planner_js_1 = require("../services/Planner.js");
|
|
11
|
+
const factory_js_1 = require("../llm/factory.js");
|
|
12
|
+
const logger_js_1 = require("../utils/logger.js");
|
|
13
|
+
const api_config_js_1 = require("../config/api.config.js");
|
|
14
|
+
async function planCommand(feature, options = {}) {
|
|
15
|
+
(0, logger_js_1.header)('🧠 Feature Architect Agent - Plan Feature');
|
|
16
|
+
if (!feature) {
|
|
17
|
+
(0, logger_js_1.error)('Please provide a feature description');
|
|
18
|
+
(0, logger_js_1.info)('Usage: feature-architect plan "your feature description"');
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
// Load context
|
|
22
|
+
const contextManager = new ContextManager_js_1.ContextManager();
|
|
23
|
+
if (!(await contextManager.contextExists())) {
|
|
24
|
+
(0, logger_js_1.error)('Not initialized. Run: feature-architect init');
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const context = await contextManager.loadContext();
|
|
28
|
+
if (!context) {
|
|
29
|
+
(0, logger_js_1.error)('Failed to load context');
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
(0, logger_js_1.info)(`Feature: ${feature}`);
|
|
33
|
+
(0, logger_js_1.info)(`Project: ${context.project.name}`);
|
|
34
|
+
(0, logger_js_1.info)(`Frameworks: ${context.summary.frameworks.join(', ') || 'Unknown'}`);
|
|
35
|
+
// Auto-detect provider and API key
|
|
36
|
+
let provider = options.provider;
|
|
37
|
+
let apiKey;
|
|
38
|
+
let usingBuiltinKey = false;
|
|
39
|
+
// Auto-detect if not specified
|
|
40
|
+
if (!provider) {
|
|
41
|
+
// Check user environment variables first
|
|
42
|
+
if (process.env.ANTHROPIC_API_KEY) {
|
|
43
|
+
provider = 'anthropic';
|
|
44
|
+
apiKey = process.env.ANTHROPIC_API_KEY;
|
|
45
|
+
}
|
|
46
|
+
else if (process.env.OPENAI_API_KEY) {
|
|
47
|
+
provider = 'openai';
|
|
48
|
+
apiKey = process.env.OPENAI_API_KEY;
|
|
49
|
+
}
|
|
50
|
+
else if (process.env.GOOGLE_API_KEY) {
|
|
51
|
+
provider = 'google';
|
|
52
|
+
apiKey = process.env.GOOGLE_API_KEY;
|
|
53
|
+
}
|
|
54
|
+
else if (process.env.AI_API_KEY) {
|
|
55
|
+
// Generic API key - try OpenAI first (default built-in)
|
|
56
|
+
provider = 'openai';
|
|
57
|
+
apiKey = process.env.AI_API_KEY;
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
// Fall back to built-in team API key
|
|
61
|
+
const builtinOpenAIKey = (0, api_config_js_1.getBuiltinKey)('openai');
|
|
62
|
+
if (builtinOpenAIKey && builtinOpenAIKey !== 'sk-your-team-openai-key-here') {
|
|
63
|
+
provider = 'openai';
|
|
64
|
+
apiKey = builtinOpenAIKey;
|
|
65
|
+
usingBuiltinKey = true;
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
(0, logger_js_1.error)('No API key found');
|
|
69
|
+
(0, logger_js_1.dim)('Set any of these environment variables:');
|
|
70
|
+
(0, logger_js_1.dim)(' export ANTHROPIC_API_KEY=sk-ant-xxx # For Claude');
|
|
71
|
+
(0, logger_js_1.dim)(' export OPENAI_API_KEY=sk-xxx # For OpenAI');
|
|
72
|
+
(0, logger_js_1.dim)(' export GOOGLE_API_KEY=xxx # For Gemini');
|
|
73
|
+
(0, logger_js_1.dim)(' export AI_API_KEY=xxx # Generic');
|
|
74
|
+
(0, logger_js_1.dim)('\nOr contact your team admin to set up the built-in API key.');
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
// Use specified provider
|
|
81
|
+
const providerLower = provider.toLowerCase();
|
|
82
|
+
if (providerLower === 'claude' || providerLower === 'anthropic') {
|
|
83
|
+
provider = 'anthropic';
|
|
84
|
+
apiKey = (0, api_config_js_1.getApiKey)('anthropic');
|
|
85
|
+
}
|
|
86
|
+
else if (providerLower === 'openai') {
|
|
87
|
+
provider = 'openai';
|
|
88
|
+
apiKey = (0, api_config_js_1.getApiKey)('openai');
|
|
89
|
+
}
|
|
90
|
+
else if (providerLower === 'gemini' || providerLower === 'google') {
|
|
91
|
+
provider = 'google';
|
|
92
|
+
apiKey = (0, api_config_js_1.getApiKey)('google');
|
|
93
|
+
}
|
|
94
|
+
if (!apiKey) {
|
|
95
|
+
(0, logger_js_1.error)(`No API key found for ${provider}`);
|
|
96
|
+
(0, logger_js_1.dim)(`Set it with: export ${provider.toUpperCase()}_API_KEY=xxx`);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
// Check if using built-in key
|
|
100
|
+
const builtinKey = (0, api_config_js_1.getBuiltinKey)(provider);
|
|
101
|
+
if (builtinKey === apiKey && !process.env[`${provider.toUpperCase()}_API_KEY`]) {
|
|
102
|
+
usingBuiltinKey = true;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// Create AI provider
|
|
106
|
+
(0, logger_js_1.info)(`\nUsing AI: ${provider}${usingBuiltinKey ? ' (built-in team key)' : ''}...`);
|
|
107
|
+
const llm = (0, factory_js_1.createProvider)({
|
|
108
|
+
provider: provider,
|
|
109
|
+
apiKey
|
|
110
|
+
});
|
|
111
|
+
const planner = new Planner_js_1.PlannerService(llm);
|
|
112
|
+
// Generate plan
|
|
113
|
+
(0, logger_js_1.info)('Generating feature plan...\n');
|
|
114
|
+
try {
|
|
115
|
+
const plan = await planner.planFeature(feature, context, {
|
|
116
|
+
output: options.output,
|
|
117
|
+
includeDiagrams: !options.noDiagrams
|
|
118
|
+
});
|
|
119
|
+
// Ensure output directory exists
|
|
120
|
+
const slug = plan.slug;
|
|
121
|
+
const outputFile = options.output || path_1.default.join('docs', 'features', `${slug}.md`);
|
|
122
|
+
await promises_1.default.mkdir(path_1.default.dirname(outputFile), { recursive: true });
|
|
123
|
+
await promises_1.default.writeFile(outputFile, plan.markdown, 'utf-8');
|
|
124
|
+
(0, logger_js_1.success)('✅ Feature plan generated!');
|
|
125
|
+
(0, logger_js_1.info)(`Saved to: ${outputFile}`);
|
|
126
|
+
// Show preview
|
|
127
|
+
const lines = plan.markdown.split('\n');
|
|
128
|
+
const preview = lines.slice(0, 20).join('\n');
|
|
129
|
+
(0, logger_js_1.dim)('\n--- Preview (first 20 lines) ---\n');
|
|
130
|
+
console.log(preview);
|
|
131
|
+
if (lines.length > 20) {
|
|
132
|
+
(0, logger_js_1.dim)('\n... (truncated)');
|
|
133
|
+
}
|
|
134
|
+
(0, logger_js_1.info)('\n📖 Next steps:');
|
|
135
|
+
(0, logger_js_1.bullet)('Review the generated plan');
|
|
136
|
+
(0, logger_js_1.bullet)('Use it with your AI tool:');
|
|
137
|
+
(0, logger_js_1.dim)(` claude "Implement this feature" < ${outputFile}`);
|
|
138
|
+
(0, logger_js_1.dim)(` # Or paste into ChatGPT/Cursor/Copilot`);
|
|
139
|
+
}
|
|
140
|
+
catch (err) {
|
|
141
|
+
(0, logger_js_1.error)(`Failed to generate plan: ${err instanceof Error ? err.message : String(err)}`);
|
|
142
|
+
(0, logger_js_1.warn)('\nTroubleshooting:');
|
|
143
|
+
(0, logger_js_1.bullet)('Check your API key is set correctly');
|
|
144
|
+
(0, logger_js_1.bullet)('Ensure you have network connectivity');
|
|
145
|
+
(0, logger_js_1.bullet)('Try a shorter feature description');
|
|
146
|
+
}
|
|
147
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../../src/commands/verify.ts"],"names":[],"mappings":"AAKA,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CA0FnD"}
|
|
@@ -0,0 +1,101 @@
|
|
|
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.verifyCommand = verifyCommand;
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
const ContextManager_js_1 = require("../services/ContextManager.js");
|
|
9
|
+
const logger_js_1 = require("../utils/logger.js");
|
|
10
|
+
const api_config_js_1 = require("../config/api.config.js");
|
|
11
|
+
async function verifyCommand() {
|
|
12
|
+
(0, logger_js_1.header)('🔍 Feature Architect Agent - Verification');
|
|
13
|
+
const contextManager = new ContextManager_js_1.ContextManager();
|
|
14
|
+
let allGood = true;
|
|
15
|
+
// Check 1: Context exists
|
|
16
|
+
(0, logger_js_1.info)('Configuration:');
|
|
17
|
+
if (await contextManager.contextExists()) {
|
|
18
|
+
const context = await contextManager.loadContext();
|
|
19
|
+
(0, logger_js_1.check)(`Context file found`);
|
|
20
|
+
(0, logger_js_1.bullet)(`Project: ${context?.project.name}`, 1);
|
|
21
|
+
(0, logger_js_1.bullet)(`Type: ${context?.project.type}`, 1);
|
|
22
|
+
(0, logger_js_1.bullet)(`Initialized: ${context?.metadata.analyzedAt}`, 1);
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
(0, logger_js_1.error)('✗ Context file not found');
|
|
26
|
+
(0, logger_js_1.bullet)('Run: feature-architect init', 1);
|
|
27
|
+
allGood = false;
|
|
28
|
+
}
|
|
29
|
+
// Check 2: API key
|
|
30
|
+
(0, logger_js_1.info)('\nAI Provider:');
|
|
31
|
+
// Check for user API keys
|
|
32
|
+
const keysFound = [];
|
|
33
|
+
if (process.env.ANTHROPIC_API_KEY)
|
|
34
|
+
keysFound.push('Claude (ANTHROPIC_API_KEY)');
|
|
35
|
+
if (process.env.OPENAI_API_KEY)
|
|
36
|
+
keysFound.push('OpenAI (OPENAI_API_KEY)');
|
|
37
|
+
if (process.env.GOOGLE_API_KEY)
|
|
38
|
+
keysFound.push('Gemini (GOOGLE_API_KEY)');
|
|
39
|
+
if (process.env.AI_API_KEY)
|
|
40
|
+
keysFound.push('Generic (AI_API_KEY)');
|
|
41
|
+
// Check for built-in team keys
|
|
42
|
+
const builtinOpenAIKey = (0, api_config_js_1.getBuiltinKey)('openai');
|
|
43
|
+
const builtinAnthropicKey = (0, api_config_js_1.getBuiltinKey)('anthropic');
|
|
44
|
+
const builtinGoogleKey = (0, api_config_js_1.getBuiltinKey)('google');
|
|
45
|
+
if (builtinOpenAIKey && builtinOpenAIKey !== 'sk-your-team-openai-key-here') {
|
|
46
|
+
keysFound.push('OpenAI (built-in team key) ✓');
|
|
47
|
+
}
|
|
48
|
+
if (builtinAnthropicKey) {
|
|
49
|
+
keysFound.push('Claude (built-in team key) ✓');
|
|
50
|
+
}
|
|
51
|
+
if (builtinGoogleKey) {
|
|
52
|
+
keysFound.push('Gemini (built-in team key) ✓');
|
|
53
|
+
}
|
|
54
|
+
if (keysFound.length > 0) {
|
|
55
|
+
(0, logger_js_1.check)('API keys found:');
|
|
56
|
+
keysFound.forEach(key => (0, logger_js_1.bullet)(key, 1));
|
|
57
|
+
// Show which will be used
|
|
58
|
+
let defaultProvider = 'openai'; // Default to OpenAI (built-in)
|
|
59
|
+
if (process.env.ANTHROPIC_API_KEY)
|
|
60
|
+
defaultProvider = 'anthropic';
|
|
61
|
+
else if (process.env.OPENAI_API_KEY)
|
|
62
|
+
defaultProvider = 'openai';
|
|
63
|
+
else if (process.env.GOOGLE_API_KEY)
|
|
64
|
+
defaultProvider = 'google';
|
|
65
|
+
else if (builtinOpenAIKey && builtinOpenAIKey !== 'sk-your-team-openai-key-here')
|
|
66
|
+
defaultProvider = 'openai (built-in)';
|
|
67
|
+
(0, logger_js_1.bullet)(`Default provider: ${defaultProvider}`, 1);
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
(0, logger_js_1.error)('✗ No API key found');
|
|
71
|
+
(0, logger_js_1.bullet)('Set any API key:', 1);
|
|
72
|
+
(0, logger_js_1.bullet)('export ANTHROPIC_API_KEY=sk-ant-xxx # Claude', 2);
|
|
73
|
+
(0, logger_js_1.bullet)('export OPENAI_API_KEY=sk-xxx # OpenAI', 2);
|
|
74
|
+
(0, logger_js_1.bullet)('export GOOGLE_API_KEY=xxx # Gemini', 2);
|
|
75
|
+
(0, logger_js_1.bullet)('export AI_API_KEY=xxx # Generic', 2);
|
|
76
|
+
(0, logger_js_1.bullet)('\nOr contact your team admin to set up built-in keys.', 2);
|
|
77
|
+
allGood = false;
|
|
78
|
+
}
|
|
79
|
+
// Check 3: Output directory
|
|
80
|
+
(0, logger_js_1.info)('\nOutput:');
|
|
81
|
+
const docsDir = path_1.default.join(process.cwd(), 'docs', 'features');
|
|
82
|
+
try {
|
|
83
|
+
const { opendir } = await import('fs/promises');
|
|
84
|
+
await opendir(docsDir);
|
|
85
|
+
(0, logger_js_1.check)(`Output directory exists: ${docsDir}`);
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
(0, logger_js_1.dim)(`Output directory will be created: ${docsDir}`);
|
|
89
|
+
}
|
|
90
|
+
// Summary
|
|
91
|
+
if (allGood) {
|
|
92
|
+
(0, logger_js_1.success)('\n✅ All checks passed!');
|
|
93
|
+
(0, logger_js_1.info)('You can now plan features:');
|
|
94
|
+
(0, logger_js_1.dim)(' feature-architect plan "your feature here"\n');
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
(0, logger_js_1.error)('\n❌ Some checks failed');
|
|
98
|
+
(0, logger_js_1.info)('Fix the issues above before planning features.\n');
|
|
99
|
+
}
|
|
100
|
+
return allGood ? Promise.resolve() : Promise.reject(new Error('Verification failed'));
|
|
101
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Built-in API Configuration for Internal Team Use
|
|
3
|
+
*
|
|
4
|
+
* This file contains the pre-configured API key for team use.
|
|
5
|
+
* The package is published to a private registry for internal team access.
|
|
6
|
+
*
|
|
7
|
+
* SECURITY: This file is included in the published package.
|
|
8
|
+
* Only publish to PRIVATE npm registries (GitHub Packages, npm private, etc.)
|
|
9
|
+
*/
|
|
10
|
+
export declare const API_CONFIG: {
|
|
11
|
+
/**
|
|
12
|
+
* Built-in OpenAI API Key for team use
|
|
13
|
+
* Replace with your actual team API key
|
|
14
|
+
*/
|
|
15
|
+
openai: {
|
|
16
|
+
apiKey: string;
|
|
17
|
+
baseURL: string;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Built-in Anthropic API Key (optional)
|
|
21
|
+
*/
|
|
22
|
+
anthropic: {
|
|
23
|
+
apiKey: string;
|
|
24
|
+
baseURL: string;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Built-in Google API Key (optional)
|
|
28
|
+
*/
|
|
29
|
+
google: {
|
|
30
|
+
apiKey: string;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Default provider to use when no user key is set
|
|
34
|
+
*/
|
|
35
|
+
defaultProvider: "openai";
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Get the built-in API key for a provider
|
|
39
|
+
*/
|
|
40
|
+
export declare function getBuiltinKey(provider: 'openai' | 'anthropic' | 'google'): string | undefined;
|
|
41
|
+
/**
|
|
42
|
+
* Check if a built-in key is available
|
|
43
|
+
*/
|
|
44
|
+
export declare function hasBuiltinKey(provider: 'openai' | 'anthropic' | 'google'): boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Get the best available API key (user env var first, then built-in)
|
|
47
|
+
*/
|
|
48
|
+
export declare function getApiKey(provider: 'openai' | 'anthropic' | 'google'): string | undefined;
|
|
49
|
+
//# sourceMappingURL=api.config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.config.d.ts","sourceRoot":"","sources":["../../src/config/api.config.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,eAAO,MAAM,UAAU;IACrB;;;OAGG;;;;;IAMH;;OAEG;;;;;IAMH;;OAEG;;;;IAKH;;OAEG;;CAEJ,CAAC;AAEF;;GAEG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,QAAQ,GAAG,WAAW,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS,CAW7F;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,QAAQ,GAAG,WAAW,GAAG,QAAQ,GAAG,OAAO,CAGlF;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,QAAQ,EAAE,QAAQ,GAAG,WAAW,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS,CAUzF"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Built-in API Configuration for Internal Team Use
|
|
4
|
+
*
|
|
5
|
+
* This file contains the pre-configured API key for team use.
|
|
6
|
+
* The package is published to a private registry for internal team access.
|
|
7
|
+
*
|
|
8
|
+
* SECURITY: This file is included in the published package.
|
|
9
|
+
* Only publish to PRIVATE npm registries (GitHub Packages, npm private, etc.)
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.API_CONFIG = void 0;
|
|
13
|
+
exports.getBuiltinKey = getBuiltinKey;
|
|
14
|
+
exports.hasBuiltinKey = hasBuiltinKey;
|
|
15
|
+
exports.getApiKey = getApiKey;
|
|
16
|
+
exports.API_CONFIG = {
|
|
17
|
+
/**
|
|
18
|
+
* Built-in OpenAI API Key for team use
|
|
19
|
+
* Replace with your actual team API key
|
|
20
|
+
*/
|
|
21
|
+
openai: {
|
|
22
|
+
apiKey: process.env.TEAM_OPENAI_API_KEY || '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',
|
|
23
|
+
baseURL: 'https://api.openai.com/v1',
|
|
24
|
+
},
|
|
25
|
+
/**
|
|
26
|
+
* Built-in Anthropic API Key (optional)
|
|
27
|
+
*/
|
|
28
|
+
anthropic: {
|
|
29
|
+
apiKey: process.env.TEAM_ANTHROPIC_API_KEY || '',
|
|
30
|
+
baseURL: 'https://api.anthropic.com',
|
|
31
|
+
},
|
|
32
|
+
/**
|
|
33
|
+
* Built-in Google API Key (optional)
|
|
34
|
+
*/
|
|
35
|
+
google: {
|
|
36
|
+
apiKey: process.env.TEAM_GOOGLE_API_KEY || '',
|
|
37
|
+
},
|
|
38
|
+
/**
|
|
39
|
+
* Default provider to use when no user key is set
|
|
40
|
+
*/
|
|
41
|
+
defaultProvider: 'openai',
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Get the built-in API key for a provider
|
|
45
|
+
*/
|
|
46
|
+
function getBuiltinKey(provider) {
|
|
47
|
+
switch (provider) {
|
|
48
|
+
case 'openai':
|
|
49
|
+
return exports.API_CONFIG.openai.apiKey || undefined;
|
|
50
|
+
case 'anthropic':
|
|
51
|
+
return exports.API_CONFIG.anthropic.apiKey || undefined;
|
|
52
|
+
case 'google':
|
|
53
|
+
return exports.API_CONFIG.google.apiKey || undefined;
|
|
54
|
+
default:
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Check if a built-in key is available
|
|
60
|
+
*/
|
|
61
|
+
function hasBuiltinKey(provider) {
|
|
62
|
+
const key = getBuiltinKey(provider);
|
|
63
|
+
return !!key && key !== '' && !key.startsWith('sk-');
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Get the best available API key (user env var first, then built-in)
|
|
67
|
+
*/
|
|
68
|
+
function getApiKey(provider) {
|
|
69
|
+
// First check user's environment variable
|
|
70
|
+
const userKey = process.env[`${provider.toUpperCase()}_API_KEY`];
|
|
71
|
+
if (userKey)
|
|
72
|
+
return userKey;
|
|
73
|
+
// Then check generic AI_API_KEY
|
|
74
|
+
if (process.env.AI_API_KEY)
|
|
75
|
+
return process.env.AI_API_KEY;
|
|
76
|
+
// Finally fall back to built-in key
|
|
77
|
+
return getBuiltinKey(provider);
|
|
78
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { LLMProvider, GenerateOptions } from './types.js';
|
|
2
|
+
export declare class ClaudeProvider implements LLMProvider {
|
|
3
|
+
private apiKey;
|
|
4
|
+
private model;
|
|
5
|
+
constructor(apiKey: string, model?: string);
|
|
6
|
+
generate(prompt: string, options?: GenerateOptions): Promise<string>;
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=Claude.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Claude.d.ts","sourceRoot":"","sources":["../../src/llm/Claude.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE/D,qBAAa,cAAe,YAAW,WAAW;IAChD,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,KAAK,CAAS;gBAEV,MAAM,EAAE,MAAM,EAAE,KAAK,GAAE,MAAqC;IAKlE,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,MAAM,CAAC;CAqC/E"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ClaudeProvider = void 0;
|
|
4
|
+
class ClaudeProvider {
|
|
5
|
+
apiKey;
|
|
6
|
+
model;
|
|
7
|
+
constructor(apiKey, model = 'claude-3-5-sonnet-20241022') {
|
|
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 ?? 8000;
|
|
14
|
+
const response = await fetch('https://api.anthropic.com/v1/messages', {
|
|
15
|
+
method: 'POST',
|
|
16
|
+
headers: {
|
|
17
|
+
'x-api-key': this.apiKey,
|
|
18
|
+
'anthropic-version': '2023-06-01',
|
|
19
|
+
'content-type': 'application/json'
|
|
20
|
+
},
|
|
21
|
+
body: JSON.stringify({
|
|
22
|
+
model: this.model,
|
|
23
|
+
max_tokens: maxTokens,
|
|
24
|
+
temperature,
|
|
25
|
+
messages: [
|
|
26
|
+
{
|
|
27
|
+
role: 'user',
|
|
28
|
+
content: prompt
|
|
29
|
+
}
|
|
30
|
+
]
|
|
31
|
+
})
|
|
32
|
+
});
|
|
33
|
+
if (!response.ok) {
|
|
34
|
+
const error = await response.text();
|
|
35
|
+
throw new Error(`Claude API error: ${response.status} ${error}`);
|
|
36
|
+
}
|
|
37
|
+
const data = await response.json();
|
|
38
|
+
if (data.error) {
|
|
39
|
+
throw new Error(`Claude API error: ${data.error.message}`);
|
|
40
|
+
}
|
|
41
|
+
return data.content[0].text;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
exports.ClaudeProvider = ClaudeProvider;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { LLMProvider, GenerateOptions } from './types.js';
|
|
2
|
+
export declare class OpenAIProvider implements LLMProvider {
|
|
3
|
+
private apiKey;
|
|
4
|
+
private model;
|
|
5
|
+
constructor(apiKey: string, model?: string);
|
|
6
|
+
generate(prompt: string, options?: GenerateOptions): Promise<string>;
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=OpenAI.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OpenAI.d.ts","sourceRoot":"","sources":["../../src/llm/OpenAI.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE/D,qBAAa,cAAe,YAAW,WAAW;IAChD,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,KAAK,CAAS;gBAEV,MAAM,EAAE,MAAM,EAAE,KAAK,GAAE,MAAsB;IAKnD,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,MAAM,CAAC;CAoC/E"}
|