sophia-code
Version:
Production-ready agentic CLI code editor with AI-powered coding assistance, planning, and multi-agent delegation. Enterprise-grade security and reliability.
270 lines (230 loc) ⢠7.5 kB
JavaScript
/**
* Pre-publish validation script for sophia-code NPM package
* Ensures the package is ready for production deployment
*/
const fs = require('fs');
const path = require('path');
const { execSync } = require('child_process');
const chalk = require('chalk');
function log(message, color = 'white') {
console.log(chalk[color](message));
}
function checkFileExists(filePath, description) {
if (fs.existsSync(filePath)) {
log(`ā
${description}: ${filePath}`, 'green');
return true;
} else {
log(`ā ${description} missing: ${filePath}`, 'red');
return false;
}
}
function validatePackageJson() {
log('\nš¦ Validating package.json...', 'cyan');
const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8'));
let valid = true;
// Check version is 1.0.0+
if (!pkg.version || !pkg.version.startsWith('1.')) {
log('ā Version should be 1.0.0 or higher for production', 'red');
valid = false;
} else {
log(`ā
Version: ${pkg.version}`, 'green');
}
// Check required fields
const requiredFields = ['name', 'description', 'bin', 'engines', 'files'];
for (const field of requiredFields) {
if (!pkg[field]) {
log(`ā Missing required field: ${field}`, 'red');
valid = false;
} else {
log(`ā
${field}: present`, 'green');
}
}
// Check that all dependencies are pinned (no ^ or ~)
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
const unpinned = Object.entries(deps).filter(([name, version]) =>
version.includes('^') || version.includes('~')
);
if (unpinned.length > 0) {
log('ā Unpinned dependencies found:', 'red');
unpinned.forEach(([name, version]) => {
log(` - ${name}: ${version}`, 'red');
});
valid = false;
} else {
log('ā
All dependencies pinned', 'green');
}
return valid;
}
function validateFiles() {
log('\nš Validating required files...', 'cyan');
const requiredFiles = [
['README.md', 'README'],
['PRODUCTION_READY.md', 'Production documentation'],
['CLAUDE.md', 'Claude Code documentation'],
['AGENTS.md', 'Agent system documentation'],
['package.json', 'Package configuration'],
['setup.sh', 'Setup script'],
['requirements.txt', 'Python requirements'],
['src/cli.py', 'Main CLI module'],
['src/groq_client.py', 'Groq client'],
['config/security.py', 'Security configuration'],
['scripts/postinstall.js', 'Post-install script']
];
let allValid = true;
for (const [file, desc] of requiredFiles) {
if (!checkFileExists(file, desc)) {
allValid = false;
}
}
return allValid;
}
function validateDirectories() {
log('\nš Validating directory structure...', 'cyan');
const requiredDirs = [
['src', 'Source code'],
['config', 'Configuration'],
['plugins', 'Plugin system'],
['tests', 'Test suite'],
['scripts', 'Scripts'],
['bin', 'Executables'],
['lib', 'Library']
];
let allValid = true;
for (const [dir, desc] of requiredDirs) {
if (fs.existsSync(dir) && fs.statSync(dir).isDirectory()) {
log(`ā
${desc}: ${dir}/`, 'green');
} else {
log(`ā ${desc} directory missing: ${dir}/`, 'red');
allValid = false;
}
}
return allValid;
}
function checkGitStatus() {
log('\nš Checking git status...', 'cyan');
try {
const status = execSync('git status --porcelain', { encoding: 'utf8' });
if (status.trim()) {
log('ā Uncommitted changes found:', 'red');
log(status, 'yellow');
return false;
} else {
log('ā
No uncommitted changes', 'green');
return true;
}
} catch (error) {
log('ā Failed to check git status', 'red');
return false;
}
}
function validateReadme() {
log('\nš Validating README.md...', 'cyan');
const readme = fs.readFileSync('README.md', 'utf8');
const requiredSections = [
'Features',
'Quick Start',
'Installation',
'Production',
'Sophia'
];
let allValid = true;
for (const section of requiredSections) {
if (readme.toLowerCase().includes(section.toLowerCase())) {
log(`ā
Section found: ${section}`, 'green');
} else {
log(`ā Section missing: ${section}`, 'red');
allValid = false;
}
}
// Check if it mentions v1.0.0 or production
if (readme.includes('1.0.0') || readme.toLowerCase().includes('production')) {
log('ā
Production version mentioned', 'green');
} else {
log('ā ļø Consider mentioning production readiness', 'yellow');
}
return allValid;
}
function runSecurityCheck() {
log('\nš Running security checks...', 'cyan');
try {
// Check for obvious secrets in package files
const sensitiveFiles = ['package.json', 'scripts/postinstall.js'];
const sensitivePatterns = [
/gsk_[a-zA-Z0-9_-]{32,}/g, // Groq API key
/sk-[a-zA-Z0-9]{48,}/g, // OpenAI key
/ghp_[a-zA-Z0-9]{36}/g, // GitHub token
];
let issuesFound = false;
for (const file of sensitiveFiles) {
if (fs.existsSync(file)) {
const content = fs.readFileSync(file, 'utf8');
for (const pattern of sensitivePatterns) {
if (pattern.test(content)) {
log(`ā Potential secret found in ${file}`, 'red');
issuesFound = true;
}
}
}
}
if (!issuesFound) {
log('ā
No secrets found in package files', 'green');
}
return !issuesFound;
} catch (error) {
log('ā ļø Security check failed, but continuing', 'yellow');
return true;
}
}
function main() {
log('š Sophia NPM Package Pre-Publish Validation', 'cyan');
log('=' .repeat(50), 'cyan');
const checks = [
['Package.json', validatePackageJson],
['Required Files', validateFiles],
['Directory Structure', validateDirectories],
['Git Status', checkGitStatus],
['README', validateReadme],
['Security', runSecurityCheck]
];
let allPassed = true;
const results = {};
for (const [checkName, checkFn] of checks) {
try {
const passed = checkFn();
results[checkName] = passed;
allPassed = allPassed && passed;
} catch (error) {
log(`ā ${checkName} check failed: ${error.message}`, 'red');
results[checkName] = false;
allPassed = false;
}
}
// Summary
log('\n' + '=' .repeat(50), 'cyan');
log('š NPM PACKAGE VALIDATION SUMMARY', 'cyan');
log('=' .repeat(50), 'cyan');
for (const [checkName, passed] of Object.entries(results)) {
const status = passed ? 'ā
PASS' : 'ā FAIL';
log(`${status.padEnd(10)} ${checkName}`);
}
if (allPassed) {
log('\nš ALL CHECKS PASSED - Package ready for NPM publish!', 'green');
log('\nš” Next steps:', 'cyan');
log(' 1. npm publish --dry-run (test publish)', 'white');
log(' 2. npm publish (actual publish)', 'white');
log(' 3. Verify at: https://www.npmjs.com/package/sophia-code', 'white');
process.exit(0);
} else {
log('\nā ļø SOME CHECKS FAILED - Please fix issues before publishing', 'red');
const failedChecks = Object.entries(results)
.filter(([_, passed]) => !passed)
.map(([name, _]) => name);
log(` Failed: ${failedChecks.join(', ')}`, 'red');
process.exit(1);
}
}
if (require.main === module) {
main();
}
module.exports = { validatePackageJson, validateFiles, checkGitStatus };