apero-kit-cli 2.1.0 → 2.2.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
@@ -182,9 +182,10 @@ function getGlobalInstallPath() {
182
182
  function isAkProject(dir = process.cwd()) {
183
183
  const akConfig = join(dir, ".ak", "state.json");
184
184
  const claudeDir = join(dir, ".claude");
185
+ const geminiDir = join(dir, ".gemini");
185
186
  const opencodeDir = join(dir, ".opencode");
186
187
  const agentDir = join(dir, ".agent");
187
- return existsSync(akConfig) || existsSync(claudeDir) || existsSync(opencodeDir) || existsSync(agentDir);
188
+ return existsSync(akConfig) || existsSync(claudeDir) || existsSync(geminiDir) || existsSync(opencodeDir) || existsSync(agentDir);
188
189
  }
189
190
  function getTargetDir(projectDir, target = "claude") {
190
191
  const folder = TARGETS[target] || TARGETS.claude;
@@ -242,6 +243,7 @@ var init_paths = __esm({
242
243
  TEMPLATES_DIR = join(CLI_ROOT, "templates");
243
244
  TARGETS = {
244
245
  claude: ".claude",
246
+ gemini: ".gemini",
245
247
  opencode: ".opencode",
246
248
  generic: ".agent"
247
249
  };
@@ -250,7 +252,37 @@ var init_paths = __esm({
250
252
 
251
253
  // src/utils/copy.ts
252
254
  import fs from "fs-extra";
253
- import { join as join2 } from "path";
255
+ import { join as join2, dirname as dirname2 } from "path";
256
+ function parseFrontmatter(content) {
257
+ const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
258
+ if (!frontmatterMatch) {
259
+ return { description: "", argumentHint: "", body: content };
260
+ }
261
+ const frontmatter = frontmatterMatch[1];
262
+ const body = frontmatterMatch[2].trim();
263
+ const descMatch = frontmatter.match(/description:\s*(.+)/);
264
+ const argMatch = frontmatter.match(/argument-hint:\s*(.+)/);
265
+ return {
266
+ description: descMatch ? descMatch[1].trim() : "",
267
+ argumentHint: argMatch ? argMatch[1].trim() : "",
268
+ body
269
+ };
270
+ }
271
+ function escapeTomlString(str) {
272
+ return str.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
273
+ }
274
+ function convertMdToToml(mdContent) {
275
+ const { description, body } = parseFrontmatter(mdContent);
276
+ const prompt = body.replace(/\$ARGUMENTS/g, "{{args}}");
277
+ const lines = [];
278
+ if (description) {
279
+ lines.push(`description = "${escapeTomlString(description)}"`);
280
+ }
281
+ lines.push(`prompt = """
282
+ ${prompt}
283
+ """`);
284
+ return lines.join("\n");
285
+ }
254
286
  async function copyItems(items, type, sourceDir, destDir, mergeMode = false) {
255
287
  const typeDir = join2(sourceDir, type);
256
288
  const destTypeDir = join2(destDir, type);
@@ -378,9 +410,209 @@ function listAvailable(type, sourceDir) {
378
410
  return { name, isDir, path: itemPath };
379
411
  });
380
412
  }
413
+ async function copyCommandsForGemini(items, sourceDir, destDir, mergeMode = false) {
414
+ const typeDir = join2(sourceDir, "commands");
415
+ const destTypeDir = join2(destDir, "commands");
416
+ if (!fs.existsSync(typeDir)) {
417
+ return { copied: [], skipped: [], errors: [] };
418
+ }
419
+ await fs.ensureDir(destTypeDir);
420
+ const copied = [];
421
+ const skipped = [];
422
+ const errors = [];
423
+ let itemList;
424
+ if (items === "all") {
425
+ const entries = fs.readdirSync(typeDir);
426
+ itemList = entries.map((e) => e.replace(/\.md$/, ""));
427
+ itemList = [...new Set(itemList)];
428
+ } else {
429
+ itemList = items;
430
+ }
431
+ for (const item of itemList) {
432
+ try {
433
+ const srcPathMd = join2(typeDir, item + ".md");
434
+ const srcPathDir = join2(typeDir, item);
435
+ let copiedSomething = false;
436
+ if (fs.existsSync(srcPathMd) && fs.statSync(srcPathMd).isFile()) {
437
+ const destPath = join2(destTypeDir, item + ".toml");
438
+ if (!(mergeMode && fs.existsSync(destPath))) {
439
+ await fs.ensureDir(dirname2(destPath));
440
+ const mdContent = fs.readFileSync(srcPathMd, "utf-8");
441
+ const tomlContent = convertMdToToml(mdContent);
442
+ await fs.writeFile(destPath, tomlContent, "utf-8");
443
+ copiedSomething = true;
444
+ }
445
+ }
446
+ if (fs.existsSync(srcPathDir) && fs.statSync(srcPathDir).isDirectory()) {
447
+ await convertDirectoryToToml(srcPathDir, join2(destTypeDir, item), mergeMode);
448
+ copiedSomething = true;
449
+ }
450
+ if (copiedSomething) {
451
+ copied.push(item);
452
+ } else {
453
+ skipped.push(item);
454
+ }
455
+ } catch (err) {
456
+ errors.push({ item, error: err.message });
457
+ }
458
+ }
459
+ return { copied, skipped, errors };
460
+ }
461
+ async function convertDirectoryToToml(srcDir, destDir, mergeMode = false) {
462
+ await fs.ensureDir(destDir);
463
+ const entries = fs.readdirSync(srcDir);
464
+ for (const entry of entries) {
465
+ const srcPath = join2(srcDir, entry);
466
+ const stat = fs.statSync(srcPath);
467
+ if (stat.isDirectory()) {
468
+ await convertDirectoryToToml(srcPath, join2(destDir, entry), mergeMode);
469
+ } else if (entry.endsWith(".md")) {
470
+ const destPath = join2(destDir, entry.replace(/\.md$/, ".toml"));
471
+ if (mergeMode && fs.existsSync(destPath)) {
472
+ continue;
473
+ }
474
+ const mdContent = fs.readFileSync(srcPath, "utf-8");
475
+ const tomlContent = convertMdToToml(mdContent);
476
+ await fs.writeFile(destPath, tomlContent, "utf-8");
477
+ } else {
478
+ const destPath = join2(destDir, entry);
479
+ if (mergeMode && fs.existsSync(destPath)) {
480
+ continue;
481
+ }
482
+ await fs.copy(srcPath, destPath, { overwrite: !mergeMode });
483
+ }
484
+ }
485
+ }
486
+ function convertAgentForGemini(mdContent) {
487
+ const frontmatterMatch = mdContent.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
488
+ if (!frontmatterMatch) return mdContent;
489
+ let frontmatter = frontmatterMatch[1];
490
+ const body = frontmatterMatch[2];
491
+ const modelMap = {
492
+ "opus": "gemini-2.5-pro",
493
+ "sonnet": "gemini-2.5-flash",
494
+ "haiku": "gemini-2.0-flash-lite",
495
+ "inherit": ""
496
+ // Remove inherit, let Gemini use default
497
+ };
498
+ for (const [claudeModel, geminiModel] of Object.entries(modelMap)) {
499
+ const regex = new RegExp(`^model:\\s*${claudeModel}\\s*$`, "m");
500
+ if (geminiModel) {
501
+ frontmatter = frontmatter.replace(regex, `model: ${geminiModel}`);
502
+ } else {
503
+ frontmatter = frontmatter.replace(regex, "");
504
+ }
505
+ }
506
+ if (!frontmatter.includes("kind:")) {
507
+ frontmatter = frontmatter.trim() + "\nkind: local";
508
+ }
509
+ return `---
510
+ ${frontmatter.trim()}
511
+ ---
512
+ ${body}`;
513
+ }
514
+ async function copyAgentsForGemini(items, sourceDir, destDir, mergeMode = false) {
515
+ const typeDir = join2(sourceDir, "agents");
516
+ const destTypeDir = join2(destDir, "agents");
517
+ if (!fs.existsSync(typeDir)) {
518
+ return { copied: [], skipped: [], errors: [] };
519
+ }
520
+ await fs.ensureDir(destTypeDir);
521
+ const copied = [];
522
+ const skipped = [];
523
+ const errors = [];
524
+ let agentList;
525
+ if (items === "all") {
526
+ const entries = fs.readdirSync(typeDir);
527
+ agentList = entries.filter((e) => e.endsWith(".md") && e !== "README.md").map((e) => e.replace(/\.md$/, ""));
528
+ } else {
529
+ agentList = items;
530
+ }
531
+ for (const agent of agentList) {
532
+ try {
533
+ const srcPath = join2(typeDir, agent + ".md");
534
+ if (!fs.existsSync(srcPath)) {
535
+ skipped.push(agent);
536
+ continue;
537
+ }
538
+ const destPath = join2(destTypeDir, agent + ".md");
539
+ if (mergeMode && fs.existsSync(destPath)) {
540
+ skipped.push(agent);
541
+ continue;
542
+ }
543
+ const mdContent = fs.readFileSync(srcPath, "utf-8");
544
+ const convertedContent = convertAgentForGemini(mdContent);
545
+ await fs.writeFile(destPath, convertedContent, "utf-8");
546
+ copied.push(agent);
547
+ } catch (err) {
548
+ errors.push({ item: agent, error: err.message });
549
+ }
550
+ }
551
+ return { copied, skipped, errors };
552
+ }
553
+ async function copySkillsForGemini(items, sourceDir, destDir, mergeMode = false) {
554
+ const typeDir = join2(sourceDir, "skills");
555
+ const destTypeDir = join2(destDir, "skills");
556
+ if (!fs.existsSync(typeDir)) {
557
+ return { copied: [], skipped: [], errors: [] };
558
+ }
559
+ await fs.ensureDir(destTypeDir);
560
+ const copied = [];
561
+ const skipped = [];
562
+ const errors = [];
563
+ let skillList;
564
+ if (items === "all") {
565
+ const entries = fs.readdirSync(typeDir);
566
+ skillList = entries.filter((e) => {
567
+ const fullPath = join2(typeDir, e);
568
+ return fs.statSync(fullPath).isDirectory() && fs.existsSync(join2(fullPath, "SKILL.md"));
569
+ });
570
+ } else {
571
+ skillList = items;
572
+ }
573
+ for (const skill of skillList) {
574
+ try {
575
+ const srcPath = join2(typeDir, skill);
576
+ if (!fs.existsSync(srcPath) || !fs.statSync(srcPath).isDirectory()) {
577
+ skipped.push(skill);
578
+ continue;
579
+ }
580
+ const skillMdPath = join2(srcPath, "SKILL.md");
581
+ if (!fs.existsSync(skillMdPath)) {
582
+ skipped.push(skill);
583
+ continue;
584
+ }
585
+ const destPath = join2(destTypeDir, skill);
586
+ if (mergeMode && fs.existsSync(destPath)) {
587
+ skipped.push(skill);
588
+ continue;
589
+ }
590
+ await fs.copy(srcPath, destPath, { overwrite: !mergeMode });
591
+ copied.push(skill);
592
+ } catch (err) {
593
+ errors.push({ skill, error: err.message });
594
+ }
595
+ }
596
+ return { copied, skipped, errors };
597
+ }
598
+ async function copyGeminiBaseFiles(destDir, mergeMode = false) {
599
+ const geminiTemplates = join2(CLI_ROOT, "templates", "gemini");
600
+ const copied = [];
601
+ const settingsPath = join2(geminiTemplates, "settings.json");
602
+ const destSettingsPath = join2(destDir, "settings.json");
603
+ if (fs.existsSync(settingsPath)) {
604
+ if (mergeMode && fs.existsSync(destSettingsPath)) {
605
+ return copied;
606
+ }
607
+ await fs.copy(settingsPath, destSettingsPath, { overwrite: !mergeMode });
608
+ copied.push("settings.json");
609
+ }
610
+ return copied;
611
+ }
381
612
  var init_copy = __esm({
382
613
  "src/utils/copy.ts"() {
383
614
  "use strict";
615
+ init_paths();
384
616
  }
385
617
  });
386
618
 
@@ -446,9 +678,19 @@ async function saveState(projectDir, state) {
446
678
  await fs3.writeJson(statePath, state, { spaces: 2 });
447
679
  }
448
680
  async function createInitialState(projectDir, options) {
449
- const { kit, source, target, installed } = options;
450
- const targetDir = join4(projectDir, target);
451
- const hashes = await hashDirectory(targetDir);
681
+ const { kit, source, target, targets, installed } = options;
682
+ const allHashes = {};
683
+ const targetList = targets || [target.replace(".", "")];
684
+ for (const t of targetList) {
685
+ const targetDirName = t.startsWith(".") ? t : `.${t}`;
686
+ const targetDir = join4(projectDir, targetDirName);
687
+ if (fs3.existsSync(targetDir)) {
688
+ const hashes = await hashDirectory(targetDir);
689
+ for (const [path, hash] of Object.entries(hashes)) {
690
+ allHashes[`${targetDirName}/${path}`] = hash;
691
+ }
692
+ }
693
+ }
452
694
  const state = {
453
695
  version: "1.0.0",
454
696
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -456,8 +698,9 @@ async function createInitialState(projectDir, options) {
456
698
  kit,
457
699
  source,
458
700
  target,
701
+ targets,
459
702
  installed,
460
- originalHashes: hashes
703
+ originalHashes: allHashes
461
704
  };
462
705
  await saveState(projectDir, state);
463
706
  return state;
@@ -539,6 +782,19 @@ async function promptKit() {
539
782
  if (p.isCancel(kit)) process.exit(0);
540
783
  return kit;
541
784
  }
785
+ async function promptCliTargets() {
786
+ const targets = await p.multiselect({
787
+ message: "Select AI CLI targets:",
788
+ options: [
789
+ { value: "claude", label: "Claude Code", hint: ".claude/ - Markdown commands" },
790
+ { value: "gemini", label: "Gemini CLI", hint: ".gemini/ - TOML commands" }
791
+ ],
792
+ initialValues: ["claude"],
793
+ required: true
794
+ });
795
+ if (p.isCancel(targets)) process.exit(0);
796
+ return targets;
797
+ }
542
798
  async function promptAgents(sourceDir) {
543
799
  const available = listAvailable("agents", sourceDir);
544
800
  if (available.length === 0) return [];
@@ -672,31 +928,47 @@ async function initCommand(projectName, options) {
672
928
  } else {
673
929
  projectDir = resolve2(process.cwd(), projectName);
674
930
  }
675
- let target = options.target || "claude";
676
- if (!TARGETS[target]) {
677
- console.log(pc2.yellow(`Unknown target "${target}", using "claude"`));
678
- target = "claude";
931
+ let cliTargets;
932
+ if (options.target) {
933
+ const targetsFromFlag = options.target.split(",").map((t) => t.trim());
934
+ cliTargets = targetsFromFlag.filter((t) => t === "claude" || t === "gemini");
935
+ if (cliTargets.length === 0) {
936
+ console.log(pc2.yellow(`Unknown target "${options.target}", using "claude"`));
937
+ cliTargets = ["claude"];
938
+ }
939
+ } else if (!process.stdin.isTTY || options.yes) {
940
+ cliTargets = ["claude"];
941
+ } else {
942
+ cliTargets = await promptCliTargets();
679
943
  }
680
- const targetDir = getTargetDir(projectDir, target);
681
944
  let existingAction = null;
682
- if (options.fresh && fs4.existsSync(targetDir)) {
683
- await fs4.remove(targetDir);
945
+ const existingTargets = [];
946
+ for (const target of cliTargets) {
947
+ const targetDir = getTargetDir(projectDir, target);
948
+ if (options.fresh && fs4.existsSync(targetDir)) {
949
+ await fs4.remove(targetDir);
950
+ existingTargets.push(TARGETS[target]);
951
+ } else if (fs4.existsSync(targetDir) && !options.force) {
952
+ existingTargets.push(TARGETS[target]);
953
+ }
954
+ }
955
+ if (options.fresh && existingTargets.length > 0) {
684
956
  const akDir = join5(projectDir, ".ak");
685
957
  if (fs4.existsSync(akDir)) {
686
958
  await fs4.remove(akDir);
687
959
  }
688
- console.log(pc2.cyan("Fresh install: removed existing files"));
960
+ console.log(pc2.cyan(`Fresh install: removed existing files (${existingTargets.join(", ")})`));
689
961
  existingAction = null;
690
- } else if (fs4.existsSync(targetDir) && !options.force) {
962
+ } else if (existingTargets.length > 0 && !options.force) {
691
963
  if (!process.stdin.isTTY || options.yes) {
692
964
  if (options.yes) {
693
965
  existingAction = "override";
694
966
  } else {
695
- console.log(pc2.yellow(`${TARGETS[target]} already exists. Use --force to override.`));
967
+ console.log(pc2.yellow(`${existingTargets.join(", ")} already exists. Use --force to override.`));
696
968
  return;
697
969
  }
698
970
  } else {
699
- existingAction = await promptExistingTarget(TARGETS[target]);
971
+ existingAction = await promptExistingTarget(existingTargets.join(", "));
700
972
  if (existingAction === "skip") {
701
973
  console.log(pc2.yellow("Skipped. No changes made."));
702
974
  return;
@@ -763,7 +1035,7 @@ async function initCommand(projectName, options) {
763
1035
  }
764
1036
  console.log(pc2.cyan("\nWill create:"));
765
1037
  console.log(pc2.white(` Project: ${projectName}/`));
766
- console.log(pc2.white(` Target: ${TARGETS[target]}/`));
1038
+ console.log(pc2.white(` Targets: ${cliTargets.map((t) => TARGETS[t]).join(", ")}/`));
767
1039
  console.log(pc2.white(` Kit: ${kitName}`));
768
1040
  if (Array.isArray(toInstall.agents)) {
769
1041
  console.log(pc2.gray(` Agents: ${toInstall.agents.length}`));
@@ -784,54 +1056,78 @@ async function initCommand(projectName, options) {
784
1056
  const spinner = ora("Creating project...").start();
785
1057
  try {
786
1058
  await fs4.ensureDir(projectDir);
787
- const targetDir2 = getTargetDir(projectDir, target);
788
- await fs4.ensureDir(targetDir2);
789
- spinner.text = mergeMode ? "Merging agents..." : "Copying agents...";
790
- if (toInstall.agents === "all") {
791
- await copyAllOfType("agents", source.claudeDir, targetDir2, mergeMode);
792
- } else if (toInstall.agents.length > 0) {
793
- await copyItems(toInstall.agents, "agents", source.claudeDir, targetDir2, mergeMode);
794
- }
795
- spinner.text = mergeMode ? "Merging skills..." : "Copying skills...";
796
- if (toInstall.skills === "all") {
797
- await copyAllOfType("skills", source.claudeDir, targetDir2, mergeMode);
798
- } else if (toInstall.skills.length > 0) {
799
- await copyItems(toInstall.skills, "skills", source.claudeDir, targetDir2, mergeMode);
800
- }
801
- spinner.text = mergeMode ? "Merging commands..." : "Copying commands...";
802
- if (toInstall.commands === "all") {
803
- await copyAllOfType("commands", source.claudeDir, targetDir2, mergeMode);
804
- } else if (toInstall.commands.length > 0) {
805
- await copyItems(toInstall.commands, "commands", source.claudeDir, targetDir2, mergeMode);
806
- }
807
- spinner.text = mergeMode ? "Merging workflows..." : "Copying workflows...";
808
- if (toInstall.workflows === "all") {
809
- await copyAllOfType("workflows", source.claudeDir, targetDir2, mergeMode);
810
- } else if (toInstall.workflows && toInstall.workflows.length > 0) {
811
- await copyItems(toInstall.workflows, "workflows", source.claudeDir, targetDir2, mergeMode);
812
- }
813
- if (toInstall.includeRouter) {
814
- spinner.text = mergeMode ? "Merging router..." : "Copying router...";
815
- await copyRouter(source.claudeDir, targetDir2, mergeMode);
816
- }
817
- if (toInstall.includeHooks) {
818
- spinner.text = mergeMode ? "Merging hooks..." : "Copying hooks...";
819
- await copyHooks(source.claudeDir, targetDir2, mergeMode);
820
- }
821
- spinner.text = mergeMode ? "Merging extras..." : "Copying extras...";
822
- await copyDirectory("memory", source.claudeDir, targetDir2, mergeMode);
823
- await copyDirectory("output-styles", source.claudeDir, targetDir2, mergeMode);
824
- await copyDirectory("scripts", source.claudeDir, targetDir2, mergeMode);
825
- spinner.text = mergeMode ? "Merging base files..." : "Copying base files...";
826
- await copyBaseFiles(source.claudeDir, targetDir2, mergeMode);
827
- if (source.agentsMd) {
1059
+ for (const target of cliTargets) {
1060
+ const targetDir = getTargetDir(projectDir, target);
1061
+ await fs4.ensureDir(targetDir);
1062
+ const targetLabel = target === "gemini" ? "Gemini" : "Claude";
1063
+ spinner.text = mergeMode ? `Merging agents (${targetLabel})...` : `Copying agents (${targetLabel})...`;
1064
+ if (target === "gemini") {
1065
+ await copyAgentsForGemini(toInstall.agents, source.claudeDir, targetDir, mergeMode);
1066
+ } else {
1067
+ if (toInstall.agents === "all") {
1068
+ await copyAllOfType("agents", source.claudeDir, targetDir, mergeMode);
1069
+ } else if (toInstall.agents.length > 0) {
1070
+ await copyItems(toInstall.agents, "agents", source.claudeDir, targetDir, mergeMode);
1071
+ }
1072
+ }
1073
+ spinner.text = mergeMode ? `Merging skills (${targetLabel})...` : `Copying skills (${targetLabel})...`;
1074
+ if (target === "gemini") {
1075
+ await copySkillsForGemini(toInstall.skills, source.claudeDir, targetDir, mergeMode);
1076
+ } else {
1077
+ if (toInstall.skills === "all") {
1078
+ await copyAllOfType("skills", source.claudeDir, targetDir, mergeMode);
1079
+ } else if (toInstall.skills.length > 0) {
1080
+ await copyItems(toInstall.skills, "skills", source.claudeDir, targetDir, mergeMode);
1081
+ }
1082
+ }
1083
+ spinner.text = mergeMode ? `Merging commands (${targetLabel})...` : `Copying commands (${targetLabel})...`;
1084
+ if (target === "gemini") {
1085
+ await copyCommandsForGemini(toInstall.commands, source.claudeDir, targetDir, mergeMode);
1086
+ } else {
1087
+ if (toInstall.commands === "all") {
1088
+ await copyAllOfType("commands", source.claudeDir, targetDir, mergeMode);
1089
+ } else if (toInstall.commands.length > 0) {
1090
+ await copyItems(toInstall.commands, "commands", source.claudeDir, targetDir, mergeMode);
1091
+ }
1092
+ }
1093
+ if (target === "claude") {
1094
+ spinner.text = mergeMode ? `Merging workflows (${targetLabel})...` : `Copying workflows (${targetLabel})...`;
1095
+ if (toInstall.workflows === "all") {
1096
+ await copyAllOfType("workflows", source.claudeDir, targetDir, mergeMode);
1097
+ } else if (toInstall.workflows && toInstall.workflows.length > 0) {
1098
+ await copyItems(toInstall.workflows, "workflows", source.claudeDir, targetDir, mergeMode);
1099
+ }
1100
+ }
1101
+ if (target === "claude" && toInstall.includeRouter) {
1102
+ spinner.text = mergeMode ? `Merging router (${targetLabel})...` : `Copying router (${targetLabel})...`;
1103
+ await copyRouter(source.claudeDir, targetDir, mergeMode);
1104
+ }
1105
+ if (target === "claude" && toInstall.includeHooks) {
1106
+ spinner.text = mergeMode ? `Merging hooks (${targetLabel})...` : `Copying hooks (${targetLabel})...`;
1107
+ await copyHooks(source.claudeDir, targetDir, mergeMode);
1108
+ }
1109
+ if (target === "claude") {
1110
+ spinner.text = mergeMode ? `Merging extras (${targetLabel})...` : `Copying extras (${targetLabel})...`;
1111
+ await copyDirectory("memory", source.claudeDir, targetDir, mergeMode);
1112
+ await copyDirectory("output-styles", source.claudeDir, targetDir, mergeMode);
1113
+ await copyDirectory("scripts", source.claudeDir, targetDir, mergeMode);
1114
+ spinner.text = mergeMode ? `Merging base files (${targetLabel})...` : `Copying base files (${targetLabel})...`;
1115
+ await copyBaseFiles(source.claudeDir, targetDir, mergeMode);
1116
+ } else if (target === "gemini") {
1117
+ spinner.text = mergeMode ? `Merging settings (${targetLabel})...` : `Copying settings (${targetLabel})...`;
1118
+ await copyGeminiBaseFiles(targetDir, mergeMode);
1119
+ }
1120
+ }
1121
+ if (source.agentsMd && cliTargets.includes("claude")) {
828
1122
  await copyAgentsMd(source.agentsMd, projectDir, mergeMode);
829
1123
  }
830
1124
  spinner.text = "Saving state...";
831
1125
  await createInitialState(projectDir, {
832
1126
  kit: kitName,
833
1127
  source: source.path,
834
- target: TARGETS[target],
1128
+ targets: cliTargets,
1129
+ target: TARGETS[cliTargets[0]],
1130
+ // Keep backward compat
835
1131
  installed: {
836
1132
  agents: toInstall.agents === "all" ? ["all"] : toInstall.agents,
837
1133
  skills: toInstall.skills === "all" ? ["all"] : toInstall.skills,
@@ -847,10 +1143,9 @@ async function initCommand(projectName, options) {
847
1143
  if (!isCurrentDir) {
848
1144
  console.log(pc2.cyan("Next steps:"));
849
1145
  console.log(pc2.white(` cd ${projectName}`));
850
- console.log(pc2.white(" # Start coding with Claude Code"));
851
- } else {
852
- console.log(pc2.cyan("Ready to code with Claude Code!"));
853
1146
  }
1147
+ const targetNames = cliTargets.map((t) => t === "gemini" ? "Gemini CLI" : "Claude Code").join(" & ");
1148
+ console.log(pc2.cyan(`Ready to code with ${targetNames}!`));
854
1149
  console.log("");
855
1150
  console.log(pc2.gray("Useful commands:"));
856
1151
  console.log(pc2.gray(" ak status - Check file status"));
@@ -2369,12 +2664,12 @@ var init_skills = __esm({
2369
2664
  import cac from "cac";
2370
2665
  import { readFileSync as readFileSync3 } from "fs";
2371
2666
  import { fileURLToPath as fileURLToPath2 } from "url";
2372
- import { dirname as dirname2, join as join13 } from "path";
2667
+ import { dirname as dirname3, join as join13 } from "path";
2373
2668
  import pc13 from "picocolors";
2374
2669
 
2375
2670
  // src/cli/command-registry.ts
2376
2671
  function registerCommands(cli2) {
2377
- 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 folder (claude, opencode, generic)").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) => {
2672
+ 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) => {
2378
2673
  const { initCommand: initCommand2 } = await Promise.resolve().then(() => (init_init(), init_exports));
2379
2674
  await initCommand2(projectName, options);
2380
2675
  });
@@ -2453,7 +2748,7 @@ function isNewerVersion(current, latest) {
2453
2748
 
2454
2749
  // src/index.ts
2455
2750
  var __filename2 = fileURLToPath2(import.meta.url);
2456
- var __dirname2 = dirname2(__filename2);
2751
+ var __dirname2 = dirname3(__filename2);
2457
2752
  function getVersion() {
2458
2753
  try {
2459
2754
  const pkg = JSON.parse(readFileSync3(join13(__dirname2, "..", "package.json"), "utf-8"));