mirror of
https://github.com/luckyyzh/pi-agent-integrated.git
synced 2026-10-03 02:59:35 +00:00
103 lines
3.9 KiB
JavaScript
103 lines
3.9 KiB
JavaScript
import { cpSync, existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
|
|
import { homedir } from "node:os";
|
|
import { isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
import { agentDir, ensureProfile, rootDir, skillsHomeDir } from "./profile.mjs";
|
|
|
|
const args = process.argv.slice(2);
|
|
const force = args.includes("--force");
|
|
const dryRun = args.includes("--dry-run");
|
|
|
|
function valueAfter(flag, fallback) {
|
|
const index = args.indexOf(flag);
|
|
return index >= 0 && args[index + 1] ? resolve(args[index + 1]) : fallback;
|
|
}
|
|
|
|
const sourceAgentDir = valueAfter("--from", join(homedir(), ".pi", "agent"));
|
|
const sourceAgentsDir = valueAfter("--skills-from", join(homedir(), ".agents"));
|
|
|
|
ensureProfile({ quiet: true });
|
|
|
|
function copyContents(source, destination, label) {
|
|
if (!existsSync(source)) {
|
|
console.log(`Skip ${label}: source does not exist (${source})`);
|
|
return { copied: 0, skipped: 0 };
|
|
}
|
|
|
|
mkdirSync(destination, { recursive: true });
|
|
let copied = 0;
|
|
let skipped = 0;
|
|
for (const entry of readdirSync(source, { withFileTypes: true })) {
|
|
const sourcePath = join(source, entry.name);
|
|
const destinationPath = join(destination, entry.name);
|
|
if (existsSync(destinationPath) && !force) {
|
|
skipped++;
|
|
console.log(`Skip existing: ${destinationPath}`);
|
|
continue;
|
|
}
|
|
console.log(`${dryRun ? "Would copy" : "Copy"}: ${sourcePath} -> ${destinationPath}`);
|
|
if (!dryRun) cpSync(sourcePath, destinationPath, { recursive: true, force });
|
|
copied++;
|
|
}
|
|
return { copied, skipped };
|
|
}
|
|
|
|
function isInsideApp(path) {
|
|
const rel = relative(rootDir, resolve(path));
|
|
return rel === "" || (rel !== ".." && !rel.startsWith(`..${sep}`) && !isAbsolute(rel));
|
|
}
|
|
|
|
function isRemotePackageSource(source) {
|
|
return source.startsWith("npm:") || source.startsWith("git:") || /^[a-z]+:\/\//i.test(source);
|
|
}
|
|
|
|
function sanitizeMigratedSettings() {
|
|
const settingsPath = join(agentDir, "settings.json");
|
|
if (!existsSync(settingsPath)) return { changed: false, removed: [] };
|
|
|
|
let settings;
|
|
try {
|
|
settings = JSON.parse(readFileSync(settingsPath, "utf8"));
|
|
} catch {
|
|
console.log(`Skip settings cleanup: invalid JSON (${settingsPath})`);
|
|
return { changed: false, removed: [] };
|
|
}
|
|
if (!settings || typeof settings !== "object" || Array.isArray(settings)) {
|
|
console.log(`Skip settings cleanup: expected a JSON object (${settingsPath})`);
|
|
return { changed: false, removed: [] };
|
|
}
|
|
|
|
const removed = [];
|
|
for (const key of ["extensions", "skills", "prompts", "themes"]) {
|
|
if (!Array.isArray(settings[key])) continue;
|
|
settings[key] = settings[key].filter((entry) => {
|
|
if (typeof entry !== "string" || !isAbsolute(entry) || isInsideApp(entry)) return true;
|
|
removed.push(`${key}:${entry}`);
|
|
return false;
|
|
});
|
|
}
|
|
|
|
if (Array.isArray(settings.packages)) {
|
|
settings.packages = settings.packages.filter((entry) => {
|
|
const source = typeof entry === "string" ? entry : entry?.source;
|
|
if (typeof source !== "string" || isRemotePackageSource(source)) return true;
|
|
const resolvedSource = resolve(agentDir, source);
|
|
if (isInsideApp(resolvedSource)) return true;
|
|
removed.push(`packages:${source}`);
|
|
return false;
|
|
});
|
|
}
|
|
|
|
if (removed.length > 0) {
|
|
console.log(`Remove ${removed.length} external resource reference(s) from migrated settings.`);
|
|
for (const entry of removed) console.log(` ${entry}`);
|
|
if (!dryRun) writeFileSync(settingsPath, `${JSON.stringify(settings, null, 2)}\n`, "utf8");
|
|
}
|
|
return { changed: removed.length > 0, removed };
|
|
}
|
|
|
|
const agentResult = copyContents(sourceAgentDir, agentDir, "Pi agent profile");
|
|
const skillsResult = copyContents(sourceAgentsDir, join(skillsHomeDir, ".agents"), "global skills profile");
|
|
const settingsCleanup = sanitizeMigratedSettings();
|
|
|
|
console.log(JSON.stringify({ dryRun, force, agent: agentResult, skills: skillsResult, settingsCleanup }, null, 2));
|