@iservu-inc/adf-cli 0.14.0 ā 0.14.2
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/CHANGELOG.md +21 -0
- package/README.md +12 -2
- package/lib/ai/ai-config.js +14 -15
- package/lib/commands/init.js +94 -0
- package/lib/frameworks/interviewer.js +4 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,27 @@ All notable changes to `@iservu-inc/adf-cli` will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.14.2] - 2026-01-12
|
|
9
|
+
|
|
10
|
+
### š Fix - Google Gemini API Validation
|
|
11
|
+
|
|
12
|
+
**Patch Release:** Fixed a bug where valid Google API keys were sometimes reported as invalid.
|
|
13
|
+
|
|
14
|
+
#### Fixed
|
|
15
|
+
- š§ **Improved Validation** - Switched Google API key validation to use the official SDK's `countTokens` method instead of a manual REST API call.
|
|
16
|
+
- š§ **URL Encoding** - Added `encodeURIComponent` to the API key in model fetching requests to handle special characters.
|
|
17
|
+
- š§ **Better Error Messaging** - Clearer error descriptions when a Google key fails validation.
|
|
18
|
+
|
|
19
|
+
## [0.14.1] - 2026-01-12
|
|
20
|
+
|
|
21
|
+
### š Integration - Project Context Synthesis in core CLI
|
|
22
|
+
|
|
23
|
+
**Patch Release:** Integrated the Synthesis Engine and Adaptive Augmentation flow directly into the `adf init` command.
|
|
24
|
+
|
|
25
|
+
#### Fixed/Integrated
|
|
26
|
+
- š§ **Core CLI Integration** - The `adf init` command now automatically detects existing BMAD, OpenSpec, and Spec-Kit frameworks and offers a "Synthesize & Augment" path.
|
|
27
|
+
- š§ **Dependency Fix** - Ensured all new analysis utilities are correctly required in the command entry point.
|
|
28
|
+
|
|
8
29
|
## [0.14.0] - 2026-01-12
|
|
9
30
|
|
|
10
31
|
### š Major Feature - Project Context Synthesis & Adaptive Augmentation
|
package/README.md
CHANGED
|
@@ -501,8 +501,18 @@ When we release updates to the framework:
|
|
|
501
501
|
|
|
502
502
|
See [CHANGELOG.md](./CHANGELOG.md) for detailed version history.
|
|
503
503
|
|
|
504
|
-
**Latest:** v0.
|
|
505
|
-
- **
|
|
504
|
+
**Latest:** v0.14.0 (2026-01-12)
|
|
505
|
+
- **Project Context Synthesis & Extended Tool Support (v0.14.0)** - Minor release
|
|
506
|
+
- ⨠**Adaptive Project Augmentation** - Synthesizes existing .adf, BMAD, OpenSpec, and Spec-Kit project files into unified context.
|
|
507
|
+
- ⨠**AI-Driven Gap Analysis** - Automatically identifies knowledge gaps and generates targeted questionnaire to fill them.
|
|
508
|
+
- ⨠**Extended Tool Support** - Native generators for Kiro, Trae, and Codex CLI.
|
|
509
|
+
- ⨠**Tool Audit Registry** - New `adf tools audit` command to monitor integration features across all 12+ supported tools.
|
|
510
|
+
|
|
511
|
+
**Previous Releases:**
|
|
512
|
+
- **v0.13.0 (2026-01-03)** - OpenCode Multi-Agent Integration
|
|
513
|
+
- Complete OpenCode generator rewrite with multi-provider, multi-agent support.
|
|
514
|
+
- Optimal model assignment and MCP server integration.
|
|
515
|
+
- **v0.12.10 (2025-12-23)** - Critical Bug Fixes (v0.12.10) - Patch release
|
|
506
516
|
- Fixed EISDIR deployment error for directory-based tools
|
|
507
517
|
- Improved rate limit error detection for Gemini models
|
|
508
518
|
- Added free tier model recommendations for Google Gemini
|
package/lib/ai/ai-config.js
CHANGED
|
@@ -215,20 +215,19 @@ async function validateAPIKeyWithProvider(provider, apiKey) {
|
|
|
215
215
|
break;
|
|
216
216
|
|
|
217
217
|
case 'google':
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
throw new Error('No models available for this API key');
|
|
218
|
+
try {
|
|
219
|
+
const { GoogleGenerativeAI } = require('@google/generative-ai');
|
|
220
|
+
const genAI = new GoogleGenerativeAI(apiKey);
|
|
221
|
+
// Use gemini-1.5-flash as it's the most available model for validation
|
|
222
|
+
const model = genAI.getGenerativeModel({ model: 'gemini-1.5-flash' });
|
|
223
|
+
// countTokens is a metadata call that validates the API key without incurring costs
|
|
224
|
+
await model.countTokens("connectivity test");
|
|
225
|
+
} catch (error) {
|
|
226
|
+
// If SDK fails, provide a clearer error message
|
|
227
|
+
if (error.message.includes('API_KEY_INVALID') || error.message.includes('API key not found')) {
|
|
228
|
+
throw new Error('Invalid Google API key. Please check your key at https://aistudio.google.com/app/apikey');
|
|
229
|
+
}
|
|
230
|
+
throw error;
|
|
232
231
|
}
|
|
233
232
|
break;
|
|
234
233
|
|
|
@@ -332,7 +331,7 @@ async function fetchAvailableModels(provider, apiKey) {
|
|
|
332
331
|
|
|
333
332
|
// Use Google's REST API to list available models
|
|
334
333
|
const response = await fetch(
|
|
335
|
-
`https://generativelanguage.googleapis.com/v1beta/models?key=${apiKey}`
|
|
334
|
+
`https://generativelanguage.googleapis.com/v1beta/models?key=${encodeURIComponent(apiKey)}`
|
|
336
335
|
);
|
|
337
336
|
|
|
338
337
|
if (!response.ok) {
|
package/lib/commands/init.js
CHANGED
|
@@ -7,6 +7,12 @@ const {
|
|
|
7
7
|
detectProjectType,
|
|
8
8
|
getWorkflowRecommendation
|
|
9
9
|
} = require('../utils/project-detector');
|
|
10
|
+
const FrameworkDetector = require('../utils/framework-detector');
|
|
11
|
+
const ContextExtractor = require('../utils/context-extractor');
|
|
12
|
+
const SynthesisEngine = require('../analysis/synthesis-engine');
|
|
13
|
+
const HeuristicGapAnalyzer = require('../analysis/heuristic-gap-analyzer');
|
|
14
|
+
const AIGapAnalyzer = require('../analysis/ai-gap-analyzer');
|
|
15
|
+
const DynamicQuestionGenerator = require('../analysis/dynamic-question-generator');
|
|
10
16
|
const Interviewer = require('../frameworks/interviewer');
|
|
11
17
|
const SessionManager = require('../frameworks/session-manager');
|
|
12
18
|
const { deployToTool } = require('./deploy');
|
|
@@ -24,6 +30,94 @@ async function init(options) {
|
|
|
24
30
|
loadEnvIntoProcess(envPath);
|
|
25
31
|
}
|
|
26
32
|
|
|
33
|
+
// Detect existing frameworks for potential synthesis
|
|
34
|
+
const detectedFrameworks = await FrameworkDetector.detect(cwd);
|
|
35
|
+
const hasOtherFrameworks = detectedFrameworks.filter(f => f !== 'adf').length > 0;
|
|
36
|
+
|
|
37
|
+
if (hasOtherFrameworks) {
|
|
38
|
+
console.log(chalk.cyan('š¦ Existing Development Frameworks Detected:'));
|
|
39
|
+
console.log(chalk.gray(` ${detectedFrameworks.join(', ')}\n`));
|
|
40
|
+
|
|
41
|
+
const { action } = await inquirer.prompt([
|
|
42
|
+
{
|
|
43
|
+
type: 'list',
|
|
44
|
+
name: 'action',
|
|
45
|
+
message: 'How would you like to proceed?',
|
|
46
|
+
choices: [
|
|
47
|
+
{
|
|
48
|
+
name: 'Synthesize & Augment (Seamless Merge - Recommended)',
|
|
49
|
+
value: 'synthesize'
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
name: 'Start Fresh (Ignore existing frameworks)',
|
|
53
|
+
value: 'fresh'
|
|
54
|
+
},
|
|
55
|
+
new inquirer.Separator(),
|
|
56
|
+
{
|
|
57
|
+
name: chalk.gray('ā Exit'),
|
|
58
|
+
value: 'exit'
|
|
59
|
+
}
|
|
60
|
+
]
|
|
61
|
+
}
|
|
62
|
+
]);
|
|
63
|
+
|
|
64
|
+
if (action === 'exit') return;
|
|
65
|
+
|
|
66
|
+
if (action === 'synthesize') {
|
|
67
|
+
const spinner = ora('Synthesizing project context...').start();
|
|
68
|
+
try {
|
|
69
|
+
const context = await ContextExtractor.extract(cwd, detectedFrameworks);
|
|
70
|
+
const sessionPath = await SynthesisEngine.createAugmentedSession(cwd, context, 'balanced');
|
|
71
|
+
spinner.succeed(chalk.green(`ā Project synthesized into new session: ${path.basename(sessionPath)}`));
|
|
72
|
+
|
|
73
|
+
console.log(chalk.yellow('\nš” Starting Adaptive Augmentation Interview to fill knowledge gaps...\n'));
|
|
74
|
+
|
|
75
|
+
// Load AI config for the interviewer
|
|
76
|
+
const aiConfig = await configureAIProvider(cwd);
|
|
77
|
+
const sessionProgress = await fs.readJson(path.join(sessionPath, '_progress.json'));
|
|
78
|
+
|
|
79
|
+
// Perform Knowledge Gap Analysis
|
|
80
|
+
console.log(chalk.cyan('š Analyzing for knowledge gaps...'));
|
|
81
|
+
const hGaps = HeuristicGapAnalyzer.analyze(context, 'balanced');
|
|
82
|
+
|
|
83
|
+
let aiGaps = [];
|
|
84
|
+
if (aiConfig) {
|
|
85
|
+
const AIClient = require('../ai/ai-client');
|
|
86
|
+
const aiClient = new AIClient(aiConfig);
|
|
87
|
+
const aiAnalyzer = new AIGapAnalyzer(aiClient);
|
|
88
|
+
aiGaps = await aiAnalyzer.analyze(context);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const dynamicQuestions = DynamicQuestionGenerator.generate(hGaps, aiGaps);
|
|
92
|
+
|
|
93
|
+
if (dynamicQuestions.length === 0) {
|
|
94
|
+
console.log(chalk.green('ā Context is comprehensive. No additional questions needed.'));
|
|
95
|
+
const interviewer = new Interviewer('balanced', cwd, {
|
|
96
|
+
sessionId: path.basename(sessionPath),
|
|
97
|
+
sessionPath,
|
|
98
|
+
progress: sessionProgress
|
|
99
|
+
}, aiConfig);
|
|
100
|
+
await interviewer.generateOutputs();
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
console.log(chalk.yellow(`\nš” Identified ${dynamicQuestions.length} gaps. Starting targeted interview...\n`));
|
|
105
|
+
|
|
106
|
+
const interviewer = new Interviewer('balanced', cwd, {
|
|
107
|
+
sessionId: path.basename(sessionPath),
|
|
108
|
+
sessionPath,
|
|
109
|
+
progress: sessionProgress
|
|
110
|
+
}, aiConfig, dynamicQuestions);
|
|
111
|
+
|
|
112
|
+
await interviewer.start();
|
|
113
|
+
return;
|
|
114
|
+
} catch (error) {
|
|
115
|
+
spinner.fail(chalk.red(`Synthesis failed: ${error.message}`));
|
|
116
|
+
console.log(chalk.yellow('Falling back to standard initialization...\n'));
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
27
121
|
// Check for resumable sessions FIRST (before asking to overwrite)
|
|
28
122
|
const sessionManager = new SessionManager(cwd);
|
|
29
123
|
const existingSession = await sessionManager.promptToResume();
|
|
@@ -21,10 +21,11 @@ const DynamicPipeline = require('../analysis/dynamic-pipeline');
|
|
|
21
21
|
*/
|
|
22
22
|
|
|
23
23
|
class Interviewer {
|
|
24
|
-
constructor(framework, projectPath, existingSession = null, aiConfig = null) {
|
|
24
|
+
constructor(framework, projectPath, existingSession = null, aiConfig = null, customQuestions = null) {
|
|
25
25
|
this.framework = framework;
|
|
26
26
|
this.projectPath = projectPath;
|
|
27
27
|
this.aiConfig = aiConfig; // Store AI configuration
|
|
28
|
+
this.customQuestions = customQuestions; // Dynamic questions for augmentation
|
|
28
29
|
|
|
29
30
|
if (existingSession) {
|
|
30
31
|
// Resuming existing session
|
|
@@ -293,8 +294,8 @@ class Interviewer {
|
|
|
293
294
|
projectContext = { type: 'unknown', frameworks: [], languages: [], confidence: 0 };
|
|
294
295
|
}
|
|
295
296
|
|
|
296
|
-
// Get questions for framework
|
|
297
|
-
let questions = getQuestionsForFramework(this.framework);
|
|
297
|
+
// Get questions for framework (or use custom ones if provided)
|
|
298
|
+
let questions = this.customQuestions || getQuestionsForFramework(this.framework);
|
|
298
299
|
|
|
299
300
|
// Apply smart filtering if enabled
|
|
300
301
|
let filteringResult = null;
|