dhurandhar 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/.dhurandhar-session-start.md +242 -0
- package/LICENSE +21 -0
- package/README.md +416 -0
- package/docs/ARCHITECTURE_V2.md +249 -0
- package/docs/DECISION_REGISTRY.md +357 -0
- package/docs/IMPLEMENTATION_PERSONAS.md +406 -0
- package/docs/PLUGGABLE_STRATEGIES.md +439 -0
- package/docs/SYSTEM_OBSERVER.md +433 -0
- package/docs/TEST_FIRST_AGILE.md +359 -0
- package/docs/architecture.md +279 -0
- package/docs/engineering-first-philosophy.md +263 -0
- package/docs/getting-started.md +218 -0
- package/docs/module-development.md +323 -0
- package/docs/strategy-example.md +299 -0
- package/docs/test-first-example.md +392 -0
- package/package.json +79 -0
- package/src/core/README.md +92 -0
- package/src/core/agent-instructions/backend-developer.md +412 -0
- package/src/core/agent-instructions/devops-engineer.md +372 -0
- package/src/core/agent-instructions/dhurandhar-council.md +547 -0
- package/src/core/agent-instructions/edge-case-hunter.md +322 -0
- package/src/core/agent-instructions/frontend-developer.md +494 -0
- package/src/core/agent-instructions/lead-system-architect.md +631 -0
- package/src/core/agent-instructions/system-observer.md +319 -0
- package/src/core/agent-instructions/test-architect.md +284 -0
- package/src/core/module.yaml +54 -0
- package/src/core/schemas/design-module-schema.yaml +995 -0
- package/src/core/schemas/system-design-map-schema.yaml +324 -0
- package/src/modules/example/README.md +130 -0
- package/src/modules/example/module.yaml +252 -0
- package/tools/cli/commands/audit.js +267 -0
- package/tools/cli/commands/config.js +113 -0
- package/tools/cli/commands/context.js +170 -0
- package/tools/cli/commands/decisions.js +398 -0
- package/tools/cli/commands/entity.js +218 -0
- package/tools/cli/commands/epic.js +125 -0
- package/tools/cli/commands/install.js +172 -0
- package/tools/cli/commands/module.js +109 -0
- package/tools/cli/commands/service.js +167 -0
- package/tools/cli/commands/story.js +225 -0
- package/tools/cli/commands/strategy.js +294 -0
- package/tools/cli/commands/test.js +277 -0
- package/tools/cli/commands/validate.js +107 -0
- package/tools/cli/dhurandhar.js +212 -0
- package/tools/lib/config-manager.js +170 -0
- package/tools/lib/filesystem.js +126 -0
- package/tools/lib/module-installer.js +61 -0
- package/tools/lib/module-manager.js +149 -0
- package/tools/lib/sdm-manager.js +982 -0
- package/tools/lib/test-engine.js +255 -0
- package/tools/lib/test-templates/api-client.template.js +100 -0
- package/tools/lib/test-templates/vitest.config.template.js +37 -0
- package/tools/lib/validators/config-validator.js +113 -0
- package/tools/lib/validators/module-validator.js +137 -0
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test Command - Contract-First Testing
|
|
3
|
+
* Generate and validate test suites from Agile Blueprint
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import * as clack from '@clack/prompts';
|
|
7
|
+
import chalk from 'chalk';
|
|
8
|
+
import { SDMManager } from '../../lib/sdm-manager.js';
|
|
9
|
+
import { ConfigManager } from '../../lib/config-manager.js';
|
|
10
|
+
import { TestEngine } from '../../lib/test-engine.js';
|
|
11
|
+
import { existsSync } from 'fs';
|
|
12
|
+
import { readFile, writeFile, copyFile } from 'fs/promises';
|
|
13
|
+
import { join } from 'path';
|
|
14
|
+
|
|
15
|
+
export async function testCommand(options) {
|
|
16
|
+
try {
|
|
17
|
+
const sdmManager = new SDMManager(process.cwd());
|
|
18
|
+
const configManager = new ConfigManager(process.cwd());
|
|
19
|
+
|
|
20
|
+
if (!configManager.exists()) {
|
|
21
|
+
clack.log.error(chalk.red('Framework not installed.'));
|
|
22
|
+
clack.log.info('Run: dhurandhar install');
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (!sdmManager.exists()) {
|
|
27
|
+
clack.log.error(chalk.red('SYSTEM_DESIGN_MAP.yaml not found.'));
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const testEngine = new TestEngine(process.cwd());
|
|
32
|
+
|
|
33
|
+
if (options.generate) {
|
|
34
|
+
// Generate test suite from Agile Blueprint
|
|
35
|
+
await generateTestSuite(sdmManager, testEngine);
|
|
36
|
+
|
|
37
|
+
} else if (options.validate) {
|
|
38
|
+
// Validate implementation against Agile Blueprint
|
|
39
|
+
await validateCoverage(sdmManager, testEngine);
|
|
40
|
+
|
|
41
|
+
} else if (options.edgeCases) {
|
|
42
|
+
// Generate edge case tests for specific story
|
|
43
|
+
await generateEdgeCases(sdmManager, testEngine, options.edgeCases);
|
|
44
|
+
|
|
45
|
+
} else if (options.init) {
|
|
46
|
+
// Initialize test infrastructure
|
|
47
|
+
await initializeTestInfra(testEngine);
|
|
48
|
+
|
|
49
|
+
} else {
|
|
50
|
+
// Show help
|
|
51
|
+
console.log(chalk.cyan('Usage:'));
|
|
52
|
+
console.log(' dhurandhar test --init Initialize test infrastructure');
|
|
53
|
+
console.log(' dhurandhar test --generate Generate tests from Agile Blueprint');
|
|
54
|
+
console.log(' dhurandhar test --validate Validate coverage against Stories');
|
|
55
|
+
console.log(' dhurandhar test --edge-cases ID Generate edge cases for Story');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
} catch (error) {
|
|
59
|
+
clack.log.error(chalk.red('Test operation failed:'), error.message);
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Generate test suite from SDM Agile Blueprint
|
|
66
|
+
*/
|
|
67
|
+
async function generateTestSuite(sdmManager, testEngine) {
|
|
68
|
+
clack.intro(chalk.cyan.bold('📋 Generate Contract-First Test Suite'));
|
|
69
|
+
|
|
70
|
+
const sdm = await sdmManager.load();
|
|
71
|
+
|
|
72
|
+
if (!sdm.agile_blueprint || !sdm.agile_blueprint.epics) {
|
|
73
|
+
clack.log.warn('No Agile Blueprint found in SDM.');
|
|
74
|
+
clack.log.info('Add Epics and Stories first: dhurandhar epic add');
|
|
75
|
+
process.exit(0);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const spinner = clack.spinner();
|
|
79
|
+
spinner.start('Generating tests from Agile Blueprint...');
|
|
80
|
+
|
|
81
|
+
// Initialize test structure
|
|
82
|
+
await testEngine.initializeTestStructure();
|
|
83
|
+
|
|
84
|
+
let totalTests = 0;
|
|
85
|
+
const generatedFiles = [];
|
|
86
|
+
|
|
87
|
+
// Iterate through all Epics and Stories
|
|
88
|
+
for (const epic of sdm.agile_blueprint.epics) {
|
|
89
|
+
for (const story of epic.stories) {
|
|
90
|
+
spinner.message(`Generating tests for ${story.id}: ${story.name}`);
|
|
91
|
+
|
|
92
|
+
// Generate test suite for this story
|
|
93
|
+
const tests = await testEngine.generateStoryTests(story, epic);
|
|
94
|
+
|
|
95
|
+
// Save tests to files
|
|
96
|
+
const files = await testEngine.saveTests(story, tests);
|
|
97
|
+
generatedFiles.push(...files);
|
|
98
|
+
totalTests += 3; // standard, errors, edge cases
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
spinner.stop(`Generated ${totalTests} test suites`);
|
|
103
|
+
|
|
104
|
+
// Copy test utilities
|
|
105
|
+
await copyTestUtilities();
|
|
106
|
+
|
|
107
|
+
// Generate package.json test scripts
|
|
108
|
+
await addTestScripts();
|
|
109
|
+
|
|
110
|
+
clack.outro(chalk.green('✓ Contract-first test suite generated'));
|
|
111
|
+
|
|
112
|
+
console.log('');
|
|
113
|
+
console.log(chalk.cyan('Generated files:'));
|
|
114
|
+
generatedFiles.slice(0, 10).forEach(file => {
|
|
115
|
+
console.log(` ${chalk.dim(file)}`);
|
|
116
|
+
});
|
|
117
|
+
if (generatedFiles.length > 10) {
|
|
118
|
+
console.log(` ${chalk.dim(`... and ${generatedFiles.length - 10} more`)}`);
|
|
119
|
+
}
|
|
120
|
+
console.log('');
|
|
121
|
+
console.log(chalk.cyan('Next steps:'));
|
|
122
|
+
console.log(' 1. Install test dependencies: npm install -D vitest');
|
|
123
|
+
console.log(' 2. Review generated tests in tests/ directory');
|
|
124
|
+
console.log(' 3. Run tests: npm test');
|
|
125
|
+
console.log(' 4. Implement services to pass the tests');
|
|
126
|
+
console.log('');
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Validate implementation coverage against Agile Blueprint
|
|
131
|
+
*/
|
|
132
|
+
async function validateCoverage(sdmManager, testEngine) {
|
|
133
|
+
clack.intro(chalk.cyan.bold('✅ Validate Story Coverage'));
|
|
134
|
+
|
|
135
|
+
const sdm = await sdmManager.load();
|
|
136
|
+
|
|
137
|
+
if (!sdm.agile_blueprint) {
|
|
138
|
+
clack.log.warn('No Agile Blueprint to validate.');
|
|
139
|
+
process.exit(0);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const results = {
|
|
143
|
+
total_stories: 0,
|
|
144
|
+
tested_stories: 0,
|
|
145
|
+
implemented_stories: 0,
|
|
146
|
+
uncovered_stories: [],
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
// Check each Story
|
|
150
|
+
for (const epic of sdm.agile_blueprint.epics || []) {
|
|
151
|
+
for (const story of epic.stories || []) {
|
|
152
|
+
results.total_stories++;
|
|
153
|
+
|
|
154
|
+
// Check if tests exist
|
|
155
|
+
const testFile = join(process.cwd(), 'tests', 'contracts',
|
|
156
|
+
`${story.id.toLowerCase()}-standard.test.js`);
|
|
157
|
+
|
|
158
|
+
if (existsSync(testFile)) {
|
|
159
|
+
results.tested_stories++;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Check if tasks are complete
|
|
163
|
+
const allTasksComplete = story.tasks?.every(t => t.status === 'complete');
|
|
164
|
+
if (allTasksComplete) {
|
|
165
|
+
results.implemented_stories++;
|
|
166
|
+
} else {
|
|
167
|
+
results.uncovered_stories.push({
|
|
168
|
+
id: story.id,
|
|
169
|
+
name: story.name,
|
|
170
|
+
has_tests: existsSync(testFile),
|
|
171
|
+
pending_tasks: story.tasks?.filter(t => t.status !== 'complete').length || 0,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Calculate coverage
|
|
178
|
+
const testCoverage = results.total_stories > 0
|
|
179
|
+
? ((results.tested_stories / results.total_stories) * 100).toFixed(1)
|
|
180
|
+
: 0;
|
|
181
|
+
|
|
182
|
+
const implCoverage = results.total_stories > 0
|
|
183
|
+
? ((results.implemented_stories / results.total_stories) * 100).toFixed(1)
|
|
184
|
+
: 0;
|
|
185
|
+
|
|
186
|
+
// Update SDM
|
|
187
|
+
sdm.agile_blueprint.test_suite_status = {
|
|
188
|
+
total_stories: results.total_stories,
|
|
189
|
+
tested_stories: results.tested_stories,
|
|
190
|
+
coverage_percentage: parseFloat(testCoverage),
|
|
191
|
+
last_test_run: new Date().toISOString(),
|
|
192
|
+
};
|
|
193
|
+
await sdmManager.save(sdm);
|
|
194
|
+
|
|
195
|
+
// Display results
|
|
196
|
+
console.log('');
|
|
197
|
+
console.log(chalk.bold('Coverage Report:'));
|
|
198
|
+
console.log(` Total Stories: ${results.total_stories}`);
|
|
199
|
+
console.log(` Test Coverage: ${testCoverage}% (${results.tested_stories}/${results.total_stories})`);
|
|
200
|
+
console.log(` Implementation: ${implCoverage}% (${results.implemented_stories}/${results.total_stories})`);
|
|
201
|
+
console.log('');
|
|
202
|
+
|
|
203
|
+
if (results.uncovered_stories.length > 0) {
|
|
204
|
+
console.log(chalk.yellow('Uncovered Stories:'));
|
|
205
|
+
results.uncovered_stories.forEach(story => {
|
|
206
|
+
const status = story.has_tests ? '🟡 Tests exist' : '🔴 No tests';
|
|
207
|
+
console.log(` ${status} ${story.id}: ${story.name}`);
|
|
208
|
+
console.log(` Pending tasks: ${story.pending_tasks}`);
|
|
209
|
+
});
|
|
210
|
+
console.log('');
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
clack.outro(testCoverage === '100.0' ? chalk.green('✓ Full coverage!') : chalk.yellow('⚠ Incomplete coverage'));
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Generate edge case tests
|
|
218
|
+
*/
|
|
219
|
+
async function generateEdgeCases(sdmManager, testEngine, storyId) {
|
|
220
|
+
// TODO: Implement edge case generation
|
|
221
|
+
console.log(chalk.cyan(`Generating edge cases for ${storyId}...`));
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Initialize test infrastructure
|
|
226
|
+
*/
|
|
227
|
+
async function initializeTestInfra(testEngine) {
|
|
228
|
+
clack.intro(chalk.cyan.bold('🔧 Initialize Test Infrastructure'));
|
|
229
|
+
|
|
230
|
+
const spinner = clack.spinner();
|
|
231
|
+
spinner.start('Setting up test structure...');
|
|
232
|
+
|
|
233
|
+
await testEngine.initializeTestStructure();
|
|
234
|
+
await copyTestUtilities();
|
|
235
|
+
await addTestScripts();
|
|
236
|
+
|
|
237
|
+
spinner.stop('Test infrastructure ready');
|
|
238
|
+
|
|
239
|
+
clack.outro(chalk.green('✓ Initialized'));
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Copy test utilities
|
|
244
|
+
*/
|
|
245
|
+
async function copyTestUtilities() {
|
|
246
|
+
const templatesDir = join(process.cwd(), 'tools/lib/test-templates');
|
|
247
|
+
const testUtilsDir = join(process.cwd(), 'tests/utils');
|
|
248
|
+
|
|
249
|
+
const files = [
|
|
250
|
+
'api-client.template.js',
|
|
251
|
+
'vitest.config.template.js',
|
|
252
|
+
];
|
|
253
|
+
|
|
254
|
+
for (const file of files) {
|
|
255
|
+
const src = join(templatesDir, file);
|
|
256
|
+
const dest = join(testUtilsDir, file.replace('.template', ''));
|
|
257
|
+
|
|
258
|
+
if (existsSync(src)) {
|
|
259
|
+
await copyFile(src, dest);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Add test scripts to package.json
|
|
266
|
+
*/
|
|
267
|
+
async function addTestScripts() {
|
|
268
|
+
const pkgPath = join(process.cwd(), 'package.json');
|
|
269
|
+
const pkg = JSON.parse(await readFile(pkgPath, 'utf-8'));
|
|
270
|
+
|
|
271
|
+
pkg.scripts = pkg.scripts || {};
|
|
272
|
+
pkg.scripts.test = pkg.scripts.test || 'vitest run';
|
|
273
|
+
pkg.scripts['test:watch'] = 'vitest watch';
|
|
274
|
+
pkg.scripts['test:coverage'] = 'vitest run --coverage';
|
|
275
|
+
|
|
276
|
+
await writeFile(pkgPath, JSON.stringify(pkg, null, 2), 'utf-8');
|
|
277
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validate Command
|
|
3
|
+
* Validates configuration and modules
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import * as clack from '@clack/prompts';
|
|
7
|
+
import chalk from 'chalk';
|
|
8
|
+
import { ConfigValidator } from '../../lib/validators/config-validator.js';
|
|
9
|
+
import { ModuleValidator } from '../../lib/validators/module-validator.js';
|
|
10
|
+
import { ConfigManager } from '../../lib/config-manager.js';
|
|
11
|
+
|
|
12
|
+
export async function validateCommand(options) {
|
|
13
|
+
try {
|
|
14
|
+
const configManager = new ConfigManager(process.cwd());
|
|
15
|
+
|
|
16
|
+
if (!configManager.exists()) {
|
|
17
|
+
clack.log.error(chalk.red('Dhurandhar is not installed in this directory.'));
|
|
18
|
+
clack.log.info('Run: dhurandhar install');
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
clack.intro(chalk.cyan.bold('Dhurandhar Validation'));
|
|
23
|
+
|
|
24
|
+
const results = {
|
|
25
|
+
config: { valid: true, errors: [], warnings: [] },
|
|
26
|
+
modules: { valid: true, errors: [], warnings: [] },
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// Validate configuration
|
|
30
|
+
if (!options.modules) {
|
|
31
|
+
const spinner = clack.spinner();
|
|
32
|
+
spinner.start('Validating configuration...');
|
|
33
|
+
|
|
34
|
+
const configValidator = new ConfigValidator(process.cwd());
|
|
35
|
+
results.config = await configValidator.validate(options.strict);
|
|
36
|
+
|
|
37
|
+
if (results.config.valid) {
|
|
38
|
+
spinner.stop(chalk.green('✓ Configuration is valid'));
|
|
39
|
+
} else {
|
|
40
|
+
spinner.stop(chalk.red('✗ Configuration has errors'));
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Validate modules
|
|
45
|
+
if (!options.config) {
|
|
46
|
+
const spinner = clack.spinner();
|
|
47
|
+
spinner.start('Validating modules...');
|
|
48
|
+
|
|
49
|
+
const moduleValidator = new ModuleValidator(process.cwd());
|
|
50
|
+
results.modules = await moduleValidator.validate(options.strict);
|
|
51
|
+
|
|
52
|
+
if (results.modules.valid) {
|
|
53
|
+
spinner.stop(chalk.green('✓ Modules are valid'));
|
|
54
|
+
} else {
|
|
55
|
+
spinner.stop(chalk.red('✗ Modules have errors'));
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Display detailed results
|
|
60
|
+
console.log('');
|
|
61
|
+
|
|
62
|
+
if (results.config.errors.length > 0) {
|
|
63
|
+
console.log(chalk.red.bold('Configuration Errors:'));
|
|
64
|
+
results.config.errors.forEach(error => {
|
|
65
|
+
console.log(` ${chalk.red('✗')} ${error}`);
|
|
66
|
+
});
|
|
67
|
+
console.log('');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (results.config.warnings.length > 0) {
|
|
71
|
+
console.log(chalk.yellow.bold('Configuration Warnings:'));
|
|
72
|
+
results.config.warnings.forEach(warning => {
|
|
73
|
+
console.log(` ${chalk.yellow('⚠')} ${warning}`);
|
|
74
|
+
});
|
|
75
|
+
console.log('');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (results.modules.errors.length > 0) {
|
|
79
|
+
console.log(chalk.red.bold('Module Errors:'));
|
|
80
|
+
results.modules.errors.forEach(error => {
|
|
81
|
+
console.log(` ${chalk.red('✗')} ${error}`);
|
|
82
|
+
});
|
|
83
|
+
console.log('');
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (results.modules.warnings.length > 0) {
|
|
87
|
+
console.log(chalk.yellow.bold('Module Warnings:'));
|
|
88
|
+
results.modules.warnings.forEach(warning => {
|
|
89
|
+
console.log(` ${chalk.yellow('⚠')} ${warning}`);
|
|
90
|
+
});
|
|
91
|
+
console.log('');
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const allValid = results.config.valid && results.modules.valid;
|
|
95
|
+
|
|
96
|
+
if (allValid) {
|
|
97
|
+
clack.outro(chalk.green('✓ All validations passed!'));
|
|
98
|
+
} else {
|
|
99
|
+
clack.outro(chalk.red('✗ Validation failed. Please fix the errors above.'));
|
|
100
|
+
process.exit(1);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
} catch (error) {
|
|
104
|
+
clack.log.error(chalk.red('Validation failed:'), error.message);
|
|
105
|
+
process.exit(1);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Dhurandhar CLI
|
|
5
|
+
* Main entry point for the Dhurandhar system design framework
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { program } from 'commander';
|
|
9
|
+
import { fileURLToPath } from 'url';
|
|
10
|
+
import { dirname, join } from 'path';
|
|
11
|
+
import { readFileSync } from 'fs';
|
|
12
|
+
import chalk from 'chalk';
|
|
13
|
+
import { installCommand } from './commands/install.js';
|
|
14
|
+
import { configCommand } from './commands/config.js';
|
|
15
|
+
import { moduleCommand } from './commands/module.js';
|
|
16
|
+
import { validateCommand } from './commands/validate.js';
|
|
17
|
+
import { serviceCommand } from './commands/service.js';
|
|
18
|
+
import { entityCommand } from './commands/entity.js';
|
|
19
|
+
import { contextCommand } from './commands/context.js';
|
|
20
|
+
import { epicCommand } from './commands/epic.js';
|
|
21
|
+
import { storyCommand } from './commands/story.js';
|
|
22
|
+
import { testCommand } from './commands/test.js';
|
|
23
|
+
import { strategyCommand } from './commands/strategy.js';
|
|
24
|
+
import { auditCommand } from './commands/audit.js';
|
|
25
|
+
import { decisionsCommand } from './commands/decisions.js';
|
|
26
|
+
|
|
27
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
28
|
+
const __dirname = dirname(__filename);
|
|
29
|
+
|
|
30
|
+
// Read package.json for version info
|
|
31
|
+
const packagePath = join(__dirname, '../../package.json');
|
|
32
|
+
const packageJson = JSON.parse(readFileSync(packagePath, 'utf-8'));
|
|
33
|
+
|
|
34
|
+
// Configure CLI
|
|
35
|
+
program
|
|
36
|
+
.name('dhurandhar')
|
|
37
|
+
.description('A System Building Mapwork - Framework for system design and build processes')
|
|
38
|
+
.version(packageJson.version, '-v, --version', 'Output the current version');
|
|
39
|
+
|
|
40
|
+
// Install command - Initialize framework in a project
|
|
41
|
+
program
|
|
42
|
+
.command('install')
|
|
43
|
+
.description('Install Dhurandhar framework in the current project')
|
|
44
|
+
.option('-d, --directory <path>', 'Target directory', process.cwd())
|
|
45
|
+
.option('-m, --modules <modules...>', 'Modules to install')
|
|
46
|
+
.option('-y, --yes', 'Skip prompts and use defaults')
|
|
47
|
+
.action(installCommand);
|
|
48
|
+
|
|
49
|
+
// Config command - Manage configuration
|
|
50
|
+
program
|
|
51
|
+
.command('config')
|
|
52
|
+
.description('Manage Dhurandhar configuration')
|
|
53
|
+
.option('-s, --show', 'Show current configuration')
|
|
54
|
+
.option('-e, --edit', 'Edit configuration interactively')
|
|
55
|
+
.option('-r, --reset', 'Reset to default configuration')
|
|
56
|
+
.action(configCommand);
|
|
57
|
+
|
|
58
|
+
// Module command - Manage modules
|
|
59
|
+
program
|
|
60
|
+
.command('module')
|
|
61
|
+
.description('Manage Dhurandhar modules')
|
|
62
|
+
.option('-l, --list', 'List available modules')
|
|
63
|
+
.option('-i, --info <module>', 'Show module information')
|
|
64
|
+
.option('-a, --add <module>', 'Add a module')
|
|
65
|
+
.option('-r, --remove <module>', 'Remove a module')
|
|
66
|
+
.action(moduleCommand);
|
|
67
|
+
|
|
68
|
+
// Validate command - Validate configuration and modules
|
|
69
|
+
program
|
|
70
|
+
.command('validate')
|
|
71
|
+
.description('Validate Dhurandhar configuration and modules')
|
|
72
|
+
.option('-c, --config', 'Validate configuration only')
|
|
73
|
+
.option('-m, --modules', 'Validate modules only')
|
|
74
|
+
.option('--strict', 'Use strict validation')
|
|
75
|
+
.action(validateCommand);
|
|
76
|
+
|
|
77
|
+
// Service command - Engineering-first service management
|
|
78
|
+
program
|
|
79
|
+
.command('service')
|
|
80
|
+
.description('Manage microservices and system components')
|
|
81
|
+
.option('-l, --list', 'List all services')
|
|
82
|
+
.option('-a, --add [spec]', 'Add service (optional quick spec: "name: description")')
|
|
83
|
+
.action(serviceCommand);
|
|
84
|
+
|
|
85
|
+
// Entity command - Engineering-first entity management
|
|
86
|
+
program
|
|
87
|
+
.command('entity')
|
|
88
|
+
.description('Manage database entities and relationships')
|
|
89
|
+
.option('-l, --list', 'List all entities')
|
|
90
|
+
.option('-a, --add [name]', 'Add entity')
|
|
91
|
+
.option('-r, --relate', 'Add relationship between entities')
|
|
92
|
+
.action(entityCommand);
|
|
93
|
+
|
|
94
|
+
// Context command - Display SDM for rehydration
|
|
95
|
+
program
|
|
96
|
+
.command('context')
|
|
97
|
+
.description('Display system architecture context')
|
|
98
|
+
.option('-s, --summary', 'Show brief summary')
|
|
99
|
+
.option('-f, --full', 'Show full architecture')
|
|
100
|
+
.option('--json', 'Output JSON format')
|
|
101
|
+
.action(contextCommand);
|
|
102
|
+
|
|
103
|
+
// Epic command - Agile Blueprint (high-level capabilities)
|
|
104
|
+
program
|
|
105
|
+
.command('epic')
|
|
106
|
+
.description('Manage Epics (system capabilities)')
|
|
107
|
+
.option('-l, --list', 'List all epics')
|
|
108
|
+
.option('-a, --add [name]', 'Add epic')
|
|
109
|
+
.action(epicCommand);
|
|
110
|
+
|
|
111
|
+
// Story command - User journeys with API contracts
|
|
112
|
+
program
|
|
113
|
+
.command('story')
|
|
114
|
+
.description('Manage Stories (user journeys)')
|
|
115
|
+
.option('-l, --list', 'List all stories')
|
|
116
|
+
.option('-a, --add [name]', 'Add story')
|
|
117
|
+
.option('-e, --epic <id>', 'Target epic ID')
|
|
118
|
+
.action(storyCommand);
|
|
119
|
+
|
|
120
|
+
// Test command - Contract-first test generation
|
|
121
|
+
program
|
|
122
|
+
.command('test')
|
|
123
|
+
.description('Contract-first testing')
|
|
124
|
+
.option('--init', 'Initialize test infrastructure')
|
|
125
|
+
.option('--generate', 'Generate tests from Agile Blueprint')
|
|
126
|
+
.option('--validate', 'Validate coverage against Stories')
|
|
127
|
+
.option('--edge-cases <id>', 'Generate edge cases for Story ID')
|
|
128
|
+
.action(testCommand);
|
|
129
|
+
|
|
130
|
+
// Strategy command - Pluggable architectural patterns
|
|
131
|
+
program
|
|
132
|
+
.command('strategy')
|
|
133
|
+
.description('Manage architectural strategies')
|
|
134
|
+
.option('--show', 'Show active strategies')
|
|
135
|
+
.option('--set <category>', 'Set strategy (persistence, communication, state, security, resilience)')
|
|
136
|
+
.option('--pivot <category>', 'Pivot existing strategy')
|
|
137
|
+
.option('--align', 'Align all services to current strategies')
|
|
138
|
+
.action(strategyCommand);
|
|
139
|
+
|
|
140
|
+
// Audit command - Architectural drift detection
|
|
141
|
+
program
|
|
142
|
+
.command('audit')
|
|
143
|
+
.description('Detect architectural drift (SDM vs codebase)')
|
|
144
|
+
.option('--summary', 'Show high-level alignment status')
|
|
145
|
+
.option('--drift', 'Show detailed drift report')
|
|
146
|
+
.option('--sync', 'Sync SDM to match codebase state')
|
|
147
|
+
.action(auditCommand);
|
|
148
|
+
|
|
149
|
+
// Decisions command - Technical decision registry
|
|
150
|
+
program
|
|
151
|
+
.command('decisions')
|
|
152
|
+
.description('Manage technical decision registry (Bmad-inspired)')
|
|
153
|
+
.option('--show', 'Show all registered decisions')
|
|
154
|
+
.option('--set <category>', 'Set decisions (naming, patterns, errors, observability)')
|
|
155
|
+
.option('--stock', 'Stock all decisions (complete setup)')
|
|
156
|
+
.action(decisionsCommand);
|
|
157
|
+
|
|
158
|
+
// Help command enhancement
|
|
159
|
+
program.on('--help', () => {
|
|
160
|
+
console.log('');
|
|
161
|
+
console.log(chalk.cyan('Examples - Engineering-First + Test-First Workflow:'));
|
|
162
|
+
console.log('');
|
|
163
|
+
console.log(chalk.bold('1. Initialize:'));
|
|
164
|
+
console.log(' $ dhurandhar install # Framework setup');
|
|
165
|
+
console.log('');
|
|
166
|
+
console.log(chalk.bold('2. Define Architecture:'));
|
|
167
|
+
console.log(' $ dhurandhar service add "auth: JWT auth" # Add service');
|
|
168
|
+
console.log(' $ dhurandhar entity add User # Add entity');
|
|
169
|
+
console.log('');
|
|
170
|
+
console.log(chalk.bold('3. Test-First Agile:'));
|
|
171
|
+
console.log(' $ dhurandhar epic add "User Authentication" # Add Epic');
|
|
172
|
+
console.log(' $ dhurandhar story add --epic EPIC-001 # Add Story');
|
|
173
|
+
console.log(' $ dhurandhar test --generate # Generate contract tests');
|
|
174
|
+
console.log(' $ npm test # Run tests (will fail)');
|
|
175
|
+
console.log(' $ # Implement services to pass tests');
|
|
176
|
+
console.log('');
|
|
177
|
+
console.log(chalk.bold('4. Validate:'));
|
|
178
|
+
console.log(' $ dhurandhar test --validate # Check coverage');
|
|
179
|
+
console.log(' $ dhurandhar context --full # View full architecture');
|
|
180
|
+
console.log('');
|
|
181
|
+
console.log(chalk.cyan('Test-First Philosophy:'));
|
|
182
|
+
console.log(' 1. Epic/Story define WHAT (capabilities & journeys)');
|
|
183
|
+
console.log(' 2. Tests define HOW (API contracts & boundaries)');
|
|
184
|
+
console.log(' 3. Implementation fulfills the contract');
|
|
185
|
+
console.log('');
|
|
186
|
+
console.log(chalk.cyan('Context Persistence:'));
|
|
187
|
+
console.log(' SYSTEM_DESIGN_MAP.yaml contains:');
|
|
188
|
+
console.log(' - Services & Entities (architecture)');
|
|
189
|
+
console.log(' - Epics & Stories (agile blueprint)');
|
|
190
|
+
console.log(' - Test coverage status (definition of done)');
|
|
191
|
+
console.log('');
|
|
192
|
+
console.log(chalk.cyan('Documentation:'));
|
|
193
|
+
console.log(' https://github.com/yourusername/dhurandhar');
|
|
194
|
+
console.log('');
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
// Error handling
|
|
198
|
+
program.exitOverride();
|
|
199
|
+
|
|
200
|
+
try {
|
|
201
|
+
await program.parseAsync(process.argv);
|
|
202
|
+
} catch (err) {
|
|
203
|
+
if (err.code === 'commander.help') {
|
|
204
|
+
process.exit(0);
|
|
205
|
+
}
|
|
206
|
+
if (err.code === 'commander.version') {
|
|
207
|
+
process.exit(0);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
console.error(chalk.red('Error:'), err.message);
|
|
211
|
+
process.exit(1);
|
|
212
|
+
}
|