@baton-dx/cli 0.3.1 → 0.4.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/ai-tool-detection-BFep6YS9.mjs +4 -0
- package/dist/{agent-detection-DTiVeO5W.mjs → ai-tool-detection-CMsBNa9e.mjs} +26 -82
- package/dist/ai-tool-detection-CMsBNa9e.mjs.map +1 -0
- package/dist/{create-BOcW-DBk.mjs → create-SYKl8g0B.mjs} +3 -3
- package/dist/{create-BOcW-DBk.mjs.map → create-SYKl8g0B.mjs.map} +1 -1
- package/dist/index.mjs +191 -105
- package/dist/index.mjs.map +1 -1
- package/dist/{list-DmzVXCNF.mjs → list-UzuMEqbc.mjs} +3 -3
- package/dist/{list-DmzVXCNF.mjs.map → list-UzuMEqbc.mjs.map} +1 -1
- package/dist/{src-C3-Vz-R7.mjs → src-D41VR6ro.mjs} +225 -114
- package/dist/src-D41VR6ro.mjs.map +1 -0
- package/package.json +2 -2
- package/dist/agent-detection-DTiVeO5W.mjs.map +0 -1
- package/dist/agent-detection-l61K-AbU.mjs +0 -4
- package/dist/src-C3-Vz-R7.mjs.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
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
|
|
5
|
-
import { n as
|
|
4
|
+
import { $ as loadLockfile, A as resolveProfileChain, B as cloneGitSource, C as mergeSkills, D as sortProfilesByWeight, E as isLockedProfile, F as removePlacedFiles, G as getIdePlatformTargetDir, H as ensureBatonDirGitignored, I as generateLock, J as isKnownIdePlatform, K as getRegisteredIdePlatforms, L as readLock, M as placeFile, N as discoverProfilesInSourceRepo, O as mergeContentParts, P as findSourceManifest, Q as parseSource, R as writeLock, S as mergeRulesWithWarnings, T as getProfileWeight, U as removeGitignoreManagedSection, V as collectComprehensivePatterns, W as updateGitignore, X as getAllAIToolAdapters, Y as getAIToolAdaptersForKeys, Z as parseFrontmatter, _ as require_lib, a as clearIdeCache, at as getAIToolConfig, b as mergeAgentsWithWarnings, c as getBatonHome, d as getGlobalConfigPath, et as loadProfileManifest, f as getGlobalIdePlatforms, g as setGlobalIdePlatforms, h as setGlobalAiTools, i as computeIntersection, it as SourceParseError, j as detectLegacyPaths, k as resolveProfileSupport, l as getDefaultGlobalSource, m as removeGlobalSource, n as readProjectPreferences, nt as KEBAB_CASE_REGEX, o as detectInstalledIdes, ot as getAIToolPath, p as getGlobalSources, q as idePlatformRegistry, r as writeProjectPreferences, rt as FileNotFoundError, s as addGlobalSource, st as getAllAIToolKeys, t as resolvePreferences, tt as loadProjectManifest, u as getGlobalAiTools, v as mergeMemory, w as mergeSkillsWithWarnings, x as mergeRules, y as mergeMemoryWithWarnings, z as resolveVersion } from "./src-D41VR6ro.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";
|
|
9
9
|
import { fileURLToPath } from "node:url";
|
|
10
|
-
import { homedir } from "node:os";
|
|
11
10
|
|
|
12
11
|
//#region src/commands/ai-tools/configure.ts
|
|
13
12
|
const aiToolsConfigureCommand = defineCommand({
|
|
@@ -40,7 +39,7 @@ async function runGlobalMode$1(nonInteractive) {
|
|
|
40
39
|
Le("No changes made.");
|
|
41
40
|
return;
|
|
42
41
|
}
|
|
43
|
-
const options =
|
|
42
|
+
const options = getAllAIToolAdapters().map((adapter) => {
|
|
44
43
|
const isSaved = currentTools.includes(adapter.key);
|
|
45
44
|
return {
|
|
46
45
|
value: adapter.key,
|
|
@@ -126,7 +125,7 @@ async function runProjectMode$1(nonInteractive) {
|
|
|
126
125
|
Le("Configuration complete.");
|
|
127
126
|
return;
|
|
128
127
|
}
|
|
129
|
-
const allAdapters =
|
|
128
|
+
const allAdapters = getAllAIToolAdapters();
|
|
130
129
|
const currentProjectTools = existing?.ai.useGlobal === false ? existing.ai.tools : globalTools;
|
|
131
130
|
const options = allAdapters.map((adapter) => {
|
|
132
131
|
const isGlobal = globalTools.includes(adapter.key);
|
|
@@ -182,37 +181,37 @@ const aiToolsListCommand = defineCommand({
|
|
|
182
181
|
async run({ args }) {
|
|
183
182
|
if (!args.json) We("Baton - AI Tools");
|
|
184
183
|
const savedTools = await getGlobalAiTools();
|
|
185
|
-
const
|
|
186
|
-
const keysToShow = args.all ?
|
|
187
|
-
const
|
|
188
|
-
const isSaved = savedTools.includes(
|
|
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);
|
|
189
188
|
let skillCount = 0;
|
|
190
189
|
let ruleCount = 0;
|
|
191
|
-
let
|
|
190
|
+
let aiToolConfigCount = 0;
|
|
192
191
|
let memoryCount = 0;
|
|
193
192
|
let commandCount = 0;
|
|
194
193
|
if (isSaved) {
|
|
195
|
-
skillCount = await countConfigs(
|
|
196
|
-
ruleCount = await countConfigs(
|
|
197
|
-
|
|
198
|
-
memoryCount = await countConfigs(
|
|
199
|
-
commandCount = await countConfigs(
|
|
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");
|
|
200
199
|
}
|
|
201
200
|
const paths = {
|
|
202
|
-
skills:
|
|
203
|
-
rules:
|
|
204
|
-
agents:
|
|
205
|
-
memory:
|
|
206
|
-
commands:
|
|
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", "")
|
|
207
206
|
};
|
|
208
207
|
return {
|
|
209
|
-
key:
|
|
210
|
-
name:
|
|
208
|
+
key: toolKey,
|
|
209
|
+
name: getAIToolConfig(toolKey).name,
|
|
211
210
|
saved: isSaved,
|
|
212
211
|
counts: {
|
|
213
212
|
skills: skillCount,
|
|
214
213
|
rules: ruleCount,
|
|
215
|
-
agents:
|
|
214
|
+
agents: aiToolConfigCount,
|
|
216
215
|
memory: memoryCount,
|
|
217
216
|
commands: commandCount
|
|
218
217
|
},
|
|
@@ -220,23 +219,23 @@ const aiToolsListCommand = defineCommand({
|
|
|
220
219
|
};
|
|
221
220
|
}));
|
|
222
221
|
if (args.json) {
|
|
223
|
-
console.log(JSON.stringify(
|
|
222
|
+
console.log(JSON.stringify(toolStatuses, null, 2));
|
|
224
223
|
return;
|
|
225
224
|
}
|
|
226
225
|
if (savedTools.length === 0) {
|
|
227
226
|
R.warn("No AI tools saved in global config.");
|
|
228
227
|
R.info("Run 'baton ai-tools scan' to detect and save your AI tools.");
|
|
229
228
|
console.log("");
|
|
230
|
-
R.info(`All ${
|
|
231
|
-
for (const key of
|
|
232
|
-
const config =
|
|
229
|
+
R.info(`All ${allAIToolKeys.length} supported tools:`);
|
|
230
|
+
for (const key of allAIToolKeys) {
|
|
231
|
+
const config = getAIToolConfig(key);
|
|
233
232
|
console.log(` \x1b[90m- ${config.name}\x1b[0m`);
|
|
234
233
|
}
|
|
235
234
|
Le("Run 'baton ai-tools scan' to get started.");
|
|
236
235
|
return;
|
|
237
236
|
}
|
|
238
237
|
console.log(`\nSaved AI tools (${savedTools.length}):\n`);
|
|
239
|
-
for (const agent of
|
|
238
|
+
for (const agent of toolStatuses) {
|
|
240
239
|
const statusColor = agent.saved ? "\x1B[32m" : "\x1B[90m";
|
|
241
240
|
const status = agent.saved ? "✓" : "✗";
|
|
242
241
|
console.log(`${statusColor}${status}[0m ${agent.name.padEnd(20)}`);
|
|
@@ -257,11 +256,11 @@ const aiToolsListCommand = defineCommand({
|
|
|
257
256
|
}
|
|
258
257
|
});
|
|
259
258
|
/**
|
|
260
|
-
* Count config files of a given type for
|
|
259
|
+
* Count config files of a given type for a tool
|
|
261
260
|
*/
|
|
262
|
-
async function countConfigs(
|
|
261
|
+
async function countConfigs(toolKey, configType, scope) {
|
|
263
262
|
try {
|
|
264
|
-
const dirPath =
|
|
263
|
+
const dirPath = getAIToolPath(toolKey, configType, scope, "").replace(/{name}.*$/, "").replace(/\/$/, "");
|
|
265
264
|
if (!(await stat(dirPath)).isDirectory()) return 0;
|
|
266
265
|
return (await readdir(dirPath)).length;
|
|
267
266
|
} catch (_error) {
|
|
@@ -285,15 +284,15 @@ const aiToolsScanCommand = defineCommand({
|
|
|
285
284
|
We("Baton - AI Tool Scanner");
|
|
286
285
|
const spinner = bt();
|
|
287
286
|
spinner.start("Scanning for AI tools...");
|
|
288
|
-
|
|
289
|
-
const
|
|
290
|
-
const allAdapters =
|
|
287
|
+
clearAIToolCache();
|
|
288
|
+
const detectedAITools = await detectInstalledAITools();
|
|
289
|
+
const allAdapters = getAllAIToolAdapters();
|
|
291
290
|
const currentTools = await getGlobalAiTools();
|
|
292
291
|
spinner.stop("Scan complete.");
|
|
293
|
-
if (
|
|
292
|
+
if (detectedAITools.length > 0) R.success(`Found ${detectedAITools.length} AI tool${detectedAITools.length !== 1 ? "s" : ""} on your system.`);
|
|
294
293
|
else R.warn("No AI tools detected on your system.");
|
|
295
294
|
if (args.yes) {
|
|
296
|
-
const detectedKeys =
|
|
295
|
+
const detectedKeys = detectedAITools;
|
|
297
296
|
if (detectedKeys.length !== currentTools.length || detectedKeys.some((key) => !currentTools.includes(key))) {
|
|
298
297
|
await setGlobalAiTools(detectedKeys);
|
|
299
298
|
R.success(`Saved ${detectedKeys.length} detected tool(s) to global config.`);
|
|
@@ -302,7 +301,7 @@ const aiToolsScanCommand = defineCommand({
|
|
|
302
301
|
return;
|
|
303
302
|
}
|
|
304
303
|
const options = allAdapters.map((adapter) => {
|
|
305
|
-
const isDetected =
|
|
304
|
+
const isDetected = detectedAITools.includes(adapter.key);
|
|
306
305
|
return {
|
|
307
306
|
value: adapter.key,
|
|
308
307
|
label: isDetected ? `${adapter.name} (detected)` : adapter.name
|
|
@@ -311,7 +310,7 @@ const aiToolsScanCommand = defineCommand({
|
|
|
311
310
|
const selected = await je({
|
|
312
311
|
message: "Select which AI tools to save:",
|
|
313
312
|
options,
|
|
314
|
-
initialValues:
|
|
313
|
+
initialValues: detectedAITools
|
|
315
314
|
});
|
|
316
315
|
if (Ct(selected)) {
|
|
317
316
|
Le("Scan finished (not saved).");
|
|
@@ -421,7 +420,6 @@ function formatIntersectionSummary(intersection) {
|
|
|
421
420
|
|
|
422
421
|
//#endregion
|
|
423
422
|
//#region src/commands/config.ts
|
|
424
|
-
const CONFIG_FILE = join(homedir(), ".baton", "config.yaml");
|
|
425
423
|
const VALID_KEYS = [
|
|
426
424
|
"cache-dir",
|
|
427
425
|
"default-scope",
|
|
@@ -429,16 +427,18 @@ const VALID_KEYS = [
|
|
|
429
427
|
"default-tools"
|
|
430
428
|
];
|
|
431
429
|
async function loadConfig() {
|
|
430
|
+
const configFile = getGlobalConfigPath();
|
|
432
431
|
try {
|
|
433
|
-
await access(
|
|
432
|
+
await access(configFile);
|
|
434
433
|
} catch {
|
|
435
434
|
return {};
|
|
436
435
|
}
|
|
437
|
-
return (0, import_dist.parse)(await readFile(
|
|
436
|
+
return (0, import_dist.parse)(await readFile(configFile, "utf-8"));
|
|
438
437
|
}
|
|
439
438
|
async function saveConfig(config) {
|
|
440
|
-
|
|
441
|
-
await
|
|
439
|
+
const configFile = getGlobalConfigPath();
|
|
440
|
+
await mkdir(getBatonHome(), { recursive: true });
|
|
441
|
+
await writeFile(configFile, (0, import_dist.stringify)(config), "utf-8");
|
|
442
442
|
}
|
|
443
443
|
async function showDashboard() {
|
|
444
444
|
We("Baton Dashboard");
|
|
@@ -467,7 +467,7 @@ async function showDashboard() {
|
|
|
467
467
|
if (resolvedAiTools.length > 0) {
|
|
468
468
|
const toolNames = resolvedAiTools.map((key) => {
|
|
469
469
|
try {
|
|
470
|
-
return
|
|
470
|
+
return getAIToolConfig(key).name;
|
|
471
471
|
} catch {
|
|
472
472
|
return key;
|
|
473
473
|
}
|
|
@@ -485,7 +485,7 @@ async function showDashboard() {
|
|
|
485
485
|
if (aiTools.length > 0) {
|
|
486
486
|
const toolNames = aiTools.map((key) => {
|
|
487
487
|
try {
|
|
488
|
-
return
|
|
488
|
+
return getAIToolConfig(key).name;
|
|
489
489
|
} catch {
|
|
490
490
|
return key;
|
|
491
491
|
}
|
|
@@ -727,7 +727,7 @@ const diffCommand = defineCommand({
|
|
|
727
727
|
spinner.stop("Configurations merged");
|
|
728
728
|
spinner.start("Computing tool intersection...");
|
|
729
729
|
const globalAiTools = await getGlobalAiTools();
|
|
730
|
-
const
|
|
730
|
+
const detectedAITools = await detectInstalledAITools();
|
|
731
731
|
let syncedAiTools;
|
|
732
732
|
if (globalAiTools.length > 0) {
|
|
733
733
|
const developerTools = {
|
|
@@ -740,13 +740,13 @@ const diffCommand = defineCommand({
|
|
|
740
740
|
if (intersection) for (const tool of intersection.aiTools.synced) aggregatedSyncedAi.add(tool);
|
|
741
741
|
} catch {}
|
|
742
742
|
syncedAiTools = aggregatedSyncedAi.size > 0 ? [...aggregatedSyncedAi] : [];
|
|
743
|
-
} else syncedAiTools =
|
|
743
|
+
} else syncedAiTools = detectedAITools;
|
|
744
744
|
if (syncedAiTools.length === 0) {
|
|
745
745
|
spinner.stop("No AI tools in intersection");
|
|
746
746
|
Ne("No AI tools match. Run `baton ai-tools scan`.");
|
|
747
747
|
process.exit(1);
|
|
748
748
|
}
|
|
749
|
-
const adapters =
|
|
749
|
+
const adapters = getAIToolAdaptersForKeys(syncedAiTools);
|
|
750
750
|
spinner.stop(`Comparing for: ${syncedAiTools.join(", ")}`);
|
|
751
751
|
spinner.start("Comparing remote sources with placed files...");
|
|
752
752
|
const diffs = [];
|
|
@@ -1339,7 +1339,7 @@ async function promptFirstRunPreferences(projectRoot, nonInteractive) {
|
|
|
1339
1339
|
let aiTools = [];
|
|
1340
1340
|
if (aiMode === "customize") {
|
|
1341
1341
|
const globalTools = await getGlobalAiTools();
|
|
1342
|
-
const allAdapters =
|
|
1342
|
+
const allAdapters = getAllAIToolAdapters();
|
|
1343
1343
|
const selected = await je({
|
|
1344
1344
|
message: "Select AI tools for this project:",
|
|
1345
1345
|
options: allAdapters.map((adapter) => ({
|
|
@@ -1650,7 +1650,22 @@ const initCommand = defineCommand({
|
|
|
1650
1650
|
}
|
|
1651
1651
|
}
|
|
1652
1652
|
await showProfileIntersections(profileSources);
|
|
1653
|
-
|
|
1653
|
+
let gitignoreSetting = true;
|
|
1654
|
+
if (isInteractive) {
|
|
1655
|
+
const shouldGitignore = await Re({
|
|
1656
|
+
message: "Add synced AI tool and IDE config files to .gitignore?",
|
|
1657
|
+
initialValue: true
|
|
1658
|
+
});
|
|
1659
|
+
if (Ct(shouldGitignore)) {
|
|
1660
|
+
Ne("Setup cancelled.");
|
|
1661
|
+
process.exit(0);
|
|
1662
|
+
}
|
|
1663
|
+
gitignoreSetting = shouldGitignore;
|
|
1664
|
+
}
|
|
1665
|
+
const yamlContent = (0, import_dist.stringify)({
|
|
1666
|
+
profiles: profileSources.map((source) => ({ source })),
|
|
1667
|
+
gitignore: gitignoreSetting
|
|
1668
|
+
});
|
|
1654
1669
|
spinner.start("Creating baton.yaml...");
|
|
1655
1670
|
await writeFile(join(cwd, "baton.yaml"), yamlContent, "utf-8");
|
|
1656
1671
|
spinner.stop("✅ Created baton.yaml");
|
|
@@ -1660,10 +1675,11 @@ const initCommand = defineCommand({
|
|
|
1660
1675
|
try {
|
|
1661
1676
|
gitignoreContent = await readFile(gitignorePath, "utf-8");
|
|
1662
1677
|
} catch (_error) {}
|
|
1663
|
-
if (!gitignoreContent.includes(".baton/")) {
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1678
|
+
if (!gitignoreContent.includes(".baton/")) await writeFile(gitignorePath, gitignoreContent ? `${gitignoreContent}\n\n# Baton cache\n.baton/\n` : "# Baton cache\n.baton/\n", "utf-8");
|
|
1679
|
+
if (gitignoreSetting) {
|
|
1680
|
+
await updateGitignore(cwd, collectComprehensivePatterns({ fileTargets: [] }));
|
|
1681
|
+
spinner.stop("✅ Updated .gitignore with managed file patterns");
|
|
1682
|
+
} else spinner.stop("✅ Added .baton/ to .gitignore");
|
|
1667
1683
|
spinner.start("Creating .baton directory...");
|
|
1668
1684
|
try {
|
|
1669
1685
|
await mkdir(join(cwd, ".baton"), { recursive: true });
|
|
@@ -1695,8 +1711,8 @@ async function autoScanAiTools(spinner, isInteractive) {
|
|
|
1695
1711
|
return;
|
|
1696
1712
|
}
|
|
1697
1713
|
spinner.start("Scanning for installed AI tools...");
|
|
1698
|
-
|
|
1699
|
-
const detectedTools = await
|
|
1714
|
+
clearAIToolCache();
|
|
1715
|
+
const detectedTools = await detectInstalledAITools();
|
|
1700
1716
|
spinner.stop(detectedTools.length > 0 ? `Found ${detectedTools.length} AI tool${detectedTools.length !== 1 ? "s" : ""}: ${detectedTools.join(", ")}` : "No AI tools detected.");
|
|
1701
1717
|
if (detectedTools.length === 0) {
|
|
1702
1718
|
R.warn("No AI tools detected. You can run 'baton ai-tools scan' later.");
|
|
@@ -2038,7 +2054,7 @@ async function handleConfigureAiTools(cwd) {
|
|
|
2038
2054
|
R.success("Project configured to use global AI tools.");
|
|
2039
2055
|
return;
|
|
2040
2056
|
}
|
|
2041
|
-
const allAdapters =
|
|
2057
|
+
const allAdapters = getAllAIToolAdapters();
|
|
2042
2058
|
const currentProjectTools = existing?.ai.useGlobal === false ? existing.ai.tools : globalTools;
|
|
2043
2059
|
const options = allAdapters.map((adapter) => ({
|
|
2044
2060
|
value: adapter.key,
|
|
@@ -2132,6 +2148,31 @@ async function handleConfigureIdes(cwd) {
|
|
|
2132
2148
|
});
|
|
2133
2149
|
R.success(`Project configured with ${selectedKeys.length} IDE platform(s).`);
|
|
2134
2150
|
}
|
|
2151
|
+
async function handleConfigureGitignore(cwd) {
|
|
2152
|
+
const manifestPath = join(cwd, "baton.yaml");
|
|
2153
|
+
const manifest = await loadProjectManifestSafe(cwd);
|
|
2154
|
+
if (!manifest) {
|
|
2155
|
+
R.error("Could not load baton.yaml");
|
|
2156
|
+
return;
|
|
2157
|
+
}
|
|
2158
|
+
const currentSetting = manifest.gitignore !== false;
|
|
2159
|
+
R.info(currentSetting ? "Currently: synced files ARE gitignored" : "Currently: synced files are NOT gitignored (committed to repo)");
|
|
2160
|
+
const newSetting = await Re({
|
|
2161
|
+
message: "Add synced AI tool and IDE config files to .gitignore?",
|
|
2162
|
+
initialValue: currentSetting
|
|
2163
|
+
});
|
|
2164
|
+
if (Ct(newSetting)) {
|
|
2165
|
+
R.warn("Cancelled.");
|
|
2166
|
+
return;
|
|
2167
|
+
}
|
|
2168
|
+
if (newSetting === currentSetting) {
|
|
2169
|
+
R.info("No change.");
|
|
2170
|
+
return;
|
|
2171
|
+
}
|
|
2172
|
+
manifest.gitignore = newSetting;
|
|
2173
|
+
await writeFile(manifestPath, (0, import_dist.stringify)(manifest), "utf-8");
|
|
2174
|
+
R.success(newSetting ? "Enabled .gitignore management. Run 'baton sync' to update." : "Disabled .gitignore management. Run 'baton sync' to clean up.");
|
|
2175
|
+
}
|
|
2135
2176
|
const manageCommand = defineCommand({
|
|
2136
2177
|
meta: {
|
|
2137
2178
|
name: "manage",
|
|
@@ -2174,6 +2215,11 @@ const manageCommand = defineCommand({
|
|
|
2174
2215
|
label: "Configure IDEs for this project",
|
|
2175
2216
|
hint: "Choose which IDEs to sync"
|
|
2176
2217
|
},
|
|
2218
|
+
{
|
|
2219
|
+
value: "configure-gitignore",
|
|
2220
|
+
label: "Configure .gitignore",
|
|
2221
|
+
hint: "Choose whether synced files are gitignored"
|
|
2222
|
+
},
|
|
2177
2223
|
{
|
|
2178
2224
|
value: "remove-baton",
|
|
2179
2225
|
label: "Remove Baton",
|
|
@@ -2209,6 +2255,10 @@ const manageCommand = defineCommand({
|
|
|
2209
2255
|
console.log("");
|
|
2210
2256
|
await handleConfigureIdes(cwd);
|
|
2211
2257
|
console.log("");
|
|
2258
|
+
} else if (action === "configure-gitignore") {
|
|
2259
|
+
console.log("");
|
|
2260
|
+
await handleConfigureGitignore(cwd);
|
|
2261
|
+
console.log("");
|
|
2212
2262
|
} else if (action === "remove-baton") {
|
|
2213
2263
|
console.log("");
|
|
2214
2264
|
if (await handleRemoveBaton(cwd)) {
|
|
@@ -2229,8 +2279,8 @@ const profileCommand = defineCommand({
|
|
|
2229
2279
|
description: "Manage profiles (create, list, remove)"
|
|
2230
2280
|
},
|
|
2231
2281
|
subCommands: {
|
|
2232
|
-
create: () => import("./create-
|
|
2233
|
-
list: () => import("./list-
|
|
2282
|
+
create: () => import("./create-SYKl8g0B.mjs").then((m) => m.createCommand),
|
|
2283
|
+
list: () => import("./list-UzuMEqbc.mjs").then((m) => m.profileListCommand),
|
|
2234
2284
|
remove: () => import("./remove-BBs6Mv8t.mjs").then((m) => m.profileRemoveCommand)
|
|
2235
2285
|
}
|
|
2236
2286
|
});
|
|
@@ -2688,35 +2738,25 @@ async function copyDirectoryRecursive(sourceDir, targetDir) {
|
|
|
2688
2738
|
return placed;
|
|
2689
2739
|
}
|
|
2690
2740
|
/**
|
|
2691
|
-
*
|
|
2692
|
-
*
|
|
2693
|
-
*
|
|
2741
|
+
* Handle .gitignore update based on the project manifest's gitignore setting.
|
|
2742
|
+
*
|
|
2743
|
+
* When gitignore is enabled (default): writes comprehensive patterns for ALL
|
|
2744
|
+
* known AI tools and IDE platforms to ensure stable, dev-independent content.
|
|
2745
|
+
* When disabled: removes any existing managed section.
|
|
2746
|
+
* Always ensures .baton/ is gitignored regardless of setting.
|
|
2694
2747
|
*/
|
|
2695
|
-
async function
|
|
2696
|
-
const {
|
|
2697
|
-
const
|
|
2698
|
-
|
|
2699
|
-
if (
|
|
2700
|
-
for (const tool of intersection.aiTools.synced) profileSupportedAiTools.add(tool);
|
|
2701
|
-
for (const tool of intersection.aiTools.unavailable) profileSupportedAiTools.add(tool);
|
|
2702
|
-
for (const plat of intersection.idePlatforms.synced) profileSupportedIdePlatforms.add(plat);
|
|
2703
|
-
for (const plat of intersection.idePlatforms.unavailable) profileSupportedIdePlatforms.add(plat);
|
|
2704
|
-
}
|
|
2705
|
-
else {
|
|
2706
|
-
for (const adapter of adapters) profileSupportedAiTools.add(adapter.key);
|
|
2707
|
-
for (const entry of ideMap.values()) profileSupportedIdePlatforms.add(entry.ideKey);
|
|
2708
|
-
}
|
|
2709
|
-
const hasContent = mergedSkills.length > 0 || mergedRules.length > 0 || mergedMemory.length > 0 || mergedCommandCount > 0;
|
|
2710
|
-
const gitignorePatterns = collectProfileSupportPatterns({
|
|
2711
|
-
profileAiTools: [...profileSupportedAiTools],
|
|
2712
|
-
profileIdePlatforms: [...profileSupportedIdePlatforms],
|
|
2713
|
-
fileTargets: [...fileMap.values()].map((f) => f.target),
|
|
2714
|
-
hasContent
|
|
2715
|
-
});
|
|
2716
|
-
if (gitignorePatterns.length > 0) {
|
|
2748
|
+
async function handleGitignoreUpdate(params) {
|
|
2749
|
+
const { projectManifest, fileMap, projectRoot, spinner } = params;
|
|
2750
|
+
const gitignoreEnabled = projectManifest.gitignore !== false;
|
|
2751
|
+
await ensureBatonDirGitignored(projectRoot);
|
|
2752
|
+
if (gitignoreEnabled) {
|
|
2717
2753
|
spinner.start("Updating .gitignore...");
|
|
2718
|
-
const updated = await updateGitignore(projectRoot,
|
|
2719
|
-
spinner.stop(updated ? "Updated .gitignore with
|
|
2754
|
+
const updated = await updateGitignore(projectRoot, collectComprehensivePatterns({ fileTargets: [...fileMap.values()].map((f) => f.target) }));
|
|
2755
|
+
spinner.stop(updated ? "Updated .gitignore with managed patterns" : ".gitignore already up to date");
|
|
2756
|
+
} else {
|
|
2757
|
+
spinner.start("Checking .gitignore...");
|
|
2758
|
+
const removed = await removeGitignoreManagedSection(projectRoot);
|
|
2759
|
+
spinner.stop(removed ? "Removed managed section from .gitignore" : ".gitignore unchanged");
|
|
2720
2760
|
}
|
|
2721
2761
|
}
|
|
2722
2762
|
/**
|
|
@@ -2893,6 +2933,9 @@ const syncCommand = defineCommand({
|
|
|
2893
2933
|
const rulesResult = mergeRulesWithWarnings(weightSortedProfiles);
|
|
2894
2934
|
const mergedRules = rulesResult.rules;
|
|
2895
2935
|
allWeightWarnings.push(...rulesResult.warnings);
|
|
2936
|
+
const agentsResult = mergeAgentsWithWarnings(weightSortedProfiles);
|
|
2937
|
+
const mergedAgents = agentsResult.agents;
|
|
2938
|
+
allWeightWarnings.push(...agentsResult.warnings);
|
|
2896
2939
|
const memoryResult = mergeMemoryWithWarnings(weightSortedProfiles);
|
|
2897
2940
|
const mergedMemory = memoryResult.entries;
|
|
2898
2941
|
allWeightWarnings.push(...memoryResult.warnings);
|
|
@@ -2991,11 +3034,11 @@ const syncCommand = defineCommand({
|
|
|
2991
3034
|
}
|
|
2992
3035
|
}
|
|
2993
3036
|
const mergedIdeCount = ideMap.size;
|
|
2994
|
-
spinner.stop(`Merged: ${mergedSkills.length} skills, ${mergedRules.length} rules, ${mergedMemory.length} memory files, ${mergedCommandCount} commands, ${mergedFileCount} files, ${mergedIdeCount} IDE configs`);
|
|
3037
|
+
spinner.stop(`Merged: ${mergedSkills.length} skills, ${mergedRules.length} rules, ${mergedAgents.length} agents, ${mergedMemory.length} memory files, ${mergedCommandCount} commands, ${mergedFileCount} files, ${mergedIdeCount} IDE configs`);
|
|
2995
3038
|
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.`);
|
|
2996
3039
|
spinner.start("Computing tool intersection...");
|
|
2997
3040
|
const prefs = await resolvePreferences(projectRoot);
|
|
2998
|
-
const
|
|
3041
|
+
const detectedAITools = await detectInstalledAITools();
|
|
2999
3042
|
if (verbose) {
|
|
3000
3043
|
R.info(`AI tools: ${prefs.ai.tools.join(", ") || "(none)"} (from ${prefs.ai.source} preferences)`);
|
|
3001
3044
|
R.info(`IDE platforms: ${prefs.ide.platforms.join(", ") || "(none)"} (from ${prefs.ide.source} preferences)`);
|
|
@@ -3022,14 +3065,14 @@ const syncCommand = defineCommand({
|
|
|
3022
3065
|
syncedAiTools = aggregatedSyncedAi.size > 0 ? [...aggregatedSyncedAi] : [];
|
|
3023
3066
|
syncedIdePlatforms = [...aggregatedSyncedIde];
|
|
3024
3067
|
} else {
|
|
3025
|
-
syncedAiTools =
|
|
3068
|
+
syncedAiTools = detectedAITools;
|
|
3026
3069
|
syncedIdePlatforms = null;
|
|
3027
|
-
if (
|
|
3070
|
+
if (detectedAITools.length > 0) {
|
|
3028
3071
|
R.warn("No AI tools configured. Run `baton ai-tools scan` to configure your tools.");
|
|
3029
|
-
R.info(`Falling back to detected tools: ${
|
|
3072
|
+
R.info(`Falling back to detected tools: ${detectedAITools.join(", ")}`);
|
|
3030
3073
|
}
|
|
3031
3074
|
}
|
|
3032
|
-
if (syncedAiTools.length === 0 &&
|
|
3075
|
+
if (syncedAiTools.length === 0 && detectedAITools.length === 0) {
|
|
3033
3076
|
spinner.stop("No AI tools available");
|
|
3034
3077
|
Ne("No AI tools found. Install an AI coding tool first.");
|
|
3035
3078
|
process.exit(1);
|
|
@@ -3058,7 +3101,7 @@ const syncCommand = defineCommand({
|
|
|
3058
3101
|
}
|
|
3059
3102
|
} else spinner.stop("No legacy files found");
|
|
3060
3103
|
spinner.start("Processing configurations...");
|
|
3061
|
-
const adapters =
|
|
3104
|
+
const adapters = getAIToolAdaptersForKeys(syncedAiTools);
|
|
3062
3105
|
const placementConfig = {
|
|
3063
3106
|
mode: "copy",
|
|
3064
3107
|
projectRoot
|
|
@@ -3173,6 +3216,7 @@ const syncCommand = defineCommand({
|
|
|
3173
3216
|
if (!dryRun && syncAi) for (const adapter of adapters) {
|
|
3174
3217
|
if (verbose) R.step(`[${adapter.key}] Placing rules...`);
|
|
3175
3218
|
for (const ruleEntry of mergedRules) try {
|
|
3219
|
+
const ruleName = ruleEntry.name.replace(/\.md$/, "");
|
|
3176
3220
|
const isUniversal = ruleEntry.agents.length === 0;
|
|
3177
3221
|
const isForThisAdapter = ruleEntry.agents.includes(adapter.key);
|
|
3178
3222
|
if (!isUniversal && !isForThisAdapter) continue;
|
|
@@ -3181,7 +3225,7 @@ const syncCommand = defineCommand({
|
|
|
3181
3225
|
spinner.message(`Warning: Could not resolve local path for profile ${ruleEntry.profileName}`);
|
|
3182
3226
|
continue;
|
|
3183
3227
|
}
|
|
3184
|
-
const ruleSourcePath = resolve(profileDir, "ai", "rules", isUniversal ? "universal" : ruleEntry.agents[0], `${
|
|
3228
|
+
const ruleSourcePath = resolve(profileDir, "ai", "rules", isUniversal ? "universal" : ruleEntry.agents[0], `${ruleName}.md`);
|
|
3185
3229
|
let rawContent;
|
|
3186
3230
|
try {
|
|
3187
3231
|
rawContent = await readFile(ruleSourcePath, "utf-8");
|
|
@@ -3191,12 +3235,12 @@ const syncCommand = defineCommand({
|
|
|
3191
3235
|
}
|
|
3192
3236
|
const parsed = parseFrontmatter(rawContent);
|
|
3193
3237
|
const ruleFile = {
|
|
3194
|
-
name:
|
|
3238
|
+
name: ruleName,
|
|
3195
3239
|
content: rawContent,
|
|
3196
3240
|
frontmatter: Object.keys(parsed.data).length > 0 ? parsed.data : void 0
|
|
3197
3241
|
};
|
|
3198
3242
|
const transformed = adapter.transformRule(ruleFile);
|
|
3199
|
-
const targetPath = adapter.getPath("rules", "project",
|
|
3243
|
+
const targetPath = adapter.getPath("rules", "project", ruleName);
|
|
3200
3244
|
const absolutePath = targetPath.startsWith("/") ? targetPath : resolve(projectRoot, targetPath);
|
|
3201
3245
|
const existing = contentAccumulator.get(absolutePath);
|
|
3202
3246
|
if (existing) {
|
|
@@ -3206,7 +3250,7 @@ const syncCommand = defineCommand({
|
|
|
3206
3250
|
parts: [transformed.content],
|
|
3207
3251
|
adapter,
|
|
3208
3252
|
type: "rules",
|
|
3209
|
-
name:
|
|
3253
|
+
name: ruleName,
|
|
3210
3254
|
profiles: new Set([ruleEntry.profileName])
|
|
3211
3255
|
});
|
|
3212
3256
|
} catch (error) {
|
|
@@ -3214,6 +3258,53 @@ const syncCommand = defineCommand({
|
|
|
3214
3258
|
stats.errors++;
|
|
3215
3259
|
}
|
|
3216
3260
|
}
|
|
3261
|
+
if (!dryRun && syncAi) for (const adapter of adapters) {
|
|
3262
|
+
if (verbose) R.step(`[${adapter.key}] Placing agents...`);
|
|
3263
|
+
for (const agentEntry of mergedAgents) try {
|
|
3264
|
+
const agentName = agentEntry.name.replace(/\.md$/, "");
|
|
3265
|
+
const isUniversal = agentEntry.agents.length === 0;
|
|
3266
|
+
const isForThisAdapter = agentEntry.agents.includes(adapter.key);
|
|
3267
|
+
if (!isUniversal && !isForThisAdapter) continue;
|
|
3268
|
+
const profileDir = profileLocalPaths.get(agentEntry.profileName);
|
|
3269
|
+
if (!profileDir) {
|
|
3270
|
+
spinner.message(`Warning: Could not resolve local path for profile ${agentEntry.profileName}`);
|
|
3271
|
+
continue;
|
|
3272
|
+
}
|
|
3273
|
+
const agentSourcePath = resolve(profileDir, "ai", "agents", isUniversal ? "universal" : agentEntry.agents[0], `${agentName}.md`);
|
|
3274
|
+
let rawContent;
|
|
3275
|
+
try {
|
|
3276
|
+
rawContent = await readFile(agentSourcePath, "utf-8");
|
|
3277
|
+
} catch {
|
|
3278
|
+
spinner.message(`Warning: Could not read agent file: ${agentSourcePath}`);
|
|
3279
|
+
continue;
|
|
3280
|
+
}
|
|
3281
|
+
const parsed = parseFrontmatter(rawContent);
|
|
3282
|
+
const frontmatter = Object.keys(parsed.data).length > 0 ? parsed.data : { name: agentName };
|
|
3283
|
+
const agentFile = {
|
|
3284
|
+
name: agentName,
|
|
3285
|
+
content: rawContent,
|
|
3286
|
+
description: frontmatter.description,
|
|
3287
|
+
frontmatter
|
|
3288
|
+
};
|
|
3289
|
+
const transformed = adapter.transformAgent(agentFile);
|
|
3290
|
+
const targetPath = adapter.getPath("agents", "project", agentName);
|
|
3291
|
+
const absolutePath = targetPath.startsWith("/") ? targetPath : resolve(projectRoot, targetPath);
|
|
3292
|
+
const existing = contentAccumulator.get(absolutePath);
|
|
3293
|
+
if (existing) {
|
|
3294
|
+
existing.parts.push(transformed.content);
|
|
3295
|
+
existing.profiles.add(agentEntry.profileName);
|
|
3296
|
+
} else contentAccumulator.set(absolutePath, {
|
|
3297
|
+
parts: [transformed.content],
|
|
3298
|
+
adapter,
|
|
3299
|
+
type: "agents",
|
|
3300
|
+
name: agentName,
|
|
3301
|
+
profiles: new Set([agentEntry.profileName])
|
|
3302
|
+
});
|
|
3303
|
+
} catch (error) {
|
|
3304
|
+
spinner.message(`Error placing agent ${agentEntry.name} for ${adapter.name}: ${error}`);
|
|
3305
|
+
stats.errors++;
|
|
3306
|
+
}
|
|
3307
|
+
}
|
|
3217
3308
|
if (!dryRun && syncAi) for (const [absolutePath, entry] of contentAccumulator) try {
|
|
3218
3309
|
const combinedContent = entry.parts.join("\n\n");
|
|
3219
3310
|
const result = await placeFile(combinedContent, entry.adapter, entry.type, "project", entry.name, placementConfig);
|
|
@@ -3327,14 +3418,8 @@ const syncCommand = defineCommand({
|
|
|
3327
3418
|
stats.errors++;
|
|
3328
3419
|
}
|
|
3329
3420
|
spinner.stop(dryRun ? `Would place files for ${adapters.length} agent(s)` : `Placed ${stats.created} file(s) for ${adapters.length} agent(s)`);
|
|
3330
|
-
if (!dryRun) await
|
|
3331
|
-
|
|
3332
|
-
adapters,
|
|
3333
|
-
ideMap,
|
|
3334
|
-
mergedSkills,
|
|
3335
|
-
mergedRules,
|
|
3336
|
-
mergedMemory,
|
|
3337
|
-
mergedCommandCount,
|
|
3421
|
+
if (!dryRun) await handleGitignoreUpdate({
|
|
3422
|
+
projectManifest,
|
|
3338
3423
|
fileMap,
|
|
3339
3424
|
projectRoot,
|
|
3340
3425
|
spinner
|
|
@@ -3359,6 +3444,7 @@ const syncCommand = defineCommand({
|
|
|
3359
3444
|
if (syncAi) {
|
|
3360
3445
|
parts.push(` • ${mergedSkills.length} skills`);
|
|
3361
3446
|
parts.push(` • ${mergedRules.length} rules`);
|
|
3447
|
+
parts.push(` • ${mergedAgents.length} agents`);
|
|
3362
3448
|
parts.push(` • ${mergedMemory.length} memory files`);
|
|
3363
3449
|
parts.push(` • ${mergedCommandCount} commands`);
|
|
3364
3450
|
}
|