@kurokeita/add-skill 2.6.1 → 3.0.1
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 +43 -64
- package/dist/bin/cli.js +270 -478
- package/dist/skills/statusline-setup/.claude-plugin/plugin.json +1 -1
- package/dist/skills/statusline-setup/SKILL.md +48 -448
- package/dist/skills/statusline-setup/mod/hooks/register.tsx +93 -103
- package/dist/skills/statusline-setup/mod/tests/work.test.ts +28 -10
- package/dist/skills/statusline-setup/mod/types/index.d.ts +2 -1
- package/dist/skills/universalize-agents/reference/mapping.md +3 -2
- package/package.json +3 -3
- package/dist/agents/backend-specialist.md +0 -277
- package/dist/agents/code-archaeologist.md +0 -114
- package/dist/agents/database-architect.md +0 -238
- package/dist/agents/debugger.md +0 -231
- package/dist/agents/devops-engineer.md +0 -242
- package/dist/agents/documentation-writer.md +0 -104
- package/dist/agents/explorer-agent.md +0 -79
- package/dist/agents/frontend-specialist.md +0 -594
- package/dist/agents/game-developer.md +0 -162
- package/dist/agents/mobile-developer.md +0 -383
- package/dist/agents/orchestrator.md +0 -435
- package/dist/agents/penetration-tester.md +0 -188
- package/dist/agents/performance-optimizer.md +0 -191
- package/dist/agents/product-manager.md +0 -122
- package/dist/agents/product-owner.md +0 -106
- package/dist/agents/project-planner.md +0 -408
- package/dist/agents/qa-automation-engineer.md +0 -109
- package/dist/agents/security-auditor.md +0 -171
- package/dist/agents/test-engineer.md +0 -158
- package/dist/skills/agent-identifier/SKILL.md +0 -431
- package/dist/skills/agent-identifier/examples/agent-creation-prompt.md +0 -247
- package/dist/skills/agent-identifier/examples/complete-agent-examples.md +0 -432
- package/dist/skills/agent-identifier/references/agent-creation-system-prompt.md +0 -215
- package/dist/skills/agent-identifier/references/system-prompt-design.md +0 -419
- package/dist/skills/agent-identifier/references/triggering-examples.md +0 -508
- package/dist/skills/agent-identifier/scripts/validate-agent.sh +0 -217
- package/dist/skills/code-review-excellence/SKILL.md +0 -525
- package/dist/skills/component-refactoring/SKILL.md +0 -492
- package/dist/skills/component-refactoring/references/complexity-patterns.md +0 -493
- package/dist/skills/component-refactoring/references/component-splitting.md +0 -477
- package/dist/skills/component-refactoring/references/hook-extraction.md +0 -317
- package/dist/skills/context7-guidelines/SKILL.md +0 -31
- package/dist/skills/feature-design-assistant/SKILL.md +0 -371
- package/dist/skills/find-bugs/SKILL.md +0 -75
- package/dist/skills/frontend-design/SKILL.md +0 -45
- package/dist/skills/go-concurrency-patterns/SKILL.md +0 -657
- package/dist/skills/grill-me/SKILL.md +0 -9
- package/dist/skills/handoff/SKILL.md +0 -16
- package/dist/skills/koreader-plugin-development/SKILL.md +0 -141
- package/dist/skills/koreader-plugin-development/examples/2-example-patch.lua +0 -17
- package/dist/skills/koreader-plugin-development/examples/_meta.lua +0 -6
- package/dist/skills/koreader-plugin-development/examples/helloworld.lua +0 -36
- package/dist/skills/koreader-plugin-development/references/debugging.md +0 -92
- package/dist/skills/koreader-plugin-development/references/events-and-widgets.md +0 -102
- package/dist/skills/koreader-plugin-development/references/patches.md +0 -118
- package/dist/skills/koreader-plugin-development/references/plugin-anatomy.md +0 -97
- package/dist/skills/make-instruction/SKILL.md +0 -68
- package/dist/skills/make-prompt/SKILL.md +0 -70
- package/dist/skills/make-skill/SKILL.md +0 -147
- package/dist/skills/prd-to-tasks/SKILL.md +0 -164
- package/dist/skills/react-native-async-patterns/SKILL.md +0 -256
- package/dist/skills/react-native-performance/SKILL.md +0 -425
- package/dist/skills/react-native-testing/SKILL.md +0 -458
- package/dist/skills/react-native-zustand-patterns/SKILL.md +0 -343
- package/dist/skills/skill-creator/SKILL.md +0 -385
- package/dist/skills/skill-creator/scripts/init_skill.cjs +0 -235
- package/dist/skills/skill-creator/scripts/package_skill.cjs +0 -84
- package/dist/skills/skill-creator/scripts/validate_skill.cjs +0 -127
- package/dist/skills/skill-development/SKILL.md +0 -676
- package/dist/skills/skill-development/references/skill-creator-original.md +0 -210
- package/dist/skills/sql-optimization-patterns/SKILL.md +0 -509
- package/dist/skills/test-driven-development/SKILL.md +0 -114
- package/dist/skills/test-driven-development/deep-modules.md +0 -33
- package/dist/skills/test-driven-development/interface-design.md +0 -35
- package/dist/skills/test-driven-development/mocking.md +0 -68
- package/dist/skills/test-driven-development/refactoring.md +0 -10
- package/dist/skills/test-driven-development/tests.md +0 -61
- package/dist/skills/vercel-react-best-practices/AGENTS.md +0 -2410
- package/dist/skills/vercel-react-best-practices/SKILL.md +0 -127
- package/dist/skills/vercel-react-best-practices/rules/advanced-event-handler-refs.md +0 -55
- package/dist/skills/vercel-react-best-practices/rules/advanced-use-latest.md +0 -49
- package/dist/skills/vercel-react-best-practices/rules/async-api-routes.md +0 -38
- package/dist/skills/vercel-react-best-practices/rules/async-defer-await.md +0 -80
- package/dist/skills/vercel-react-best-practices/rules/async-dependencies.md +0 -36
- package/dist/skills/vercel-react-best-practices/rules/async-parallel.md +0 -28
- package/dist/skills/vercel-react-best-practices/rules/async-suspense-boundaries.md +0 -99
- package/dist/skills/vercel-react-best-practices/rules/bundle-barrel-imports.md +0 -59
- package/dist/skills/vercel-react-best-practices/rules/bundle-conditional.md +0 -31
- package/dist/skills/vercel-react-best-practices/rules/bundle-defer-third-party.md +0 -49
- package/dist/skills/vercel-react-best-practices/rules/bundle-dynamic-imports.md +0 -35
- package/dist/skills/vercel-react-best-practices/rules/bundle-preload.md +0 -50
- package/dist/skills/vercel-react-best-practices/rules/client-event-listeners.md +0 -74
- package/dist/skills/vercel-react-best-practices/rules/client-localstorage-schema.md +0 -71
- package/dist/skills/vercel-react-best-practices/rules/client-passive-event-listeners.md +0 -48
- package/dist/skills/vercel-react-best-practices/rules/client-swr-dedup.md +0 -56
- package/dist/skills/vercel-react-best-practices/rules/js-batch-dom-css.md +0 -82
- package/dist/skills/vercel-react-best-practices/rules/js-cache-function-results.md +0 -80
- package/dist/skills/vercel-react-best-practices/rules/js-cache-property-access.md +0 -28
- package/dist/skills/vercel-react-best-practices/rules/js-cache-storage.md +0 -70
- package/dist/skills/vercel-react-best-practices/rules/js-combine-iterations.md +0 -32
- package/dist/skills/vercel-react-best-practices/rules/js-early-exit.md +0 -50
- package/dist/skills/vercel-react-best-practices/rules/js-hoist-regexp.md +0 -45
- package/dist/skills/vercel-react-best-practices/rules/js-index-maps.md +0 -37
- package/dist/skills/vercel-react-best-practices/rules/js-length-check-first.md +0 -50
- package/dist/skills/vercel-react-best-practices/rules/js-min-max-loop.md +0 -82
- package/dist/skills/vercel-react-best-practices/rules/js-set-map-lookups.md +0 -24
- package/dist/skills/vercel-react-best-practices/rules/js-tosorted-immutable.md +0 -57
- package/dist/skills/vercel-react-best-practices/rules/rendering-activity.md +0 -26
- package/dist/skills/vercel-react-best-practices/rules/rendering-animate-svg-wrapper.md +0 -47
- package/dist/skills/vercel-react-best-practices/rules/rendering-conditional-render.md +0 -40
- package/dist/skills/vercel-react-best-practices/rules/rendering-content-visibility.md +0 -38
- package/dist/skills/vercel-react-best-practices/rules/rendering-hoist-jsx.md +0 -46
- package/dist/skills/vercel-react-best-practices/rules/rendering-hydration-no-flicker.md +0 -82
- package/dist/skills/vercel-react-best-practices/rules/rendering-svg-precision.md +0 -28
- package/dist/skills/vercel-react-best-practices/rules/rerender-defer-reads.md +0 -39
- package/dist/skills/vercel-react-best-practices/rules/rerender-dependencies.md +0 -45
- package/dist/skills/vercel-react-best-practices/rules/rerender-derived-state.md +0 -29
- package/dist/skills/vercel-react-best-practices/rules/rerender-functional-setstate.md +0 -74
- package/dist/skills/vercel-react-best-practices/rules/rerender-lazy-state-init.md +0 -58
- package/dist/skills/vercel-react-best-practices/rules/rerender-memo.md +0 -44
- package/dist/skills/vercel-react-best-practices/rules/rerender-transitions.md +0 -40
- package/dist/skills/vercel-react-best-practices/rules/server-after-nonblocking.md +0 -73
- package/dist/skills/vercel-react-best-practices/rules/server-cache-lru.md +0 -41
- package/dist/skills/vercel-react-best-practices/rules/server-cache-react.md +0 -76
- package/dist/skills/vercel-react-best-practices/rules/server-parallel-fetching.md +0 -83
- package/dist/skills/vercel-react-best-practices/rules/server-serialization.md +0 -38
- package/dist/skills/web-design-guidelines/SKILL.md +0 -40
- package/dist/skills/workflow-patterns/SKILL.md +0 -623
- package/dist/skills/write-prd/SKILL.md +0 -162
- package/dist/workflows/brainstorm.md +0 -113
- package/dist/workflows/create.md +0 -60
- package/dist/workflows/debug.md +0 -107
- package/dist/workflows/deploy.md +0 -178
- package/dist/workflows/enhance.md +0 -64
- package/dist/workflows/github-pull-request-create.md +0 -134
- package/dist/workflows/orchestrate.md +0 -246
- package/dist/workflows/plan.md +0 -90
- package/dist/workflows/preview.md +0 -83
- package/dist/workflows/status.md +0 -87
- package/dist/workflows/test.md +0 -144
- package/dist/workflows/ui-ux-pro-max.md +0 -315
package/dist/bin/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// bin/cli.ts
|
|
4
|
-
import { intro as intro5, isCancel as isCancel6, outro as outro5, select as
|
|
4
|
+
import { intro as intro5, isCancel as isCancel6, outro as outro5, select as select3 } from "@clack/prompts";
|
|
5
5
|
import { Command } from "commander";
|
|
6
6
|
import pc6 from "picocolors";
|
|
7
7
|
|
|
@@ -17,7 +17,6 @@ import {
|
|
|
17
17
|
multiselect,
|
|
18
18
|
note as note2,
|
|
19
19
|
outro,
|
|
20
|
-
select as select2,
|
|
21
20
|
spinner
|
|
22
21
|
} from "@clack/prompts";
|
|
23
22
|
import fs5 from "fs-extra";
|
|
@@ -37,9 +36,7 @@ import { fileURLToPath } from "node:url";
|
|
|
37
36
|
var __filename = fileURLToPath(import.meta.url);
|
|
38
37
|
var __dirname = path.dirname(__filename);
|
|
39
38
|
function hasBundledAssets(rootDir) {
|
|
40
|
-
return
|
|
41
|
-
(dir) => fs.existsSync(path.join(rootDir, dir))
|
|
42
|
-
);
|
|
39
|
+
return fs.existsSync(path.join(rootDir, "skills"));
|
|
43
40
|
}
|
|
44
41
|
function resolveProjectRoot(moduleDir) {
|
|
45
42
|
const sourceRoot = path.resolve(moduleDir, "../..");
|
|
@@ -65,20 +62,6 @@ var PLATFORM_PATHS_SKILLS = {
|
|
|
65
62
|
gemini: path.join(os.homedir(), ".gemini/skills"),
|
|
66
63
|
windsurf: path.join(os.homedir(), ".codeium/windsurf/skills")
|
|
67
64
|
};
|
|
68
|
-
var PLATFORM_PATHS_AGENTS = {
|
|
69
|
-
"claude-code": path.join(os.homedir(), ".claude/agents"),
|
|
70
|
-
codex: path.join(os.homedir(), ".codex/skills"),
|
|
71
|
-
copilot: path.join(os.homedir(), ".copilot/agents"),
|
|
72
|
-
gemini: path.join(os.homedir(), ".gemini/agents")
|
|
73
|
-
};
|
|
74
|
-
var PLATFORM_PATHS_WORKFLOWS = {
|
|
75
|
-
antigravity: path.join(os.homedir(), ".gemini/antigravity/global_workflows"),
|
|
76
|
-
"claude-code": path.join(os.homedir(), ".claude/commands"),
|
|
77
|
-
codex: path.join(os.homedir(), ".codex/skills"),
|
|
78
|
-
copilot: path.join(os.homedir(), ".copilot/prompts"),
|
|
79
|
-
gemini: path.join(os.homedir(), ".gemini/commands"),
|
|
80
|
-
windsurf: path.join(os.homedir(), ".codeium/windsurf/global_workflows")
|
|
81
|
-
};
|
|
82
65
|
var PROJECT_RELATIVE_PLATFORM_PATHS_SKILLS = {
|
|
83
66
|
antigravity: ".gemini/antigravity/skills",
|
|
84
67
|
"claude-code": ".claude/skills",
|
|
@@ -87,20 +70,6 @@ var PROJECT_RELATIVE_PLATFORM_PATHS_SKILLS = {
|
|
|
87
70
|
gemini: ".gemini/skills",
|
|
88
71
|
windsurf: ".codeium/windsurf/skills"
|
|
89
72
|
};
|
|
90
|
-
var PROJECT_RELATIVE_PLATFORM_PATHS_AGENTS = {
|
|
91
|
-
"claude-code": ".claude/agents",
|
|
92
|
-
codex: ".codex/skills",
|
|
93
|
-
copilot: ".copilot/agents",
|
|
94
|
-
gemini: ".gemini/agents"
|
|
95
|
-
};
|
|
96
|
-
var PROJECT_RELATIVE_PLATFORM_PATHS_WORKFLOWS = {
|
|
97
|
-
antigravity: ".gemini/antigravity/workflows",
|
|
98
|
-
"claude-code": ".claude/commands",
|
|
99
|
-
codex: ".codex/skills",
|
|
100
|
-
copilot: ".copilot/prompts",
|
|
101
|
-
gemini: ".gemini/commands",
|
|
102
|
-
windsurf: ".codeium/windsurf/workflows"
|
|
103
|
-
};
|
|
104
73
|
function resolveProjectPaths(relative, root) {
|
|
105
74
|
const out = {};
|
|
106
75
|
for (const [platform, rel] of Object.entries(relative)) {
|
|
@@ -111,21 +80,8 @@ function resolveProjectPaths(relative, root) {
|
|
|
111
80
|
function getProjectPlatformPathsSkills(root) {
|
|
112
81
|
return resolveProjectPaths(PROJECT_RELATIVE_PLATFORM_PATHS_SKILLS, root);
|
|
113
82
|
}
|
|
114
|
-
function getProjectPlatformPathsAgents(root) {
|
|
115
|
-
return resolveProjectPaths(PROJECT_RELATIVE_PLATFORM_PATHS_AGENTS, root);
|
|
116
|
-
}
|
|
117
|
-
function getProjectPlatformPathsWorkflows(root) {
|
|
118
|
-
return resolveProjectPaths(PROJECT_RELATIVE_PLATFORM_PATHS_WORKFLOWS, root);
|
|
119
|
-
}
|
|
120
83
|
var TYPE_DIRS = {
|
|
121
|
-
skill: path.join(PROJECT_ROOT, "skills")
|
|
122
|
-
agent: path.join(PROJECT_ROOT, "agents"),
|
|
123
|
-
workflow: path.join(PROJECT_ROOT, "workflows")
|
|
124
|
-
};
|
|
125
|
-
var TYPE_SUBDIRS = {
|
|
126
|
-
skill: "skills",
|
|
127
|
-
agent: "agents",
|
|
128
|
-
workflow: "commands"
|
|
84
|
+
skill: path.join(PROJECT_ROOT, "skills")
|
|
129
85
|
};
|
|
130
86
|
var AGENT_SETUP_SCRIPTS_DIR = path.join(
|
|
131
87
|
PROJECT_ROOT,
|
|
@@ -143,25 +99,16 @@ function getAgentsBase(scope, root) {
|
|
|
143
99
|
}
|
|
144
100
|
|
|
145
101
|
// src/utils/agent-setup.ts
|
|
146
|
-
var LINK_SUBDIRS = ["skills"
|
|
102
|
+
var LINK_SUBDIRS = ["skills"];
|
|
147
103
|
function getScopePaths(scope, root) {
|
|
148
104
|
if (scope === "project") {
|
|
149
|
-
return {
|
|
150
|
-
skills: getProjectPlatformPathsSkills(root),
|
|
151
|
-
agents: getProjectPlatformPathsAgents(root),
|
|
152
|
-
commands: getProjectPlatformPathsWorkflows(root)
|
|
153
|
-
};
|
|
105
|
+
return { skills: getProjectPlatformPathsSkills(root) };
|
|
154
106
|
}
|
|
155
|
-
return {
|
|
156
|
-
skills: PLATFORM_PATHS_SKILLS,
|
|
157
|
-
agents: PLATFORM_PATHS_AGENTS,
|
|
158
|
-
commands: PLATFORM_PATHS_WORKFLOWS
|
|
159
|
-
};
|
|
107
|
+
return { skills: PLATFORM_PATHS_SKILLS };
|
|
160
108
|
}
|
|
161
109
|
function buildLinkMap(scope, root, presentSubdirs, platforms) {
|
|
162
110
|
const scopePaths = getScopePaths(scope, root);
|
|
163
111
|
const lines = [];
|
|
164
|
-
const seen = /* @__PURE__ */ new Set();
|
|
165
112
|
for (const sub of LINK_SUBDIRS) {
|
|
166
113
|
if (!presentSubdirs.includes(sub)) continue;
|
|
167
114
|
const platformDirs = scopePaths[sub];
|
|
@@ -169,10 +116,7 @@ function buildLinkMap(scope, root, presentSubdirs, platforms) {
|
|
|
169
116
|
if (!dest) continue;
|
|
170
117
|
if (platforms && !platforms.includes(platform)) continue;
|
|
171
118
|
const formattedDest = scope === "project" ? path2.relative(root, dest).replace(/\\/g, "/") : dest;
|
|
172
|
-
|
|
173
|
-
if (seen.has(line)) continue;
|
|
174
|
-
seen.add(line);
|
|
175
|
-
lines.push(line);
|
|
119
|
+
lines.push(`${sub}|${formattedDest}`);
|
|
176
120
|
}
|
|
177
121
|
}
|
|
178
122
|
return lines.join("\n");
|
|
@@ -894,196 +838,156 @@ async function add(type, url, options) {
|
|
|
894
838
|
if (!options?.skipIntro) {
|
|
895
839
|
intro(pc2.bgCyan(pc2.black(" AI Manager : Add ")));
|
|
896
840
|
}
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
841
|
+
await installSkills(type, url, options);
|
|
842
|
+
if (!options?.skipIntro) {
|
|
843
|
+
outro("You're all set!");
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
async function installSkills(type, url, options) {
|
|
847
|
+
const normalizedType = type ? type.toLowerCase().endsWith("s") && type.length > 1 ? type.slice(0, -1) : type : "skill";
|
|
848
|
+
if (!TYPE_DIRS[normalizedType]) {
|
|
849
|
+
cancel(`Unknown type: ${type}. Supported: skill`);
|
|
850
|
+
process.exit(1);
|
|
851
|
+
return;
|
|
852
|
+
}
|
|
853
|
+
let tempDir = null;
|
|
854
|
+
let isGitHubFile = false;
|
|
855
|
+
let selectedItems = [];
|
|
856
|
+
try {
|
|
857
|
+
if (url) {
|
|
858
|
+
const s2 = spinner();
|
|
859
|
+
s2.start("Fetching skill from GitHub...");
|
|
860
|
+
try {
|
|
861
|
+
const result = await fetchSkillFromGitHub(url);
|
|
862
|
+
tempDir = result.tempDir;
|
|
863
|
+
isGitHubFile = result.isFile;
|
|
864
|
+
selectedItems = [result.skillName];
|
|
865
|
+
s2.stop(pc2.green(`Fetched skill: ${result.skillName}`));
|
|
866
|
+
} catch (e) {
|
|
867
|
+
s2.error("Failed to fetch");
|
|
868
|
+
throw e;
|
|
912
869
|
}
|
|
913
|
-
|
|
870
|
+
} else {
|
|
871
|
+
const sourceDir = TYPE_DIRS.skill;
|
|
872
|
+
if (!await fs5.pathExists(sourceDir)) {
|
|
873
|
+
cancel("skill directory not found!");
|
|
874
|
+
process.exit(1);
|
|
875
|
+
return;
|
|
876
|
+
}
|
|
877
|
+
const entries = await fs5.readdir(sourceDir, { withFileTypes: true });
|
|
878
|
+
const availableItems = entries.filter((entry) => entry.isDirectory()).map((entry) => ({ label: entry.name, value: entry.name })).sort((a, b) => a.label.localeCompare(b.label));
|
|
879
|
+
if (availableItems.length === 0) {
|
|
880
|
+
note2("No skills found in directory.", "Information");
|
|
881
|
+
return;
|
|
882
|
+
}
|
|
883
|
+
const items = await autocompleteMultiselect({
|
|
884
|
+
message: "Select skills to install:",
|
|
885
|
+
options: availableItems
|
|
886
|
+
});
|
|
887
|
+
if (isCancel2(items)) return;
|
|
888
|
+
selectedItems = items;
|
|
914
889
|
}
|
|
915
|
-
const
|
|
916
|
-
if (
|
|
917
|
-
|
|
890
|
+
const scopeChoice = await chooseInstallScope({ flag: options?.scope });
|
|
891
|
+
if ("rejected" in scopeChoice) {
|
|
892
|
+
if (tempDir) await fs5.remove(tempDir);
|
|
893
|
+
cancel(
|
|
894
|
+
`Scope --scope=${options?.scope} unavailable: ${scopeChoice.reason}`
|
|
895
|
+
);
|
|
918
896
|
process.exit(1);
|
|
919
897
|
return;
|
|
920
898
|
}
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
} catch (e) {
|
|
935
|
-
s2.error("Failed to fetch");
|
|
936
|
-
throw e;
|
|
937
|
-
}
|
|
938
|
-
} else {
|
|
939
|
-
const sourceDir = TYPE_DIRS[normalizedType];
|
|
940
|
-
if (!await fs5.pathExists(sourceDir)) {
|
|
941
|
-
cancel(`${normalizedType} directory not found!`);
|
|
942
|
-
process.exit(1);
|
|
943
|
-
return;
|
|
944
|
-
}
|
|
945
|
-
const entries = await fs5.readdir(sourceDir, { withFileTypes: true });
|
|
946
|
-
const availableItems = entries.filter(
|
|
947
|
-
(entry) => entry.isDirectory() || (normalizedType === "workflow" || normalizedType === "agent") && entry.isFile() && entry.name.endsWith(".md")
|
|
948
|
-
).map((entry) => ({ label: entry.name, value: entry.name })).sort((a, b) => a.label.localeCompare(b.label));
|
|
949
|
-
if (availableItems.length === 0) {
|
|
950
|
-
note2(`No ${normalizedType}s found in directory.`, "Information");
|
|
951
|
-
if (isSingleShot) break;
|
|
952
|
-
currentType = void 0;
|
|
953
|
-
continue;
|
|
954
|
-
}
|
|
955
|
-
const items = await autocompleteMultiselect({
|
|
956
|
-
message: `Select ${normalizedType}s to install:`,
|
|
957
|
-
options: availableItems
|
|
958
|
-
});
|
|
959
|
-
if (isCancel2(items)) {
|
|
960
|
-
if (isSingleShot) break;
|
|
961
|
-
currentType = void 0;
|
|
962
|
-
continue;
|
|
963
|
-
}
|
|
964
|
-
selectedItems = items;
|
|
965
|
-
}
|
|
966
|
-
const scopeChoice = await chooseInstallScope({ flag: options?.scope });
|
|
967
|
-
if ("rejected" in scopeChoice) {
|
|
899
|
+
if (scopeChoice.cancelled) {
|
|
900
|
+
if (tempDir) await fs5.remove(tempDir);
|
|
901
|
+
return;
|
|
902
|
+
}
|
|
903
|
+
const { scope: chosenScope, root: scopeRoot } = scopeChoice;
|
|
904
|
+
const agentsBase = getAgentsBase(chosenScope, scopeRoot);
|
|
905
|
+
const targetBase = path5.join(agentsBase, "skills");
|
|
906
|
+
if (chosenScope === "project") {
|
|
907
|
+
const proceed = await confirm({
|
|
908
|
+
message: `Install to project scope at ${pc2.cyan(scopeRoot)}?`,
|
|
909
|
+
initialValue: true
|
|
910
|
+
});
|
|
911
|
+
if (isCancel2(proceed) || !proceed) {
|
|
968
912
|
if (tempDir) await fs5.remove(tempDir);
|
|
969
|
-
cancel(
|
|
970
|
-
`Scope --scope=${options?.scope} unavailable: ${scopeChoice.reason}`
|
|
971
|
-
);
|
|
972
|
-
process.exit(1);
|
|
973
913
|
return;
|
|
974
914
|
}
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
915
|
+
}
|
|
916
|
+
const preExisting = [];
|
|
917
|
+
for (const item of selectedItems) {
|
|
918
|
+
const targetName = path5.parse(item).name;
|
|
919
|
+
if (await fs5.pathExists(path5.join(targetBase, targetName))) {
|
|
920
|
+
preExisting.push({ entry: targetName, subdir: "skills" });
|
|
980
921
|
}
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
const
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
if (
|
|
991
|
-
|
|
992
|
-
if (isSingleShot) break;
|
|
993
|
-
currentType = void 0;
|
|
994
|
-
continue;
|
|
995
|
-
}
|
|
922
|
+
}
|
|
923
|
+
const skipEntries = /* @__PURE__ */ new Set();
|
|
924
|
+
if (preExisting.length > 0) {
|
|
925
|
+
const toOverwrite = await multiselect({
|
|
926
|
+
message: "These entries already exist in .agents. Choose which to OVERWRITE (unselected entries are kept and skipped):",
|
|
927
|
+
options: preExisting.map((p) => ({ label: p.entry, value: p.entry })),
|
|
928
|
+
required: false
|
|
929
|
+
});
|
|
930
|
+
if (isCancel2(toOverwrite)) {
|
|
931
|
+
if (tempDir) await fs5.remove(tempDir);
|
|
932
|
+
return;
|
|
996
933
|
}
|
|
997
|
-
const
|
|
998
|
-
const preExisting
|
|
999
|
-
|
|
1000
|
-
const targetName = normalizedType === "skill" ? path5.parse(item).name : item;
|
|
1001
|
-
if (await fs5.pathExists(path5.join(targetBase, targetName))) {
|
|
1002
|
-
preExisting.push({ entry: targetName, subdir: subdirName });
|
|
1003
|
-
}
|
|
934
|
+
const keep = new Set(toOverwrite);
|
|
935
|
+
for (const { entry } of preExisting) {
|
|
936
|
+
if (!keep.has(entry)) skipEntries.add(entry);
|
|
1004
937
|
}
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
938
|
+
}
|
|
939
|
+
note2(
|
|
940
|
+
`Installing ${selectedItems.length} skills to ${targetBase} (scope: ${chosenScope})...`,
|
|
941
|
+
"Summary"
|
|
942
|
+
);
|
|
943
|
+
const s = spinner();
|
|
944
|
+
s.start("Installing...");
|
|
945
|
+
const errors = [];
|
|
946
|
+
const installed = [];
|
|
947
|
+
await fs5.ensureDir(targetBase);
|
|
948
|
+
for (const item of selectedItems) {
|
|
949
|
+
const targetName = path5.parse(item).name;
|
|
950
|
+
if (skipEntries.has(targetName)) continue;
|
|
951
|
+
s.message(`Installing ${pc2.bold(item)}...`);
|
|
952
|
+
try {
|
|
953
|
+
const sourcePath = url && tempDir ? isGitHubFile ? path5.join(tempDir, item) : tempDir : path5.join(TYPE_DIRS.skill, item);
|
|
954
|
+
if (!await fs5.pathExists(sourcePath)) {
|
|
955
|
+
throw new Error(`Item '${item}' not found at ${sourcePath}`);
|
|
1021
956
|
}
|
|
957
|
+
const targetPath = path5.join(targetBase, targetName);
|
|
958
|
+
await fs5.copy(sourcePath, targetPath, { overwrite: true });
|
|
959
|
+
installed.push(targetPath);
|
|
960
|
+
} catch (err) {
|
|
961
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
962
|
+
errors.push(`${item}: ${errorMessage}`);
|
|
1022
963
|
}
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
964
|
+
}
|
|
965
|
+
if (tempDir) {
|
|
966
|
+
await fs5.remove(tempDir);
|
|
967
|
+
tempDir = null;
|
|
968
|
+
}
|
|
969
|
+
if (errors.length > 0) {
|
|
970
|
+
s.stop(
|
|
971
|
+
pc2.yellow(
|
|
972
|
+
`Completed with errors. Installed: ${installed.length}, Errors: ${errors.length}`
|
|
973
|
+
)
|
|
1026
974
|
);
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
const installed = [];
|
|
1031
|
-
await fs5.ensureDir(targetBase);
|
|
1032
|
-
for (const item of selectedItems) {
|
|
1033
|
-
const targetName = normalizedType === "skill" ? path5.parse(item).name : item;
|
|
1034
|
-
if (skipEntries.has(targetName)) continue;
|
|
1035
|
-
s.message(`Installing ${pc2.bold(item)}...`);
|
|
1036
|
-
try {
|
|
1037
|
-
const sourcePath = url && tempDir ? isGitHubFile ? path5.join(tempDir, item) : tempDir : path5.join(TYPE_DIRS[normalizedType], item);
|
|
1038
|
-
if (!await fs5.pathExists(sourcePath)) {
|
|
1039
|
-
throw new Error(`Item '${item}' not found at ${sourcePath}`);
|
|
1040
|
-
}
|
|
1041
|
-
const targetPath = path5.join(targetBase, targetName);
|
|
1042
|
-
await fs5.copy(sourcePath, targetPath, { overwrite: true });
|
|
1043
|
-
installed.push(targetPath);
|
|
1044
|
-
} catch (err) {
|
|
1045
|
-
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
1046
|
-
errors.push(`${item}: ${errorMessage}`);
|
|
1047
|
-
}
|
|
1048
|
-
}
|
|
1049
|
-
if (tempDir) {
|
|
1050
|
-
await fs5.remove(tempDir);
|
|
1051
|
-
tempDir = null;
|
|
1052
|
-
}
|
|
1053
|
-
if (errors.length > 0) {
|
|
1054
|
-
s.stop(
|
|
1055
|
-
pc2.yellow(
|
|
1056
|
-
`Completed with errors. Installed: ${installed.length}, Errors: ${errors.length}`
|
|
1057
|
-
)
|
|
1058
|
-
);
|
|
1059
|
-
console.error(pc2.red("\nErrors encountered:"));
|
|
1060
|
-
for (const e of errors) {
|
|
1061
|
-
console.error(pc2.red(`- ${e}`));
|
|
1062
|
-
}
|
|
1063
|
-
} else {
|
|
1064
|
-
s.stop(
|
|
1065
|
-
pc2.green(
|
|
1066
|
-
`Successfully installed ${installed.length} ${normalizedType}s.`
|
|
1067
|
-
)
|
|
1068
|
-
);
|
|
1069
|
-
}
|
|
1070
|
-
if (installed.length > 0) {
|
|
1071
|
-
await maybeWireAgentSetup(agentsBase, chosenScope, scopeRoot);
|
|
1072
|
-
if (normalizedType === "skill") {
|
|
1073
|
-
await maybeInstallSkillHooks(installed, chosenScope, scopeRoot, !!url);
|
|
1074
|
-
}
|
|
975
|
+
console.error(pc2.red("\nErrors encountered:"));
|
|
976
|
+
for (const e of errors) {
|
|
977
|
+
console.error(pc2.red(`- ${e}`));
|
|
1075
978
|
}
|
|
1076
|
-
}
|
|
1077
|
-
|
|
1078
|
-
cancel(`An error occurred: ${error}`);
|
|
1079
|
-
process.exit(1);
|
|
1080
|
-
return;
|
|
979
|
+
} else {
|
|
980
|
+
s.stop(pc2.green(`Successfully installed ${installed.length} skills.`));
|
|
1081
981
|
}
|
|
1082
|
-
if (
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
982
|
+
if (installed.length > 0) {
|
|
983
|
+
await maybeWireAgentSetup(agentsBase, chosenScope, scopeRoot);
|
|
984
|
+
await maybeInstallSkillHooks(installed, chosenScope, scopeRoot, !!url);
|
|
985
|
+
}
|
|
986
|
+
} catch (error) {
|
|
987
|
+
if (tempDir) await fs5.remove(tempDir);
|
|
988
|
+
cancel(`An error occurred: ${error}`);
|
|
989
|
+
process.exit(1);
|
|
990
|
+
return;
|
|
1087
991
|
}
|
|
1088
992
|
}
|
|
1089
993
|
async function maybeWireAgentSetup(agentsBase, scope, root) {
|
|
@@ -1175,7 +1079,6 @@ import {
|
|
|
1175
1079
|
intro as intro2,
|
|
1176
1080
|
isCancel as isCancel3,
|
|
1177
1081
|
outro as outro2,
|
|
1178
|
-
select as select3,
|
|
1179
1082
|
spinner as spinner2,
|
|
1180
1083
|
text
|
|
1181
1084
|
} from "@clack/prompts";
|
|
@@ -1185,23 +1088,11 @@ async function importItem(type, url, options) {
|
|
|
1185
1088
|
if (!options?.skipIntro) {
|
|
1186
1089
|
intro2(pc3.bgCyan(pc3.black(" AI Manager : Import ")));
|
|
1187
1090
|
}
|
|
1188
|
-
|
|
1189
|
-
if (!currentType) {
|
|
1190
|
-
const selectedType = await select3({
|
|
1191
|
-
message: "What would you like to import?",
|
|
1192
|
-
options: [
|
|
1193
|
-
{ label: "Skill", value: "skill" },
|
|
1194
|
-
{ label: "Agent", value: "agent" },
|
|
1195
|
-
{ label: "Workflow", value: "workflow" }
|
|
1196
|
-
]
|
|
1197
|
-
});
|
|
1198
|
-
if (isCancel3(selectedType)) return;
|
|
1199
|
-
currentType = selectedType;
|
|
1200
|
-
}
|
|
1091
|
+
const currentType = type || "skill";
|
|
1201
1092
|
const normalizedType = currentType.toLowerCase().endsWith("s") ? currentType.slice(0, -1) : currentType;
|
|
1202
1093
|
const targetBaseDir = TYPE_DIRS[normalizedType];
|
|
1203
1094
|
if (!targetBaseDir) {
|
|
1204
|
-
cancel2(`Unknown type: ${currentType}. Supported: skill
|
|
1095
|
+
cancel2(`Unknown type: ${currentType}. Supported: skill`);
|
|
1205
1096
|
process.exit(1);
|
|
1206
1097
|
return;
|
|
1207
1098
|
}
|
|
@@ -1236,31 +1127,8 @@ async function importItem(type, url, options) {
|
|
|
1236
1127
|
s.error("Failed to fetch");
|
|
1237
1128
|
throw e;
|
|
1238
1129
|
}
|
|
1239
|
-
const
|
|
1240
|
-
|
|
1241
|
-
if (shouldFlatten) {
|
|
1242
|
-
const srcFiles = await fs6.readdir(tempDir);
|
|
1243
|
-
const conflicts = [];
|
|
1244
|
-
for (const file of srcFiles) {
|
|
1245
|
-
if (await fs6.pathExists(path6.join(targetBaseDir, file))) {
|
|
1246
|
-
conflicts.push(file);
|
|
1247
|
-
}
|
|
1248
|
-
}
|
|
1249
|
-
if (conflicts.length > 0) {
|
|
1250
|
-
const limit = 5;
|
|
1251
|
-
const displayList = conflicts.slice(0, limit).join(", ");
|
|
1252
|
-
const remainder = conflicts.length - limit;
|
|
1253
|
-
const message = remainder > 0 ? `Files ${displayList} (and ${remainder} others) already exist in ${targetBaseDir}. Overwrite?` : `Files ${displayList} already exist in ${targetBaseDir}. Overwrite?`;
|
|
1254
|
-
const shouldOverwrite = await confirm2({
|
|
1255
|
-
message,
|
|
1256
|
-
initialValue: false
|
|
1257
|
-
});
|
|
1258
|
-
if (isCancel3(shouldOverwrite) || !shouldOverwrite) {
|
|
1259
|
-
await fs6.remove(tempDir);
|
|
1260
|
-
return;
|
|
1261
|
-
}
|
|
1262
|
-
}
|
|
1263
|
-
} else if (await fs6.pathExists(targetPath)) {
|
|
1130
|
+
const targetPath = path6.join(targetBaseDir, itemName);
|
|
1131
|
+
if (await fs6.pathExists(targetPath)) {
|
|
1264
1132
|
const shouldOverwrite = await confirm2({
|
|
1265
1133
|
message: `${normalizedType} '${itemName}' already exists in the repo. Overwrite?`,
|
|
1266
1134
|
initialValue: false
|
|
@@ -1277,11 +1145,7 @@ async function importItem(type, url, options) {
|
|
|
1277
1145
|
overwrite: true
|
|
1278
1146
|
});
|
|
1279
1147
|
} else {
|
|
1280
|
-
|
|
1281
|
-
await fs6.copy(tempDir, targetBaseDir, { overwrite: true });
|
|
1282
|
-
} else {
|
|
1283
|
-
await fs6.copy(tempDir, targetPath, { overwrite: true });
|
|
1284
|
-
}
|
|
1148
|
+
await fs6.copy(tempDir, targetPath, { overwrite: true });
|
|
1285
1149
|
}
|
|
1286
1150
|
s.stop(pc3.green(`Successfully imported ${itemName}!`));
|
|
1287
1151
|
await fs6.remove(tempDir);
|
|
@@ -1299,7 +1163,7 @@ async function importItem(type, url, options) {
|
|
|
1299
1163
|
// src/commands/list.ts
|
|
1300
1164
|
import os6 from "node:os";
|
|
1301
1165
|
import path7 from "node:path";
|
|
1302
|
-
import { cancel as cancel3, intro as intro3, isCancel as isCancel4, outro as outro3, select as
|
|
1166
|
+
import { cancel as cancel3, intro as intro3, isCancel as isCancel4, outro as outro3, select as select2 } from "@clack/prompts";
|
|
1303
1167
|
import fs7 from "fs-extra";
|
|
1304
1168
|
import pc4 from "picocolors";
|
|
1305
1169
|
async function collectEntries(subdirPath) {
|
|
@@ -1312,12 +1176,11 @@ function displayBase(base, scope) {
|
|
|
1312
1176
|
if (scope === "global") return base.replace(os6.homedir(), "~");
|
|
1313
1177
|
return base;
|
|
1314
1178
|
}
|
|
1315
|
-
async function listLocalForScopes(
|
|
1316
|
-
|
|
1317
|
-
console.log(pc4.bold(pc4.blue(`Installed ${normalizedType}s (Local):`)));
|
|
1179
|
+
async function listLocalForScopes(sections) {
|
|
1180
|
+
console.log(pc4.bold(pc4.blue("Installed skills (Local):")));
|
|
1318
1181
|
let foundAny = false;
|
|
1319
1182
|
for (const section of sections) {
|
|
1320
|
-
const subdirPath = path7.join(section.base,
|
|
1183
|
+
const subdirPath = path7.join(section.base, "skills");
|
|
1321
1184
|
if (!await fs7.pathExists(subdirPath)) continue;
|
|
1322
1185
|
const items = await collectEntries(subdirPath);
|
|
1323
1186
|
if (items.length === 0) continue;
|
|
@@ -1336,7 +1199,7 @@ async function listLocalForScopes(normalizedType, sections) {
|
|
|
1336
1199
|
}
|
|
1337
1200
|
}
|
|
1338
1201
|
if (!foundAny) {
|
|
1339
|
-
console.log(pc4.dim(
|
|
1202
|
+
console.log(pc4.dim(" No installed skills found."));
|
|
1340
1203
|
}
|
|
1341
1204
|
console.log("");
|
|
1342
1205
|
}
|
|
@@ -1353,14 +1216,7 @@ function buildSections(scope, projectRoot) {
|
|
|
1353
1216
|
}
|
|
1354
1217
|
return sections;
|
|
1355
1218
|
}
|
|
1356
|
-
async function performListing(
|
|
1357
|
-
const normalizedType = type.toLowerCase().endsWith("s") ? type.slice(0, -1) : type.toLowerCase();
|
|
1358
|
-
if (!TYPE_DIRS[normalizedType]) {
|
|
1359
|
-
console.error(
|
|
1360
|
-
pc4.red(`Unknown type: ${type}. Supported types: skill, agent, workflow`)
|
|
1361
|
-
);
|
|
1362
|
-
return;
|
|
1363
|
-
}
|
|
1219
|
+
async function performListing(normalizedType) {
|
|
1364
1220
|
const dirPath = TYPE_DIRS[normalizedType];
|
|
1365
1221
|
if (!await fs7.pathExists(dirPath)) {
|
|
1366
1222
|
console.log(
|
|
@@ -1369,9 +1225,7 @@ async function performListing(type) {
|
|
|
1369
1225
|
return;
|
|
1370
1226
|
}
|
|
1371
1227
|
const entries = await fs7.readdir(dirPath, { withFileTypes: true });
|
|
1372
|
-
const items = entries.filter(
|
|
1373
|
-
(entry) => entry.isDirectory() || normalizedType !== "skill" && entry.isFile() && entry.name.endsWith(".md")
|
|
1374
|
-
).map((entry) => entry.name).sort();
|
|
1228
|
+
const items = entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort();
|
|
1375
1229
|
if (items.length > 0) {
|
|
1376
1230
|
console.log(pc4.bold(pc4.blue(`Available ${normalizedType}s:`)));
|
|
1377
1231
|
for (const item of items) {
|
|
@@ -1393,12 +1247,10 @@ async function list(type, options) {
|
|
|
1393
1247
|
try {
|
|
1394
1248
|
while (true) {
|
|
1395
1249
|
if (!currentType) {
|
|
1396
|
-
const selected = await
|
|
1250
|
+
const selected = await select2({
|
|
1397
1251
|
message: `What would you like to list? (View: ${isLocal ? "Local" : "Repository"})`,
|
|
1398
1252
|
options: [
|
|
1399
1253
|
{ label: "All Skills", value: "skill" },
|
|
1400
|
-
{ label: "All Agents", value: "agent" },
|
|
1401
|
-
{ label: "All Workflows", value: "workflow" },
|
|
1402
1254
|
{
|
|
1403
1255
|
label: `Switch to ${isLocal ? "Repository" : "Local"} View`,
|
|
1404
1256
|
value: "toggle"
|
|
@@ -1415,7 +1267,13 @@ async function list(type, options) {
|
|
|
1415
1267
|
}
|
|
1416
1268
|
currentType = selected;
|
|
1417
1269
|
}
|
|
1418
|
-
const
|
|
1270
|
+
const normalizedType = currentType.toLowerCase().endsWith("s") ? currentType.slice(0, -1) : currentType.toLowerCase();
|
|
1271
|
+
if (!TYPE_DIRS[normalizedType]) {
|
|
1272
|
+
console.error(
|
|
1273
|
+
pc4.red(`Unknown type: ${currentType}. Supported types: skill`)
|
|
1274
|
+
);
|
|
1275
|
+
break;
|
|
1276
|
+
}
|
|
1419
1277
|
if (isLocal) {
|
|
1420
1278
|
const scopeChoice = await chooseListRemoveScope({
|
|
1421
1279
|
action: "list",
|
|
@@ -1433,18 +1291,11 @@ async function list(type, options) {
|
|
|
1433
1291
|
currentType = void 0;
|
|
1434
1292
|
continue;
|
|
1435
1293
|
}
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
scopeChoice.scope,
|
|
1440
|
-
scopeChoice.projectRoot
|
|
1441
|
-
);
|
|
1442
|
-
await listLocalForScopes(normalizedType, sections);
|
|
1443
|
-
}
|
|
1294
|
+
await listLocalForScopes(
|
|
1295
|
+
buildSections(scopeChoice.scope, scopeChoice.projectRoot)
|
|
1296
|
+
);
|
|
1444
1297
|
} else {
|
|
1445
|
-
|
|
1446
|
-
await performListing(t);
|
|
1447
|
-
}
|
|
1298
|
+
await performListing(normalizedType);
|
|
1448
1299
|
}
|
|
1449
1300
|
if (isSingleShot) break;
|
|
1450
1301
|
currentType = void 0;
|
|
@@ -1467,7 +1318,6 @@ import {
|
|
|
1467
1318
|
isCancel as isCancel5,
|
|
1468
1319
|
note as note3,
|
|
1469
1320
|
outro as outro4,
|
|
1470
|
-
select as select5,
|
|
1471
1321
|
spinner as spinner3
|
|
1472
1322
|
} from "@clack/prompts";
|
|
1473
1323
|
import fs8 from "fs-extra";
|
|
@@ -1491,17 +1341,16 @@ function buildScopedBases(scope, homedir, projectRoot) {
|
|
|
1491
1341
|
}
|
|
1492
1342
|
return out;
|
|
1493
1343
|
}
|
|
1494
|
-
async function scanInstalledItems(scopedBases
|
|
1344
|
+
async function scanInstalledItems(scopedBases) {
|
|
1495
1345
|
const seen = /* @__PURE__ */ new Set();
|
|
1496
1346
|
const items = [];
|
|
1497
1347
|
const showScopePrefix = scopedBases.length > 1;
|
|
1498
1348
|
for (const { scope, base } of scopedBases) {
|
|
1499
|
-
const subdirPath = path8.join(base,
|
|
1349
|
+
const subdirPath = path8.join(base, "skills");
|
|
1500
1350
|
if (!await fs8.pathExists(subdirPath)) continue;
|
|
1501
1351
|
const entries = await fs8.readdir(subdirPath, { withFileTypes: true });
|
|
1502
1352
|
for (const entry of entries) {
|
|
1503
|
-
|
|
1504
|
-
if (!qualifies) continue;
|
|
1353
|
+
if (!entry.isDirectory()) continue;
|
|
1505
1354
|
const key = showScopePrefix ? `${scope}:${entry.name}` : entry.name;
|
|
1506
1355
|
if (seen.has(key)) continue;
|
|
1507
1356
|
seen.add(key);
|
|
@@ -1515,25 +1364,8 @@ async function scanInstalledItems(scopedBases, subdir, normalizedType) {
|
|
|
1515
1364
|
}
|
|
1516
1365
|
return items.sort((a, b) => a.label.localeCompare(b.label));
|
|
1517
1366
|
}
|
|
1518
|
-
function getPlatformDirs(scope, root
|
|
1519
|
-
|
|
1520
|
-
switch (normalizedType) {
|
|
1521
|
-
case "agent":
|
|
1522
|
-
return getProjectPlatformPathsAgents(root);
|
|
1523
|
-
case "workflow":
|
|
1524
|
-
return getProjectPlatformPathsWorkflows(root);
|
|
1525
|
-
default:
|
|
1526
|
-
return getProjectPlatformPathsSkills(root);
|
|
1527
|
-
}
|
|
1528
|
-
}
|
|
1529
|
-
switch (normalizedType) {
|
|
1530
|
-
case "agent":
|
|
1531
|
-
return PLATFORM_PATHS_AGENTS;
|
|
1532
|
-
case "workflow":
|
|
1533
|
-
return PLATFORM_PATHS_WORKFLOWS;
|
|
1534
|
-
default:
|
|
1535
|
-
return PLATFORM_PATHS_SKILLS;
|
|
1536
|
-
}
|
|
1367
|
+
function getPlatformDirs(scope, root) {
|
|
1368
|
+
return scope === "project" ? getProjectPlatformPathsSkills(root) : PLATFORM_PATHS_SKILLS;
|
|
1537
1369
|
}
|
|
1538
1370
|
async function pruneDanglingLinks(platformDirs, entry, removedAgentsPath) {
|
|
1539
1371
|
for (const dir of Object.values(platformDirs)) {
|
|
@@ -1554,147 +1386,109 @@ async function remove(type, options) {
|
|
|
1554
1386
|
if (!options?.skipIntro) {
|
|
1555
1387
|
intro4(pc5.bgCyan(pc5.black(" AI Manager : Remove ")));
|
|
1556
1388
|
}
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
}
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
)
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
if (isCancel5(confirmDelete) || !confirmDelete) {
|
|
1631
|
-
if (isSingleShot) break;
|
|
1632
|
-
currentType = void 0;
|
|
1633
|
-
continue;
|
|
1634
|
-
}
|
|
1635
|
-
const sDel = spinner3();
|
|
1636
|
-
sDel.start("Removing items...");
|
|
1637
|
-
let removedCount = 0;
|
|
1638
|
-
let notFoundCount = 0;
|
|
1639
|
-
const errors = [];
|
|
1640
|
-
const basesByScope = /* @__PURE__ */ new Map();
|
|
1641
|
-
for (const sb of scopedBases) {
|
|
1642
|
-
basesByScope.set(sb.scope, sb);
|
|
1643
|
-
}
|
|
1644
|
-
for (const item of selectedScannedItems) {
|
|
1645
|
-
const scoped = basesByScope.get(item.scope);
|
|
1646
|
-
if (!scoped) continue;
|
|
1647
|
-
const agentsPath = path8.join(scoped.base, subdir, item.name);
|
|
1648
|
-
if (await fs8.pathExists(agentsPath)) {
|
|
1649
|
-
sDel.message(`Removing ${pc5.bold(item.label)} from .agents...`);
|
|
1650
|
-
try {
|
|
1651
|
-
await fs8.remove(agentsPath);
|
|
1652
|
-
removedCount++;
|
|
1653
|
-
const platformDirs = getPlatformDirs(
|
|
1654
|
-
item.scope,
|
|
1655
|
-
scoped.root,
|
|
1656
|
-
normalizedType
|
|
1657
|
-
);
|
|
1658
|
-
await pruneDanglingLinks(platformDirs, item.name, agentsPath);
|
|
1659
|
-
} catch (err) {
|
|
1660
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
1661
|
-
errors.push(`${item.label}: ${msg}`);
|
|
1662
|
-
}
|
|
1663
|
-
} else {
|
|
1664
|
-
notFoundCount++;
|
|
1665
|
-
}
|
|
1666
|
-
}
|
|
1667
|
-
if (errors.length > 0) {
|
|
1668
|
-
sDel.stop(
|
|
1669
|
-
pc5.yellow(
|
|
1670
|
-
`Completed with errors. Removed: ${removedCount}, Not Found: ${notFoundCount}, Errors: ${errors.length}`
|
|
1671
|
-
)
|
|
1672
|
-
);
|
|
1673
|
-
console.error(pc5.red("\nErrors encountered:"));
|
|
1674
|
-
for (const e of errors) {
|
|
1675
|
-
console.error(pc5.red(`- ${e}`));
|
|
1389
|
+
await removeSkills(type, options);
|
|
1390
|
+
if (!options?.skipIntro) {
|
|
1391
|
+
outro4("Done!");
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
async function removeSkills(type, options) {
|
|
1395
|
+
const normalizedType = type?.toLowerCase().endsWith("s") ? type.slice(0, -1) : type;
|
|
1396
|
+
if (normalizedType && !TYPE_DIRS[normalizedType]) {
|
|
1397
|
+
cancel4(`Unknown type: ${type}. Supported: skill`);
|
|
1398
|
+
process.exit(1);
|
|
1399
|
+
return;
|
|
1400
|
+
}
|
|
1401
|
+
const scopeChoice = await chooseListRemoveScope({
|
|
1402
|
+
action: "remove",
|
|
1403
|
+
flag: options?.scope
|
|
1404
|
+
});
|
|
1405
|
+
if ("rejected" in scopeChoice) {
|
|
1406
|
+
cancel4(`Scope --scope=${options?.scope} unavailable: ${scopeChoice.reason}`);
|
|
1407
|
+
process.exit(1);
|
|
1408
|
+
return;
|
|
1409
|
+
}
|
|
1410
|
+
if (scopeChoice.cancelled) return;
|
|
1411
|
+
const scopedBases = buildScopedBases(
|
|
1412
|
+
scopeChoice.scope,
|
|
1413
|
+
scopeChoice.homedir,
|
|
1414
|
+
scopeChoice.projectRoot
|
|
1415
|
+
);
|
|
1416
|
+
const s = spinner3();
|
|
1417
|
+
s.start("Scanning for installed items...");
|
|
1418
|
+
const scannedItems = await scanInstalledItems(scopedBases);
|
|
1419
|
+
s.stop(`Found ${scannedItems.length} installed skills.`);
|
|
1420
|
+
if (scannedItems.length === 0) {
|
|
1421
|
+
note3("No installed skills found.", "Information");
|
|
1422
|
+
return;
|
|
1423
|
+
}
|
|
1424
|
+
const itemSelections = await autocompleteMultiselect2({
|
|
1425
|
+
message: "Select skills to remove:",
|
|
1426
|
+
options: scannedItems.map((item) => ({
|
|
1427
|
+
value: item.key,
|
|
1428
|
+
label: item.label
|
|
1429
|
+
}))
|
|
1430
|
+
});
|
|
1431
|
+
if (isCancel5(itemSelections)) return;
|
|
1432
|
+
const itemsByKey = new Map(scannedItems.map((i) => [i.key, i]));
|
|
1433
|
+
const selectedScannedItems = itemSelections.map((key) => itemsByKey.get(key)).filter((i) => !!i);
|
|
1434
|
+
const confirmDelete = await confirm3({
|
|
1435
|
+
message: `Are you sure you want to remove ${selectedScannedItems.length} item(s) from .agents?`,
|
|
1436
|
+
initialValue: false
|
|
1437
|
+
});
|
|
1438
|
+
if (isCancel5(confirmDelete) || !confirmDelete) return;
|
|
1439
|
+
const sDel = spinner3();
|
|
1440
|
+
sDel.start("Removing items...");
|
|
1441
|
+
let removedCount = 0;
|
|
1442
|
+
let notFoundCount = 0;
|
|
1443
|
+
const errors = [];
|
|
1444
|
+
const basesByScope = /* @__PURE__ */ new Map();
|
|
1445
|
+
for (const sb of scopedBases) {
|
|
1446
|
+
basesByScope.set(sb.scope, sb);
|
|
1447
|
+
}
|
|
1448
|
+
for (const item of selectedScannedItems) {
|
|
1449
|
+
const scoped = basesByScope.get(item.scope);
|
|
1450
|
+
if (!scoped) continue;
|
|
1451
|
+
const agentsPath = path8.join(scoped.base, "skills", item.name);
|
|
1452
|
+
if (await fs8.pathExists(agentsPath)) {
|
|
1453
|
+
sDel.message(`Removing ${pc5.bold(item.label)} from .agents...`);
|
|
1454
|
+
try {
|
|
1455
|
+
await fs8.remove(agentsPath);
|
|
1456
|
+
removedCount++;
|
|
1457
|
+
const platformDirs = getPlatformDirs(item.scope, scoped.root);
|
|
1458
|
+
await pruneDanglingLinks(platformDirs, item.name, agentsPath);
|
|
1459
|
+
} catch (err) {
|
|
1460
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1461
|
+
errors.push(`${item.label}: ${msg}`);
|
|
1676
1462
|
}
|
|
1677
1463
|
} else {
|
|
1678
|
-
|
|
1679
|
-
pc5.green(
|
|
1680
|
-
`Successfully removed ${removedCount} items. (${notFoundCount} not found)`
|
|
1681
|
-
)
|
|
1682
|
-
);
|
|
1464
|
+
notFoundCount++;
|
|
1683
1465
|
}
|
|
1684
|
-
if (isSingleShot) break;
|
|
1685
|
-
currentType = void 0;
|
|
1686
1466
|
}
|
|
1687
|
-
if (
|
|
1688
|
-
|
|
1467
|
+
if (errors.length > 0) {
|
|
1468
|
+
sDel.stop(
|
|
1469
|
+
pc5.yellow(
|
|
1470
|
+
`Completed with errors. Removed: ${removedCount}, Not Found: ${notFoundCount}, Errors: ${errors.length}`
|
|
1471
|
+
)
|
|
1472
|
+
);
|
|
1473
|
+
console.error(pc5.red("\nErrors encountered:"));
|
|
1474
|
+
for (const e of errors) {
|
|
1475
|
+
console.error(pc5.red(`- ${e}`));
|
|
1476
|
+
}
|
|
1477
|
+
} else {
|
|
1478
|
+
sDel.stop(
|
|
1479
|
+
pc5.green(
|
|
1480
|
+
`Successfully removed ${removedCount} items. (${notFoundCount} not found)`
|
|
1481
|
+
)
|
|
1482
|
+
);
|
|
1689
1483
|
}
|
|
1690
1484
|
}
|
|
1691
1485
|
|
|
1692
1486
|
// package.json
|
|
1693
|
-
var version = "
|
|
1487
|
+
var version = "3.0.1";
|
|
1694
1488
|
|
|
1695
1489
|
// bin/cli.ts
|
|
1696
1490
|
var program = new Command();
|
|
1697
|
-
program.name("ai-agent").description("CLI to manage AI
|
|
1491
|
+
program.name("ai-agent").description("CLI to manage AI agent skills").version(version);
|
|
1698
1492
|
function parseScopeFlag(value) {
|
|
1699
1493
|
if (value === void 0) return void 0;
|
|
1700
1494
|
if (isValidScopeFlag(value)) return value;
|
|
@@ -1716,13 +1510,13 @@ function parseListRemoveScopeFlag(value) {
|
|
|
1716
1510
|
async function interactiveMain() {
|
|
1717
1511
|
intro5(pc6.bgCyan(pc6.black(" AI Manager ")));
|
|
1718
1512
|
while (true) {
|
|
1719
|
-
const action = await
|
|
1513
|
+
const action = await select3({
|
|
1720
1514
|
message: "What would you like to do?",
|
|
1721
1515
|
options: [
|
|
1722
|
-
{ label: "Add
|
|
1723
|
-
{ label: "List
|
|
1516
|
+
{ label: "Add Skills (Install)", value: "add" },
|
|
1517
|
+
{ label: "List Skills", value: "list" },
|
|
1724
1518
|
{ label: "Import from GitHub", value: "import" },
|
|
1725
|
-
{ label: "Remove
|
|
1519
|
+
{ label: "Remove Skills", value: "remove" },
|
|
1726
1520
|
{ label: "Exit", value: "exit" }
|
|
1727
1521
|
]
|
|
1728
1522
|
});
|
|
@@ -1746,23 +1540,21 @@ async function interactiveMain() {
|
|
|
1746
1540
|
}
|
|
1747
1541
|
outro5("Goodbye!");
|
|
1748
1542
|
}
|
|
1749
|
-
program.command("list [type]").description("List available
|
|
1543
|
+
program.command("list [type]").description("List available skills").option("-l, --local", "List installed items locally").option(
|
|
1750
1544
|
"-s, --scope <scope>",
|
|
1751
1545
|
'Scope when listing local installs: "global", "project", or "both"'
|
|
1752
1546
|
).action(async (type, opts) => {
|
|
1753
1547
|
const scope = parseListRemoveScopeFlag(opts?.scope);
|
|
1754
1548
|
await list(type, { local: opts?.local, scope });
|
|
1755
1549
|
});
|
|
1756
|
-
program.command("add [type] [url]").description(
|
|
1757
|
-
"Add an item (skill, agent, workflow) to platforms (Interactive or from GitHub URL)"
|
|
1758
|
-
).option("-s, --scope <scope>", 'Install scope: "global" or "project"').action(async (type, url, opts) => {
|
|
1550
|
+
program.command("add [type] [url]").description("Add a skill to platforms (Interactive or from GitHub URL)").option("-s, --scope <scope>", 'Install scope: "global" or "project"').action(async (type, url, opts) => {
|
|
1759
1551
|
const scope = parseScopeFlag(opts?.scope);
|
|
1760
1552
|
await add(type, url, { scope });
|
|
1761
1553
|
});
|
|
1762
|
-
program.command("import [type] [url]").description("Import
|
|
1554
|
+
program.command("import [type] [url]").description("Import a skill from GitHub to the repo").action(async (type, url) => {
|
|
1763
1555
|
await importItem(type || "", url || "");
|
|
1764
1556
|
});
|
|
1765
|
-
program.command("remove [type]").description("Remove locally installed
|
|
1557
|
+
program.command("remove [type]").description("Remove locally installed skills").option(
|
|
1766
1558
|
"-s, --scope <scope>",
|
|
1767
1559
|
'Scope to remove from: "global", "project", or "both"'
|
|
1768
1560
|
).action(async (type, opts) => {
|