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.
Files changed (62) hide show
  1. package/.aios-core/core/session/context-detector.js +3 -0
  2. package/.aios-core/core/session/context-loader.js +154 -0
  3. package/.aios-core/data/learned-patterns.yaml +3 -0
  4. package/.aios-core/data/workflow-patterns.yaml +347 -3
  5. package/.aios-core/development/agents/dev.md +13 -0
  6. package/.aios-core/development/agents/squad-creator.md +30 -0
  7. package/.aios-core/development/scripts/squad/squad-analyzer.js +638 -0
  8. package/.aios-core/development/scripts/squad/squad-extender.js +871 -0
  9. package/.aios-core/development/scripts/squad/squad-generator.js +107 -19
  10. package/.aios-core/development/scripts/squad/squad-migrator.js +3 -5
  11. package/.aios-core/development/scripts/squad/squad-validator.js +98 -0
  12. package/.aios-core/development/tasks/create-service.md +391 -0
  13. package/.aios-core/development/tasks/next.md +294 -0
  14. package/.aios-core/development/tasks/patterns.md +334 -0
  15. package/.aios-core/development/tasks/squad-creator-analyze.md +315 -0
  16. package/.aios-core/development/tasks/squad-creator-create.md +26 -3
  17. package/.aios-core/development/tasks/squad-creator-extend.md +411 -0
  18. package/.aios-core/development/tasks/squad-creator-validate.md +9 -1
  19. package/.aios-core/development/tasks/waves.md +205 -0
  20. package/.aios-core/development/templates/service-template/README.md.hbs +158 -0
  21. package/.aios-core/development/templates/service-template/__tests__/index.test.ts.hbs +237 -0
  22. package/.aios-core/development/templates/service-template/client.ts.hbs +403 -0
  23. package/.aios-core/development/templates/service-template/errors.ts.hbs +182 -0
  24. package/.aios-core/development/templates/service-template/index.ts.hbs +120 -0
  25. package/.aios-core/development/templates/service-template/jest.config.js +89 -0
  26. package/.aios-core/development/templates/service-template/package.json.hbs +87 -0
  27. package/.aios-core/development/templates/service-template/tsconfig.json +45 -0
  28. package/.aios-core/development/templates/service-template/types.ts.hbs +145 -0
  29. package/.aios-core/development/templates/squad/agent-template.md +69 -0
  30. package/.aios-core/development/templates/squad/checklist-template.md +82 -0
  31. package/.aios-core/development/templates/squad/data-template.yaml +105 -0
  32. package/.aios-core/development/templates/squad/script-template.js +179 -0
  33. package/.aios-core/development/templates/squad/task-template.md +125 -0
  34. package/.aios-core/development/templates/squad/template-template.md +97 -0
  35. package/.aios-core/development/templates/squad/tool-template.js +103 -0
  36. package/.aios-core/development/templates/squad/workflow-template.yaml +108 -0
  37. package/.aios-core/infrastructure/scripts/ide-sync/agent-parser.js +45 -1
  38. package/.aios-core/infrastructure/scripts/ide-sync/transformers/antigravity.js +6 -6
  39. package/.aios-core/infrastructure/scripts/ide-sync/transformers/cursor.js +5 -4
  40. package/.aios-core/infrastructure/scripts/ide-sync/transformers/trae.js +3 -3
  41. package/.aios-core/infrastructure/scripts/ide-sync/transformers/windsurf.js +3 -3
  42. package/.aios-core/install-manifest.yaml +139 -35
  43. package/.aios-core/quality/metrics-collector.js +27 -0
  44. package/.aios-core/scripts/session-context-loader.js +13 -254
  45. package/.aios-core/utils/aios-validator.js +25 -0
  46. package/.aios-core/workflow-intelligence/__tests__/confidence-scorer.test.js +334 -0
  47. package/.aios-core/workflow-intelligence/__tests__/integration.test.js +337 -0
  48. package/.aios-core/workflow-intelligence/__tests__/suggestion-engine.test.js +431 -0
  49. package/.aios-core/workflow-intelligence/__tests__/wave-analyzer.test.js +458 -0
  50. package/.aios-core/workflow-intelligence/__tests__/workflow-registry.test.js +302 -0
  51. package/.aios-core/workflow-intelligence/engine/confidence-scorer.js +305 -0
  52. package/.aios-core/workflow-intelligence/engine/output-formatter.js +285 -0
  53. package/.aios-core/workflow-intelligence/engine/suggestion-engine.js +603 -0
  54. package/.aios-core/workflow-intelligence/engine/wave-analyzer.js +676 -0
  55. package/.aios-core/workflow-intelligence/index.js +327 -0
  56. package/.aios-core/workflow-intelligence/learning/capture-hook.js +147 -0
  57. package/.aios-core/workflow-intelligence/learning/index.js +230 -0
  58. package/.aios-core/workflow-intelligence/learning/pattern-capture.js +340 -0
  59. package/.aios-core/workflow-intelligence/learning/pattern-store.js +498 -0
  60. package/.aios-core/workflow-intelligence/learning/pattern-validator.js +309 -0
  61. package/.aios-core/workflow-intelligence/registry/workflow-registry.js +358 -0
  62. package/package.json +1 -1
@@ -304,15 +304,27 @@ function generateSquadYaml(config) {
304
304
  components.tasks = [];
305
305
  }
306
306
 
307
- const configSection =
308
- config.configMode === 'none'
309
- ? {}
310
- : {
311
- extends: config.configMode,
312
- 'coding-standards': 'config/coding-standards.md',
313
- 'tech-stack': 'config/tech-stack.md',
314
- 'source-tree': 'config/source-tree.md',
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
- const configFiles = {
739
- 'config/coding-standards.md': generateCodingStandards(fullConfig),
740
- 'config/tech-stack.md': generateTechStack(fullConfig),
741
- 'config/source-tree.md': generateSourceTree(fullConfig),
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
- for (const [filename, content] of Object.entries(configFiles)) {
745
- const filePath = path.join(squadPath, filename);
746
- await fs.writeFile(filePath, content, 'utf-8');
747
- files.push(filePath);
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
+ ```