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
@@ -106,6 +106,16 @@ commands:
106
106
  description: "Migrate legacy squad to AIOS 2.1 format"
107
107
  task: squad-creator-migrate.md
108
108
 
109
+ # Analysis & Extension (Sprint 14)
110
+ - name: analyze-squad
111
+ visibility: [full, quick, key]
112
+ description: "Analyze squad structure, coverage, and get improvement suggestions"
113
+ task: squad-creator-analyze.md
114
+ - name: extend-squad
115
+ visibility: [full, quick, key]
116
+ description: "Add new components (agents, tasks, templates, etc.) to existing squad"
117
+ task: squad-creator-extend.md
118
+
109
119
  # Distribution (Sprint 8 - Placeholders)
110
120
  - name: download-squad
111
121
  visibility: [full]
@@ -135,6 +145,8 @@ dependencies:
135
145
  - squad-creator-validate.md
136
146
  - squad-creator-list.md
137
147
  - squad-creator-migrate.md
148
+ - squad-creator-analyze.md
149
+ - squad-creator-extend.md
138
150
  - squad-creator-download.md
139
151
  - squad-creator-publish.md
140
152
  - squad-creator-sync-synkra.md
@@ -144,6 +156,8 @@ dependencies:
144
156
  - squad/squad-generator.js
145
157
  - squad/squad-designer.js
146
158
  - squad/squad-migrator.js
159
+ - squad/squad-analyzer.js
160
+ - squad/squad-extender.js
147
161
  schemas:
148
162
  - squad-schema.json
149
163
  - squad-design-schema.json
@@ -179,6 +193,14 @@ squad_distribution:
179
193
  - `*validate-squad {name}` - Validate existing squad
180
194
  - `*list-squads` - List local squads
181
195
 
196
+ **Analysis & Extension (NEW):**
197
+ - `*analyze-squad {name}` - Analyze squad structure and get suggestions
198
+ - `*analyze-squad {name} --verbose` - Include file details in analysis
199
+ - `*analyze-squad {name} --format markdown` - Output as markdown file
200
+ - `*extend-squad {name}` - Add component interactively
201
+ - `*extend-squad {name} --add agent --name my-agent` - Add agent directly
202
+ - `*extend-squad {name} --add task --name my-task --agent lead-agent` - Add task with agent
203
+
182
204
  **Migration:**
183
205
  - `*migrate-squad {path}` - Migrate legacy squad to AIOS 2.1 format
184
206
  - `*migrate-squad {path} --dry-run` - Preview migration changes
@@ -212,6 +234,8 @@ Type `*help` to see all commands, or `*guide` for detailed usage.
212
234
  ### When to Use Me
213
235
  - **Designing squads from documentation** (PRDs, specs, requirements)
214
236
  - Creating new squads for your project
237
+ - **Analyzing existing squads** for coverage and improvements
238
+ - **Extending squads** with new components (agents, tasks, templates, etc.)
215
239
  - Validating existing squad structure
216
240
  - Preparing squads for distribution
217
241
  - Listing available local squads
@@ -239,6 +263,12 @@ Type `*help` to see all commands, or `*guide` for detailed usage.
239
263
  - Publish to aios-squads (public)
240
264
  - Sync to Synkra API (marketplace)
241
265
 
266
+ **Option C: Continuous Improvement (For existing squads)**
267
+ 1. **Analyze squad** → `*analyze-squad my-squad`
268
+ 2. **Review suggestions** → Coverage metrics and improvement hints
269
+ 3. **Add components** → `*extend-squad my-squad`
270
+ 4. **Validate** → `*validate-squad my-squad`
271
+
242
272
  ### Squad Structure
243
273
  ```text
244
274
  ./squads/my-squad/
@@ -0,0 +1,638 @@
1
+ /**
2
+ * Squad Analyzer Utility
3
+ *
4
+ * Analyzes existing squads and generates comprehensive reports
5
+ * with component inventory, coverage metrics, and improvement suggestions.
6
+ *
7
+ * Used by: squad-creator agent (*analyze-squad task)
8
+ *
9
+ * @module squad-analyzer
10
+ * @version 1.0.0
11
+ * @see Story SQS-11: Squad Analyze & Extend
12
+ */
13
+
14
+ const fs = require('fs').promises;
15
+ const path = require('path');
16
+ const yaml = require('js-yaml');
17
+
18
+ /**
19
+ * Default path for squads directory
20
+ * @constant {string}
21
+ */
22
+ const DEFAULT_SQUADS_PATH = './squads';
23
+
24
+ /**
25
+ * Component directories in a squad (from squad-schema.json)
26
+ * @constant {string[]}
27
+ */
28
+ const COMPONENT_DIRECTORIES = [
29
+ 'agents',
30
+ 'tasks',
31
+ 'workflows',
32
+ 'checklists',
33
+ 'templates',
34
+ 'tools',
35
+ 'scripts',
36
+ 'data',
37
+ ];
38
+
39
+ /**
40
+ * Config files to check for coverage
41
+ * @constant {string[]}
42
+ */
43
+ const CONFIG_FILES = [
44
+ 'README.md',
45
+ 'config/coding-standards.md',
46
+ 'config/tech-stack.md',
47
+ 'config/source-tree.md',
48
+ ];
49
+
50
+ /**
51
+ * Manifest file names in order of preference
52
+ * @constant {string[]}
53
+ */
54
+ const MANIFEST_FILES = ['squad.yaml', 'config.yaml'];
55
+
56
+ /**
57
+ * Error codes for SquadAnalyzerError
58
+ * @enum {string}
59
+ */
60
+ const ErrorCodes = {
61
+ SQUAD_NOT_FOUND: 'SQUAD_NOT_FOUND',
62
+ MANIFEST_NOT_FOUND: 'MANIFEST_NOT_FOUND',
63
+ YAML_PARSE_ERROR: 'YAML_PARSE_ERROR',
64
+ PERMISSION_DENIED: 'PERMISSION_DENIED',
65
+ ANALYSIS_FAILED: 'ANALYSIS_FAILED',
66
+ };
67
+
68
+ /**
69
+ * Custom error class for Squad Analyzer operations
70
+ * @extends Error
71
+ */
72
+ class SquadAnalyzerError extends Error {
73
+ /**
74
+ * Create a SquadAnalyzerError
75
+ * @param {string} code - Error code from ErrorCodes enum
76
+ * @param {string} message - Human-readable error message
77
+ * @param {string} [suggestion] - Suggested fix for the error
78
+ */
79
+ constructor(code, message, suggestion) {
80
+ super(message);
81
+ this.name = 'SquadAnalyzerError';
82
+ this.code = code;
83
+ this.suggestion = suggestion || '';
84
+
85
+ if (Error.captureStackTrace) {
86
+ Error.captureStackTrace(this, SquadAnalyzerError);
87
+ }
88
+ }
89
+
90
+ /**
91
+ * Create error for squad not found
92
+ * @param {string} squadName - Name of the squad
93
+ * @returns {SquadAnalyzerError}
94
+ */
95
+ static squadNotFound(squadName) {
96
+ return new SquadAnalyzerError(
97
+ ErrorCodes.SQUAD_NOT_FOUND,
98
+ `Squad "${squadName}" not found`,
99
+ `Use *list-squads to see available squads, or *create-squad ${squadName} to create it`
100
+ );
101
+ }
102
+
103
+ /**
104
+ * Create error for manifest not found
105
+ * @param {string} squadPath - Path to squad directory
106
+ * @returns {SquadAnalyzerError}
107
+ */
108
+ static manifestNotFound(squadPath) {
109
+ return new SquadAnalyzerError(
110
+ ErrorCodes.MANIFEST_NOT_FOUND,
111
+ `No squad.yaml or config.yaml found in ${squadPath}`,
112
+ 'Create squad.yaml with squad metadata'
113
+ );
114
+ }
115
+ }
116
+
117
+ /**
118
+ * Squad Analyzer class for analyzing squad structure and content
119
+ */
120
+ class SquadAnalyzer {
121
+ /**
122
+ * Create a SquadAnalyzer instance
123
+ * @param {Object} [options={}] - Configuration options
124
+ * @param {string} [options.squadsPath] - Custom squads directory path
125
+ * @param {boolean} [options.verbose=false] - Enable verbose output
126
+ */
127
+ constructor(options = {}) {
128
+ this.squadsPath = options.squadsPath || DEFAULT_SQUADS_PATH;
129
+ this.verbose = options.verbose || false;
130
+ }
131
+
132
+ /**
133
+ * Analyze a squad and generate complete report
134
+ * @param {string} squadName - Name of the squad to analyze
135
+ * @param {Object} [options={}] - Analysis options
136
+ * @param {boolean} [options.suggestions=true] - Include suggestions
137
+ * @param {boolean} [options.verbose=false] - Include file details
138
+ * @returns {Promise<Object>} Analysis result
139
+ */
140
+ async analyze(squadName, options = {}) {
141
+ const includeSuggestions = options.suggestions !== false;
142
+ const verbose = options.verbose || this.verbose;
143
+
144
+ const squadPath = path.join(this.squadsPath, squadName);
145
+
146
+ // Check if squad exists
147
+ const exists = await this._directoryExists(squadPath);
148
+ if (!exists) {
149
+ throw SquadAnalyzerError.squadNotFound(squadName);
150
+ }
151
+
152
+ // Load manifest
153
+ const manifest = await this.loadManifest(squadPath);
154
+
155
+ // Build overview
156
+ const overview = this._buildOverview(manifest, squadName);
157
+
158
+ // Inventory components
159
+ const inventory = await this.inventoryComponents(squadPath, verbose);
160
+
161
+ // Calculate coverage
162
+ const coverage = this.calculateCoverage(inventory, manifest, squadPath);
163
+
164
+ // Generate suggestions
165
+ const suggestions = includeSuggestions
166
+ ? this.generateSuggestions(inventory, coverage, manifest)
167
+ : [];
168
+
169
+ return {
170
+ overview,
171
+ inventory,
172
+ coverage,
173
+ suggestions,
174
+ squadPath,
175
+ };
176
+ }
177
+
178
+ /**
179
+ * Load and parse squad manifest
180
+ * @param {string} squadPath - Path to squad directory
181
+ * @returns {Promise<Object>} Parsed manifest
182
+ */
183
+ async loadManifest(squadPath) {
184
+ for (const manifestFile of MANIFEST_FILES) {
185
+ const manifestPath = path.join(squadPath, manifestFile);
186
+ try {
187
+ const content = await fs.readFile(manifestPath, 'utf8');
188
+ return yaml.load(content);
189
+ } catch (error) {
190
+ if (error.code !== 'ENOENT') {
191
+ throw new SquadAnalyzerError(
192
+ ErrorCodes.YAML_PARSE_ERROR,
193
+ `Failed to parse ${manifestFile}: ${error.message}`,
194
+ 'Check YAML syntax - use a YAML linter'
195
+ );
196
+ }
197
+ }
198
+ }
199
+
200
+ throw SquadAnalyzerError.manifestNotFound(squadPath);
201
+ }
202
+
203
+ /**
204
+ * Inventory all components in squad
205
+ * @param {string} squadPath - Path to squad directory
206
+ * @param {boolean} [verbose=false] - Include file content previews
207
+ * @returns {Promise<Object>} Component inventory by type
208
+ */
209
+ async inventoryComponents(squadPath, verbose = false) {
210
+ const inventory = {};
211
+
212
+ for (const dir of COMPONENT_DIRECTORIES) {
213
+ const dirPath = path.join(squadPath, dir);
214
+ inventory[dir] = await this._listFiles(dirPath, verbose);
215
+ }
216
+
217
+ return inventory;
218
+ }
219
+
220
+ /**
221
+ * Calculate coverage metrics
222
+ * @param {Object} inventory - Component inventory
223
+ * @param {Object} manifest - Squad manifest
224
+ * @param {string} squadPath - Path to squad
225
+ * @returns {Object} Coverage metrics
226
+ */
227
+ calculateCoverage(inventory, manifest, squadPath) {
228
+ // Agents coverage
229
+ const agentCount = inventory.agents.length;
230
+ const agentsWithTasks = this._countAgentsWithTasks(inventory);
231
+ const agentCoverage = agentCount > 0
232
+ ? Math.round((agentsWithTasks / agentCount) * 100)
233
+ : 0;
234
+
235
+ // Tasks coverage (relative to agents)
236
+ const taskCount = inventory.tasks.length;
237
+ const expectedTasks = agentCount * 2; // Expect at least 2 tasks per agent
238
+ const taskCoverage = expectedTasks > 0
239
+ ? Math.min(100, Math.round((taskCount / expectedTasks) * 100))
240
+ : 0;
241
+
242
+ // Directory coverage
243
+ const populatedDirs = COMPONENT_DIRECTORIES.filter(
244
+ (dir) => inventory[dir].length > 0
245
+ ).length;
246
+ const dirCoverage = Math.round(
247
+ (populatedDirs / COMPONENT_DIRECTORIES.length) * 100
248
+ );
249
+
250
+ // Config coverage (check for common files)
251
+ const configCoverage = this._calculateConfigCoverage(squadPath, inventory);
252
+
253
+ return {
254
+ agents: {
255
+ total: agentCount,
256
+ withTasks: agentsWithTasks,
257
+ percentage: agentCoverage,
258
+ },
259
+ tasks: {
260
+ total: taskCount,
261
+ expected: expectedTasks,
262
+ percentage: taskCoverage,
263
+ },
264
+ directories: {
265
+ populated: populatedDirs,
266
+ total: COMPONENT_DIRECTORIES.length,
267
+ percentage: dirCoverage,
268
+ },
269
+ config: configCoverage,
270
+ };
271
+ }
272
+
273
+ /**
274
+ * Generate improvement suggestions
275
+ * @param {Object} inventory - Component inventory
276
+ * @param {Object} coverage - Coverage metrics
277
+ * @param {Object} manifest - Squad manifest
278
+ * @returns {Array} List of suggestions
279
+ */
280
+ generateSuggestions(inventory, coverage, manifest) {
281
+ const suggestions = [];
282
+
283
+ // Suggest adding tasks for agents without tasks
284
+ if (coverage.agents.withTasks < coverage.agents.total) {
285
+ const agentsWithoutTasks = coverage.agents.total - coverage.agents.withTasks;
286
+ suggestions.push({
287
+ priority: 'high',
288
+ category: 'tasks',
289
+ message: `Add tasks for ${agentsWithoutTasks} agent(s) without tasks`,
290
+ action: '*extend-squad --add task',
291
+ });
292
+ }
293
+
294
+ // Suggest workflows if none exist
295
+ if (inventory.workflows.length === 0 && inventory.tasks.length >= 3) {
296
+ suggestions.push({
297
+ priority: 'medium',
298
+ category: 'workflows',
299
+ message: 'Create workflows to combine related tasks',
300
+ action: '*extend-squad --add workflow',
301
+ });
302
+ }
303
+
304
+ // Suggest checklists if none exist
305
+ if (inventory.checklists.length === 0) {
306
+ suggestions.push({
307
+ priority: 'medium',
308
+ category: 'checklists',
309
+ message: 'Add validation checklists for quality assurance',
310
+ action: '*extend-squad --add checklist',
311
+ });
312
+ }
313
+
314
+ // Suggest config files
315
+ if (coverage.config.percentage < 100) {
316
+ const missing = coverage.config.missing || [];
317
+ if (missing.length > 0) {
318
+ suggestions.push({
319
+ priority: 'low',
320
+ category: 'config',
321
+ message: `Add missing config files: ${missing.join(', ')}`,
322
+ action: 'Create files in config/ directory',
323
+ });
324
+ }
325
+ }
326
+
327
+ // Suggest tools if none exist and agents have complex tasks
328
+ if (inventory.tools.length === 0 && inventory.tasks.length >= 5) {
329
+ suggestions.push({
330
+ priority: 'low',
331
+ category: 'tools',
332
+ message: 'Consider adding custom tools for automation',
333
+ action: '*extend-squad --add tool',
334
+ });
335
+ }
336
+
337
+ // Suggest templates if none exist
338
+ if (inventory.templates.length === 0) {
339
+ suggestions.push({
340
+ priority: 'low',
341
+ category: 'templates',
342
+ message: 'Add document templates for consistent output',
343
+ action: '*extend-squad --add template',
344
+ });
345
+ }
346
+
347
+ return suggestions;
348
+ }
349
+
350
+ /**
351
+ * Format analysis report for output
352
+ * @param {Object} analysis - Complete analysis
353
+ * @param {string} [format='console'] - Output format
354
+ * @returns {string} Formatted report
355
+ */
356
+ formatReport(analysis, format = 'console') {
357
+ if (format === 'json') {
358
+ return JSON.stringify(analysis, null, 2);
359
+ }
360
+
361
+ if (format === 'markdown') {
362
+ return this._formatMarkdown(analysis);
363
+ }
364
+
365
+ return this._formatConsole(analysis);
366
+ }
367
+
368
+ // ============================================
369
+ // Private Helper Methods
370
+ // ============================================
371
+
372
+ /**
373
+ * Check if directory exists
374
+ * @private
375
+ */
376
+ async _directoryExists(dirPath) {
377
+ try {
378
+ const stats = await fs.stat(dirPath);
379
+ return stats.isDirectory();
380
+ } catch {
381
+ return false;
382
+ }
383
+ }
384
+
385
+ /**
386
+ * List files in a directory
387
+ * @private
388
+ */
389
+ async _listFiles(dirPath, verbose = false) {
390
+ try {
391
+ const entries = await fs.readdir(dirPath, { withFileTypes: true });
392
+ const files = entries
393
+ .filter((entry) => entry.isFile() && !entry.name.startsWith('.'))
394
+ .map((entry) => entry.name);
395
+
396
+ if (verbose) {
397
+ return files.map((file) => ({
398
+ name: file,
399
+ path: path.join(dirPath, file),
400
+ }));
401
+ }
402
+
403
+ return files;
404
+ } catch {
405
+ return [];
406
+ }
407
+ }
408
+
409
+ /**
410
+ * Build overview object from manifest
411
+ * @private
412
+ */
413
+ _buildOverview(manifest, squadName) {
414
+ return {
415
+ name: manifest.name || squadName,
416
+ version: manifest.version || '0.0.0',
417
+ author: manifest.author || 'Unknown',
418
+ license: manifest.license || 'MIT',
419
+ description: manifest.description || '',
420
+ aiosMinVersion: manifest.aios?.minVersion || '2.1.0',
421
+ };
422
+ }
423
+
424
+ /**
425
+ * Count agents that have at least one task
426
+ * @private
427
+ */
428
+ _countAgentsWithTasks(inventory) {
429
+ const agentIds = inventory.agents.map((file) => {
430
+ const name = typeof file === 'string' ? file : file.name;
431
+ return name.replace(/\.md$/, '');
432
+ });
433
+
434
+ let count = 0;
435
+ for (const agentId of agentIds) {
436
+ const hasTask = inventory.tasks.some((task) => {
437
+ const taskName = typeof task === 'string' ? task : task.name;
438
+ return taskName.startsWith(agentId + '-');
439
+ });
440
+ if (hasTask) {
441
+ count++;
442
+ }
443
+ }
444
+
445
+ return count;
446
+ }
447
+
448
+ /**
449
+ * Calculate config file coverage
450
+ * @private
451
+ */
452
+ _calculateConfigCoverage(squadPath, inventory) {
453
+ const found = [];
454
+ const missing = [];
455
+
456
+ // Check README
457
+ const hasReadme = inventory.agents.length > 0; // Simplified check
458
+ if (hasReadme) {
459
+ found.push('README.md');
460
+ } else {
461
+ missing.push('README.md');
462
+ }
463
+
464
+ // For now, simplified - just check if config directory has files
465
+ const percentage = found.length > 0 ? 50 : 0;
466
+
467
+ return {
468
+ found,
469
+ missing,
470
+ percentage,
471
+ };
472
+ }
473
+
474
+ /**
475
+ * Format report for console output
476
+ * @private
477
+ */
478
+ _formatConsole(analysis) {
479
+ const { overview, inventory, coverage, suggestions, squadPath } = analysis;
480
+ const lines = [];
481
+
482
+ // Header
483
+ lines.push(`=== Squad Analysis: ${overview.name} ===`);
484
+ lines.push('');
485
+
486
+ // Overview
487
+ lines.push('Overview');
488
+ lines.push(` Name: ${overview.name}`);
489
+ lines.push(` Version: ${overview.version}`);
490
+ lines.push(` Author: ${overview.author}`);
491
+ lines.push(` License: ${overview.license}`);
492
+ lines.push(` AIOS Min Version: ${overview.aiosMinVersion}`);
493
+ if (overview.description) {
494
+ lines.push(` Description: ${overview.description}`);
495
+ }
496
+ lines.push('');
497
+
498
+ // Components
499
+ lines.push('Components');
500
+ for (const dir of COMPONENT_DIRECTORIES) {
501
+ const files = inventory[dir];
502
+ const count = files.length;
503
+ const emptyIndicator = count === 0 ? ' <- Empty' : '';
504
+
505
+ lines.push(` ${dir}/ (${count})${emptyIndicator}`);
506
+
507
+ if (count > 0 && count <= 5) {
508
+ for (const file of files) {
509
+ const fileName = typeof file === 'string' ? file : file.name;
510
+ lines.push(` - ${fileName}`);
511
+ }
512
+ } else if (count > 5) {
513
+ for (let i = 0; i < 3; i++) {
514
+ const file = files[i];
515
+ const fileName = typeof file === 'string' ? file : file.name;
516
+ lines.push(` - ${fileName}`);
517
+ }
518
+ lines.push(` ... and ${count - 3} more`);
519
+ }
520
+ }
521
+ lines.push('');
522
+
523
+ // Coverage
524
+ lines.push('Coverage');
525
+ lines.push(
526
+ ` Agents: ${this._formatBar(coverage.agents.percentage)} ${coverage.agents.percentage}% ` +
527
+ `(${coverage.agents.withTasks}/${coverage.agents.total} with tasks)`
528
+ );
529
+ lines.push(
530
+ ` Tasks: ${this._formatBar(coverage.tasks.percentage)} ${coverage.tasks.percentage}% ` +
531
+ `(${coverage.tasks.total} tasks)`
532
+ );
533
+ lines.push(
534
+ ` Directories: ${this._formatBar(coverage.directories.percentage)} ${coverage.directories.percentage}% ` +
535
+ `(${coverage.directories.populated}/${coverage.directories.total} populated)`
536
+ );
537
+ lines.push(
538
+ ` Config: ${this._formatBar(coverage.config.percentage)} ${coverage.config.percentage}%`
539
+ );
540
+ lines.push('');
541
+
542
+ // Suggestions
543
+ if (suggestions.length > 0) {
544
+ lines.push('Suggestions');
545
+ suggestions.forEach((suggestion, index) => {
546
+ const priorityIcon = suggestion.priority === 'high' ? '!' :
547
+ suggestion.priority === 'medium' ? '*' : '-';
548
+ lines.push(` ${index + 1}. [${priorityIcon}] ${suggestion.message}`);
549
+ });
550
+ lines.push('');
551
+ }
552
+
553
+ // Next steps
554
+ lines.push(`Next: *extend-squad ${overview.name}`);
555
+
556
+ return lines.join('\n');
557
+ }
558
+
559
+ /**
560
+ * Format report as markdown
561
+ * @private
562
+ */
563
+ _formatMarkdown(analysis) {
564
+ const { overview, inventory, coverage, suggestions } = analysis;
565
+ const lines = [];
566
+
567
+ lines.push(`# Squad Analysis: ${overview.name}`);
568
+ lines.push('');
569
+ lines.push(`**Generated:** ${new Date().toISOString()}`);
570
+ lines.push('');
571
+
572
+ lines.push('## Overview');
573
+ lines.push('');
574
+ lines.push('| Property | Value |');
575
+ lines.push('|----------|-------|');
576
+ lines.push(`| Name | ${overview.name} |`);
577
+ lines.push(`| Version | ${overview.version} |`);
578
+ lines.push(`| Author | ${overview.author} |`);
579
+ lines.push(`| License | ${overview.license} |`);
580
+ lines.push(`| AIOS Min Version | ${overview.aiosMinVersion} |`);
581
+ lines.push('');
582
+
583
+ lines.push('## Components');
584
+ lines.push('');
585
+ for (const dir of COMPONENT_DIRECTORIES) {
586
+ const files = inventory[dir];
587
+ lines.push(`### ${dir}/ (${files.length})`);
588
+ if (files.length > 0) {
589
+ files.forEach((file) => {
590
+ const fileName = typeof file === 'string' ? file : file.name;
591
+ lines.push(`- ${fileName}`);
592
+ });
593
+ } else {
594
+ lines.push('*Empty*');
595
+ }
596
+ lines.push('');
597
+ }
598
+
599
+ lines.push('## Coverage');
600
+ lines.push('');
601
+ lines.push('| Category | Percentage | Details |');
602
+ lines.push('|----------|------------|---------|');
603
+ lines.push(`| Agents | ${coverage.agents.percentage}% | ${coverage.agents.withTasks}/${coverage.agents.total} with tasks |`);
604
+ lines.push(`| Tasks | ${coverage.tasks.percentage}% | ${coverage.tasks.total} total |`);
605
+ lines.push(`| Directories | ${coverage.directories.percentage}% | ${coverage.directories.populated}/${coverage.directories.total} populated |`);
606
+ lines.push(`| Config | ${coverage.config.percentage}% | - |`);
607
+ lines.push('');
608
+
609
+ if (suggestions.length > 0) {
610
+ lines.push('## Suggestions');
611
+ lines.push('');
612
+ suggestions.forEach((suggestion, index) => {
613
+ lines.push(`${index + 1}. **[${suggestion.priority.toUpperCase()}]** ${suggestion.message}`);
614
+ });
615
+ lines.push('');
616
+ }
617
+
618
+ return lines.join('\n');
619
+ }
620
+
621
+ /**
622
+ * Format progress bar
623
+ * @private
624
+ */
625
+ _formatBar(percentage) {
626
+ const filled = Math.round(percentage / 10);
627
+ const empty = 10 - filled;
628
+ return '[' + '#'.repeat(filled) + '-'.repeat(empty) + ']';
629
+ }
630
+ }
631
+
632
+ module.exports = {
633
+ SquadAnalyzer,
634
+ SquadAnalyzerError,
635
+ ErrorCodes,
636
+ COMPONENT_DIRECTORIES,
637
+ CONFIG_FILES,
638
+ };