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,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Epic Command - Agile Blueprint Management
|
|
3
|
+
* Manage Epics (high-level system capabilities)
|
|
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
|
+
|
|
11
|
+
export async function epicCommand(options) {
|
|
12
|
+
try {
|
|
13
|
+
const sdmManager = new SDMManager(process.cwd());
|
|
14
|
+
const configManager = new ConfigManager(process.cwd());
|
|
15
|
+
|
|
16
|
+
if (!configManager.exists()) {
|
|
17
|
+
clack.log.error(chalk.red('Framework not installed.'));
|
|
18
|
+
clack.log.info('Run: dhurandhar install');
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (options.list) {
|
|
23
|
+
await listEpics(sdmManager);
|
|
24
|
+
} else if (options.add) {
|
|
25
|
+
await addEpic(sdmManager, options.add);
|
|
26
|
+
} else {
|
|
27
|
+
console.log(chalk.cyan('Usage:'));
|
|
28
|
+
console.log(' dhurandhar epic --list List all epics');
|
|
29
|
+
console.log(' dhurandhar epic --add "Epic Name" Add epic');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
} catch (error) {
|
|
33
|
+
clack.log.error(chalk.red('Epic operation failed:'), error.message);
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async function listEpics(sdmManager) {
|
|
39
|
+
const sdm = await sdmManager.load();
|
|
40
|
+
|
|
41
|
+
if (!sdm.agile_blueprint || !sdm.agile_blueprint.epics || sdm.agile_blueprint.epics.length === 0) {
|
|
42
|
+
console.log(chalk.dim(' No epics defined yet.'));
|
|
43
|
+
console.log('');
|
|
44
|
+
console.log(chalk.cyan('Add one:'));
|
|
45
|
+
console.log(' dhurandhar epic add "User Authentication & Authorization"');
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
clack.intro(chalk.cyan.bold(`Epics (${sdm.agile_blueprint.epics.length})`));
|
|
50
|
+
|
|
51
|
+
console.log('');
|
|
52
|
+
sdm.agile_blueprint.epics.forEach(epic => {
|
|
53
|
+
const storyCount = epic.stories?.length || 0;
|
|
54
|
+
console.log(chalk.bold(`${epic.id}: ${epic.name}`));
|
|
55
|
+
console.log(` Stories: ${storyCount}`);
|
|
56
|
+
if (epic.description) {
|
|
57
|
+
console.log(` ${chalk.dim(epic.description)}`);
|
|
58
|
+
}
|
|
59
|
+
console.log('');
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
clack.outro('');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async function addEpic(sdmManager, epicName) {
|
|
66
|
+
clack.intro(chalk.cyan.bold('Add Epic'));
|
|
67
|
+
|
|
68
|
+
const name = typeof epicName === 'string' ? epicName : null;
|
|
69
|
+
|
|
70
|
+
let epicTitle = name;
|
|
71
|
+
|
|
72
|
+
if (!epicTitle) {
|
|
73
|
+
epicTitle = await clack.text({
|
|
74
|
+
message: 'Epic name (system capability):',
|
|
75
|
+
placeholder: 'User Authentication & Authorization',
|
|
76
|
+
validate: (v) => !v ? 'Required' : undefined,
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
if (clack.isCancel(epicTitle)) {
|
|
80
|
+
clack.cancel('Cancelled.');
|
|
81
|
+
process.exit(0);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Technical description
|
|
86
|
+
const description = await clack.text({
|
|
87
|
+
message: 'Technical description (optional):',
|
|
88
|
+
placeholder: 'JWT-based auth with OAuth2 social login',
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
if (clack.isCancel(description)) {
|
|
92
|
+
clack.cancel('Cancelled.');
|
|
93
|
+
process.exit(0);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Generate Epic ID
|
|
97
|
+
const sdm = await sdmManager.load();
|
|
98
|
+
if (!sdm.agile_blueprint) {
|
|
99
|
+
sdm.agile_blueprint = { epics: [], test_suite_status: {} };
|
|
100
|
+
}
|
|
101
|
+
if (!sdm.agile_blueprint.epics) {
|
|
102
|
+
sdm.agile_blueprint.epics = [];
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const epicId = `EPIC-${String(sdm.agile_blueprint.epics.length + 1).padStart(3, '0')}`;
|
|
106
|
+
|
|
107
|
+
// Create Epic
|
|
108
|
+
const epic = {
|
|
109
|
+
id: epicId,
|
|
110
|
+
name: epicTitle,
|
|
111
|
+
description: description || undefined,
|
|
112
|
+
acceptance_criteria: [],
|
|
113
|
+
stories: [],
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
sdm.agile_blueprint.epics.push(epic);
|
|
117
|
+
await sdmManager.save(sdm);
|
|
118
|
+
|
|
119
|
+
clack.outro(chalk.green(`✓ Epic ${epicId} added`));
|
|
120
|
+
|
|
121
|
+
console.log('');
|
|
122
|
+
console.log(chalk.cyan('Next: Add Stories'));
|
|
123
|
+
console.log(` dhurandhar story add --epic ${epicId}`);
|
|
124
|
+
console.log('');
|
|
125
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Install Command - Engineering-First Installation
|
|
3
|
+
* Direct action model: 3 technical questions max, no justification loops
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import * as clack from '@clack/prompts';
|
|
7
|
+
import chalk from 'chalk';
|
|
8
|
+
import { existsSync } from 'fs';
|
|
9
|
+
import { join } from 'path';
|
|
10
|
+
import { ConfigManager } from '../../lib/config-manager.js';
|
|
11
|
+
import { ModuleInstaller } from '../../lib/module-installer.js';
|
|
12
|
+
import { FileSystem } from '../../lib/filesystem.js';
|
|
13
|
+
import { SDMManager } from '../../lib/sdm-manager.js';
|
|
14
|
+
|
|
15
|
+
export async function installCommand(options) {
|
|
16
|
+
console.clear();
|
|
17
|
+
|
|
18
|
+
clack.intro(chalk.cyan.bold('🔧 Dhurandhar - Engineering-First System Design'));
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
const targetDir = options.directory || process.cwd();
|
|
22
|
+
|
|
23
|
+
// Check if already installed
|
|
24
|
+
const configPath = join(targetDir, '.dhurandhar', 'config.yaml');
|
|
25
|
+
if (existsSync(configPath) && !options.yes) {
|
|
26
|
+
const shouldReinstall = await clack.confirm({
|
|
27
|
+
message: 'Framework already installed. Reinstall?',
|
|
28
|
+
initialValue: false,
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
if (clack.isCancel(shouldReinstall) || !shouldReinstall) {
|
|
32
|
+
clack.cancel('Cancelled.');
|
|
33
|
+
process.exit(0);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// ENGINEERING-FIRST: Only 3 technical questions
|
|
38
|
+
let config = {};
|
|
39
|
+
let techConfig = {};
|
|
40
|
+
|
|
41
|
+
if (!options.yes) {
|
|
42
|
+
// Question 1: Project identifier
|
|
43
|
+
const projectName = await clack.text({
|
|
44
|
+
message: 'Project identifier (kebab-case):',
|
|
45
|
+
placeholder: 'my-system',
|
|
46
|
+
validate: (value) => {
|
|
47
|
+
if (!value) return 'Required';
|
|
48
|
+
if (!/^[a-z0-9-]+$/.test(value)) {
|
|
49
|
+
return 'Must be lowercase, alphanumeric, hyphens only';
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
if (clack.isCancel(projectName)) {
|
|
55
|
+
clack.cancel('Cancelled.');
|
|
56
|
+
process.exit(0);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Question 2: Architecture type (technical decision)
|
|
60
|
+
const architectureType = await clack.select({
|
|
61
|
+
message: 'Architecture type:',
|
|
62
|
+
options: [
|
|
63
|
+
{ value: 'microservices', label: 'Microservices', hint: 'Multiple services, independent deployment' },
|
|
64
|
+
{ value: 'monolith', label: 'Monolith', hint: 'Single deployable unit' },
|
|
65
|
+
{ value: 'serverless', label: 'Serverless', hint: 'FaaS, event-driven' },
|
|
66
|
+
{ value: 'hybrid', label: 'Hybrid', hint: 'Mix of patterns' },
|
|
67
|
+
],
|
|
68
|
+
initialValue: 'microservices',
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
if (clack.isCancel(architectureType)) {
|
|
72
|
+
clack.cancel('Cancelled.');
|
|
73
|
+
process.exit(0);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Question 3: Primary tech stack
|
|
77
|
+
const primaryLanguage = await clack.select({
|
|
78
|
+
message: 'Primary language:',
|
|
79
|
+
options: [
|
|
80
|
+
{ value: 'go', label: 'Go', hint: 'Performance, concurrency' },
|
|
81
|
+
{ value: 'typescript', label: 'TypeScript', hint: 'Type-safe JavaScript' },
|
|
82
|
+
{ value: 'python', label: 'Python', hint: 'Rapid development, ML/AI' },
|
|
83
|
+
{ value: 'rust', label: 'Rust', hint: 'Systems programming, safety' },
|
|
84
|
+
{ value: 'java', label: 'Java', hint: 'Enterprise, JVM ecosystem' },
|
|
85
|
+
],
|
|
86
|
+
initialValue: 'go',
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
if (clack.isCancel(primaryLanguage)) {
|
|
90
|
+
clack.cancel('Cancelled.');
|
|
91
|
+
process.exit(0);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
config = {
|
|
95
|
+
projectName,
|
|
96
|
+
userName: 'Engineer', // Default, not critical
|
|
97
|
+
outputFolder: '_dhurandhar-output',
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
techConfig = {
|
|
101
|
+
architectureType,
|
|
102
|
+
primaryLanguage,
|
|
103
|
+
};
|
|
104
|
+
} else {
|
|
105
|
+
config = {
|
|
106
|
+
projectName: 'dhurandhar-project',
|
|
107
|
+
userName: 'Engineer',
|
|
108
|
+
outputFolder: '_dhurandhar-output',
|
|
109
|
+
};
|
|
110
|
+
techConfig = {
|
|
111
|
+
architectureType: 'microservices',
|
|
112
|
+
primaryLanguage: 'go',
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Auto-install core only (no module selection prompts)
|
|
117
|
+
const selectedModules = options.modules || ['core'];
|
|
118
|
+
|
|
119
|
+
// Perform installation
|
|
120
|
+
const spinner = clack.spinner();
|
|
121
|
+
|
|
122
|
+
spinner.start('Installing framework...');
|
|
123
|
+
|
|
124
|
+
// Create directory structure
|
|
125
|
+
await FileSystem.ensureDirectories(targetDir);
|
|
126
|
+
|
|
127
|
+
// Initialize configuration
|
|
128
|
+
const configManager = new ConfigManager(targetDir);
|
|
129
|
+
await configManager.initialize(config);
|
|
130
|
+
|
|
131
|
+
spinner.message('Initializing System Design Map...');
|
|
132
|
+
|
|
133
|
+
// Initialize SDM (System Design Map) for persistent state
|
|
134
|
+
const sdmManager = new SDMManager(targetDir);
|
|
135
|
+
await sdmManager.initialize(config.projectName);
|
|
136
|
+
|
|
137
|
+
// Update SDM with architecture type
|
|
138
|
+
const sdm = await sdmManager.load();
|
|
139
|
+
sdm.metadata.architecture_type = techConfig.architectureType;
|
|
140
|
+
sdm.tech_stack.languages = [techConfig.primaryLanguage];
|
|
141
|
+
await sdmManager.save(sdm);
|
|
142
|
+
|
|
143
|
+
spinner.message('Installing core module...');
|
|
144
|
+
|
|
145
|
+
// Install modules
|
|
146
|
+
const installer = new ModuleInstaller(targetDir);
|
|
147
|
+
await installer.installModules(selectedModules);
|
|
148
|
+
|
|
149
|
+
spinner.stop('Framework installed!');
|
|
150
|
+
|
|
151
|
+
clack.outro(chalk.green('✓ Ready for engineering'));
|
|
152
|
+
|
|
153
|
+
// Show next steps - ENGINEERING-FOCUSED
|
|
154
|
+
console.log('');
|
|
155
|
+
console.log(chalk.cyan('System initialized:'));
|
|
156
|
+
console.log(` ${chalk.bold('SYSTEM_DESIGN_MAP.yaml')} - Persistent architecture state`);
|
|
157
|
+
console.log(` ${chalk.dim('.dhurandhar/config.yaml')} - Framework configuration`);
|
|
158
|
+
console.log('');
|
|
159
|
+
console.log(chalk.cyan('Add your first service:'));
|
|
160
|
+
console.log(` ${chalk.bold('dhurandhar service add "auth-service: JWT auth using ' + techConfig.primaryLanguage + '"')}`);
|
|
161
|
+
console.log('');
|
|
162
|
+
console.log(chalk.cyan('Or add an entity:'));
|
|
163
|
+
console.log(` ${chalk.bold('dhurandhar entity add User')}`);
|
|
164
|
+
console.log('');
|
|
165
|
+
console.log(chalk.dim('The SDM persists context across sessions - no rediscovery needed.'));
|
|
166
|
+
console.log('');
|
|
167
|
+
|
|
168
|
+
} catch (error) {
|
|
169
|
+
clack.log.error(chalk.red('Installation failed:'), error.message);
|
|
170
|
+
process.exit(1);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Module Command
|
|
3
|
+
* Manages framework modules
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import * as clack from '@clack/prompts';
|
|
7
|
+
import chalk from 'chalk';
|
|
8
|
+
import { ModuleManager } from '../../lib/module-manager.js';
|
|
9
|
+
import { ConfigManager } from '../../lib/config-manager.js';
|
|
10
|
+
|
|
11
|
+
export async function moduleCommand(options) {
|
|
12
|
+
try {
|
|
13
|
+
const moduleManager = new ModuleManager(process.cwd());
|
|
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
|
+
if (options.list) {
|
|
23
|
+
// List available modules
|
|
24
|
+
clack.intro(chalk.cyan.bold('Available Modules'));
|
|
25
|
+
|
|
26
|
+
const modules = await moduleManager.listAvailable();
|
|
27
|
+
const installed = await moduleManager.listInstalled();
|
|
28
|
+
|
|
29
|
+
console.log('');
|
|
30
|
+
modules.forEach(module => {
|
|
31
|
+
const isInstalled = installed.includes(module.code);
|
|
32
|
+
const status = isInstalled ? chalk.green('✓ installed') : chalk.dim('not installed');
|
|
33
|
+
console.log(` ${chalk.bold(module.code)} - ${module.name}`);
|
|
34
|
+
console.log(` ${status}`);
|
|
35
|
+
console.log(` ${chalk.dim(module.description)}`);
|
|
36
|
+
console.log('');
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
clack.outro(chalk.dim(`Total: ${modules.length} modules available`));
|
|
40
|
+
|
|
41
|
+
} else if (options.info) {
|
|
42
|
+
// Show module information
|
|
43
|
+
const moduleInfo = await moduleManager.getInfo(options.info);
|
|
44
|
+
|
|
45
|
+
if (!moduleInfo) {
|
|
46
|
+
clack.log.error(chalk.red(`Module "${options.info}" not found.`));
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
clack.intro(chalk.cyan.bold(`Module: ${moduleInfo.name}`));
|
|
51
|
+
|
|
52
|
+
console.log('');
|
|
53
|
+
console.log(chalk.bold('Details:'));
|
|
54
|
+
console.log(` Code: ${chalk.cyan(moduleInfo.code)}`);
|
|
55
|
+
console.log(` Name: ${chalk.cyan(moduleInfo.name)}`);
|
|
56
|
+
console.log(` Version: ${chalk.cyan(moduleInfo.version || '1.0.0')}`);
|
|
57
|
+
console.log(` Description: ${moduleInfo.description}`);
|
|
58
|
+
console.log('');
|
|
59
|
+
|
|
60
|
+
if (moduleInfo.dependencies && moduleInfo.dependencies.length > 0) {
|
|
61
|
+
console.log(chalk.bold('Dependencies:'));
|
|
62
|
+
moduleInfo.dependencies.forEach(dep => {
|
|
63
|
+
console.log(` - ${dep}`);
|
|
64
|
+
});
|
|
65
|
+
console.log('');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
clack.outro('');
|
|
69
|
+
|
|
70
|
+
} else if (options.add) {
|
|
71
|
+
// Add a module
|
|
72
|
+
clack.intro(chalk.cyan.bold('Add Module'));
|
|
73
|
+
|
|
74
|
+
const spinner = clack.spinner();
|
|
75
|
+
spinner.start(`Installing module: ${options.add}`);
|
|
76
|
+
|
|
77
|
+
await moduleManager.install(options.add);
|
|
78
|
+
|
|
79
|
+
spinner.stop(`Module "${options.add}" installed successfully!`);
|
|
80
|
+
clack.outro(chalk.green('✓ Done'));
|
|
81
|
+
|
|
82
|
+
} else if (options.remove) {
|
|
83
|
+
// Remove a module
|
|
84
|
+
const confirm = await clack.confirm({
|
|
85
|
+
message: `Remove module "${options.remove}"?`,
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
if (clack.isCancel(confirm) || !confirm) {
|
|
89
|
+
clack.cancel('Operation cancelled.');
|
|
90
|
+
process.exit(0);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
await moduleManager.uninstall(options.remove);
|
|
94
|
+
clack.outro(chalk.green(`✓ Module "${options.remove}" removed.`));
|
|
95
|
+
|
|
96
|
+
} else {
|
|
97
|
+
// No option provided, show help
|
|
98
|
+
console.log(chalk.cyan('Usage:'));
|
|
99
|
+
console.log(' dhurandhar module --list List all available modules');
|
|
100
|
+
console.log(' dhurandhar module --info <module> Show module information');
|
|
101
|
+
console.log(' dhurandhar module --add <module> Add a module');
|
|
102
|
+
console.log(' dhurandhar module --remove <module> Remove a module');
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
} catch (error) {
|
|
106
|
+
clack.log.error(chalk.red('Module operation failed:'), error.message);
|
|
107
|
+
process.exit(1);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Service Command - Direct-Action Service Management
|
|
3
|
+
* Add/modify services with technical specs only, no justification loops
|
|
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
|
+
|
|
11
|
+
export async function serviceCommand(options) {
|
|
12
|
+
try {
|
|
13
|
+
const sdmManager = new SDMManager(process.cwd());
|
|
14
|
+
const configManager = new ConfigManager(process.cwd());
|
|
15
|
+
|
|
16
|
+
if (!configManager.exists()) {
|
|
17
|
+
clack.log.error(chalk.red('Framework not installed.'));
|
|
18
|
+
clack.log.info('Run: dhurandhar install');
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (options.list) {
|
|
23
|
+
// List services
|
|
24
|
+
const sdm = await sdmManager.load();
|
|
25
|
+
|
|
26
|
+
clack.intro(chalk.cyan.bold(`Services (${sdm.services.length})`));
|
|
27
|
+
|
|
28
|
+
if (sdm.services.length === 0) {
|
|
29
|
+
console.log(chalk.dim(' No services defined yet.'));
|
|
30
|
+
console.log('');
|
|
31
|
+
console.log(chalk.cyan('Add one:'));
|
|
32
|
+
console.log(' dhurandhar service add "auth-service: JWT auth using Go/Echo"');
|
|
33
|
+
} else {
|
|
34
|
+
console.log('');
|
|
35
|
+
sdm.services.forEach(s => {
|
|
36
|
+
console.log(chalk.bold(`${s.name}`));
|
|
37
|
+
console.log(` ${chalk.dim(s.scope)}`);
|
|
38
|
+
console.log(` Stack: ${chalk.cyan(s.tech_stack.language)}/${chalk.cyan(s.tech_stack.framework)}`);
|
|
39
|
+
if (s.tech_stack.database) {
|
|
40
|
+
console.log(` Database: ${chalk.cyan(s.tech_stack.database)}`);
|
|
41
|
+
}
|
|
42
|
+
if (s.api) {
|
|
43
|
+
console.log(` API: ${s.api.type} ${s.api.base_path || ''} ${s.api.port ? ':' + s.api.port : ''}`);
|
|
44
|
+
}
|
|
45
|
+
console.log('');
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
clack.outro('');
|
|
50
|
+
|
|
51
|
+
} else if (options.add) {
|
|
52
|
+
// Add service - DIRECT ACTION, max 3 questions
|
|
53
|
+
clack.intro(chalk.cyan.bold('Add Service'));
|
|
54
|
+
|
|
55
|
+
// Parse quick spec if provided: "service-name: description"
|
|
56
|
+
let serviceName, serviceScope;
|
|
57
|
+
|
|
58
|
+
if (typeof options.add === 'string' && options.add.includes(':')) {
|
|
59
|
+
const parts = options.add.split(':');
|
|
60
|
+
serviceName = parts[0].trim();
|
|
61
|
+
serviceScope = parts.slice(1).join(':').trim();
|
|
62
|
+
} else {
|
|
63
|
+
serviceName = typeof options.add === 'string' ? options.add : null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Question 1: Service name (if not provided)
|
|
67
|
+
if (!serviceName) {
|
|
68
|
+
serviceName = await clack.text({
|
|
69
|
+
message: 'Service name (kebab-case):',
|
|
70
|
+
placeholder: 'auth-service',
|
|
71
|
+
validate: (v) => !v ? 'Required' : !/^[a-z0-9-]+$/.test(v) ? 'Use kebab-case' : undefined,
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
if (clack.isCancel(serviceName)) {
|
|
75
|
+
clack.cancel('Cancelled.');
|
|
76
|
+
process.exit(0);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Question 2: Scope (if not provided)
|
|
81
|
+
if (!serviceScope) {
|
|
82
|
+
serviceScope = await clack.text({
|
|
83
|
+
message: 'What does it do? (1-2 sentences):',
|
|
84
|
+
placeholder: 'Handles JWT authentication and session management',
|
|
85
|
+
validate: (v) => !v ? 'Required' : undefined,
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
if (clack.isCancel(serviceScope)) {
|
|
89
|
+
clack.cancel('Cancelled.');
|
|
90
|
+
process.exit(0);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Question 3: Tech stack
|
|
95
|
+
const sdm = await sdmManager.load();
|
|
96
|
+
const defaultLang = sdm.tech_stack.languages[0] || 'go';
|
|
97
|
+
|
|
98
|
+
const language = await clack.select({
|
|
99
|
+
message: 'Language:',
|
|
100
|
+
options: [
|
|
101
|
+
{ value: 'go', label: 'Go' },
|
|
102
|
+
{ value: 'typescript', label: 'TypeScript' },
|
|
103
|
+
{ value: 'python', label: 'Python' },
|
|
104
|
+
{ value: 'rust', label: 'Rust' },
|
|
105
|
+
{ value: 'java', label: 'Java' },
|
|
106
|
+
],
|
|
107
|
+
initialValue: defaultLang,
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
if (clack.isCancel(language)) {
|
|
111
|
+
clack.cancel('Cancelled.');
|
|
112
|
+
process.exit(0);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Auto-select common framework for language
|
|
116
|
+
const frameworkMap = {
|
|
117
|
+
go: 'Echo',
|
|
118
|
+
typescript: 'Express',
|
|
119
|
+
python: 'FastAPI',
|
|
120
|
+
rust: 'Actix',
|
|
121
|
+
java: 'Spring',
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
const framework = frameworkMap[language];
|
|
125
|
+
const database = 'PostgreSQL'; // Default
|
|
126
|
+
|
|
127
|
+
// Build service object
|
|
128
|
+
const service = {
|
|
129
|
+
name: serviceName,
|
|
130
|
+
scope: serviceScope,
|
|
131
|
+
tech_stack: {
|
|
132
|
+
language: language.charAt(0).toUpperCase() + language.slice(1),
|
|
133
|
+
framework,
|
|
134
|
+
database,
|
|
135
|
+
},
|
|
136
|
+
api: {
|
|
137
|
+
type: 'rest',
|
|
138
|
+
base_path: `/api/v1/${serviceName.replace('-service', '')}`,
|
|
139
|
+
port: 8080 + sdm.services.length, // Auto-increment
|
|
140
|
+
},
|
|
141
|
+
dependencies: [],
|
|
142
|
+
data_access: [],
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
// Add to SDM
|
|
146
|
+
await sdmManager.addService(service);
|
|
147
|
+
|
|
148
|
+
clack.outro(chalk.green(`✓ Service "${serviceName}" added to architecture`));
|
|
149
|
+
|
|
150
|
+
console.log('');
|
|
151
|
+
console.log(chalk.cyan('Updated SYSTEM_DESIGN_MAP.yaml'));
|
|
152
|
+
console.log(chalk.dim('Context persisted for next session.'));
|
|
153
|
+
console.log('');
|
|
154
|
+
|
|
155
|
+
} else {
|
|
156
|
+
// Show help
|
|
157
|
+
console.log(chalk.cyan('Usage:'));
|
|
158
|
+
console.log(' dhurandhar service --list List all services');
|
|
159
|
+
console.log(' dhurandhar service --add "name: description" Add service with quick spec');
|
|
160
|
+
console.log(' dhurandhar service --add Add service interactively');
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
} catch (error) {
|
|
164
|
+
clack.log.error(chalk.red('Service operation failed:'), error.message);
|
|
165
|
+
process.exit(1);
|
|
166
|
+
}
|
|
167
|
+
}
|