apero-kit-cli 2.0.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);
@@ -327,7 +359,7 @@ async function copyHooks(sourceDir, destDir, mergeMode = false) {
327
359
  }
328
360
  }
329
361
  async function copyBaseFiles(sourceDir, destDir, mergeMode = false) {
330
- const baseFiles = ["README.md", "settings.json", ".env.example"];
362
+ const baseFiles = ["README.md", "settings.json", ".env.example", "statusline.cjs", "statusline.ps1", "statusline.sh"];
331
363
  const copied = [];
332
364
  for (const file of baseFiles) {
333
365
  const srcPath = join2(sourceDir, file);
@@ -342,6 +374,18 @@ async function copyBaseFiles(sourceDir, destDir, mergeMode = false) {
342
374
  }
343
375
  return copied;
344
376
  }
377
+ async function copyDirectory(dirName, sourceDir, destDir, mergeMode = false) {
378
+ const srcPath = join2(sourceDir, dirName);
379
+ if (!fs.existsSync(srcPath)) {
380
+ return { success: false, error: `${dirName} directory not found` };
381
+ }
382
+ try {
383
+ await fs.copy(srcPath, join2(destDir, dirName), { overwrite: !mergeMode });
384
+ return { success: true };
385
+ } catch (err) {
386
+ return { success: false, error: err.message };
387
+ }
388
+ }
345
389
  async function copyAgentsMd(agentsMdPath, projectDir, mergeMode = false) {
346
390
  if (!agentsMdPath || !fs.existsSync(agentsMdPath)) {
347
391
  return false;
@@ -366,9 +410,209 @@ function listAvailable(type, sourceDir) {
366
410
  return { name, isDir, path: itemPath };
367
411
  });
368
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
+ }
369
612
  var init_copy = __esm({
370
613
  "src/utils/copy.ts"() {
371
614
  "use strict";
615
+ init_paths();
372
616
  }
373
617
  });
374
618
 
@@ -434,9 +678,19 @@ async function saveState(projectDir, state) {
434
678
  await fs3.writeJson(statePath, state, { spaces: 2 });
435
679
  }
436
680
  async function createInitialState(projectDir, options) {
437
- const { kit, source, target, installed } = options;
438
- const targetDir = join4(projectDir, target);
439
- 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
+ }
440
694
  const state = {
441
695
  version: "1.0.0",
442
696
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -444,8 +698,9 @@ async function createInitialState(projectDir, options) {
444
698
  kit,
445
699
  source,
446
700
  target,
701
+ targets,
447
702
  installed,
448
- originalHashes: hashes
703
+ originalHashes: allHashes
449
704
  };
450
705
  await saveState(projectDir, state);
451
706
  return state;
@@ -527,6 +782,19 @@ async function promptKit() {
527
782
  if (p.isCancel(kit)) process.exit(0);
528
783
  return kit;
529
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
+ }
530
798
  async function promptAgents(sourceDir) {
531
799
  const available = listAvailable("agents", sourceDir);
532
800
  if (available.length === 0) return [];
@@ -660,31 +928,47 @@ async function initCommand(projectName, options) {
660
928
  } else {
661
929
  projectDir = resolve2(process.cwd(), projectName);
662
930
  }
663
- let target = options.target || "claude";
664
- if (!TARGETS[target]) {
665
- console.log(pc2.yellow(`Unknown target "${target}", using "claude"`));
666
- 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();
667
943
  }
668
- const targetDir = getTargetDir(projectDir, target);
669
944
  let existingAction = null;
670
- if (options.fresh && fs4.existsSync(targetDir)) {
671
- 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) {
672
956
  const akDir = join5(projectDir, ".ak");
673
957
  if (fs4.existsSync(akDir)) {
674
958
  await fs4.remove(akDir);
675
959
  }
676
- console.log(pc2.cyan("Fresh install: removed existing files"));
960
+ console.log(pc2.cyan(`Fresh install: removed existing files (${existingTargets.join(", ")})`));
677
961
  existingAction = null;
678
- } else if (fs4.existsSync(targetDir) && !options.force) {
962
+ } else if (existingTargets.length > 0 && !options.force) {
679
963
  if (!process.stdin.isTTY || options.yes) {
680
964
  if (options.yes) {
681
965
  existingAction = "override";
682
966
  } else {
683
- 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.`));
684
968
  return;
685
969
  }
686
970
  } else {
687
- existingAction = await promptExistingTarget(TARGETS[target]);
971
+ existingAction = await promptExistingTarget(existingTargets.join(", "));
688
972
  if (existingAction === "skip") {
689
973
  console.log(pc2.yellow("Skipped. No changes made."));
690
974
  return;
@@ -751,7 +1035,7 @@ async function initCommand(projectName, options) {
751
1035
  }
752
1036
  console.log(pc2.cyan("\nWill create:"));
753
1037
  console.log(pc2.white(` Project: ${projectName}/`));
754
- console.log(pc2.white(` Target: ${TARGETS[target]}/`));
1038
+ console.log(pc2.white(` Targets: ${cliTargets.map((t) => TARGETS[t]).join(", ")}/`));
755
1039
  console.log(pc2.white(` Kit: ${kitName}`));
756
1040
  if (Array.isArray(toInstall.agents)) {
757
1041
  console.log(pc2.gray(` Agents: ${toInstall.agents.length}`));
@@ -772,50 +1056,78 @@ async function initCommand(projectName, options) {
772
1056
  const spinner = ora("Creating project...").start();
773
1057
  try {
774
1058
  await fs4.ensureDir(projectDir);
775
- const targetDir2 = getTargetDir(projectDir, target);
776
- await fs4.ensureDir(targetDir2);
777
- spinner.text = mergeMode ? "Merging agents..." : "Copying agents...";
778
- if (toInstall.agents === "all") {
779
- await copyAllOfType("agents", source.claudeDir, targetDir2, mergeMode);
780
- } else if (toInstall.agents.length > 0) {
781
- await copyItems(toInstall.agents, "agents", source.claudeDir, targetDir2, mergeMode);
782
- }
783
- spinner.text = mergeMode ? "Merging skills..." : "Copying skills...";
784
- if (toInstall.skills === "all") {
785
- await copyAllOfType("skills", source.claudeDir, targetDir2, mergeMode);
786
- } else if (toInstall.skills.length > 0) {
787
- await copyItems(toInstall.skills, "skills", source.claudeDir, targetDir2, mergeMode);
788
- }
789
- spinner.text = mergeMode ? "Merging commands..." : "Copying commands...";
790
- if (toInstall.commands === "all") {
791
- await copyAllOfType("commands", source.claudeDir, targetDir2, mergeMode);
792
- } else if (toInstall.commands.length > 0) {
793
- await copyItems(toInstall.commands, "commands", source.claudeDir, targetDir2, mergeMode);
794
- }
795
- spinner.text = mergeMode ? "Merging workflows..." : "Copying workflows...";
796
- if (toInstall.workflows === "all") {
797
- await copyAllOfType("workflows", source.claudeDir, targetDir2, mergeMode);
798
- } else if (toInstall.workflows && toInstall.workflows.length > 0) {
799
- await copyItems(toInstall.workflows, "workflows", source.claudeDir, targetDir2, mergeMode);
800
- }
801
- if (toInstall.includeRouter) {
802
- spinner.text = mergeMode ? "Merging router..." : "Copying router...";
803
- await copyRouter(source.claudeDir, targetDir2, mergeMode);
804
- }
805
- if (toInstall.includeHooks) {
806
- spinner.text = mergeMode ? "Merging hooks..." : "Copying hooks...";
807
- await copyHooks(source.claudeDir, targetDir2, mergeMode);
808
- }
809
- spinner.text = mergeMode ? "Merging base files..." : "Copying base files...";
810
- await copyBaseFiles(source.claudeDir, targetDir2, mergeMode);
811
- 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")) {
812
1122
  await copyAgentsMd(source.agentsMd, projectDir, mergeMode);
813
1123
  }
814
1124
  spinner.text = "Saving state...";
815
1125
  await createInitialState(projectDir, {
816
1126
  kit: kitName,
817
1127
  source: source.path,
818
- target: TARGETS[target],
1128
+ targets: cliTargets,
1129
+ target: TARGETS[cliTargets[0]],
1130
+ // Keep backward compat
819
1131
  installed: {
820
1132
  agents: toInstall.agents === "all" ? ["all"] : toInstall.agents,
821
1133
  skills: toInstall.skills === "all" ? ["all"] : toInstall.skills,
@@ -831,10 +1143,9 @@ async function initCommand(projectName, options) {
831
1143
  if (!isCurrentDir) {
832
1144
  console.log(pc2.cyan("Next steps:"));
833
1145
  console.log(pc2.white(` cd ${projectName}`));
834
- console.log(pc2.white(" # Start coding with Claude Code"));
835
- } else {
836
- console.log(pc2.cyan("Ready to code with Claude Code!"));
837
1146
  }
1147
+ const targetNames = cliTargets.map((t) => t === "gemini" ? "Gemini CLI" : "Claude Code").join(" & ");
1148
+ console.log(pc2.cyan(`Ready to code with ${targetNames}!`));
838
1149
  console.log("");
839
1150
  console.log(pc2.gray("Useful commands:"));
840
1151
  console.log(pc2.gray(" ak status - Check file status"));
@@ -1541,6 +1852,33 @@ async function doctorCommand(options = {}) {
1541
1852
  }
1542
1853
  return {};
1543
1854
  }
1855
+ },
1856
+ {
1857
+ name: "subdirs_scripts",
1858
+ label: "scripts/ exists",
1859
+ severity: "warning",
1860
+ check: () => {
1861
+ if (!targetDir) return false;
1862
+ return fs8.existsSync(join8(targetDir.dir, "scripts"));
1863
+ }
1864
+ },
1865
+ {
1866
+ name: "subdirs_hooks",
1867
+ label: "hooks/ exists",
1868
+ severity: "warning",
1869
+ check: () => {
1870
+ if (!targetDir) return false;
1871
+ return fs8.existsSync(join8(targetDir.dir, "hooks"));
1872
+ }
1873
+ },
1874
+ {
1875
+ name: "statusline",
1876
+ label: "statusline files exist",
1877
+ severity: "warning",
1878
+ check: () => {
1879
+ if (!targetDir) return false;
1880
+ return fs8.existsSync(join8(targetDir.dir, "statusline.cjs")) || fs8.existsSync(join8(targetDir.dir, "statusline.sh"));
1881
+ }
1544
1882
  }
1545
1883
  ];
1546
1884
  const results = await runChecks(checks, options);
@@ -2326,12 +2664,12 @@ var init_skills = __esm({
2326
2664
  import cac from "cac";
2327
2665
  import { readFileSync as readFileSync3 } from "fs";
2328
2666
  import { fileURLToPath as fileURLToPath2 } from "url";
2329
- import { dirname as dirname2, join as join13 } from "path";
2667
+ import { dirname as dirname3, join as join13 } from "path";
2330
2668
  import pc13 from "picocolors";
2331
2669
 
2332
2670
  // src/cli/command-registry.ts
2333
2671
  function registerCommands(cli2) {
2334
- 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) => {
2335
2673
  const { initCommand: initCommand2 } = await Promise.resolve().then(() => (init_init(), init_exports));
2336
2674
  await initCommand2(projectName, options);
2337
2675
  });
@@ -2410,7 +2748,7 @@ function isNewerVersion(current, latest) {
2410
2748
 
2411
2749
  // src/index.ts
2412
2750
  var __filename2 = fileURLToPath2(import.meta.url);
2413
- var __dirname2 = dirname2(__filename2);
2751
+ var __dirname2 = dirname3(__filename2);
2414
2752
  function getVersion() {
2415
2753
  try {
2416
2754
  const pkg = JSON.parse(readFileSync3(join13(__dirname2, "..", "package.json"), "utf-8"));