feat(coding-agent): package deduplication and collision detection

- Package deduplication: same package in global+project, project wins
- Collision detection for skills, prompts, and themes with ResourceCollision type
- PathMetadata tracking with parent directory lookup for file paths
- Display improvements: section headers, sorted groups, accent colors for packages
- pi list shows full paths below package names
- Extension loader discovers files in directories without index.ts
- In-memory SettingsManager properly tracks project settings

fixes #645
This commit is contained in:
Mario Zechner 2026-01-24 00:35:19 +01:00
parent c5c515f560
commit 50c8323590
18 changed files with 738 additions and 389 deletions

View file

@ -190,11 +190,22 @@ async function handlePackageCommand(args: string[]): Promise<boolean> {
return true;
}
const formatPackage = (pkg: (typeof globalPackages)[number], scope: "global" | "project") => {
const source = typeof pkg === "string" ? pkg : pkg.source;
const filtered = typeof pkg === "object";
const display = filtered ? `${source} (filtered)` : source;
console.log(` ${display}`);
// Show resolved path
const path = packageManager.getInstalledPath(source, scope);
if (path) {
console.log(chalk.dim(` ${path}`));
}
};
if (globalPackages.length > 0) {
console.log(chalk.bold("Global packages:"));
for (const pkg of globalPackages) {
const display = typeof pkg === "string" ? pkg : `${pkg.source} (filtered)`;
console.log(` ${display}`);
formatPackage(pkg, "global");
}
}
@ -202,8 +213,7 @@ async function handlePackageCommand(args: string[]): Promise<boolean> {
if (globalPackages.length > 0) console.log();
console.log(chalk.bold("Project packages:"));
for (const pkg of projectPackages) {
const display = typeof pkg === "string" ? pkg : `${pkg.source} (filtered)`;
console.log(` ${display}`);
formatPackage(pkg, "project");
}
}