@smicolon/ai-kit 0.5.0 → 0.5.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/dist/index.js +151 -18
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -268,10 +268,7 @@ function resolvePack(plugin, marketplaceDir) {
|
|
|
268
268
|
if (fs2.existsSync(rulesDir)) {
|
|
269
269
|
rules = fs2.readdirSync(rulesDir).filter((f) => f.endsWith(".md")).map((f) => path2.join(rulesDir, f));
|
|
270
270
|
}
|
|
271
|
-
const skills = (plugin.skills ?? []).map((s) =>
|
|
272
|
-
const skillMdPath = path2.resolve(sourceDir, s);
|
|
273
|
-
return path2.dirname(skillMdPath);
|
|
274
|
-
});
|
|
271
|
+
const skills = (plugin.skills ?? []).map((s) => path2.resolve(sourceDir, s));
|
|
275
272
|
return {
|
|
276
273
|
name: plugin.name,
|
|
277
274
|
version: plugin.version,
|
|
@@ -682,6 +679,114 @@ function isSymlink(p3) {
|
|
|
682
679
|
return false;
|
|
683
680
|
}
|
|
684
681
|
}
|
|
682
|
+
function findOrphans(pack, projectDir) {
|
|
683
|
+
const orphans = [];
|
|
684
|
+
const canonicalBase = path5.join(projectDir, CANONICAL_SKILLS_DIR);
|
|
685
|
+
for (const sourceSkillDir of pack.skills) {
|
|
686
|
+
const skillName = path5.basename(sourceSkillDir);
|
|
687
|
+
const canonicalDest = path5.join(canonicalBase, skillName);
|
|
688
|
+
if (skillDirMatchesSource(canonicalDest, sourceSkillDir)) {
|
|
689
|
+
orphans.push(canonicalDest);
|
|
690
|
+
}
|
|
691
|
+
for (const toolId of TOOL_IDS) {
|
|
692
|
+
const toolSkillsDir = TOOL_REGISTRY[toolId].components.skills;
|
|
693
|
+
if (!toolSkillsDir) continue;
|
|
694
|
+
const linkPath = path5.join(projectDir, toolSkillsDir, skillName);
|
|
695
|
+
if (path5.resolve(linkPath) === path5.resolve(canonicalDest)) continue;
|
|
696
|
+
if (symlinkPointsInto(linkPath, canonicalDest)) {
|
|
697
|
+
orphans.push(linkPath);
|
|
698
|
+
} else if (!isSymlink(linkPath) && skillDirMatchesSource(linkPath, sourceSkillDir)) {
|
|
699
|
+
orphans.push(linkPath);
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
const collectFiles = (sourceFiles, component) => {
|
|
704
|
+
for (const sourceFile of sourceFiles) {
|
|
705
|
+
const base = path5.basename(sourceFile);
|
|
706
|
+
for (const toolId of TOOL_IDS) {
|
|
707
|
+
const dir = TOOL_REGISTRY[toolId].components[component];
|
|
708
|
+
if (!dir) continue;
|
|
709
|
+
let destPath;
|
|
710
|
+
let expected;
|
|
711
|
+
if (component === "rules" && toolId === "cursor") {
|
|
712
|
+
const mdcName = path5.basename(sourceFile, ".md") + ".mdc";
|
|
713
|
+
destPath = path5.join(projectDir, dir, mdcName);
|
|
714
|
+
expected = Buffer.from(convertToMdc(sourceFile, pack.name));
|
|
715
|
+
if (fileContentEquals(destPath, expected)) orphans.push(destPath);
|
|
716
|
+
const legacyPath = path5.join(projectDir, dir, base);
|
|
717
|
+
try {
|
|
718
|
+
const legacyExpected = fs6.readFileSync(sourceFile);
|
|
719
|
+
if (fileContentEquals(legacyPath, legacyExpected)) {
|
|
720
|
+
orphans.push(legacyPath);
|
|
721
|
+
}
|
|
722
|
+
} catch {
|
|
723
|
+
}
|
|
724
|
+
continue;
|
|
725
|
+
}
|
|
726
|
+
destPath = path5.join(projectDir, dir, base);
|
|
727
|
+
try {
|
|
728
|
+
expected = fs6.readFileSync(sourceFile);
|
|
729
|
+
} catch {
|
|
730
|
+
continue;
|
|
731
|
+
}
|
|
732
|
+
if (fileContentEquals(destPath, expected)) {
|
|
733
|
+
orphans.push(destPath);
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
};
|
|
738
|
+
collectFiles(pack.agents, "agents");
|
|
739
|
+
collectFiles(pack.commands, "commands");
|
|
740
|
+
collectFiles(pack.rules, "rules");
|
|
741
|
+
return [...new Set(orphans)];
|
|
742
|
+
}
|
|
743
|
+
function fileContentEquals(filePath, expected) {
|
|
744
|
+
try {
|
|
745
|
+
if (!fs6.existsSync(filePath) || isSymlink(filePath)) return false;
|
|
746
|
+
const stat = fs6.statSync(filePath);
|
|
747
|
+
if (!stat.isFile()) return false;
|
|
748
|
+
return fs6.readFileSync(filePath).equals(expected);
|
|
749
|
+
} catch {
|
|
750
|
+
return false;
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
function symlinkPointsInto(linkPath, expectedDir) {
|
|
754
|
+
try {
|
|
755
|
+
if (!isSymlink(linkPath)) return false;
|
|
756
|
+
const resolved = path5.resolve(path5.dirname(linkPath), fs6.readlinkSync(linkPath));
|
|
757
|
+
return path5.resolve(resolved) === path5.resolve(expectedDir);
|
|
758
|
+
} catch {
|
|
759
|
+
return false;
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
function skillDirMatchesSource(destDir, sourceDir) {
|
|
763
|
+
if (!fs6.existsSync(destDir) || isSymlink(destDir)) return false;
|
|
764
|
+
if (!fs6.statSync(destDir).isDirectory()) return false;
|
|
765
|
+
const sourceFiles = collectRelativeFiles(sourceDir);
|
|
766
|
+
const destFiles = collectRelativeFiles(destDir);
|
|
767
|
+
if (sourceFiles.length !== destFiles.length) return false;
|
|
768
|
+
const sourceSet = new Set(sourceFiles);
|
|
769
|
+
for (const f of destFiles) if (!sourceSet.has(f)) return false;
|
|
770
|
+
for (const rel of sourceFiles) {
|
|
771
|
+
const src = fs6.readFileSync(path5.join(sourceDir, rel));
|
|
772
|
+
const dst = fs6.readFileSync(path5.join(destDir, rel));
|
|
773
|
+
if (!src.equals(dst)) return false;
|
|
774
|
+
}
|
|
775
|
+
return true;
|
|
776
|
+
}
|
|
777
|
+
function collectRelativeFiles(dir, base = dir) {
|
|
778
|
+
const out = [];
|
|
779
|
+
if (!fs6.existsSync(dir)) return out;
|
|
780
|
+
for (const entry of fs6.readdirSync(dir, { withFileTypes: true })) {
|
|
781
|
+
const full = path5.join(dir, entry.name);
|
|
782
|
+
if (entry.isDirectory()) {
|
|
783
|
+
out.push(...collectRelativeFiles(full, base));
|
|
784
|
+
} else if (entry.isFile()) {
|
|
785
|
+
out.push(path5.relative(base, full));
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
return out.sort();
|
|
789
|
+
}
|
|
685
790
|
|
|
686
791
|
// src/global-config.ts
|
|
687
792
|
import fs7 from "fs";
|
|
@@ -1021,30 +1126,58 @@ ${pc3.dim(`${packs.length} packs available`)}`);
|
|
|
1021
1126
|
import { Command as Command4 } from "commander";
|
|
1022
1127
|
import path9 from "path";
|
|
1023
1128
|
import pc4 from "picocolors";
|
|
1024
|
-
var removeCommand = new Command4("remove").description("Remove a pack from your project").argument("<pack>", "Pack name to remove").option("--cwd <dir>", "Project directory").action((packName, opts) => {
|
|
1129
|
+
var removeCommand = new Command4("remove").description("Remove a pack from your project").argument("<pack>", "Pack name to remove").option("--cwd <dir>", "Project directory").action(async (packName, opts) => {
|
|
1025
1130
|
const projectDir = opts.cwd ? path9.resolve(opts.cwd) : process.cwd();
|
|
1026
1131
|
const config = readConfig(projectDir);
|
|
1027
|
-
|
|
1028
|
-
|
|
1132
|
+
const packConfig = config?.packs[packName];
|
|
1133
|
+
const trackedFiles2 = packConfig?.files ?? [];
|
|
1134
|
+
if (trackedFiles2.length > 0) {
|
|
1135
|
+
const removed2 = removePack2(projectDir, trackedFiles2);
|
|
1136
|
+
console.log(pc4.green(`Removed ${removed2} file(s) for ${packName}`));
|
|
1137
|
+
writeConfig(projectDir, removePack(config, packName));
|
|
1138
|
+
console.log(pc4.dim("Config updated."));
|
|
1029
1139
|
return;
|
|
1030
1140
|
}
|
|
1031
|
-
const
|
|
1032
|
-
if (!
|
|
1141
|
+
const pack = await findPack(packName);
|
|
1142
|
+
if (!pack) {
|
|
1143
|
+
const installed = config && Object.keys(config.packs).length > 0 ? "\nInstalled: " + Object.keys(config.packs).join(", ") : "";
|
|
1033
1144
|
console.error(
|
|
1034
|
-
pc4.red(`Pack "${packName}" is not installed
|
|
1145
|
+
pc4.red(`Pack "${packName}" is not installed and not found in the marketplace.`) + installed
|
|
1035
1146
|
);
|
|
1036
1147
|
process.exit(1);
|
|
1037
1148
|
}
|
|
1038
|
-
const
|
|
1039
|
-
if (
|
|
1040
|
-
console.log(pc4.yellow(
|
|
1149
|
+
const orphans = findOrphans(pack, projectDir);
|
|
1150
|
+
if (packConfig) {
|
|
1151
|
+
console.log(pc4.yellow(
|
|
1152
|
+
`"${packName}" has no tracked files in .ai-kit.json (legacy install).`
|
|
1153
|
+
));
|
|
1154
|
+
}
|
|
1155
|
+
if (orphans.length === 0) {
|
|
1156
|
+
if (packConfig) {
|
|
1157
|
+
writeConfig(projectDir, removePack(config, packName));
|
|
1158
|
+
console.log(pc4.dim("Config entry removed; nothing to clean on disk."));
|
|
1159
|
+
} else {
|
|
1160
|
+
console.log(pc4.dim(`Nothing to remove for "${packName}" \u2014 no tracked entry and no orphan files found.`));
|
|
1161
|
+
}
|
|
1162
|
+
return;
|
|
1163
|
+
}
|
|
1164
|
+
if (!packConfig) {
|
|
1165
|
+
console.log(pc4.yellow(
|
|
1166
|
+
`"${packName}" not in .ai-kit.json \u2014 found ${orphans.length} orphan file(s) to clean up:`
|
|
1167
|
+
));
|
|
1041
1168
|
} else {
|
|
1042
|
-
|
|
1043
|
-
|
|
1169
|
+
console.log(pc4.yellow(`Found ${orphans.length} orphan file(s):`));
|
|
1170
|
+
}
|
|
1171
|
+
for (const p3 of orphans) {
|
|
1172
|
+
console.log(pc4.dim(" " + path9.relative(projectDir, p3)));
|
|
1173
|
+
}
|
|
1174
|
+
const relPaths = orphans.map((p3) => path9.relative(projectDir, p3));
|
|
1175
|
+
const removed = removePack2(projectDir, relPaths);
|
|
1176
|
+
console.log(pc4.green(`Removed ${removed} orphan file(s) for ${packName}`));
|
|
1177
|
+
if (packConfig) {
|
|
1178
|
+
writeConfig(projectDir, removePack(config, packName));
|
|
1179
|
+
console.log(pc4.dim("Config updated."));
|
|
1044
1180
|
}
|
|
1045
|
-
const updated = removePack(config, packName);
|
|
1046
|
-
writeConfig(projectDir, updated);
|
|
1047
|
-
console.log(pc4.dim("Config updated."));
|
|
1048
1181
|
});
|
|
1049
1182
|
|
|
1050
1183
|
// src/commands/update.ts
|