aios-core 4.2.3 → 4.2.4
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/registry/service-registry.json +6466 -6586
- package/.aios-core/core-config.yaml +10 -5
- package/.aios-core/data/aios-kb.md +19 -25
- package/.aios-core/data/entity-registry.yaml +311 -204
- package/.aios-core/data/registry-update-log.jsonl +8 -0
- package/.aios-core/development/tasks/db-squad-integration.md +3 -3
- package/.aios-core/development/tasks/dev-develop-story.md +1 -1
- package/.aios-core/development/tasks/integrate-squad.md +1 -1
- package/.aios-core/development/tasks/pr-automation.md +3 -3
- package/.aios-core/development/tasks/squad-creator-migrate.md +1 -1
- package/.aios-core/development/tasks/squad-creator-sync-ide-command.md +0 -2
- package/.aios-core/development/tasks/update-aios.md +2 -2
- package/.aios-core/development/tasks/validate-next-story.md +2 -99
- package/.aios-core/development/workflows/README.md +0 -4
- package/.aios-core/docs/standards/AIOS-COLOR-PALETTE-V2.1.md +0 -1
- package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.1-COMPLETE.md +3 -3
- package/.aios-core/docs/standards/QUALITY-GATES-SPECIFICATION.md +1 -1
- package/.aios-core/docs/standards/STANDARDS-INDEX.md +4 -4
- package/.aios-core/docs/standards/STORY-TEMPLATE-V2-SPECIFICATION.md +2 -2
- package/.aios-core/framework-config.yaml +8 -4
- package/.aios-core/infrastructure/scripts/ide-sync/README.md +29 -5
- package/.aios-core/infrastructure/scripts/ide-sync/gemini-commands.js +205 -0
- package/.aios-core/infrastructure/scripts/ide-sync/index.js +48 -11
- package/.aios-core/infrastructure/scripts/ide-sync/redirect-generator.js +1 -1
- package/.aios-core/infrastructure/scripts/test-utilities.js +1 -1
- package/.aios-core/infrastructure/scripts/tool-resolver.js +2 -2
- package/.aios-core/infrastructure/scripts/validate-claude-integration.js +101 -0
- package/.aios-core/infrastructure/scripts/validate-codex-integration.js +141 -0
- package/.aios-core/infrastructure/scripts/validate-gemini-integration.js +151 -0
- package/.aios-core/infrastructure/scripts/validate-parity.js +119 -0
- package/.aios-core/infrastructure/templates/aios-sync.yaml.template +0 -11
- package/.aios-core/install-manifest.yaml +83 -71
- package/.aios-core/local-config.yaml.template +2 -1
- package/.aios-core/presets/README.md +0 -1
- package/.aios-core/product/data/integration-patterns.md +1 -1
- package/.aios-core/product/templates/ide-rules/gemini-rules.md +87 -233
- package/.aios-core/product/templates/statusline/statusline-script.js +188 -0
- package/.aios-core/product/templates/statusline/track-agent.sh +68 -0
- package/.aios-core/user-guide.md +14 -19
- package/LICENSE +0 -27
- package/README.md +42 -6
- package/bin/aios-init.js +98 -54
- package/bin/modules/env-config.js +0 -1
- package/package.json +18 -5
- package/packages/gemini-aios-extension/README.md +13 -8
- package/packages/gemini-aios-extension/commands/aios-agent.js +7 -0
- package/packages/gemini-aios-extension/commands/aios-agents.js +2 -1
- package/packages/gemini-aios-extension/commands/aios-analyst.js +6 -0
- package/packages/gemini-aios-extension/commands/aios-architect.js +6 -0
- package/packages/gemini-aios-extension/commands/aios-data-engineer.js +6 -0
- package/packages/gemini-aios-extension/commands/aios-dev.js +6 -0
- package/packages/gemini-aios-extension/commands/aios-devops.js +6 -0
- package/packages/gemini-aios-extension/commands/aios-master.js +6 -0
- package/packages/gemini-aios-extension/commands/aios-menu.js +6 -0
- package/packages/gemini-aios-extension/commands/aios-pm.js +6 -0
- package/packages/gemini-aios-extension/commands/aios-po.js +6 -0
- package/packages/gemini-aios-extension/commands/aios-qa.js +6 -0
- package/packages/gemini-aios-extension/commands/aios-sm.js +6 -0
- package/packages/gemini-aios-extension/commands/aios-squad-creator.js +6 -0
- package/packages/gemini-aios-extension/commands/aios-ux-design-expert.js +6 -0
- package/packages/gemini-aios-extension/commands/lib/agent-launcher.js +138 -0
- package/packages/gemini-aios-extension/extension.json +70 -0
- package/packages/gemini-aios-extension/gemini-extension.json +147 -0
- package/packages/gemini-aios-extension/hooks/hooks.json +67 -65
- package/packages/installer/src/config/ide-configs.js +10 -10
- package/packages/installer/src/config/templates/core-config-template.js +6 -3
- package/packages/installer/src/wizard/ide-config-generator.js +373 -2
- package/packages/installer/src/wizard/ide-selector.js +1 -1
- package/scripts/code-intel-health-check.js +125 -125
- package/scripts/ensure-manifest.js +58 -0
- package/.aios-core/infrastructure/scripts/ide-sync/transformers/windsurf.js +0 -106
- package/.aios-core/product/templates/ide-rules/cline-rules.md +0 -84
- package/.aios-core/product/templates/ide-rules/roo-rules.md +0 -86
- package/.aios-core/product/templates/ide-rules/windsurf-rules.md +0 -80
|
@@ -24,11 +24,11 @@ const yaml = require('js-yaml');
|
|
|
24
24
|
const { parseAllAgents } = require('./agent-parser');
|
|
25
25
|
const { generateAllRedirects, writeRedirects } = require('./redirect-generator');
|
|
26
26
|
const { validateAllIdes, formatValidationReport } = require('./validator');
|
|
27
|
+
const { syncGeminiCommands, buildGeminiCommandFiles } = require('./gemini-commands');
|
|
27
28
|
|
|
28
29
|
// Transformers
|
|
29
30
|
const claudeCodeTransformer = require('./transformers/claude-code');
|
|
30
31
|
const cursorTransformer = require('./transformers/cursor');
|
|
31
|
-
const windsurfTransformer = require('./transformers/windsurf');
|
|
32
32
|
const antigravityTransformer = require('./transformers/antigravity');
|
|
33
33
|
|
|
34
34
|
// ANSI colors for output
|
|
@@ -66,16 +66,21 @@ function loadConfig(projectRoot) {
|
|
|
66
66
|
path: '.codex/agents',
|
|
67
67
|
format: 'full-markdown-yaml',
|
|
68
68
|
},
|
|
69
|
+
gemini: {
|
|
70
|
+
enabled: true,
|
|
71
|
+
path: '.gemini/rules/AIOS/agents',
|
|
72
|
+
format: 'full-markdown-yaml',
|
|
73
|
+
},
|
|
74
|
+
'github-copilot': {
|
|
75
|
+
enabled: true,
|
|
76
|
+
path: '.github/agents',
|
|
77
|
+
format: 'full-markdown-yaml',
|
|
78
|
+
},
|
|
69
79
|
cursor: {
|
|
70
80
|
enabled: true,
|
|
71
81
|
path: '.cursor/rules/agents',
|
|
72
82
|
format: 'condensed-rules',
|
|
73
83
|
},
|
|
74
|
-
windsurf: {
|
|
75
|
-
enabled: false, // Disabled - consolidating to core IDEs (v3.10.0)
|
|
76
|
-
path: '.windsurf/rules/agents',
|
|
77
|
-
format: 'xml-tagged-markdown',
|
|
78
|
-
},
|
|
79
84
|
antigravity: {
|
|
80
85
|
enabled: true,
|
|
81
86
|
path: '.antigravity/rules/agents',
|
|
@@ -120,7 +125,6 @@ function getTransformer(format) {
|
|
|
120
125
|
const transformers = {
|
|
121
126
|
'full-markdown-yaml': claudeCodeTransformer,
|
|
122
127
|
'condensed-rules': cursorTransformer,
|
|
123
|
-
'xml-tagged-markdown': windsurfTransformer,
|
|
124
128
|
'cursor-style': antigravityTransformer,
|
|
125
129
|
};
|
|
126
130
|
|
|
@@ -258,6 +262,15 @@ async function commandSync(options) {
|
|
|
258
262
|
}
|
|
259
263
|
|
|
260
264
|
const result = syncIde(agents, ideConfig, ideName, projectRoot, options);
|
|
265
|
+
|
|
266
|
+
// Gemini CLI: also sync slash launcher command files (.gemini/commands/*.toml)
|
|
267
|
+
if (ideName === 'gemini') {
|
|
268
|
+
const geminiCommands = syncGeminiCommands(agents, projectRoot, options);
|
|
269
|
+
result.commandFiles = geminiCommands.files;
|
|
270
|
+
} else {
|
|
271
|
+
result.commandFiles = [];
|
|
272
|
+
}
|
|
273
|
+
|
|
261
274
|
results.push(result);
|
|
262
275
|
|
|
263
276
|
// Generate redirects for this IDE
|
|
@@ -269,6 +282,7 @@ async function commandSync(options) {
|
|
|
269
282
|
}
|
|
270
283
|
|
|
271
284
|
const agentCount = result.files.length;
|
|
285
|
+
const commandCount = (result.commandFiles || []).length;
|
|
272
286
|
const redirectCount = redirectResult.written.length;
|
|
273
287
|
const errorCount = result.errors.length;
|
|
274
288
|
|
|
@@ -279,7 +293,7 @@ async function commandSync(options) {
|
|
|
279
293
|
}
|
|
280
294
|
|
|
281
295
|
console.log(
|
|
282
|
-
` ${status} ${agentCount} agents, ${redirectCount} redirects${errorCount > 0 ? `, ${errorCount} errors` : ''}`
|
|
296
|
+
` ${status} ${agentCount} agents${commandCount > 0 ? `, ${commandCount} commands` : ''}, ${redirectCount} redirects${errorCount > 0 ? `, ${errorCount} errors` : ''}`
|
|
283
297
|
);
|
|
284
298
|
|
|
285
299
|
if (options.verbose && result.errors.length > 0) {
|
|
@@ -291,7 +305,7 @@ async function commandSync(options) {
|
|
|
291
305
|
}
|
|
292
306
|
|
|
293
307
|
// Summary
|
|
294
|
-
const totalFiles = results.reduce((sum, r) => sum + r.files.length, 0);
|
|
308
|
+
const totalFiles = results.reduce((sum, r) => sum + r.files.length + (r.commandFiles || []).length, 0);
|
|
295
309
|
const totalRedirects =
|
|
296
310
|
Object.keys(config.redirects).length * targetIdes.filter(([, c]) => c.enabled).length;
|
|
297
311
|
const totalErrors = results.reduce((sum, r) => sum + r.errors.length, 0);
|
|
@@ -337,9 +351,18 @@ async function commandValidate(options) {
|
|
|
337
351
|
|
|
338
352
|
// Build expected files for each IDE
|
|
339
353
|
const ideConfigs = {};
|
|
354
|
+
let targetIdes = Object.entries(config.targets).filter(([, ideConfig]) => ideConfig.enabled);
|
|
340
355
|
|
|
341
|
-
|
|
342
|
-
|
|
356
|
+
// Filter IDEs if --ide flag specified
|
|
357
|
+
if (options.ide) {
|
|
358
|
+
targetIdes = targetIdes.filter(([name]) => name === options.ide);
|
|
359
|
+
if (targetIdes.length === 0) {
|
|
360
|
+
console.error(`${colors.red}Error: IDE '${options.ide}' not found in config${colors.reset}`);
|
|
361
|
+
process.exit(1);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
for (const [ideName, ideConfig] of targetIdes) {
|
|
343
366
|
|
|
344
367
|
const transformer = getTransformer(ideConfig.format);
|
|
345
368
|
const expectedFiles = [];
|
|
@@ -374,6 +397,18 @@ async function commandValidate(options) {
|
|
|
374
397
|
expectedFiles,
|
|
375
398
|
targetDir: path.join(projectRoot, ideConfig.path),
|
|
376
399
|
};
|
|
400
|
+
|
|
401
|
+
// Gemini CLI command launcher files are synced under .gemini/commands/*.toml
|
|
402
|
+
if (ideName === 'gemini') {
|
|
403
|
+
const commandFiles = buildGeminiCommandFiles(agents).map((entry) => ({
|
|
404
|
+
filename: entry.filename,
|
|
405
|
+
content: entry.content,
|
|
406
|
+
}));
|
|
407
|
+
ideConfigs['gemini-commands'] = {
|
|
408
|
+
expectedFiles: commandFiles,
|
|
409
|
+
targetDir: path.join(projectRoot, '.gemini', 'commands'),
|
|
410
|
+
};
|
|
411
|
+
}
|
|
377
412
|
}
|
|
378
413
|
|
|
379
414
|
// Validate
|
|
@@ -450,7 +485,9 @@ ${colors.bright}Options:${colors.reset}
|
|
|
450
485
|
${colors.bright}Examples:${colors.reset}
|
|
451
486
|
node ide-sync/index.js sync
|
|
452
487
|
node ide-sync/index.js sync --ide codex
|
|
488
|
+
node ide-sync/index.js sync --ide gemini
|
|
453
489
|
node ide-sync/index.js sync --ide cursor
|
|
490
|
+
node ide-sync/index.js validate --ide gemini --strict
|
|
454
491
|
node ide-sync/index.js validate --strict
|
|
455
492
|
node ide-sync/index.js sync --dry-run --verbose
|
|
456
493
|
`);
|
|
@@ -48,7 +48,7 @@ class ToolResolver {
|
|
|
48
48
|
// 2. Build search paths (squad → core priority)
|
|
49
49
|
const searchPaths = [];
|
|
50
50
|
if (context.expansionPack) {
|
|
51
|
-
searchPaths.push(`
|
|
51
|
+
searchPaths.push(`squads/${context.expansionPack}/tools`);
|
|
52
52
|
}
|
|
53
53
|
searchPaths.push(...this.basePaths);
|
|
54
54
|
|
|
@@ -304,7 +304,7 @@ class ToolResolver {
|
|
|
304
304
|
async toolExists(toolName, context = {}) {
|
|
305
305
|
const searchPaths = [];
|
|
306
306
|
if (context.expansionPack) {
|
|
307
|
-
searchPaths.push(`
|
|
307
|
+
searchPaths.push(`squads/${context.expansionPack}/tools`);
|
|
308
308
|
}
|
|
309
309
|
searchPaths.push(...this.basePaths);
|
|
310
310
|
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
|
|
7
|
+
function parseArgs(argv = process.argv.slice(2)) {
|
|
8
|
+
const args = new Set(argv);
|
|
9
|
+
return {
|
|
10
|
+
quiet: args.has('--quiet') || args.has('-q'),
|
|
11
|
+
json: args.has('--json'),
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function countMarkdownFiles(dirPath) {
|
|
16
|
+
if (!fs.existsSync(dirPath)) return 0;
|
|
17
|
+
return fs.readdirSync(dirPath).filter((f) => f.endsWith('.md')).length;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function validateClaudeIntegration(options = {}) {
|
|
21
|
+
const projectRoot = options.projectRoot || process.cwd();
|
|
22
|
+
const rulesFile = options.rulesFile || path.join(projectRoot, '.claude', 'CLAUDE.md');
|
|
23
|
+
const agentsDir = options.agentsDir || path.join(projectRoot, '.claude', 'commands', 'AIOS', 'agents');
|
|
24
|
+
const hooksDir = options.hooksDir || path.join(projectRoot, '.claude', 'hooks');
|
|
25
|
+
const sourceAgentsDir =
|
|
26
|
+
options.sourceAgentsDir || path.join(projectRoot, '.aios-core', 'development', 'agents');
|
|
27
|
+
|
|
28
|
+
const errors = [];
|
|
29
|
+
const warnings = [];
|
|
30
|
+
|
|
31
|
+
if (!fs.existsSync(agentsDir)) {
|
|
32
|
+
errors.push(`Missing Claude agents dir: ${path.relative(projectRoot, agentsDir)}`);
|
|
33
|
+
}
|
|
34
|
+
if (!fs.existsSync(rulesFile)) {
|
|
35
|
+
warnings.push(`Claude rules file not found yet: ${path.relative(projectRoot, rulesFile)}`);
|
|
36
|
+
}
|
|
37
|
+
if (!fs.existsSync(hooksDir)) {
|
|
38
|
+
warnings.push(`Claude hooks dir not found yet: ${path.relative(projectRoot, hooksDir)}`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const sourceCount = countMarkdownFiles(sourceAgentsDir);
|
|
42
|
+
const claudeCount = countMarkdownFiles(agentsDir);
|
|
43
|
+
if (sourceCount > 0 && claudeCount !== sourceCount) {
|
|
44
|
+
warnings.push(`Claude agent count differs from source (${claudeCount}/${sourceCount})`);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
ok: errors.length === 0,
|
|
49
|
+
errors,
|
|
50
|
+
warnings,
|
|
51
|
+
metrics: {
|
|
52
|
+
sourceAgents: sourceCount,
|
|
53
|
+
claudeAgents: claudeCount,
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function formatHumanReport(result) {
|
|
59
|
+
if (result.ok) {
|
|
60
|
+
const lines = [`✅ Claude integration validation passed (agents: ${result.metrics.claudeAgents})`];
|
|
61
|
+
if (result.warnings.length > 0) {
|
|
62
|
+
lines.push(...result.warnings.map((w) => `⚠️ ${w}`));
|
|
63
|
+
}
|
|
64
|
+
return lines.join('\n');
|
|
65
|
+
}
|
|
66
|
+
const lines = [
|
|
67
|
+
`❌ Claude integration validation failed (${result.errors.length} issue(s))`,
|
|
68
|
+
...result.errors.map((e) => `- ${e}`),
|
|
69
|
+
];
|
|
70
|
+
if (result.warnings.length > 0) {
|
|
71
|
+
lines.push(...result.warnings.map((w) => `⚠️ ${w}`));
|
|
72
|
+
}
|
|
73
|
+
return lines.join('\n');
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function main() {
|
|
77
|
+
const args = parseArgs();
|
|
78
|
+
const result = validateClaudeIntegration(args);
|
|
79
|
+
|
|
80
|
+
if (!args.quiet) {
|
|
81
|
+
if (args.json) {
|
|
82
|
+
console.log(JSON.stringify(result, null, 2));
|
|
83
|
+
} else {
|
|
84
|
+
console.log(formatHumanReport(result));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (!result.ok) {
|
|
89
|
+
process.exitCode = 1;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (require.main === module) {
|
|
94
|
+
main();
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
module.exports = {
|
|
98
|
+
validateClaudeIntegration,
|
|
99
|
+
parseArgs,
|
|
100
|
+
countMarkdownFiles,
|
|
101
|
+
};
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
|
|
7
|
+
function getDefaultOptions() {
|
|
8
|
+
const projectRoot = process.cwd();
|
|
9
|
+
return {
|
|
10
|
+
projectRoot,
|
|
11
|
+
instructionsFile: path.join(projectRoot, 'AGENTS.md'),
|
|
12
|
+
agentsDir: path.join(projectRoot, '.codex', 'agents'),
|
|
13
|
+
skillsDir: path.join(projectRoot, '.codex', 'skills'),
|
|
14
|
+
sourceAgentsDir: path.join(projectRoot, '.aios-core', 'development', 'agents'),
|
|
15
|
+
quiet: false,
|
|
16
|
+
json: false,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function parseArgs(argv = process.argv.slice(2)) {
|
|
21
|
+
const args = new Set(argv);
|
|
22
|
+
return {
|
|
23
|
+
quiet: args.has('--quiet') || args.has('-q'),
|
|
24
|
+
json: args.has('--json'),
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function countMarkdownFiles(dirPath) {
|
|
29
|
+
if (!fs.existsSync(dirPath)) return 0;
|
|
30
|
+
return fs.readdirSync(dirPath).filter((f) => f.endsWith('.md')).length;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function countSkillFiles(skillsDir) {
|
|
34
|
+
if (!fs.existsSync(skillsDir)) return 0;
|
|
35
|
+
const entries = fs.readdirSync(skillsDir, { withFileTypes: true });
|
|
36
|
+
return entries
|
|
37
|
+
.filter((entry) => entry.isDirectory() && entry.name.startsWith('aios-'))
|
|
38
|
+
.filter((entry) => fs.existsSync(path.join(skillsDir, entry.name, 'SKILL.md')))
|
|
39
|
+
.length;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function validateCodexIntegration(options = {}) {
|
|
43
|
+
const projectRoot = options.projectRoot || process.cwd();
|
|
44
|
+
const resolved = {
|
|
45
|
+
...getDefaultOptions(),
|
|
46
|
+
...options,
|
|
47
|
+
projectRoot,
|
|
48
|
+
instructionsFile: options.instructionsFile || path.join(projectRoot, 'AGENTS.md'),
|
|
49
|
+
agentsDir: options.agentsDir || path.join(projectRoot, '.codex', 'agents'),
|
|
50
|
+
skillsDir: options.skillsDir || path.join(projectRoot, '.codex', 'skills'),
|
|
51
|
+
sourceAgentsDir: options.sourceAgentsDir || path.join(projectRoot, '.aios-core', 'development', 'agents'),
|
|
52
|
+
};
|
|
53
|
+
const errors = [];
|
|
54
|
+
const warnings = [];
|
|
55
|
+
|
|
56
|
+
if (!fs.existsSync(resolved.instructionsFile)) {
|
|
57
|
+
warnings.push(
|
|
58
|
+
`Codex instructions file not found yet: ${path.relative(resolved.projectRoot, resolved.instructionsFile)}`,
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (!fs.existsSync(resolved.agentsDir)) {
|
|
63
|
+
errors.push(`Missing Codex agents dir: ${path.relative(resolved.projectRoot, resolved.agentsDir)}`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (!fs.existsSync(resolved.skillsDir)) {
|
|
67
|
+
errors.push(`Missing Codex skills dir: ${path.relative(resolved.projectRoot, resolved.skillsDir)}`);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const sourceCount = countMarkdownFiles(resolved.sourceAgentsDir);
|
|
71
|
+
const codexAgentsCount = countMarkdownFiles(resolved.agentsDir);
|
|
72
|
+
const codexSkillsCount = countSkillFiles(resolved.skillsDir);
|
|
73
|
+
|
|
74
|
+
if (sourceCount > 0 && codexAgentsCount !== sourceCount) {
|
|
75
|
+
warnings.push(`Codex agent count differs from source (${codexAgentsCount}/${sourceCount})`);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (sourceCount > 0 && codexSkillsCount !== sourceCount) {
|
|
79
|
+
warnings.push(`Codex skill count differs from source (${codexSkillsCount}/${sourceCount})`);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
ok: errors.length === 0,
|
|
84
|
+
errors,
|
|
85
|
+
warnings,
|
|
86
|
+
metrics: {
|
|
87
|
+
sourceAgents: sourceCount,
|
|
88
|
+
codexAgents: codexAgentsCount,
|
|
89
|
+
codexSkills: codexSkillsCount,
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function formatHumanReport(result) {
|
|
95
|
+
if (result.ok) {
|
|
96
|
+
const lines = [
|
|
97
|
+
`✅ Codex integration validation passed (agents: ${result.metrics.codexAgents}, skills: ${result.metrics.codexSkills})`,
|
|
98
|
+
];
|
|
99
|
+
if (result.warnings.length > 0) {
|
|
100
|
+
lines.push(...result.warnings.map((w) => `⚠️ ${w}`));
|
|
101
|
+
}
|
|
102
|
+
return lines.join('\n');
|
|
103
|
+
}
|
|
104
|
+
const lines = [
|
|
105
|
+
`❌ Codex integration validation failed (${result.errors.length} issue(s))`,
|
|
106
|
+
...result.errors.map((e) => `- ${e}`),
|
|
107
|
+
];
|
|
108
|
+
if (result.warnings.length > 0) {
|
|
109
|
+
lines.push(...result.warnings.map((w) => `⚠️ ${w}`));
|
|
110
|
+
}
|
|
111
|
+
return lines.join('\n');
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function main() {
|
|
115
|
+
const args = parseArgs();
|
|
116
|
+
const result = validateCodexIntegration(args);
|
|
117
|
+
|
|
118
|
+
if (!args.quiet) {
|
|
119
|
+
if (args.json) {
|
|
120
|
+
console.log(JSON.stringify(result, null, 2));
|
|
121
|
+
} else {
|
|
122
|
+
console.log(formatHumanReport(result));
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (!result.ok) {
|
|
127
|
+
process.exitCode = 1;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (require.main === module) {
|
|
132
|
+
main();
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
module.exports = {
|
|
136
|
+
validateCodexIntegration,
|
|
137
|
+
parseArgs,
|
|
138
|
+
getDefaultOptions,
|
|
139
|
+
countMarkdownFiles,
|
|
140
|
+
countSkillFiles,
|
|
141
|
+
};
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
|
|
7
|
+
function getDefaultOptions() {
|
|
8
|
+
const projectRoot = process.cwd();
|
|
9
|
+
return {
|
|
10
|
+
projectRoot,
|
|
11
|
+
rulesFile: path.join(projectRoot, '.gemini', 'rules.md'),
|
|
12
|
+
agentsDir: path.join(projectRoot, '.gemini', 'rules', 'AIOS', 'agents'),
|
|
13
|
+
commandsDir: path.join(projectRoot, '.gemini', 'commands'),
|
|
14
|
+
extensionDir: path.join(projectRoot, 'packages', 'gemini-aios-extension'),
|
|
15
|
+
sourceAgentsDir: path.join(projectRoot, '.aios-core', 'development', 'agents'),
|
|
16
|
+
quiet: false,
|
|
17
|
+
json: false,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function parseArgs(argv = process.argv.slice(2)) {
|
|
22
|
+
const args = new Set(argv);
|
|
23
|
+
return {
|
|
24
|
+
quiet: args.has('--quiet') || args.has('-q'),
|
|
25
|
+
json: args.has('--json'),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function countMarkdownFiles(dirPath) {
|
|
30
|
+
if (!fs.existsSync(dirPath)) return 0;
|
|
31
|
+
return fs.readdirSync(dirPath).filter((f) => f.endsWith('.md')).length;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function validateGeminiIntegration(options = {}) {
|
|
35
|
+
const projectRoot = options.projectRoot || process.cwd();
|
|
36
|
+
const resolved = {
|
|
37
|
+
...getDefaultOptions(),
|
|
38
|
+
...options,
|
|
39
|
+
projectRoot,
|
|
40
|
+
rulesFile: options.rulesFile || path.join(projectRoot, '.gemini', 'rules.md'),
|
|
41
|
+
agentsDir: options.agentsDir || path.join(projectRoot, '.gemini', 'rules', 'AIOS', 'agents'),
|
|
42
|
+
commandsDir: options.commandsDir || path.join(projectRoot, '.gemini', 'commands'),
|
|
43
|
+
extensionDir: options.extensionDir || path.join(projectRoot, 'packages', 'gemini-aios-extension'),
|
|
44
|
+
sourceAgentsDir: options.sourceAgentsDir || path.join(projectRoot, '.aios-core', 'development', 'agents'),
|
|
45
|
+
};
|
|
46
|
+
const errors = [];
|
|
47
|
+
const warnings = [];
|
|
48
|
+
|
|
49
|
+
if (!fs.existsSync(resolved.rulesFile)) {
|
|
50
|
+
warnings.push(`Gemini rules file not found yet: ${path.relative(resolved.projectRoot, resolved.rulesFile)}`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (!fs.existsSync(resolved.agentsDir)) {
|
|
54
|
+
errors.push(`Missing Gemini agents dir: ${path.relative(resolved.projectRoot, resolved.agentsDir)}`);
|
|
55
|
+
}
|
|
56
|
+
if (!fs.existsSync(resolved.commandsDir)) {
|
|
57
|
+
errors.push(`Missing Gemini commands dir: ${path.relative(resolved.projectRoot, resolved.commandsDir)}`);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const sourceCount = countMarkdownFiles(resolved.sourceAgentsDir);
|
|
61
|
+
const geminiCount = countMarkdownFiles(resolved.agentsDir);
|
|
62
|
+
const commandFiles = fs.existsSync(resolved.commandsDir)
|
|
63
|
+
? fs.readdirSync(resolved.commandsDir).filter((f) => f.endsWith('.toml'))
|
|
64
|
+
: [];
|
|
65
|
+
const expectedCommandCount = sourceCount > 0 ? sourceCount + 1 : 0;
|
|
66
|
+
|
|
67
|
+
if (sourceCount > 0 && commandFiles.length !== expectedCommandCount) {
|
|
68
|
+
warnings.push(`Gemini command count differs from source (${commandFiles.length}/${expectedCommandCount})`);
|
|
69
|
+
}
|
|
70
|
+
if (!commandFiles.includes('aios-menu.toml')) {
|
|
71
|
+
errors.push(`Missing Gemini command file: ${path.relative(resolved.projectRoot, path.join(resolved.commandsDir, 'aios-menu.toml'))}`);
|
|
72
|
+
}
|
|
73
|
+
if (sourceCount > 0 && geminiCount !== sourceCount) {
|
|
74
|
+
warnings.push(`Gemini agent count differs from source (${geminiCount}/${sourceCount})`);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const requiredExtensionFiles = [
|
|
78
|
+
'extension.json',
|
|
79
|
+
'README.md',
|
|
80
|
+
path.join('commands', 'aios-status.js'),
|
|
81
|
+
path.join('commands', 'aios-agents.js'),
|
|
82
|
+
path.join('commands', 'aios-validate.js'),
|
|
83
|
+
path.join('hooks', 'hooks.json'),
|
|
84
|
+
];
|
|
85
|
+
|
|
86
|
+
for (const rel of requiredExtensionFiles) {
|
|
87
|
+
const abs = path.join(resolved.extensionDir, rel);
|
|
88
|
+
if (!fs.existsSync(abs)) {
|
|
89
|
+
errors.push(`Missing Gemini extension file: ${path.relative(resolved.projectRoot, abs)}`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return {
|
|
94
|
+
ok: errors.length === 0,
|
|
95
|
+
errors,
|
|
96
|
+
warnings,
|
|
97
|
+
metrics: {
|
|
98
|
+
sourceAgents: sourceCount,
|
|
99
|
+
geminiAgents: geminiCount,
|
|
100
|
+
geminiCommands: commandFiles.length,
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function formatHumanReport(result) {
|
|
106
|
+
if (result.ok) {
|
|
107
|
+
const lines = [
|
|
108
|
+
`✅ Gemini integration validation passed (agents: ${result.metrics.geminiAgents}, commands: ${result.metrics.geminiCommands})`,
|
|
109
|
+
];
|
|
110
|
+
if (result.warnings.length > 0) {
|
|
111
|
+
lines.push(...result.warnings.map((w) => `⚠️ ${w}`));
|
|
112
|
+
}
|
|
113
|
+
return lines.join('\n');
|
|
114
|
+
}
|
|
115
|
+
const lines = [
|
|
116
|
+
`❌ Gemini integration validation failed (${result.errors.length} issue(s))`,
|
|
117
|
+
...result.errors.map((e) => `- ${e}`),
|
|
118
|
+
];
|
|
119
|
+
if (result.warnings.length > 0) {
|
|
120
|
+
lines.push(...result.warnings.map((w) => `⚠️ ${w}`));
|
|
121
|
+
}
|
|
122
|
+
return lines.join('\n');
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function main() {
|
|
126
|
+
const args = parseArgs();
|
|
127
|
+
const result = validateGeminiIntegration(args);
|
|
128
|
+
|
|
129
|
+
if (!args.quiet) {
|
|
130
|
+
if (args.json) {
|
|
131
|
+
console.log(JSON.stringify(result, null, 2));
|
|
132
|
+
} else {
|
|
133
|
+
console.log(formatHumanReport(result));
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (!result.ok) {
|
|
138
|
+
process.exitCode = 1;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (require.main === module) {
|
|
143
|
+
main();
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
module.exports = {
|
|
147
|
+
validateGeminiIntegration,
|
|
148
|
+
parseArgs,
|
|
149
|
+
getDefaultOptions,
|
|
150
|
+
countMarkdownFiles,
|
|
151
|
+
};
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const { spawnSync } = require('child_process');
|
|
6
|
+
const { validateClaudeIntegration } = require('./validate-claude-integration');
|
|
7
|
+
const { validateCodexIntegration } = require('./validate-codex-integration');
|
|
8
|
+
const { validateGeminiIntegration } = require('./validate-gemini-integration');
|
|
9
|
+
const { validateCodexSkills } = require('./codex-skills-sync/validate');
|
|
10
|
+
const { validatePaths } = require('./validate-paths');
|
|
11
|
+
|
|
12
|
+
function parseArgs(argv = process.argv.slice(2)) {
|
|
13
|
+
const args = new Set(argv);
|
|
14
|
+
return {
|
|
15
|
+
quiet: args.has('--quiet') || args.has('-q'),
|
|
16
|
+
json: args.has('--json'),
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function runSyncValidate(ide, projectRoot) {
|
|
21
|
+
const script = path.join('.aios-core', 'infrastructure', 'scripts', 'ide-sync', 'index.js');
|
|
22
|
+
const result = spawnSync('node', [script, 'validate', '--ide', ide, '--strict'], {
|
|
23
|
+
cwd: projectRoot,
|
|
24
|
+
encoding: 'utf8',
|
|
25
|
+
});
|
|
26
|
+
return {
|
|
27
|
+
ok: result.status === 0,
|
|
28
|
+
errors: result.status === 0 ? [] : [`Sync validation failed for ${ide}`],
|
|
29
|
+
warnings: [],
|
|
30
|
+
raw: result.stdout || result.stderr || '',
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function normalizeResult(input) {
|
|
35
|
+
if (!input || typeof input !== 'object') {
|
|
36
|
+
return { ok: false, errors: ['Validator returned invalid result'], warnings: [] };
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
ok: Boolean(input.ok),
|
|
40
|
+
errors: Array.isArray(input.errors) ? input.errors : [],
|
|
41
|
+
warnings: Array.isArray(input.warnings) ? input.warnings : [],
|
|
42
|
+
metrics: input.metrics || {},
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function runParityValidation(options = {}, deps = {}) {
|
|
47
|
+
const projectRoot = options.projectRoot || process.cwd();
|
|
48
|
+
const runSync = deps.runSyncValidate || runSyncValidate;
|
|
49
|
+
const runClaudeIntegration = deps.validateClaudeIntegration || validateClaudeIntegration;
|
|
50
|
+
const runCodexIntegration = deps.validateCodexIntegration || validateCodexIntegration;
|
|
51
|
+
const runGeminiIntegration = deps.validateGeminiIntegration || validateGeminiIntegration;
|
|
52
|
+
const runCodexSkills = deps.validateCodexSkills || validateCodexSkills;
|
|
53
|
+
const runPaths = deps.validatePaths || validatePaths;
|
|
54
|
+
const checks = [
|
|
55
|
+
{ id: 'claude-sync', exec: () => runSync('claude-code', projectRoot) },
|
|
56
|
+
{ id: 'claude-integration', exec: () => runClaudeIntegration({ projectRoot }) },
|
|
57
|
+
{ id: 'codex-sync', exec: () => runSync('codex', projectRoot) },
|
|
58
|
+
{ id: 'codex-integration', exec: () => runCodexIntegration({ projectRoot }) },
|
|
59
|
+
{ id: 'gemini-sync', exec: () => runSync('gemini', projectRoot) },
|
|
60
|
+
{ id: 'gemini-integration', exec: () => runGeminiIntegration({ projectRoot }) },
|
|
61
|
+
{ id: 'codex-skills', exec: () => runCodexSkills({ projectRoot, strict: true, quiet: true }) },
|
|
62
|
+
{ id: 'paths', exec: () => runPaths({ projectRoot }) },
|
|
63
|
+
];
|
|
64
|
+
|
|
65
|
+
const results = checks.map((check) => {
|
|
66
|
+
const normalized = normalizeResult(check.exec());
|
|
67
|
+
return { id: check.id, ...normalized };
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
ok: results.every((r) => r.ok),
|
|
72
|
+
checks: results,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function formatHumanReport(result) {
|
|
77
|
+
const lines = [];
|
|
78
|
+
for (const check of result.checks) {
|
|
79
|
+
lines.push(`${check.ok ? '✅' : '❌'} ${check.id}`);
|
|
80
|
+
if (check.errors.length > 0) {
|
|
81
|
+
lines.push(...check.errors.map((e) => `- ${e}`));
|
|
82
|
+
}
|
|
83
|
+
if (check.warnings.length > 0) {
|
|
84
|
+
lines.push(...check.warnings.map((w) => `⚠️ ${w}`));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
lines.push('');
|
|
88
|
+
lines.push(result.ok ? '✅ Parity validation passed' : '❌ Parity validation failed');
|
|
89
|
+
return lines.join('\n');
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function main() {
|
|
93
|
+
const args = parseArgs();
|
|
94
|
+
const result = runParityValidation(args);
|
|
95
|
+
|
|
96
|
+
if (!args.quiet) {
|
|
97
|
+
if (args.json) {
|
|
98
|
+
console.log(JSON.stringify(result, null, 2));
|
|
99
|
+
} else {
|
|
100
|
+
console.log(formatHumanReport(result));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (!result.ok) {
|
|
105
|
+
process.exitCode = 1;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (require.main === module) {
|
|
110
|
+
main();
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
module.exports = {
|
|
114
|
+
parseArgs,
|
|
115
|
+
runSyncValidate,
|
|
116
|
+
runParityValidation,
|
|
117
|
+
normalizeResult,
|
|
118
|
+
formatHumanReport,
|
|
119
|
+
};
|