@baton-dx/cli 0.2.0 → 0.3.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/{create-C0x3t4GX.mjs → create-BOcW-DBk.mjs} +2 -2
- package/dist/{create-C0x3t4GX.mjs.map → create-BOcW-DBk.mjs.map} +1 -1
- package/dist/index.mjs +702 -123
- package/dist/index.mjs.map +1 -1
- package/dist/{list-CGmYHSHW.mjs → list-DmzVXCNF.mjs} +2 -2
- package/dist/{list-CGmYHSHW.mjs.map → list-DmzVXCNF.mjs.map} +1 -1
- package/dist/{src-DBbk6iAs.mjs → src-C3-Vz-R7.mjs} +179 -3
- package/dist/src-C3-Vz-R7.mjs.map +1 -0
- package/package.json +1 -1
- package/dist/src-DBbk6iAs.mjs.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -1,14 +1,166 @@
|
|
|
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 { A as
|
|
4
|
+
import { $ as SourceParseError, A as discoverProfilesInSourceRepo, B as getIdePlatformTargetDir, C as isLockedProfile, D as resolveProfileChain, E as resolveProfileSupport, F as writeLock, G as getAllAdapters, H as idePlatformRegistry, I as resolveVersion, J as loadLockfile, K as parseFrontmatter, L as cloneGitSource, M as removePlacedFiles, N as generateLock, O as detectLegacyPaths, P as readLock, Q as FileNotFoundError, R as collectProfileSupportPatterns, S as getProfileWeight, T as mergeContentParts, U as isKnownIdePlatform, V as getRegisteredIdePlatforms, W as getAdaptersForKeys, X as loadProjectManifest, Y as loadProfileManifest, Z as KEBAB_CASE_REGEX, _ as mergeMemoryWithWarnings, a as clearIdeCache, b as mergeSkills, c as getDefaultGlobalSource, d as getGlobalSources, et as getAgentConfig, f as removeGlobalSource, g as mergeMemory, h as require_lib, i as computeIntersection, j as findSourceManifest, k as placeFile, l as getGlobalAiTools, m as setGlobalIdePlatforms, n as readProjectPreferences, nt as getAllAgentKeys, o as detectInstalledIdes, p as setGlobalAiTools, q as parseSource, r as writeProjectPreferences, s as addGlobalSource, t as resolvePreferences, tt as getAgentPath, u as getGlobalIdePlatforms, v as mergeRules, w as sortProfilesByWeight, x as mergeSkillsWithWarnings, y as mergeRulesWithWarnings, z as updateGitignore } from "./src-C3-Vz-R7.mjs";
|
|
5
5
|
import { n as detectInstalledAgents, t as clearAgentCache } from "./agent-detection-DTiVeO5W.mjs";
|
|
6
6
|
import { d as esm_default } from "./esm-BagM-kVd.mjs";
|
|
7
|
-
import { access, mkdir, readFile, readdir, rm,
|
|
8
|
-
import { dirname, join, resolve } from "node:path";
|
|
7
|
+
import { access, mkdir, readFile, readdir, rm, stat, writeFile } from "node:fs/promises";
|
|
8
|
+
import { dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
9
9
|
import { fileURLToPath } from "node:url";
|
|
10
10
|
import { homedir } from "node:os";
|
|
11
11
|
|
|
12
|
+
//#region src/commands/ai-tools/configure.ts
|
|
13
|
+
const aiToolsConfigureCommand = defineCommand({
|
|
14
|
+
meta: {
|
|
15
|
+
name: "configure",
|
|
16
|
+
description: "Manually configure which AI tools Baton manages"
|
|
17
|
+
},
|
|
18
|
+
args: {
|
|
19
|
+
yes: {
|
|
20
|
+
type: "boolean",
|
|
21
|
+
alias: "y",
|
|
22
|
+
description: "Keep current selection unchanged (no-op), or with --project write useGlobal: true"
|
|
23
|
+
},
|
|
24
|
+
project: {
|
|
25
|
+
type: "boolean",
|
|
26
|
+
description: "Configure AI tools for this project instead of globally"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
async run({ args }) {
|
|
30
|
+
if (args.project) await runProjectMode$1(args.yes ?? false);
|
|
31
|
+
else await runGlobalMode$1(args.yes ?? false);
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
async function runGlobalMode$1(nonInteractive) {
|
|
35
|
+
We("Baton - Configure AI Tools");
|
|
36
|
+
const currentTools = await getGlobalAiTools();
|
|
37
|
+
if (nonInteractive) {
|
|
38
|
+
if (currentTools.length > 0) R.info(`Current AI tools: ${currentTools.join(", ")}`);
|
|
39
|
+
else R.info("No AI tools currently configured.");
|
|
40
|
+
Le("No changes made.");
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const options = getAllAdapters().map((adapter) => {
|
|
44
|
+
const isSaved = currentTools.includes(adapter.key);
|
|
45
|
+
return {
|
|
46
|
+
value: adapter.key,
|
|
47
|
+
label: isSaved ? `${adapter.name} (currently saved)` : adapter.name
|
|
48
|
+
};
|
|
49
|
+
});
|
|
50
|
+
const selected = await je({
|
|
51
|
+
message: "Select which AI tools to save:",
|
|
52
|
+
options,
|
|
53
|
+
initialValues: currentTools
|
|
54
|
+
});
|
|
55
|
+
if (Ct(selected)) {
|
|
56
|
+
Le("No changes made.");
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const selectedKeys = selected;
|
|
60
|
+
if (selectedKeys.length !== currentTools.length || selectedKeys.some((key) => !currentTools.includes(key))) {
|
|
61
|
+
await setGlobalAiTools(selectedKeys);
|
|
62
|
+
R.success(`Saved ${selectedKeys.length} tool(s) to global config.`);
|
|
63
|
+
} else R.info("No changes made.");
|
|
64
|
+
Le("Configuration complete.");
|
|
65
|
+
}
|
|
66
|
+
async function runProjectMode$1(nonInteractive) {
|
|
67
|
+
We("Baton - Configure AI Tools (Project)");
|
|
68
|
+
const projectRoot = process.cwd();
|
|
69
|
+
const manifestPath = resolve(projectRoot, "baton.yaml");
|
|
70
|
+
try {
|
|
71
|
+
await stat(manifestPath);
|
|
72
|
+
} catch {
|
|
73
|
+
Ne("No baton.yaml found in current directory. Run `baton init` first.");
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
if (nonInteractive) {
|
|
77
|
+
const existing = await readProjectPreferences(projectRoot);
|
|
78
|
+
await writeProjectPreferences(projectRoot, {
|
|
79
|
+
version: "1.0",
|
|
80
|
+
ai: {
|
|
81
|
+
useGlobal: true,
|
|
82
|
+
tools: existing?.ai.tools ?? []
|
|
83
|
+
},
|
|
84
|
+
ide: existing?.ide ?? {
|
|
85
|
+
useGlobal: true,
|
|
86
|
+
platforms: []
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
R.info("Set AI tools to use global config for this project.");
|
|
90
|
+
Le("Configuration complete.");
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
const existing = await readProjectPreferences(projectRoot);
|
|
94
|
+
const globalTools = await getGlobalAiTools();
|
|
95
|
+
if (globalTools.length > 0) R.info(`Global AI tools: ${globalTools.join(", ")}`);
|
|
96
|
+
const mode = await Je({
|
|
97
|
+
message: "How should this project resolve AI tools?",
|
|
98
|
+
options: [{
|
|
99
|
+
value: "global",
|
|
100
|
+
label: "Use global config",
|
|
101
|
+
hint: "always follows your global AI tools setting"
|
|
102
|
+
}, {
|
|
103
|
+
value: "project",
|
|
104
|
+
label: "Customize for this project",
|
|
105
|
+
hint: "choose specific tools for this project"
|
|
106
|
+
}],
|
|
107
|
+
initialValue: existing?.ai.useGlobal === false ? "project" : "global"
|
|
108
|
+
});
|
|
109
|
+
if (Ct(mode)) {
|
|
110
|
+
Le("No changes made.");
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
if (mode === "global") {
|
|
114
|
+
await writeProjectPreferences(projectRoot, {
|
|
115
|
+
version: "1.0",
|
|
116
|
+
ai: {
|
|
117
|
+
useGlobal: true,
|
|
118
|
+
tools: []
|
|
119
|
+
},
|
|
120
|
+
ide: existing?.ide ?? {
|
|
121
|
+
useGlobal: true,
|
|
122
|
+
platforms: []
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
R.success("Project configured to use global AI tools.");
|
|
126
|
+
Le("Configuration complete.");
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
const allAdapters = getAllAdapters();
|
|
130
|
+
const currentProjectTools = existing?.ai.useGlobal === false ? existing.ai.tools : globalTools;
|
|
131
|
+
const options = allAdapters.map((adapter) => {
|
|
132
|
+
const isGlobal = globalTools.includes(adapter.key);
|
|
133
|
+
return {
|
|
134
|
+
value: adapter.key,
|
|
135
|
+
label: isGlobal ? `${adapter.name} (in global config)` : adapter.name
|
|
136
|
+
};
|
|
137
|
+
});
|
|
138
|
+
const selected = await je({
|
|
139
|
+
message: "Select AI tools for this project:",
|
|
140
|
+
options,
|
|
141
|
+
initialValues: currentProjectTools
|
|
142
|
+
});
|
|
143
|
+
if (Ct(selected)) {
|
|
144
|
+
Le("No changes made.");
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
const selectedKeys = selected;
|
|
148
|
+
await writeProjectPreferences(projectRoot, {
|
|
149
|
+
version: "1.0",
|
|
150
|
+
ai: {
|
|
151
|
+
useGlobal: false,
|
|
152
|
+
tools: selectedKeys
|
|
153
|
+
},
|
|
154
|
+
ide: existing?.ide ?? {
|
|
155
|
+
useGlobal: true,
|
|
156
|
+
platforms: []
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
R.success(`Project configured with ${selectedKeys.length} AI tool(s).`);
|
|
160
|
+
Le("Configuration complete.");
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
//#endregion
|
|
12
164
|
//#region src/commands/ai-tools/list.ts
|
|
13
165
|
const aiToolsListCommand = defineCommand({
|
|
14
166
|
meta: {
|
|
@@ -135,55 +287,41 @@ const aiToolsScanCommand = defineCommand({
|
|
|
135
287
|
spinner.start("Scanning for AI tools...");
|
|
136
288
|
clearAgentCache();
|
|
137
289
|
const detectedAgents = await detectInstalledAgents();
|
|
138
|
-
const
|
|
290
|
+
const allAdapters = getAllAdapters();
|
|
139
291
|
const currentTools = await getGlobalAiTools();
|
|
140
292
|
spinner.stop("Scan complete.");
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
else notInstalled.push(entry);
|
|
150
|
-
}
|
|
151
|
-
if (installed.length > 0) {
|
|
152
|
-
R.success(`Found ${installed.length} AI tool${installed.length !== 1 ? "s" : ""}:`);
|
|
153
|
-
for (const agent of installed) {
|
|
154
|
-
const badge = currentTools.includes(agent.key) ? " (saved)" : " (new)";
|
|
155
|
-
console.log(` \x1b[32m✓\x1b[0m ${agent.name}${badge}`);
|
|
156
|
-
}
|
|
157
|
-
} else {
|
|
158
|
-
R.warn("No AI tools detected on your system.");
|
|
293
|
+
if (detectedAgents.length > 0) R.success(`Found ${detectedAgents.length} AI tool${detectedAgents.length !== 1 ? "s" : ""} on your system.`);
|
|
294
|
+
else R.warn("No AI tools detected on your system.");
|
|
295
|
+
if (args.yes) {
|
|
296
|
+
const detectedKeys = detectedAgents;
|
|
297
|
+
if (detectedKeys.length !== currentTools.length || detectedKeys.some((key) => !currentTools.includes(key))) {
|
|
298
|
+
await setGlobalAiTools(detectedKeys);
|
|
299
|
+
R.success(`Saved ${detectedKeys.length} detected tool(s) to global config.`);
|
|
300
|
+
} else R.info("Global config is already up to date.");
|
|
159
301
|
Le("Scan finished.");
|
|
160
302
|
return;
|
|
161
303
|
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
shouldSave = confirm;
|
|
178
|
-
}
|
|
179
|
-
if (shouldSave) {
|
|
180
|
-
await setGlobalAiTools(detectedKeys);
|
|
181
|
-
R.success("Tools saved to global config.");
|
|
182
|
-
}
|
|
183
|
-
} else {
|
|
184
|
-
console.log("");
|
|
185
|
-
R.info("Global config is already up to date.");
|
|
304
|
+
const options = allAdapters.map((adapter) => {
|
|
305
|
+
const isDetected = detectedAgents.includes(adapter.key);
|
|
306
|
+
return {
|
|
307
|
+
value: adapter.key,
|
|
308
|
+
label: isDetected ? `${adapter.name} (detected)` : adapter.name
|
|
309
|
+
};
|
|
310
|
+
});
|
|
311
|
+
const selected = await je({
|
|
312
|
+
message: "Select which AI tools to save:",
|
|
313
|
+
options,
|
|
314
|
+
initialValues: detectedAgents
|
|
315
|
+
});
|
|
316
|
+
if (Ct(selected)) {
|
|
317
|
+
Le("Scan finished (not saved).");
|
|
318
|
+
return;
|
|
186
319
|
}
|
|
320
|
+
const selectedKeys = selected;
|
|
321
|
+
if (selectedKeys.length !== currentTools.length || selectedKeys.some((key) => !currentTools.includes(key))) {
|
|
322
|
+
await setGlobalAiTools(selectedKeys);
|
|
323
|
+
R.success(`Saved ${selectedKeys.length} tool(s) to global config.`);
|
|
324
|
+
} else R.info("Global config is already up to date.");
|
|
187
325
|
Le("Scan finished.");
|
|
188
326
|
}
|
|
189
327
|
});
|
|
@@ -196,6 +334,7 @@ const aiToolsCommand = defineCommand({
|
|
|
196
334
|
description: "Manage AI tool detection and configuration"
|
|
197
335
|
},
|
|
198
336
|
subCommands: {
|
|
337
|
+
configure: aiToolsConfigureCommand,
|
|
199
338
|
list: aiToolsListCommand,
|
|
200
339
|
scan: aiToolsScanCommand
|
|
201
340
|
}
|
|
@@ -319,7 +458,29 @@ async function showDashboard() {
|
|
|
319
458
|
}
|
|
320
459
|
console.log("");
|
|
321
460
|
R.step("Developer Tools");
|
|
322
|
-
if (
|
|
461
|
+
if (projectManifest) {
|
|
462
|
+
const prefs = await resolvePreferences(process.cwd());
|
|
463
|
+
const resolvedAiTools = prefs.ai.tools;
|
|
464
|
+
const resolvedIdePlatforms = prefs.ide.platforms;
|
|
465
|
+
if (resolvedAiTools.length === 0 && resolvedIdePlatforms.length === 0) R.info(" No tools configured. Run: baton ai-tools scan && baton ides scan");
|
|
466
|
+
else {
|
|
467
|
+
if (resolvedAiTools.length > 0) {
|
|
468
|
+
const toolNames = resolvedAiTools.map((key) => {
|
|
469
|
+
try {
|
|
470
|
+
return getAgentConfig(key).name;
|
|
471
|
+
} catch {
|
|
472
|
+
return key;
|
|
473
|
+
}
|
|
474
|
+
});
|
|
475
|
+
const aiSourceLabel = prefs.ai.source === "project" ? "project preferences" : "global config";
|
|
476
|
+
R.info(` AI Tools: ${toolNames.join(", ")} (from ${aiSourceLabel})`);
|
|
477
|
+
}
|
|
478
|
+
if (resolvedIdePlatforms.length > 0) {
|
|
479
|
+
const ideSourceLabel = prefs.ide.source === "project" ? "project preferences" : "global config";
|
|
480
|
+
R.info(` IDE Platforms: ${resolvedIdePlatforms.join(", ")} (from ${ideSourceLabel})`);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
} else if (aiTools.length === 0 && idePlatforms.length === 0) R.info(" No tools configured. Run: baton ai-tools scan && baton ides scan");
|
|
323
484
|
else {
|
|
324
485
|
if (aiTools.length > 0) {
|
|
325
486
|
const toolNames = aiTools.map((key) => {
|
|
@@ -329,9 +490,9 @@ async function showDashboard() {
|
|
|
329
490
|
return key;
|
|
330
491
|
}
|
|
331
492
|
});
|
|
332
|
-
R.info(` AI Tools: ${toolNames.join(", ")}`);
|
|
493
|
+
R.info(` AI Tools: ${toolNames.join(", ")} (from global config)`);
|
|
333
494
|
}
|
|
334
|
-
if (idePlatforms.length > 0) R.info(` IDE Platforms: ${idePlatforms.join(", ")}`);
|
|
495
|
+
if (idePlatforms.length > 0) R.info(` IDE Platforms: ${idePlatforms.join(", ")} (from global config)`);
|
|
335
496
|
}
|
|
336
497
|
console.log("");
|
|
337
498
|
R.step("Current Project");
|
|
@@ -830,7 +991,7 @@ async function loadFilesFromDirectory(dirPath) {
|
|
|
830
991
|
/**
|
|
831
992
|
* Format an IDE platform key into a display name.
|
|
832
993
|
*/
|
|
833
|
-
function formatIdeName(ideKey) {
|
|
994
|
+
function formatIdeName$2(ideKey) {
|
|
834
995
|
return {
|
|
835
996
|
vscode: "VS Code",
|
|
836
997
|
jetbrains: "JetBrains",
|
|
@@ -841,6 +1002,156 @@ function formatIdeName(ideKey) {
|
|
|
841
1002
|
}[ideKey] ?? ideKey;
|
|
842
1003
|
}
|
|
843
1004
|
|
|
1005
|
+
//#endregion
|
|
1006
|
+
//#region src/commands/ides/configure.ts
|
|
1007
|
+
const idesConfigureCommand = defineCommand({
|
|
1008
|
+
meta: {
|
|
1009
|
+
name: "configure",
|
|
1010
|
+
description: "Manually configure which IDE platforms Baton manages"
|
|
1011
|
+
},
|
|
1012
|
+
args: {
|
|
1013
|
+
yes: {
|
|
1014
|
+
type: "boolean",
|
|
1015
|
+
alias: "y",
|
|
1016
|
+
description: "Keep current selection unchanged (no-op), or with --project write useGlobal: true"
|
|
1017
|
+
},
|
|
1018
|
+
project: {
|
|
1019
|
+
type: "boolean",
|
|
1020
|
+
description: "Configure IDE platforms for this project instead of globally"
|
|
1021
|
+
}
|
|
1022
|
+
},
|
|
1023
|
+
async run({ args }) {
|
|
1024
|
+
if (args.project) await runProjectMode(args.yes ?? false);
|
|
1025
|
+
else await runGlobalMode(args.yes ?? false);
|
|
1026
|
+
}
|
|
1027
|
+
});
|
|
1028
|
+
async function runGlobalMode(nonInteractive) {
|
|
1029
|
+
We("Baton - Configure IDE Platforms");
|
|
1030
|
+
const currentPlatforms = await getGlobalIdePlatforms();
|
|
1031
|
+
if (nonInteractive) {
|
|
1032
|
+
if (currentPlatforms.length > 0) R.info(`Current IDE platforms: ${currentPlatforms.join(", ")}`);
|
|
1033
|
+
else R.info("No IDE platforms currently configured.");
|
|
1034
|
+
Le("No changes made.");
|
|
1035
|
+
return;
|
|
1036
|
+
}
|
|
1037
|
+
const options = getRegisteredIdePlatforms().map((ideKey) => {
|
|
1038
|
+
return {
|
|
1039
|
+
value: ideKey,
|
|
1040
|
+
label: currentPlatforms.includes(ideKey) ? `${formatIdeName$2(ideKey)} (currently saved)` : formatIdeName$2(ideKey)
|
|
1041
|
+
};
|
|
1042
|
+
});
|
|
1043
|
+
const selected = await je({
|
|
1044
|
+
message: "Select which IDE platforms to save:",
|
|
1045
|
+
options,
|
|
1046
|
+
initialValues: currentPlatforms
|
|
1047
|
+
});
|
|
1048
|
+
if (Ct(selected)) {
|
|
1049
|
+
Le("No changes made.");
|
|
1050
|
+
return;
|
|
1051
|
+
}
|
|
1052
|
+
const selectedKeys = selected;
|
|
1053
|
+
if (selectedKeys.length !== currentPlatforms.length || selectedKeys.some((key) => !currentPlatforms.includes(key))) {
|
|
1054
|
+
await setGlobalIdePlatforms(selectedKeys);
|
|
1055
|
+
R.success(`Saved ${selectedKeys.length} platform(s) to global config.`);
|
|
1056
|
+
} else R.info("No changes made.");
|
|
1057
|
+
Le("Configuration complete.");
|
|
1058
|
+
}
|
|
1059
|
+
async function runProjectMode(nonInteractive) {
|
|
1060
|
+
We("Baton - Configure IDE Platforms (Project)");
|
|
1061
|
+
const projectRoot = process.cwd();
|
|
1062
|
+
const manifestPath = resolve(projectRoot, "baton.yaml");
|
|
1063
|
+
try {
|
|
1064
|
+
await stat(manifestPath);
|
|
1065
|
+
} catch {
|
|
1066
|
+
Ne("No baton.yaml found in current directory. Run `baton init` first.");
|
|
1067
|
+
process.exit(1);
|
|
1068
|
+
}
|
|
1069
|
+
if (nonInteractive) {
|
|
1070
|
+
const existing = await readProjectPreferences(projectRoot);
|
|
1071
|
+
await writeProjectPreferences(projectRoot, {
|
|
1072
|
+
version: "1.0",
|
|
1073
|
+
ai: existing?.ai ?? {
|
|
1074
|
+
useGlobal: true,
|
|
1075
|
+
tools: []
|
|
1076
|
+
},
|
|
1077
|
+
ide: {
|
|
1078
|
+
useGlobal: true,
|
|
1079
|
+
platforms: existing?.ide.platforms ?? []
|
|
1080
|
+
}
|
|
1081
|
+
});
|
|
1082
|
+
R.info("Set IDE platforms to use global config for this project.");
|
|
1083
|
+
Le("Configuration complete.");
|
|
1084
|
+
return;
|
|
1085
|
+
}
|
|
1086
|
+
const existing = await readProjectPreferences(projectRoot);
|
|
1087
|
+
const globalPlatforms = await getGlobalIdePlatforms();
|
|
1088
|
+
if (globalPlatforms.length > 0) R.info(`Global IDE platforms: ${globalPlatforms.join(", ")}`);
|
|
1089
|
+
const mode = await Je({
|
|
1090
|
+
message: "How should this project resolve IDE platforms?",
|
|
1091
|
+
options: [{
|
|
1092
|
+
value: "global",
|
|
1093
|
+
label: "Use global config",
|
|
1094
|
+
hint: "always follows your global IDE platforms setting"
|
|
1095
|
+
}, {
|
|
1096
|
+
value: "project",
|
|
1097
|
+
label: "Customize for this project",
|
|
1098
|
+
hint: "choose specific IDEs for this project"
|
|
1099
|
+
}],
|
|
1100
|
+
initialValue: existing?.ide.useGlobal === false ? "project" : "global"
|
|
1101
|
+
});
|
|
1102
|
+
if (Ct(mode)) {
|
|
1103
|
+
Le("No changes made.");
|
|
1104
|
+
return;
|
|
1105
|
+
}
|
|
1106
|
+
if (mode === "global") {
|
|
1107
|
+
await writeProjectPreferences(projectRoot, {
|
|
1108
|
+
version: "1.0",
|
|
1109
|
+
ai: existing?.ai ?? {
|
|
1110
|
+
useGlobal: true,
|
|
1111
|
+
tools: []
|
|
1112
|
+
},
|
|
1113
|
+
ide: {
|
|
1114
|
+
useGlobal: true,
|
|
1115
|
+
platforms: []
|
|
1116
|
+
}
|
|
1117
|
+
});
|
|
1118
|
+
R.success("Project configured to use global IDE platforms.");
|
|
1119
|
+
Le("Configuration complete.");
|
|
1120
|
+
return;
|
|
1121
|
+
}
|
|
1122
|
+
const allIdeKeys = getRegisteredIdePlatforms();
|
|
1123
|
+
const currentProjectPlatforms = existing?.ide.useGlobal === false ? existing.ide.platforms : globalPlatforms;
|
|
1124
|
+
const options = allIdeKeys.map((ideKey) => {
|
|
1125
|
+
return {
|
|
1126
|
+
value: ideKey,
|
|
1127
|
+
label: globalPlatforms.includes(ideKey) ? `${formatIdeName$2(ideKey)} (in global config)` : formatIdeName$2(ideKey)
|
|
1128
|
+
};
|
|
1129
|
+
});
|
|
1130
|
+
const selected = await je({
|
|
1131
|
+
message: "Select IDE platforms for this project:",
|
|
1132
|
+
options,
|
|
1133
|
+
initialValues: currentProjectPlatforms
|
|
1134
|
+
});
|
|
1135
|
+
if (Ct(selected)) {
|
|
1136
|
+
Le("No changes made.");
|
|
1137
|
+
return;
|
|
1138
|
+
}
|
|
1139
|
+
const selectedKeys = selected;
|
|
1140
|
+
await writeProjectPreferences(projectRoot, {
|
|
1141
|
+
version: "1.0",
|
|
1142
|
+
ai: existing?.ai ?? {
|
|
1143
|
+
useGlobal: true,
|
|
1144
|
+
tools: []
|
|
1145
|
+
},
|
|
1146
|
+
ide: {
|
|
1147
|
+
useGlobal: false,
|
|
1148
|
+
platforms: selectedKeys
|
|
1149
|
+
}
|
|
1150
|
+
});
|
|
1151
|
+
R.success(`Project configured with ${selectedKeys.length} IDE platform(s).`);
|
|
1152
|
+
Le("Configuration complete.");
|
|
1153
|
+
}
|
|
1154
|
+
|
|
844
1155
|
//#endregion
|
|
845
1156
|
//#region src/commands/ides/list.ts
|
|
846
1157
|
const idesListCommand = defineCommand({
|
|
@@ -869,7 +1180,7 @@ const idesListCommand = defineCommand({
|
|
|
869
1180
|
const entry = idePlatformRegistry[ideKey];
|
|
870
1181
|
return {
|
|
871
1182
|
key: ideKey,
|
|
872
|
-
name: formatIdeName(ideKey),
|
|
1183
|
+
name: formatIdeName$2(ideKey),
|
|
873
1184
|
saved: isSaved,
|
|
874
1185
|
targetDir: entry?.targetDir ?? "unknown"
|
|
875
1186
|
};
|
|
@@ -885,7 +1196,7 @@ const idesListCommand = defineCommand({
|
|
|
885
1196
|
R.info(`All ${allIdeKeys.length} supported platforms:`);
|
|
886
1197
|
for (const key of allIdeKeys) {
|
|
887
1198
|
const entry = idePlatformRegistry[key];
|
|
888
|
-
console.log(` \x1b[90m- ${formatIdeName(key)} (${entry?.targetDir ?? key})\x1b[0m`);
|
|
1199
|
+
console.log(` \x1b[90m- ${formatIdeName$2(key)} (${entry?.targetDir ?? key})\x1b[0m`);
|
|
889
1200
|
}
|
|
890
1201
|
Le("Run 'baton ides scan' to get started.");
|
|
891
1202
|
return;
|
|
@@ -922,52 +1233,36 @@ const idesScanCommand = defineCommand({
|
|
|
922
1233
|
const allIdeKeys = getRegisteredIdePlatforms();
|
|
923
1234
|
const currentPlatforms = await getGlobalIdePlatforms();
|
|
924
1235
|
spinner.stop("Scan complete.");
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
};
|
|
932
|
-
if (detectedIdes.includes(ideKey)) installed.push(entry);
|
|
933
|
-
else notInstalled.push(entry);
|
|
934
|
-
}
|
|
935
|
-
if (installed.length > 0) {
|
|
936
|
-
R.success(`Found ${installed.length} IDE platform${installed.length !== 1 ? "s" : ""}:`);
|
|
937
|
-
for (const ide of installed) {
|
|
938
|
-
const badge = currentPlatforms.includes(ide.key) ? " (saved)" : " (new)";
|
|
939
|
-
console.log(` \x1b[32m✓\x1b[0m ${ide.name}${badge}`);
|
|
940
|
-
}
|
|
941
|
-
} else {
|
|
942
|
-
R.warn("No IDE platforms detected on your system.");
|
|
1236
|
+
if (detectedIdes.length > 0) R.success(`Found ${detectedIdes.length} IDE platform${detectedIdes.length !== 1 ? "s" : ""} on your system.`);
|
|
1237
|
+
else R.warn("No IDE platforms detected on your system.");
|
|
1238
|
+
if (args.yes) {
|
|
1239
|
+
if (detectedIdes.length !== currentPlatforms.length || detectedIdes.some((key) => !currentPlatforms.includes(key))) {
|
|
1240
|
+
await setGlobalIdePlatforms(detectedIdes);
|
|
1241
|
+
R.success(`Saved ${detectedIdes.length} detected platform(s) to global config.`);
|
|
1242
|
+
} else R.info("Global config is already up to date.");
|
|
943
1243
|
Le("Scan finished.");
|
|
944
1244
|
return;
|
|
945
1245
|
}
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
}
|
|
961
|
-
shouldSave = confirm;
|
|
962
|
-
}
|
|
963
|
-
if (shouldSave) {
|
|
964
|
-
await setGlobalIdePlatforms(detectedKeys);
|
|
965
|
-
R.success("Platforms saved to global config.");
|
|
966
|
-
}
|
|
967
|
-
} else {
|
|
968
|
-
console.log("");
|
|
969
|
-
R.info("Global config is already up to date.");
|
|
1246
|
+
const options = allIdeKeys.map((ideKey) => {
|
|
1247
|
+
return {
|
|
1248
|
+
value: ideKey,
|
|
1249
|
+
label: detectedIdes.includes(ideKey) ? `${formatIdeName$2(ideKey)} (detected)` : formatIdeName$2(ideKey)
|
|
1250
|
+
};
|
|
1251
|
+
});
|
|
1252
|
+
const selected = await je({
|
|
1253
|
+
message: "Select which IDE platforms to save:",
|
|
1254
|
+
options,
|
|
1255
|
+
initialValues: detectedIdes
|
|
1256
|
+
});
|
|
1257
|
+
if (Ct(selected)) {
|
|
1258
|
+
Le("Scan finished (not saved).");
|
|
1259
|
+
return;
|
|
970
1260
|
}
|
|
1261
|
+
const selectedKeys = selected;
|
|
1262
|
+
if (selectedKeys.length !== currentPlatforms.length || selectedKeys.some((key) => !currentPlatforms.includes(key))) {
|
|
1263
|
+
await setGlobalIdePlatforms(selectedKeys);
|
|
1264
|
+
R.success(`Saved ${selectedKeys.length} platform(s) to global config.`);
|
|
1265
|
+
} else R.info("Global config is already up to date.");
|
|
971
1266
|
Le("Scan finished.");
|
|
972
1267
|
}
|
|
973
1268
|
});
|
|
@@ -980,11 +1275,126 @@ const idesCommand = defineCommand({
|
|
|
980
1275
|
description: "Manage IDE platform detection and configuration"
|
|
981
1276
|
},
|
|
982
1277
|
subCommands: {
|
|
1278
|
+
configure: idesConfigureCommand,
|
|
983
1279
|
list: idesListCommand,
|
|
984
1280
|
scan: idesScanCommand
|
|
985
1281
|
}
|
|
986
1282
|
});
|
|
987
1283
|
|
|
1284
|
+
//#endregion
|
|
1285
|
+
//#region src/utils/first-run-preferences.ts
|
|
1286
|
+
/**
|
|
1287
|
+
* Format an IDE platform key into a display name.
|
|
1288
|
+
* Duplicated here to avoid circular dependency with ides/utils.
|
|
1289
|
+
*/
|
|
1290
|
+
function formatIdeName$1(ideKey) {
|
|
1291
|
+
return {
|
|
1292
|
+
vscode: "VS Code",
|
|
1293
|
+
jetbrains: "JetBrains",
|
|
1294
|
+
cursor: "Cursor",
|
|
1295
|
+
windsurf: "Windsurf",
|
|
1296
|
+
antigravity: "Antigravity",
|
|
1297
|
+
zed: "Zed"
|
|
1298
|
+
}[ideKey] ?? ideKey;
|
|
1299
|
+
}
|
|
1300
|
+
/**
|
|
1301
|
+
* Shows the first-run preferences prompt if .baton/preferences.yaml doesn't exist.
|
|
1302
|
+
*
|
|
1303
|
+
* Asks the user whether to use global config or customize AI tools and IDEs
|
|
1304
|
+
* for this project, then writes the preferences file.
|
|
1305
|
+
*
|
|
1306
|
+
* @param projectRoot - Absolute path to the project root
|
|
1307
|
+
* @param nonInteractive - If true, writes useGlobal: true silently
|
|
1308
|
+
* @returns true if preferences were written, false if already existed
|
|
1309
|
+
*/
|
|
1310
|
+
async function promptFirstRunPreferences(projectRoot, nonInteractive) {
|
|
1311
|
+
if (await readProjectPreferences(projectRoot)) return false;
|
|
1312
|
+
if (nonInteractive) {
|
|
1313
|
+
await writeProjectPreferences(projectRoot, {
|
|
1314
|
+
version: "1.0",
|
|
1315
|
+
ai: {
|
|
1316
|
+
useGlobal: true,
|
|
1317
|
+
tools: []
|
|
1318
|
+
},
|
|
1319
|
+
ide: {
|
|
1320
|
+
useGlobal: true,
|
|
1321
|
+
platforms: []
|
|
1322
|
+
}
|
|
1323
|
+
});
|
|
1324
|
+
return true;
|
|
1325
|
+
}
|
|
1326
|
+
const aiMode = await Je({
|
|
1327
|
+
message: "How do you want to configure AI tools for this project?",
|
|
1328
|
+
options: [{
|
|
1329
|
+
value: "global",
|
|
1330
|
+
label: "Use global config",
|
|
1331
|
+
hint: "recommended"
|
|
1332
|
+
}, {
|
|
1333
|
+
value: "customize",
|
|
1334
|
+
label: "Customize for this project"
|
|
1335
|
+
}]
|
|
1336
|
+
});
|
|
1337
|
+
if (Ct(aiMode)) return false;
|
|
1338
|
+
let aiUseGlobal = true;
|
|
1339
|
+
let aiTools = [];
|
|
1340
|
+
if (aiMode === "customize") {
|
|
1341
|
+
const globalTools = await getGlobalAiTools();
|
|
1342
|
+
const allAdapters = getAllAdapters();
|
|
1343
|
+
const selected = await je({
|
|
1344
|
+
message: "Select AI tools for this project:",
|
|
1345
|
+
options: allAdapters.map((adapter) => ({
|
|
1346
|
+
value: adapter.key,
|
|
1347
|
+
label: globalTools.includes(adapter.key) ? `${adapter.name} (in global config)` : adapter.name
|
|
1348
|
+
})),
|
|
1349
|
+
initialValues: globalTools
|
|
1350
|
+
});
|
|
1351
|
+
if (Ct(selected)) return false;
|
|
1352
|
+
aiUseGlobal = false;
|
|
1353
|
+
aiTools = selected;
|
|
1354
|
+
}
|
|
1355
|
+
const ideMode = await Je({
|
|
1356
|
+
message: "How do you want to configure IDE platforms for this project?",
|
|
1357
|
+
options: [{
|
|
1358
|
+
value: "global",
|
|
1359
|
+
label: "Use global config",
|
|
1360
|
+
hint: "recommended"
|
|
1361
|
+
}, {
|
|
1362
|
+
value: "customize",
|
|
1363
|
+
label: "Customize for this project"
|
|
1364
|
+
}]
|
|
1365
|
+
});
|
|
1366
|
+
if (Ct(ideMode)) return false;
|
|
1367
|
+
let ideUseGlobal = true;
|
|
1368
|
+
let idePlatforms = [];
|
|
1369
|
+
if (ideMode === "customize") {
|
|
1370
|
+
const globalPlatforms = await getGlobalIdePlatforms();
|
|
1371
|
+
const allIdeKeys = getRegisteredIdePlatforms();
|
|
1372
|
+
const selected = await je({
|
|
1373
|
+
message: "Select IDE platforms for this project:",
|
|
1374
|
+
options: allIdeKeys.map((ideKey) => ({
|
|
1375
|
+
value: ideKey,
|
|
1376
|
+
label: globalPlatforms.includes(ideKey) ? `${formatIdeName$1(ideKey)} (in global config)` : formatIdeName$1(ideKey)
|
|
1377
|
+
})),
|
|
1378
|
+
initialValues: globalPlatforms
|
|
1379
|
+
});
|
|
1380
|
+
if (Ct(selected)) return false;
|
|
1381
|
+
ideUseGlobal = false;
|
|
1382
|
+
idePlatforms = selected;
|
|
1383
|
+
}
|
|
1384
|
+
await writeProjectPreferences(projectRoot, {
|
|
1385
|
+
version: "1.0",
|
|
1386
|
+
ai: {
|
|
1387
|
+
useGlobal: aiUseGlobal,
|
|
1388
|
+
tools: aiTools
|
|
1389
|
+
},
|
|
1390
|
+
ide: {
|
|
1391
|
+
useGlobal: ideUseGlobal,
|
|
1392
|
+
platforms: idePlatforms
|
|
1393
|
+
}
|
|
1394
|
+
});
|
|
1395
|
+
return true;
|
|
1396
|
+
}
|
|
1397
|
+
|
|
988
1398
|
//#endregion
|
|
989
1399
|
//#region src/utils/profile-selection.ts
|
|
990
1400
|
/**
|
|
@@ -1261,6 +1671,7 @@ const initCommand = defineCommand({
|
|
|
1261
1671
|
} catch (_error) {
|
|
1262
1672
|
spinner.stop("✅ .baton directory already exists");
|
|
1263
1673
|
}
|
|
1674
|
+
await promptFirstRunPreferences(cwd, !isInteractive);
|
|
1264
1675
|
if (profileSources.length > 0) {
|
|
1265
1676
|
const shouldSync = isInteractive ? await Re({
|
|
1266
1677
|
message: "Sync profiles now?",
|
|
@@ -1554,13 +1965,173 @@ async function handleRemoveBaton(cwd) {
|
|
|
1554
1965
|
R.warn("Cancelled.");
|
|
1555
1966
|
return false;
|
|
1556
1967
|
}
|
|
1968
|
+
const lockPath = join(cwd, "baton.lock");
|
|
1969
|
+
await cleanupPlacedFilesFromLock(lockPath, cwd);
|
|
1557
1970
|
await rm(join(cwd, "baton.yaml"), { force: true });
|
|
1558
|
-
await rm(
|
|
1971
|
+
await rm(lockPath, { force: true });
|
|
1559
1972
|
R.success("Baton has been removed from this project.");
|
|
1560
|
-
R.info("Note: Synced files (rules, skills, memory) were not removed.");
|
|
1561
|
-
R.info("Run 'baton sync' before removing to clean up, or delete them manually.");
|
|
1562
1973
|
return true;
|
|
1563
1974
|
}
|
|
1975
|
+
async function cleanupPlacedFilesFromLock(lockPath, projectRoot) {
|
|
1976
|
+
let placedPaths;
|
|
1977
|
+
try {
|
|
1978
|
+
const lockfile = await readLock(lockPath);
|
|
1979
|
+
placedPaths = Object.values(lockfile.packages).flatMap((pkg) => Object.keys(pkg.integrity));
|
|
1980
|
+
} catch (error) {
|
|
1981
|
+
if (error instanceof FileNotFoundError) return;
|
|
1982
|
+
return;
|
|
1983
|
+
}
|
|
1984
|
+
if (placedPaths.length === 0) return;
|
|
1985
|
+
R.info(`Found ${placedPaths.length} placed file(s):`);
|
|
1986
|
+
for (const filePath of placedPaths) R.info(` ${filePath}`);
|
|
1987
|
+
const shouldClean = await Re({
|
|
1988
|
+
message: `Also remove ${placedPaths.length} placed file(s)?`,
|
|
1989
|
+
initialValue: false
|
|
1990
|
+
});
|
|
1991
|
+
if (Ct(shouldClean) || !shouldClean) return;
|
|
1992
|
+
const removedCount = await removePlacedFiles(placedPaths, projectRoot);
|
|
1993
|
+
R.success(`Removed ${removedCount} placed file(s).`);
|
|
1994
|
+
}
|
|
1995
|
+
function formatIdeName(ideKey) {
|
|
1996
|
+
return {
|
|
1997
|
+
vscode: "VS Code",
|
|
1998
|
+
jetbrains: "JetBrains",
|
|
1999
|
+
cursor: "Cursor",
|
|
2000
|
+
windsurf: "Windsurf",
|
|
2001
|
+
antigravity: "Antigravity",
|
|
2002
|
+
zed: "Zed"
|
|
2003
|
+
}[ideKey] ?? ideKey;
|
|
2004
|
+
}
|
|
2005
|
+
async function handleConfigureAiTools(cwd) {
|
|
2006
|
+
const existing = await readProjectPreferences(cwd);
|
|
2007
|
+
const globalTools = await getGlobalAiTools();
|
|
2008
|
+
if (globalTools.length > 0) R.info(`Global AI tools: ${globalTools.join(", ")}`);
|
|
2009
|
+
const mode = await Je({
|
|
2010
|
+
message: "How should this project resolve AI tools?",
|
|
2011
|
+
options: [{
|
|
2012
|
+
value: "global",
|
|
2013
|
+
label: "Use global config",
|
|
2014
|
+
hint: "always follows your global AI tools setting"
|
|
2015
|
+
}, {
|
|
2016
|
+
value: "project",
|
|
2017
|
+
label: "Customize for this project",
|
|
2018
|
+
hint: "choose specific tools for this project"
|
|
2019
|
+
}],
|
|
2020
|
+
initialValue: existing?.ai.useGlobal === false ? "project" : "global"
|
|
2021
|
+
});
|
|
2022
|
+
if (Ct(mode)) {
|
|
2023
|
+
R.warn("Cancelled.");
|
|
2024
|
+
return;
|
|
2025
|
+
}
|
|
2026
|
+
if (mode === "global") {
|
|
2027
|
+
await writeProjectPreferences(cwd, {
|
|
2028
|
+
version: "1.0",
|
|
2029
|
+
ai: {
|
|
2030
|
+
useGlobal: true,
|
|
2031
|
+
tools: []
|
|
2032
|
+
},
|
|
2033
|
+
ide: existing?.ide ?? {
|
|
2034
|
+
useGlobal: true,
|
|
2035
|
+
platforms: []
|
|
2036
|
+
}
|
|
2037
|
+
});
|
|
2038
|
+
R.success("Project configured to use global AI tools.");
|
|
2039
|
+
return;
|
|
2040
|
+
}
|
|
2041
|
+
const allAdapters = getAllAdapters();
|
|
2042
|
+
const currentProjectTools = existing?.ai.useGlobal === false ? existing.ai.tools : globalTools;
|
|
2043
|
+
const options = allAdapters.map((adapter) => ({
|
|
2044
|
+
value: adapter.key,
|
|
2045
|
+
label: globalTools.includes(adapter.key) ? `${adapter.name} (in global config)` : adapter.name
|
|
2046
|
+
}));
|
|
2047
|
+
const selected = await je({
|
|
2048
|
+
message: "Select AI tools for this project:",
|
|
2049
|
+
options,
|
|
2050
|
+
initialValues: currentProjectTools
|
|
2051
|
+
});
|
|
2052
|
+
if (Ct(selected)) {
|
|
2053
|
+
R.warn("Cancelled.");
|
|
2054
|
+
return;
|
|
2055
|
+
}
|
|
2056
|
+
const selectedKeys = selected;
|
|
2057
|
+
await writeProjectPreferences(cwd, {
|
|
2058
|
+
version: "1.0",
|
|
2059
|
+
ai: {
|
|
2060
|
+
useGlobal: false,
|
|
2061
|
+
tools: selectedKeys
|
|
2062
|
+
},
|
|
2063
|
+
ide: existing?.ide ?? {
|
|
2064
|
+
useGlobal: true,
|
|
2065
|
+
platforms: []
|
|
2066
|
+
}
|
|
2067
|
+
});
|
|
2068
|
+
R.success(`Project configured with ${selectedKeys.length} AI tool(s).`);
|
|
2069
|
+
}
|
|
2070
|
+
async function handleConfigureIdes(cwd) {
|
|
2071
|
+
const existing = await readProjectPreferences(cwd);
|
|
2072
|
+
const globalPlatforms = await getGlobalIdePlatforms();
|
|
2073
|
+
if (globalPlatforms.length > 0) R.info(`Global IDE platforms: ${globalPlatforms.join(", ")}`);
|
|
2074
|
+
const mode = await Je({
|
|
2075
|
+
message: "How should this project resolve IDE platforms?",
|
|
2076
|
+
options: [{
|
|
2077
|
+
value: "global",
|
|
2078
|
+
label: "Use global config",
|
|
2079
|
+
hint: "always follows your global IDE platforms setting"
|
|
2080
|
+
}, {
|
|
2081
|
+
value: "project",
|
|
2082
|
+
label: "Customize for this project",
|
|
2083
|
+
hint: "choose specific IDEs for this project"
|
|
2084
|
+
}],
|
|
2085
|
+
initialValue: existing?.ide.useGlobal === false ? "project" : "global"
|
|
2086
|
+
});
|
|
2087
|
+
if (Ct(mode)) {
|
|
2088
|
+
R.warn("Cancelled.");
|
|
2089
|
+
return;
|
|
2090
|
+
}
|
|
2091
|
+
if (mode === "global") {
|
|
2092
|
+
await writeProjectPreferences(cwd, {
|
|
2093
|
+
version: "1.0",
|
|
2094
|
+
ai: existing?.ai ?? {
|
|
2095
|
+
useGlobal: true,
|
|
2096
|
+
tools: []
|
|
2097
|
+
},
|
|
2098
|
+
ide: {
|
|
2099
|
+
useGlobal: true,
|
|
2100
|
+
platforms: []
|
|
2101
|
+
}
|
|
2102
|
+
});
|
|
2103
|
+
R.success("Project configured to use global IDE platforms.");
|
|
2104
|
+
return;
|
|
2105
|
+
}
|
|
2106
|
+
const allIdeKeys = getRegisteredIdePlatforms();
|
|
2107
|
+
const currentProjectPlatforms = existing?.ide.useGlobal === false ? existing.ide.platforms : globalPlatforms;
|
|
2108
|
+
const options = allIdeKeys.map((ideKey) => ({
|
|
2109
|
+
value: ideKey,
|
|
2110
|
+
label: globalPlatforms.includes(ideKey) ? `${formatIdeName(ideKey)} (in global config)` : formatIdeName(ideKey)
|
|
2111
|
+
}));
|
|
2112
|
+
const selected = await je({
|
|
2113
|
+
message: "Select IDE platforms for this project:",
|
|
2114
|
+
options,
|
|
2115
|
+
initialValues: currentProjectPlatforms
|
|
2116
|
+
});
|
|
2117
|
+
if (Ct(selected)) {
|
|
2118
|
+
R.warn("Cancelled.");
|
|
2119
|
+
return;
|
|
2120
|
+
}
|
|
2121
|
+
const selectedKeys = selected;
|
|
2122
|
+
await writeProjectPreferences(cwd, {
|
|
2123
|
+
version: "1.0",
|
|
2124
|
+
ai: existing?.ai ?? {
|
|
2125
|
+
useGlobal: true,
|
|
2126
|
+
tools: []
|
|
2127
|
+
},
|
|
2128
|
+
ide: {
|
|
2129
|
+
useGlobal: false,
|
|
2130
|
+
platforms: selectedKeys
|
|
2131
|
+
}
|
|
2132
|
+
});
|
|
2133
|
+
R.success(`Project configured with ${selectedKeys.length} IDE platform(s).`);
|
|
2134
|
+
}
|
|
1564
2135
|
const manageCommand = defineCommand({
|
|
1565
2136
|
meta: {
|
|
1566
2137
|
name: "manage",
|
|
@@ -1593,6 +2164,16 @@ const manageCommand = defineCommand({
|
|
|
1593
2164
|
label: "Remove profile",
|
|
1594
2165
|
hint: "Remove an installed profile"
|
|
1595
2166
|
},
|
|
2167
|
+
{
|
|
2168
|
+
value: "configure-ai",
|
|
2169
|
+
label: "Configure AI tools for this project",
|
|
2170
|
+
hint: "Choose which AI tools to sync"
|
|
2171
|
+
},
|
|
2172
|
+
{
|
|
2173
|
+
value: "configure-ides",
|
|
2174
|
+
label: "Configure IDEs for this project",
|
|
2175
|
+
hint: "Choose which IDEs to sync"
|
|
2176
|
+
},
|
|
1596
2177
|
{
|
|
1597
2178
|
value: "remove-baton",
|
|
1598
2179
|
label: "Remove Baton",
|
|
@@ -1620,6 +2201,14 @@ const manageCommand = defineCommand({
|
|
|
1620
2201
|
console.log("");
|
|
1621
2202
|
await handleRemoveProfile(cwd);
|
|
1622
2203
|
console.log("");
|
|
2204
|
+
} else if (action === "configure-ai") {
|
|
2205
|
+
console.log("");
|
|
2206
|
+
await handleConfigureAiTools(cwd);
|
|
2207
|
+
console.log("");
|
|
2208
|
+
} else if (action === "configure-ides") {
|
|
2209
|
+
console.log("");
|
|
2210
|
+
await handleConfigureIdes(cwd);
|
|
2211
|
+
console.log("");
|
|
1623
2212
|
} else if (action === "remove-baton") {
|
|
1624
2213
|
console.log("");
|
|
1625
2214
|
if (await handleRemoveBaton(cwd)) {
|
|
@@ -1640,8 +2229,8 @@ const profileCommand = defineCommand({
|
|
|
1640
2229
|
description: "Manage profiles (create, list, remove)"
|
|
1641
2230
|
},
|
|
1642
2231
|
subCommands: {
|
|
1643
|
-
create: () => import("./create-
|
|
1644
|
-
list: () => import("./list-
|
|
2232
|
+
create: () => import("./create-BOcW-DBk.mjs").then((m) => m.createCommand),
|
|
2233
|
+
list: () => import("./list-DmzVXCNF.mjs").then((m) => m.profileListCommand),
|
|
1645
2234
|
remove: () => import("./remove-BBs6Mv8t.mjs").then((m) => m.profileRemoveCommand)
|
|
1646
2235
|
}
|
|
1647
2236
|
});
|
|
@@ -2181,23 +2770,7 @@ async function cleanupOrphanedFiles(params) {
|
|
|
2181
2770
|
return;
|
|
2182
2771
|
}
|
|
2183
2772
|
spinner.start("Removing orphaned files...");
|
|
2184
|
-
|
|
2185
|
-
for (const orphanedPath of orphanedPaths) {
|
|
2186
|
-
const absolutePath = orphanedPath.startsWith("/") ? orphanedPath : resolve(projectRoot, orphanedPath);
|
|
2187
|
-
try {
|
|
2188
|
-
await unlink(absolutePath);
|
|
2189
|
-
removedCount++;
|
|
2190
|
-
let dir = dirname(absolutePath);
|
|
2191
|
-
while (dir !== projectRoot && dir.startsWith(projectRoot)) try {
|
|
2192
|
-
if ((await readdir(dir)).length === 0) {
|
|
2193
|
-
await rmdir(dir);
|
|
2194
|
-
dir = dirname(dir);
|
|
2195
|
-
} else break;
|
|
2196
|
-
} catch {
|
|
2197
|
-
break;
|
|
2198
|
-
}
|
|
2199
|
-
} catch {}
|
|
2200
|
-
}
|
|
2773
|
+
const removedCount = await removePlacedFiles(orphanedPaths, projectRoot);
|
|
2201
2774
|
spinner.stop(`Removed ${removedCount} orphaned file(s)`);
|
|
2202
2775
|
}
|
|
2203
2776
|
const syncCommand = defineCommand({
|
|
@@ -2260,6 +2833,7 @@ const syncCommand = defineCommand({
|
|
|
2260
2833
|
else Ne(`Failed to load baton.yaml: ${error instanceof Error ? error.message : String(error)}`);
|
|
2261
2834
|
process.exit(1);
|
|
2262
2835
|
}
|
|
2836
|
+
await promptFirstRunPreferences(projectRoot, !!args.yes);
|
|
2263
2837
|
const previousPaths = /* @__PURE__ */ new Set();
|
|
2264
2838
|
try {
|
|
2265
2839
|
const previousLock = await readLock(resolve(projectRoot, "baton.lock"));
|
|
@@ -2420,16 +2994,19 @@ const syncCommand = defineCommand({
|
|
|
2420
2994
|
spinner.stop(`Merged: ${mergedSkills.length} skills, ${mergedRules.length} rules, ${mergedMemory.length} memory files, ${mergedCommandCount} commands, ${mergedFileCount} files, ${mergedIdeCount} IDE configs`);
|
|
2421
2995
|
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.`);
|
|
2422
2996
|
spinner.start("Computing tool intersection...");
|
|
2423
|
-
const
|
|
2424
|
-
const globalIdePlatforms = await getGlobalIdePlatforms();
|
|
2997
|
+
const prefs = await resolvePreferences(projectRoot);
|
|
2425
2998
|
const detectedAgents = await detectInstalledAgents();
|
|
2999
|
+
if (verbose) {
|
|
3000
|
+
R.info(`AI tools: ${prefs.ai.tools.join(", ") || "(none)"} (from ${prefs.ai.source} preferences)`);
|
|
3001
|
+
R.info(`IDE platforms: ${prefs.ide.platforms.join(", ") || "(none)"} (from ${prefs.ide.source} preferences)`);
|
|
3002
|
+
}
|
|
2426
3003
|
let syncedAiTools;
|
|
2427
3004
|
let syncedIdePlatforms = null;
|
|
2428
3005
|
let allIntersections = null;
|
|
2429
|
-
if (
|
|
3006
|
+
if (prefs.ai.tools.length > 0) {
|
|
2430
3007
|
const developerTools = {
|
|
2431
|
-
aiTools:
|
|
2432
|
-
idePlatforms:
|
|
3008
|
+
aiTools: prefs.ai.tools,
|
|
3009
|
+
idePlatforms: prefs.ide.platforms
|
|
2433
3010
|
};
|
|
2434
3011
|
const aggregatedSyncedAi = /* @__PURE__ */ new Set();
|
|
2435
3012
|
const aggregatedSyncedIde = /* @__PURE__ */ new Set();
|
|
@@ -2641,9 +3218,10 @@ const syncCommand = defineCommand({
|
|
|
2641
3218
|
const combinedContent = entry.parts.join("\n\n");
|
|
2642
3219
|
const result = await placeFile(combinedContent, entry.adapter, entry.type, "project", entry.name, placementConfig);
|
|
2643
3220
|
if (result.action !== "skipped") stats.created++;
|
|
3221
|
+
const relPath = isAbsolute(result.path) ? relative(projectRoot, result.path) : result.path;
|
|
2644
3222
|
for (const profileName of entry.profiles) {
|
|
2645
3223
|
const pf = getOrCreatePlacedFiles(placedFiles, profileName);
|
|
2646
|
-
pf[
|
|
3224
|
+
pf[relPath] = {
|
|
2647
3225
|
content: combinedContent,
|
|
2648
3226
|
tool: entry.adapter.key,
|
|
2649
3227
|
category: "ai"
|
|
@@ -2673,8 +3251,9 @@ const syncCommand = defineCommand({
|
|
|
2673
3251
|
}
|
|
2674
3252
|
const result = await placeFile(content, adapter, "commands", "project", commandName, placementConfig);
|
|
2675
3253
|
if (result.action !== "skipped") stats.created++;
|
|
3254
|
+
const cmdRelPath = isAbsolute(result.path) ? relative(projectRoot, result.path) : result.path;
|
|
2676
3255
|
const pf = getOrCreatePlacedFiles(placedFiles, profile.name);
|
|
2677
|
-
pf[
|
|
3256
|
+
pf[cmdRelPath] = {
|
|
2678
3257
|
content,
|
|
2679
3258
|
tool: adapter.key,
|
|
2680
3259
|
category: "ai"
|