@kurokeita/add-skill 1.18.0 → 1.20.0
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 +30 -0
- package/dist/bin/cli.js +623 -166
- package/dist/skills/statusline-setup/SKILL.md +479 -0
- package/package.json +1 -1
package/dist/bin/cli.js
CHANGED
|
@@ -1,27 +1,27 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// bin/cli.ts
|
|
4
|
-
import { intro as intro5, isCancel as
|
|
4
|
+
import { intro as intro5, isCancel as isCancel6, outro as outro5, select as select6 } from "@clack/prompts";
|
|
5
5
|
import { Command } from "commander";
|
|
6
|
-
import
|
|
6
|
+
import pc6 from "picocolors";
|
|
7
7
|
|
|
8
8
|
// src/commands/add.ts
|
|
9
|
-
import
|
|
9
|
+
import os4 from "node:os";
|
|
10
10
|
import path6 from "node:path";
|
|
11
11
|
import {
|
|
12
12
|
autocompleteMultiselect,
|
|
13
13
|
cancel,
|
|
14
14
|
confirm,
|
|
15
15
|
intro,
|
|
16
|
-
isCancel,
|
|
16
|
+
isCancel as isCancel2,
|
|
17
17
|
multiselect,
|
|
18
|
-
note,
|
|
18
|
+
note as note2,
|
|
19
19
|
outro,
|
|
20
|
-
select,
|
|
20
|
+
select as select2,
|
|
21
21
|
spinner
|
|
22
22
|
} from "@clack/prompts";
|
|
23
23
|
import fs4 from "fs-extra";
|
|
24
|
-
import
|
|
24
|
+
import pc2 from "picocolors";
|
|
25
25
|
|
|
26
26
|
// src/utils/github.ts
|
|
27
27
|
import os from "node:os";
|
|
@@ -148,12 +148,50 @@ var PLATFORM_PATHS_WORKFLOWS = {
|
|
|
148
148
|
gemini: path2.join(os2.homedir(), ".gemini/commands"),
|
|
149
149
|
windsurf: path2.join(os2.homedir(), ".codeium/windsurf/global_workflows")
|
|
150
150
|
};
|
|
151
|
+
var PROJECT_RELATIVE_PLATFORM_PATHS_SKILLS = {
|
|
152
|
+
antigravity: ".gemini/antigravity/skills",
|
|
153
|
+
"claude-code": ".claude/skills",
|
|
154
|
+
codex: ".codex/skills",
|
|
155
|
+
copilot: ".copilot/skills",
|
|
156
|
+
gemini: ".gemini/skills",
|
|
157
|
+
windsurf: ".codeium/windsurf/skills"
|
|
158
|
+
};
|
|
159
|
+
var PROJECT_RELATIVE_PLATFORM_PATHS_AGENTS = {
|
|
160
|
+
"claude-code": ".claude/agents",
|
|
161
|
+
codex: ".codex/skills",
|
|
162
|
+
copilot: ".copilot/agents",
|
|
163
|
+
gemini: ".gemini/agents"
|
|
164
|
+
};
|
|
165
|
+
var PROJECT_RELATIVE_PLATFORM_PATHS_WORKFLOWS = {
|
|
166
|
+
antigravity: ".gemini/antigravity/workflows",
|
|
167
|
+
"claude-code": ".claude/skills",
|
|
168
|
+
codex: ".codex/skills",
|
|
169
|
+
copilot: ".copilot/prompts",
|
|
170
|
+
gemini: ".gemini/commands",
|
|
171
|
+
windsurf: ".codeium/windsurf/workflows"
|
|
172
|
+
};
|
|
173
|
+
function resolveProjectPaths(relative, root) {
|
|
174
|
+
const out = {};
|
|
175
|
+
for (const [platform, rel] of Object.entries(relative)) {
|
|
176
|
+
if (rel) out[platform] = path2.join(root, rel);
|
|
177
|
+
}
|
|
178
|
+
return out;
|
|
179
|
+
}
|
|
180
|
+
function getProjectPlatformPathsSkills(root) {
|
|
181
|
+
return resolveProjectPaths(PROJECT_RELATIVE_PLATFORM_PATHS_SKILLS, root);
|
|
182
|
+
}
|
|
183
|
+
function getProjectPlatformPathsAgents(root) {
|
|
184
|
+
return resolveProjectPaths(PROJECT_RELATIVE_PLATFORM_PATHS_AGENTS, root);
|
|
185
|
+
}
|
|
186
|
+
function getProjectPlatformPathsWorkflows(root) {
|
|
187
|
+
return resolveProjectPaths(PROJECT_RELATIVE_PLATFORM_PATHS_WORKFLOWS, root);
|
|
188
|
+
}
|
|
151
189
|
var TYPE_DIRS = {
|
|
152
190
|
skill: path2.join(PROJECT_ROOT, "skills"),
|
|
153
191
|
agent: path2.join(PROJECT_ROOT, "agents"),
|
|
154
192
|
workflow: path2.join(PROJECT_ROOT, "workflows")
|
|
155
193
|
};
|
|
156
|
-
function
|
|
194
|
+
function getGlobalTargetPaths(type) {
|
|
157
195
|
switch (type) {
|
|
158
196
|
case "agent":
|
|
159
197
|
return PLATFORM_PATHS_AGENTS;
|
|
@@ -163,6 +201,23 @@ function getTargetPaths(type) {
|
|
|
163
201
|
return PLATFORM_PATHS_SKILLS;
|
|
164
202
|
}
|
|
165
203
|
}
|
|
204
|
+
function getProjectTargetPaths(type, root) {
|
|
205
|
+
switch (type) {
|
|
206
|
+
case "agent":
|
|
207
|
+
return getProjectPlatformPathsAgents(root);
|
|
208
|
+
case "workflow":
|
|
209
|
+
return getProjectPlatformPathsWorkflows(root);
|
|
210
|
+
default:
|
|
211
|
+
return getProjectPlatformPathsSkills(root);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
function getTargetPaths(type, scope = "global", projectRoot) {
|
|
215
|
+
if (scope === "project") {
|
|
216
|
+
const root = projectRoot ?? process.cwd();
|
|
217
|
+
return getProjectTargetPaths(type, root);
|
|
218
|
+
}
|
|
219
|
+
return getGlobalTargetPaths(type);
|
|
220
|
+
}
|
|
166
221
|
|
|
167
222
|
// src/utils/platforms/antigravity.ts
|
|
168
223
|
var AntigravityHandler = class {
|
|
@@ -683,6 +738,197 @@ function enableAutocompleteMultiSelectShiftAToggle() {
|
|
|
683
738
|
autocompletePromptPrototype.__aiManagerShiftAPatched__ = true;
|
|
684
739
|
}
|
|
685
740
|
|
|
741
|
+
// src/utils/scope-prompt.ts
|
|
742
|
+
import { isCancel, note, select } from "@clack/prompts";
|
|
743
|
+
import pc from "picocolors";
|
|
744
|
+
|
|
745
|
+
// src/utils/scope.ts
|
|
746
|
+
import { execFileSync } from "node:child_process";
|
|
747
|
+
import os3 from "node:os";
|
|
748
|
+
function isInsideGitWorkTree(cwd) {
|
|
749
|
+
try {
|
|
750
|
+
const out = execFileSync("git", ["rev-parse", "--is-inside-work-tree"], {
|
|
751
|
+
cwd,
|
|
752
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
753
|
+
encoding: "utf-8"
|
|
754
|
+
});
|
|
755
|
+
return out.trim() === "true";
|
|
756
|
+
} catch {
|
|
757
|
+
return false;
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
function resolveScope(scope, options = {}) {
|
|
761
|
+
const cwd = options.cwd ?? process.cwd();
|
|
762
|
+
if (scope === "global") {
|
|
763
|
+
return { ok: true, scope: "global", root: options.homedir ?? os3.homedir() };
|
|
764
|
+
}
|
|
765
|
+
const home = options.homedir ?? os3.homedir();
|
|
766
|
+
if (cwd === home) {
|
|
767
|
+
return { ok: false, reason: "homedir" };
|
|
768
|
+
}
|
|
769
|
+
const gitCheck = options.gitCheck ?? isInsideGitWorkTree;
|
|
770
|
+
if (!gitCheck(cwd)) {
|
|
771
|
+
return { ok: false, reason: "not-a-git-repo" };
|
|
772
|
+
}
|
|
773
|
+
return { ok: true, scope: "project", root: cwd };
|
|
774
|
+
}
|
|
775
|
+
function describeRefusal(reason) {
|
|
776
|
+
switch (reason) {
|
|
777
|
+
case "homedir":
|
|
778
|
+
return "Project scope is not allowed when the current directory is your home directory.";
|
|
779
|
+
case "not-a-git-repo":
|
|
780
|
+
return "Project scope requires running inside a git repository.";
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
// src/utils/scope-prompt.ts
|
|
785
|
+
function isValidScopeFlag(value) {
|
|
786
|
+
return value === "global" || value === "project";
|
|
787
|
+
}
|
|
788
|
+
function isValidListRemoveScopeFlag(value) {
|
|
789
|
+
return value === "global" || value === "project" || value === "both";
|
|
790
|
+
}
|
|
791
|
+
async function pickScope(initial) {
|
|
792
|
+
const result = await select({
|
|
793
|
+
message: "Choose install scope:",
|
|
794
|
+
options: [
|
|
795
|
+
{
|
|
796
|
+
label: "Global",
|
|
797
|
+
value: "global",
|
|
798
|
+
hint: "install under your home directory (default)"
|
|
799
|
+
},
|
|
800
|
+
{
|
|
801
|
+
label: "Project",
|
|
802
|
+
value: "project",
|
|
803
|
+
hint: "install under the current project directory"
|
|
804
|
+
}
|
|
805
|
+
],
|
|
806
|
+
initialValue: initial
|
|
807
|
+
});
|
|
808
|
+
if (isCancel(result)) return "__cancel__";
|
|
809
|
+
return result;
|
|
810
|
+
}
|
|
811
|
+
async function offerGlobalFallback(reason) {
|
|
812
|
+
note(reason, "Project scope unavailable");
|
|
813
|
+
const choice = await select({
|
|
814
|
+
message: "What would you like to do?",
|
|
815
|
+
options: [
|
|
816
|
+
{ label: "Install at global scope instead", value: "global" },
|
|
817
|
+
{ label: "Cancel", value: "cancel" }
|
|
818
|
+
],
|
|
819
|
+
initialValue: "global"
|
|
820
|
+
});
|
|
821
|
+
if (isCancel(choice) || choice === "cancel") return "__cancel__";
|
|
822
|
+
return "global";
|
|
823
|
+
}
|
|
824
|
+
async function chooseInstallScope(options = {}) {
|
|
825
|
+
const requested = options.flag ?? await pickScope(options.initial ?? "global");
|
|
826
|
+
if (requested === "__cancel__") return { cancelled: true };
|
|
827
|
+
const resolution = resolveScope(requested, options);
|
|
828
|
+
if (resolution.ok) {
|
|
829
|
+
return {
|
|
830
|
+
cancelled: false,
|
|
831
|
+
scope: resolution.scope,
|
|
832
|
+
root: resolution.root
|
|
833
|
+
};
|
|
834
|
+
}
|
|
835
|
+
if (options.flag === "project") {
|
|
836
|
+
const reason = describeRefusal(resolution.reason);
|
|
837
|
+
note(pc.yellow(reason), "Project scope unavailable");
|
|
838
|
+
return { rejected: true, reason };
|
|
839
|
+
}
|
|
840
|
+
const fallback = await offerGlobalFallback(describeRefusal(resolution.reason));
|
|
841
|
+
if (fallback === "__cancel__") return { cancelled: true };
|
|
842
|
+
const fallbackResolution = resolveScope("global", options);
|
|
843
|
+
if (!fallbackResolution.ok) return { cancelled: true };
|
|
844
|
+
return {
|
|
845
|
+
cancelled: false,
|
|
846
|
+
scope: fallbackResolution.scope,
|
|
847
|
+
root: fallbackResolution.root
|
|
848
|
+
};
|
|
849
|
+
}
|
|
850
|
+
async function pickListRemoveScope(action) {
|
|
851
|
+
const verb = action === "list" ? "List" : "Remove from";
|
|
852
|
+
const result = await select({
|
|
853
|
+
message: `Choose scope to ${action === "list" ? "list" : "remove from"}:`,
|
|
854
|
+
options: [
|
|
855
|
+
{
|
|
856
|
+
label: "Global",
|
|
857
|
+
value: "global",
|
|
858
|
+
hint: `${verb} items installed under your home directory`
|
|
859
|
+
},
|
|
860
|
+
{
|
|
861
|
+
label: "Project",
|
|
862
|
+
value: "project",
|
|
863
|
+
hint: `${verb} items under the current project`
|
|
864
|
+
},
|
|
865
|
+
{
|
|
866
|
+
label: "Both",
|
|
867
|
+
value: "both",
|
|
868
|
+
hint: `${verb} items at both scopes`
|
|
869
|
+
}
|
|
870
|
+
],
|
|
871
|
+
initialValue: "global"
|
|
872
|
+
});
|
|
873
|
+
if (isCancel(result)) return "__cancel__";
|
|
874
|
+
return result;
|
|
875
|
+
}
|
|
876
|
+
async function offerGlobalOnlyFallback(reason, action) {
|
|
877
|
+
note(reason, "Project scope unavailable");
|
|
878
|
+
const verb = action === "list" ? "List" : "Remove from";
|
|
879
|
+
const choice = await select({
|
|
880
|
+
message: "What would you like to do?",
|
|
881
|
+
options: [
|
|
882
|
+
{ label: `${verb} global scope only`, value: "global" },
|
|
883
|
+
{ label: "Cancel", value: "cancel" }
|
|
884
|
+
],
|
|
885
|
+
initialValue: "global"
|
|
886
|
+
});
|
|
887
|
+
if (isCancel(choice) || choice === "cancel") return "__cancel__";
|
|
888
|
+
return "global";
|
|
889
|
+
}
|
|
890
|
+
async function chooseListRemoveScope(options) {
|
|
891
|
+
const requested = options.flag ?? await pickListRemoveScope(options.action);
|
|
892
|
+
if (requested === "__cancel__") return { cancelled: true };
|
|
893
|
+
if (requested === "global") {
|
|
894
|
+
const resolved = resolveScope("global", options);
|
|
895
|
+
if (!resolved.ok) return { cancelled: true };
|
|
896
|
+
return {
|
|
897
|
+
cancelled: false,
|
|
898
|
+
scope: "global",
|
|
899
|
+
homedir: resolved.root
|
|
900
|
+
};
|
|
901
|
+
}
|
|
902
|
+
const projectResolution = resolveScope("project", options);
|
|
903
|
+
if (projectResolution.ok) {
|
|
904
|
+
const homeResolution2 = resolveScope("global", options);
|
|
905
|
+
if (!homeResolution2.ok) return { cancelled: true };
|
|
906
|
+
return {
|
|
907
|
+
cancelled: false,
|
|
908
|
+
scope: requested,
|
|
909
|
+
homedir: homeResolution2.root,
|
|
910
|
+
projectRoot: projectResolution.root
|
|
911
|
+
};
|
|
912
|
+
}
|
|
913
|
+
if (options.flag) {
|
|
914
|
+
const reason = describeRefusal(projectResolution.reason);
|
|
915
|
+
note(pc.yellow(reason), "Project scope unavailable");
|
|
916
|
+
return { rejected: true, reason };
|
|
917
|
+
}
|
|
918
|
+
const fallback = await offerGlobalOnlyFallback(
|
|
919
|
+
describeRefusal(projectResolution.reason),
|
|
920
|
+
options.action
|
|
921
|
+
);
|
|
922
|
+
if (fallback === "__cancel__") return { cancelled: true };
|
|
923
|
+
const homeResolution = resolveScope("global", options);
|
|
924
|
+
if (!homeResolution.ok) return { cancelled: true };
|
|
925
|
+
return {
|
|
926
|
+
cancelled: false,
|
|
927
|
+
scope: "global",
|
|
928
|
+
homedir: homeResolution.root
|
|
929
|
+
};
|
|
930
|
+
}
|
|
931
|
+
|
|
686
932
|
// src/commands/add.ts
|
|
687
933
|
enableAutocompleteMultiSelectShiftAToggle();
|
|
688
934
|
function getPlatformOptions(type) {
|
|
@@ -690,7 +936,7 @@ function getPlatformOptions(type) {
|
|
|
690
936
|
return Object.entries(paths).filter(([_, pathStr]) => !!pathStr).map(([platform, pathStr]) => ({
|
|
691
937
|
label: PLATFORM_LABELS[platform],
|
|
692
938
|
value: platform,
|
|
693
|
-
hint: pathStr.replace(
|
|
939
|
+
hint: pathStr.replace(os4.homedir(), "~")
|
|
694
940
|
}));
|
|
695
941
|
}
|
|
696
942
|
async function installItem(itemName, overwrite, sourcePath, targetBaseDir) {
|
|
@@ -707,13 +953,13 @@ async function installItem(itemName, overwrite, sourcePath, targetBaseDir) {
|
|
|
707
953
|
}
|
|
708
954
|
async function add(type, url, options) {
|
|
709
955
|
if (!options?.skipIntro) {
|
|
710
|
-
intro(
|
|
956
|
+
intro(pc2.bgCyan(pc2.black(" AI Manager : Add ")));
|
|
711
957
|
}
|
|
712
958
|
let currentType = type;
|
|
713
959
|
const isSingleShot = !!type || !!url;
|
|
714
960
|
while (true) {
|
|
715
961
|
if (!currentType && !url) {
|
|
716
|
-
const selectedType = await
|
|
962
|
+
const selectedType = await select2({
|
|
717
963
|
message: "What would you like to add?",
|
|
718
964
|
options: [
|
|
719
965
|
{ label: "Skills", value: "skill" },
|
|
@@ -722,7 +968,7 @@ async function add(type, url, options) {
|
|
|
722
968
|
{ label: "Exit", value: "exit" }
|
|
723
969
|
]
|
|
724
970
|
});
|
|
725
|
-
if (
|
|
971
|
+
if (isCancel2(selectedType) || selectedType === "exit") {
|
|
726
972
|
break;
|
|
727
973
|
}
|
|
728
974
|
currentType = selectedType;
|
|
@@ -743,7 +989,7 @@ async function add(type, url, options) {
|
|
|
743
989
|
const result = await fetchSkillFromGitHub(url);
|
|
744
990
|
tempDir = result.tempDir;
|
|
745
991
|
selectedItems = [result.skillName];
|
|
746
|
-
s2.stop(
|
|
992
|
+
s2.stop(pc2.green(`Fetched ${normalizedType}: ${result.skillName}`));
|
|
747
993
|
} catch (e) {
|
|
748
994
|
s2.error("Failed to fetch");
|
|
749
995
|
throw e;
|
|
@@ -760,7 +1006,7 @@ async function add(type, url, options) {
|
|
|
760
1006
|
(entry) => entry.isDirectory() || (normalizedType === "workflow" || normalizedType === "agent") && entry.isFile() && entry.name.endsWith(".md")
|
|
761
1007
|
).map((entry) => ({ label: entry.name, value: entry.name })).sort((a, b) => a.label.localeCompare(b.label));
|
|
762
1008
|
if (availableItems.length === 0) {
|
|
763
|
-
|
|
1009
|
+
note2(`No ${normalizedType}s found in directory.`, "Information");
|
|
764
1010
|
if (isSingleShot) break;
|
|
765
1011
|
currentType = void 0;
|
|
766
1012
|
continue;
|
|
@@ -769,14 +1015,13 @@ async function add(type, url, options) {
|
|
|
769
1015
|
message: `Select ${normalizedType}s to install:`,
|
|
770
1016
|
options: availableItems
|
|
771
1017
|
});
|
|
772
|
-
if (
|
|
1018
|
+
if (isCancel2(items)) {
|
|
773
1019
|
if (isSingleShot) break;
|
|
774
1020
|
currentType = void 0;
|
|
775
1021
|
continue;
|
|
776
1022
|
}
|
|
777
1023
|
selectedItems = items;
|
|
778
1024
|
}
|
|
779
|
-
const supportedPaths = getTargetPaths(normalizedType);
|
|
780
1025
|
const currentPlatformOptions = getPlatformOptions(normalizedType);
|
|
781
1026
|
if (currentPlatformOptions.length === 0) {
|
|
782
1027
|
cancel(`No supported platforms found for type '${normalizedType}'.`);
|
|
@@ -788,13 +1033,46 @@ async function add(type, url, options) {
|
|
|
788
1033
|
options: currentPlatformOptions,
|
|
789
1034
|
required: true
|
|
790
1035
|
});
|
|
791
|
-
if (
|
|
1036
|
+
if (isCancel2(platforms)) {
|
|
792
1037
|
if (tempDir) await fs4.remove(tempDir);
|
|
793
1038
|
if (isSingleShot) break;
|
|
794
1039
|
currentType = void 0;
|
|
795
1040
|
continue;
|
|
796
1041
|
}
|
|
797
1042
|
const selectedPlatforms = platforms;
|
|
1043
|
+
const scopeChoice = await chooseInstallScope({ flag: options?.scope });
|
|
1044
|
+
if ("rejected" in scopeChoice) {
|
|
1045
|
+
if (tempDir) await fs4.remove(tempDir);
|
|
1046
|
+
cancel(
|
|
1047
|
+
`Scope --scope=${options?.scope} unavailable: ${scopeChoice.reason}`
|
|
1048
|
+
);
|
|
1049
|
+
process.exit(1);
|
|
1050
|
+
return;
|
|
1051
|
+
}
|
|
1052
|
+
if (scopeChoice.cancelled) {
|
|
1053
|
+
if (tempDir) await fs4.remove(tempDir);
|
|
1054
|
+
if (isSingleShot) break;
|
|
1055
|
+
currentType = void 0;
|
|
1056
|
+
continue;
|
|
1057
|
+
}
|
|
1058
|
+
const { scope: chosenScope, root: scopeRoot } = scopeChoice;
|
|
1059
|
+
const supportedPaths = getTargetPaths(
|
|
1060
|
+
normalizedType,
|
|
1061
|
+
chosenScope,
|
|
1062
|
+
scopeRoot
|
|
1063
|
+
);
|
|
1064
|
+
if (chosenScope === "project") {
|
|
1065
|
+
const proceed = await confirm({
|
|
1066
|
+
message: `Install to project scope at ${pc2.cyan(scopeRoot)}?`,
|
|
1067
|
+
initialValue: true
|
|
1068
|
+
});
|
|
1069
|
+
if (isCancel2(proceed) || !proceed) {
|
|
1070
|
+
if (tempDir) await fs4.remove(tempDir);
|
|
1071
|
+
if (isSingleShot) break;
|
|
1072
|
+
currentType = void 0;
|
|
1073
|
+
continue;
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
798
1076
|
const existingItems = [];
|
|
799
1077
|
for (const platform of selectedPlatforms) {
|
|
800
1078
|
const targetBase = supportedPaths[platform];
|
|
@@ -811,15 +1089,15 @@ async function add(type, url, options) {
|
|
|
811
1089
|
let overwrite = true;
|
|
812
1090
|
if (existingItems.length > 0) {
|
|
813
1091
|
const limit = 5;
|
|
814
|
-
const displayList = existingItems.slice(0, limit).map((e) => `${
|
|
1092
|
+
const displayList = existingItems.slice(0, limit).map((e) => `${pc2.bold(e.item)} (${e.platform})`).join(", ");
|
|
815
1093
|
const remainder = existingItems.length - limit;
|
|
816
1094
|
const message = remainder > 0 ? `The following items already exist: ${displayList}, and ${remainder} others.` : `The following items already exist: ${displayList}.`;
|
|
817
|
-
|
|
1095
|
+
note2(message, "Attention");
|
|
818
1096
|
const shouldOverwrite = await confirm({
|
|
819
1097
|
message: "Do you want to overwrite existing items?",
|
|
820
1098
|
initialValue: false
|
|
821
1099
|
});
|
|
822
|
-
if (
|
|
1100
|
+
if (isCancel2(shouldOverwrite)) {
|
|
823
1101
|
if (tempDir) await fs4.remove(tempDir);
|
|
824
1102
|
if (isSingleShot) break;
|
|
825
1103
|
currentType = void 0;
|
|
@@ -827,8 +1105,8 @@ async function add(type, url, options) {
|
|
|
827
1105
|
}
|
|
828
1106
|
overwrite = shouldOverwrite;
|
|
829
1107
|
}
|
|
830
|
-
|
|
831
|
-
`Installing ${selectedItems.length} ${normalizedType}s to ${selectedPlatforms.length} platforms...`,
|
|
1108
|
+
note2(
|
|
1109
|
+
`Installing ${selectedItems.length} ${normalizedType}s to ${selectedPlatforms.length} platforms (scope: ${chosenScope})...`,
|
|
832
1110
|
"Summary"
|
|
833
1111
|
);
|
|
834
1112
|
const s = spinner();
|
|
@@ -840,7 +1118,7 @@ async function add(type, url, options) {
|
|
|
840
1118
|
const targetBase = supportedPaths[platform];
|
|
841
1119
|
if (!targetBase) continue;
|
|
842
1120
|
for (const item of selectedItems) {
|
|
843
|
-
const message = `Installing ${
|
|
1121
|
+
const message = `Installing ${pc2.bold(item)} to ${pc2.cyan(platform)}...`;
|
|
844
1122
|
s.message(message);
|
|
845
1123
|
try {
|
|
846
1124
|
let currentSourcePath;
|
|
@@ -903,17 +1181,17 @@ async function add(type, url, options) {
|
|
|
903
1181
|
}
|
|
904
1182
|
if (errors.length > 0) {
|
|
905
1183
|
s.stop(
|
|
906
|
-
|
|
1184
|
+
pc2.yellow(
|
|
907
1185
|
`Completed with errors. Installed: ${installedCount}, Skipped: ${skippedCount}, Errors: ${errors.length}`
|
|
908
1186
|
)
|
|
909
1187
|
);
|
|
910
|
-
console.error(
|
|
1188
|
+
console.error(pc2.red("\nErrors encountered:"));
|
|
911
1189
|
for (const e of errors) {
|
|
912
|
-
console.error(
|
|
1190
|
+
console.error(pc2.red(`- ${e}`));
|
|
913
1191
|
}
|
|
914
1192
|
} else {
|
|
915
1193
|
s.stop(
|
|
916
|
-
|
|
1194
|
+
pc2.green(
|
|
917
1195
|
`Successfully installed ${installedCount} ${normalizedType}s. (${skippedCount} skipped)`
|
|
918
1196
|
)
|
|
919
1197
|
);
|
|
@@ -938,21 +1216,21 @@ import {
|
|
|
938
1216
|
cancel as cancel2,
|
|
939
1217
|
confirm as confirm2,
|
|
940
1218
|
intro as intro2,
|
|
941
|
-
isCancel as
|
|
1219
|
+
isCancel as isCancel3,
|
|
942
1220
|
outro as outro2,
|
|
943
|
-
select as
|
|
1221
|
+
select as select3,
|
|
944
1222
|
spinner as spinner2,
|
|
945
1223
|
text
|
|
946
1224
|
} from "@clack/prompts";
|
|
947
1225
|
import fs5 from "fs-extra";
|
|
948
|
-
import
|
|
1226
|
+
import pc3 from "picocolors";
|
|
949
1227
|
async function importItem(type, url, options) {
|
|
950
1228
|
if (!options?.skipIntro) {
|
|
951
|
-
intro2(
|
|
1229
|
+
intro2(pc3.bgCyan(pc3.black(" AI Manager : Import ")));
|
|
952
1230
|
}
|
|
953
1231
|
let currentType = type;
|
|
954
1232
|
if (!currentType) {
|
|
955
|
-
const selectedType = await
|
|
1233
|
+
const selectedType = await select3({
|
|
956
1234
|
message: "What would you like to import?",
|
|
957
1235
|
options: [
|
|
958
1236
|
{ label: "Skill", value: "skill" },
|
|
@@ -960,7 +1238,7 @@ async function importItem(type, url, options) {
|
|
|
960
1238
|
{ label: "Workflow", value: "workflow" }
|
|
961
1239
|
]
|
|
962
1240
|
});
|
|
963
|
-
if (
|
|
1241
|
+
if (isCancel3(selectedType)) return;
|
|
964
1242
|
currentType = selectedType;
|
|
965
1243
|
}
|
|
966
1244
|
const normalizedType = currentType.toLowerCase().endsWith("s") ? currentType.slice(0, -1) : currentType;
|
|
@@ -981,7 +1259,7 @@ async function importItem(type, url, options) {
|
|
|
981
1259
|
return "Must be a GitHub URL.";
|
|
982
1260
|
}
|
|
983
1261
|
});
|
|
984
|
-
if (
|
|
1262
|
+
if (isCancel3(inputUrl)) return;
|
|
985
1263
|
currentUrl = inputUrl;
|
|
986
1264
|
}
|
|
987
1265
|
let tempDir = null;
|
|
@@ -996,7 +1274,7 @@ async function importItem(type, url, options) {
|
|
|
996
1274
|
if (!tempDir) throw new Error("Failed to create temp directory");
|
|
997
1275
|
itemName = result.skillName;
|
|
998
1276
|
isFile = result.isFile;
|
|
999
|
-
s.stop(
|
|
1277
|
+
s.stop(pc3.green(`Fetched: ${itemName}`));
|
|
1000
1278
|
} catch (e) {
|
|
1001
1279
|
s.error("Failed to fetch");
|
|
1002
1280
|
throw e;
|
|
@@ -1020,7 +1298,7 @@ async function importItem(type, url, options) {
|
|
|
1020
1298
|
message,
|
|
1021
1299
|
initialValue: false
|
|
1022
1300
|
});
|
|
1023
|
-
if (
|
|
1301
|
+
if (isCancel3(shouldOverwrite) || !shouldOverwrite) {
|
|
1024
1302
|
await fs5.remove(tempDir);
|
|
1025
1303
|
return;
|
|
1026
1304
|
}
|
|
@@ -1030,7 +1308,7 @@ async function importItem(type, url, options) {
|
|
|
1030
1308
|
message: `${normalizedType} '${itemName}' already exists in the repo. Overwrite?`,
|
|
1031
1309
|
initialValue: false
|
|
1032
1310
|
});
|
|
1033
|
-
if (
|
|
1311
|
+
if (isCancel3(shouldOverwrite) || !shouldOverwrite) {
|
|
1034
1312
|
await fs5.remove(tempDir);
|
|
1035
1313
|
return;
|
|
1036
1314
|
}
|
|
@@ -1048,7 +1326,7 @@ async function importItem(type, url, options) {
|
|
|
1048
1326
|
await fs5.copy(tempDir, targetPath, { overwrite: true });
|
|
1049
1327
|
}
|
|
1050
1328
|
}
|
|
1051
|
-
s.stop(
|
|
1329
|
+
s.stop(pc3.green(`Successfully imported ${itemName}!`));
|
|
1052
1330
|
await fs5.remove(tempDir);
|
|
1053
1331
|
if (!options?.skipIntro) {
|
|
1054
1332
|
outro2(`${normalizedType} available at: ${targetPath}`);
|
|
@@ -1062,82 +1340,135 @@ async function importItem(type, url, options) {
|
|
|
1062
1340
|
}
|
|
1063
1341
|
|
|
1064
1342
|
// src/commands/list.ts
|
|
1065
|
-
import
|
|
1343
|
+
import os5 from "node:os";
|
|
1344
|
+
import { cancel as cancel3, intro as intro3, isCancel as isCancel4, outro as outro3, select as select4 } from "@clack/prompts";
|
|
1066
1345
|
import fs6 from "fs-extra";
|
|
1067
|
-
import
|
|
1068
|
-
async function
|
|
1069
|
-
const
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
const fullPath = pathStr;
|
|
1076
|
-
if (await fs6.pathExists(fullPath)) {
|
|
1077
|
-
const entries = await fs6.readdir(fullPath, {
|
|
1078
|
-
withFileTypes: true
|
|
1079
|
-
});
|
|
1080
|
-
const items = [];
|
|
1081
|
-
for (const entry of entries) {
|
|
1082
|
-
if (platform === "codex") {
|
|
1083
|
-
if (await codexEntryMatchesType(fullPath, entry, normalizedType)) {
|
|
1084
|
-
items.push(entry.name);
|
|
1085
|
-
}
|
|
1086
|
-
continue;
|
|
1087
|
-
}
|
|
1088
|
-
if (entry.isDirectory() || entry.isFile() && entry.name.endsWith(".md")) {
|
|
1089
|
-
items.push(entry.name);
|
|
1090
|
-
}
|
|
1091
|
-
}
|
|
1092
|
-
items.sort();
|
|
1093
|
-
if (items.length > 0) {
|
|
1094
|
-
foundAny = true;
|
|
1095
|
-
console.log(pc3.cyan(` ${platform}:`));
|
|
1096
|
-
for (const item of items) {
|
|
1097
|
-
console.log(` - ${item}`);
|
|
1098
|
-
}
|
|
1099
|
-
}
|
|
1346
|
+
import pc4 from "picocolors";
|
|
1347
|
+
async function collectItemsForPlatform(platform, fullPath, normalizedType) {
|
|
1348
|
+
const items = [];
|
|
1349
|
+
const entries = await fs6.readdir(fullPath, { withFileTypes: true });
|
|
1350
|
+
for (const entry of entries) {
|
|
1351
|
+
if (platform === "codex") {
|
|
1352
|
+
if (await codexEntryMatchesType(fullPath, entry, normalizedType)) {
|
|
1353
|
+
items.push(entry.name);
|
|
1100
1354
|
}
|
|
1355
|
+
continue;
|
|
1101
1356
|
}
|
|
1102
|
-
if (
|
|
1103
|
-
|
|
1357
|
+
if (entry.isDirectory() || entry.isFile() && entry.name.endsWith(".md")) {
|
|
1358
|
+
items.push(entry.name);
|
|
1104
1359
|
}
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1360
|
+
}
|
|
1361
|
+
items.sort();
|
|
1362
|
+
return items;
|
|
1363
|
+
}
|
|
1364
|
+
function displayPath(absolute, root, scope) {
|
|
1365
|
+
if (scope === "global") return absolute.replace(os5.homedir(), "~");
|
|
1366
|
+
const rel = absolute.startsWith(root) ? `.${absolute.slice(root.length)}` : absolute;
|
|
1367
|
+
return rel;
|
|
1368
|
+
}
|
|
1369
|
+
async function listLocalForScopes(normalizedType, sections) {
|
|
1370
|
+
console.log(pc4.bold(pc4.blue(`Installed ${normalizedType}s (Local):`)));
|
|
1371
|
+
let foundAny = false;
|
|
1372
|
+
const platformsByOrder = [];
|
|
1373
|
+
for (const section of sections) {
|
|
1374
|
+
for (const platform of Object.keys(section.paths)) {
|
|
1375
|
+
if (!platformsByOrder.includes(platform)) platformsByOrder.push(platform);
|
|
1114
1376
|
}
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1377
|
+
}
|
|
1378
|
+
for (const platform of platformsByOrder) {
|
|
1379
|
+
let printedHeader = false;
|
|
1380
|
+
for (const section of sections) {
|
|
1381
|
+
const fullPath = section.paths[platform];
|
|
1382
|
+
if (!fullPath) continue;
|
|
1383
|
+
if (!await fs6.pathExists(fullPath)) continue;
|
|
1384
|
+
const items = await collectItemsForPlatform(
|
|
1385
|
+
platform,
|
|
1386
|
+
fullPath,
|
|
1387
|
+
normalizedType
|
|
1119
1388
|
);
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
).map((entry) => entry.name).sort();
|
|
1126
|
-
if (items.length > 0) {
|
|
1127
|
-
console.log(pc3.bold(pc3.blue(`Available ${normalizedType}s:`)));
|
|
1128
|
-
for (const item of items) {
|
|
1129
|
-
console.log(`- ${item}`);
|
|
1389
|
+
if (items.length === 0) continue;
|
|
1390
|
+
foundAny = true;
|
|
1391
|
+
if (!printedHeader) {
|
|
1392
|
+
console.log(pc4.cyan(` ${platform}:`));
|
|
1393
|
+
printedHeader = true;
|
|
1130
1394
|
}
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1395
|
+
if (sections.length > 1) {
|
|
1396
|
+
const scopeLabel = section.scope === "global" ? "Global" : "Project";
|
|
1397
|
+
const display = displayPath(fullPath, section.root, section.scope);
|
|
1398
|
+
console.log(pc4.dim(` ${scopeLabel} (${display}):`));
|
|
1399
|
+
for (const item of items) {
|
|
1400
|
+
console.log(` - ${item}`);
|
|
1401
|
+
}
|
|
1402
|
+
} else {
|
|
1403
|
+
for (const item of items) {
|
|
1404
|
+
console.log(` - ${item}`);
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
if (!foundAny) {
|
|
1410
|
+
console.log(pc4.dim(` No installed ${normalizedType}s found.`));
|
|
1411
|
+
}
|
|
1412
|
+
console.log("");
|
|
1413
|
+
}
|
|
1414
|
+
function buildSections(normalizedType, scope, homedir, projectRoot) {
|
|
1415
|
+
const sections = [];
|
|
1416
|
+
if (scope === "global" || scope === "both") {
|
|
1417
|
+
sections.push({
|
|
1418
|
+
scope: "global",
|
|
1419
|
+
root: homedir,
|
|
1420
|
+
paths: getTargetPaths(normalizedType, "global")
|
|
1421
|
+
});
|
|
1422
|
+
}
|
|
1423
|
+
if (scope === "project" || scope === "both") {
|
|
1424
|
+
if (projectRoot) {
|
|
1425
|
+
sections.push({
|
|
1426
|
+
scope: "project",
|
|
1427
|
+
root: projectRoot,
|
|
1428
|
+
paths: getTargetPaths(normalizedType, "project", projectRoot)
|
|
1429
|
+
});
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
return sections;
|
|
1433
|
+
}
|
|
1434
|
+
async function performListing(type, local) {
|
|
1435
|
+
const normalizedType = type.toLowerCase().endsWith("s") ? type.slice(0, -1) : type.toLowerCase();
|
|
1436
|
+
if (local) {
|
|
1437
|
+
throw new Error(
|
|
1438
|
+
"performListing should not be called for local mode; use listLocalForScopes"
|
|
1439
|
+
);
|
|
1440
|
+
}
|
|
1441
|
+
if (!TYPE_DIRS[normalizedType]) {
|
|
1442
|
+
console.error(
|
|
1443
|
+
pc4.red(`Unknown type: ${type}. Supported types: skill, agent, workflow`)
|
|
1444
|
+
);
|
|
1445
|
+
return;
|
|
1446
|
+
}
|
|
1447
|
+
const dirPath = TYPE_DIRS[normalizedType];
|
|
1448
|
+
if (!await fs6.pathExists(dirPath)) {
|
|
1449
|
+
console.log(
|
|
1450
|
+
pc4.yellow(`No ${normalizedType} directory found at: ${dirPath}`)
|
|
1451
|
+
);
|
|
1452
|
+
return;
|
|
1453
|
+
}
|
|
1454
|
+
const entries = await fs6.readdir(dirPath, { withFileTypes: true });
|
|
1455
|
+
const items = entries.filter(
|
|
1456
|
+
(entry) => entry.isDirectory() || normalizedType !== "skill" && entry.isFile() && entry.name.endsWith(".md")
|
|
1457
|
+
).map((entry) => entry.name).sort();
|
|
1458
|
+
if (items.length > 0) {
|
|
1459
|
+
console.log(pc4.bold(pc4.blue(`Available ${normalizedType}s:`)));
|
|
1460
|
+
for (const item of items) {
|
|
1461
|
+
console.log(`- ${item}`);
|
|
1135
1462
|
}
|
|
1463
|
+
console.log(pc4.dim(`Total: ${items.length} ${normalizedType}s
|
|
1464
|
+
`));
|
|
1465
|
+
} else {
|
|
1466
|
+
console.log(pc4.dim(`No ${normalizedType}s found.`));
|
|
1136
1467
|
}
|
|
1137
1468
|
}
|
|
1138
1469
|
async function list(type, options) {
|
|
1139
1470
|
if (!options?.skipIntro) {
|
|
1140
|
-
intro3(
|
|
1471
|
+
intro3(pc4.bgCyan(pc4.black(" AI Manager : List ")));
|
|
1141
1472
|
}
|
|
1142
1473
|
let currentType = type;
|
|
1143
1474
|
let isLocal = !!options?.local;
|
|
@@ -1145,7 +1476,7 @@ async function list(type, options) {
|
|
|
1145
1476
|
try {
|
|
1146
1477
|
while (true) {
|
|
1147
1478
|
if (!currentType) {
|
|
1148
|
-
const selected = await
|
|
1479
|
+
const selected = await select4({
|
|
1149
1480
|
message: `What would you like to list? (View: ${isLocal ? "Local" : "Repository"})`,
|
|
1150
1481
|
options: [
|
|
1151
1482
|
{ label: "All Skills", value: "skill" },
|
|
@@ -1158,7 +1489,7 @@ async function list(type, options) {
|
|
|
1158
1489
|
{ label: "Exit", value: "exit" }
|
|
1159
1490
|
]
|
|
1160
1491
|
});
|
|
1161
|
-
if (
|
|
1492
|
+
if (isCancel4(selected) || selected === "exit") {
|
|
1162
1493
|
break;
|
|
1163
1494
|
}
|
|
1164
1495
|
if (selected === "toggle") {
|
|
@@ -1168,14 +1499,43 @@ async function list(type, options) {
|
|
|
1168
1499
|
currentType = selected;
|
|
1169
1500
|
}
|
|
1170
1501
|
const typesToList = currentType === "all" ? ["skill", "agent", "workflow"] : [currentType];
|
|
1171
|
-
|
|
1172
|
-
await
|
|
1502
|
+
if (isLocal) {
|
|
1503
|
+
const scopeChoice = await chooseListRemoveScope({
|
|
1504
|
+
action: "list",
|
|
1505
|
+
flag: options?.scope
|
|
1506
|
+
});
|
|
1507
|
+
if ("rejected" in scopeChoice) {
|
|
1508
|
+
cancel3(
|
|
1509
|
+
`Scope --scope=${options?.scope} unavailable: ${scopeChoice.reason}`
|
|
1510
|
+
);
|
|
1511
|
+
process.exit(1);
|
|
1512
|
+
return;
|
|
1513
|
+
}
|
|
1514
|
+
if (scopeChoice.cancelled) {
|
|
1515
|
+
if (isSingleShot) break;
|
|
1516
|
+
currentType = void 0;
|
|
1517
|
+
continue;
|
|
1518
|
+
}
|
|
1519
|
+
for (const t of typesToList) {
|
|
1520
|
+
const normalizedType = t.toLowerCase().endsWith("s") ? t.slice(0, -1) : t.toLowerCase();
|
|
1521
|
+
const sections = buildSections(
|
|
1522
|
+
normalizedType,
|
|
1523
|
+
scopeChoice.scope,
|
|
1524
|
+
scopeChoice.homedir,
|
|
1525
|
+
scopeChoice.projectRoot
|
|
1526
|
+
);
|
|
1527
|
+
await listLocalForScopes(normalizedType, sections);
|
|
1528
|
+
}
|
|
1529
|
+
} else {
|
|
1530
|
+
for (const t of typesToList) {
|
|
1531
|
+
await performListing(t, false);
|
|
1532
|
+
}
|
|
1173
1533
|
}
|
|
1174
1534
|
if (isSingleShot) break;
|
|
1175
1535
|
currentType = void 0;
|
|
1176
1536
|
}
|
|
1177
1537
|
} catch (error) {
|
|
1178
|
-
console.error(
|
|
1538
|
+
console.error(pc4.red("Error listing items:"), error);
|
|
1179
1539
|
}
|
|
1180
1540
|
if (!options?.skipIntro) {
|
|
1181
1541
|
outro3("Done!");
|
|
@@ -1186,27 +1546,79 @@ async function list(type, options) {
|
|
|
1186
1546
|
import path8 from "node:path";
|
|
1187
1547
|
import {
|
|
1188
1548
|
autocompleteMultiselect as autocompleteMultiselect2,
|
|
1549
|
+
cancel as cancel4,
|
|
1189
1550
|
confirm as confirm3,
|
|
1190
1551
|
intro as intro4,
|
|
1191
|
-
isCancel as
|
|
1552
|
+
isCancel as isCancel5,
|
|
1192
1553
|
multiselect as multiselect2,
|
|
1193
|
-
note as
|
|
1554
|
+
note as note3,
|
|
1194
1555
|
outro as outro4,
|
|
1195
|
-
select as
|
|
1556
|
+
select as select5,
|
|
1196
1557
|
spinner as spinner3
|
|
1197
1558
|
} from "@clack/prompts";
|
|
1198
1559
|
import fs7 from "fs-extra";
|
|
1199
|
-
import
|
|
1560
|
+
import pc5 from "picocolors";
|
|
1200
1561
|
enableAutocompleteMultiSelectShiftAToggle();
|
|
1562
|
+
function buildScopedTargetPaths(normalizedType, scope, projectRoot) {
|
|
1563
|
+
const out = [];
|
|
1564
|
+
if (scope === "global" || scope === "both") {
|
|
1565
|
+
out.push({
|
|
1566
|
+
scope: "global",
|
|
1567
|
+
paths: getTargetPaths(normalizedType, "global")
|
|
1568
|
+
});
|
|
1569
|
+
}
|
|
1570
|
+
if ((scope === "project" || scope === "both") && projectRoot) {
|
|
1571
|
+
out.push({
|
|
1572
|
+
scope: "project",
|
|
1573
|
+
paths: getTargetPaths(normalizedType, "project", projectRoot)
|
|
1574
|
+
});
|
|
1575
|
+
}
|
|
1576
|
+
return out;
|
|
1577
|
+
}
|
|
1578
|
+
async function scanInstalledItems(scopedPaths, normalizedType) {
|
|
1579
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1580
|
+
const items = [];
|
|
1581
|
+
const showScopePrefix = scopedPaths.length > 1;
|
|
1582
|
+
for (const { scope, paths } of scopedPaths) {
|
|
1583
|
+
for (const [platform, pathStr] of Object.entries(paths)) {
|
|
1584
|
+
if (!pathStr) continue;
|
|
1585
|
+
if (!await fs7.pathExists(pathStr)) continue;
|
|
1586
|
+
const entries = await fs7.readdir(pathStr, { withFileTypes: true });
|
|
1587
|
+
for (const entry of entries) {
|
|
1588
|
+
let qualifies = false;
|
|
1589
|
+
if (platform === "codex") {
|
|
1590
|
+
qualifies = await codexEntryMatchesType(
|
|
1591
|
+
pathStr,
|
|
1592
|
+
entry,
|
|
1593
|
+
normalizedType
|
|
1594
|
+
);
|
|
1595
|
+
} else if (entry.isDirectory() || normalizedType === "workflow" && entry.isFile() && entry.name.endsWith(".md") || normalizedType === "agent" && entry.isFile() && entry.name.endsWith(".md")) {
|
|
1596
|
+
qualifies = true;
|
|
1597
|
+
}
|
|
1598
|
+
if (!qualifies) continue;
|
|
1599
|
+
const key = showScopePrefix ? `${scope}:${entry.name}` : entry.name;
|
|
1600
|
+
if (seen.has(key)) continue;
|
|
1601
|
+
seen.add(key);
|
|
1602
|
+
items.push({
|
|
1603
|
+
key,
|
|
1604
|
+
name: entry.name,
|
|
1605
|
+
scope,
|
|
1606
|
+
label: showScopePrefix ? `[${scope}] ${entry.name}` : entry.name
|
|
1607
|
+
});
|
|
1608
|
+
}
|
|
1609
|
+
}
|
|
1610
|
+
}
|
|
1611
|
+
return items.sort((a, b) => a.label.localeCompare(b.label));
|
|
1612
|
+
}
|
|
1201
1613
|
async function remove(type, options) {
|
|
1202
1614
|
if (!options?.skipIntro) {
|
|
1203
|
-
intro4(
|
|
1615
|
+
intro4(pc5.bgCyan(pc5.black(" AI Manager : Remove ")));
|
|
1204
1616
|
}
|
|
1205
1617
|
let currentType = type;
|
|
1206
1618
|
const isSingleShot = !!type;
|
|
1207
1619
|
while (true) {
|
|
1208
1620
|
if (!currentType) {
|
|
1209
|
-
const selectedType = await
|
|
1621
|
+
const selectedType = await select5({
|
|
1210
1622
|
message: "What would you like to remove?",
|
|
1211
1623
|
options: [
|
|
1212
1624
|
{ label: "Skills", value: "skill" },
|
|
@@ -1215,57 +1627,65 @@ async function remove(type, options) {
|
|
|
1215
1627
|
{ label: "Exit", value: "exit" }
|
|
1216
1628
|
]
|
|
1217
1629
|
});
|
|
1218
|
-
if (
|
|
1630
|
+
if (isCancel5(selectedType) || selectedType === "exit") {
|
|
1219
1631
|
break;
|
|
1220
1632
|
}
|
|
1221
1633
|
currentType = selectedType;
|
|
1222
1634
|
}
|
|
1223
1635
|
const normalizedType = currentType.toLowerCase().endsWith("s") ? currentType.slice(0, -1) : currentType;
|
|
1224
|
-
const
|
|
1225
|
-
|
|
1636
|
+
const scopeChoice = await chooseListRemoveScope({
|
|
1637
|
+
action: "remove",
|
|
1638
|
+
flag: options?.scope
|
|
1639
|
+
});
|
|
1640
|
+
if ("rejected" in scopeChoice) {
|
|
1641
|
+
cancel4(
|
|
1642
|
+
`Scope --scope=${options?.scope} unavailable: ${scopeChoice.reason}`
|
|
1643
|
+
);
|
|
1644
|
+
process.exit(1);
|
|
1645
|
+
return;
|
|
1646
|
+
}
|
|
1647
|
+
if (scopeChoice.cancelled) {
|
|
1648
|
+
if (isSingleShot) break;
|
|
1649
|
+
currentType = void 0;
|
|
1650
|
+
continue;
|
|
1651
|
+
}
|
|
1652
|
+
const scopedPaths = buildScopedTargetPaths(
|
|
1653
|
+
normalizedType,
|
|
1654
|
+
scopeChoice.scope,
|
|
1655
|
+
scopeChoice.projectRoot
|
|
1656
|
+
);
|
|
1226
1657
|
const s = spinner3();
|
|
1227
1658
|
s.start("Scanning for installed items...");
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
});
|
|
1233
|
-
for (const entry of entries) {
|
|
1234
|
-
if (platform === "codex") {
|
|
1235
|
-
if (await codexEntryMatchesType(
|
|
1236
|
-
pathStr,
|
|
1237
|
-
entry,
|
|
1238
|
-
normalizedType
|
|
1239
|
-
)) {
|
|
1240
|
-
uniqueItems.add(entry.name);
|
|
1241
|
-
}
|
|
1242
|
-
continue;
|
|
1243
|
-
}
|
|
1244
|
-
if (entry.isDirectory() || normalizedType === "workflow" && entry.isFile() && entry.name.endsWith(".md") || normalizedType === "agent" && entry.isFile() && entry.name.endsWith(".md")) {
|
|
1245
|
-
uniqueItems.add(entry.name);
|
|
1246
|
-
}
|
|
1247
|
-
}
|
|
1248
|
-
}
|
|
1249
|
-
}
|
|
1250
|
-
s.stop(`Found ${uniqueItems.size} unique installed ${normalizedType}s.`);
|
|
1251
|
-
if (uniqueItems.size === 0) {
|
|
1252
|
-
note2(`No installed ${normalizedType}s found.`, "Information");
|
|
1659
|
+
const scannedItems = await scanInstalledItems(scopedPaths, normalizedType);
|
|
1660
|
+
s.stop(`Found ${scannedItems.length} installed ${normalizedType}s.`);
|
|
1661
|
+
if (scannedItems.length === 0) {
|
|
1662
|
+
note3(`No installed ${normalizedType}s found.`, "Information");
|
|
1253
1663
|
if (isSingleShot) break;
|
|
1254
1664
|
currentType = void 0;
|
|
1255
1665
|
continue;
|
|
1256
1666
|
}
|
|
1257
|
-
const sortedItems = Array.from(uniqueItems).sort();
|
|
1258
1667
|
const itemSelections = await autocompleteMultiselect2({
|
|
1259
1668
|
message: `Select ${normalizedType}s to remove:`,
|
|
1260
|
-
options:
|
|
1669
|
+
options: scannedItems.map((item) => ({
|
|
1670
|
+
value: item.key,
|
|
1671
|
+
label: item.label
|
|
1672
|
+
}))
|
|
1261
1673
|
});
|
|
1262
|
-
if (
|
|
1674
|
+
if (isCancel5(itemSelections)) {
|
|
1263
1675
|
if (isSingleShot) break;
|
|
1264
1676
|
currentType = void 0;
|
|
1265
1677
|
continue;
|
|
1266
1678
|
}
|
|
1267
|
-
const
|
|
1268
|
-
const
|
|
1679
|
+
const itemsByKey = new Map(scannedItems.map((i) => [i.key, i]));
|
|
1680
|
+
const selectedScannedItems = itemSelections.map((key) => itemsByKey.get(key)).filter((i) => !!i);
|
|
1681
|
+
const supportedPlatforms = /* @__PURE__ */ new Set();
|
|
1682
|
+
for (const { paths } of scopedPaths) {
|
|
1683
|
+
for (const platform of Object.keys(paths)) {
|
|
1684
|
+
if (paths[platform])
|
|
1685
|
+
supportedPlatforms.add(platform);
|
|
1686
|
+
}
|
|
1687
|
+
}
|
|
1688
|
+
const platformOptions = [...supportedPlatforms].map((platform) => ({
|
|
1269
1689
|
label: PLATFORM_LABELS[platform],
|
|
1270
1690
|
value: platform
|
|
1271
1691
|
}));
|
|
@@ -1274,17 +1694,17 @@ async function remove(type, options) {
|
|
|
1274
1694
|
options: platformOptions,
|
|
1275
1695
|
required: true
|
|
1276
1696
|
});
|
|
1277
|
-
if (
|
|
1697
|
+
if (isCancel5(platformSelections)) {
|
|
1278
1698
|
if (isSingleShot) break;
|
|
1279
1699
|
currentType = void 0;
|
|
1280
1700
|
continue;
|
|
1281
1701
|
}
|
|
1282
1702
|
const selectedPlatforms = platformSelections;
|
|
1283
1703
|
const confirmDelete = await confirm3({
|
|
1284
|
-
message: `Are you sure you want to remove ${
|
|
1704
|
+
message: `Are you sure you want to remove ${selectedScannedItems.length} item(s) from ${selectedPlatforms.length} platform(s)?`,
|
|
1285
1705
|
initialValue: false
|
|
1286
1706
|
});
|
|
1287
|
-
if (
|
|
1707
|
+
if (isCancel5(confirmDelete) || !confirmDelete) {
|
|
1288
1708
|
if (isSingleShot) break;
|
|
1289
1709
|
currentType = void 0;
|
|
1290
1710
|
continue;
|
|
@@ -1294,19 +1714,25 @@ async function remove(type, options) {
|
|
|
1294
1714
|
let removedCount = 0;
|
|
1295
1715
|
let notFoundCount = 0;
|
|
1296
1716
|
const errors = [];
|
|
1717
|
+
const pathsByScope = /* @__PURE__ */ new Map();
|
|
1718
|
+
for (const { scope, paths } of scopedPaths) {
|
|
1719
|
+
pathsByScope.set(scope, paths);
|
|
1720
|
+
}
|
|
1297
1721
|
for (const platform of selectedPlatforms) {
|
|
1298
|
-
const
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
const targetPath = path8.join(targetBase, item);
|
|
1722
|
+
for (const item of selectedScannedItems) {
|
|
1723
|
+
const targetBase = pathsByScope.get(item.scope)?.[platform];
|
|
1724
|
+
if (!targetBase) continue;
|
|
1725
|
+
const targetPath = path8.join(targetBase, item.name);
|
|
1302
1726
|
if (await fs7.pathExists(targetPath)) {
|
|
1303
|
-
sDel.message(
|
|
1727
|
+
sDel.message(
|
|
1728
|
+
`Removing ${pc5.bold(item.label)} from ${pc5.cyan(platform)}...`
|
|
1729
|
+
);
|
|
1304
1730
|
try {
|
|
1305
1731
|
await fs7.remove(targetPath);
|
|
1306
1732
|
removedCount++;
|
|
1307
1733
|
} catch (err) {
|
|
1308
1734
|
const msg = err instanceof Error ? err.message : String(err);
|
|
1309
|
-
errors.push(`${item} (${platform}): ${msg}`);
|
|
1735
|
+
errors.push(`${item.label} (${platform}): ${msg}`);
|
|
1310
1736
|
}
|
|
1311
1737
|
} else {
|
|
1312
1738
|
notFoundCount++;
|
|
@@ -1315,17 +1741,17 @@ async function remove(type, options) {
|
|
|
1315
1741
|
}
|
|
1316
1742
|
if (errors.length > 0) {
|
|
1317
1743
|
sDel.stop(
|
|
1318
|
-
|
|
1744
|
+
pc5.yellow(
|
|
1319
1745
|
`Completed with errors. Removed: ${removedCount}, Not Found: ${notFoundCount}, Errors: ${errors.length}`
|
|
1320
1746
|
)
|
|
1321
1747
|
);
|
|
1322
|
-
console.error(
|
|
1748
|
+
console.error(pc5.red("\nErrors encountered:"));
|
|
1323
1749
|
for (const e of errors) {
|
|
1324
|
-
console.error(
|
|
1750
|
+
console.error(pc5.red(`- ${e}`));
|
|
1325
1751
|
}
|
|
1326
1752
|
} else {
|
|
1327
1753
|
sDel.stop(
|
|
1328
|
-
|
|
1754
|
+
pc5.green(
|
|
1329
1755
|
`Successfully removed ${removedCount} items. (${notFoundCount} not found)`
|
|
1330
1756
|
)
|
|
1331
1757
|
);
|
|
@@ -1341,10 +1767,28 @@ async function remove(type, options) {
|
|
|
1341
1767
|
// bin/cli.ts
|
|
1342
1768
|
var program = new Command();
|
|
1343
1769
|
program.name("ai-agent").description("CLI to manage AI agents, skills, and workflows").version("1.4.0");
|
|
1770
|
+
function parseScopeFlag(value) {
|
|
1771
|
+
if (value === void 0) return void 0;
|
|
1772
|
+
if (isValidScopeFlag(value)) return value;
|
|
1773
|
+
console.error(
|
|
1774
|
+
pc6.red(`Invalid --scope value: ${value}. Expected "global" or "project".`)
|
|
1775
|
+
);
|
|
1776
|
+
process.exit(1);
|
|
1777
|
+
}
|
|
1778
|
+
function parseListRemoveScopeFlag(value) {
|
|
1779
|
+
if (value === void 0) return void 0;
|
|
1780
|
+
if (isValidListRemoveScopeFlag(value)) return value;
|
|
1781
|
+
console.error(
|
|
1782
|
+
pc6.red(
|
|
1783
|
+
`Invalid --scope value: ${value}. Expected "global", "project", or "both".`
|
|
1784
|
+
)
|
|
1785
|
+
);
|
|
1786
|
+
process.exit(1);
|
|
1787
|
+
}
|
|
1344
1788
|
async function interactiveMain() {
|
|
1345
|
-
intro5(
|
|
1789
|
+
intro5(pc6.bgCyan(pc6.black(" AI Manager ")));
|
|
1346
1790
|
while (true) {
|
|
1347
|
-
const action = await
|
|
1791
|
+
const action = await select6({
|
|
1348
1792
|
message: "What would you like to do?",
|
|
1349
1793
|
options: [
|
|
1350
1794
|
{ label: "Add Item (Install)", value: "add" },
|
|
@@ -1354,7 +1798,7 @@ async function interactiveMain() {
|
|
|
1354
1798
|
{ label: "Exit", value: "exit" }
|
|
1355
1799
|
]
|
|
1356
1800
|
});
|
|
1357
|
-
if (
|
|
1801
|
+
if (isCancel6(action) || action === "exit") {
|
|
1358
1802
|
break;
|
|
1359
1803
|
}
|
|
1360
1804
|
switch (action) {
|
|
@@ -1374,15 +1818,28 @@ async function interactiveMain() {
|
|
|
1374
1818
|
}
|
|
1375
1819
|
outro5("Goodbye!");
|
|
1376
1820
|
}
|
|
1377
|
-
program.command("list [type]").description("List available items (skills, agents, workflows)").option("-l, --local", "List installed items locally").
|
|
1821
|
+
program.command("list [type]").description("List available items (skills, agents, workflows)").option("-l, --local", "List installed items locally").option(
|
|
1822
|
+
"-s, --scope <scope>",
|
|
1823
|
+
'Scope when listing local installs: "global", "project", or "both"'
|
|
1824
|
+
).action(async (type, opts) => {
|
|
1825
|
+
const scope = parseListRemoveScopeFlag(opts?.scope);
|
|
1826
|
+
await list(type, { local: opts?.local, scope });
|
|
1827
|
+
});
|
|
1378
1828
|
program.command("add [type] [url]").description(
|
|
1379
1829
|
"Add an item (skill, agent, workflow) to platforms (Interactive or from GitHub URL)"
|
|
1380
|
-
).action(
|
|
1830
|
+
).option("-s, --scope <scope>", 'Install scope: "global" or "project"').action(async (type, url, opts) => {
|
|
1831
|
+
const scope = parseScopeFlag(opts?.scope);
|
|
1832
|
+
await add(type, url, { scope });
|
|
1833
|
+
});
|
|
1381
1834
|
program.command("import [type] [url]").description("Import an item from GitHub to the repo").action(async (type, url) => {
|
|
1382
1835
|
await importItem(type || "", url || "");
|
|
1383
1836
|
});
|
|
1384
|
-
program.command("remove [type]").description("Remove locally installed items (skills, agents, workflows)").
|
|
1385
|
-
|
|
1837
|
+
program.command("remove [type]").description("Remove locally installed items (skills, agents, workflows)").option(
|
|
1838
|
+
"-s, --scope <scope>",
|
|
1839
|
+
'Scope to remove from: "global", "project", or "both"'
|
|
1840
|
+
).action(async (type, opts) => {
|
|
1841
|
+
const scope = parseListRemoveScopeFlag(opts?.scope);
|
|
1842
|
+
await remove(type || "", { scope });
|
|
1386
1843
|
});
|
|
1387
1844
|
if (process.argv.length <= 2) {
|
|
1388
1845
|
await interactiveMain();
|