@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.
Files changed (70) hide show
  1. package/.context/memory/architecture.md +40 -0
  2. package/.context/memory/glossary.md +19 -0
  3. package/.project/docs/VERSIONING-GUIDE.md +127 -0
  4. package/AGENTS.md +53 -0
  5. package/CHANGELOG.md +47 -0
  6. package/bin/adf.js +10 -0
  7. package/conductor/archive/context_synthesis_20260112/metadata.json +8 -0
  8. package/conductor/archive/context_synthesis_20260112/plan.md +40 -0
  9. package/conductor/archive/context_synthesis_20260112/spec.md +43 -0
  10. package/conductor/archive/verify_opencode_20260111/metadata.json +8 -0
  11. package/conductor/archive/verify_opencode_20260111/plan.md +34 -0
  12. package/conductor/archive/verify_opencode_20260111/spec.md +25 -0
  13. package/conductor/code_styleguides/javascript.md +51 -0
  14. package/conductor/product-guidelines.md +26 -0
  15. package/conductor/product.md +25 -0
  16. package/conductor/setup_state.json +1 -0
  17. package/conductor/tech-stack.md +28 -0
  18. package/conductor/tracks/bootstrap_agents_20260111/metadata.json +8 -0
  19. package/conductor/tracks/bootstrap_agents_20260111/plan.md +17 -0
  20. package/conductor/tracks/bootstrap_agents_20260111/spec.md +27 -0
  21. package/conductor/tracks.md +9 -0
  22. package/conductor/workflow.md +333 -0
  23. package/lib/analysis/ai-gap-analyzer.js +66 -0
  24. package/lib/analysis/dynamic-question-generator.js +55 -0
  25. package/lib/analysis/heuristic-gap-analyzer.js +45 -0
  26. package/lib/analysis/synthesis-engine.js +142 -0
  27. package/lib/commands/deploy.js +28 -2
  28. package/lib/commands/tools.js +38 -0
  29. package/lib/generators/codex-cli-generator.js +41 -0
  30. package/lib/generators/index.js +33 -0
  31. package/lib/generators/kiro-generator.js +49 -0
  32. package/lib/generators/opencode-generator.js +332 -313
  33. package/lib/generators/trae-generator.js +34 -0
  34. package/lib/templates/scripts/analyze-framework-updates.js +361 -0
  35. package/lib/templates/scripts/build.js +608 -0
  36. package/lib/templates/scripts/check-framework-updates.js +118 -0
  37. package/lib/templates/scripts/config-helpers.js +1 -1
  38. package/lib/templates/scripts/deploy.js +13 -28
  39. package/lib/templates/scripts/init.js +110 -220
  40. package/lib/templates/scripts/postinstall.js +13 -0
  41. package/lib/templates/scripts/update-frameworks.js +28 -0
  42. package/lib/templates/scripts/validate-env.js +428 -0
  43. package/lib/templates/scripts/validate-mcp.js +471 -0
  44. package/lib/templates/scripts/validate.js +482 -0
  45. package/lib/templates/shared/agents/analyst.md +1 -1
  46. package/lib/templates/shared/agents/architect.md +13 -17
  47. package/lib/templates/shared/agents/dev.md +2 -2
  48. package/lib/templates/shared/agents/pm.md +1 -1
  49. package/lib/templates/shared/agents/qa.md +1 -1
  50. package/lib/templates/shared/agents/sm.md +2 -2
  51. package/lib/templates/shared/templates/README.md +2 -2
  52. package/lib/templates/shared/templates/openspec-proposal.md +2 -2
  53. package/lib/templates/shared/templates/prd-template.md +1 -1
  54. package/lib/templates/shared/templates/story-template.md +1 -1
  55. package/lib/utils/context-extractor.js +157 -0
  56. package/lib/utils/framework-detector.js +54 -0
  57. package/lib/utils/tool-feature-registry.js +102 -0
  58. package/package.json +1 -1
  59. package/tests/ai-gap-analyzer.test.js +38 -0
  60. package/tests/codex-cli-generator.test.js +29 -0
  61. package/tests/context-extractor.test.js +70 -0
  62. package/tests/deploy-integration.test.js +36 -0
  63. package/tests/deploy.test.js +57 -0
  64. package/tests/dynamic-question-generator.test.js +29 -0
  65. package/tests/framework-detector.test.js +55 -0
  66. package/tests/heuristic-gap-analyzer.test.js +46 -0
  67. package/tests/kiro-trae-generators.test.js +43 -0
  68. package/tests/opencode-generator.test.js +113 -0
  69. package/tests/synthesis-engine.test.js +52 -0
  70. 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
+ });