@supa-magic/spm 0.2.3 → 0.2.4
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/bin/spm.js +44 -16
- package/package.json +6 -2
package/dist/bin/spm.js
CHANGED
|
@@ -3,7 +3,7 @@ import { Command } from "commander";
|
|
|
3
3
|
import { existsSync, readFileSync, writeFileSync, mkdirSync, unlinkSync, readdirSync, statSync } from "node:fs";
|
|
4
4
|
import { join, dirname, relative, resolve, normalize } from "node:path";
|
|
5
5
|
import { parse, stringify } from "yaml";
|
|
6
|
-
import { execSync, spawn } from "node:child_process";
|
|
6
|
+
import { execFileSync, execSync, spawn } from "node:child_process";
|
|
7
7
|
import { readFile, mkdir, writeFile, rm } from "node:fs/promises";
|
|
8
8
|
import { tmpdir } from "node:os";
|
|
9
9
|
import { createHash } from "node:crypto";
|
|
@@ -20,10 +20,28 @@ const knownProviders = {
|
|
|
20
20
|
codeium: ".codeium",
|
|
21
21
|
cody: ".cody"
|
|
22
22
|
};
|
|
23
|
+
const cliCommands = {
|
|
24
|
+
claude: "claude",
|
|
25
|
+
aider: "aider"
|
|
26
|
+
};
|
|
27
|
+
const hasCliInstalled = (command) => {
|
|
28
|
+
try {
|
|
29
|
+
execFileSync(command, ["--version"], {
|
|
30
|
+
stdio: "ignore",
|
|
31
|
+
timeout: 5e3,
|
|
32
|
+
shell: process.platform === "win32"
|
|
33
|
+
});
|
|
34
|
+
return true;
|
|
35
|
+
} catch {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
};
|
|
23
39
|
const detectProviders = (root) => Object.entries(knownProviders).reduce(
|
|
24
40
|
(providers, [name, path]) => {
|
|
25
|
-
const
|
|
26
|
-
|
|
41
|
+
const hasDir = existsSync(join(root, path));
|
|
42
|
+
const cli = cliCommands[name];
|
|
43
|
+
const hasCli = cli ? hasCliInstalled(cli) : false;
|
|
44
|
+
if (hasDir || hasCli) {
|
|
27
45
|
providers[name] = { path };
|
|
28
46
|
}
|
|
29
47
|
return providers;
|
|
@@ -436,7 +454,7 @@ const spawnClaude = (instructionsFilePath, stepper, providerDir, model) => new P
|
|
|
436
454
|
return;
|
|
437
455
|
}
|
|
438
456
|
if (state.setupReached || currentStepHeader.startsWith("Cleaning")) return;
|
|
439
|
-
const context = stepItems.length
|
|
457
|
+
const context = stepItems.length > 0 ? stepItems.join(", ") : void 0;
|
|
440
458
|
stepper.succeed(base, context);
|
|
441
459
|
};
|
|
442
460
|
stepper.start("Analyzing existing setup...", "skills");
|
|
@@ -477,9 +495,11 @@ const spawnClaude = (instructionsFilePath, stepper, providerDir, model) => new P
|
|
|
477
495
|
state.setupReached = true;
|
|
478
496
|
}
|
|
479
497
|
stepper.start(trimmed, stepCategory(trimmed));
|
|
480
|
-
} else
|
|
498
|
+
} else {
|
|
481
499
|
stepItems.push(trimmed);
|
|
482
|
-
|
|
500
|
+
if (currentStepHeader.startsWith("Integrating")) {
|
|
501
|
+
stepper.item(trimmed);
|
|
502
|
+
}
|
|
483
503
|
}
|
|
484
504
|
});
|
|
485
505
|
}
|
|
@@ -800,7 +820,10 @@ const renderTree = (node, prefix = "") => node.children.flatMap((child, i) => {
|
|
|
800
820
|
});
|
|
801
821
|
const stripProviderPrefix = (file, prefix) => {
|
|
802
822
|
const norm = prefix.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
803
|
-
|
|
823
|
+
if (file.startsWith(`${norm}/`)) return file.slice(norm.length + 1);
|
|
824
|
+
const bare = norm.replace(/^\./, "");
|
|
825
|
+
if (bare && file.startsWith(`${bare}/`)) return file.slice(bare.length + 1);
|
|
826
|
+
return file;
|
|
804
827
|
};
|
|
805
828
|
const printSummary = (files, providerPath) => {
|
|
806
829
|
if (files.length === 0) return;
|
|
@@ -847,20 +870,26 @@ const registerInstallCommand = (program2) => {
|
|
|
847
870
|
}
|
|
848
871
|
const projectRoot = getProjectRoot();
|
|
849
872
|
const downloadDir = join(projectRoot, ".spm", skillset.name);
|
|
850
|
-
const
|
|
851
|
-
|
|
873
|
+
const toTargetPath = (entry) => {
|
|
874
|
+
if (entry.type === "skill" && entry.skillName) {
|
|
875
|
+
const fileName = entry.path.split("/").pop() ?? entry.path;
|
|
876
|
+
return `skills/${entry.skillName}/${fileName}`;
|
|
877
|
+
}
|
|
878
|
+
const skillsetDir = location.path.replace(/\/[^/]+$/, "");
|
|
879
|
+
return entry.path.startsWith(`${skillsetDir}/`) ? entry.path.slice(skillsetDir.length + 1) : entry.path;
|
|
880
|
+
};
|
|
852
881
|
stepper.start(`Downloading ${entries.length} file(s)...`, "packages");
|
|
853
882
|
const results = await downloadEntries(
|
|
854
883
|
entries,
|
|
855
884
|
location,
|
|
856
|
-
(type, path) => stepper.item(`${type} ${
|
|
885
|
+
(type, path) => stepper.item(`${type} ${path.split("/").pop() ?? path}`)
|
|
857
886
|
);
|
|
858
887
|
const setupResults = results.filter((r) => r.type === "setup");
|
|
859
888
|
const installResults = results.filter((r) => r.type !== "setup");
|
|
860
889
|
await Promise.all(
|
|
861
890
|
installResults.map(async (result2) => {
|
|
862
|
-
const
|
|
863
|
-
const filePath = safePath(downloadDir,
|
|
891
|
+
const target = toTargetPath(result2);
|
|
892
|
+
const filePath = safePath(downloadDir, target);
|
|
864
893
|
await mkdir(dirname(filePath), { recursive: true });
|
|
865
894
|
await writeFile(filePath, result2.content, "utf-8");
|
|
866
895
|
})
|
|
@@ -868,13 +897,12 @@ const registerInstallCommand = (program2) => {
|
|
|
868
897
|
let setupFile;
|
|
869
898
|
if (setupResults.length > 0) {
|
|
870
899
|
const setup = setupResults[0];
|
|
871
|
-
|
|
872
|
-
setupFile = join(downloadDir, relative2);
|
|
900
|
+
setupFile = join(downloadDir, "SETUP.md");
|
|
873
901
|
await mkdir(dirname(setupFile), { recursive: true });
|
|
874
902
|
await writeFile(setupFile, setup.content, "utf-8");
|
|
875
903
|
}
|
|
876
904
|
stepper.succeed(`Downloaded ${results.length} file(s)`);
|
|
877
|
-
const downloadedPaths = installResults.map((r) =>
|
|
905
|
+
const downloadedPaths = installResults.map((r) => toTargetPath(r));
|
|
878
906
|
const providerFullPath = join(projectRoot, provider.path);
|
|
879
907
|
const pruned = pruneUnchanged(downloadDir, providerFullPath);
|
|
880
908
|
if (pruned > 0) {
|
|
@@ -932,7 +960,7 @@ const banner = (version2) => [
|
|
|
932
960
|
`${shade}▝▜${light}████▛▘ ${reset$1}${dim}v${version2} ✨ supa-magic${reset$1}`,
|
|
933
961
|
`${shade} ▘${light} ▝`
|
|
934
962
|
].join("\n");
|
|
935
|
-
const version = "0.2.
|
|
963
|
+
const version = "0.2.4";
|
|
936
964
|
const program = new Command();
|
|
937
965
|
const gray = "\x1B[90m";
|
|
938
966
|
const reset = "\x1B[0m";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@supa-magic/spm",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4",
|
|
4
4
|
"description": "CLI tool for managing AI skillsets",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"contributors": [
|
|
@@ -28,11 +28,14 @@
|
|
|
28
28
|
},
|
|
29
29
|
"scripts": {
|
|
30
30
|
"build": "vite build",
|
|
31
|
+
"install": "npm install @supa-magic/spm",
|
|
32
|
+
"link": "npm link",
|
|
31
33
|
"pretest": "npm run build",
|
|
32
34
|
"start": "clear && claude --dangerously-skip-permissions",
|
|
33
35
|
"test": "vitest run",
|
|
34
36
|
"test:watch": "vitest",
|
|
35
|
-
"
|
|
37
|
+
"un": "npm uninstall @supa-magic/spm",
|
|
38
|
+
"unlink": "npm unlink spm"
|
|
36
39
|
},
|
|
37
40
|
"devDependencies": {
|
|
38
41
|
"@biomejs/biome": "2.4.6",
|
|
@@ -43,6 +46,7 @@
|
|
|
43
46
|
"vitest": "^4.0.18"
|
|
44
47
|
},
|
|
45
48
|
"dependencies": {
|
|
49
|
+
"@supa-magic/spm": "^0.2.3",
|
|
46
50
|
"commander": "^14.0.3",
|
|
47
51
|
"yaml": "^2.8.2"
|
|
48
52
|
}
|