aios-core 3.2.0 → 3.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.aios-core/core-config.yaml +44 -0
- package/.aios-core/infrastructure/scripts/ide-sync/agent-parser.js +251 -0
- package/.aios-core/infrastructure/scripts/ide-sync/index.js +480 -0
- package/.aios-core/infrastructure/scripts/ide-sync/redirect-generator.js +200 -0
- package/.aios-core/infrastructure/scripts/ide-sync/transformers/antigravity.js +105 -0
- package/.aios-core/infrastructure/scripts/ide-sync/transformers/claude-code.js +84 -0
- package/.aios-core/infrastructure/scripts/ide-sync/transformers/cursor.js +93 -0
- package/.aios-core/infrastructure/scripts/ide-sync/transformers/trae.js +125 -0
- package/.aios-core/infrastructure/scripts/ide-sync/transformers/windsurf.js +106 -0
- package/.aios-core/infrastructure/scripts/ide-sync/validator.js +273 -0
- package/.aios-core/install-manifest.yaml +41 -5
- package/package.json +10 -1
|
@@ -0,0 +1,480 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* IDE Sync - Main orchestrator for syncing agents to IDEs
|
|
5
|
+
* @story 6.19 - IDE Command Auto-Sync System
|
|
6
|
+
*
|
|
7
|
+
* Commands:
|
|
8
|
+
* sync - Sync agents to all enabled IDEs
|
|
9
|
+
* validate - Validate sync status (report mode)
|
|
10
|
+
* report - Generate sync status report
|
|
11
|
+
*
|
|
12
|
+
* Flags:
|
|
13
|
+
* --ide <name> - Sync specific IDE only
|
|
14
|
+
* --strict - Exit with code 1 if drift detected (CI mode)
|
|
15
|
+
* --dry-run - Preview changes without writing
|
|
16
|
+
* --verbose - Show detailed output
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
const fs = require('fs-extra');
|
|
20
|
+
const path = require('path');
|
|
21
|
+
const yaml = require('js-yaml');
|
|
22
|
+
|
|
23
|
+
const { parseAllAgents } = require('./agent-parser');
|
|
24
|
+
const { generateAllRedirects, writeRedirects } = require('./redirect-generator');
|
|
25
|
+
const { validateAllIdes, formatValidationReport } = require('./validator');
|
|
26
|
+
|
|
27
|
+
// Transformers
|
|
28
|
+
const claudeCodeTransformer = require('./transformers/claude-code');
|
|
29
|
+
const cursorTransformer = require('./transformers/cursor');
|
|
30
|
+
const windsurfTransformer = require('./transformers/windsurf');
|
|
31
|
+
const traeTransformer = require('./transformers/trae');
|
|
32
|
+
const antigravityTransformer = require('./transformers/antigravity');
|
|
33
|
+
|
|
34
|
+
// ANSI colors for output
|
|
35
|
+
const colors = {
|
|
36
|
+
reset: '\x1b[0m',
|
|
37
|
+
bright: '\x1b[1m',
|
|
38
|
+
dim: '\x1b[2m',
|
|
39
|
+
red: '\x1b[31m',
|
|
40
|
+
green: '\x1b[32m',
|
|
41
|
+
yellow: '\x1b[33m',
|
|
42
|
+
blue: '\x1b[34m',
|
|
43
|
+
cyan: '\x1b[36m',
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Load core-config.yaml and extract ideSync section
|
|
48
|
+
* @param {string} projectRoot - Project root directory
|
|
49
|
+
* @returns {object} - ideSync configuration
|
|
50
|
+
*/
|
|
51
|
+
function loadConfig(projectRoot) {
|
|
52
|
+
const configPath = path.join(projectRoot, '.aios-core', 'core-config.yaml');
|
|
53
|
+
|
|
54
|
+
// Default configuration
|
|
55
|
+
const defaultConfig = {
|
|
56
|
+
enabled: true,
|
|
57
|
+
source: '.aios-core/development/agents',
|
|
58
|
+
targets: {
|
|
59
|
+
'claude-code': {
|
|
60
|
+
enabled: true,
|
|
61
|
+
path: '.claude/commands/AIOS/agents',
|
|
62
|
+
format: 'full-markdown-yaml',
|
|
63
|
+
},
|
|
64
|
+
cursor: {
|
|
65
|
+
enabled: true,
|
|
66
|
+
path: '.cursor/rules/agents',
|
|
67
|
+
format: 'condensed-rules',
|
|
68
|
+
},
|
|
69
|
+
windsurf: {
|
|
70
|
+
enabled: true,
|
|
71
|
+
path: '.windsurf/rules/agents',
|
|
72
|
+
format: 'xml-tagged-markdown',
|
|
73
|
+
},
|
|
74
|
+
trae: {
|
|
75
|
+
enabled: true,
|
|
76
|
+
path: '.trae/rules/agents',
|
|
77
|
+
format: 'project-rules',
|
|
78
|
+
},
|
|
79
|
+
antigravity: {
|
|
80
|
+
enabled: true,
|
|
81
|
+
path: '.antigravity/rules/agents',
|
|
82
|
+
format: 'cursor-style',
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
redirects: {
|
|
86
|
+
'aios-developer': 'aios-master',
|
|
87
|
+
'aios-orchestrator': 'aios-master',
|
|
88
|
+
'db-sage': 'data-engineer',
|
|
89
|
+
'github-devops': 'devops',
|
|
90
|
+
},
|
|
91
|
+
validation: {
|
|
92
|
+
strictMode: true,
|
|
93
|
+
failOnDrift: true,
|
|
94
|
+
failOnOrphaned: false,
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
try {
|
|
99
|
+
if (fs.existsSync(configPath)) {
|
|
100
|
+
const content = fs.readFileSync(configPath, 'utf8');
|
|
101
|
+
const config = yaml.load(content);
|
|
102
|
+
|
|
103
|
+
if (config && config.ideSync) {
|
|
104
|
+
return { ...defaultConfig, ...config.ideSync };
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
} catch (error) {
|
|
108
|
+
console.warn(`${colors.yellow}Warning: Could not load config, using defaults${colors.reset}`);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return defaultConfig;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Get transformer for IDE format
|
|
116
|
+
* @param {string} format - IDE format name
|
|
117
|
+
* @returns {object} - Transformer module
|
|
118
|
+
*/
|
|
119
|
+
function getTransformer(format) {
|
|
120
|
+
const transformers = {
|
|
121
|
+
'full-markdown-yaml': claudeCodeTransformer,
|
|
122
|
+
'condensed-rules': cursorTransformer,
|
|
123
|
+
'xml-tagged-markdown': windsurfTransformer,
|
|
124
|
+
'project-rules': traeTransformer,
|
|
125
|
+
'cursor-style': antigravityTransformer,
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
return transformers[format] || claudeCodeTransformer;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Sync agents to a specific IDE
|
|
133
|
+
* @param {object[]} agents - Parsed agent data
|
|
134
|
+
* @param {object} ideConfig - IDE configuration
|
|
135
|
+
* @param {string} ideName - IDE name
|
|
136
|
+
* @param {string} projectRoot - Project root
|
|
137
|
+
* @param {object} options - Sync options
|
|
138
|
+
* @returns {object} - Sync result
|
|
139
|
+
*/
|
|
140
|
+
function syncIde(agents, ideConfig, ideName, projectRoot, options) {
|
|
141
|
+
const result = {
|
|
142
|
+
ide: ideName,
|
|
143
|
+
targetDir: path.join(projectRoot, ideConfig.path),
|
|
144
|
+
files: [],
|
|
145
|
+
errors: [],
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
if (!ideConfig.enabled) {
|
|
149
|
+
result.skipped = true;
|
|
150
|
+
return result;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const transformer = getTransformer(ideConfig.format);
|
|
154
|
+
|
|
155
|
+
// Ensure target directory exists
|
|
156
|
+
if (!options.dryRun) {
|
|
157
|
+
fs.ensureDirSync(result.targetDir);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Transform and write each agent
|
|
161
|
+
for (const agent of agents) {
|
|
162
|
+
// Skip agents with fatal errors (no YAML block found or failed parse with no fallback)
|
|
163
|
+
if (agent.error && agent.error === 'Failed to parse YAML') {
|
|
164
|
+
result.errors.push({
|
|
165
|
+
agent: agent.id,
|
|
166
|
+
error: agent.error,
|
|
167
|
+
});
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
if (agent.error && agent.error === 'No YAML block found') {
|
|
171
|
+
result.errors.push({
|
|
172
|
+
agent: agent.id,
|
|
173
|
+
error: agent.error,
|
|
174
|
+
});
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
try {
|
|
179
|
+
const content = transformer.transform(agent);
|
|
180
|
+
const filename = transformer.getFilename(agent);
|
|
181
|
+
const targetPath = path.join(result.targetDir, filename);
|
|
182
|
+
|
|
183
|
+
if (!options.dryRun) {
|
|
184
|
+
fs.writeFileSync(targetPath, content, 'utf8');
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
result.files.push({
|
|
188
|
+
agent: agent.id,
|
|
189
|
+
filename,
|
|
190
|
+
path: targetPath,
|
|
191
|
+
content,
|
|
192
|
+
});
|
|
193
|
+
} catch (error) {
|
|
194
|
+
result.errors.push({
|
|
195
|
+
agent: agent.id,
|
|
196
|
+
error: error.message,
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return result;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Execute sync command
|
|
206
|
+
* @param {object} options - Command options
|
|
207
|
+
*/
|
|
208
|
+
async function commandSync(options) {
|
|
209
|
+
const projectRoot = process.cwd();
|
|
210
|
+
const config = loadConfig(projectRoot);
|
|
211
|
+
|
|
212
|
+
if (!config.enabled) {
|
|
213
|
+
console.log(`${colors.yellow}IDE sync is disabled in config${colors.reset}`);
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
console.log(`${colors.bright}${colors.blue}🔄 IDE Sync${colors.reset}`);
|
|
218
|
+
console.log('');
|
|
219
|
+
|
|
220
|
+
// Parse all agents
|
|
221
|
+
const agentsDir = path.join(projectRoot, config.source);
|
|
222
|
+
console.log(`${colors.dim}Source: ${agentsDir}${colors.reset}`);
|
|
223
|
+
|
|
224
|
+
const agents = parseAllAgents(agentsDir);
|
|
225
|
+
console.log(`${colors.dim}Found ${agents.length} agents${colors.reset}`);
|
|
226
|
+
console.log('');
|
|
227
|
+
|
|
228
|
+
// Filter IDEs if --ide flag specified
|
|
229
|
+
let targetIdes = Object.entries(config.targets);
|
|
230
|
+
if (options.ide) {
|
|
231
|
+
targetIdes = targetIdes.filter(([name]) => name === options.ide);
|
|
232
|
+
if (targetIdes.length === 0) {
|
|
233
|
+
console.error(`${colors.red}Error: IDE '${options.ide}' not found in config${colors.reset}`);
|
|
234
|
+
process.exit(1);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const results = [];
|
|
239
|
+
|
|
240
|
+
// Sync to each IDE
|
|
241
|
+
for (const [ideName, ideConfig] of targetIdes) {
|
|
242
|
+
if (!ideConfig.enabled) {
|
|
243
|
+
console.log(`${colors.dim}⏭️ ${ideName}: skipped (disabled)${colors.reset}`);
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
console.log(`${colors.cyan}📁 Syncing ${ideName}...${colors.reset}`);
|
|
248
|
+
|
|
249
|
+
const result = syncIde(agents, ideConfig, ideName, projectRoot, options);
|
|
250
|
+
results.push(result);
|
|
251
|
+
|
|
252
|
+
// Generate redirects for this IDE
|
|
253
|
+
const redirects = generateAllRedirects(
|
|
254
|
+
config.redirects,
|
|
255
|
+
result.targetDir,
|
|
256
|
+
ideConfig.format
|
|
257
|
+
);
|
|
258
|
+
const redirectResult = writeRedirects(redirects, options.dryRun);
|
|
259
|
+
|
|
260
|
+
if (options.verbose) {
|
|
261
|
+
console.log(` ${colors.dim}Target: ${result.targetDir}${colors.reset}`);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const agentCount = result.files.length;
|
|
265
|
+
const redirectCount = redirectResult.written.length;
|
|
266
|
+
const errorCount = result.errors.length;
|
|
267
|
+
|
|
268
|
+
let status = `${colors.green}✓${colors.reset}`;
|
|
269
|
+
if (errorCount > 0) {
|
|
270
|
+
status = `${colors.yellow}⚠${colors.reset}`;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
console.log(
|
|
274
|
+
` ${status} ${agentCount} agents, ${redirectCount} redirects${errorCount > 0 ? `, ${errorCount} errors` : ''}`
|
|
275
|
+
);
|
|
276
|
+
|
|
277
|
+
if (options.verbose && result.errors.length > 0) {
|
|
278
|
+
for (const err of result.errors) {
|
|
279
|
+
console.log(` ${colors.red}✗ ${err.agent}: ${err.error}${colors.reset}`);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
console.log('');
|
|
285
|
+
|
|
286
|
+
// Summary
|
|
287
|
+
const totalFiles = results.reduce((sum, r) => sum + r.files.length, 0);
|
|
288
|
+
const totalRedirects = Object.keys(config.redirects).length * targetIdes.filter(([, c]) => c.enabled).length;
|
|
289
|
+
const totalErrors = results.reduce((sum, r) => sum + r.errors.length, 0);
|
|
290
|
+
|
|
291
|
+
if (options.dryRun) {
|
|
292
|
+
console.log(`${colors.yellow}Dry run: ${totalFiles} agents + ${totalRedirects} redirects would be written${colors.reset}`);
|
|
293
|
+
} else {
|
|
294
|
+
console.log(`${colors.green}✅ Sync complete: ${totalFiles} agents + ${totalRedirects} redirects${colors.reset}`);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
if (totalErrors > 0) {
|
|
298
|
+
console.log(`${colors.yellow}⚠️ ${totalErrors} errors occurred${colors.reset}`);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Execute validate command
|
|
304
|
+
* @param {object} options - Command options
|
|
305
|
+
*/
|
|
306
|
+
async function commandValidate(options) {
|
|
307
|
+
const projectRoot = process.cwd();
|
|
308
|
+
const config = loadConfig(projectRoot);
|
|
309
|
+
|
|
310
|
+
if (!config.enabled) {
|
|
311
|
+
console.log(`${colors.yellow}IDE sync is disabled in config${colors.reset}`);
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
console.log(`${colors.bright}${colors.blue}🔍 IDE Sync Validation${colors.reset}`);
|
|
316
|
+
console.log('');
|
|
317
|
+
|
|
318
|
+
// Parse all agents
|
|
319
|
+
const agentsDir = path.join(projectRoot, config.source);
|
|
320
|
+
const agents = parseAllAgents(agentsDir);
|
|
321
|
+
|
|
322
|
+
// Build expected files for each IDE
|
|
323
|
+
const ideConfigs = {};
|
|
324
|
+
|
|
325
|
+
for (const [ideName, ideConfig] of Object.entries(config.targets)) {
|
|
326
|
+
if (!ideConfig.enabled) continue;
|
|
327
|
+
|
|
328
|
+
const transformer = getTransformer(ideConfig.format);
|
|
329
|
+
const expectedFiles = [];
|
|
330
|
+
|
|
331
|
+
for (const agent of agents) {
|
|
332
|
+
if (agent.error) continue;
|
|
333
|
+
|
|
334
|
+
try {
|
|
335
|
+
const content = transformer.transform(agent);
|
|
336
|
+
const filename = transformer.getFilename(agent);
|
|
337
|
+
expectedFiles.push({ filename, content });
|
|
338
|
+
} catch (error) {
|
|
339
|
+
// Skip agents that fail to transform
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// Add redirect files
|
|
344
|
+
const redirects = generateAllRedirects(
|
|
345
|
+
config.redirects,
|
|
346
|
+
path.join(projectRoot, ideConfig.path),
|
|
347
|
+
ideConfig.format
|
|
348
|
+
);
|
|
349
|
+
|
|
350
|
+
for (const redirect of redirects) {
|
|
351
|
+
expectedFiles.push({
|
|
352
|
+
filename: redirect.filename,
|
|
353
|
+
content: redirect.content,
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
ideConfigs[ideName] = {
|
|
358
|
+
expectedFiles,
|
|
359
|
+
targetDir: path.join(projectRoot, ideConfig.path),
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// Validate
|
|
364
|
+
const results = validateAllIdes(ideConfigs, config.redirects);
|
|
365
|
+
|
|
366
|
+
// Output report
|
|
367
|
+
const report = formatValidationReport(results, options.verbose);
|
|
368
|
+
console.log(report);
|
|
369
|
+
|
|
370
|
+
// Exit code
|
|
371
|
+
if (options.strict && !results.summary.pass) {
|
|
372
|
+
console.log('');
|
|
373
|
+
console.log(`${colors.red}Validation failed in strict mode${colors.reset}`);
|
|
374
|
+
process.exit(1);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* Parse command line arguments
|
|
380
|
+
* @returns {object} - Parsed options
|
|
381
|
+
*/
|
|
382
|
+
function parseArgs() {
|
|
383
|
+
const args = process.argv.slice(2);
|
|
384
|
+
const options = {
|
|
385
|
+
command: args[0] || 'sync',
|
|
386
|
+
ide: null,
|
|
387
|
+
strict: false,
|
|
388
|
+
dryRun: false,
|
|
389
|
+
verbose: false,
|
|
390
|
+
};
|
|
391
|
+
|
|
392
|
+
for (let i = 1; i < args.length; i++) {
|
|
393
|
+
const arg = args[i];
|
|
394
|
+
|
|
395
|
+
if (arg === '--ide' && args[i + 1]) {
|
|
396
|
+
options.ide = args[++i];
|
|
397
|
+
} else if (arg === '--strict') {
|
|
398
|
+
options.strict = true;
|
|
399
|
+
} else if (arg === '--dry-run') {
|
|
400
|
+
options.dryRun = true;
|
|
401
|
+
} else if (arg === '--verbose' || arg === '-v') {
|
|
402
|
+
options.verbose = true;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
return options;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* Show help
|
|
411
|
+
*/
|
|
412
|
+
function showHelp() {
|
|
413
|
+
console.log(`
|
|
414
|
+
${colors.bright}IDE Sync${colors.reset} - Sync AIOS agents to IDE command files
|
|
415
|
+
|
|
416
|
+
${colors.bright}Usage:${colors.reset}
|
|
417
|
+
node ide-sync/index.js <command> [options]
|
|
418
|
+
|
|
419
|
+
${colors.bright}Commands:${colors.reset}
|
|
420
|
+
sync Sync agents to all enabled IDEs (default)
|
|
421
|
+
validate Validate sync status
|
|
422
|
+
report Generate sync status report (alias for validate)
|
|
423
|
+
|
|
424
|
+
${colors.bright}Options:${colors.reset}
|
|
425
|
+
--ide <name> Sync/validate specific IDE only
|
|
426
|
+
--strict Exit with code 1 if drift detected (CI mode)
|
|
427
|
+
--dry-run Preview changes without writing files
|
|
428
|
+
--verbose, -v Show detailed output
|
|
429
|
+
|
|
430
|
+
${colors.bright}Examples:${colors.reset}
|
|
431
|
+
node ide-sync/index.js sync
|
|
432
|
+
node ide-sync/index.js sync --ide cursor
|
|
433
|
+
node ide-sync/index.js validate --strict
|
|
434
|
+
node ide-sync/index.js sync --dry-run --verbose
|
|
435
|
+
`);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* Main entry point
|
|
440
|
+
*/
|
|
441
|
+
async function main() {
|
|
442
|
+
const options = parseArgs();
|
|
443
|
+
|
|
444
|
+
if (options.command === 'help' || options.command === '--help' || options.command === '-h') {
|
|
445
|
+
showHelp();
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
switch (options.command) {
|
|
450
|
+
case 'sync':
|
|
451
|
+
await commandSync(options);
|
|
452
|
+
break;
|
|
453
|
+
|
|
454
|
+
case 'validate':
|
|
455
|
+
case 'report':
|
|
456
|
+
await commandValidate(options);
|
|
457
|
+
break;
|
|
458
|
+
|
|
459
|
+
default:
|
|
460
|
+
console.error(`${colors.red}Unknown command: ${options.command}${colors.reset}`);
|
|
461
|
+
showHelp();
|
|
462
|
+
process.exit(1);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
// Run if executed directly
|
|
467
|
+
if (require.main === module) {
|
|
468
|
+
main().catch(error => {
|
|
469
|
+
console.error(`${colors.red}Error: ${error.message}${colors.reset}`);
|
|
470
|
+
process.exit(1);
|
|
471
|
+
});
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
module.exports = {
|
|
475
|
+
loadConfig,
|
|
476
|
+
getTransformer,
|
|
477
|
+
syncIde,
|
|
478
|
+
commandSync,
|
|
479
|
+
commandValidate,
|
|
480
|
+
};
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Redirect Generator - Creates redirect files for deprecated agents
|
|
3
|
+
* @story 6.19 - IDE Command Auto-Sync System
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const fs = require('fs-extra');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Default redirects configuration
|
|
11
|
+
* Maps deprecated agent IDs to their new target IDs
|
|
12
|
+
*/
|
|
13
|
+
const DEFAULT_REDIRECTS = {
|
|
14
|
+
'aios-developer': 'aios-master',
|
|
15
|
+
'aios-orchestrator': 'aios-master',
|
|
16
|
+
'db-sage': 'data-engineer',
|
|
17
|
+
'github-devops': 'devops',
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Generate redirect content for a specific IDE format
|
|
22
|
+
* @param {string} oldId - Deprecated agent ID
|
|
23
|
+
* @param {string} newId - New target agent ID
|
|
24
|
+
* @param {string} format - IDE format
|
|
25
|
+
* @returns {string} - Redirect file content
|
|
26
|
+
*/
|
|
27
|
+
function generateRedirectContent(oldId, newId, format) {
|
|
28
|
+
const baseContent = {
|
|
29
|
+
header: `# Agent Redirect: ${oldId} → ${newId}`,
|
|
30
|
+
notice: `**DEPRECATED:** This agent has been renamed/merged.`,
|
|
31
|
+
instruction: `Use \`@${newId}\` instead.`,
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
switch (format) {
|
|
35
|
+
case 'full-markdown-yaml':
|
|
36
|
+
// Claude Code format
|
|
37
|
+
return `${baseContent.header}
|
|
38
|
+
|
|
39
|
+
${baseContent.notice}
|
|
40
|
+
|
|
41
|
+
${baseContent.instruction}
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## Redirect Details
|
|
46
|
+
|
|
47
|
+
| Property | Value |
|
|
48
|
+
|----------|-------|
|
|
49
|
+
| Old ID | @${oldId} |
|
|
50
|
+
| New ID | @${newId} |
|
|
51
|
+
| Status | Deprecated |
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
*AIOS Redirect - Synced automatically*
|
|
55
|
+
`;
|
|
56
|
+
|
|
57
|
+
case 'xml-tagged-markdown':
|
|
58
|
+
// Windsurf format
|
|
59
|
+
return `${baseContent.header}
|
|
60
|
+
|
|
61
|
+
<redirect>
|
|
62
|
+
Old: @${oldId}
|
|
63
|
+
New: @${newId}
|
|
64
|
+
Status: Deprecated
|
|
65
|
+
</redirect>
|
|
66
|
+
|
|
67
|
+
<notice>
|
|
68
|
+
${baseContent.notice}
|
|
69
|
+
${baseContent.instruction}
|
|
70
|
+
</notice>
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
*AIOS Redirect - Synced automatically*
|
|
74
|
+
`;
|
|
75
|
+
|
|
76
|
+
case 'project-rules':
|
|
77
|
+
// Trae format
|
|
78
|
+
return `${baseContent.header}
|
|
79
|
+
|
|
80
|
+
## Redirect Notice
|
|
81
|
+
|
|
82
|
+
${baseContent.notice}
|
|
83
|
+
|
|
84
|
+
## Action Required
|
|
85
|
+
|
|
86
|
+
${baseContent.instruction}
|
|
87
|
+
|
|
88
|
+
## Mapping
|
|
89
|
+
|
|
90
|
+
| Old Agent | New Agent |
|
|
91
|
+
|-----------|-----------|
|
|
92
|
+
| @${oldId} | @${newId} |
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
*AIOS Redirect - Synced automatically*
|
|
96
|
+
`;
|
|
97
|
+
|
|
98
|
+
case 'condensed-rules':
|
|
99
|
+
case 'cursor-style':
|
|
100
|
+
default:
|
|
101
|
+
// Cursor/Antigravity format
|
|
102
|
+
return `${baseContent.header}
|
|
103
|
+
|
|
104
|
+
> ${baseContent.notice} ${baseContent.instruction}
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
*AIOS Redirect - Synced automatically*
|
|
108
|
+
`;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Generate redirect file for a deprecated agent
|
|
114
|
+
* @param {string} oldId - Deprecated agent ID
|
|
115
|
+
* @param {string} newId - New target agent ID
|
|
116
|
+
* @param {string} targetDir - Target directory for the redirect file
|
|
117
|
+
* @param {string} format - IDE format
|
|
118
|
+
* @returns {object} - Result with path and content
|
|
119
|
+
*/
|
|
120
|
+
function generateRedirect(oldId, newId, targetDir, format) {
|
|
121
|
+
const filename = `${oldId}.md`;
|
|
122
|
+
const filePath = path.join(targetDir, filename);
|
|
123
|
+
const content = generateRedirectContent(oldId, newId, format);
|
|
124
|
+
|
|
125
|
+
return {
|
|
126
|
+
oldId,
|
|
127
|
+
newId,
|
|
128
|
+
filename,
|
|
129
|
+
path: filePath,
|
|
130
|
+
content,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Generate all redirects for a specific IDE
|
|
136
|
+
* @param {object} redirectsConfig - Redirects configuration (oldId -> newId)
|
|
137
|
+
* @param {string} targetDir - Target directory
|
|
138
|
+
* @param {string} format - IDE format
|
|
139
|
+
* @returns {object[]} - Array of redirect objects
|
|
140
|
+
*/
|
|
141
|
+
function generateAllRedirects(redirectsConfig, targetDir, format) {
|
|
142
|
+
const redirects = redirectsConfig || DEFAULT_REDIRECTS;
|
|
143
|
+
const results = [];
|
|
144
|
+
|
|
145
|
+
for (const [oldId, newId] of Object.entries(redirects)) {
|
|
146
|
+
const redirect = generateRedirect(oldId, newId, targetDir, format);
|
|
147
|
+
results.push(redirect);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return results;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Write redirect files to disk
|
|
155
|
+
* @param {object[]} redirects - Array of redirect objects
|
|
156
|
+
* @param {boolean} dryRun - If true, don't write files
|
|
157
|
+
* @returns {object} - Result summary
|
|
158
|
+
*/
|
|
159
|
+
function writeRedirects(redirects, dryRun = false) {
|
|
160
|
+
const results = {
|
|
161
|
+
written: [],
|
|
162
|
+
errors: [],
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
for (const redirect of redirects) {
|
|
166
|
+
try {
|
|
167
|
+
if (!dryRun) {
|
|
168
|
+
fs.ensureDirSync(path.dirname(redirect.path));
|
|
169
|
+
fs.writeFileSync(redirect.path, redirect.content, 'utf8');
|
|
170
|
+
}
|
|
171
|
+
results.written.push(redirect.path);
|
|
172
|
+
} catch (error) {
|
|
173
|
+
results.errors.push({
|
|
174
|
+
path: redirect.path,
|
|
175
|
+
error: error.message,
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return results;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Get list of redirect filenames
|
|
185
|
+
* @param {object} redirectsConfig - Redirects configuration
|
|
186
|
+
* @returns {string[]} - Array of filenames
|
|
187
|
+
*/
|
|
188
|
+
function getRedirectFilenames(redirectsConfig) {
|
|
189
|
+
const redirects = redirectsConfig || DEFAULT_REDIRECTS;
|
|
190
|
+
return Object.keys(redirects).map(id => `${id}.md`);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
module.exports = {
|
|
194
|
+
DEFAULT_REDIRECTS,
|
|
195
|
+
generateRedirectContent,
|
|
196
|
+
generateRedirect,
|
|
197
|
+
generateAllRedirects,
|
|
198
|
+
writeRedirects,
|
|
199
|
+
getRedirectFilenames,
|
|
200
|
+
};
|