aios-core 3.6.0 → 3.8.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/.aios-core/core/session/context-detector.js +3 -0
- package/.aios-core/core/session/context-loader.js +154 -0
- package/.aios-core/data/learned-patterns.yaml +3 -0
- package/.aios-core/data/workflow-patterns.yaml +347 -3
- package/.aios-core/development/agents/dev.md +13 -0
- package/.aios-core/development/agents/squad-creator.md +30 -0
- package/.aios-core/development/scripts/squad/squad-analyzer.js +638 -0
- package/.aios-core/development/scripts/squad/squad-extender.js +871 -0
- package/.aios-core/development/scripts/squad/squad-generator.js +107 -19
- package/.aios-core/development/scripts/squad/squad-migrator.js +3 -5
- package/.aios-core/development/scripts/squad/squad-validator.js +98 -0
- package/.aios-core/development/tasks/create-service.md +391 -0
- package/.aios-core/development/tasks/next.md +294 -0
- package/.aios-core/development/tasks/patterns.md +334 -0
- package/.aios-core/development/tasks/squad-creator-analyze.md +315 -0
- package/.aios-core/development/tasks/squad-creator-create.md +26 -3
- package/.aios-core/development/tasks/squad-creator-extend.md +411 -0
- package/.aios-core/development/tasks/squad-creator-validate.md +9 -1
- package/.aios-core/development/tasks/waves.md +205 -0
- package/.aios-core/development/templates/service-template/README.md.hbs +158 -0
- package/.aios-core/development/templates/service-template/__tests__/index.test.ts.hbs +237 -0
- package/.aios-core/development/templates/service-template/client.ts.hbs +403 -0
- package/.aios-core/development/templates/service-template/errors.ts.hbs +182 -0
- package/.aios-core/development/templates/service-template/index.ts.hbs +120 -0
- package/.aios-core/development/templates/service-template/jest.config.js +89 -0
- package/.aios-core/development/templates/service-template/package.json.hbs +87 -0
- package/.aios-core/development/templates/service-template/tsconfig.json +45 -0
- package/.aios-core/development/templates/service-template/types.ts.hbs +145 -0
- package/.aios-core/development/templates/squad/agent-template.md +69 -0
- package/.aios-core/development/templates/squad/checklist-template.md +82 -0
- package/.aios-core/development/templates/squad/data-template.yaml +105 -0
- package/.aios-core/development/templates/squad/script-template.js +179 -0
- package/.aios-core/development/templates/squad/task-template.md +125 -0
- package/.aios-core/development/templates/squad/template-template.md +97 -0
- package/.aios-core/development/templates/squad/tool-template.js +103 -0
- package/.aios-core/development/templates/squad/workflow-template.yaml +108 -0
- package/.aios-core/infrastructure/scripts/ide-sync/agent-parser.js +45 -1
- package/.aios-core/infrastructure/scripts/ide-sync/transformers/antigravity.js +6 -6
- package/.aios-core/infrastructure/scripts/ide-sync/transformers/cursor.js +5 -4
- package/.aios-core/infrastructure/scripts/ide-sync/transformers/trae.js +3 -3
- package/.aios-core/infrastructure/scripts/ide-sync/transformers/windsurf.js +3 -3
- package/.aios-core/install-manifest.yaml +139 -35
- package/.aios-core/quality/metrics-collector.js +27 -0
- package/.aios-core/scripts/session-context-loader.js +13 -254
- package/.aios-core/utils/aios-validator.js +25 -0
- package/.aios-core/workflow-intelligence/__tests__/confidence-scorer.test.js +334 -0
- package/.aios-core/workflow-intelligence/__tests__/integration.test.js +337 -0
- package/.aios-core/workflow-intelligence/__tests__/suggestion-engine.test.js +431 -0
- package/.aios-core/workflow-intelligence/__tests__/wave-analyzer.test.js +458 -0
- package/.aios-core/workflow-intelligence/__tests__/workflow-registry.test.js +302 -0
- package/.aios-core/workflow-intelligence/engine/confidence-scorer.js +305 -0
- package/.aios-core/workflow-intelligence/engine/output-formatter.js +285 -0
- package/.aios-core/workflow-intelligence/engine/suggestion-engine.js +603 -0
- package/.aios-core/workflow-intelligence/engine/wave-analyzer.js +676 -0
- package/.aios-core/workflow-intelligence/index.js +327 -0
- package/.aios-core/workflow-intelligence/learning/capture-hook.js +147 -0
- package/.aios-core/workflow-intelligence/learning/index.js +230 -0
- package/.aios-core/workflow-intelligence/learning/pattern-capture.js +340 -0
- package/.aios-core/workflow-intelligence/learning/pattern-store.js +498 -0
- package/.aios-core/workflow-intelligence/learning/pattern-validator.js +309 -0
- package/.aios-core/workflow-intelligence/registry/workflow-registry.js +358 -0
- package/package.json +1 -1
|
@@ -304,15 +304,27 @@ function generateSquadYaml(config) {
|
|
|
304
304
|
components.tasks = [];
|
|
305
305
|
}
|
|
306
306
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
307
|
+
// SQS-10: Use project configs if available, otherwise use local paths
|
|
308
|
+
let configSection;
|
|
309
|
+
if (config.configMode === 'none') {
|
|
310
|
+
configSection = {};
|
|
311
|
+
} else if (config._useProjectConfigs && config._projectConfigs) {
|
|
312
|
+
// Reference project-level config files
|
|
313
|
+
configSection = {
|
|
314
|
+
extends: config.configMode,
|
|
315
|
+
'coding-standards': config._projectConfigs['coding-standards'] || 'config/coding-standards.md',
|
|
316
|
+
'tech-stack': config._projectConfigs['tech-stack'] || 'config/tech-stack.md',
|
|
317
|
+
'source-tree': config._projectConfigs['source-tree'] || 'config/source-tree.md',
|
|
318
|
+
};
|
|
319
|
+
} else {
|
|
320
|
+
// Fallback to local config files
|
|
321
|
+
configSection = {
|
|
322
|
+
extends: config.configMode,
|
|
323
|
+
'coding-standards': 'config/coding-standards.md',
|
|
324
|
+
'tech-stack': 'config/tech-stack.md',
|
|
325
|
+
'source-tree': 'config/source-tree.md',
|
|
326
|
+
};
|
|
327
|
+
}
|
|
316
328
|
|
|
317
329
|
const yaml = `name: ${config.name}
|
|
318
330
|
version: 1.0.0
|
|
@@ -644,6 +656,53 @@ class SquadGenerator {
|
|
|
644
656
|
}
|
|
645
657
|
}
|
|
646
658
|
|
|
659
|
+
/**
|
|
660
|
+
* Detect project-level configuration files in docs/framework/
|
|
661
|
+
* Implements AC10.1: Project Config Detection
|
|
662
|
+
*
|
|
663
|
+
* @param {string} projectRoot - Project root directory
|
|
664
|
+
* @param {string} squadPath - Path to squad being created (for relative paths)
|
|
665
|
+
* @returns {Promise<Object|null>} Detected config paths (relative to squadPath) or null if not found
|
|
666
|
+
* @see Story SQS-10: Project Config Reference for Squads
|
|
667
|
+
*/
|
|
668
|
+
async detectProjectConfigs(projectRoot, squadPath) {
|
|
669
|
+
const frameworkDir = path.join(projectRoot, 'docs', 'framework');
|
|
670
|
+
|
|
671
|
+
// Check if docs/framework/ exists
|
|
672
|
+
if (!(await this.pathExists(frameworkDir))) {
|
|
673
|
+
return null;
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
// Config files to detect (case-insensitive variants)
|
|
677
|
+
const configFiles = {
|
|
678
|
+
'coding-standards': ['CODING-STANDARDS.md', 'coding-standards.md', 'Coding-Standards.md'],
|
|
679
|
+
'tech-stack': ['TECH-STACK.md', 'tech-stack.md', 'Tech-Stack.md'],
|
|
680
|
+
'source-tree': ['SOURCE-TREE.md', 'source-tree.md', 'Source-Tree.md'],
|
|
681
|
+
};
|
|
682
|
+
|
|
683
|
+
const detected = {};
|
|
684
|
+
|
|
685
|
+
for (const [key, variants] of Object.entries(configFiles)) {
|
|
686
|
+
for (const filename of variants) {
|
|
687
|
+
const fullPath = path.join(frameworkDir, filename);
|
|
688
|
+
if (await this.pathExists(fullPath)) {
|
|
689
|
+
// Calculate relative path from squad to project config
|
|
690
|
+
detected[key] = path.relative(squadPath, fullPath).replace(/\\/g, '/');
|
|
691
|
+
break;
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
// Only return if at least one config file was found
|
|
697
|
+
const foundCount = Object.keys(detected).length;
|
|
698
|
+
if (foundCount > 0) {
|
|
699
|
+
console.log(`[squad-generator] Detected ${foundCount} project config(s) in docs/framework/`);
|
|
700
|
+
return detected;
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
return null;
|
|
704
|
+
}
|
|
705
|
+
|
|
647
706
|
/**
|
|
648
707
|
* Validate generation configuration
|
|
649
708
|
* @param {Object} config - Configuration to validate
|
|
@@ -686,6 +745,7 @@ class SquadGenerator {
|
|
|
686
745
|
* @param {boolean} [config.includeAgent=true] - Include example agent
|
|
687
746
|
* @param {boolean} [config.includeTask=true] - Include example task
|
|
688
747
|
* @param {string} [config.aiosMinVersion] - Minimum AIOS version
|
|
748
|
+
* @param {string} [config.projectRoot] - Project root directory (for detecting project configs)
|
|
689
749
|
* @returns {Promise<Object>} Generation result with path and files
|
|
690
750
|
* @throws {SquadGeneratorError} If generation fails
|
|
691
751
|
*/
|
|
@@ -701,6 +761,7 @@ class SquadGenerator {
|
|
|
701
761
|
includeAgent: config.includeAgent !== false,
|
|
702
762
|
includeTask: config.includeTask !== false,
|
|
703
763
|
aiosMinVersion: config.aiosMinVersion || DEFAULT_AIOS_MIN_VERSION,
|
|
764
|
+
projectRoot: config.projectRoot || process.cwd(),
|
|
704
765
|
};
|
|
705
766
|
|
|
706
767
|
// Validate configuration
|
|
@@ -708,6 +769,23 @@ class SquadGenerator {
|
|
|
708
769
|
|
|
709
770
|
const squadPath = path.join(this.squadsPath, fullConfig.name);
|
|
710
771
|
|
|
772
|
+
// SQS-10: Detect project-level configs when configMode is 'extend'
|
|
773
|
+
let projectConfigs = null;
|
|
774
|
+
let useProjectConfigs = false;
|
|
775
|
+
|
|
776
|
+
if (fullConfig.configMode === 'extend') {
|
|
777
|
+
projectConfigs = await this.detectProjectConfigs(fullConfig.projectRoot, squadPath);
|
|
778
|
+
useProjectConfigs = projectConfigs !== null;
|
|
779
|
+
|
|
780
|
+
if (useProjectConfigs) {
|
|
781
|
+
console.log('[squad-generator] Using project-level configuration from docs/framework/');
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
// Store for use in squad.yaml generation
|
|
786
|
+
fullConfig._projectConfigs = projectConfigs;
|
|
787
|
+
fullConfig._useProjectConfigs = useProjectConfigs;
|
|
788
|
+
|
|
711
789
|
// Check if squad already exists
|
|
712
790
|
if (await this.pathExists(squadPath)) {
|
|
713
791
|
throw SquadGeneratorError.squadExists(fullConfig.name, squadPath);
|
|
@@ -734,17 +812,27 @@ class SquadGenerator {
|
|
|
734
812
|
files.push(filePath);
|
|
735
813
|
}
|
|
736
814
|
|
|
737
|
-
// Generate config files
|
|
738
|
-
|
|
739
|
-
'config
|
|
740
|
-
'config
|
|
741
|
-
'config
|
|
742
|
-
|
|
815
|
+
// Generate config files (SQS-10: skip if using project configs)
|
|
816
|
+
if (useProjectConfigs) {
|
|
817
|
+
// Don't create local config files, just add .gitkeep to config directory
|
|
818
|
+
console.log('[squad-generator] Skipping local config file creation (using project-level configs)');
|
|
819
|
+
const gitkeepPath = path.join(squadPath, 'config', '.gitkeep');
|
|
820
|
+
await fs.writeFile(gitkeepPath, '# Config files are referenced from project docs/framework/\n', 'utf-8');
|
|
821
|
+
files.push(gitkeepPath);
|
|
822
|
+
} else {
|
|
823
|
+
// Fallback: Create local config files (AC10.3)
|
|
824
|
+
console.log('[squad-generator] Creating local configuration files');
|
|
825
|
+
const configFiles = {
|
|
826
|
+
'config/coding-standards.md': generateCodingStandards(fullConfig),
|
|
827
|
+
'config/tech-stack.md': generateTechStack(fullConfig),
|
|
828
|
+
'config/source-tree.md': generateSourceTree(fullConfig),
|
|
829
|
+
};
|
|
743
830
|
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
831
|
+
for (const [filename, content] of Object.entries(configFiles)) {
|
|
832
|
+
const filePath = path.join(squadPath, filename);
|
|
833
|
+
await fs.writeFile(filePath, content, 'utf-8');
|
|
834
|
+
files.push(filePath);
|
|
835
|
+
}
|
|
748
836
|
}
|
|
749
837
|
|
|
750
838
|
// Generate example agent if requested
|
|
@@ -192,10 +192,11 @@ class SquadMigrator {
|
|
|
192
192
|
});
|
|
193
193
|
}
|
|
194
194
|
|
|
195
|
-
// Check for flat structure (missing directories)
|
|
195
|
+
// Check for flat structure (missing required directories)
|
|
196
|
+
// Note: Only 'tasks' and 'agents' are required for task-first architecture
|
|
197
|
+
// 'config' directory is optional and not checked
|
|
196
198
|
const hasTasksDir = await this._pathExists(path.join(squadPath, 'tasks'));
|
|
197
199
|
const hasAgentsDir = await this._pathExists(path.join(squadPath, 'agents'));
|
|
198
|
-
const hasConfigDir = await this._pathExists(path.join(squadPath, 'config'));
|
|
199
200
|
|
|
200
201
|
const missingDirs = [];
|
|
201
202
|
if (!hasTasksDir) {
|
|
@@ -204,9 +205,6 @@ class SquadMigrator {
|
|
|
204
205
|
if (!hasAgentsDir) {
|
|
205
206
|
missingDirs.push('agents');
|
|
206
207
|
}
|
|
207
|
-
if (!hasConfigDir) {
|
|
208
|
-
missingDirs.push('config');
|
|
209
|
-
}
|
|
210
208
|
|
|
211
209
|
if (missingDirs.length > 0) {
|
|
212
210
|
analysis.needsMigration = true;
|
|
@@ -172,6 +172,10 @@ class SquadValidator {
|
|
|
172
172
|
const agentsResult = await this.validateAgents(squadPath);
|
|
173
173
|
this._mergeResults(result, agentsResult);
|
|
174
174
|
|
|
175
|
+
// 5. Validate config references (SQS-10)
|
|
176
|
+
const configResult = await this.validateConfigReferences(squadPath);
|
|
177
|
+
this._mergeResults(result, configResult);
|
|
178
|
+
|
|
175
179
|
// In strict mode, warnings become errors
|
|
176
180
|
if (this.strict && result.warnings.length > 0) {
|
|
177
181
|
result.errors.push(...result.warnings);
|
|
@@ -381,6 +385,74 @@ class SquadValidator {
|
|
|
381
385
|
return result;
|
|
382
386
|
}
|
|
383
387
|
|
|
388
|
+
/**
|
|
389
|
+
* Validate config references in squad.yaml
|
|
390
|
+
* Implements AC10.4: Validates that referenced config files actually exist
|
|
391
|
+
*
|
|
392
|
+
* @param {string} squadPath - Path to squad directory
|
|
393
|
+
* @returns {Promise<ValidationResult>} Validation result for config references
|
|
394
|
+
* @see Story SQS-10: Project Config Reference for Squads
|
|
395
|
+
*/
|
|
396
|
+
async validateConfigReferences(squadPath) {
|
|
397
|
+
this._log(`Validating config references in: ${squadPath}`);
|
|
398
|
+
|
|
399
|
+
const result = {
|
|
400
|
+
valid: true,
|
|
401
|
+
errors: [],
|
|
402
|
+
warnings: [],
|
|
403
|
+
suggestions: [],
|
|
404
|
+
};
|
|
405
|
+
|
|
406
|
+
// Find and parse manifest
|
|
407
|
+
const manifestPath = await this._findManifest(squadPath);
|
|
408
|
+
if (!manifestPath) {
|
|
409
|
+
return result; // Already handled in manifest validation
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
let manifest;
|
|
413
|
+
try {
|
|
414
|
+
const content = await fs.readFile(manifestPath, 'utf-8');
|
|
415
|
+
manifest = yaml.load(content);
|
|
416
|
+
} catch {
|
|
417
|
+
return result; // Already handled in manifest validation
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// Check config section
|
|
421
|
+
if (!manifest || !manifest.config) {
|
|
422
|
+
return result; // No config section to validate
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
const configFields = ['coding-standards', 'tech-stack', 'source-tree'];
|
|
426
|
+
|
|
427
|
+
for (const field of configFields) {
|
|
428
|
+
const configPath = manifest.config[field];
|
|
429
|
+
if (!configPath) continue;
|
|
430
|
+
|
|
431
|
+
const resolvedPath = await this._resolveConfigPath(squadPath, configPath);
|
|
432
|
+
if (!resolvedPath) {
|
|
433
|
+
// Check if this is a project-level reference that doesn't exist
|
|
434
|
+
if (configPath.includes('..') || configPath.includes('docs/framework')) {
|
|
435
|
+
result.warnings.push({
|
|
436
|
+
code: ValidationErrorCodes.FILE_NOT_FOUND,
|
|
437
|
+
message: `Config reference not found: ${configPath}`,
|
|
438
|
+
suggestion: `Create the file at ${configPath} or update squad.yaml to use local config (config/${field}.md)`,
|
|
439
|
+
});
|
|
440
|
+
} else {
|
|
441
|
+
// Local config file missing - this is an error
|
|
442
|
+
result.errors.push({
|
|
443
|
+
code: ValidationErrorCodes.FILE_NOT_FOUND,
|
|
444
|
+
message: `Local config file not found: ${configPath}`,
|
|
445
|
+
suggestion: `Create ${path.join(squadPath, configPath)} or remove from config section`,
|
|
446
|
+
});
|
|
447
|
+
result.valid = false;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
this._log(`Config validation: ${result.errors.length} errors, ${result.warnings.length} warnings`);
|
|
453
|
+
return result;
|
|
454
|
+
}
|
|
455
|
+
|
|
384
456
|
/**
|
|
385
457
|
* Validate agent definitions
|
|
386
458
|
*
|
|
@@ -540,6 +612,32 @@ class SquadValidator {
|
|
|
540
612
|
}
|
|
541
613
|
}
|
|
542
614
|
|
|
615
|
+
/**
|
|
616
|
+
* Resolve config path - check both local and project-level paths
|
|
617
|
+
* Implements AC10.4: Validator Path Resolution
|
|
618
|
+
*
|
|
619
|
+
* @param {string} squadPath - Squad directory
|
|
620
|
+
* @param {string} configPath - Path from squad.yaml config section
|
|
621
|
+
* @returns {Promise<string|null>} Resolved absolute path or null if not found
|
|
622
|
+
* @see Story SQS-10: Project Config Reference for Squads
|
|
623
|
+
* @private
|
|
624
|
+
*/
|
|
625
|
+
async _resolveConfigPath(squadPath, configPath) {
|
|
626
|
+
if (!configPath) return null;
|
|
627
|
+
|
|
628
|
+
// Resolve path relative to squad directory
|
|
629
|
+
// path.resolve handles both local paths (config/file.md) and relative paths (../../docs/framework/...)
|
|
630
|
+
// Simplified from redundant path.resolve + path.join (CodeRabbit nitpick)
|
|
631
|
+
const resolvedPath = path.resolve(squadPath, configPath);
|
|
632
|
+
if (await this._pathExists(resolvedPath)) {
|
|
633
|
+
this._log(`Resolved config path: ${configPath} -> ${resolvedPath}`);
|
|
634
|
+
return resolvedPath;
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
this._log(`Config path not found: ${configPath}`);
|
|
638
|
+
return null;
|
|
639
|
+
}
|
|
640
|
+
|
|
543
641
|
/**
|
|
544
642
|
* Validate a single task file
|
|
545
643
|
* @private
|
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
# Create Service
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
Create a new service using standardized Handlebars templates from WIS-10. Generates consistent TypeScript service structures with proper configuration, testing, and documentation.
|
|
6
|
+
|
|
7
|
+
## Task Definition (AIOS Task Format V1.0)
|
|
8
|
+
|
|
9
|
+
```yaml
|
|
10
|
+
task: createService()
|
|
11
|
+
agent: "@dev"
|
|
12
|
+
responsável: Dex (Developer)
|
|
13
|
+
responsavel_type: Agente
|
|
14
|
+
atomic_layer: Config
|
|
15
|
+
|
|
16
|
+
elicit: true
|
|
17
|
+
|
|
18
|
+
inputs:
|
|
19
|
+
- name: service_name
|
|
20
|
+
type: string
|
|
21
|
+
required: true
|
|
22
|
+
pattern: "^[a-z][a-z0-9-]*$"
|
|
23
|
+
validation: Must be kebab-case, start with letter
|
|
24
|
+
|
|
25
|
+
- name: service_type
|
|
26
|
+
type: enum
|
|
27
|
+
options: ["api-integration", "utility", "agent-tool"]
|
|
28
|
+
required: true
|
|
29
|
+
default: "utility"
|
|
30
|
+
|
|
31
|
+
- name: has_auth
|
|
32
|
+
type: boolean
|
|
33
|
+
required: false
|
|
34
|
+
default: false
|
|
35
|
+
|
|
36
|
+
- name: description
|
|
37
|
+
type: string
|
|
38
|
+
required: true
|
|
39
|
+
validation: Non-empty, max 200 characters
|
|
40
|
+
|
|
41
|
+
- name: env_vars
|
|
42
|
+
type: array
|
|
43
|
+
required: false
|
|
44
|
+
default: []
|
|
45
|
+
|
|
46
|
+
outputs:
|
|
47
|
+
- name: service_directory
|
|
48
|
+
type: directory
|
|
49
|
+
location: ".aios-core/infrastructure/services/{service_name}/"
|
|
50
|
+
persistido: true
|
|
51
|
+
|
|
52
|
+
- name: files_created
|
|
53
|
+
type: array
|
|
54
|
+
destino: Memory
|
|
55
|
+
persistido: false
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## Pre-Conditions
|
|
61
|
+
|
|
62
|
+
```yaml
|
|
63
|
+
pre-conditions:
|
|
64
|
+
- [ ] WIS-10 templates exist at .aios-core/development/templates/service-template/
|
|
65
|
+
tipo: pre-condition
|
|
66
|
+
blocker: true
|
|
67
|
+
validação: Check template directory exists with required .hbs files
|
|
68
|
+
error_message: "Templates not found. Run WIS-10 first."
|
|
69
|
+
|
|
70
|
+
- [ ] Service name is unique (no existing service with same name)
|
|
71
|
+
tipo: pre-condition
|
|
72
|
+
blocker: true
|
|
73
|
+
validação: Check .aios-core/infrastructure/services/{name}/ does not exist
|
|
74
|
+
error_message: "Service '{name}' already exists. Choose a different name."
|
|
75
|
+
|
|
76
|
+
- [ ] Service name follows kebab-case pattern
|
|
77
|
+
tipo: pre-condition
|
|
78
|
+
blocker: true
|
|
79
|
+
validação: Regex match ^[a-z][a-z0-9-]*$
|
|
80
|
+
error_message: "Invalid name. Use kebab-case (e.g., my-api-service)"
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## Interactive Elicitation Process
|
|
86
|
+
|
|
87
|
+
### Step 1: Service Name
|
|
88
|
+
```
|
|
89
|
+
ELICIT: Service Name
|
|
90
|
+
|
|
91
|
+
What is the service name?
|
|
92
|
+
(Use kebab-case, e.g., "github-api", "file-processor", "auth-helper")
|
|
93
|
+
|
|
94
|
+
→ Validation: ^[a-z][a-z0-9-]*$
|
|
95
|
+
→ Check: Unique (not existing)
|
|
96
|
+
→ On invalid: Re-prompt with error message
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Step 2: Service Type
|
|
100
|
+
```
|
|
101
|
+
ELICIT: Service Type
|
|
102
|
+
|
|
103
|
+
What type of service is this?
|
|
104
|
+
|
|
105
|
+
1. api-integration - External API client with rate limiting and auth
|
|
106
|
+
2. utility - Internal helper/utility service
|
|
107
|
+
3. agent-tool - Tool for AIOS agents
|
|
108
|
+
|
|
109
|
+
→ Default: utility
|
|
110
|
+
→ If api-integration: Enable client.ts generation
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Step 3: Authentication
|
|
114
|
+
```
|
|
115
|
+
ELICIT: Authentication Required
|
|
116
|
+
|
|
117
|
+
Does this service require authentication?
|
|
118
|
+
|
|
119
|
+
1. Yes - Include auth configuration and secure headers
|
|
120
|
+
2. No - No authentication needed
|
|
121
|
+
|
|
122
|
+
→ Default: No
|
|
123
|
+
→ If Yes: Add auth placeholders to config
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Step 4: Description
|
|
127
|
+
```
|
|
128
|
+
ELICIT: Service Description
|
|
129
|
+
|
|
130
|
+
Brief description of the service:
|
|
131
|
+
(Max 200 characters, will appear in README and JSDoc)
|
|
132
|
+
|
|
133
|
+
→ Validation: Non-empty, <= 200 chars
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Step 5: Environment Variables
|
|
137
|
+
```
|
|
138
|
+
ELICIT: Environment Variables
|
|
139
|
+
|
|
140
|
+
What environment variables does this service need?
|
|
141
|
+
(Enter comma-separated list, or 'none')
|
|
142
|
+
|
|
143
|
+
Examples: API_KEY, BASE_URL, TIMEOUT_MS
|
|
144
|
+
|
|
145
|
+
→ Default: none
|
|
146
|
+
→ Parse: Split by comma, trim whitespace
|
|
147
|
+
→ Generate: .env.example entries
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## Implementation Steps
|
|
153
|
+
|
|
154
|
+
### Step 1: Validate Inputs
|
|
155
|
+
```javascript
|
|
156
|
+
// Validate service_name
|
|
157
|
+
const namePattern = /^[a-z][a-z0-9-]*$/;
|
|
158
|
+
if (!namePattern.test(serviceName)) {
|
|
159
|
+
throw new Error(`Invalid service name: ${serviceName}. Use kebab-case.`);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Check uniqueness
|
|
163
|
+
const targetDir = `.aios-core/infrastructure/services/${serviceName}/`;
|
|
164
|
+
if (fs.existsSync(targetDir)) {
|
|
165
|
+
throw new Error(`Service '${serviceName}' already exists.`);
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Step 2: Load Templates
|
|
170
|
+
```javascript
|
|
171
|
+
const templateDir = '.aios-core/development/templates/service-template/';
|
|
172
|
+
const templates = [
|
|
173
|
+
'README.md.hbs',
|
|
174
|
+
'index.ts.hbs',
|
|
175
|
+
'types.ts.hbs',
|
|
176
|
+
'errors.ts.hbs',
|
|
177
|
+
'package.json.hbs',
|
|
178
|
+
'tsconfig.json', // Static (no .hbs)
|
|
179
|
+
'jest.config.js', // Static (no .hbs)
|
|
180
|
+
'__tests__/index.test.ts.hbs'
|
|
181
|
+
];
|
|
182
|
+
|
|
183
|
+
// Conditional: client.ts.hbs only for api-integration
|
|
184
|
+
if (serviceType === 'api-integration') {
|
|
185
|
+
templates.push('client.ts.hbs');
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Step 3: Prepare Template Context
|
|
190
|
+
```javascript
|
|
191
|
+
const context = {
|
|
192
|
+
serviceName: serviceName, // kebab-case
|
|
193
|
+
pascalCase: toPascalCase(serviceName), // PascalCase
|
|
194
|
+
camelCase: toCamelCase(serviceName), // camelCase
|
|
195
|
+
description: description,
|
|
196
|
+
isApiIntegration: serviceType === 'api-integration',
|
|
197
|
+
hasAuth: hasAuth,
|
|
198
|
+
envVars: envVars.map(v => ({
|
|
199
|
+
name: v,
|
|
200
|
+
description: `${v} environment variable`
|
|
201
|
+
})),
|
|
202
|
+
storyId: 'WIS-11',
|
|
203
|
+
createdAt: new Date().toISOString().split('T')[0]
|
|
204
|
+
};
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### Step 4: Generate Files
|
|
208
|
+
```javascript
|
|
209
|
+
// Create target directory
|
|
210
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
211
|
+
fs.mkdirSync(`${targetDir}__tests__/`, { recursive: true });
|
|
212
|
+
|
|
213
|
+
// Process each template
|
|
214
|
+
for (const templateFile of templates) {
|
|
215
|
+
const templatePath = `${templateDir}${templateFile}`;
|
|
216
|
+
const isHandlebars = templateFile.endsWith('.hbs');
|
|
217
|
+
|
|
218
|
+
// Determine output filename
|
|
219
|
+
const outputFile = isHandlebars
|
|
220
|
+
? templateFile.replace('.hbs', '')
|
|
221
|
+
: templateFile;
|
|
222
|
+
const outputPath = `${targetDir}${outputFile}`;
|
|
223
|
+
|
|
224
|
+
if (isHandlebars) {
|
|
225
|
+
// Render Handlebars template
|
|
226
|
+
const template = fs.readFileSync(templatePath, 'utf8');
|
|
227
|
+
const compiled = Handlebars.compile(template);
|
|
228
|
+
const content = compiled(context);
|
|
229
|
+
fs.writeFileSync(outputPath, content);
|
|
230
|
+
} else {
|
|
231
|
+
// Copy static file
|
|
232
|
+
fs.copyFileSync(templatePath, outputPath);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### Step 5: Post-Generation
|
|
238
|
+
```bash
|
|
239
|
+
# Navigate to service directory
|
|
240
|
+
cd .aios-core/infrastructure/services/{service_name}/
|
|
241
|
+
|
|
242
|
+
# Install dependencies
|
|
243
|
+
npm install
|
|
244
|
+
|
|
245
|
+
# Build TypeScript
|
|
246
|
+
npm run build
|
|
247
|
+
|
|
248
|
+
# Run tests
|
|
249
|
+
npm test
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
## Handlebars Helpers Required
|
|
255
|
+
|
|
256
|
+
The following helpers must be available:
|
|
257
|
+
|
|
258
|
+
```javascript
|
|
259
|
+
Handlebars.registerHelper('pascalCase', (str) => {
|
|
260
|
+
return str.split('-').map(word =>
|
|
261
|
+
word.charAt(0).toUpperCase() + word.slice(1)
|
|
262
|
+
).join('');
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
Handlebars.registerHelper('camelCase', (str) => {
|
|
266
|
+
const pascal = str.split('-').map(word =>
|
|
267
|
+
word.charAt(0).toUpperCase() + word.slice(1)
|
|
268
|
+
).join('');
|
|
269
|
+
return pascal.charAt(0).toLowerCase() + pascal.slice(1);
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
Handlebars.registerHelper('kebabCase', (str) => {
|
|
273
|
+
return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
Handlebars.registerHelper('upperCase', (str) => {
|
|
277
|
+
return str.toUpperCase().replace(/-/g, '_');
|
|
278
|
+
});
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
---
|
|
282
|
+
|
|
283
|
+
## Post-Conditions
|
|
284
|
+
|
|
285
|
+
```yaml
|
|
286
|
+
post-conditions:
|
|
287
|
+
- [ ] All template files generated successfully
|
|
288
|
+
tipo: post-condition
|
|
289
|
+
blocker: true
|
|
290
|
+
validação: Verify all expected files exist in target directory
|
|
291
|
+
|
|
292
|
+
- [ ] TypeScript compiles without errors
|
|
293
|
+
tipo: post-condition
|
|
294
|
+
blocker: false
|
|
295
|
+
validação: Run npm run build, check exit code
|
|
296
|
+
|
|
297
|
+
- [ ] Tests pass
|
|
298
|
+
tipo: post-condition
|
|
299
|
+
blocker: false
|
|
300
|
+
validação: Run npm test, check exit code
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
---
|
|
304
|
+
|
|
305
|
+
## Error Handling
|
|
306
|
+
|
|
307
|
+
| Error | Cause | Resolution |
|
|
308
|
+
|-------|-------|------------|
|
|
309
|
+
| Service name exists | Directory already present | Prompt for different name |
|
|
310
|
+
| Template not found | WIS-10 not installed | Error: "Run WIS-10 first" |
|
|
311
|
+
| npm install fails | Network/package issues | Warning, continue without deps |
|
|
312
|
+
| Build fails | TypeScript errors | Warning, show errors, continue |
|
|
313
|
+
| Invalid name format | Name not kebab-case | Re-prompt with validation error |
|
|
314
|
+
|
|
315
|
+
**Error Recovery Strategy:**
|
|
316
|
+
```javascript
|
|
317
|
+
// Atomic generation - rollback on failure
|
|
318
|
+
try {
|
|
319
|
+
generateAllFiles(targetDir, templates, context);
|
|
320
|
+
} catch (error) {
|
|
321
|
+
// Clean up partial files
|
|
322
|
+
if (fs.existsSync(targetDir)) {
|
|
323
|
+
fs.rmSync(targetDir, { recursive: true, force: true });
|
|
324
|
+
}
|
|
325
|
+
throw error;
|
|
326
|
+
}
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
---
|
|
330
|
+
|
|
331
|
+
## Performance
|
|
332
|
+
|
|
333
|
+
```yaml
|
|
334
|
+
duration_expected: 5-30s (excluding npm install)
|
|
335
|
+
cost_estimated: $0.002-0.005
|
|
336
|
+
token_usage: ~1,000-2,000 tokens
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
---
|
|
340
|
+
|
|
341
|
+
## Success Output
|
|
342
|
+
|
|
343
|
+
```
|
|
344
|
+
============================================
|
|
345
|
+
SERVICE CREATED SUCCESSFULLY
|
|
346
|
+
============================================
|
|
347
|
+
|
|
348
|
+
Service: {service_name}
|
|
349
|
+
Type: {service_type}
|
|
350
|
+
Location: .aios-core/infrastructure/services/{service_name}/
|
|
351
|
+
|
|
352
|
+
Files Created:
|
|
353
|
+
README.md
|
|
354
|
+
index.ts
|
|
355
|
+
types.ts
|
|
356
|
+
errors.ts
|
|
357
|
+
client.ts (if api-integration)
|
|
358
|
+
package.json
|
|
359
|
+
tsconfig.json
|
|
360
|
+
jest.config.js
|
|
361
|
+
__tests__/index.test.ts
|
|
362
|
+
|
|
363
|
+
Next Steps:
|
|
364
|
+
1. cd .aios-core/infrastructure/services/{service_name}
|
|
365
|
+
2. Review generated code
|
|
366
|
+
3. Implement service methods in index.ts
|
|
367
|
+
4. Add tests in __tests__/
|
|
368
|
+
5. Update environment variables as needed
|
|
369
|
+
|
|
370
|
+
============================================
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
---
|
|
374
|
+
|
|
375
|
+
## Metadata
|
|
376
|
+
|
|
377
|
+
```yaml
|
|
378
|
+
story: WIS-11
|
|
379
|
+
version: 1.0.0
|
|
380
|
+
created: 2025-12-24
|
|
381
|
+
author: "@dev (Dex)"
|
|
382
|
+
dependencies:
|
|
383
|
+
templates:
|
|
384
|
+
- service-template/ (from WIS-10)
|
|
385
|
+
tasks: []
|
|
386
|
+
tags:
|
|
387
|
+
- service-generation
|
|
388
|
+
- scaffolding
|
|
389
|
+
- handlebars
|
|
390
|
+
- typescript
|
|
391
|
+
```
|