apero-kit-cli 2.3.0 → 2.4.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/dist/index.js
CHANGED
|
@@ -823,6 +823,167 @@ async function copyDiscordBaseFiles(destDir, mergeMode = false) {
|
|
|
823
823
|
}
|
|
824
824
|
return copied;
|
|
825
825
|
}
|
|
826
|
+
function extractKeywords(content, commandName) {
|
|
827
|
+
const keywords = /* @__PURE__ */ new Set();
|
|
828
|
+
keywords.add(commandName);
|
|
829
|
+
keywords.add(commandName.replace(/-/g, " "));
|
|
830
|
+
const keywordPatterns = [
|
|
831
|
+
/keywords?:\s*([^\n]+)/gi,
|
|
832
|
+
/when.*(?:says?|mentions?|asks?).*["']([^"']+)["']/gi,
|
|
833
|
+
/trigger.*["']([^"']+)["']/gi
|
|
834
|
+
];
|
|
835
|
+
for (const pattern of keywordPatterns) {
|
|
836
|
+
const matches = content.matchAll(pattern);
|
|
837
|
+
for (const match of matches) {
|
|
838
|
+
match[1].split(/[,;]/).forEach((k) => keywords.add(k.trim().toLowerCase()));
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
const intentMap = {
|
|
842
|
+
"plan": ["plan", "design", "architect", "implement", "create plan"],
|
|
843
|
+
"brainstorm": ["brainstorm", "ideas", "options", "alternatives", "think"],
|
|
844
|
+
"fix": ["fix", "debug", "error", "broken", "issue", "bug"],
|
|
845
|
+
"code": ["code", "implement", "build", "develop", "write code"],
|
|
846
|
+
"review": ["review", "check", "audit", "look at"],
|
|
847
|
+
"test": ["test", "testing", "spec", "unit test"],
|
|
848
|
+
"cook": ["cook", "implement", "build feature", "develop"],
|
|
849
|
+
"scout": ["scout", "search", "find", "explore codebase"],
|
|
850
|
+
"debug": ["debug", "trace", "diagnose", "investigate"]
|
|
851
|
+
};
|
|
852
|
+
const baseName = commandName.split("/")[0].split(":")[0];
|
|
853
|
+
if (intentMap[baseName]) {
|
|
854
|
+
intentMap[baseName].forEach((k) => keywords.add(k));
|
|
855
|
+
}
|
|
856
|
+
return Array.from(keywords).slice(0, 10);
|
|
857
|
+
}
|
|
858
|
+
function convertCommandToSkill(mdContent, commandName) {
|
|
859
|
+
const { description, argumentHint, body } = parseFrontmatter(mdContent);
|
|
860
|
+
const prompt = body.replace(/\$ARGUMENTS/g, "{{args}}");
|
|
861
|
+
const keywords = extractKeywords(body, commandName);
|
|
862
|
+
const skillContent = `---
|
|
863
|
+
name: ${commandName.replace(/\//g, "-")}
|
|
864
|
+
description: ${description || `Execute ${commandName} task`}
|
|
865
|
+
user-invocable: true
|
|
866
|
+
disable-model-invocation: false
|
|
867
|
+
metadata: {"openclaw": {"always": true}}
|
|
868
|
+
---
|
|
869
|
+
|
|
870
|
+
# ${commandName.charAt(0).toUpperCase() + commandName.slice(1).replace(/-/g, " ")}
|
|
871
|
+
|
|
872
|
+
## Trigger Conditions
|
|
873
|
+
|
|
874
|
+
Activate when user mentions:
|
|
875
|
+
${keywords.map((k) => `- "${k}"`).join("\n")}
|
|
876
|
+
|
|
877
|
+
## Input
|
|
878
|
+
${argumentHint ? `Expected input: ${argumentHint.replace("$ARGUMENTS", "{{args}}")}` : "User provides task description in natural language."}
|
|
879
|
+
|
|
880
|
+
## Workflow
|
|
881
|
+
|
|
882
|
+
${prompt}
|
|
883
|
+
|
|
884
|
+
## Output Format
|
|
885
|
+
|
|
886
|
+
Provide clear, actionable response based on the workflow above.
|
|
887
|
+
`;
|
|
888
|
+
return skillContent;
|
|
889
|
+
}
|
|
890
|
+
async function convertCommandsToSkills(items, sourceDir, destDir, mergeMode = false) {
|
|
891
|
+
const typeDir = join2(sourceDir, "commands");
|
|
892
|
+
const destTypeDir = join2(destDir, "skills");
|
|
893
|
+
if (!fs.existsSync(typeDir)) {
|
|
894
|
+
return { copied: [], skipped: [], errors: [] };
|
|
895
|
+
}
|
|
896
|
+
await fs.ensureDir(destTypeDir);
|
|
897
|
+
const copied = [];
|
|
898
|
+
const skipped = [];
|
|
899
|
+
const errors = [];
|
|
900
|
+
let itemList;
|
|
901
|
+
if (items === "all") {
|
|
902
|
+
const entries = fs.readdirSync(typeDir);
|
|
903
|
+
itemList = entries.filter((e) => e.endsWith(".md") && e !== "README.md").map((e) => e.replace(/\.md$/, ""));
|
|
904
|
+
const dirs = entries.filter((e) => {
|
|
905
|
+
const fullPath = join2(typeDir, e);
|
|
906
|
+
return fs.statSync(fullPath).isDirectory();
|
|
907
|
+
});
|
|
908
|
+
itemList = [.../* @__PURE__ */ new Set([...itemList, ...dirs])];
|
|
909
|
+
} else {
|
|
910
|
+
itemList = items;
|
|
911
|
+
}
|
|
912
|
+
for (const item of itemList) {
|
|
913
|
+
try {
|
|
914
|
+
const srcPathMd = join2(typeDir, item + ".md");
|
|
915
|
+
const srcPathDir = join2(typeDir, item);
|
|
916
|
+
if (fs.existsSync(srcPathMd) && fs.statSync(srcPathMd).isFile()) {
|
|
917
|
+
const skillDir = join2(destTypeDir, item.replace(/\//g, "-"));
|
|
918
|
+
const skillPath = join2(skillDir, "SKILL.md");
|
|
919
|
+
if (mergeMode && fs.existsSync(skillPath)) {
|
|
920
|
+
skipped.push(item);
|
|
921
|
+
continue;
|
|
922
|
+
}
|
|
923
|
+
await fs.ensureDir(skillDir);
|
|
924
|
+
const mdContent = fs.readFileSync(srcPathMd, "utf-8");
|
|
925
|
+
const skillContent = convertCommandToSkill(mdContent, item);
|
|
926
|
+
await fs.writeFile(skillPath, skillContent, "utf-8");
|
|
927
|
+
copied.push(item);
|
|
928
|
+
}
|
|
929
|
+
if (fs.existsSync(srcPathDir) && fs.statSync(srcPathDir).isDirectory()) {
|
|
930
|
+
await convertNestedCommandsToSkills(srcPathDir, destTypeDir, item, mergeMode);
|
|
931
|
+
copied.push(item + "/*");
|
|
932
|
+
}
|
|
933
|
+
} catch (err) {
|
|
934
|
+
errors.push({ item, error: err.message });
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
return { copied, skipped, errors };
|
|
938
|
+
}
|
|
939
|
+
async function convertNestedCommandsToSkills(srcDir, destDir, parentName, mergeMode) {
|
|
940
|
+
const entries = fs.readdirSync(srcDir);
|
|
941
|
+
for (const entry of entries) {
|
|
942
|
+
const srcPath = join2(srcDir, entry);
|
|
943
|
+
const stat = fs.statSync(srcPath);
|
|
944
|
+
if (stat.isDirectory()) {
|
|
945
|
+
await convertNestedCommandsToSkills(
|
|
946
|
+
srcPath,
|
|
947
|
+
destDir,
|
|
948
|
+
`${parentName}-${entry}`,
|
|
949
|
+
mergeMode
|
|
950
|
+
);
|
|
951
|
+
} else if (entry.endsWith(".md") && entry !== "README.md") {
|
|
952
|
+
const skillName = `${parentName}-${entry.replace(/\.md$/, "")}`;
|
|
953
|
+
const skillDir = join2(destDir, skillName);
|
|
954
|
+
const skillPath = join2(skillDir, "SKILL.md");
|
|
955
|
+
if (mergeMode && fs.existsSync(skillPath)) {
|
|
956
|
+
continue;
|
|
957
|
+
}
|
|
958
|
+
await fs.ensureDir(skillDir);
|
|
959
|
+
const mdContent = fs.readFileSync(srcPath, "utf-8");
|
|
960
|
+
const skillContent = convertCommandToSkill(mdContent, skillName);
|
|
961
|
+
await fs.writeFile(skillPath, skillContent, "utf-8");
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
async function copyBundledSkillsForDiscord(destDir, mergeMode = false) {
|
|
966
|
+
const bundledSkillsDir = join2(CLI_ROOT, "templates", "discord", "skills");
|
|
967
|
+
const destSkillsDir = join2(destDir, "skills");
|
|
968
|
+
const copied = [];
|
|
969
|
+
if (!fs.existsSync(bundledSkillsDir)) {
|
|
970
|
+
return copied;
|
|
971
|
+
}
|
|
972
|
+
await fs.ensureDir(destSkillsDir);
|
|
973
|
+
const skills = fs.readdirSync(bundledSkillsDir);
|
|
974
|
+
for (const skill of skills) {
|
|
975
|
+
const srcPath = join2(bundledSkillsDir, skill);
|
|
976
|
+
const destPath = join2(destSkillsDir, skill);
|
|
977
|
+
if (fs.statSync(srcPath).isDirectory()) {
|
|
978
|
+
if (mergeMode && fs.existsSync(destPath)) {
|
|
979
|
+
continue;
|
|
980
|
+
}
|
|
981
|
+
await fs.copy(srcPath, destPath, { overwrite: !mergeMode });
|
|
982
|
+
copied.push(skill);
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
return copied;
|
|
986
|
+
}
|
|
826
987
|
var init_copy = __esm({
|
|
827
988
|
"src/utils/copy.ts"() {
|
|
828
989
|
"use strict";
|
|
@@ -1325,6 +1486,10 @@ async function initCommand(projectName, options) {
|
|
|
1325
1486
|
await copyCommandsForGemini(toInstall.commands, source.claudeDir, targetDir, mergeMode);
|
|
1326
1487
|
} else if (target === "discord") {
|
|
1327
1488
|
await copyCommandsForDiscord(toInstall.commands, source.claudeDir, targetDir, mergeMode);
|
|
1489
|
+
spinner.text = mergeMode ? `Converting commands to skills (${targetLabel})...` : `Converting commands to skills (${targetLabel})...`;
|
|
1490
|
+
await convertCommandsToSkills(toInstall.commands, source.claudeDir, targetDir, mergeMode);
|
|
1491
|
+
spinner.text = `Copying bundled skills (${targetLabel})...`;
|
|
1492
|
+
await copyBundledSkillsForDiscord(targetDir, mergeMode);
|
|
1328
1493
|
} else {
|
|
1329
1494
|
if (toInstall.commands === "all") {
|
|
1330
1495
|
await copyAllOfType("commands", source.claudeDir, targetDir, mergeMode);
|