aios-core 3.7.0 → 3.9.1
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 +6 -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/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/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/test-generator.js +8 -8
- package/.aios-core/infrastructure/scripts/test-quality-assessment.js +5 -5
- package/.aios-core/infrastructure/scripts/test-utilities.js +3 -3
- package/.aios-core/install-manifest.yaml +97 -33
- package/.aios-core/quality/metrics-collector.js +27 -0
- package/.aios-core/scripts/session-context-loader.js +13 -254
- package/.aios-core/scripts/test-template-system.js +6 -6
- 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
- package/src/installer/brownfield-upgrader.js +1 -1
- package/bin/aios-init.backup-v1.1.4.js +0 -352
|
@@ -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
|
+
};
|