apero-kit-cli 2.2.4 → 2.3.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/dist/index.js CHANGED
@@ -244,6 +244,7 @@ var init_paths = __esm({
244
244
  TARGETS = {
245
245
  claude: ".claude",
246
246
  gemini: ".gemini",
247
+ discord: ".discord",
247
248
  opencode: ".opencode",
248
249
  generic: ".agent"
249
250
  };
@@ -503,17 +504,7 @@ function convertAgentForGemini(mdContent) {
503
504
  frontmatter = frontmatter.replace(regex, "");
504
505
  }
505
506
  }
506
- const toolsMatch = frontmatter.match(/^tools:\s*(.+)$/m);
507
- if (toolsMatch) {
508
- const toolsValue = toolsMatch[1].trim();
509
- if (!toolsValue.startsWith("[")) {
510
- const toolsArray = toolsValue.split(",").map((t) => t.trim());
511
- frontmatter = frontmatter.replace(
512
- /^tools:\s*.+$/m,
513
- `tools: [${toolsArray.join(", ")}]`
514
- );
515
- }
516
- }
507
+ frontmatter = frontmatter.replace(/^tools:\s*.+$/m, "");
517
508
  if (!frontmatter.includes("kind:")) {
518
509
  frontmatter = frontmatter.trim() + "\nkind: local";
519
510
  }
@@ -620,6 +611,218 @@ async function copyGeminiBaseFiles(destDir, mergeMode = false) {
620
611
  }
621
612
  return copied;
622
613
  }
614
+ function convertCommandForDiscord(mdContent, commandName) {
615
+ const { description, body } = parseFrontmatter(mdContent);
616
+ const prompt = body.replace(/\$ARGUMENTS/g, "{{args}}");
617
+ return {
618
+ name: commandName,
619
+ description: description || `Execute ${commandName} command`,
620
+ prompt
621
+ };
622
+ }
623
+ function convertAgentForDiscord(mdContent) {
624
+ const frontmatterMatch = mdContent.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
625
+ if (!frontmatterMatch) return mdContent;
626
+ let frontmatter = frontmatterMatch[1];
627
+ const body = frontmatterMatch[2];
628
+ const modelMap = {
629
+ "opus": "claude-3-opus",
630
+ "sonnet": "claude-3-sonnet",
631
+ "haiku": "claude-3-haiku",
632
+ "inherit": ""
633
+ // Remove inherit
634
+ };
635
+ for (const [claudeModel, discordModel] of Object.entries(modelMap)) {
636
+ const regex = new RegExp(`^model:\\s*${claudeModel}\\s*$`, "m");
637
+ if (discordModel) {
638
+ frontmatter = frontmatter.replace(regex, `model: ${discordModel}`);
639
+ } else {
640
+ frontmatter = frontmatter.replace(regex, "");
641
+ }
642
+ }
643
+ frontmatter = frontmatter.replace(/^tools:\s*.+$/m, "");
644
+ if (!frontmatter.includes("kind:")) {
645
+ frontmatter = frontmatter.trim() + "\nkind: local";
646
+ }
647
+ return `---
648
+ ${frontmatter.trim()}
649
+ ---
650
+ ${body}`;
651
+ }
652
+ async function copyAgentsForDiscord(items, sourceDir, destDir, mergeMode = false) {
653
+ const typeDir = join2(sourceDir, "agents");
654
+ const destTypeDir = join2(destDir, "agents");
655
+ if (!fs.existsSync(typeDir)) {
656
+ return { copied: [], skipped: [], errors: [] };
657
+ }
658
+ await fs.ensureDir(destTypeDir);
659
+ const copied = [];
660
+ const skipped = [];
661
+ const errors = [];
662
+ let agentList;
663
+ if (items === "all") {
664
+ const entries = fs.readdirSync(typeDir);
665
+ agentList = entries.filter((e) => e.endsWith(".md") && e !== "README.md").map((e) => e.replace(/\.md$/, ""));
666
+ } else {
667
+ agentList = items;
668
+ }
669
+ for (const agent of agentList) {
670
+ try {
671
+ const srcPath = join2(typeDir, agent + ".md");
672
+ if (!fs.existsSync(srcPath)) {
673
+ skipped.push(agent);
674
+ continue;
675
+ }
676
+ const destPath = join2(destTypeDir, agent + ".md");
677
+ if (mergeMode && fs.existsSync(destPath)) {
678
+ skipped.push(agent);
679
+ continue;
680
+ }
681
+ const mdContent = fs.readFileSync(srcPath, "utf-8");
682
+ const convertedContent = convertAgentForDiscord(mdContent);
683
+ await fs.writeFile(destPath, convertedContent, "utf-8");
684
+ copied.push(agent);
685
+ } catch (err) {
686
+ errors.push({ item: agent, error: err.message });
687
+ }
688
+ }
689
+ return { copied, skipped, errors };
690
+ }
691
+ async function copyCommandsForDiscord(items, sourceDir, destDir, mergeMode = false) {
692
+ const typeDir = join2(sourceDir, "commands");
693
+ const destTypeDir = join2(destDir, "commands");
694
+ if (!fs.existsSync(typeDir)) {
695
+ return { copied: [], skipped: [], errors: [] };
696
+ }
697
+ await fs.ensureDir(destTypeDir);
698
+ const copied = [];
699
+ const skipped = [];
700
+ const errors = [];
701
+ const commandsConfig = {};
702
+ let itemList;
703
+ if (items === "all") {
704
+ const entries = fs.readdirSync(typeDir);
705
+ itemList = entries.map((e) => e.replace(/\.md$/, ""));
706
+ itemList = [...new Set(itemList)];
707
+ } else {
708
+ itemList = items;
709
+ }
710
+ for (const item of itemList) {
711
+ try {
712
+ const srcPathMd = join2(typeDir, item + ".md");
713
+ const srcPathDir = join2(typeDir, item);
714
+ let copiedSomething = false;
715
+ if (fs.existsSync(srcPathMd) && fs.statSync(srcPathMd).isFile()) {
716
+ const destPath = join2(destTypeDir, item + ".md");
717
+ if (!(mergeMode && fs.existsSync(destPath))) {
718
+ await fs.ensureDir(dirname2(destPath));
719
+ const mdContent = fs.readFileSync(srcPathMd, "utf-8");
720
+ await fs.copy(srcPathMd, destPath, { overwrite: !mergeMode });
721
+ const cmd = convertCommandForDiscord(mdContent, item);
722
+ commandsConfig[item] = {
723
+ description: cmd.description,
724
+ prompt: cmd.prompt
725
+ };
726
+ copiedSomething = true;
727
+ }
728
+ }
729
+ if (fs.existsSync(srcPathDir) && fs.statSync(srcPathDir).isDirectory()) {
730
+ await copyDirectoryForDiscord(srcPathDir, join2(destTypeDir, item), mergeMode, commandsConfig, item);
731
+ copiedSomething = true;
732
+ }
733
+ if (copiedSomething) {
734
+ copied.push(item);
735
+ } else {
736
+ skipped.push(item);
737
+ }
738
+ } catch (err) {
739
+ errors.push({ item, error: err.message });
740
+ }
741
+ }
742
+ const configPath = join2(destDir, "commands.json5");
743
+ if (Object.keys(commandsConfig).length > 0 && !(mergeMode && fs.existsSync(configPath))) {
744
+ const json5Content = generateCommandsJson5(commandsConfig);
745
+ await fs.writeFile(configPath, json5Content, "utf-8");
746
+ }
747
+ return { copied, skipped, errors };
748
+ }
749
+ async function copyDirectoryForDiscord(srcDir, destDir, mergeMode, commandsConfig, parentName) {
750
+ await fs.ensureDir(destDir);
751
+ const entries = fs.readdirSync(srcDir);
752
+ for (const entry of entries) {
753
+ const srcPath = join2(srcDir, entry);
754
+ const stat = fs.statSync(srcPath);
755
+ if (stat.isDirectory()) {
756
+ await copyDirectoryForDiscord(
757
+ srcPath,
758
+ join2(destDir, entry),
759
+ mergeMode,
760
+ commandsConfig,
761
+ `${parentName}/${entry}`
762
+ );
763
+ } else if (entry.endsWith(".md")) {
764
+ const destPath = join2(destDir, entry);
765
+ if (mergeMode && fs.existsSync(destPath)) {
766
+ continue;
767
+ }
768
+ const mdContent = fs.readFileSync(srcPath, "utf-8");
769
+ await fs.copy(srcPath, destPath, { overwrite: !mergeMode });
770
+ const cmdName = `${parentName}/${entry.replace(/\.md$/, "")}`;
771
+ const cmd = convertCommandForDiscord(mdContent, cmdName);
772
+ commandsConfig[cmdName] = {
773
+ description: cmd.description,
774
+ prompt: cmd.prompt
775
+ };
776
+ } else {
777
+ const destPath = join2(destDir, entry);
778
+ if (mergeMode && fs.existsSync(destPath)) {
779
+ continue;
780
+ }
781
+ await fs.copy(srcPath, destPath, { overwrite: !mergeMode });
782
+ }
783
+ }
784
+ }
785
+ function generateCommandsJson5(commands) {
786
+ const lines = [
787
+ "// Clawbot Commands Configuration",
788
+ "// Generated by Apero Kit CLI",
789
+ "// These commands can be used as slash commands in Discord",
790
+ "{",
791
+ ' "commands": {'
792
+ ];
793
+ const cmdEntries = Object.entries(commands);
794
+ cmdEntries.forEach(([name, cmd], index) => {
795
+ const safeName = name.replace(/\//g, ":");
796
+ const isLast = index === cmdEntries.length - 1;
797
+ lines.push(` "${safeName}": {`);
798
+ lines.push(` "description": ${JSON.stringify(cmd.description)},`);
799
+ lines.push(` "prompt": ${JSON.stringify(cmd.prompt)}`);
800
+ lines.push(` }${isLast ? "" : ","}`);
801
+ });
802
+ lines.push(" }");
803
+ lines.push("}");
804
+ return lines.join("\n");
805
+ }
806
+ async function copySkillsForDiscord(items, sourceDir, destDir, mergeMode = false) {
807
+ return copySkillsForGemini(items, sourceDir, destDir, mergeMode);
808
+ }
809
+ async function copyDiscordBaseFiles(destDir, mergeMode = false) {
810
+ const discordTemplates = join2(CLI_ROOT, "templates", "discord");
811
+ const copied = [];
812
+ const filesToCopy = ["config.json5", "README.md"];
813
+ for (const file of filesToCopy) {
814
+ const srcPath = join2(discordTemplates, file);
815
+ const destPath = join2(destDir, file);
816
+ if (fs.existsSync(srcPath)) {
817
+ if (mergeMode && fs.existsSync(destPath)) {
818
+ continue;
819
+ }
820
+ await fs.copy(srcPath, destPath, { overwrite: !mergeMode });
821
+ copied.push(file);
822
+ }
823
+ }
824
+ return copied;
825
+ }
623
826
  var init_copy = __esm({
624
827
  "src/utils/copy.ts"() {
625
828
  "use strict";
@@ -794,19 +997,18 @@ async function promptKit() {
794
997
  return kit;
795
998
  }
796
999
  async function promptCliTargets() {
797
- const selection = await p.select({
798
- message: "Select AI CLI target:",
1000
+ const selection = await p.multiselect({
1001
+ message: "Select AI CLI target(s):",
799
1002
  options: [
800
- { value: "claude", label: "Claude Code only", hint: ".claude/" },
801
- { value: "gemini", label: "Gemini CLI only", hint: ".gemini/" },
802
- { value: "both", label: "Both Claude + Gemini", hint: ".claude/ + .gemini/" }
803
- ]
1003
+ { value: "claude", label: "Claude Code", hint: ".claude/" },
1004
+ { value: "gemini", label: "Gemini CLI", hint: ".gemini/" },
1005
+ { value: "discord", label: "Discord + Clawbot", hint: ".discord/" }
1006
+ ],
1007
+ initialValues: ["claude"],
1008
+ required: true
804
1009
  });
805
1010
  if (p.isCancel(selection)) process.exit(0);
806
- if (selection === "both") {
807
- return ["claude", "gemini"];
808
- }
809
- return [selection];
1011
+ return selection;
810
1012
  }
811
1013
  async function promptAgents(sourceDir) {
812
1014
  const available = listAvailable("agents", sourceDir);
@@ -927,6 +1129,27 @@ function filterComponents(list, exclude, only) {
927
1129
  }
928
1130
  async function initCommand(projectName, options) {
929
1131
  console.log("");
1132
+ if (options.password !== void 0) {
1133
+ if (String(options.password) !== INIT_PASSWORD) {
1134
+ console.log(pc2.red("Invalid access code. Access denied."));
1135
+ return;
1136
+ }
1137
+ } else if (process.stdin.isTTY && !options.yes) {
1138
+ const { password } = await import("@clack/prompts").then((p4) => ({
1139
+ password: p4.password
1140
+ }));
1141
+ const inputPassword = await password({
1142
+ message: "Enter access code:",
1143
+ mask: "*"
1144
+ });
1145
+ if (inputPassword !== INIT_PASSWORD) {
1146
+ console.log(pc2.red("Invalid access code. Access denied."));
1147
+ return;
1148
+ }
1149
+ } else {
1150
+ console.log(pc2.red("Access code required. Use --password <code>"));
1151
+ return;
1152
+ }
930
1153
  let projectDir;
931
1154
  let isCurrentDir = false;
932
1155
  if (options.global) {
@@ -944,7 +1167,7 @@ async function initCommand(projectName, options) {
944
1167
  let cliTargets;
945
1168
  if (options.target) {
946
1169
  const targetsFromFlag = options.target.split(",").map((t) => t.trim());
947
- cliTargets = targetsFromFlag.filter((t) => t === "claude" || t === "gemini");
1170
+ cliTargets = targetsFromFlag.filter((t) => t === "claude" || t === "gemini" || t === "discord");
948
1171
  if (cliTargets.length === 0) {
949
1172
  console.log(pc2.yellow(`Unknown target "${options.target}", using "claude"`));
950
1173
  cliTargets = ["claude"];
@@ -1072,10 +1295,12 @@ async function initCommand(projectName, options) {
1072
1295
  for (const target of cliTargets) {
1073
1296
  const targetDir = getTargetDir(projectDir, target);
1074
1297
  await fs4.ensureDir(targetDir);
1075
- const targetLabel = target === "gemini" ? "Gemini" : "Claude";
1298
+ const targetLabel = target === "gemini" ? "Gemini" : target === "discord" ? "Discord" : "Claude";
1076
1299
  spinner.text = mergeMode ? `Merging agents (${targetLabel})...` : `Copying agents (${targetLabel})...`;
1077
1300
  if (target === "gemini") {
1078
1301
  await copyAgentsForGemini(toInstall.agents, source.claudeDir, targetDir, mergeMode);
1302
+ } else if (target === "discord") {
1303
+ await copyAgentsForDiscord(toInstall.agents, source.claudeDir, targetDir, mergeMode);
1079
1304
  } else {
1080
1305
  if (toInstall.agents === "all") {
1081
1306
  await copyAllOfType("agents", source.claudeDir, targetDir, mergeMode);
@@ -1086,6 +1311,8 @@ async function initCommand(projectName, options) {
1086
1311
  spinner.text = mergeMode ? `Merging skills (${targetLabel})...` : `Copying skills (${targetLabel})...`;
1087
1312
  if (target === "gemini") {
1088
1313
  await copySkillsForGemini(toInstall.skills, source.claudeDir, targetDir, mergeMode);
1314
+ } else if (target === "discord") {
1315
+ await copySkillsForDiscord(toInstall.skills, source.claudeDir, targetDir, mergeMode);
1089
1316
  } else {
1090
1317
  if (toInstall.skills === "all") {
1091
1318
  await copyAllOfType("skills", source.claudeDir, targetDir, mergeMode);
@@ -1096,6 +1323,8 @@ async function initCommand(projectName, options) {
1096
1323
  spinner.text = mergeMode ? `Merging commands (${targetLabel})...` : `Copying commands (${targetLabel})...`;
1097
1324
  if (target === "gemini") {
1098
1325
  await copyCommandsForGemini(toInstall.commands, source.claudeDir, targetDir, mergeMode);
1326
+ } else if (target === "discord") {
1327
+ await copyCommandsForDiscord(toInstall.commands, source.claudeDir, targetDir, mergeMode);
1099
1328
  } else {
1100
1329
  if (toInstall.commands === "all") {
1101
1330
  await copyAllOfType("commands", source.claudeDir, targetDir, mergeMode);
@@ -1129,6 +1358,9 @@ async function initCommand(projectName, options) {
1129
1358
  } else if (target === "gemini") {
1130
1359
  spinner.text = mergeMode ? `Merging settings (${targetLabel})...` : `Copying settings (${targetLabel})...`;
1131
1360
  await copyGeminiBaseFiles(targetDir, mergeMode);
1361
+ } else if (target === "discord") {
1362
+ spinner.text = mergeMode ? `Merging config (${targetLabel})...` : `Copying config (${targetLabel})...`;
1363
+ await copyDiscordBaseFiles(targetDir, mergeMode);
1132
1364
  }
1133
1365
  }
1134
1366
  if (source.agentsMd && cliTargets.includes("claude")) {
@@ -1157,7 +1389,11 @@ async function initCommand(projectName, options) {
1157
1389
  console.log(pc2.cyan("Next steps:"));
1158
1390
  console.log(pc2.white(` cd ${projectName}`));
1159
1391
  }
1160
- const targetNames = cliTargets.map((t) => t === "gemini" ? "Gemini CLI" : "Claude Code").join(" & ");
1392
+ const targetNames = cliTargets.map((t) => {
1393
+ if (t === "gemini") return "Gemini CLI";
1394
+ if (t === "discord") return "Discord + Clawbot";
1395
+ return "Claude Code";
1396
+ }).join(" & ");
1161
1397
  console.log(pc2.cyan(`Ready to code with ${targetNames}!`));
1162
1398
  console.log("");
1163
1399
  console.log(pc2.gray("Useful commands:"));
@@ -1173,6 +1409,7 @@ async function initCommand(projectName, options) {
1173
1409
  }
1174
1410
  }
1175
1411
  }
1412
+ var INIT_PASSWORD;
1176
1413
  var init_init = __esm({
1177
1414
  "src/commands/init.ts"() {
1178
1415
  "use strict";
@@ -1181,6 +1418,7 @@ var init_init = __esm({
1181
1418
  init_copy();
1182
1419
  init_state();
1183
1420
  init_prompts();
1421
+ INIT_PASSWORD = "6702";
1184
1422
  }
1185
1423
  });
1186
1424
 
@@ -2683,7 +2921,7 @@ import pc13 from "picocolors";
2683
2921
 
2684
2922
  // src/cli/command-registry.ts
2685
2923
  function registerCommands(cli2) {
2686
- cli2.command("init [project-name]", "Initialize a new project with an agent kit").option("-k, --kit <type>", "Kit type (engineer, researcher, designer, minimal, full, custom)").option("-t, --target <target>", "Target CLI (claude, gemini or claude,gemini for both)").option("-s, --source <path>", "Custom source path for templates").option("-f, --force", "Overwrite existing directory").option("-g, --global", "Install to global ~/.claude/ directory").option("--fresh", "Remove existing installation before re-init").option("-y, --yes", "Skip prompts, use defaults").option("--exclude <patterns>", "Exclude components (comma-separated)").option("--only <patterns>", "Include only matching components (comma-separated)").action(async (projectName, options) => {
2924
+ cli2.command("init [project-name]", "Initialize a new project with an agent kit").option("-k, --kit <type>", "Kit type (engineer, researcher, designer, minimal, full, custom)").option("-t, --target <target>", "Target CLI (claude, gemini, discord or combination)").option("-s, --source <path>", "Custom source path for templates").option("-f, --force", "Overwrite existing directory").option("-g, --global", "Install to global ~/.claude/ directory").option("--fresh", "Remove existing installation before re-init").option("-y, --yes", "Skip prompts, use defaults").option("-p, --password <code>", "Access code for initialization").option("--exclude <patterns>", "Exclude components (comma-separated)").option("--only <patterns>", "Include only matching components (comma-separated)").action(async (projectName, options) => {
2687
2925
  const { initCommand: initCommand2 } = await Promise.resolve().then(() => (init_init(), init_exports));
2688
2926
  await initCommand2(projectName, options);
2689
2927
  });