* chore: upgrade maintenance scripts to robust PyYAML parsing - Replaces fragile regex frontmatter parsing with PyYAML/yaml library - Ensures multi-line descriptions and complex characters are handled safely - Normalizes quoting and field ordering across all maintenance scripts - Updates validator to strictly enforce description quality * fix: restore and refine truncated skill descriptions - Recovered 223+ truncated descriptions from git history (6.5.0 regression) - Refined long descriptions into concise, complete sentences (<200 chars) - Added missing descriptions for brainstorming and orchestration skills - Manually fixed imagen skill description - Resolved dangling links in competitor-alternatives skill * chore: sync generated registry files and document fixes - Regenerated skills index with normalized forward-slash paths - Updated README and CATALOG to reflect restored descriptions - Documented restoration and script improvements in CHANGELOG.md * fix: restore missing skill and align metadata for full 955 count - Renamed SKILL.MD to SKILL.md in andruia-skill-smith to ensure indexing - Fixed risk level and missing section in andruia-skill-smith - Synchronized all registry files for final 955 skill count * chore(scripts): add cross-platform runners and hermetic test orchestration * fix(scripts): harden utf-8 output and clone target writeability * fix(skills): add missing date metadata for strict validation * chore(index): sync generated metadata dates * fix(catalog): normalize skill paths to prevent CI drift * chore: sync generated registry files * fix: enforce LF line endings for generated registry files
91 lines
2.0 KiB
JavaScript
91 lines
2.0 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
'use strict';
|
|
|
|
const { spawn, spawnSync } = require('node:child_process');
|
|
|
|
const args = process.argv.slice(2);
|
|
|
|
if (args.length === 0) {
|
|
console.error('Usage: node scripts/run-python.js <script.py> [args...]');
|
|
process.exit(1);
|
|
}
|
|
|
|
function uniqueCandidates(candidates) {
|
|
const seen = new Set();
|
|
const unique = [];
|
|
|
|
for (const candidate of candidates) {
|
|
const key = candidate.join('\u0000');
|
|
if (!seen.has(key)) {
|
|
seen.add(key);
|
|
unique.push(candidate);
|
|
}
|
|
}
|
|
|
|
return unique;
|
|
}
|
|
|
|
function getPythonCandidates() {
|
|
// Optional override for CI/local pinning without editing scripts.
|
|
const configuredPython =
|
|
process.env.ANTIGRAVITY_PYTHON || process.env.npm_config_python;
|
|
const candidates = [
|
|
configuredPython ? [configuredPython] : null,
|
|
// Keep this ordered list easy to update if project requirements change.
|
|
['python3'],
|
|
['python'],
|
|
['py', '-3'],
|
|
].filter(Boolean);
|
|
|
|
return uniqueCandidates(candidates);
|
|
}
|
|
|
|
function canRun(candidate) {
|
|
const [command, ...baseArgs] = candidate;
|
|
const probe = spawnSync(
|
|
command,
|
|
[...baseArgs, '-c', 'import sys; raise SystemExit(0 if sys.version_info[0] == 3 else 1)'],
|
|
{
|
|
stdio: 'ignore',
|
|
shell: false,
|
|
},
|
|
);
|
|
|
|
return probe.error == null && probe.status === 0;
|
|
}
|
|
|
|
const pythonCandidates = getPythonCandidates();
|
|
const selected = pythonCandidates.find(canRun);
|
|
|
|
if (!selected) {
|
|
console.error(
|
|
'Unable to find a Python 3 interpreter. Tried: python3, python, py -3',
|
|
);
|
|
process.exit(1);
|
|
}
|
|
|
|
const [command, ...baseArgs] = selected;
|
|
const child = spawn(command, [...baseArgs, ...args], {
|
|
stdio: 'inherit',
|
|
shell: false,
|
|
});
|
|
|
|
child.on('error', (error) => {
|
|
console.error(`Failed to start Python interpreter "${command}": ${error.message}`);
|
|
process.exit(1);
|
|
});
|
|
|
|
child.on('exit', (code, signal) => {
|
|
if (signal) {
|
|
try {
|
|
process.kill(process.pid, signal);
|
|
} catch {
|
|
process.exit(1);
|
|
}
|
|
return;
|
|
}
|
|
|
|
process.exit(code ?? 1);
|
|
});
|