aios-core 4.2.13 → 4.2.15
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/code-intel/helpers/dev-helper.js +206 -0
- package/.aios-core/core/registry/registry-schema.json +166 -166
- package/.aios-core/core/synapse/diagnostics/collectors/hook-collector.js +3 -3
- package/.aios-core/data/entity-registry.yaml +27 -0
- package/.aios-core/development/scripts/approval-workflow.js +642 -642
- package/.aios-core/development/scripts/backup-manager.js +606 -606
- package/.aios-core/development/scripts/branch-manager.js +389 -389
- package/.aios-core/development/scripts/code-quality-improver.js +1311 -1311
- package/.aios-core/development/scripts/commit-message-generator.js +849 -849
- package/.aios-core/development/scripts/conflict-resolver.js +674 -674
- package/.aios-core/development/scripts/dependency-analyzer.js +637 -637
- package/.aios-core/development/scripts/diff-generator.js +351 -351
- package/.aios-core/development/scripts/elicitation-engine.js +384 -384
- package/.aios-core/development/scripts/elicitation-session-manager.js +299 -299
- package/.aios-core/development/scripts/git-wrapper.js +461 -461
- package/.aios-core/development/scripts/manifest-preview.js +244 -244
- package/.aios-core/development/scripts/metrics-tracker.js +775 -775
- package/.aios-core/development/scripts/modification-validator.js +554 -554
- package/.aios-core/development/scripts/pattern-learner.js +1224 -1224
- package/.aios-core/development/scripts/performance-analyzer.js +757 -757
- package/.aios-core/development/scripts/refactoring-suggester.js +1138 -1138
- package/.aios-core/development/scripts/rollback-handler.js +530 -530
- package/.aios-core/development/scripts/security-checker.js +358 -358
- package/.aios-core/development/scripts/template-engine.js +239 -239
- package/.aios-core/development/scripts/template-validator.js +278 -278
- package/.aios-core/development/scripts/test-generator.js +843 -843
- package/.aios-core/development/scripts/transaction-manager.js +589 -589
- package/.aios-core/development/scripts/usage-tracker.js +673 -673
- package/.aios-core/development/scripts/validate-filenames.js +226 -226
- package/.aios-core/development/scripts/version-tracker.js +526 -526
- package/.aios-core/development/scripts/yaml-validator.js +396 -396
- package/.aios-core/development/tasks/build-autonomous.md +10 -4
- package/.aios-core/development/tasks/create-service.md +23 -0
- package/.aios-core/development/tasks/dev-develop-story.md +12 -6
- package/.aios-core/development/tasks/dev-suggest-refactoring.md +7 -1
- package/.aios-core/development/tasks/publish-npm.md +3 -3
- package/.aios-core/hooks/unified/README.md +1 -1
- package/.aios-core/install-manifest.yaml +65 -61
- package/.aios-core/manifests/schema/manifest-schema.json +190 -190
- package/.aios-core/product/templates/component-react-tmpl.tsx +98 -98
- package/.aios-core/product/templates/engine/schemas/adr.schema.json +102 -102
- package/.aios-core/product/templates/engine/schemas/dbdr.schema.json +205 -205
- package/.aios-core/product/templates/engine/schemas/epic.schema.json +175 -175
- package/.aios-core/product/templates/engine/schemas/pmdr.schema.json +175 -175
- package/.aios-core/product/templates/engine/schemas/prd-v2.schema.json +300 -300
- package/.aios-core/product/templates/engine/schemas/prd.schema.json +152 -152
- package/.aios-core/product/templates/engine/schemas/story.schema.json +222 -222
- package/.aios-core/product/templates/engine/schemas/task.schema.json +154 -154
- package/.aios-core/product/templates/eslintrc-security.json +32 -32
- package/.aios-core/product/templates/github-actions-cd.yml +212 -212
- package/.aios-core/product/templates/github-actions-ci.yml +172 -172
- package/.aios-core/product/templates/shock-report-tmpl.html +502 -502
- package/.aios-core/product/templates/token-exports-css-tmpl.css +240 -240
- package/.aios-core/quality/schemas/quality-metrics.schema.json +233 -233
- package/.aios-core/scripts/migrate-framework-docs.sh +300 -300
- package/README.en.md +747 -0
- package/README.md +4 -2
- package/bin/aios.js +7 -4
- package/package.json +1 -1
- package/packages/aios-pro-cli/src/recover.js +1 -1
- package/packages/installer/src/wizard/ide-config-generator.js +6 -6
- package/packages/installer/src/wizard/pro-setup.js +3 -3
- package/pro/license/degradation.js +220 -220
- package/pro/license/errors.js +450 -450
- package/pro/license/feature-gate.js +354 -354
- package/pro/license/index.js +181 -181
- package/pro/license/license-cache.js +523 -523
- package/pro/license/license-crypto.js +303 -303
- package/scripts/package-synapse.js +5 -5
- package/scripts/validate-package-completeness.js +3 -3
- package/.aios-core/.session/current-session.json +0 -14
- package/.aios-core/data/registry-update-log.jsonl +0 -191
- package/.aios-core/docs/SHARD-TRANSLATION-GUIDE.md +0 -335
- package/.aios-core/docs/component-creation-guide.md +0 -458
- package/.aios-core/docs/session-update-pattern.md +0 -307
- package/.aios-core/docs/standards/AIOS-FRAMEWORK-MASTER.md +0 -1963
- package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.1-SUMMARY.md +0 -1190
- package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.1.md +0 -439
- package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO.md +0 -5398
- package/.aios-core/docs/standards/V3-ARCHITECTURAL-DECISIONS.md +0 -523
- package/.aios-core/docs/template-syntax.md +0 -267
- package/.aios-core/docs/troubleshooting-guide.md +0 -625
- package/.aios-core/infrastructure/tests/utilities-audit-results.json +0 -501
- package/.aios-core/manifests/agents.csv +0 -29
- package/.aios-core/manifests/tasks.csv +0 -198
- package/.aios-core/manifests/workers.csv +0 -204
- package/.claude/rules/agent-authority.md +0 -105
- package/.claude/rules/coderabbit-integration.md +0 -93
- package/.claude/rules/ids-principles.md +0 -112
- package/.claude/rules/story-lifecycle.md +0 -139
- package/.claude/rules/workflow-execution.md +0 -150
- package/scripts/glue/README.md +0 -355
- package/scripts/glue/compose-agent-prompt.cjs +0 -362
- /package/.claude/hooks/{precompact-session-digest.js → precompact-session-digest.cjs} +0 -0
- /package/.claude/hooks/{synapse-engine.js → synapse-engine.cjs} +0 -0
|
@@ -1,674 +1,674 @@
|
|
|
1
|
-
const fs = require('fs').promises;
|
|
2
|
-
const path = require('path');
|
|
3
|
-
const chalk = require('chalk');
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Usage tracker for AIOS-FULLSTACK framework components
|
|
7
|
-
* Tracks component usage patterns for deprecation warnings and impact analysis
|
|
8
|
-
*/
|
|
9
|
-
class UsageTracker {
|
|
10
|
-
constructor(options = {}) {
|
|
11
|
-
this.rootPath = options.rootPath || process.cwd();
|
|
12
|
-
this.usageDir = path.join(this.rootPath, '.aios', 'usage');
|
|
13
|
-
this.usageCache = new Map();
|
|
14
|
-
this.scanPatterns = this.initializeScanPatterns();
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Initialize usage tracker
|
|
19
|
-
*/
|
|
20
|
-
async initialize() {
|
|
21
|
-
try {
|
|
22
|
-
// Create usage directory
|
|
23
|
-
await fs.mkdir(this.usageDir, { recursive: true });
|
|
24
|
-
|
|
25
|
-
console.log(chalk.green('✅ Usage tracker initialized'));
|
|
26
|
-
return true;
|
|
27
|
-
|
|
28
|
-
} catch (_error) {
|
|
29
|
-
console.error(chalk.red(`Failed to initialize usage tracker: ${error.message}`));
|
|
30
|
-
throw error;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Analyze component usage across the codebase
|
|
36
|
-
*/
|
|
37
|
-
async analyzeComponentUsage(componentId, options = {}) {
|
|
38
|
-
const analysis = {
|
|
39
|
-
component_id: componentId,
|
|
40
|
-
analysis_timestamp: new Date().toISOString(),
|
|
41
|
-
total_references: 0,
|
|
42
|
-
usage_locations: [],
|
|
43
|
-
dependent_components: [],
|
|
44
|
-
external_references: [],
|
|
45
|
-
usage_patterns: {},
|
|
46
|
-
risk_assessment: {
|
|
47
|
-
impact_level: 'low',
|
|
48
|
-
affected_areas: [],
|
|
49
|
-
migration_complexity: 'simple'
|
|
50
|
-
}
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
try {
|
|
54
|
-
console.log(chalk.blue(`🔍 Analyzing usage for component: ${componentId}`));
|
|
55
|
-
|
|
56
|
-
// Get component information
|
|
57
|
-
const component = await this.getComponentInfo(componentId);
|
|
58
|
-
if (!component) {
|
|
59
|
-
throw new Error(`Component not found: ${componentId}`);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// Scan for direct references
|
|
63
|
-
const directReferences = await this.scanForDirectReferences(component, options);
|
|
64
|
-
analysis.usage_locations = directReferences;
|
|
65
|
-
analysis.total_references = directReferences.length;
|
|
66
|
-
|
|
67
|
-
// Analyze dependency relationships
|
|
68
|
-
const dependencies = await this.analyzeDependencyRelationships(component, options);
|
|
69
|
-
analysis.dependent_components = dependencies;
|
|
70
|
-
|
|
71
|
-
// Check for external references (imports, configurations, etc.)
|
|
72
|
-
const externalRefs = await this.scanForExternalReferences(component, options);
|
|
73
|
-
analysis.external_references = externalRefs;
|
|
74
|
-
|
|
75
|
-
// Analyze usage patterns
|
|
76
|
-
analysis.usage_patterns = await this.analyzeUsagePatterns(component, directReferences);
|
|
77
|
-
|
|
78
|
-
// Assess migration risk
|
|
79
|
-
analysis.risk_assessment = this.assessMigrationRisk(analysis);
|
|
80
|
-
|
|
81
|
-
// Cache results
|
|
82
|
-
this.usageCache.set(componentId, analysis);
|
|
83
|
-
|
|
84
|
-
// Save detailed analysis
|
|
85
|
-
await this.saveUsageAnalysis(analysis);
|
|
86
|
-
|
|
87
|
-
console.log(chalk.green(`✅ Usage analysis completed for ${componentId}`));
|
|
88
|
-
console.log(chalk.gray(` Total references: ${analysis.total_references}`));
|
|
89
|
-
console.log(chalk.gray(` Dependent components: ${analysis.dependent_components.length}`));
|
|
90
|
-
console.log(chalk.gray(` Impact level: ${analysis.risk_assessment.impact_level}`));
|
|
91
|
-
|
|
92
|
-
return analysis;
|
|
93
|
-
|
|
94
|
-
} catch (_error) {
|
|
95
|
-
console.error(chalk.red(`Usage analysis failed for ${componentId}: ${error.message}`));
|
|
96
|
-
throw error;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Track usage changes over time for deprecated components
|
|
102
|
-
*/
|
|
103
|
-
async trackUsageChanges(componentId, baselineAnalysis) {
|
|
104
|
-
const currentAnalysis = await this.analyzeComponentUsage(componentId);
|
|
105
|
-
|
|
106
|
-
const changes = {
|
|
107
|
-
component_id: componentId,
|
|
108
|
-
comparison_timestamp: new Date().toISOString(),
|
|
109
|
-
baseline_timestamp: baselineAnalysis.analysis_timestamp,
|
|
110
|
-
changes: {
|
|
111
|
-
total_references: {
|
|
112
|
-
before: baselineAnalysis.total_references,
|
|
113
|
-
after: currentAnalysis.total_references,
|
|
114
|
-
change: currentAnalysis.total_references - baselineAnalysis.total_references
|
|
115
|
-
},
|
|
116
|
-
dependent_components: {
|
|
117
|
-
before: baselineAnalysis.dependent_components.length,
|
|
118
|
-
after: currentAnalysis.dependent_components.length,
|
|
119
|
-
change: currentAnalysis.dependent_components.length - baselineAnalysis.dependent_components.length
|
|
120
|
-
},
|
|
121
|
-
new_usages: this.findNewUsages(baselineAnalysis.usage_locations, currentAnalysis.usage_locations),
|
|
122
|
-
removed_usages: this.findRemovedUsages(baselineAnalysis.usage_locations, currentAnalysis.usage_locations)
|
|
123
|
-
},
|
|
124
|
-
trend: this.calculateUsageTrend(baselineAnalysis, currentAnalysis),
|
|
125
|
-
migration_progress: this.calculateMigrationProgress(baselineAnalysis, currentAnalysis)
|
|
126
|
-
};
|
|
127
|
-
|
|
128
|
-
// Save change tracking
|
|
129
|
-
await this.saveUsageChanges(changes);
|
|
130
|
-
|
|
131
|
-
return changes;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Generate usage warnings for deprecated components
|
|
136
|
-
*/
|
|
137
|
-
async generateUsageWarnings(componentId, deprecationInfo) {
|
|
138
|
-
const analysis = await this.analyzeComponentUsage(componentId);
|
|
139
|
-
const warnings = [];
|
|
140
|
-
|
|
141
|
-
for (const _usage of analysis.usage_locations) {
|
|
142
|
-
const warning = {
|
|
143
|
-
type: 'deprecation_warning',
|
|
144
|
-
component_id: componentId,
|
|
145
|
-
usage_location: {
|
|
146
|
-
file: usage.file,
|
|
147
|
-
line: usage.line,
|
|
148
|
-
context: usage.context
|
|
149
|
-
},
|
|
150
|
-
message: this.generateWarningMessage(componentId, deprecationInfo, usage),
|
|
151
|
-
severity: this.calculateWarningSeverity(deprecationInfo, usage),
|
|
152
|
-
suggested_actions: this.generateSuggestedActions(componentId, deprecationInfo, usage)
|
|
153
|
-
};
|
|
154
|
-
|
|
155
|
-
warnings.push(warning);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// Save warnings for later processing
|
|
159
|
-
await this.saveUsageWarnings(componentId, warnings);
|
|
160
|
-
|
|
161
|
-
return warnings;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Get usage statistics for reporting
|
|
166
|
-
*/
|
|
167
|
-
async getUsageStatistics(componentIds = null) {
|
|
168
|
-
const stats = {
|
|
169
|
-
generated_at: new Date().toISOString(),
|
|
170
|
-
total_components_analyzed: 0,
|
|
171
|
-
high_usage_components: [],
|
|
172
|
-
zero_usage_components: [],
|
|
173
|
-
usage_distribution: {},
|
|
174
|
-
dependency_graph: {}
|
|
175
|
-
};
|
|
176
|
-
|
|
177
|
-
const componentsToAnalyze = componentIds || await this.getAllTrackedComponents();
|
|
178
|
-
|
|
179
|
-
for (const componentId of componentsToAnalyze) {
|
|
180
|
-
try {
|
|
181
|
-
const analysis = await this.getOrAnalyzeUsage(componentId);
|
|
182
|
-
stats.total_components_analyzed++;
|
|
183
|
-
|
|
184
|
-
// Categorize by usage level
|
|
185
|
-
if (analysis.total_references === 0) {
|
|
186
|
-
stats.zero_usage_components.push(componentId);
|
|
187
|
-
} else if (analysis.total_references >= 10) {
|
|
188
|
-
stats.high_usage_components.push({
|
|
189
|
-
component_id: componentId,
|
|
190
|
-
reference_count: analysis.total_references,
|
|
191
|
-
impact_level: analysis.risk_assessment.impact_level
|
|
192
|
-
});
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
// Add to usage distribution
|
|
196
|
-
const usageRange = this.categorizeUsageLevel(analysis.total_references);
|
|
197
|
-
stats.usage_distribution[usageRange] = (stats.usage_distribution[usageRange] || 0) + 1;
|
|
198
|
-
|
|
199
|
-
// Add to dependency graph
|
|
200
|
-
stats.dependency_graph[componentId] = analysis.dependent_components.map(dep => dep.component_id);
|
|
201
|
-
|
|
202
|
-
} catch (_error) {
|
|
203
|
-
console.warn(chalk.yellow(`Failed to analyze usage for ${componentId}: ${error.message}`));
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
return stats;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
/**
|
|
211
|
-
* Scan for direct references to a component
|
|
212
|
-
*/
|
|
213
|
-
async scanForDirectReferences(component, options = {}) {
|
|
214
|
-
const references = [];
|
|
215
|
-
const scanPaths = await this.getScanPaths(_options);
|
|
216
|
-
|
|
217
|
-
for (const scanPath of scanPaths) {
|
|
218
|
-
try {
|
|
219
|
-
const pathReferences = await this.scanPath(scanPath, component);
|
|
220
|
-
references.push(...pathReferences);
|
|
221
|
-
} catch (_error) {
|
|
222
|
-
console.warn(chalk.yellow(`Failed to scan path ${scanPath}: ${error.message}`));
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
return references;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
/**
|
|
230
|
-
* Scan a specific path for component references
|
|
231
|
-
*/
|
|
232
|
-
async scanPath(scanPath, component) {
|
|
233
|
-
const references = [];
|
|
234
|
-
const files = await this.getFilesToScan(scanPath);
|
|
235
|
-
|
|
236
|
-
for (const file of files) {
|
|
237
|
-
try {
|
|
238
|
-
const fileReferences = await this.scanFile(file, component);
|
|
239
|
-
references.push(...fileReferences);
|
|
240
|
-
} catch (_error) {
|
|
241
|
-
// Skip files that can't be read
|
|
242
|
-
continue;
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
return references;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
/**
|
|
250
|
-
* Scan a single file for component references
|
|
251
|
-
*/
|
|
252
|
-
async scanFile(filePath, component) {
|
|
253
|
-
const references = [];
|
|
254
|
-
|
|
255
|
-
try {
|
|
256
|
-
const content = await fs.readFile(filePath, 'utf-8');
|
|
257
|
-
const lines = content.split('\n');
|
|
258
|
-
|
|
259
|
-
for (let i = 0; i < lines.length; i++) {
|
|
260
|
-
const line = lines[i];
|
|
261
|
-
const lineNumber = i + 1;
|
|
262
|
-
|
|
263
|
-
// Check for various reference patterns
|
|
264
|
-
const matches = this.findReferencesInLine(line, component);
|
|
265
|
-
|
|
266
|
-
for (const match of matches) {
|
|
267
|
-
references.push({
|
|
268
|
-
file: filePath,
|
|
269
|
-
line: lineNumber,
|
|
270
|
-
column: match.column,
|
|
271
|
-
context: line.trim(),
|
|
272
|
-
reference_type: match.type,
|
|
273
|
-
match_text: match.text
|
|
274
|
-
});
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
} catch (_error) {
|
|
278
|
-
// File couldn't be read, skip
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
return references;
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
/**
|
|
285
|
-
* Find references in a single line of code
|
|
286
|
-
*/
|
|
287
|
-
findReferencesInLine(line, component) {
|
|
288
|
-
const matches = [];
|
|
289
|
-
const patterns = this.scanPatterns[component.type] || this.scanPatterns.default;
|
|
290
|
-
|
|
291
|
-
for (const pattern of patterns) {
|
|
292
|
-
const regex = new RegExp(pattern.pattern.replace('{component_name}', component.name), pattern.flags || 'gi');
|
|
293
|
-
let match;
|
|
294
|
-
|
|
295
|
-
while ((match = regex.exec(line)) !== null) {
|
|
296
|
-
matches.push({
|
|
297
|
-
type: pattern.type,
|
|
298
|
-
text: match[0],
|
|
299
|
-
column: match.index,
|
|
300
|
-
confidence: pattern.confidence || 0.8
|
|
301
|
-
});
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
return matches;
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
/**
|
|
309
|
-
* Analyze dependency relationships
|
|
310
|
-
*/
|
|
311
|
-
async analyzeDependencyRelationships(component, options = {}) {
|
|
312
|
-
const dependencies = [];
|
|
313
|
-
|
|
314
|
-
// This would analyze manifest files, import statements, etc.
|
|
315
|
-
// For now, return empty array as placeholder
|
|
316
|
-
|
|
317
|
-
return dependencies;
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
/**
|
|
321
|
-
* Scan for external references (config files, documentation, etc.)
|
|
322
|
-
*/
|
|
323
|
-
async scanForExternalReferences(component, options = {}) {
|
|
324
|
-
const externalRefs = [];
|
|
325
|
-
|
|
326
|
-
// Check configuration files
|
|
327
|
-
const configRefs = await this.scanConfigurationFiles(component);
|
|
328
|
-
externalRefs.push(...configRefs);
|
|
329
|
-
|
|
330
|
-
// Check documentation
|
|
331
|
-
const docRefs = await this.scanDocumentationFiles(component);
|
|
332
|
-
externalRefs.push(...docRefs);
|
|
333
|
-
|
|
334
|
-
return externalRefs;
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
/**
|
|
338
|
-
* Analyze usage patterns
|
|
339
|
-
*/
|
|
340
|
-
async analyzeUsagePatterns(component, references) {
|
|
341
|
-
const patterns = {
|
|
342
|
-
by_file_type: {},
|
|
343
|
-
by_usage_type: {},
|
|
344
|
-
temporal_distribution: {},
|
|
345
|
-
complexity_indicators: {}
|
|
346
|
-
};
|
|
347
|
-
|
|
348
|
-
for (const ref of references) {
|
|
349
|
-
// Group by file extension
|
|
350
|
-
const ext = path.extname(ref.file);
|
|
351
|
-
patterns.by_file_type[ext] = (patterns.by_file_type[ext] || 0) + 1;
|
|
352
|
-
|
|
353
|
-
// Group by reference type
|
|
354
|
-
patterns.by_usage_type[ref.reference_type] = (patterns.by_usage_type[ref.reference_type] || 0) + 1;
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
return patterns;
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
/**
|
|
361
|
-
* Assess migration risk based on usage analysis
|
|
362
|
-
*/
|
|
363
|
-
assessMigrationRisk(analysis) {
|
|
364
|
-
let impactLevel = 'low';
|
|
365
|
-
let migrationComplexity = 'simple';
|
|
366
|
-
const affectedAreas = [];
|
|
367
|
-
|
|
368
|
-
// Assess based on total references
|
|
369
|
-
if (analysis.total_references > 20) {
|
|
370
|
-
impactLevel = 'high';
|
|
371
|
-
migrationComplexity = 'complex';
|
|
372
|
-
} else if (analysis.total_references > 10) {
|
|
373
|
-
impactLevel = 'medium';
|
|
374
|
-
migrationComplexity = 'moderate';
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
// Check for complex usage patterns
|
|
378
|
-
if (analysis.dependent_components.length > 5) {
|
|
379
|
-
migrationComplexity = 'complex';
|
|
380
|
-
affectedAreas.push('component_dependencies');
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
if (analysis.external_references.length > 0) {
|
|
384
|
-
affectedAreas.push('external_configuration');
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
return {
|
|
388
|
-
impact_level: impactLevel,
|
|
389
|
-
affected_areas: affectedAreas,
|
|
390
|
-
migration_complexity: migrationComplexity
|
|
391
|
-
};
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
// Helper methods
|
|
395
|
-
|
|
396
|
-
async getComponentInfo(componentId) {
|
|
397
|
-
// This would typically integrate with the component registry
|
|
398
|
-
// For now, return a basic component structure
|
|
399
|
-
const [type, name] = componentId.split('/');
|
|
400
|
-
|
|
401
|
-
return {
|
|
402
|
-
id: componentId,
|
|
403
|
-
type: type,
|
|
404
|
-
name: name,
|
|
405
|
-
file_path: `aios-core/${type}s/${name}.md`
|
|
406
|
-
};
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
async getScanPaths(_options) {
|
|
410
|
-
const defaultPaths = [
|
|
411
|
-
path.join(this.rootPath, 'aios-core'),
|
|
412
|
-
path.join(this.rootPath, 'src'),
|
|
413
|
-
path.join(this.rootPath, 'lib')
|
|
414
|
-
];
|
|
415
|
-
|
|
416
|
-
if (options.scanPaths) {
|
|
417
|
-
return options.scanPaths;
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
// Filter paths that exist
|
|
421
|
-
const existingPaths = [];
|
|
422
|
-
for (const scanPath of defaultPaths) {
|
|
423
|
-
try {
|
|
424
|
-
await fs.access(scanPath);
|
|
425
|
-
existingPaths.push(scanPath);
|
|
426
|
-
} catch (_error) {
|
|
427
|
-
// Path doesn't exist, skip
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
return existingPaths;
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
async getFilesToScan(scanPath) {
|
|
435
|
-
const files = [];
|
|
436
|
-
|
|
437
|
-
try {
|
|
438
|
-
const entries = await fs.readdir(scanPath, { withFileTypes: true });
|
|
439
|
-
|
|
440
|
-
for (const entry of entries) {
|
|
441
|
-
const fullPath = path.join(scanPath, entry.name);
|
|
442
|
-
|
|
443
|
-
if (entry.isDirectory()) {
|
|
444
|
-
// Recursively scan subdirectories
|
|
445
|
-
const subFiles = await this.getFilesToScan(fullPath);
|
|
446
|
-
files.push(...subFiles);
|
|
447
|
-
} else if (this.shouldScanFile(entry.name)) {
|
|
448
|
-
files.push(fullPath);
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
} catch (_error) {
|
|
452
|
-
// Can't read directory, skip
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
return files;
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
shouldScanFile(filename) {
|
|
459
|
-
const scanExtensions = ['.js', '.ts', '.md', '.yaml', '.yml', '.json'];
|
|
460
|
-
const ext = path.extname(filename);
|
|
461
|
-
return scanExtensions.includes(ext);
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
async scanConfigurationFiles(component) {
|
|
465
|
-
const configRefs = [];
|
|
466
|
-
const configFiles = [
|
|
467
|
-
'package.json',
|
|
468
|
-
'.aiosrc',
|
|
469
|
-
'aios.config.js',
|
|
470
|
-
'manifest.yaml'
|
|
471
|
-
];
|
|
472
|
-
|
|
473
|
-
for (const configFile of configFiles) {
|
|
474
|
-
const configPath = path.join(this.rootPath, configFile);
|
|
475
|
-
|
|
476
|
-
try {
|
|
477
|
-
const refs = await this.scanFile(configPath, component);
|
|
478
|
-
configRefs.push(...refs.map(ref => ({ ...ref, source: 'configuration' })));
|
|
479
|
-
} catch (_error) {
|
|
480
|
-
// Config file doesn't exist or can't be read
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
return configRefs;
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
async scanDocumentationFiles(component) {
|
|
488
|
-
const docRefs = [];
|
|
489
|
-
const docsPath = path.join(this.rootPath, 'docs');
|
|
490
|
-
|
|
491
|
-
try {
|
|
492
|
-
const files = await this.getFilesToScan(docsPath);
|
|
493
|
-
|
|
494
|
-
for (const file of files) {
|
|
495
|
-
const refs = await this.scanFile(file, component);
|
|
496
|
-
docRefs.push(...refs.map(ref => ({ ...ref, source: 'documentation' })));
|
|
497
|
-
}
|
|
498
|
-
} catch (_error) {
|
|
499
|
-
// Docs directory doesn't exist
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
return docRefs;
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
findNewUsages(oldUsages, newUsages) {
|
|
506
|
-
return newUsages.filter(newUsage =>
|
|
507
|
-
!oldUsages.some(oldUsage =>
|
|
508
|
-
oldUsage.file === newUsage.file && oldUsage.line === newUsage.line
|
|
509
|
-
)
|
|
510
|
-
);
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
findRemovedUsages(oldUsages, newUsages) {
|
|
514
|
-
return oldUsages.filter(oldUsage =>
|
|
515
|
-
!newUsages.some(newUsage =>
|
|
516
|
-
newUsage.file === oldUsage.file && newUsage.line === oldUsage.line
|
|
517
|
-
)
|
|
518
|
-
);
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
calculateUsageTrend(baseline, current) {
|
|
522
|
-
const referenceChange = current.total_references - baseline.total_references;
|
|
523
|
-
|
|
524
|
-
if (referenceChange > 0) return 'increasing';
|
|
525
|
-
if (referenceChange < 0) return 'decreasing';
|
|
526
|
-
return 'stable';
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
calculateMigrationProgress(baseline, current) {
|
|
530
|
-
if (baseline.total_references === 0) return 1.0;
|
|
531
|
-
|
|
532
|
-
const remainingUsages = current.total_references;
|
|
533
|
-
const originalUsages = baseline.total_references;
|
|
534
|
-
|
|
535
|
-
return Math.max(0, (originalUsages - remainingUsages) / originalUsages);
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
generateWarningMessage(componentId, deprecationInfo, usage) {
|
|
539
|
-
let message = `DEPRECATED: ${componentId} is deprecated`;
|
|
540
|
-
|
|
541
|
-
if (deprecationInfo.replacement) {
|
|
542
|
-
message += ` - use ${deprecationInfo.replacement} instead`;
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
if (deprecationInfo.removalVersion) {
|
|
546
|
-
message += ` (will be removed in ${deprecationInfo.removalVersion})`;
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
return message;
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
calculateWarningSeverity(deprecationInfo, usage) {
|
|
553
|
-
if (deprecationInfo.severity === 'critical') return 'error';
|
|
554
|
-
if (deprecationInfo.severity === 'high') return 'warning';
|
|
555
|
-
return 'info';
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
generateSuggestedActions(componentId, deprecationInfo, usage) {
|
|
559
|
-
const actions = [];
|
|
560
|
-
|
|
561
|
-
if (deprecationInfo.replacement) {
|
|
562
|
-
actions.push(`Replace with ${deprecationInfo.replacement}`);
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
if (deprecationInfo.migrationGuide) {
|
|
566
|
-
actions.push(`See migration guide: ${deprecationInfo.migrationGuide}`);
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
actions.push('Update imports and references');
|
|
570
|
-
actions.push('Test functionality after replacement');
|
|
571
|
-
|
|
572
|
-
return actions;
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
categorizeUsageLevel(referenceCount) {
|
|
576
|
-
if (referenceCount === 0) return 'zero';
|
|
577
|
-
if (referenceCount <= 5) return 'low';
|
|
578
|
-
if (referenceCount <= 15) return 'medium';
|
|
579
|
-
if (referenceCount <= 30) return 'high';
|
|
580
|
-
return 'very_high';
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
async getOrAnalyzeUsage(componentId) {
|
|
584
|
-
if (this.usageCache.has(componentId)) {
|
|
585
|
-
return this.usageCache.get(componentId);
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
return await this.analyzeComponentUsage(componentId);
|
|
589
|
-
}
|
|
590
|
-
|
|
591
|
-
async getAllTrackedComponents() {
|
|
592
|
-
// This would typically come from a component registry
|
|
593
|
-
// For now, return empty array
|
|
594
|
-
return [];
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
async saveUsageAnalysis(analysis) {
|
|
598
|
-
const filename = `usage-${analysis.component_id.replace('/', '-')}-${Date.now()}.json`;
|
|
599
|
-
const filePath = path.join(this.usageDir, filename);
|
|
600
|
-
|
|
601
|
-
await fs.writeFile(filePath, JSON.stringify(analysis, null, 2));
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
async saveUsageChanges(changes) {
|
|
605
|
-
const filename = `changes-${changes.component_id.replace('/', '-')}-${Date.now()}.json`;
|
|
606
|
-
const filePath = path.join(this.usageDir, filename);
|
|
607
|
-
|
|
608
|
-
await fs.writeFile(filePath, JSON.stringify(changes, null, 2));
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
async saveUsageWarnings(componentId, warnings) {
|
|
612
|
-
const filename = `warnings-${componentId.replace('/', '-')}-${Date.now()}.json`;
|
|
613
|
-
const filePath = path.join(this.usageDir, filename);
|
|
614
|
-
|
|
615
|
-
await fs.writeFile(filePath, JSON.stringify(warnings, null, 2));
|
|
616
|
-
}
|
|
617
|
-
|
|
618
|
-
initializeScanPatterns() {
|
|
619
|
-
return {
|
|
620
|
-
agent: [
|
|
621
|
-
{
|
|
622
|
-
pattern: 'agent:\\s*{component_name}',
|
|
623
|
-
type: 'yaml_reference',
|
|
624
|
-
confidence: 0.9
|
|
625
|
-
},
|
|
626
|
-
{
|
|
627
|
-
pattern: 'use\\s+{component_name}',
|
|
628
|
-
type: 'usage_statement',
|
|
629
|
-
confidence: 0.8
|
|
630
|
-
}
|
|
631
|
-
],
|
|
632
|
-
task: [
|
|
633
|
-
{
|
|
634
|
-
pattern: '\\*{component_name}',
|
|
635
|
-
type: 'task_invocation',
|
|
636
|
-
confidence: 0.9
|
|
637
|
-
},
|
|
638
|
-
{
|
|
639
|
-
pattern: 'require\\(.*{component_name}.*\\)',
|
|
640
|
-
type: 'import_statement',
|
|
641
|
-
confidence: 0.8
|
|
642
|
-
}
|
|
643
|
-
],
|
|
644
|
-
workflow: [
|
|
645
|
-
{
|
|
646
|
-
pattern: 'workflow:\\s*{component_name}',
|
|
647
|
-
type: 'workflow_reference',
|
|
648
|
-
confidence: 0.9
|
|
649
|
-
}
|
|
650
|
-
],
|
|
651
|
-
util: [
|
|
652
|
-
{
|
|
653
|
-
pattern: 'require\\(.*{component_name}.*\\)',
|
|
654
|
-
type: 'import_statement',
|
|
655
|
-
confidence: 0.9
|
|
656
|
-
},
|
|
657
|
-
{
|
|
658
|
-
pattern: 'import.*{component_name}',
|
|
659
|
-
type: 'import_statement',
|
|
660
|
-
confidence: 0.9
|
|
661
|
-
}
|
|
662
|
-
],
|
|
663
|
-
default: [
|
|
664
|
-
{
|
|
665
|
-
pattern: '{component_name}',
|
|
666
|
-
type: 'general_reference',
|
|
667
|
-
confidence: 0.6
|
|
668
|
-
}
|
|
669
|
-
]
|
|
670
|
-
};
|
|
671
|
-
}
|
|
672
|
-
}
|
|
673
|
-
|
|
1
|
+
const fs = require('fs').promises;
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const chalk = require('chalk');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Usage tracker for AIOS-FULLSTACK framework components
|
|
7
|
+
* Tracks component usage patterns for deprecation warnings and impact analysis
|
|
8
|
+
*/
|
|
9
|
+
class UsageTracker {
|
|
10
|
+
constructor(options = {}) {
|
|
11
|
+
this.rootPath = options.rootPath || process.cwd();
|
|
12
|
+
this.usageDir = path.join(this.rootPath, '.aios', 'usage');
|
|
13
|
+
this.usageCache = new Map();
|
|
14
|
+
this.scanPatterns = this.initializeScanPatterns();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Initialize usage tracker
|
|
19
|
+
*/
|
|
20
|
+
async initialize() {
|
|
21
|
+
try {
|
|
22
|
+
// Create usage directory
|
|
23
|
+
await fs.mkdir(this.usageDir, { recursive: true });
|
|
24
|
+
|
|
25
|
+
console.log(chalk.green('✅ Usage tracker initialized'));
|
|
26
|
+
return true;
|
|
27
|
+
|
|
28
|
+
} catch (_error) {
|
|
29
|
+
console.error(chalk.red(`Failed to initialize usage tracker: ${error.message}`));
|
|
30
|
+
throw error;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Analyze component usage across the codebase
|
|
36
|
+
*/
|
|
37
|
+
async analyzeComponentUsage(componentId, options = {}) {
|
|
38
|
+
const analysis = {
|
|
39
|
+
component_id: componentId,
|
|
40
|
+
analysis_timestamp: new Date().toISOString(),
|
|
41
|
+
total_references: 0,
|
|
42
|
+
usage_locations: [],
|
|
43
|
+
dependent_components: [],
|
|
44
|
+
external_references: [],
|
|
45
|
+
usage_patterns: {},
|
|
46
|
+
risk_assessment: {
|
|
47
|
+
impact_level: 'low',
|
|
48
|
+
affected_areas: [],
|
|
49
|
+
migration_complexity: 'simple'
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
try {
|
|
54
|
+
console.log(chalk.blue(`🔍 Analyzing usage for component: ${componentId}`));
|
|
55
|
+
|
|
56
|
+
// Get component information
|
|
57
|
+
const component = await this.getComponentInfo(componentId);
|
|
58
|
+
if (!component) {
|
|
59
|
+
throw new Error(`Component not found: ${componentId}`);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Scan for direct references
|
|
63
|
+
const directReferences = await this.scanForDirectReferences(component, options);
|
|
64
|
+
analysis.usage_locations = directReferences;
|
|
65
|
+
analysis.total_references = directReferences.length;
|
|
66
|
+
|
|
67
|
+
// Analyze dependency relationships
|
|
68
|
+
const dependencies = await this.analyzeDependencyRelationships(component, options);
|
|
69
|
+
analysis.dependent_components = dependencies;
|
|
70
|
+
|
|
71
|
+
// Check for external references (imports, configurations, etc.)
|
|
72
|
+
const externalRefs = await this.scanForExternalReferences(component, options);
|
|
73
|
+
analysis.external_references = externalRefs;
|
|
74
|
+
|
|
75
|
+
// Analyze usage patterns
|
|
76
|
+
analysis.usage_patterns = await this.analyzeUsagePatterns(component, directReferences);
|
|
77
|
+
|
|
78
|
+
// Assess migration risk
|
|
79
|
+
analysis.risk_assessment = this.assessMigrationRisk(analysis);
|
|
80
|
+
|
|
81
|
+
// Cache results
|
|
82
|
+
this.usageCache.set(componentId, analysis);
|
|
83
|
+
|
|
84
|
+
// Save detailed analysis
|
|
85
|
+
await this.saveUsageAnalysis(analysis);
|
|
86
|
+
|
|
87
|
+
console.log(chalk.green(`✅ Usage analysis completed for ${componentId}`));
|
|
88
|
+
console.log(chalk.gray(` Total references: ${analysis.total_references}`));
|
|
89
|
+
console.log(chalk.gray(` Dependent components: ${analysis.dependent_components.length}`));
|
|
90
|
+
console.log(chalk.gray(` Impact level: ${analysis.risk_assessment.impact_level}`));
|
|
91
|
+
|
|
92
|
+
return analysis;
|
|
93
|
+
|
|
94
|
+
} catch (_error) {
|
|
95
|
+
console.error(chalk.red(`Usage analysis failed for ${componentId}: ${error.message}`));
|
|
96
|
+
throw error;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Track usage changes over time for deprecated components
|
|
102
|
+
*/
|
|
103
|
+
async trackUsageChanges(componentId, baselineAnalysis) {
|
|
104
|
+
const currentAnalysis = await this.analyzeComponentUsage(componentId);
|
|
105
|
+
|
|
106
|
+
const changes = {
|
|
107
|
+
component_id: componentId,
|
|
108
|
+
comparison_timestamp: new Date().toISOString(),
|
|
109
|
+
baseline_timestamp: baselineAnalysis.analysis_timestamp,
|
|
110
|
+
changes: {
|
|
111
|
+
total_references: {
|
|
112
|
+
before: baselineAnalysis.total_references,
|
|
113
|
+
after: currentAnalysis.total_references,
|
|
114
|
+
change: currentAnalysis.total_references - baselineAnalysis.total_references
|
|
115
|
+
},
|
|
116
|
+
dependent_components: {
|
|
117
|
+
before: baselineAnalysis.dependent_components.length,
|
|
118
|
+
after: currentAnalysis.dependent_components.length,
|
|
119
|
+
change: currentAnalysis.dependent_components.length - baselineAnalysis.dependent_components.length
|
|
120
|
+
},
|
|
121
|
+
new_usages: this.findNewUsages(baselineAnalysis.usage_locations, currentAnalysis.usage_locations),
|
|
122
|
+
removed_usages: this.findRemovedUsages(baselineAnalysis.usage_locations, currentAnalysis.usage_locations)
|
|
123
|
+
},
|
|
124
|
+
trend: this.calculateUsageTrend(baselineAnalysis, currentAnalysis),
|
|
125
|
+
migration_progress: this.calculateMigrationProgress(baselineAnalysis, currentAnalysis)
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
// Save change tracking
|
|
129
|
+
await this.saveUsageChanges(changes);
|
|
130
|
+
|
|
131
|
+
return changes;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Generate usage warnings for deprecated components
|
|
136
|
+
*/
|
|
137
|
+
async generateUsageWarnings(componentId, deprecationInfo) {
|
|
138
|
+
const analysis = await this.analyzeComponentUsage(componentId);
|
|
139
|
+
const warnings = [];
|
|
140
|
+
|
|
141
|
+
for (const _usage of analysis.usage_locations) {
|
|
142
|
+
const warning = {
|
|
143
|
+
type: 'deprecation_warning',
|
|
144
|
+
component_id: componentId,
|
|
145
|
+
usage_location: {
|
|
146
|
+
file: usage.file,
|
|
147
|
+
line: usage.line,
|
|
148
|
+
context: usage.context
|
|
149
|
+
},
|
|
150
|
+
message: this.generateWarningMessage(componentId, deprecationInfo, usage),
|
|
151
|
+
severity: this.calculateWarningSeverity(deprecationInfo, usage),
|
|
152
|
+
suggested_actions: this.generateSuggestedActions(componentId, deprecationInfo, usage)
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
warnings.push(warning);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Save warnings for later processing
|
|
159
|
+
await this.saveUsageWarnings(componentId, warnings);
|
|
160
|
+
|
|
161
|
+
return warnings;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Get usage statistics for reporting
|
|
166
|
+
*/
|
|
167
|
+
async getUsageStatistics(componentIds = null) {
|
|
168
|
+
const stats = {
|
|
169
|
+
generated_at: new Date().toISOString(),
|
|
170
|
+
total_components_analyzed: 0,
|
|
171
|
+
high_usage_components: [],
|
|
172
|
+
zero_usage_components: [],
|
|
173
|
+
usage_distribution: {},
|
|
174
|
+
dependency_graph: {}
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
const componentsToAnalyze = componentIds || await this.getAllTrackedComponents();
|
|
178
|
+
|
|
179
|
+
for (const componentId of componentsToAnalyze) {
|
|
180
|
+
try {
|
|
181
|
+
const analysis = await this.getOrAnalyzeUsage(componentId);
|
|
182
|
+
stats.total_components_analyzed++;
|
|
183
|
+
|
|
184
|
+
// Categorize by usage level
|
|
185
|
+
if (analysis.total_references === 0) {
|
|
186
|
+
stats.zero_usage_components.push(componentId);
|
|
187
|
+
} else if (analysis.total_references >= 10) {
|
|
188
|
+
stats.high_usage_components.push({
|
|
189
|
+
component_id: componentId,
|
|
190
|
+
reference_count: analysis.total_references,
|
|
191
|
+
impact_level: analysis.risk_assessment.impact_level
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Add to usage distribution
|
|
196
|
+
const usageRange = this.categorizeUsageLevel(analysis.total_references);
|
|
197
|
+
stats.usage_distribution[usageRange] = (stats.usage_distribution[usageRange] || 0) + 1;
|
|
198
|
+
|
|
199
|
+
// Add to dependency graph
|
|
200
|
+
stats.dependency_graph[componentId] = analysis.dependent_components.map(dep => dep.component_id);
|
|
201
|
+
|
|
202
|
+
} catch (_error) {
|
|
203
|
+
console.warn(chalk.yellow(`Failed to analyze usage for ${componentId}: ${error.message}`));
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return stats;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Scan for direct references to a component
|
|
212
|
+
*/
|
|
213
|
+
async scanForDirectReferences(component, options = {}) {
|
|
214
|
+
const references = [];
|
|
215
|
+
const scanPaths = await this.getScanPaths(_options);
|
|
216
|
+
|
|
217
|
+
for (const scanPath of scanPaths) {
|
|
218
|
+
try {
|
|
219
|
+
const pathReferences = await this.scanPath(scanPath, component);
|
|
220
|
+
references.push(...pathReferences);
|
|
221
|
+
} catch (_error) {
|
|
222
|
+
console.warn(chalk.yellow(`Failed to scan path ${scanPath}: ${error.message}`));
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return references;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Scan a specific path for component references
|
|
231
|
+
*/
|
|
232
|
+
async scanPath(scanPath, component) {
|
|
233
|
+
const references = [];
|
|
234
|
+
const files = await this.getFilesToScan(scanPath);
|
|
235
|
+
|
|
236
|
+
for (const file of files) {
|
|
237
|
+
try {
|
|
238
|
+
const fileReferences = await this.scanFile(file, component);
|
|
239
|
+
references.push(...fileReferences);
|
|
240
|
+
} catch (_error) {
|
|
241
|
+
// Skip files that can't be read
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return references;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Scan a single file for component references
|
|
251
|
+
*/
|
|
252
|
+
async scanFile(filePath, component) {
|
|
253
|
+
const references = [];
|
|
254
|
+
|
|
255
|
+
try {
|
|
256
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
257
|
+
const lines = content.split('\n');
|
|
258
|
+
|
|
259
|
+
for (let i = 0; i < lines.length; i++) {
|
|
260
|
+
const line = lines[i];
|
|
261
|
+
const lineNumber = i + 1;
|
|
262
|
+
|
|
263
|
+
// Check for various reference patterns
|
|
264
|
+
const matches = this.findReferencesInLine(line, component);
|
|
265
|
+
|
|
266
|
+
for (const match of matches) {
|
|
267
|
+
references.push({
|
|
268
|
+
file: filePath,
|
|
269
|
+
line: lineNumber,
|
|
270
|
+
column: match.column,
|
|
271
|
+
context: line.trim(),
|
|
272
|
+
reference_type: match.type,
|
|
273
|
+
match_text: match.text
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
} catch (_error) {
|
|
278
|
+
// File couldn't be read, skip
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
return references;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Find references in a single line of code
|
|
286
|
+
*/
|
|
287
|
+
findReferencesInLine(line, component) {
|
|
288
|
+
const matches = [];
|
|
289
|
+
const patterns = this.scanPatterns[component.type] || this.scanPatterns.default;
|
|
290
|
+
|
|
291
|
+
for (const pattern of patterns) {
|
|
292
|
+
const regex = new RegExp(pattern.pattern.replace('{component_name}', component.name), pattern.flags || 'gi');
|
|
293
|
+
let match;
|
|
294
|
+
|
|
295
|
+
while ((match = regex.exec(line)) !== null) {
|
|
296
|
+
matches.push({
|
|
297
|
+
type: pattern.type,
|
|
298
|
+
text: match[0],
|
|
299
|
+
column: match.index,
|
|
300
|
+
confidence: pattern.confidence || 0.8
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
return matches;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Analyze dependency relationships
|
|
310
|
+
*/
|
|
311
|
+
async analyzeDependencyRelationships(component, options = {}) {
|
|
312
|
+
const dependencies = [];
|
|
313
|
+
|
|
314
|
+
// This would analyze manifest files, import statements, etc.
|
|
315
|
+
// For now, return empty array as placeholder
|
|
316
|
+
|
|
317
|
+
return dependencies;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Scan for external references (config files, documentation, etc.)
|
|
322
|
+
*/
|
|
323
|
+
async scanForExternalReferences(component, options = {}) {
|
|
324
|
+
const externalRefs = [];
|
|
325
|
+
|
|
326
|
+
// Check configuration files
|
|
327
|
+
const configRefs = await this.scanConfigurationFiles(component);
|
|
328
|
+
externalRefs.push(...configRefs);
|
|
329
|
+
|
|
330
|
+
// Check documentation
|
|
331
|
+
const docRefs = await this.scanDocumentationFiles(component);
|
|
332
|
+
externalRefs.push(...docRefs);
|
|
333
|
+
|
|
334
|
+
return externalRefs;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Analyze usage patterns
|
|
339
|
+
*/
|
|
340
|
+
async analyzeUsagePatterns(component, references) {
|
|
341
|
+
const patterns = {
|
|
342
|
+
by_file_type: {},
|
|
343
|
+
by_usage_type: {},
|
|
344
|
+
temporal_distribution: {},
|
|
345
|
+
complexity_indicators: {}
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
for (const ref of references) {
|
|
349
|
+
// Group by file extension
|
|
350
|
+
const ext = path.extname(ref.file);
|
|
351
|
+
patterns.by_file_type[ext] = (patterns.by_file_type[ext] || 0) + 1;
|
|
352
|
+
|
|
353
|
+
// Group by reference type
|
|
354
|
+
patterns.by_usage_type[ref.reference_type] = (patterns.by_usage_type[ref.reference_type] || 0) + 1;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
return patterns;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Assess migration risk based on usage analysis
|
|
362
|
+
*/
|
|
363
|
+
assessMigrationRisk(analysis) {
|
|
364
|
+
let impactLevel = 'low';
|
|
365
|
+
let migrationComplexity = 'simple';
|
|
366
|
+
const affectedAreas = [];
|
|
367
|
+
|
|
368
|
+
// Assess based on total references
|
|
369
|
+
if (analysis.total_references > 20) {
|
|
370
|
+
impactLevel = 'high';
|
|
371
|
+
migrationComplexity = 'complex';
|
|
372
|
+
} else if (analysis.total_references > 10) {
|
|
373
|
+
impactLevel = 'medium';
|
|
374
|
+
migrationComplexity = 'moderate';
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// Check for complex usage patterns
|
|
378
|
+
if (analysis.dependent_components.length > 5) {
|
|
379
|
+
migrationComplexity = 'complex';
|
|
380
|
+
affectedAreas.push('component_dependencies');
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
if (analysis.external_references.length > 0) {
|
|
384
|
+
affectedAreas.push('external_configuration');
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
return {
|
|
388
|
+
impact_level: impactLevel,
|
|
389
|
+
affected_areas: affectedAreas,
|
|
390
|
+
migration_complexity: migrationComplexity
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// Helper methods
|
|
395
|
+
|
|
396
|
+
async getComponentInfo(componentId) {
|
|
397
|
+
// This would typically integrate with the component registry
|
|
398
|
+
// For now, return a basic component structure
|
|
399
|
+
const [type, name] = componentId.split('/');
|
|
400
|
+
|
|
401
|
+
return {
|
|
402
|
+
id: componentId,
|
|
403
|
+
type: type,
|
|
404
|
+
name: name,
|
|
405
|
+
file_path: `aios-core/${type}s/${name}.md`
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
async getScanPaths(_options) {
|
|
410
|
+
const defaultPaths = [
|
|
411
|
+
path.join(this.rootPath, 'aios-core'),
|
|
412
|
+
path.join(this.rootPath, 'src'),
|
|
413
|
+
path.join(this.rootPath, 'lib')
|
|
414
|
+
];
|
|
415
|
+
|
|
416
|
+
if (options.scanPaths) {
|
|
417
|
+
return options.scanPaths;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// Filter paths that exist
|
|
421
|
+
const existingPaths = [];
|
|
422
|
+
for (const scanPath of defaultPaths) {
|
|
423
|
+
try {
|
|
424
|
+
await fs.access(scanPath);
|
|
425
|
+
existingPaths.push(scanPath);
|
|
426
|
+
} catch (_error) {
|
|
427
|
+
// Path doesn't exist, skip
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
return existingPaths;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
async getFilesToScan(scanPath) {
|
|
435
|
+
const files = [];
|
|
436
|
+
|
|
437
|
+
try {
|
|
438
|
+
const entries = await fs.readdir(scanPath, { withFileTypes: true });
|
|
439
|
+
|
|
440
|
+
for (const entry of entries) {
|
|
441
|
+
const fullPath = path.join(scanPath, entry.name);
|
|
442
|
+
|
|
443
|
+
if (entry.isDirectory()) {
|
|
444
|
+
// Recursively scan subdirectories
|
|
445
|
+
const subFiles = await this.getFilesToScan(fullPath);
|
|
446
|
+
files.push(...subFiles);
|
|
447
|
+
} else if (this.shouldScanFile(entry.name)) {
|
|
448
|
+
files.push(fullPath);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
} catch (_error) {
|
|
452
|
+
// Can't read directory, skip
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
return files;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
shouldScanFile(filename) {
|
|
459
|
+
const scanExtensions = ['.js', '.ts', '.md', '.yaml', '.yml', '.json'];
|
|
460
|
+
const ext = path.extname(filename);
|
|
461
|
+
return scanExtensions.includes(ext);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
async scanConfigurationFiles(component) {
|
|
465
|
+
const configRefs = [];
|
|
466
|
+
const configFiles = [
|
|
467
|
+
'package.json',
|
|
468
|
+
'.aiosrc',
|
|
469
|
+
'aios.config.js',
|
|
470
|
+
'manifest.yaml'
|
|
471
|
+
];
|
|
472
|
+
|
|
473
|
+
for (const configFile of configFiles) {
|
|
474
|
+
const configPath = path.join(this.rootPath, configFile);
|
|
475
|
+
|
|
476
|
+
try {
|
|
477
|
+
const refs = await this.scanFile(configPath, component);
|
|
478
|
+
configRefs.push(...refs.map(ref => ({ ...ref, source: 'configuration' })));
|
|
479
|
+
} catch (_error) {
|
|
480
|
+
// Config file doesn't exist or can't be read
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
return configRefs;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
async scanDocumentationFiles(component) {
|
|
488
|
+
const docRefs = [];
|
|
489
|
+
const docsPath = path.join(this.rootPath, 'docs');
|
|
490
|
+
|
|
491
|
+
try {
|
|
492
|
+
const files = await this.getFilesToScan(docsPath);
|
|
493
|
+
|
|
494
|
+
for (const file of files) {
|
|
495
|
+
const refs = await this.scanFile(file, component);
|
|
496
|
+
docRefs.push(...refs.map(ref => ({ ...ref, source: 'documentation' })));
|
|
497
|
+
}
|
|
498
|
+
} catch (_error) {
|
|
499
|
+
// Docs directory doesn't exist
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
return docRefs;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
findNewUsages(oldUsages, newUsages) {
|
|
506
|
+
return newUsages.filter(newUsage =>
|
|
507
|
+
!oldUsages.some(oldUsage =>
|
|
508
|
+
oldUsage.file === newUsage.file && oldUsage.line === newUsage.line
|
|
509
|
+
)
|
|
510
|
+
);
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
findRemovedUsages(oldUsages, newUsages) {
|
|
514
|
+
return oldUsages.filter(oldUsage =>
|
|
515
|
+
!newUsages.some(newUsage =>
|
|
516
|
+
newUsage.file === oldUsage.file && newUsage.line === oldUsage.line
|
|
517
|
+
)
|
|
518
|
+
);
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
calculateUsageTrend(baseline, current) {
|
|
522
|
+
const referenceChange = current.total_references - baseline.total_references;
|
|
523
|
+
|
|
524
|
+
if (referenceChange > 0) return 'increasing';
|
|
525
|
+
if (referenceChange < 0) return 'decreasing';
|
|
526
|
+
return 'stable';
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
calculateMigrationProgress(baseline, current) {
|
|
530
|
+
if (baseline.total_references === 0) return 1.0;
|
|
531
|
+
|
|
532
|
+
const remainingUsages = current.total_references;
|
|
533
|
+
const originalUsages = baseline.total_references;
|
|
534
|
+
|
|
535
|
+
return Math.max(0, (originalUsages - remainingUsages) / originalUsages);
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
generateWarningMessage(componentId, deprecationInfo, usage) {
|
|
539
|
+
let message = `DEPRECATED: ${componentId} is deprecated`;
|
|
540
|
+
|
|
541
|
+
if (deprecationInfo.replacement) {
|
|
542
|
+
message += ` - use ${deprecationInfo.replacement} instead`;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
if (deprecationInfo.removalVersion) {
|
|
546
|
+
message += ` (will be removed in ${deprecationInfo.removalVersion})`;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
return message;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
calculateWarningSeverity(deprecationInfo, usage) {
|
|
553
|
+
if (deprecationInfo.severity === 'critical') return 'error';
|
|
554
|
+
if (deprecationInfo.severity === 'high') return 'warning';
|
|
555
|
+
return 'info';
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
generateSuggestedActions(componentId, deprecationInfo, usage) {
|
|
559
|
+
const actions = [];
|
|
560
|
+
|
|
561
|
+
if (deprecationInfo.replacement) {
|
|
562
|
+
actions.push(`Replace with ${deprecationInfo.replacement}`);
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
if (deprecationInfo.migrationGuide) {
|
|
566
|
+
actions.push(`See migration guide: ${deprecationInfo.migrationGuide}`);
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
actions.push('Update imports and references');
|
|
570
|
+
actions.push('Test functionality after replacement');
|
|
571
|
+
|
|
572
|
+
return actions;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
categorizeUsageLevel(referenceCount) {
|
|
576
|
+
if (referenceCount === 0) return 'zero';
|
|
577
|
+
if (referenceCount <= 5) return 'low';
|
|
578
|
+
if (referenceCount <= 15) return 'medium';
|
|
579
|
+
if (referenceCount <= 30) return 'high';
|
|
580
|
+
return 'very_high';
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
async getOrAnalyzeUsage(componentId) {
|
|
584
|
+
if (this.usageCache.has(componentId)) {
|
|
585
|
+
return this.usageCache.get(componentId);
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
return await this.analyzeComponentUsage(componentId);
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
async getAllTrackedComponents() {
|
|
592
|
+
// This would typically come from a component registry
|
|
593
|
+
// For now, return empty array
|
|
594
|
+
return [];
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
async saveUsageAnalysis(analysis) {
|
|
598
|
+
const filename = `usage-${analysis.component_id.replace('/', '-')}-${Date.now()}.json`;
|
|
599
|
+
const filePath = path.join(this.usageDir, filename);
|
|
600
|
+
|
|
601
|
+
await fs.writeFile(filePath, JSON.stringify(analysis, null, 2));
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
async saveUsageChanges(changes) {
|
|
605
|
+
const filename = `changes-${changes.component_id.replace('/', '-')}-${Date.now()}.json`;
|
|
606
|
+
const filePath = path.join(this.usageDir, filename);
|
|
607
|
+
|
|
608
|
+
await fs.writeFile(filePath, JSON.stringify(changes, null, 2));
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
async saveUsageWarnings(componentId, warnings) {
|
|
612
|
+
const filename = `warnings-${componentId.replace('/', '-')}-${Date.now()}.json`;
|
|
613
|
+
const filePath = path.join(this.usageDir, filename);
|
|
614
|
+
|
|
615
|
+
await fs.writeFile(filePath, JSON.stringify(warnings, null, 2));
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
initializeScanPatterns() {
|
|
619
|
+
return {
|
|
620
|
+
agent: [
|
|
621
|
+
{
|
|
622
|
+
pattern: 'agent:\\s*{component_name}',
|
|
623
|
+
type: 'yaml_reference',
|
|
624
|
+
confidence: 0.9
|
|
625
|
+
},
|
|
626
|
+
{
|
|
627
|
+
pattern: 'use\\s+{component_name}',
|
|
628
|
+
type: 'usage_statement',
|
|
629
|
+
confidence: 0.8
|
|
630
|
+
}
|
|
631
|
+
],
|
|
632
|
+
task: [
|
|
633
|
+
{
|
|
634
|
+
pattern: '\\*{component_name}',
|
|
635
|
+
type: 'task_invocation',
|
|
636
|
+
confidence: 0.9
|
|
637
|
+
},
|
|
638
|
+
{
|
|
639
|
+
pattern: 'require\\(.*{component_name}.*\\)',
|
|
640
|
+
type: 'import_statement',
|
|
641
|
+
confidence: 0.8
|
|
642
|
+
}
|
|
643
|
+
],
|
|
644
|
+
workflow: [
|
|
645
|
+
{
|
|
646
|
+
pattern: 'workflow:\\s*{component_name}',
|
|
647
|
+
type: 'workflow_reference',
|
|
648
|
+
confidence: 0.9
|
|
649
|
+
}
|
|
650
|
+
],
|
|
651
|
+
util: [
|
|
652
|
+
{
|
|
653
|
+
pattern: 'require\\(.*{component_name}.*\\)',
|
|
654
|
+
type: 'import_statement',
|
|
655
|
+
confidence: 0.9
|
|
656
|
+
},
|
|
657
|
+
{
|
|
658
|
+
pattern: 'import.*{component_name}',
|
|
659
|
+
type: 'import_statement',
|
|
660
|
+
confidence: 0.9
|
|
661
|
+
}
|
|
662
|
+
],
|
|
663
|
+
default: [
|
|
664
|
+
{
|
|
665
|
+
pattern: '{component_name}',
|
|
666
|
+
type: 'general_reference',
|
|
667
|
+
confidence: 0.6
|
|
668
|
+
}
|
|
669
|
+
]
|
|
670
|
+
};
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
|
|
674
674
|
module.exports = UsageTracker;
|