@iservu-inc/adf-cli 0.13.0 → 0.14.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/.context/memory/architecture.md +40 -0
- package/.context/memory/glossary.md +19 -0
- package/.project/docs/VERSIONING-GUIDE.md +127 -0
- package/AGENTS.md +53 -0
- package/CHANGELOG.md +47 -0
- package/bin/adf.js +10 -0
- package/conductor/archive/context_synthesis_20260112/metadata.json +8 -0
- package/conductor/archive/context_synthesis_20260112/plan.md +40 -0
- package/conductor/archive/context_synthesis_20260112/spec.md +43 -0
- package/conductor/archive/verify_opencode_20260111/metadata.json +8 -0
- package/conductor/archive/verify_opencode_20260111/plan.md +34 -0
- package/conductor/archive/verify_opencode_20260111/spec.md +25 -0
- package/conductor/code_styleguides/javascript.md +51 -0
- package/conductor/product-guidelines.md +26 -0
- package/conductor/product.md +25 -0
- package/conductor/setup_state.json +1 -0
- package/conductor/tech-stack.md +28 -0
- package/conductor/tracks/bootstrap_agents_20260111/metadata.json +8 -0
- package/conductor/tracks/bootstrap_agents_20260111/plan.md +17 -0
- package/conductor/tracks/bootstrap_agents_20260111/spec.md +27 -0
- package/conductor/tracks.md +9 -0
- package/conductor/workflow.md +333 -0
- package/lib/analysis/ai-gap-analyzer.js +66 -0
- package/lib/analysis/dynamic-question-generator.js +55 -0
- package/lib/analysis/heuristic-gap-analyzer.js +45 -0
- package/lib/analysis/synthesis-engine.js +142 -0
- package/lib/commands/deploy.js +28 -2
- package/lib/commands/tools.js +38 -0
- package/lib/generators/codex-cli-generator.js +41 -0
- package/lib/generators/index.js +33 -0
- package/lib/generators/kiro-generator.js +49 -0
- package/lib/generators/opencode-generator.js +332 -313
- package/lib/generators/trae-generator.js +34 -0
- package/lib/templates/scripts/analyze-framework-updates.js +361 -0
- package/lib/templates/scripts/build.js +608 -0
- package/lib/templates/scripts/check-framework-updates.js +118 -0
- package/lib/templates/scripts/config-helpers.js +1 -1
- package/lib/templates/scripts/deploy.js +13 -28
- package/lib/templates/scripts/init.js +110 -220
- package/lib/templates/scripts/postinstall.js +13 -0
- package/lib/templates/scripts/update-frameworks.js +28 -0
- package/lib/templates/scripts/validate-env.js +428 -0
- package/lib/templates/scripts/validate-mcp.js +471 -0
- package/lib/templates/scripts/validate.js +482 -0
- package/lib/templates/shared/agents/analyst.md +1 -1
- package/lib/templates/shared/agents/architect.md +13 -17
- package/lib/templates/shared/agents/dev.md +2 -2
- package/lib/templates/shared/agents/pm.md +1 -1
- package/lib/templates/shared/agents/qa.md +1 -1
- package/lib/templates/shared/agents/sm.md +2 -2
- package/lib/templates/shared/templates/README.md +2 -2
- package/lib/templates/shared/templates/openspec-proposal.md +2 -2
- package/lib/templates/shared/templates/prd-template.md +1 -1
- package/lib/templates/shared/templates/story-template.md +1 -1
- package/lib/utils/context-extractor.js +157 -0
- package/lib/utils/framework-detector.js +54 -0
- package/lib/utils/tool-feature-registry.js +102 -0
- package/package.json +1 -1
- package/tests/ai-gap-analyzer.test.js +38 -0
- package/tests/codex-cli-generator.test.js +29 -0
- package/tests/context-extractor.test.js +70 -0
- package/tests/deploy-integration.test.js +36 -0
- package/tests/deploy.test.js +57 -0
- package/tests/dynamic-question-generator.test.js +29 -0
- package/tests/framework-detector.test.js +55 -0
- package/tests/heuristic-gap-analyzer.test.js +46 -0
- package/tests/kiro-trae-generators.test.js +43 -0
- package/tests/opencode-generator.test.js +113 -0
- package/tests/synthesis-engine.test.js +52 -0
- package/tests/tool-feature-registry.test.js +23 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const KiroGenerator = require('../lib/generators/kiro-generator');
|
|
4
|
+
const TraeGenerator = require('../lib/generators/trae-generator');
|
|
5
|
+
|
|
6
|
+
describe('Kiro and Trae Generators', () => {
|
|
7
|
+
const tempDir = path.join(__dirname, 'temp-tool-test');
|
|
8
|
+
const sessionPath = path.join(tempDir, '.adf/sessions/test-session');
|
|
9
|
+
|
|
10
|
+
beforeEach(async () => {
|
|
11
|
+
await fs.ensureDir(path.join(sessionPath, 'outputs'));
|
|
12
|
+
// Mock outputs
|
|
13
|
+
await fs.writeFile(path.join(sessionPath, 'outputs/constitution.md'), '## Overview\nTest Overview\n## Key Principles\nPrinciple 1');
|
|
14
|
+
await fs.writeJson(path.join(sessionPath, '_metadata.json'), { projectName: 'Test Project' });
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
afterEach(async () => {
|
|
18
|
+
await fs.remove(tempDir);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test('KiroGenerator should create product and technical markdown files', async () => {
|
|
22
|
+
const generator = new KiroGenerator(sessionPath, tempDir, 'balanced');
|
|
23
|
+
await generator.generate();
|
|
24
|
+
|
|
25
|
+
expect(await fs.pathExists(path.join(tempDir, '.kiro/product.md'))).toBe(true);
|
|
26
|
+
expect(await fs.pathExists(path.join(tempDir, '.kiro/technical.md'))).toBe(true);
|
|
27
|
+
|
|
28
|
+
const productContent = await fs.readFile(path.join(tempDir, '.kiro/product.md'), 'utf-8');
|
|
29
|
+
expect(productContent).toContain('Test Project');
|
|
30
|
+
expect(productContent).toContain('Test Overview');
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test('TraeGenerator should create config.json', async () => {
|
|
34
|
+
const generator = new TraeGenerator(sessionPath, tempDir, 'balanced');
|
|
35
|
+
await generator.generate();
|
|
36
|
+
|
|
37
|
+
expect(await fs.pathExists(path.join(tempDir, '.trae/config.json'))).toBe(true);
|
|
38
|
+
|
|
39
|
+
const config = await fs.readJson(path.join(tempDir, '.trae/config.json'));
|
|
40
|
+
expect(config.framework.name).toBe('AgentDevFramework');
|
|
41
|
+
expect(config.project_structure.docs).toBe('docs/');
|
|
42
|
+
});
|
|
43
|
+
});
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const OpenCodeGenerator = require('../lib/generators/opencode-generator');
|
|
4
|
+
|
|
5
|
+
const TEST_PROJECT_PATH = path.join(__dirname, 'test-project-opencode');
|
|
6
|
+
const TEST_SESSION_PATH = path.join(TEST_PROJECT_PATH, '.adf', 'sessions', 'test-session');
|
|
7
|
+
|
|
8
|
+
describe('OpenCodeGenerator', () => {
|
|
9
|
+
beforeEach(async () => {
|
|
10
|
+
// Clean up test directories
|
|
11
|
+
await fs.remove(TEST_PROJECT_PATH);
|
|
12
|
+
await fs.ensureDir(TEST_PROJECT_PATH);
|
|
13
|
+
await fs.ensureDir(TEST_SESSION_PATH);
|
|
14
|
+
await fs.ensureDir(path.join(TEST_SESSION_PATH, 'outputs'));
|
|
15
|
+
|
|
16
|
+
// Create mock .env file
|
|
17
|
+
await fs.ensureDir(path.join(TEST_PROJECT_PATH, '.adf'));
|
|
18
|
+
await fs.writeFile(
|
|
19
|
+
path.join(TEST_PROJECT_PATH, '.adf', '.env'),
|
|
20
|
+
'ANTHROPIC_API_KEY=test-key\nOPENAI_API_KEY=test-key',
|
|
21
|
+
'utf-8'
|
|
22
|
+
);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
afterEach(async () => {
|
|
26
|
+
// Clean up after tests
|
|
27
|
+
await fs.remove(TEST_PROJECT_PATH);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('should generate opencode.json with correct filename and standard schema', async () => {
|
|
31
|
+
// Create mock PRP output
|
|
32
|
+
const prpContent = '# Product Requirement Prompt\n\n## 1. Goal Definition\nTest project';
|
|
33
|
+
await fs.writeFile(
|
|
34
|
+
path.join(TEST_SESSION_PATH, 'outputs', 'prp.md'),
|
|
35
|
+
prpContent,
|
|
36
|
+
'utf-8'
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
// Create metadata
|
|
40
|
+
await fs.writeJson(path.join(TEST_SESSION_PATH, '_metadata.json'), {
|
|
41
|
+
framework: 'rapid',
|
|
42
|
+
projectName: 'Test Project'
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const generator = new OpenCodeGenerator(TEST_SESSION_PATH, TEST_PROJECT_PATH, 'rapid');
|
|
46
|
+
const result = await generator.generate();
|
|
47
|
+
|
|
48
|
+
// 1. Verify filename is opencode.json (not .opencode.json)
|
|
49
|
+
const configPath = path.join(TEST_PROJECT_PATH, 'opencode.json');
|
|
50
|
+
expect(await fs.pathExists(configPath)).toBe(true);
|
|
51
|
+
expect(result.config).toBe(configPath);
|
|
52
|
+
|
|
53
|
+
// 2. Read config
|
|
54
|
+
const config = await fs.readJson(configPath);
|
|
55
|
+
|
|
56
|
+
// 3. Verify plural keys (latest schema)
|
|
57
|
+
expect(config.providers).toBeDefined();
|
|
58
|
+
expect(config.agents).toBeDefined();
|
|
59
|
+
expect(config.mcpServers).toBeDefined();
|
|
60
|
+
|
|
61
|
+
// 4. Verify singular keys are NOT present
|
|
62
|
+
expect(config.provider).toBeUndefined();
|
|
63
|
+
expect(config.agent).toBeUndefined();
|
|
64
|
+
expect(config.mcp).toBeUndefined();
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('should support small_model field', async () => {
|
|
68
|
+
const generator = new OpenCodeGenerator(TEST_SESSION_PATH, TEST_PROJECT_PATH, 'rapid');
|
|
69
|
+
const config = await generator.generateConfig();
|
|
70
|
+
|
|
71
|
+
expect(config.small_model).toBeDefined();
|
|
72
|
+
expect(typeof config.small_model).toBe('string');
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('should validate configuration before writing', async () => {
|
|
76
|
+
const generator = new OpenCodeGenerator(TEST_SESSION_PATH, TEST_PROJECT_PATH, 'rapid');
|
|
77
|
+
|
|
78
|
+
// Spy on validation method
|
|
79
|
+
const validateSpy = jest.spyOn(generator, 'validateConfig');
|
|
80
|
+
|
|
81
|
+
// Mock validateConfig to throw if not implemented yet (this test expects the method to exist)
|
|
82
|
+
if (!generator.validateConfig) {
|
|
83
|
+
generator.validateConfig = () => { throw new Error('Validation not implemented'); };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
await generator.generate();
|
|
88
|
+
} catch (e) {
|
|
89
|
+
// Ignore generation errors, we just want to check if validate was called
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// This test will fail if validateConfig doesn't exist or isn't called
|
|
93
|
+
// We expect it to fail initially as the method doesn't exist
|
|
94
|
+
expect(generator.validateConfig).toBeDefined();
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('should handle all supported providers correctly', async () => {
|
|
98
|
+
// Overwrite .env with all providers
|
|
99
|
+
await fs.writeFile(
|
|
100
|
+
path.join(TEST_PROJECT_PATH, '.adf', '.env'),
|
|
101
|
+
'ANTHROPIC_API_KEY=key\nOPENAI_API_KEY=key\nGEMINI_API_KEY=key\nOPENROUTER_API_KEY=key',
|
|
102
|
+
'utf-8'
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
const generator = new OpenCodeGenerator(TEST_SESSION_PATH, TEST_PROJECT_PATH, 'rapid');
|
|
106
|
+
const config = await generator.generateConfig();
|
|
107
|
+
|
|
108
|
+
expect(config.providers.anthropic).toBeDefined();
|
|
109
|
+
expect(config.providers.openai).toBeDefined();
|
|
110
|
+
expect(config.providers.google).toBeDefined();
|
|
111
|
+
expect(config.providers.openrouter).toBeDefined();
|
|
112
|
+
});
|
|
113
|
+
});
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const SynthesisEngine = require('../lib/analysis/synthesis-engine');
|
|
4
|
+
|
|
5
|
+
describe('Synthesis Engine', () => {
|
|
6
|
+
const tempDir = path.join(__dirname, 'temp-synthesis-test');
|
|
7
|
+
|
|
8
|
+
beforeEach(async () => {
|
|
9
|
+
await fs.ensureDir(tempDir);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
afterEach(async () => {
|
|
13
|
+
await fs.remove(tempDir);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
test('should merge overlapping context data', () => {
|
|
17
|
+
const contexts = [
|
|
18
|
+
{ name: 'Project A', overview: 'Overview 1', techStack: 'Node.js' },
|
|
19
|
+
{ name: 'Project B', overview: 'Overview 2', techStack: 'React' }
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
const merged = SynthesisEngine.merge(contexts);
|
|
23
|
+
|
|
24
|
+
expect(merged.name).toBe('Project B'); // Last one wins for singular fields
|
|
25
|
+
expect(merged.overview).toContain('Overview 1');
|
|
26
|
+
expect(merged.overview).toContain('Overview 2');
|
|
27
|
+
expect(merged.techStack).toContain('Node.js');
|
|
28
|
+
expect(merged.techStack).toContain('React');
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test('should create a new augmented session folder', async () => {
|
|
32
|
+
const mergedContext = {
|
|
33
|
+
name: 'Test Project',
|
|
34
|
+
overview: 'Synthesized overview',
|
|
35
|
+
techStack: 'Node, React'
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const adfDir = path.join(tempDir, '.adf');
|
|
39
|
+
await fs.ensureDir(adfDir);
|
|
40
|
+
|
|
41
|
+
const sessionPath = await SynthesisEngine.createAugmentedSession(tempDir, mergedContext, 'balanced');
|
|
42
|
+
|
|
43
|
+
expect(sessionPath).toContain('augmentation');
|
|
44
|
+
expect(await fs.pathExists(sessionPath)).toBe(true);
|
|
45
|
+
|
|
46
|
+
const progress = await fs.readJson(path.join(sessionPath, '_progress.json'));
|
|
47
|
+
expect(progress.status).toBe('in-progress');
|
|
48
|
+
// Pre-filled answers for the interviewer
|
|
49
|
+
expect(progress.answers['project-name'].text).toBe('Test Project');
|
|
50
|
+
expect(progress.answers['project-overview'].text).toBe('Synthesized overview');
|
|
51
|
+
});
|
|
52
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const ToolFeatureRegistry = require('../lib/utils/tool-feature-registry');
|
|
2
|
+
|
|
3
|
+
describe('Tool Feature Registry', () => {
|
|
4
|
+
test('should list all supported tools', () => {
|
|
5
|
+
const tools = ToolFeatureRegistry.listTools();
|
|
6
|
+
expect(tools).toContain('kiro');
|
|
7
|
+
expect(tools).toContain('trae');
|
|
8
|
+
expect(tools).toContain('codex-cli');
|
|
9
|
+
expect(tools).toContain('windsurf');
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
test('should get features for a specific tool', () => {
|
|
13
|
+
const features = ToolFeatureRegistry.getFeatures('kiro');
|
|
14
|
+
expect(features.supported).toBe(true);
|
|
15
|
+
expect(features.configFiles).toContain('.kiro/product.md');
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test('should audit tools and return status report', () => {
|
|
19
|
+
const audit = ToolFeatureRegistry.audit();
|
|
20
|
+
expect(audit.totalCount).toBeGreaterThanOrEqual(10);
|
|
21
|
+
expect(audit.tools.kiro.status).toBe('supported');
|
|
22
|
+
});
|
|
23
|
+
});
|