@baton-dx/cli 0.3.2 → 0.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.mjs CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import { r as __toESM } from "./chunk-BbwQpWto.mjs";
3
3
  import { a as Ne, c as Ve, d as bt, f as je, g as runMain, h as defineCommand, i as Le, l as We, m as require_dist, o as R, p as Ct, r as Je, s as Re, t as findSourceRoot, u as Ze } from "./context-detection-DqOTnD6_.mjs";
4
- import { $ as loadProfileManifest, A as detectLegacyPaths, B as collectComprehensivePatterns, C as mergeSkillsWithWarnings, D as mergeContentParts, E as sortProfilesByWeight, F as generateLock, G as getRegisteredIdePlatforms, H as removeGitignoreManagedSection, I as readLock, J as getAdaptersForKeys, K as idePlatformRegistry, L as writeLock, M as discoverProfilesInSourceRepo, N as findSourceManifest, O as resolveProfileSupport, P as removePlacedFiles, Q as loadLockfile, R as resolveVersion, S as mergeSkills, T as isLockedProfile, U as updateGitignore, V as ensureBatonDirGitignored, W as getIdePlatformTargetDir, X as parseFrontmatter, Y as getAllAdapters, Z as parseSource, _ as require_lib, a as clearIdeCache, at as getAgentPath, b as mergeRules, c as getBatonHome, d as getGlobalConfigPath, et as loadProjectManifest, f as getGlobalIdePlatforms, g as setGlobalIdePlatforms, h as setGlobalAiTools, i as computeIntersection, it as getAgentConfig, j as placeFile, k as resolveProfileChain, l as getDefaultGlobalSource, m as removeGlobalSource, n as readProjectPreferences, nt as FileNotFoundError, o as detectInstalledIdes, ot as getAllAgentKeys, p as getGlobalSources, q as isKnownIdePlatform, r as writeProjectPreferences, rt as SourceParseError, s as addGlobalSource, t as resolvePreferences, tt as KEBAB_CASE_REGEX, u as getGlobalAiTools, v as mergeMemory, w as getProfileWeight, x as mergeRulesWithWarnings, y as mergeMemoryWithWarnings, z as cloneGitSource } from "./src-BCGnnv5D.mjs";
5
- import { n as detectInstalledAgents, t as clearAgentCache } from "./agent-detection-DTiVeO5W.mjs";
4
+ import { $ as loadProjectManifest, A as placeFile, B as ensureBatonDirGitignored, C as getProfileWeight, D as resolveProfileSupport, E as mergeContentParts, F as readLock, G as idePlatformRegistry, H as updateGitignore, I as writeLock, J as getAllAIToolAdapters, K as isKnownIdePlatform, L as resolveVersion, M as findSourceManifest, N as removePlacedFiles, O as resolveProfileChain, P as generateLock, Q as loadProfileManifest, R as cloneGitSource, S as mergeSkillsWithWarnings, T as sortProfilesByWeight, U as getIdePlatformTargetDir, V as removeGitignoreManagedSection, W as getRegisteredIdePlatforms, X as parseSource, Y as parseFrontmatter, Z as loadLockfile, _ as mergeMemoryWithWarnings, a as clearIdeCache, at as getAllAIToolKeys, b as mergeRulesWithWarnings, c as getDefaultGlobalSource, d as getGlobalSources, et as KEBAB_CASE_REGEX, f as removeGlobalSource, g as mergeMemory, h as require_lib, i as computeIntersection, it as getAIToolPath, j as discoverProfilesInSourceRepo, k as detectLegacyPaths, l as getGlobalAiTools, m as setGlobalIdePlatforms, n as readProjectPreferences, nt as SourceParseError, o as detectInstalledIdes, p as setGlobalAiTools, q as getAIToolAdaptersForKeys, r as writeProjectPreferences, rt as getAIToolConfig, s as addGlobalSource, t as resolvePreferences, tt as FileNotFoundError, u as getGlobalIdePlatforms, v as mergeAgentsWithWarnings, w as isLockedProfile, x as mergeSkills, y as mergeRules, z as collectComprehensivePatterns } from "./src-CHISlTPa.mjs";
5
+ import { n as detectInstalledAITools, t as clearAIToolCache } from "./ai-tool-detection-CMsBNa9e.mjs";
6
6
  import { d as esm_default } from "./esm-BagM-kVd.mjs";
7
7
  import { access, mkdir, readFile, readdir, rm, stat, writeFile } from "node:fs/promises";
8
8
  import { dirname, isAbsolute, join, relative, resolve } from "node:path";
@@ -39,7 +39,7 @@ async function runGlobalMode$1(nonInteractive) {
39
39
  Le("No changes made.");
40
40
  return;
41
41
  }
42
- const options = getAllAdapters().map((adapter) => {
42
+ const options = getAllAIToolAdapters().map((adapter) => {
43
43
  const isSaved = currentTools.includes(adapter.key);
44
44
  return {
45
45
  value: adapter.key,
@@ -125,7 +125,7 @@ async function runProjectMode$1(nonInteractive) {
125
125
  Le("Configuration complete.");
126
126
  return;
127
127
  }
128
- const allAdapters = getAllAdapters();
128
+ const allAdapters = getAllAIToolAdapters();
129
129
  const currentProjectTools = existing?.ai.useGlobal === false ? existing.ai.tools : globalTools;
130
130
  const options = allAdapters.map((adapter) => {
131
131
  const isGlobal = globalTools.includes(adapter.key);
@@ -181,37 +181,37 @@ const aiToolsListCommand = defineCommand({
181
181
  async run({ args }) {
182
182
  if (!args.json) We("Baton - AI Tools");
183
183
  const savedTools = await getGlobalAiTools();
184
- const allAgentKeys = getAllAgentKeys();
185
- const keysToShow = args.all ? allAgentKeys : savedTools.length > 0 ? savedTools : allAgentKeys;
186
- const agentStatuses = await Promise.all(keysToShow.map(async (agentKey) => {
187
- const isSaved = savedTools.includes(agentKey);
184
+ const allAIToolKeys = getAllAIToolKeys();
185
+ const keysToShow = args.all ? allAIToolKeys : savedTools.length > 0 ? savedTools : allAIToolKeys;
186
+ const toolStatuses = await Promise.all(keysToShow.map(async (toolKey) => {
187
+ const isSaved = savedTools.includes(toolKey);
188
188
  let skillCount = 0;
189
189
  let ruleCount = 0;
190
- let agentCount = 0;
190
+ let aiToolConfigCount = 0;
191
191
  let memoryCount = 0;
192
192
  let commandCount = 0;
193
193
  if (isSaved) {
194
- skillCount = await countConfigs(agentKey, "skills", "project");
195
- ruleCount = await countConfigs(agentKey, "rules", "project");
196
- agentCount = await countConfigs(agentKey, "agents", "project");
197
- memoryCount = await countConfigs(agentKey, "memory", "project");
198
- commandCount = await countConfigs(agentKey, "commands", "project");
194
+ skillCount = await countConfigs(toolKey, "skills", "project");
195
+ ruleCount = await countConfigs(toolKey, "rules", "project");
196
+ aiToolConfigCount = await countConfigs(toolKey, "agents", "project");
197
+ memoryCount = await countConfigs(toolKey, "memory", "project");
198
+ commandCount = await countConfigs(toolKey, "commands", "project");
199
199
  }
200
200
  const paths = {
201
- skills: getAgentPath(agentKey, "skills", "project", ""),
202
- rules: getAgentPath(agentKey, "rules", "project", ""),
203
- agents: getAgentPath(agentKey, "agents", "project", ""),
204
- memory: getAgentPath(agentKey, "memory", "project", ""),
205
- commands: getAgentPath(agentKey, "commands", "project", "")
201
+ skills: getAIToolPath(toolKey, "skills", "project", ""),
202
+ rules: getAIToolPath(toolKey, "rules", "project", ""),
203
+ agents: getAIToolPath(toolKey, "agents", "project", ""),
204
+ memory: getAIToolPath(toolKey, "memory", "project", ""),
205
+ commands: getAIToolPath(toolKey, "commands", "project", "")
206
206
  };
207
207
  return {
208
- key: agentKey,
209
- name: getAgentConfig(agentKey).name,
208
+ key: toolKey,
209
+ name: getAIToolConfig(toolKey).name,
210
210
  saved: isSaved,
211
211
  counts: {
212
212
  skills: skillCount,
213
213
  rules: ruleCount,
214
- agents: agentCount,
214
+ agents: aiToolConfigCount,
215
215
  memory: memoryCount,
216
216
  commands: commandCount
217
217
  },
@@ -219,23 +219,23 @@ const aiToolsListCommand = defineCommand({
219
219
  };
220
220
  }));
221
221
  if (args.json) {
222
- console.log(JSON.stringify(agentStatuses, null, 2));
222
+ console.log(JSON.stringify(toolStatuses, null, 2));
223
223
  return;
224
224
  }
225
225
  if (savedTools.length === 0) {
226
226
  R.warn("No AI tools saved in global config.");
227
227
  R.info("Run 'baton ai-tools scan' to detect and save your AI tools.");
228
228
  console.log("");
229
- R.info(`All ${allAgentKeys.length} supported tools:`);
230
- for (const key of allAgentKeys) {
231
- const config = getAgentConfig(key);
229
+ R.info(`All ${allAIToolKeys.length} supported tools:`);
230
+ for (const key of allAIToolKeys) {
231
+ const config = getAIToolConfig(key);
232
232
  console.log(` \x1b[90m- ${config.name}\x1b[0m`);
233
233
  }
234
234
  Le("Run 'baton ai-tools scan' to get started.");
235
235
  return;
236
236
  }
237
237
  console.log(`\nSaved AI tools (${savedTools.length}):\n`);
238
- for (const agent of agentStatuses) {
238
+ for (const agent of toolStatuses) {
239
239
  const statusColor = agent.saved ? "\x1B[32m" : "\x1B[90m";
240
240
  const status = agent.saved ? "✓" : "✗";
241
241
  console.log(`${statusColor}${status} ${agent.name.padEnd(20)}`);
@@ -252,15 +252,15 @@ const aiToolsListCommand = defineCommand({
252
252
  }
253
253
  console.log("");
254
254
  }
255
- Le("Manage tools: 'baton ai-tools scan' (detect) | 'baton config set default-tools <tools>'");
255
+ Le("Manage tools: 'baton ai-tools scan' (detect) | 'baton ai-tools configure' (select)");
256
256
  }
257
257
  });
258
258
  /**
259
- * Count config files of a given type for an agent
259
+ * Count config files of a given type for a tool
260
260
  */
261
- async function countConfigs(agentKey, configType, scope) {
261
+ async function countConfigs(toolKey, configType, scope) {
262
262
  try {
263
- const dirPath = getAgentPath(agentKey, configType, scope, "").replace(/{name}.*$/, "").replace(/\/$/, "");
263
+ const dirPath = getAIToolPath(toolKey, configType, scope, "").replace(/{name}.*$/, "").replace(/\/$/, "");
264
264
  if (!(await stat(dirPath)).isDirectory()) return 0;
265
265
  return (await readdir(dirPath)).length;
266
266
  } catch (_error) {
@@ -284,15 +284,15 @@ const aiToolsScanCommand = defineCommand({
284
284
  We("Baton - AI Tool Scanner");
285
285
  const spinner = bt();
286
286
  spinner.start("Scanning for AI tools...");
287
- clearAgentCache();
288
- const detectedAgents = await detectInstalledAgents();
289
- const allAdapters = getAllAdapters();
287
+ clearAIToolCache();
288
+ const detectedAITools = await detectInstalledAITools();
289
+ const allAdapters = getAllAIToolAdapters();
290
290
  const currentTools = await getGlobalAiTools();
291
291
  spinner.stop("Scan complete.");
292
- if (detectedAgents.length > 0) R.success(`Found ${detectedAgents.length} AI tool${detectedAgents.length !== 1 ? "s" : ""} on your system.`);
292
+ if (detectedAITools.length > 0) R.success(`Found ${detectedAITools.length} AI tool${detectedAITools.length !== 1 ? "s" : ""} on your system.`);
293
293
  else R.warn("No AI tools detected on your system.");
294
294
  if (args.yes) {
295
- const detectedKeys = detectedAgents;
295
+ const detectedKeys = detectedAITools;
296
296
  if (detectedKeys.length !== currentTools.length || detectedKeys.some((key) => !currentTools.includes(key))) {
297
297
  await setGlobalAiTools(detectedKeys);
298
298
  R.success(`Saved ${detectedKeys.length} detected tool(s) to global config.`);
@@ -301,7 +301,7 @@ const aiToolsScanCommand = defineCommand({
301
301
  return;
302
302
  }
303
303
  const options = allAdapters.map((adapter) => {
304
- const isDetected = detectedAgents.includes(adapter.key);
304
+ const isDetected = detectedAITools.includes(adapter.key);
305
305
  return {
306
306
  value: adapter.key,
307
307
  label: isDetected ? `${adapter.name} (detected)` : adapter.name
@@ -310,7 +310,7 @@ const aiToolsScanCommand = defineCommand({
310
310
  const selected = await je({
311
311
  message: "Select which AI tools to save:",
312
312
  options,
313
- initialValues: detectedAgents
313
+ initialValues: detectedAITools
314
314
  });
315
315
  if (Ct(selected)) {
316
316
  Le("Scan finished (not saved).");
@@ -341,7 +341,6 @@ const aiToolsCommand = defineCommand({
341
341
 
342
342
  //#endregion
343
343
  //#region src/utils/build-intersection.ts
344
- var import_dist = require_dist();
345
344
  /**
346
345
  * Compute the intersection for a single profile source string.
347
346
  * Shared utility used by sync, config, and manage commands.
@@ -420,26 +419,6 @@ function formatIntersectionSummary(intersection) {
420
419
 
421
420
  //#endregion
422
421
  //#region src/commands/config.ts
423
- const VALID_KEYS = [
424
- "cache-dir",
425
- "default-scope",
426
- "symlink-mode",
427
- "default-tools"
428
- ];
429
- async function loadConfig() {
430
- const configFile = getGlobalConfigPath();
431
- try {
432
- await access(configFile);
433
- } catch {
434
- return {};
435
- }
436
- return (0, import_dist.parse)(await readFile(configFile, "utf-8"));
437
- }
438
- async function saveConfig(config) {
439
- const configFile = getGlobalConfigPath();
440
- await mkdir(getBatonHome(), { recursive: true });
441
- await writeFile(configFile, (0, import_dist.stringify)(config), "utf-8");
442
- }
443
422
  async function showDashboard() {
444
423
  We("Baton Dashboard");
445
424
  const [sources, aiTools, idePlatforms, projectManifest] = await Promise.all([
@@ -467,7 +446,7 @@ async function showDashboard() {
467
446
  if (resolvedAiTools.length > 0) {
468
447
  const toolNames = resolvedAiTools.map((key) => {
469
448
  try {
470
- return getAgentConfig(key).name;
449
+ return getAIToolConfig(key).name;
471
450
  } catch {
472
451
  return key;
473
452
  }
@@ -485,7 +464,7 @@ async function showDashboard() {
485
464
  if (aiTools.length > 0) {
486
465
  const toolNames = aiTools.map((key) => {
487
466
  try {
488
- return getAgentConfig(key).name;
467
+ return getAIToolConfig(key).name;
489
468
  } catch {
490
469
  return key;
491
470
  }
@@ -520,7 +499,7 @@ async function showDashboard() {
520
499
  }
521
500
  }
522
501
  console.log("");
523
- Le("Use 'baton config list' to view configuration settings");
502
+ Le("Manage tools: 'baton ai-tools configure' | 'baton ides configure'");
524
503
  }
525
504
  async function loadProjectManifestSafe$1() {
526
505
  try {
@@ -532,101 +511,10 @@ async function loadProjectManifestSafe$1() {
532
511
  const configCommand = defineCommand({
533
512
  meta: {
534
513
  name: "config",
535
- description: "Show Baton dashboard overview or configure settings (set, get, list)"
536
- },
537
- args: {
538
- action: {
539
- type: "positional",
540
- description: "Action: set, get, or list",
541
- required: false
542
- },
543
- key: {
544
- type: "positional",
545
- description: "Configuration key",
546
- required: false
547
- },
548
- value: {
549
- type: "positional",
550
- description: "Configuration value (for set)",
551
- required: false
552
- }
514
+ description: "Show Baton dashboard overview"
553
515
  },
554
- async run({ args }) {
555
- const action = args.action;
556
- const key = args.key;
557
- const value = args.value;
558
- if (!action) {
559
- await showDashboard();
560
- return;
561
- }
562
- const selectedAction = action;
563
- if (selectedAction === "list") {
564
- We("⚙️ Baton Configuration");
565
- const config = await loadConfig();
566
- if (Object.keys(config).length === 0) {
567
- Le("No configuration set. Using defaults.");
568
- return;
569
- }
570
- console.log("");
571
- for (const [configKey, configValue] of Object.entries(config)) console.log(`${configKey}: ${Array.isArray(configValue) ? configValue.join(", ") : configValue}`);
572
- console.log("");
573
- Le("Configuration loaded");
574
- return;
575
- }
576
- if (selectedAction === "get") {
577
- if (!key) {
578
- We("⚙️ Baton Configuration");
579
- Le("Error: Missing key argument. Usage: baton config get <key>");
580
- process.exit(1);
581
- }
582
- if (!VALID_KEYS.includes(key)) {
583
- We("⚙️ Baton Configuration");
584
- Le(`Error: Invalid key "${key}". Valid keys: ${VALID_KEYS.join(", ")}`);
585
- process.exit(1);
586
- }
587
- const configValue = (await loadConfig())[key];
588
- if (configValue === void 0) {
589
- We("⚙️ Baton Configuration");
590
- Le(`"${key}" is not set (using default)`);
591
- return;
592
- }
593
- console.log(Array.isArray(configValue) ? configValue.join(", ") : configValue);
594
- return;
595
- }
596
- if (selectedAction === "set") {
597
- if (!key || !value) {
598
- We("⚙️ Baton Configuration");
599
- Le("Error: Missing arguments. Usage: baton config set <key> <value>");
600
- process.exit(1);
601
- }
602
- if (!VALID_KEYS.includes(key)) {
603
- We("⚙️ Baton Configuration");
604
- Le(`Error: Invalid key "${key}". Valid keys: ${VALID_KEYS.join(", ")}`);
605
- process.exit(1);
606
- }
607
- We("⚙️ Baton Configuration");
608
- const config = await loadConfig();
609
- if (key === "default-scope") {
610
- if (value !== "project" && value !== "global") {
611
- Le("Error: default-scope must be either \"project\" or \"global\"");
612
- process.exit(1);
613
- }
614
- config["default-scope"] = value;
615
- } else if (key === "symlink-mode") {
616
- if (value !== "true" && value !== "false") {
617
- Le("Error: symlink-mode must be either \"true\" or \"false\"");
618
- process.exit(1);
619
- }
620
- config["symlink-mode"] = value === "true";
621
- } else if (key === "default-tools") config["default-tools"] = value.split(",").map((s) => s.trim());
622
- else if (key === "cache-dir") config["cache-dir"] = value;
623
- await saveConfig(config);
624
- Le(`✓ Set ${key} = ${Array.isArray(config[key]) ? config[key].join(", ") : config[key]}`);
625
- return;
626
- }
627
- We("⚙️ Baton Configuration");
628
- Le(`Error: Unknown action "${selectedAction}". Use: set, get, or list`);
629
- process.exit(1);
516
+ async run() {
517
+ await showDashboard();
630
518
  }
631
519
  });
632
520
 
@@ -727,7 +615,7 @@ const diffCommand = defineCommand({
727
615
  spinner.stop("Configurations merged");
728
616
  spinner.start("Computing tool intersection...");
729
617
  const globalAiTools = await getGlobalAiTools();
730
- const detectedAgents = await detectInstalledAgents();
618
+ const detectedAITools = await detectInstalledAITools();
731
619
  let syncedAiTools;
732
620
  if (globalAiTools.length > 0) {
733
621
  const developerTools = {
@@ -740,13 +628,13 @@ const diffCommand = defineCommand({
740
628
  if (intersection) for (const tool of intersection.aiTools.synced) aggregatedSyncedAi.add(tool);
741
629
  } catch {}
742
630
  syncedAiTools = aggregatedSyncedAi.size > 0 ? [...aggregatedSyncedAi] : [];
743
- } else syncedAiTools = detectedAgents;
631
+ } else syncedAiTools = detectedAITools;
744
632
  if (syncedAiTools.length === 0) {
745
633
  spinner.stop("No AI tools in intersection");
746
634
  Ne("No AI tools match. Run `baton ai-tools scan`.");
747
635
  process.exit(1);
748
636
  }
749
- const adapters = getAdaptersForKeys(syncedAiTools);
637
+ const adapters = getAIToolAdaptersForKeys(syncedAiTools);
750
638
  spinner.stop(`Comparing for: ${syncedAiTools.join(", ")}`);
751
639
  spinner.start("Comparing remote sources with placed files...");
752
640
  const diffs = [];
@@ -1283,6 +1171,7 @@ const idesCommand = defineCommand({
1283
1171
 
1284
1172
  //#endregion
1285
1173
  //#region src/utils/first-run-preferences.ts
1174
+ var import_dist = require_dist();
1286
1175
  /**
1287
1176
  * Format an IDE platform key into a display name.
1288
1177
  * Duplicated here to avoid circular dependency with ides/utils.
@@ -1339,7 +1228,7 @@ async function promptFirstRunPreferences(projectRoot, nonInteractive) {
1339
1228
  let aiTools = [];
1340
1229
  if (aiMode === "customize") {
1341
1230
  const globalTools = await getGlobalAiTools();
1342
- const allAdapters = getAllAdapters();
1231
+ const allAdapters = getAllAIToolAdapters();
1343
1232
  const selected = await je({
1344
1233
  message: "Select AI tools for this project:",
1345
1234
  options: allAdapters.map((adapter) => ({
@@ -1711,8 +1600,8 @@ async function autoScanAiTools(spinner, isInteractive) {
1711
1600
  return;
1712
1601
  }
1713
1602
  spinner.start("Scanning for installed AI tools...");
1714
- clearAgentCache();
1715
- const detectedTools = await detectInstalledAgents();
1603
+ clearAIToolCache();
1604
+ const detectedTools = await detectInstalledAITools();
1716
1605
  spinner.stop(detectedTools.length > 0 ? `Found ${detectedTools.length} AI tool${detectedTools.length !== 1 ? "s" : ""}: ${detectedTools.join(", ")}` : "No AI tools detected.");
1717
1606
  if (detectedTools.length === 0) {
1718
1607
  R.warn("No AI tools detected. You can run 'baton ai-tools scan' later.");
@@ -2054,7 +1943,7 @@ async function handleConfigureAiTools(cwd) {
2054
1943
  R.success("Project configured to use global AI tools.");
2055
1944
  return;
2056
1945
  }
2057
- const allAdapters = getAllAdapters();
1946
+ const allAdapters = getAllAIToolAdapters();
2058
1947
  const currentProjectTools = existing?.ai.useGlobal === false ? existing.ai.tools : globalTools;
2059
1948
  const options = allAdapters.map((adapter) => ({
2060
1949
  value: adapter.key,
@@ -2279,8 +2168,8 @@ const profileCommand = defineCommand({
2279
2168
  description: "Manage profiles (create, list, remove)"
2280
2169
  },
2281
2170
  subCommands: {
2282
- create: () => import("./create-BkpEXaht.mjs").then((m) => m.createCommand),
2283
- list: () => import("./list-DSLwzhBG.mjs").then((m) => m.profileListCommand),
2171
+ create: () => import("./create-DYQJmn8s.mjs").then((m) => m.createCommand),
2172
+ list: () => import("./list-o1wqD5W_.mjs").then((m) => m.profileListCommand),
2284
2173
  remove: () => import("./remove-BBs6Mv8t.mjs").then((m) => m.profileRemoveCommand)
2285
2174
  }
2286
2175
  });
@@ -2933,6 +2822,9 @@ const syncCommand = defineCommand({
2933
2822
  const rulesResult = mergeRulesWithWarnings(weightSortedProfiles);
2934
2823
  const mergedRules = rulesResult.rules;
2935
2824
  allWeightWarnings.push(...rulesResult.warnings);
2825
+ const agentsResult = mergeAgentsWithWarnings(weightSortedProfiles);
2826
+ const mergedAgents = agentsResult.agents;
2827
+ allWeightWarnings.push(...agentsResult.warnings);
2936
2828
  const memoryResult = mergeMemoryWithWarnings(weightSortedProfiles);
2937
2829
  const mergedMemory = memoryResult.entries;
2938
2830
  allWeightWarnings.push(...memoryResult.warnings);
@@ -3031,11 +2923,11 @@ const syncCommand = defineCommand({
3031
2923
  }
3032
2924
  }
3033
2925
  const mergedIdeCount = ideMap.size;
3034
- spinner.stop(`Merged: ${mergedSkills.length} skills, ${mergedRules.length} rules, ${mergedMemory.length} memory files, ${mergedCommandCount} commands, ${mergedFileCount} files, ${mergedIdeCount} IDE configs`);
2926
+ spinner.stop(`Merged: ${mergedSkills.length} skills, ${mergedRules.length} rules, ${mergedAgents.length} agents, ${mergedMemory.length} memory files, ${mergedCommandCount} commands, ${mergedFileCount} files, ${mergedIdeCount} IDE configs`);
3035
2927
  if (allWeightWarnings.length > 0) for (const w of allWeightWarnings) R.warn(`Weight conflict: "${w.profileA}" and "${w.profileB}" both define ${w.category} "${w.key}" with weight ${w.weight}. Last declared wins.`);
3036
2928
  spinner.start("Computing tool intersection...");
3037
2929
  const prefs = await resolvePreferences(projectRoot);
3038
- const detectedAgents = await detectInstalledAgents();
2930
+ const detectedAITools = await detectInstalledAITools();
3039
2931
  if (verbose) {
3040
2932
  R.info(`AI tools: ${prefs.ai.tools.join(", ") || "(none)"} (from ${prefs.ai.source} preferences)`);
3041
2933
  R.info(`IDE platforms: ${prefs.ide.platforms.join(", ") || "(none)"} (from ${prefs.ide.source} preferences)`);
@@ -3062,14 +2954,14 @@ const syncCommand = defineCommand({
3062
2954
  syncedAiTools = aggregatedSyncedAi.size > 0 ? [...aggregatedSyncedAi] : [];
3063
2955
  syncedIdePlatforms = [...aggregatedSyncedIde];
3064
2956
  } else {
3065
- syncedAiTools = detectedAgents;
2957
+ syncedAiTools = detectedAITools;
3066
2958
  syncedIdePlatforms = null;
3067
- if (detectedAgents.length > 0) {
2959
+ if (detectedAITools.length > 0) {
3068
2960
  R.warn("No AI tools configured. Run `baton ai-tools scan` to configure your tools.");
3069
- R.info(`Falling back to detected tools: ${detectedAgents.join(", ")}`);
2961
+ R.info(`Falling back to detected tools: ${detectedAITools.join(", ")}`);
3070
2962
  }
3071
2963
  }
3072
- if (syncedAiTools.length === 0 && detectedAgents.length === 0) {
2964
+ if (syncedAiTools.length === 0 && detectedAITools.length === 0) {
3073
2965
  spinner.stop("No AI tools available");
3074
2966
  Ne("No AI tools found. Install an AI coding tool first.");
3075
2967
  process.exit(1);
@@ -3098,7 +2990,7 @@ const syncCommand = defineCommand({
3098
2990
  }
3099
2991
  } else spinner.stop("No legacy files found");
3100
2992
  spinner.start("Processing configurations...");
3101
- const adapters = getAdaptersForKeys(syncedAiTools);
2993
+ const adapters = getAIToolAdaptersForKeys(syncedAiTools);
3102
2994
  const placementConfig = {
3103
2995
  mode: "copy",
3104
2996
  projectRoot
@@ -3213,6 +3105,7 @@ const syncCommand = defineCommand({
3213
3105
  if (!dryRun && syncAi) for (const adapter of adapters) {
3214
3106
  if (verbose) R.step(`[${adapter.key}] Placing rules...`);
3215
3107
  for (const ruleEntry of mergedRules) try {
3108
+ const ruleName = ruleEntry.name.replace(/\.md$/, "");
3216
3109
  const isUniversal = ruleEntry.agents.length === 0;
3217
3110
  const isForThisAdapter = ruleEntry.agents.includes(adapter.key);
3218
3111
  if (!isUniversal && !isForThisAdapter) continue;
@@ -3221,7 +3114,7 @@ const syncCommand = defineCommand({
3221
3114
  spinner.message(`Warning: Could not resolve local path for profile ${ruleEntry.profileName}`);
3222
3115
  continue;
3223
3116
  }
3224
- const ruleSourcePath = resolve(profileDir, "ai", "rules", isUniversal ? "universal" : ruleEntry.agents[0], `${ruleEntry.name}.md`);
3117
+ const ruleSourcePath = resolve(profileDir, "ai", "rules", isUniversal ? "universal" : ruleEntry.agents[0], `${ruleName}.md`);
3225
3118
  let rawContent;
3226
3119
  try {
3227
3120
  rawContent = await readFile(ruleSourcePath, "utf-8");
@@ -3231,12 +3124,12 @@ const syncCommand = defineCommand({
3231
3124
  }
3232
3125
  const parsed = parseFrontmatter(rawContent);
3233
3126
  const ruleFile = {
3234
- name: ruleEntry.name,
3127
+ name: ruleName,
3235
3128
  content: rawContent,
3236
3129
  frontmatter: Object.keys(parsed.data).length > 0 ? parsed.data : void 0
3237
3130
  };
3238
3131
  const transformed = adapter.transformRule(ruleFile);
3239
- const targetPath = adapter.getPath("rules", "project", ruleEntry.name);
3132
+ const targetPath = adapter.getPath("rules", "project", ruleName);
3240
3133
  const absolutePath = targetPath.startsWith("/") ? targetPath : resolve(projectRoot, targetPath);
3241
3134
  const existing = contentAccumulator.get(absolutePath);
3242
3135
  if (existing) {
@@ -3246,7 +3139,7 @@ const syncCommand = defineCommand({
3246
3139
  parts: [transformed.content],
3247
3140
  adapter,
3248
3141
  type: "rules",
3249
- name: ruleEntry.name,
3142
+ name: ruleName,
3250
3143
  profiles: new Set([ruleEntry.profileName])
3251
3144
  });
3252
3145
  } catch (error) {
@@ -3254,6 +3147,53 @@ const syncCommand = defineCommand({
3254
3147
  stats.errors++;
3255
3148
  }
3256
3149
  }
3150
+ if (!dryRun && syncAi) for (const adapter of adapters) {
3151
+ if (verbose) R.step(`[${adapter.key}] Placing agents...`);
3152
+ for (const agentEntry of mergedAgents) try {
3153
+ const agentName = agentEntry.name.replace(/\.md$/, "");
3154
+ const isUniversal = agentEntry.agents.length === 0;
3155
+ const isForThisAdapter = agentEntry.agents.includes(adapter.key);
3156
+ if (!isUniversal && !isForThisAdapter) continue;
3157
+ const profileDir = profileLocalPaths.get(agentEntry.profileName);
3158
+ if (!profileDir) {
3159
+ spinner.message(`Warning: Could not resolve local path for profile ${agentEntry.profileName}`);
3160
+ continue;
3161
+ }
3162
+ const agentSourcePath = resolve(profileDir, "ai", "agents", isUniversal ? "universal" : agentEntry.agents[0], `${agentName}.md`);
3163
+ let rawContent;
3164
+ try {
3165
+ rawContent = await readFile(agentSourcePath, "utf-8");
3166
+ } catch {
3167
+ spinner.message(`Warning: Could not read agent file: ${agentSourcePath}`);
3168
+ continue;
3169
+ }
3170
+ const parsed = parseFrontmatter(rawContent);
3171
+ const frontmatter = Object.keys(parsed.data).length > 0 ? parsed.data : { name: agentName };
3172
+ const agentFile = {
3173
+ name: agentName,
3174
+ content: rawContent,
3175
+ description: frontmatter.description,
3176
+ frontmatter
3177
+ };
3178
+ const transformed = adapter.transformAgent(agentFile);
3179
+ const targetPath = adapter.getPath("agents", "project", agentName);
3180
+ const absolutePath = targetPath.startsWith("/") ? targetPath : resolve(projectRoot, targetPath);
3181
+ const existing = contentAccumulator.get(absolutePath);
3182
+ if (existing) {
3183
+ existing.parts.push(transformed.content);
3184
+ existing.profiles.add(agentEntry.profileName);
3185
+ } else contentAccumulator.set(absolutePath, {
3186
+ parts: [transformed.content],
3187
+ adapter,
3188
+ type: "agents",
3189
+ name: agentName,
3190
+ profiles: new Set([agentEntry.profileName])
3191
+ });
3192
+ } catch (error) {
3193
+ spinner.message(`Error placing agent ${agentEntry.name} for ${adapter.name}: ${error}`);
3194
+ stats.errors++;
3195
+ }
3196
+ }
3257
3197
  if (!dryRun && syncAi) for (const [absolutePath, entry] of contentAccumulator) try {
3258
3198
  const combinedContent = entry.parts.join("\n\n");
3259
3199
  const result = await placeFile(combinedContent, entry.adapter, entry.type, "project", entry.name, placementConfig);
@@ -3393,6 +3333,7 @@ const syncCommand = defineCommand({
3393
3333
  if (syncAi) {
3394
3334
  parts.push(` • ${mergedSkills.length} skills`);
3395
3335
  parts.push(` • ${mergedRules.length} rules`);
3336
+ parts.push(` • ${mergedAgents.length} agents`);
3396
3337
  parts.push(` • ${mergedMemory.length} memory files`);
3397
3338
  parts.push(` • ${mergedCommandCount} commands`);
3398
3339
  }