@valentia-ai-skills/framework 2.0.1 → 2.0.2
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/README.md +315 -39
- package/bin/cli.js +244 -1
- package/package.json +1 -1
- package/skills/global/aisupportapp-project-architecture/SKILL.md +1 -1
- package/skills/global/aisupportapp-project-conventions/SKILL.md +1 -1
- package/skills/global/aisupportapp-project-workflows/SKILL.md +1 -1
- package/skills/global/aisupportapp-test-installation/SKILL.md +1 -1
- package/skills/global/api-design/SKILL.md +1 -1
- package/skills/global/appointment-oas-app/SKILL.md +1 -1
- package/skills/global/code-standards/SKILL.md +1 -1
- package/skills/global/codebase-legacy-intelligence/SKILL.md +627 -0
- package/skills/global/project-scanner/SKILL.md +1 -1
- package/skills/global/viteapp-core-workflows/SKILL.md +1 -1
package/bin/cli.js
CHANGED
|
@@ -1217,7 +1217,6 @@ async function cmdAnalyze() {
|
|
|
1217
1217
|
console.log(`Analyzing ${c("bold", filesChanged.length)} file(s) against ${c("bold", skillNames.length)} skill(s)...`);
|
|
1218
1218
|
console.log(c("dim", `Commit: ${commitHash?.slice(0, 8)} — ${commitMessage}`));
|
|
1219
1219
|
console.log(c("dim", `Branch: ${branch}\n`));
|
|
1220
|
-
|
|
1221
1220
|
let projectName = null;
|
|
1222
1221
|
try {
|
|
1223
1222
|
const pkgPath = path.join(PROJECT_ROOT, "package.json");
|
|
@@ -1279,6 +1278,245 @@ async function cmdAnalyze() {
|
|
|
1279
1278
|
}
|
|
1280
1279
|
}
|
|
1281
1280
|
|
|
1281
|
+
// ── Upload Legacy Scan Command ──
|
|
1282
|
+
|
|
1283
|
+
const UPLOAD_LEGACY_SCAN_URL =
|
|
1284
|
+
process.env.AI_SKILLS_UPLOAD_LEGACY_URL ||
|
|
1285
|
+
"https://znshdhjquohrzvbnloki.supabase.co/functions/v1/upload-legacy-scan";
|
|
1286
|
+
|
|
1287
|
+
function fetchJSONWithAuth(url, body, token) {
|
|
1288
|
+
return new Promise((resolve, reject) => {
|
|
1289
|
+
const parsed = new URL(url);
|
|
1290
|
+
const mod = parsed.protocol === "https:" ? https : http;
|
|
1291
|
+
const postData = JSON.stringify(body);
|
|
1292
|
+
|
|
1293
|
+
const headers = {
|
|
1294
|
+
"Content-Type": "application/json",
|
|
1295
|
+
"Content-Length": Buffer.byteLength(postData),
|
|
1296
|
+
};
|
|
1297
|
+
if (token) headers["Authorization"] = `Bearer ${token}`;
|
|
1298
|
+
|
|
1299
|
+
const req = mod.request(
|
|
1300
|
+
{ hostname: parsed.hostname, port: parsed.port, path: parsed.pathname + parsed.search, method: "POST", headers },
|
|
1301
|
+
(res) => {
|
|
1302
|
+
let data = "";
|
|
1303
|
+
res.on("data", (chunk) => (data += chunk));
|
|
1304
|
+
res.on("end", () => {
|
|
1305
|
+
try {
|
|
1306
|
+
const json = JSON.parse(data);
|
|
1307
|
+
if (res.statusCode >= 400) {
|
|
1308
|
+
reject(new Error(json.error || `HTTP ${res.statusCode}`));
|
|
1309
|
+
} else {
|
|
1310
|
+
resolve(json);
|
|
1311
|
+
}
|
|
1312
|
+
} catch {
|
|
1313
|
+
reject(new Error(`Invalid response: ${data.slice(0, 200)}`));
|
|
1314
|
+
}
|
|
1315
|
+
});
|
|
1316
|
+
}
|
|
1317
|
+
);
|
|
1318
|
+
req.on("error", reject);
|
|
1319
|
+
req.write(postData);
|
|
1320
|
+
req.end();
|
|
1321
|
+
});
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
async function cmdUploadLegacyScan() {
|
|
1325
|
+
console.log(c("blue", "\n━━━ AI Skills Framework — Upload Legacy Scan ━━━\n"));
|
|
1326
|
+
|
|
1327
|
+
const args = process.argv.slice(3);
|
|
1328
|
+
let scanPath = null;
|
|
1329
|
+
let authToken = null;
|
|
1330
|
+
let dryRun = false;
|
|
1331
|
+
|
|
1332
|
+
for (let i = 0; i < args.length; i++) {
|
|
1333
|
+
if (args[i] === "--path" && args[i + 1]) { scanPath = args[++i]; }
|
|
1334
|
+
else if (args[i] === "--token" && args[i + 1]) { authToken = args[++i]; }
|
|
1335
|
+
else if (args[i] === "--dry-run") { dryRun = true; }
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1338
|
+
if (!scanPath) {
|
|
1339
|
+
console.log(c("red", "✗ --path is required."));
|
|
1340
|
+
console.log(c("dim", " Usage: npx ai-skills upload-legacy-scan --path ./my-project-intelligence/\n"));
|
|
1341
|
+
process.exit(1);
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
const resolvedPath = path.resolve(scanPath);
|
|
1345
|
+
if (!fs.existsSync(resolvedPath)) {
|
|
1346
|
+
console.log(c("red", `✗ Path not found: ${resolvedPath}`));
|
|
1347
|
+
process.exit(1);
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
// ── Read and validate manifest.json ──
|
|
1351
|
+
const manifestPath = path.join(resolvedPath, "manifest.json");
|
|
1352
|
+
if (!fs.existsSync(manifestPath)) {
|
|
1353
|
+
console.log(c("red", `✗ manifest.json not found in ${resolvedPath}`));
|
|
1354
|
+
console.log(c("dim", " The intelligence folder must contain a manifest.json file.\n"));
|
|
1355
|
+
process.exit(1);
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
let manifest;
|
|
1359
|
+
try {
|
|
1360
|
+
manifest = JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
|
|
1361
|
+
} catch (err) {
|
|
1362
|
+
console.log(c("red", `✗ Failed to parse manifest.json: ${err.message}`));
|
|
1363
|
+
process.exit(1);
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
const missing = ["project", "scanned_at", "skills", "statistics"].filter((k) => !manifest[k]);
|
|
1367
|
+
if (missing.length > 0) {
|
|
1368
|
+
console.log(c("red", `✗ manifest.json missing required fields: ${missing.join(", ")}`));
|
|
1369
|
+
process.exit(1);
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1372
|
+
const projectName = manifest.project;
|
|
1373
|
+
console.log(`Project: ${c("bold", projectName)}`);
|
|
1374
|
+
console.log(`Scanned: ${c("dim", manifest.scanned_at)}`);
|
|
1375
|
+
if (manifest.tech_stack) {
|
|
1376
|
+
const stackParts = Object.entries(manifest.tech_stack)
|
|
1377
|
+
.filter(([, v]) => v)
|
|
1378
|
+
.map(([k, v]) => `${k}: ${v}`)
|
|
1379
|
+
.join(", ");
|
|
1380
|
+
if (stackParts) console.log(`Stack: ${c("cyan", stackParts)}`);
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
// ── Read skill files ──
|
|
1384
|
+
const skillPayload = [];
|
|
1385
|
+
for (const skillEntry of (manifest.skills || [])) {
|
|
1386
|
+
if (!skillEntry.name || !skillEntry.file) continue;
|
|
1387
|
+
const skillFilePath = path.join(resolvedPath, skillEntry.file);
|
|
1388
|
+
if (!fs.existsSync(skillFilePath)) {
|
|
1389
|
+
console.log(c("yellow", ` ⚠ Skill file not found: ${skillEntry.file} — skipping`));
|
|
1390
|
+
continue;
|
|
1391
|
+
}
|
|
1392
|
+
skillPayload.push({
|
|
1393
|
+
name: skillEntry.name,
|
|
1394
|
+
type: skillEntry.type || "module",
|
|
1395
|
+
content: fs.readFileSync(skillFilePath, "utf-8"),
|
|
1396
|
+
});
|
|
1397
|
+
}
|
|
1398
|
+
console.log(`Skills: ${c("green", skillPayload.length.toString())} module(s)`);
|
|
1399
|
+
|
|
1400
|
+
// ── Read diagram files ──
|
|
1401
|
+
const diagramPayload = [];
|
|
1402
|
+
for (const diagramEntry of (manifest.diagrams || [])) {
|
|
1403
|
+
if (!diagramEntry.name || !diagramEntry.file) continue;
|
|
1404
|
+
const diagramFilePath = path.join(resolvedPath, diagramEntry.file);
|
|
1405
|
+
if (!fs.existsSync(diagramFilePath)) {
|
|
1406
|
+
console.log(c("yellow", ` ⚠ Diagram file not found: ${diagramEntry.file} — skipping`));
|
|
1407
|
+
continue;
|
|
1408
|
+
}
|
|
1409
|
+
diagramPayload.push({
|
|
1410
|
+
name: diagramEntry.name,
|
|
1411
|
+
type: diagramEntry.type || "architecture",
|
|
1412
|
+
content: fs.readFileSync(diagramFilePath, "utf-8"),
|
|
1413
|
+
});
|
|
1414
|
+
}
|
|
1415
|
+
console.log(`Diagrams: ${c("green", diagramPayload.length.toString())} file(s)`);
|
|
1416
|
+
|
|
1417
|
+
// ── Read report files ──
|
|
1418
|
+
const reportsPayload = {};
|
|
1419
|
+
const reportFileMap = {
|
|
1420
|
+
overview: ["OVERVIEW.md", "overview.md"],
|
|
1421
|
+
api_registry: ["API_REGISTRY.md", "api_registry.md"],
|
|
1422
|
+
business_rules: ["BUSINESS_RULES.md", "business_rules.md"],
|
|
1423
|
+
data_models: ["DATA_MODELS.md", "data_models.md"],
|
|
1424
|
+
dependencies: ["DEPENDENCIES.md", "dependencies.md"],
|
|
1425
|
+
env_config: ["ENV_CONFIG.md", "env_config.md"],
|
|
1426
|
+
risk_report: ["RISK_REPORT.md", "risk_report.md"],
|
|
1427
|
+
glossary: ["GLOSSARY.md", "glossary.md"],
|
|
1428
|
+
reproduction_guide: ["REPRODUCTION_GUIDE.md", "reproduction_guide.md"],
|
|
1429
|
+
};
|
|
1430
|
+
|
|
1431
|
+
for (const [key, candidates] of Object.entries(reportFileMap)) {
|
|
1432
|
+
for (const candidate of candidates) {
|
|
1433
|
+
const reportPaths = [
|
|
1434
|
+
path.join(resolvedPath, candidate),
|
|
1435
|
+
path.join(resolvedPath, "reports", candidate),
|
|
1436
|
+
];
|
|
1437
|
+
const found = reportPaths.find((p) => fs.existsSync(p));
|
|
1438
|
+
if (found) {
|
|
1439
|
+
reportsPayload[key] = fs.readFileSync(found, "utf-8");
|
|
1440
|
+
break;
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
|
|
1445
|
+
const reportCount = Object.keys(reportsPayload).length;
|
|
1446
|
+
console.log(`Reports: ${c("green", reportCount.toString())} file(s)\n`);
|
|
1447
|
+
|
|
1448
|
+
// ── Dry run: print summary and exit ──
|
|
1449
|
+
if (dryRun) {
|
|
1450
|
+
console.log(c("yellow", "Dry run — payload summary:"));
|
|
1451
|
+
const stats = manifest.statistics || {};
|
|
1452
|
+
console.log(` Project name: ${projectName}`);
|
|
1453
|
+
console.log(` Modules: ${skillPayload.length}`);
|
|
1454
|
+
console.log(` Diagrams: ${diagramPayload.length}`);
|
|
1455
|
+
console.log(` Reports: ${reportCount}`);
|
|
1456
|
+
if (stats.completeness_score !== undefined) {
|
|
1457
|
+
console.log(` Completeness: ${stats.completeness_score}%`);
|
|
1458
|
+
}
|
|
1459
|
+
console.log(c("dim", "\nNo data was uploaded. Remove --dry-run to upload.\n"));
|
|
1460
|
+
return;
|
|
1461
|
+
}
|
|
1462
|
+
|
|
1463
|
+
// ── Resolve auth ──
|
|
1464
|
+
let email = null;
|
|
1465
|
+
if (!authToken) {
|
|
1466
|
+
const config = loadConfig();
|
|
1467
|
+
if (config?.email) {
|
|
1468
|
+
email = config.email;
|
|
1469
|
+
console.log(c("dim", `Using saved email: ${email}`));
|
|
1470
|
+
} else {
|
|
1471
|
+
console.log(c("yellow", "No saved config found. Enter your email to authenticate."));
|
|
1472
|
+
email = await ask(`${c("bold", "Work email:")} `);
|
|
1473
|
+
const otpResult = await requestOtpForEmail(email);
|
|
1474
|
+
email = otpResult.email;
|
|
1475
|
+
await verifyOtp(email); // we only need the OTP verification, not the toolkit
|
|
1476
|
+
}
|
|
1477
|
+
}
|
|
1478
|
+
|
|
1479
|
+
// ── Upload ──
|
|
1480
|
+
console.log(c("dim", `Uploading to Valentia (${projectName})...\n`));
|
|
1481
|
+
process.stdout.write(` ${c("dim", "→")} Sending ${skillPayload.length} skills, ${diagramPayload.length} diagrams, ${reportCount} reports...`);
|
|
1482
|
+
|
|
1483
|
+
let result;
|
|
1484
|
+
try {
|
|
1485
|
+
result = await fetchJSONWithAuth(
|
|
1486
|
+
UPLOAD_LEGACY_SCAN_URL,
|
|
1487
|
+
{
|
|
1488
|
+
project_name: projectName,
|
|
1489
|
+
manifest,
|
|
1490
|
+
skills: skillPayload,
|
|
1491
|
+
diagrams: diagramPayload,
|
|
1492
|
+
reports: reportsPayload,
|
|
1493
|
+
email,
|
|
1494
|
+
},
|
|
1495
|
+
authToken
|
|
1496
|
+
);
|
|
1497
|
+
console.log(c("green", " done!\n"));
|
|
1498
|
+
} catch (err) {
|
|
1499
|
+
console.log(c("red", " failed!"));
|
|
1500
|
+
console.log(c("red", `\n✗ Upload failed: ${err.message}`));
|
|
1501
|
+
console.log(c("dim", " Retry with --dry-run to validate your payload without uploading.\n"));
|
|
1502
|
+
process.exit(1);
|
|
1503
|
+
}
|
|
1504
|
+
|
|
1505
|
+
console.log(c("green", "✓ Upload complete!\n"));
|
|
1506
|
+
console.log(` Project ID: ${c("bold", result.project_id)}`);
|
|
1507
|
+
console.log(` Skills created: ${c("bold", (result.skills_created || []).length.toString())}`);
|
|
1508
|
+
if (result.storage_urls?.diagrams?.length) {
|
|
1509
|
+
console.log(` Diagrams stored: ${c("dim", result.storage_urls.diagrams.length.toString())}`);
|
|
1510
|
+
}
|
|
1511
|
+
if (result.storage_urls?.reports?.length) {
|
|
1512
|
+
console.log(` Reports stored: ${c("dim", result.storage_urls.reports.length.toString())}`);
|
|
1513
|
+
}
|
|
1514
|
+
if (result.console_url) {
|
|
1515
|
+
console.log(`\n View in console: ${c("bold", result.console_url)}`);
|
|
1516
|
+
}
|
|
1517
|
+
console.log("");
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1282
1520
|
// ── Main ──
|
|
1283
1521
|
|
|
1284
1522
|
const command = process.argv[2] || "setup";
|
|
@@ -1290,6 +1528,7 @@ switch (command) {
|
|
|
1290
1528
|
case "list": cmdList(); break;
|
|
1291
1529
|
case "doctor": cmdDoctor(); break;
|
|
1292
1530
|
case "analyze": cmdAnalyze(); break;
|
|
1531
|
+
case "upload-legacy-scan": cmdUploadLegacyScan(); break;
|
|
1293
1532
|
case "help": case "--help": case "-h":
|
|
1294
1533
|
console.log(`
|
|
1295
1534
|
${c("blue", "AI Skills Framework")} — @valentia-ai-skills/framework
|
|
@@ -1300,10 +1539,14 @@ Usage:
|
|
|
1300
1539
|
npx ai-skills status Show installed toolkit, team, and tools
|
|
1301
1540
|
npx ai-skills list List locally bundled skills
|
|
1302
1541
|
npx ai-skills analyze Analyze last commit against active skills
|
|
1542
|
+
npx ai-skills upload-legacy-scan Upload a legacy codebase intelligence package
|
|
1303
1543
|
npx ai-skills doctor Health check (config + API + tools)
|
|
1304
1544
|
|
|
1305
1545
|
Flags:
|
|
1306
1546
|
analyze --last Analyze the most recent commit
|
|
1547
|
+
upload-legacy-scan --path <dir> Path to intelligence folder
|
|
1548
|
+
upload-legacy-scan --dry-run Validate payload without uploading
|
|
1549
|
+
upload-legacy-scan --token <tok> Explicit auth token
|
|
1307
1550
|
|
|
1308
1551
|
Toolkit includes: skills, agents, commands, hooks, rules, MCP configs.
|
|
1309
1552
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@valentia-ai-skills/framework",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.2",
|
|
4
4
|
"description": "AI development skills framework centralized coding standards, security patterns, and SOPs for AI-assisted development. Works with Claude Code, Cursor, Copilot, Windsurf, and any AI coding tool.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai-skills",
|