@baton-dx/cli 0.1.1 → 0.1.3
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/README.md +66 -0
- package/dist/agent-detection-BW5-jGuR.mjs +4 -0
- package/dist/agent-detection-C5gaTtah.mjs +501 -0
- package/dist/agent-detection-C5gaTtah.mjs.map +1 -0
- package/dist/chunk-BbwQpWto.mjs +33 -0
- package/dist/context-detection-mMNLg_4F.mjs +9841 -0
- package/dist/context-detection-mMNLg_4F.mjs.map +1 -0
- package/dist/{create-CqfUSGj7.mjs → create-Diqnd3ci.mjs} +15 -15
- package/dist/create-Diqnd3ci.mjs.map +1 -0
- package/dist/esm-BagM-kVd.mjs +4526 -0
- package/dist/esm-BagM-kVd.mjs.map +1 -0
- package/dist/esm-CuRZ1S4C.mjs +4 -0
- package/dist/execa-RdtdAT4S.mjs +6343 -0
- package/dist/execa-RdtdAT4S.mjs.map +1 -0
- package/dist/index.mjs +290 -291
- package/dist/index.mjs.map +1 -1
- package/dist/{list-o76RXPxE.mjs → list-B5xUVBTU.mjs} +9 -10
- package/dist/{list-o76RXPxE.mjs.map → list-B5xUVBTU.mjs.map} +1 -1
- package/dist/prompt-CPT4cDau.mjs +849 -0
- package/dist/prompt-CPT4cDau.mjs.map +1 -0
- package/dist/{remove-BB883RDx.mjs → remove-6S8F9xcE.mjs} +15 -17
- package/dist/remove-6S8F9xcE.mjs.map +1 -0
- package/dist/src-Dh0ZvHbV.mjs +14434 -0
- package/dist/src-Dh0ZvHbV.mjs.map +1 -0
- package/package.json +5 -3
- package/dist/context-detection-0m8_Fp0j.mjs +0 -45
- package/dist/context-detection-0m8_Fp0j.mjs.map +0 -1
- package/dist/create-CqfUSGj7.mjs.map +0 -1
- package/dist/remove-BB883RDx.mjs.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -1,16 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { r as __toESM } from "./chunk-BbwQpWto.mjs";
|
|
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-mMNLg_4F.mjs";
|
|
4
|
+
import { A as readLock, B as getAdaptersForKeys, C as resolveProfileSupport, D as discoverProfilesInSourceRepo, E as placeFile, F as updateGitignore, G as loadProjectManifest, H as parseSource, I as getIdePlatformTargetDir, J as SourceParseError, K as KEBAB_CASE_REGEX, L as getRegisteredIdePlatforms, M as resolveVersion, N as cloneGitSource, O as findSourceManifest, P as collectProfileSupportPatterns, R as idePlatformRegistry, S as mergeContentParts, T as detectLegacyPaths, U as loadLockfile, V as parseFrontmatter, W as loadProfileManifest, X as getAgentPath, Y as getAgentConfig, Z as getAllAgentKeys, _ as mergeSkills, a as getDefaultGlobalSource, b as isLockedProfile, c as getGlobalSources, d as setGlobalIdePlatforms, f as require_lib, g as mergeRulesWithWarnings, h as mergeRules, i as addGlobalSource, j as writeLock, k as generateLock, l as removeGlobalSource, m as mergeMemoryWithWarnings, n as clearIdeCache, o as getGlobalAiTools, p as mergeMemory, q as FileNotFoundError, r as detectInstalledIdes, s as getGlobalIdePlatforms, t as computeIntersection, u as setGlobalAiTools, v as mergeSkillsWithWarnings, w as resolveProfileChain, x as sortProfilesByWeight, y as getProfileWeight, z as isKnownIdePlatform } from "./src-Dh0ZvHbV.mjs";
|
|
5
|
+
import { n as detectInstalledAgents, t as clearAgentCache } from "./agent-detection-C5gaTtah.mjs";
|
|
6
|
+
import { d as esm_default } from "./esm-BagM-kVd.mjs";
|
|
3
7
|
import { access, mkdir, readFile, readdir, rm, rmdir, stat, unlink, writeFile } from "node:fs/promises";
|
|
4
8
|
import { dirname, join, resolve } from "node:path";
|
|
5
9
|
import { fileURLToPath } from "node:url";
|
|
6
|
-
import { defineCommand, runMain } from "citty";
|
|
7
|
-
import { getAgentConfig, getAgentPath, getAllAgentKeys } from "@baton-dx/agent-paths";
|
|
8
|
-
import { FileNotFoundError, KEBAB_CASE_REGEX, SourceParseError, addGlobalSource, clearAgentCache, clearIdeCache, cloneGitSource, collectProfileSupportPatterns, computeIntersection, detectInstalledAgents, detectInstalledIdes, detectLegacyPaths, discoverProfilesInSourceRepo, findSourceManifest, generateLock, getAdaptersForKeys, getDefaultGlobalSource, getGlobalAiTools, getGlobalIdePlatforms, getGlobalSources, getIdePlatformTargetDir, getProfileWeight, getRegisteredIdePlatforms, idePlatformRegistry, isKnownIdePlatform, isLockedProfile, loadLockfile, loadProfileManifest, loadProjectManifest, mergeContentParts, mergeMemory, mergeMemoryWithWarnings, mergeRules, mergeRulesWithWarnings, mergeSkills, mergeSkillsWithWarnings, parseFrontmatter, parseSource, placeFile, readLock, removeGlobalSource, resolveProfileChain, resolveProfileSupport, resolveVersion, setGlobalAiTools, setGlobalIdePlatforms, sortProfilesByWeight, updateGitignore, writeLock } from "@baton-dx/core";
|
|
9
|
-
import * as p from "@clack/prompts";
|
|
10
10
|
import { homedir } from "node:os";
|
|
11
|
-
import { parse, stringify } from "yaml";
|
|
12
|
-
import Handlebars from "handlebars";
|
|
13
|
-
import simpleGit from "simple-git";
|
|
14
11
|
|
|
15
12
|
//#region src/commands/ai-tools/list.ts
|
|
16
13
|
const aiToolsListCommand = defineCommand({
|
|
@@ -31,7 +28,7 @@ const aiToolsListCommand = defineCommand({
|
|
|
31
28
|
}
|
|
32
29
|
},
|
|
33
30
|
async run({ args }) {
|
|
34
|
-
if (!args.json)
|
|
31
|
+
if (!args.json) We("Baton - AI Tools");
|
|
35
32
|
const savedTools = await getGlobalAiTools();
|
|
36
33
|
const allAgentKeys = getAllAgentKeys();
|
|
37
34
|
const keysToShow = args.all ? allAgentKeys : savedTools.length > 0 ? savedTools : allAgentKeys;
|
|
@@ -75,15 +72,15 @@ const aiToolsListCommand = defineCommand({
|
|
|
75
72
|
return;
|
|
76
73
|
}
|
|
77
74
|
if (savedTools.length === 0) {
|
|
78
|
-
|
|
79
|
-
|
|
75
|
+
R.warn("No AI tools saved in global config.");
|
|
76
|
+
R.info("Run 'baton ai-tools scan' to detect and save your AI tools.");
|
|
80
77
|
console.log("");
|
|
81
|
-
|
|
78
|
+
R.info(`All ${allAgentKeys.length} supported tools:`);
|
|
82
79
|
for (const key of allAgentKeys) {
|
|
83
80
|
const config = getAgentConfig(key);
|
|
84
81
|
console.log(` \x1b[90m- ${config.name}\x1b[0m`);
|
|
85
82
|
}
|
|
86
|
-
|
|
83
|
+
Le("Run 'baton ai-tools scan' to get started.");
|
|
87
84
|
return;
|
|
88
85
|
}
|
|
89
86
|
console.log(`\nSaved AI tools (${savedTools.length}):\n`);
|
|
@@ -104,7 +101,7 @@ const aiToolsListCommand = defineCommand({
|
|
|
104
101
|
}
|
|
105
102
|
console.log("");
|
|
106
103
|
}
|
|
107
|
-
|
|
104
|
+
Le("Manage tools: 'baton ai-tools scan' (detect) | 'baton config set default-tools <tools>'");
|
|
108
105
|
}
|
|
109
106
|
});
|
|
110
107
|
/**
|
|
@@ -133,8 +130,8 @@ const aiToolsScanCommand = defineCommand({
|
|
|
133
130
|
description: "Automatically save detected tools without confirmation"
|
|
134
131
|
} },
|
|
135
132
|
async run({ args }) {
|
|
136
|
-
|
|
137
|
-
const spinner =
|
|
133
|
+
We("Baton - AI Tool Scanner");
|
|
134
|
+
const spinner = bt();
|
|
138
135
|
spinner.start("Scanning for AI tools...");
|
|
139
136
|
clearAgentCache();
|
|
140
137
|
const detectedAgents = await detectInstalledAgents();
|
|
@@ -152,19 +149,19 @@ const aiToolsScanCommand = defineCommand({
|
|
|
152
149
|
else notInstalled.push(entry);
|
|
153
150
|
}
|
|
154
151
|
if (installed.length > 0) {
|
|
155
|
-
|
|
152
|
+
R.success(`Found ${installed.length} AI tool${installed.length !== 1 ? "s" : ""}:`);
|
|
156
153
|
for (const agent of installed) {
|
|
157
154
|
const badge = currentTools.includes(agent.key) ? " (saved)" : " (new)";
|
|
158
155
|
console.log(` \x1b[32m✓\x1b[0m ${agent.name}${badge}`);
|
|
159
156
|
}
|
|
160
157
|
} else {
|
|
161
|
-
|
|
162
|
-
|
|
158
|
+
R.warn("No AI tools detected on your system.");
|
|
159
|
+
Le("Scan finished.");
|
|
163
160
|
return;
|
|
164
161
|
}
|
|
165
162
|
if (notInstalled.length > 0) {
|
|
166
163
|
console.log("");
|
|
167
|
-
|
|
164
|
+
R.info(`Not detected (${notInstalled.length}):`);
|
|
168
165
|
for (const agent of notInstalled) console.log(` \x1b[90m✗ ${agent.name}\x1b[0m`);
|
|
169
166
|
}
|
|
170
167
|
const detectedKeys = installed.map((a) => a.key);
|
|
@@ -172,22 +169,22 @@ const aiToolsScanCommand = defineCommand({
|
|
|
172
169
|
console.log("");
|
|
173
170
|
let shouldSave = args.yes;
|
|
174
171
|
if (!shouldSave) {
|
|
175
|
-
const confirm = await
|
|
176
|
-
if (
|
|
177
|
-
|
|
172
|
+
const confirm = await Re({ message: "Save detected tools to global config (~/.baton/config.yaml)?" });
|
|
173
|
+
if (Ct(confirm)) {
|
|
174
|
+
Le("Scan finished (not saved).");
|
|
178
175
|
return;
|
|
179
176
|
}
|
|
180
177
|
shouldSave = confirm;
|
|
181
178
|
}
|
|
182
179
|
if (shouldSave) {
|
|
183
180
|
await setGlobalAiTools(detectedKeys);
|
|
184
|
-
|
|
181
|
+
R.success("Tools saved to global config.");
|
|
185
182
|
}
|
|
186
183
|
} else {
|
|
187
184
|
console.log("");
|
|
188
|
-
|
|
185
|
+
R.info("Global config is already up to date.");
|
|
189
186
|
}
|
|
190
|
-
|
|
187
|
+
Le("Scan finished.");
|
|
191
188
|
}
|
|
192
189
|
});
|
|
193
190
|
|
|
@@ -206,6 +203,7 @@ const aiToolsCommand = defineCommand({
|
|
|
206
203
|
|
|
207
204
|
//#endregion
|
|
208
205
|
//#region src/utils/build-intersection.ts
|
|
206
|
+
var import_dist = require_dist();
|
|
209
207
|
/**
|
|
210
208
|
* Compute the intersection for a single profile source string.
|
|
211
209
|
* Shared utility used by sync, config, and manage commands.
|
|
@@ -254,7 +252,7 @@ function displayIntersection(intersection) {
|
|
|
254
252
|
const hasAiData = intersection.aiTools.synced.length > 0 || intersection.aiTools.unsupported.length > 0 || intersection.aiTools.unavailable.length > 0;
|
|
255
253
|
const hasIdeData = intersection.idePlatforms.synced.length > 0 || intersection.idePlatforms.unsupported.length > 0 || intersection.idePlatforms.unavailable.length > 0;
|
|
256
254
|
if (!hasAiData && !hasIdeData) {
|
|
257
|
-
|
|
255
|
+
R.info("No tool or IDE intersection data available.");
|
|
258
256
|
return;
|
|
259
257
|
}
|
|
260
258
|
if (hasAiData) displayDimension("AI Tools", intersection.aiTools);
|
|
@@ -268,7 +266,7 @@ function displayDimension(label, dimension) {
|
|
|
268
266
|
if (dimension.synced.length > 0) for (const item of dimension.synced) lines.push(` \u2713 ${item}`);
|
|
269
267
|
if (dimension.unavailable.length > 0) for (const item of dimension.unavailable) lines.push(` - ${item} (not installed)`);
|
|
270
268
|
if (dimension.unsupported.length > 0) for (const item of dimension.unsupported) lines.push(` ~ ${item} (not supported by profile)`);
|
|
271
|
-
if (lines.length > 0)
|
|
269
|
+
if (lines.length > 0) Ve(lines.join("\n"), label);
|
|
272
270
|
}
|
|
273
271
|
/**
|
|
274
272
|
* Format a compact intersection summary for inline display.
|
|
@@ -297,14 +295,14 @@ async function loadConfig() {
|
|
|
297
295
|
} catch {
|
|
298
296
|
return {};
|
|
299
297
|
}
|
|
300
|
-
return parse(await readFile(CONFIG_FILE, "utf-8"));
|
|
298
|
+
return (0, import_dist.parse)(await readFile(CONFIG_FILE, "utf-8"));
|
|
301
299
|
}
|
|
302
300
|
async function saveConfig(config) {
|
|
303
301
|
await mkdir(dirname(CONFIG_FILE), { recursive: true });
|
|
304
|
-
await writeFile(CONFIG_FILE, stringify(config), "utf-8");
|
|
302
|
+
await writeFile(CONFIG_FILE, (0, import_dist.stringify)(config), "utf-8");
|
|
305
303
|
}
|
|
306
304
|
async function showDashboard() {
|
|
307
|
-
|
|
305
|
+
We("Baton Dashboard");
|
|
308
306
|
const [sources, aiTools, idePlatforms, projectManifest] = await Promise.all([
|
|
309
307
|
getGlobalSources(),
|
|
310
308
|
getGlobalAiTools(),
|
|
@@ -312,16 +310,16 @@ async function showDashboard() {
|
|
|
312
310
|
loadProjectManifestSafe$1()
|
|
313
311
|
]);
|
|
314
312
|
console.log("");
|
|
315
|
-
|
|
316
|
-
if (sources.length === 0)
|
|
313
|
+
R.step("Global Sources");
|
|
314
|
+
if (sources.length === 0) R.info(" No sources configured. Run: baton source connect <url>");
|
|
317
315
|
else for (const source of sources) {
|
|
318
316
|
const defaultBadge = source.default ? " (default)" : "";
|
|
319
317
|
const desc = source.description ? ` — ${source.description}` : "";
|
|
320
|
-
|
|
318
|
+
R.info(` ${source.name}${defaultBadge}: ${source.url}${desc}`);
|
|
321
319
|
}
|
|
322
320
|
console.log("");
|
|
323
|
-
|
|
324
|
-
if (aiTools.length === 0 && idePlatforms.length === 0)
|
|
321
|
+
R.step("Developer Tools");
|
|
322
|
+
if (aiTools.length === 0 && idePlatforms.length === 0) R.info(" No tools configured. Run: baton ai-tools scan && baton ides scan");
|
|
325
323
|
else {
|
|
326
324
|
if (aiTools.length > 0) {
|
|
327
325
|
const toolNames = aiTools.map((key) => {
|
|
@@ -331,17 +329,17 @@ async function showDashboard() {
|
|
|
331
329
|
return key;
|
|
332
330
|
}
|
|
333
331
|
});
|
|
334
|
-
|
|
332
|
+
R.info(` AI Tools: ${toolNames.join(", ")}`);
|
|
335
333
|
}
|
|
336
|
-
if (idePlatforms.length > 0)
|
|
334
|
+
if (idePlatforms.length > 0) R.info(` IDE Platforms: ${idePlatforms.join(", ")}`);
|
|
337
335
|
}
|
|
338
336
|
console.log("");
|
|
339
|
-
|
|
340
|
-
if (!projectManifest)
|
|
341
|
-
else if (projectManifest.profiles.length === 0)
|
|
337
|
+
R.step("Current Project");
|
|
338
|
+
if (!projectManifest) R.info(" Not inside a Baton project. Run: baton init");
|
|
339
|
+
else if (projectManifest.profiles.length === 0) R.info(" No profiles installed. Run: baton manage");
|
|
342
340
|
else for (const profile of projectManifest.profiles) {
|
|
343
341
|
const version = profile.version ? ` (${profile.version})` : "";
|
|
344
|
-
|
|
342
|
+
R.info(` ${profile.source}${version}`);
|
|
345
343
|
}
|
|
346
344
|
if (projectManifest && projectManifest.profiles.length > 0) {
|
|
347
345
|
if (aiTools.length > 0 || idePlatforms.length > 0) {
|
|
@@ -350,18 +348,18 @@ async function showDashboard() {
|
|
|
350
348
|
idePlatforms
|
|
351
349
|
};
|
|
352
350
|
console.log("");
|
|
353
|
-
|
|
351
|
+
R.step("Active Intersections");
|
|
354
352
|
for (const profile of projectManifest.profiles) try {
|
|
355
353
|
const intersection = await buildIntersection(profile.source, developerTools, process.cwd());
|
|
356
354
|
if (intersection) {
|
|
357
355
|
const summary = formatIntersectionSummary(intersection);
|
|
358
|
-
|
|
356
|
+
R.info(` ${profile.source}: ${summary}`);
|
|
359
357
|
}
|
|
360
358
|
} catch {}
|
|
361
359
|
}
|
|
362
360
|
}
|
|
363
361
|
console.log("");
|
|
364
|
-
|
|
362
|
+
Le("Use 'baton config list' to view configuration settings");
|
|
365
363
|
}
|
|
366
364
|
async function loadProjectManifestSafe$1() {
|
|
367
365
|
try {
|
|
@@ -402,33 +400,33 @@ const configCommand = defineCommand({
|
|
|
402
400
|
}
|
|
403
401
|
const selectedAction = action;
|
|
404
402
|
if (selectedAction === "list") {
|
|
405
|
-
|
|
403
|
+
We("⚙️ Baton Configuration");
|
|
406
404
|
const config = await loadConfig();
|
|
407
405
|
if (Object.keys(config).length === 0) {
|
|
408
|
-
|
|
406
|
+
Le("No configuration set. Using defaults.");
|
|
409
407
|
return;
|
|
410
408
|
}
|
|
411
409
|
console.log("");
|
|
412
410
|
for (const [configKey, configValue] of Object.entries(config)) console.log(`${configKey}: ${Array.isArray(configValue) ? configValue.join(", ") : configValue}`);
|
|
413
411
|
console.log("");
|
|
414
|
-
|
|
412
|
+
Le("Configuration loaded");
|
|
415
413
|
return;
|
|
416
414
|
}
|
|
417
415
|
if (selectedAction === "get") {
|
|
418
416
|
if (!key) {
|
|
419
|
-
|
|
420
|
-
|
|
417
|
+
We("⚙️ Baton Configuration");
|
|
418
|
+
Le("Error: Missing key argument. Usage: baton config get <key>");
|
|
421
419
|
process.exit(1);
|
|
422
420
|
}
|
|
423
421
|
if (!VALID_KEYS.includes(key)) {
|
|
424
|
-
|
|
425
|
-
|
|
422
|
+
We("⚙️ Baton Configuration");
|
|
423
|
+
Le(`Error: Invalid key "${key}". Valid keys: ${VALID_KEYS.join(", ")}`);
|
|
426
424
|
process.exit(1);
|
|
427
425
|
}
|
|
428
426
|
const configValue = (await loadConfig())[key];
|
|
429
427
|
if (configValue === void 0) {
|
|
430
|
-
|
|
431
|
-
|
|
428
|
+
We("⚙️ Baton Configuration");
|
|
429
|
+
Le(`"${key}" is not set (using default)`);
|
|
432
430
|
return;
|
|
433
431
|
}
|
|
434
432
|
console.log(Array.isArray(configValue) ? configValue.join(", ") : configValue);
|
|
@@ -436,37 +434,37 @@ const configCommand = defineCommand({
|
|
|
436
434
|
}
|
|
437
435
|
if (selectedAction === "set") {
|
|
438
436
|
if (!key || !value) {
|
|
439
|
-
|
|
440
|
-
|
|
437
|
+
We("⚙️ Baton Configuration");
|
|
438
|
+
Le("Error: Missing arguments. Usage: baton config set <key> <value>");
|
|
441
439
|
process.exit(1);
|
|
442
440
|
}
|
|
443
441
|
if (!VALID_KEYS.includes(key)) {
|
|
444
|
-
|
|
445
|
-
|
|
442
|
+
We("⚙️ Baton Configuration");
|
|
443
|
+
Le(`Error: Invalid key "${key}". Valid keys: ${VALID_KEYS.join(", ")}`);
|
|
446
444
|
process.exit(1);
|
|
447
445
|
}
|
|
448
|
-
|
|
446
|
+
We("⚙️ Baton Configuration");
|
|
449
447
|
const config = await loadConfig();
|
|
450
448
|
if (key === "default-scope") {
|
|
451
449
|
if (value !== "project" && value !== "global") {
|
|
452
|
-
|
|
450
|
+
Le("Error: default-scope must be either \"project\" or \"global\"");
|
|
453
451
|
process.exit(1);
|
|
454
452
|
}
|
|
455
453
|
config["default-scope"] = value;
|
|
456
454
|
} else if (key === "symlink-mode") {
|
|
457
455
|
if (value !== "true" && value !== "false") {
|
|
458
|
-
|
|
456
|
+
Le("Error: symlink-mode must be either \"true\" or \"false\"");
|
|
459
457
|
process.exit(1);
|
|
460
458
|
}
|
|
461
459
|
config["symlink-mode"] = value === "true";
|
|
462
460
|
} else if (key === "default-tools") config["default-tools"] = value.split(",").map((s) => s.trim());
|
|
463
461
|
else if (key === "cache-dir") config["cache-dir"] = value;
|
|
464
462
|
await saveConfig(config);
|
|
465
|
-
|
|
463
|
+
Le(`✓ Set ${key} = ${Array.isArray(config[key]) ? config[key].join(", ") : config[key]}`);
|
|
466
464
|
return;
|
|
467
465
|
}
|
|
468
|
-
|
|
469
|
-
|
|
466
|
+
We("⚙️ Baton Configuration");
|
|
467
|
+
Le(`Error: Unknown action "${selectedAction}". Use: set, get, or list`);
|
|
470
468
|
process.exit(1);
|
|
471
469
|
}
|
|
472
470
|
});
|
|
@@ -484,15 +482,15 @@ const diffCommand = defineCommand({
|
|
|
484
482
|
alias: "n"
|
|
485
483
|
} },
|
|
486
484
|
async run({ args }) {
|
|
487
|
-
|
|
485
|
+
We("🔍 Baton Diff");
|
|
488
486
|
try {
|
|
489
487
|
const projectRoot = process.cwd();
|
|
490
488
|
const manifest = await loadProjectManifest(resolve(projectRoot, "baton.yaml"));
|
|
491
489
|
if (!manifest.profiles || manifest.profiles.length === 0) {
|
|
492
|
-
|
|
490
|
+
Le("⚠️ No profiles configured in baton.yaml");
|
|
493
491
|
process.exit(0);
|
|
494
492
|
}
|
|
495
|
-
const spinner =
|
|
493
|
+
const spinner = bt();
|
|
496
494
|
spinner.start("Resolving profile chain...");
|
|
497
495
|
const allProfiles = [];
|
|
498
496
|
const profileLocalPaths = /* @__PURE__ */ new Map();
|
|
@@ -528,7 +526,7 @@ const diffCommand = defineCommand({
|
|
|
528
526
|
}
|
|
529
527
|
if (allProfiles.length === 0) {
|
|
530
528
|
spinner.stop("No profiles resolved");
|
|
531
|
-
|
|
529
|
+
Le("Nothing to diff. Run `baton manage` to add a profile.");
|
|
532
530
|
process.exit(0);
|
|
533
531
|
}
|
|
534
532
|
spinner.stop(`Resolved ${allProfiles.length} profile(s)`);
|
|
@@ -584,7 +582,7 @@ const diffCommand = defineCommand({
|
|
|
584
582
|
} else syncedAiTools = detectedAgents;
|
|
585
583
|
if (syncedAiTools.length === 0) {
|
|
586
584
|
spinner.stop("No AI tools in intersection");
|
|
587
|
-
|
|
585
|
+
Ne("No AI tools match. Run `baton ai-tools scan`.");
|
|
588
586
|
process.exit(1);
|
|
589
587
|
}
|
|
590
588
|
const adapters = getAdaptersForKeys(syncedAiTools);
|
|
@@ -719,11 +717,11 @@ const diffCommand = defineCommand({
|
|
|
719
717
|
}
|
|
720
718
|
spinner.stop();
|
|
721
719
|
if (diffs.length === 0) {
|
|
722
|
-
|
|
723
|
-
|
|
720
|
+
R.success("No differences found");
|
|
721
|
+
Le("✅ Diff complete - all placed files match remote sources");
|
|
724
722
|
process.exit(0);
|
|
725
723
|
}
|
|
726
|
-
|
|
724
|
+
R.warning(`${diffs.length} file(s) with differences`);
|
|
727
725
|
for (const diff of diffs) if (args.nameOnly) {
|
|
728
726
|
const statusSymbol = diff.status === "added" ? "+" : diff.status === "removed" ? "-" : "~";
|
|
729
727
|
console.log(` ${statusSymbol} ${diff.file}`);
|
|
@@ -747,10 +745,10 @@ const diffCommand = defineCommand({
|
|
|
747
745
|
} else if (diff.status === "added") console.log(" \x1B[32m+ New file in remote (not yet placed locally)\x1B[0m");
|
|
748
746
|
else console.log(" \x1B[31m- File exists locally but not in remote\x1B[0m");
|
|
749
747
|
}
|
|
750
|
-
|
|
748
|
+
Le("✅ Diff complete - differences found. Run `baton sync` to update.");
|
|
751
749
|
process.exit(1);
|
|
752
750
|
} catch (error) {
|
|
753
|
-
|
|
751
|
+
R.error(`Failed to run diff: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
754
752
|
process.exit(1);
|
|
755
753
|
}
|
|
756
754
|
}
|
|
@@ -863,7 +861,7 @@ const idesListCommand = defineCommand({
|
|
|
863
861
|
}
|
|
864
862
|
},
|
|
865
863
|
async run({ args }) {
|
|
866
|
-
if (!args.json)
|
|
864
|
+
if (!args.json) We("Baton - IDE Platforms");
|
|
867
865
|
const savedPlatforms = await getGlobalIdePlatforms();
|
|
868
866
|
const allIdeKeys = getRegisteredIdePlatforms();
|
|
869
867
|
const platformStatuses = (args.all ? allIdeKeys : savedPlatforms.length > 0 ? savedPlatforms : allIdeKeys).map((ideKey) => {
|
|
@@ -881,15 +879,15 @@ const idesListCommand = defineCommand({
|
|
|
881
879
|
return;
|
|
882
880
|
}
|
|
883
881
|
if (savedPlatforms.length === 0) {
|
|
884
|
-
|
|
885
|
-
|
|
882
|
+
R.warn("No IDE platforms saved in global config.");
|
|
883
|
+
R.info("Run 'baton ides scan' to detect and save your IDE platforms.");
|
|
886
884
|
console.log("");
|
|
887
|
-
|
|
885
|
+
R.info(`All ${allIdeKeys.length} supported platforms:`);
|
|
888
886
|
for (const key of allIdeKeys) {
|
|
889
887
|
const entry = idePlatformRegistry[key];
|
|
890
888
|
console.log(` \x1b[90m- ${formatIdeName(key)} (${entry?.targetDir ?? key})\x1b[0m`);
|
|
891
889
|
}
|
|
892
|
-
|
|
890
|
+
Le("Run 'baton ides scan' to get started.");
|
|
893
891
|
return;
|
|
894
892
|
}
|
|
895
893
|
console.log(`\nSaved IDE platforms (${savedPlatforms.length}):\n`);
|
|
@@ -899,7 +897,7 @@ const idesListCommand = defineCommand({
|
|
|
899
897
|
console.log(`${statusColor}${status}[0m ${platform.name.padEnd(20)} → ${platform.targetDir}`);
|
|
900
898
|
}
|
|
901
899
|
console.log("");
|
|
902
|
-
|
|
900
|
+
Le("Manage platforms: 'baton ides scan' (detect)");
|
|
903
901
|
}
|
|
904
902
|
});
|
|
905
903
|
|
|
@@ -916,8 +914,8 @@ const idesScanCommand = defineCommand({
|
|
|
916
914
|
description: "Automatically save detected platforms without confirmation"
|
|
917
915
|
} },
|
|
918
916
|
async run({ args }) {
|
|
919
|
-
|
|
920
|
-
const spinner =
|
|
917
|
+
We("Baton - IDE Platform Scanner");
|
|
918
|
+
const spinner = bt();
|
|
921
919
|
spinner.start("Scanning for IDE platforms...");
|
|
922
920
|
clearIdeCache();
|
|
923
921
|
const detectedIdes = await detectInstalledIdes();
|
|
@@ -935,19 +933,19 @@ const idesScanCommand = defineCommand({
|
|
|
935
933
|
else notInstalled.push(entry);
|
|
936
934
|
}
|
|
937
935
|
if (installed.length > 0) {
|
|
938
|
-
|
|
936
|
+
R.success(`Found ${installed.length} IDE platform${installed.length !== 1 ? "s" : ""}:`);
|
|
939
937
|
for (const ide of installed) {
|
|
940
938
|
const badge = currentPlatforms.includes(ide.key) ? " (saved)" : " (new)";
|
|
941
939
|
console.log(` \x1b[32m✓\x1b[0m ${ide.name}${badge}`);
|
|
942
940
|
}
|
|
943
941
|
} else {
|
|
944
|
-
|
|
945
|
-
|
|
942
|
+
R.warn("No IDE platforms detected on your system.");
|
|
943
|
+
Le("Scan finished.");
|
|
946
944
|
return;
|
|
947
945
|
}
|
|
948
946
|
if (notInstalled.length > 0) {
|
|
949
947
|
console.log("");
|
|
950
|
-
|
|
948
|
+
R.info(`Not detected (${notInstalled.length}):`);
|
|
951
949
|
for (const ide of notInstalled) console.log(` \x1b[90m✗ ${ide.name}\x1b[0m`);
|
|
952
950
|
}
|
|
953
951
|
const detectedKeys = installed.map((i) => i.key);
|
|
@@ -955,22 +953,22 @@ const idesScanCommand = defineCommand({
|
|
|
955
953
|
console.log("");
|
|
956
954
|
let shouldSave = args.yes;
|
|
957
955
|
if (!shouldSave) {
|
|
958
|
-
const confirm = await
|
|
959
|
-
if (
|
|
960
|
-
|
|
956
|
+
const confirm = await Re({ message: "Save detected platforms to global config (~/.baton/config.yaml)?" });
|
|
957
|
+
if (Ct(confirm)) {
|
|
958
|
+
Le("Scan finished (not saved).");
|
|
961
959
|
return;
|
|
962
960
|
}
|
|
963
961
|
shouldSave = confirm;
|
|
964
962
|
}
|
|
965
963
|
if (shouldSave) {
|
|
966
964
|
await setGlobalIdePlatforms(detectedKeys);
|
|
967
|
-
|
|
965
|
+
R.success("Platforms saved to global config.");
|
|
968
966
|
}
|
|
969
967
|
} else {
|
|
970
968
|
console.log("");
|
|
971
|
-
|
|
969
|
+
R.info("Global config is already up to date.");
|
|
972
970
|
}
|
|
973
|
-
|
|
971
|
+
Le("Scan finished.");
|
|
974
972
|
}
|
|
975
973
|
});
|
|
976
974
|
|
|
@@ -999,7 +997,7 @@ const idesCommand = defineCommand({
|
|
|
999
997
|
async function selectProfileFromSource(sourceString) {
|
|
1000
998
|
const parsedSource = parseSource(sourceString);
|
|
1001
999
|
if ((parsedSource.provider === "github" || parsedSource.provider === "gitlab") && !parsedSource.subpath) {
|
|
1002
|
-
const spinner =
|
|
1000
|
+
const spinner = bt();
|
|
1003
1001
|
spinner.start("Cloning repository to discover profiles...");
|
|
1004
1002
|
try {
|
|
1005
1003
|
const cloned = await cloneGitSource({
|
|
@@ -1010,10 +1008,10 @@ async function selectProfileFromSource(sourceString) {
|
|
|
1010
1008
|
spinner.stop("✅ Repository cloned");
|
|
1011
1009
|
const profiles = await discoverProfilesInSourceRepo(cloned.localPath);
|
|
1012
1010
|
if (profiles.length === 0) {
|
|
1013
|
-
|
|
1011
|
+
Ne("❌ No profiles found in the source repository");
|
|
1014
1012
|
process.exit(1);
|
|
1015
1013
|
}
|
|
1016
|
-
const selectedProfilePath = await
|
|
1014
|
+
const selectedProfilePath = await Je({
|
|
1017
1015
|
message: "Select a profile from this source:",
|
|
1018
1016
|
options: profiles.map((profile) => ({
|
|
1019
1017
|
value: profile.path,
|
|
@@ -1021,15 +1019,15 @@ async function selectProfileFromSource(sourceString) {
|
|
|
1021
1019
|
hint: profile.description ? `${profile.description} (v${profile.version})` : `v${profile.version}`
|
|
1022
1020
|
}))
|
|
1023
1021
|
});
|
|
1024
|
-
if (
|
|
1025
|
-
|
|
1022
|
+
if (Ct(selectedProfilePath)) {
|
|
1023
|
+
Ne("❌ Profile selection cancelled");
|
|
1026
1024
|
process.exit(0);
|
|
1027
1025
|
}
|
|
1028
1026
|
if (selectedProfilePath === ".") return sourceString;
|
|
1029
1027
|
return `${parsedSource.ref ? `${parsedSource.provider}:${parsedSource.org}/${parsedSource.repo}@${parsedSource.ref}` : `${parsedSource.provider}:${parsedSource.org}/${parsedSource.repo}`}/${selectedProfilePath}`;
|
|
1030
1028
|
} catch (error) {
|
|
1031
1029
|
spinner.stop("❌ Failed to clone repository");
|
|
1032
|
-
|
|
1030
|
+
Ne(`❌ Failed to discover profiles: ${error instanceof Error ? error.message : String(error)}`);
|
|
1033
1031
|
process.exit(1);
|
|
1034
1032
|
}
|
|
1035
1033
|
}
|
|
@@ -1037,7 +1035,7 @@ async function selectProfileFromSource(sourceString) {
|
|
|
1037
1035
|
const profiles = await discoverProfilesInSourceRepo(parsedSource.path.startsWith("/") ? parsedSource.path : resolve(process.cwd(), parsedSource.path));
|
|
1038
1036
|
if (profiles.length === 0) return sourceString;
|
|
1039
1037
|
if (profiles.length === 1) return profiles[0].path === "." ? sourceString : `${sourceString}/${profiles[0].path}`;
|
|
1040
|
-
const selectedProfilePath = await
|
|
1038
|
+
const selectedProfilePath = await Je({
|
|
1041
1039
|
message: "Select a profile from this source:",
|
|
1042
1040
|
options: profiles.map((profile) => ({
|
|
1043
1041
|
value: profile.path,
|
|
@@ -1045,8 +1043,8 @@ async function selectProfileFromSource(sourceString) {
|
|
|
1045
1043
|
hint: profile.description ? `${profile.description} (v${profile.version})` : `v${profile.version}`
|
|
1046
1044
|
}))
|
|
1047
1045
|
});
|
|
1048
|
-
if (
|
|
1049
|
-
|
|
1046
|
+
if (Ct(selectedProfilePath)) {
|
|
1047
|
+
Ne("❌ Profile selection cancelled");
|
|
1050
1048
|
process.exit(0);
|
|
1051
1049
|
}
|
|
1052
1050
|
if (selectedProfilePath === ".") return sourceString;
|
|
@@ -1066,7 +1064,7 @@ async function selectProfileFromSource(sourceString) {
|
|
|
1066
1064
|
async function selectMultipleProfilesFromSource(sourceString, options) {
|
|
1067
1065
|
const parsedSource = parseSource(sourceString);
|
|
1068
1066
|
if ((parsedSource.provider === "github" || parsedSource.provider === "gitlab") && !parsedSource.subpath) {
|
|
1069
|
-
const spinner =
|
|
1067
|
+
const spinner = bt();
|
|
1070
1068
|
spinner.start("Cloning repository to discover profiles...");
|
|
1071
1069
|
try {
|
|
1072
1070
|
const cloned = await cloneGitSource({
|
|
@@ -1077,18 +1075,18 @@ async function selectMultipleProfilesFromSource(sourceString, options) {
|
|
|
1077
1075
|
spinner.stop("✅ Repository cloned");
|
|
1078
1076
|
const profiles = await discoverProfilesInSourceRepo(cloned.localPath);
|
|
1079
1077
|
if (profiles.length === 0) {
|
|
1080
|
-
|
|
1078
|
+
Ne("❌ No profiles found in the source repository");
|
|
1081
1079
|
process.exit(1);
|
|
1082
1080
|
}
|
|
1083
1081
|
if (profiles.length === 1) {
|
|
1084
|
-
|
|
1082
|
+
Ve(`Using profile: ${profiles[0].name}`, "Profile");
|
|
1085
1083
|
return [constructProfilePath(parsedSource, profiles[0].path)];
|
|
1086
1084
|
}
|
|
1087
1085
|
if (options?.nonInteractive) {
|
|
1088
|
-
|
|
1086
|
+
Ve(`Auto-selecting all ${profiles.length} profile(s)`, "Non-interactive mode");
|
|
1089
1087
|
return profiles.map((prof) => constructProfilePath(parsedSource, prof.path));
|
|
1090
1088
|
}
|
|
1091
|
-
const selected = await
|
|
1089
|
+
const selected = await je({
|
|
1092
1090
|
message: "Select profile(s) to install: (Space to select, Enter to continue)",
|
|
1093
1091
|
options: profiles.map((prof) => ({
|
|
1094
1092
|
value: prof.path,
|
|
@@ -1097,14 +1095,14 @@ async function selectMultipleProfilesFromSource(sourceString, options) {
|
|
|
1097
1095
|
})),
|
|
1098
1096
|
required: true
|
|
1099
1097
|
});
|
|
1100
|
-
if (
|
|
1101
|
-
|
|
1098
|
+
if (Ct(selected)) {
|
|
1099
|
+
Ne("❌ Profile selection cancelled");
|
|
1102
1100
|
process.exit(0);
|
|
1103
1101
|
}
|
|
1104
1102
|
return selected.map((path) => constructProfilePath(parsedSource, path));
|
|
1105
1103
|
} catch (error) {
|
|
1106
1104
|
spinner.stop("❌ Failed to clone repository");
|
|
1107
|
-
|
|
1105
|
+
Ne(`❌ Failed to discover profiles: ${error instanceof Error ? error.message : String(error)}`);
|
|
1108
1106
|
process.exit(1);
|
|
1109
1107
|
}
|
|
1110
1108
|
}
|
|
@@ -1112,14 +1110,14 @@ async function selectMultipleProfilesFromSource(sourceString, options) {
|
|
|
1112
1110
|
const profiles = await discoverProfilesInSourceRepo(parsedSource.path.startsWith("/") ? parsedSource.path : resolve(process.cwd(), parsedSource.path));
|
|
1113
1111
|
if (profiles.length === 0) return [sourceString];
|
|
1114
1112
|
if (profiles.length === 1) {
|
|
1115
|
-
|
|
1113
|
+
Ve(`Using profile: ${profiles[0].name}`, "Profile");
|
|
1116
1114
|
return [profiles[0].path === "." ? sourceString : `${sourceString}/${profiles[0].path}`];
|
|
1117
1115
|
}
|
|
1118
1116
|
if (options?.nonInteractive) {
|
|
1119
|
-
|
|
1117
|
+
Ve(`Auto-selecting all ${profiles.length} profile(s)`, "Non-interactive mode");
|
|
1120
1118
|
return profiles.map((prof) => prof.path === "." ? sourceString : `${sourceString}/${prof.path}`);
|
|
1121
1119
|
}
|
|
1122
|
-
const selected = await
|
|
1120
|
+
const selected = await je({
|
|
1123
1121
|
message: "Select profile(s) to install: (Space to select, Enter to continue)",
|
|
1124
1122
|
options: profiles.map((prof) => ({
|
|
1125
1123
|
value: prof.path,
|
|
@@ -1128,8 +1126,8 @@ async function selectMultipleProfilesFromSource(sourceString, options) {
|
|
|
1128
1126
|
})),
|
|
1129
1127
|
required: true
|
|
1130
1128
|
});
|
|
1131
|
-
if (
|
|
1132
|
-
|
|
1129
|
+
if (Ct(selected)) {
|
|
1130
|
+
Ne("❌ Profile selection cancelled");
|
|
1133
1131
|
process.exit(0);
|
|
1134
1132
|
}
|
|
1135
1133
|
return selected.map((path) => path === "." ? sourceString : `${sourceString}/${path}`);
|
|
@@ -1161,12 +1159,12 @@ async function runBatonSync(cwd) {
|
|
|
1161
1159
|
stdio: "inherit"
|
|
1162
1160
|
});
|
|
1163
1161
|
syncProcess.on("close", (code) => {
|
|
1164
|
-
if (code === 0)
|
|
1165
|
-
else
|
|
1162
|
+
if (code === 0) R.success("Profiles synced successfully!");
|
|
1163
|
+
else R.warn(`Sync finished with exit code ${code}`);
|
|
1166
1164
|
done();
|
|
1167
1165
|
});
|
|
1168
1166
|
syncProcess.on("error", (error) => {
|
|
1169
|
-
|
|
1167
|
+
R.warn(`Failed to run sync: ${error.message}`);
|
|
1170
1168
|
done();
|
|
1171
1169
|
});
|
|
1172
1170
|
});
|
|
@@ -1197,17 +1195,17 @@ const initCommand = defineCommand({
|
|
|
1197
1195
|
async run({ args }) {
|
|
1198
1196
|
const isInteractive = !args.yes;
|
|
1199
1197
|
const cwd = process.cwd();
|
|
1200
|
-
|
|
1198
|
+
We("🎯 Welcome to Baton");
|
|
1201
1199
|
try {
|
|
1202
1200
|
await readFile(join(cwd, "baton.yaml"));
|
|
1203
1201
|
if (!args.force) {
|
|
1204
|
-
|
|
1205
|
-
|
|
1202
|
+
Ne("baton.yaml already exists in this directory.");
|
|
1203
|
+
Ve("Use `baton manage` to modify your project configuration\nor `baton sync` to sync your profiles.\n\nTo reinitialize, use `baton init --force`.", "Already initialized");
|
|
1206
1204
|
process.exit(1);
|
|
1207
1205
|
}
|
|
1208
|
-
|
|
1206
|
+
R.warn("Overwriting existing baton.yaml (--force)");
|
|
1209
1207
|
} catch (_error) {}
|
|
1210
|
-
const spinner =
|
|
1208
|
+
const spinner = bt();
|
|
1211
1209
|
await autoScanAiTools(spinner, isInteractive);
|
|
1212
1210
|
await autoScanIdePlatforms(spinner, isInteractive);
|
|
1213
1211
|
let profileSources;
|
|
@@ -1215,17 +1213,17 @@ const initCommand = defineCommand({
|
|
|
1215
1213
|
else {
|
|
1216
1214
|
const globalSources = await getGlobalSources();
|
|
1217
1215
|
if (globalSources.length === 0) {
|
|
1218
|
-
|
|
1219
|
-
|
|
1216
|
+
Ne("No sources configured.");
|
|
1217
|
+
Ve("Connect a source repository first:\n\n baton source connect <url>\n\nExample:\n baton source connect github:my-org/dx-config", "No sources found");
|
|
1220
1218
|
process.exit(1);
|
|
1221
1219
|
} else if (globalSources.length === 1 && globalSources[0].default) {
|
|
1222
1220
|
const defaultSource = globalSources[0];
|
|
1223
|
-
|
|
1221
|
+
Ve(`Using default source: ${defaultSource.name}\n${defaultSource.url}`, "Source");
|
|
1224
1222
|
profileSources = await selectMultipleProfilesFromSource(defaultSource.url, { nonInteractive: !isInteractive });
|
|
1225
1223
|
} else if (!isInteractive) profileSources = await selectMultipleProfilesFromSource((await getDefaultGlobalSource())?.url || globalSources[0].url, { nonInteractive: true });
|
|
1226
1224
|
else {
|
|
1227
1225
|
const defaultSource = await getDefaultGlobalSource();
|
|
1228
|
-
const selectedUrl = await
|
|
1226
|
+
const selectedUrl = await Je({
|
|
1229
1227
|
message: "Select a source repository:",
|
|
1230
1228
|
options: globalSources.map((s) => ({
|
|
1231
1229
|
value: s.url,
|
|
@@ -1234,15 +1232,15 @@ const initCommand = defineCommand({
|
|
|
1234
1232
|
})),
|
|
1235
1233
|
initialValue: defaultSource?.url
|
|
1236
1234
|
});
|
|
1237
|
-
if (
|
|
1238
|
-
|
|
1235
|
+
if (Ct(selectedUrl)) {
|
|
1236
|
+
Ne("Setup cancelled.");
|
|
1239
1237
|
process.exit(0);
|
|
1240
1238
|
}
|
|
1241
1239
|
profileSources = await selectMultipleProfilesFromSource(selectedUrl);
|
|
1242
1240
|
}
|
|
1243
1241
|
}
|
|
1244
1242
|
await showProfileIntersections(profileSources);
|
|
1245
|
-
const yamlContent = stringify({ profiles: profileSources.map((source) => ({ source })) });
|
|
1243
|
+
const yamlContent = (0, import_dist.stringify)({ profiles: profileSources.map((source) => ({ source })) });
|
|
1246
1244
|
spinner.start("Creating baton.yaml...");
|
|
1247
1245
|
await writeFile(join(cwd, "baton.yaml"), yamlContent, "utf-8");
|
|
1248
1246
|
spinner.stop("✅ Created baton.yaml");
|
|
@@ -1264,14 +1262,14 @@ const initCommand = defineCommand({
|
|
|
1264
1262
|
spinner.stop("✅ .baton directory already exists");
|
|
1265
1263
|
}
|
|
1266
1264
|
if (profileSources.length > 0) {
|
|
1267
|
-
const shouldSync = isInteractive ? await
|
|
1265
|
+
const shouldSync = isInteractive ? await Re({
|
|
1268
1266
|
message: "Sync profiles now?",
|
|
1269
1267
|
initialValue: true
|
|
1270
1268
|
}) : true;
|
|
1271
|
-
if (!
|
|
1272
|
-
else
|
|
1269
|
+
if (!Ct(shouldSync) && shouldSync) await runBatonSync(cwd);
|
|
1270
|
+
else R.info("Run 'baton sync' later to apply your profiles.");
|
|
1273
1271
|
}
|
|
1274
|
-
|
|
1272
|
+
Le("Baton initialized successfully!");
|
|
1275
1273
|
}
|
|
1276
1274
|
});
|
|
1277
1275
|
/**
|
|
@@ -1282,7 +1280,7 @@ const initCommand = defineCommand({
|
|
|
1282
1280
|
async function autoScanAiTools(spinner, isInteractive) {
|
|
1283
1281
|
const existingTools = await getGlobalAiTools();
|
|
1284
1282
|
if (existingTools.length > 0) {
|
|
1285
|
-
|
|
1283
|
+
R.info(`AI tools already configured: ${existingTools.join(", ")}`);
|
|
1286
1284
|
return;
|
|
1287
1285
|
}
|
|
1288
1286
|
spinner.start("Scanning for installed AI tools...");
|
|
@@ -1290,21 +1288,21 @@ async function autoScanAiTools(spinner, isInteractive) {
|
|
|
1290
1288
|
const detectedTools = await detectInstalledAgents();
|
|
1291
1289
|
spinner.stop(detectedTools.length > 0 ? `Found ${detectedTools.length} AI tool${detectedTools.length !== 1 ? "s" : ""}: ${detectedTools.join(", ")}` : "No AI tools detected.");
|
|
1292
1290
|
if (detectedTools.length === 0) {
|
|
1293
|
-
|
|
1291
|
+
R.warn("No AI tools detected. You can run 'baton ai-tools scan' later.");
|
|
1294
1292
|
return;
|
|
1295
1293
|
}
|
|
1296
1294
|
if (isInteractive) {
|
|
1297
|
-
const shouldSave = await
|
|
1295
|
+
const shouldSave = await Re({
|
|
1298
1296
|
message: "Save detected AI tools to global config?",
|
|
1299
1297
|
initialValue: true
|
|
1300
1298
|
});
|
|
1301
|
-
if (
|
|
1302
|
-
|
|
1299
|
+
if (Ct(shouldSave) || !shouldSave) {
|
|
1300
|
+
R.info("Skipped saving AI tools. Run 'baton ai-tools scan' later.");
|
|
1303
1301
|
return;
|
|
1304
1302
|
}
|
|
1305
1303
|
}
|
|
1306
1304
|
await setGlobalAiTools(detectedTools);
|
|
1307
|
-
|
|
1305
|
+
R.success("AI tools saved to global config.");
|
|
1308
1306
|
}
|
|
1309
1307
|
/**
|
|
1310
1308
|
* Auto-scan IDE platforms if none are configured in global config.
|
|
@@ -1314,7 +1312,7 @@ async function autoScanAiTools(spinner, isInteractive) {
|
|
|
1314
1312
|
async function autoScanIdePlatforms(spinner, isInteractive) {
|
|
1315
1313
|
const existingPlatforms = await getGlobalIdePlatforms();
|
|
1316
1314
|
if (existingPlatforms.length > 0) {
|
|
1317
|
-
|
|
1315
|
+
R.info(`IDE platforms already configured: ${existingPlatforms.join(", ")}`);
|
|
1318
1316
|
return;
|
|
1319
1317
|
}
|
|
1320
1318
|
spinner.start("Scanning for installed IDE platforms...");
|
|
@@ -1322,21 +1320,21 @@ async function autoScanIdePlatforms(spinner, isInteractive) {
|
|
|
1322
1320
|
const detectedIdes = await detectInstalledIdes();
|
|
1323
1321
|
spinner.stop(detectedIdes.length > 0 ? `Found ${detectedIdes.length} IDE platform${detectedIdes.length !== 1 ? "s" : ""}: ${detectedIdes.join(", ")}` : "No IDE platforms detected.");
|
|
1324
1322
|
if (detectedIdes.length === 0) {
|
|
1325
|
-
|
|
1323
|
+
R.warn("No IDE platforms detected. You can run 'baton ides scan' later.");
|
|
1326
1324
|
return;
|
|
1327
1325
|
}
|
|
1328
1326
|
if (isInteractive) {
|
|
1329
|
-
const shouldSave = await
|
|
1327
|
+
const shouldSave = await Re({
|
|
1330
1328
|
message: "Save detected IDE platforms to global config?",
|
|
1331
1329
|
initialValue: true
|
|
1332
1330
|
});
|
|
1333
|
-
if (
|
|
1334
|
-
|
|
1331
|
+
if (Ct(shouldSave) || !shouldSave) {
|
|
1332
|
+
R.info("Skipped saving IDE platforms. Run 'baton ides scan' later.");
|
|
1335
1333
|
return;
|
|
1336
1334
|
}
|
|
1337
1335
|
}
|
|
1338
1336
|
await setGlobalIdePlatforms(detectedIdes);
|
|
1339
|
-
|
|
1337
|
+
R.success("IDE platforms saved to global config.");
|
|
1340
1338
|
}
|
|
1341
1339
|
/**
|
|
1342
1340
|
* Load source/profile manifests for each selected profile and display
|
|
@@ -1382,7 +1380,7 @@ async function showProfileIntersections(profileSources) {
|
|
|
1382
1380
|
if (!profileManifest) continue;
|
|
1383
1381
|
const intersection = computeIntersection(developerTools, resolveProfileSupport(profileManifest, sourceManifest));
|
|
1384
1382
|
if (intersection.aiTools.synced.length > 0 || intersection.aiTools.unavailable.length > 0 || intersection.idePlatforms.synced.length > 0 || intersection.idePlatforms.unavailable.length > 0) {
|
|
1385
|
-
|
|
1383
|
+
R.step(`Intersection for ${profileManifest.name}`);
|
|
1386
1384
|
displayIntersection(intersection);
|
|
1387
1385
|
}
|
|
1388
1386
|
} catch {}
|
|
@@ -1412,16 +1410,16 @@ async function showOverview(cwd) {
|
|
|
1412
1410
|
hasLockfile(cwd)
|
|
1413
1411
|
]);
|
|
1414
1412
|
if (!manifest) {
|
|
1415
|
-
|
|
1413
|
+
R.warn("Could not load baton.yaml");
|
|
1416
1414
|
return;
|
|
1417
1415
|
}
|
|
1418
|
-
|
|
1419
|
-
if (manifest.profiles.length === 0)
|
|
1416
|
+
R.step("Installed Profiles");
|
|
1417
|
+
if (manifest.profiles.length === 0) R.info(" No profiles installed.");
|
|
1420
1418
|
else for (const profile of manifest.profiles) {
|
|
1421
1419
|
const version = profile.version ? ` (${profile.version})` : "";
|
|
1422
1420
|
const matchingSource = sources.find((s) => profile.source.includes(s.url) || profile.source.includes(s.name));
|
|
1423
1421
|
const sourceName = matchingSource ? ` [${matchingSource.name}]` : "";
|
|
1424
|
-
|
|
1422
|
+
R.info(` ${profile.source}${version}${sourceName}`);
|
|
1425
1423
|
}
|
|
1426
1424
|
if (manifest.profiles.length > 0) {
|
|
1427
1425
|
const aiTools = await getGlobalAiTools();
|
|
@@ -1432,48 +1430,48 @@ async function showOverview(cwd) {
|
|
|
1432
1430
|
idePlatforms
|
|
1433
1431
|
};
|
|
1434
1432
|
console.log("");
|
|
1435
|
-
|
|
1433
|
+
R.step("Tool Intersection");
|
|
1436
1434
|
for (const profile of manifest.profiles) try {
|
|
1437
1435
|
const intersection = await buildIntersection(profile.source, developerTools, cwd);
|
|
1438
1436
|
if (intersection) {
|
|
1439
1437
|
const summary = formatIntersectionSummary(intersection);
|
|
1440
|
-
|
|
1438
|
+
R.info(` ${profile.source}: ${summary}`);
|
|
1441
1439
|
displayIntersection(intersection);
|
|
1442
1440
|
}
|
|
1443
1441
|
} catch {}
|
|
1444
1442
|
}
|
|
1445
1443
|
}
|
|
1446
1444
|
console.log("");
|
|
1447
|
-
|
|
1448
|
-
if (synced)
|
|
1449
|
-
else
|
|
1445
|
+
R.step("Sync Status");
|
|
1446
|
+
if (synced) R.info(" Synced (baton.lock exists)");
|
|
1447
|
+
else R.info(" Not synced — run 'baton sync' to sync profiles");
|
|
1450
1448
|
console.log("");
|
|
1451
|
-
|
|
1452
|
-
if (sources.length === 0)
|
|
1449
|
+
R.step("Global Sources");
|
|
1450
|
+
if (sources.length === 0) R.info(" No sources configured. Run: baton source connect <url>");
|
|
1453
1451
|
else for (const source of sources) {
|
|
1454
1452
|
const defaultBadge = source.default ? " (default)" : "";
|
|
1455
|
-
|
|
1453
|
+
R.info(` ${source.name}${defaultBadge}: ${source.url}`);
|
|
1456
1454
|
}
|
|
1457
1455
|
}
|
|
1458
1456
|
async function handleAddProfile(cwd) {
|
|
1459
1457
|
const manifestPath = join(cwd, "baton.yaml");
|
|
1460
1458
|
const manifest = await loadProjectManifestSafe(cwd);
|
|
1461
1459
|
if (!manifest) {
|
|
1462
|
-
|
|
1460
|
+
R.error("Could not load baton.yaml");
|
|
1463
1461
|
return;
|
|
1464
1462
|
}
|
|
1465
1463
|
const globalSources = await getGlobalSources();
|
|
1466
1464
|
if (globalSources.length === 0) {
|
|
1467
|
-
|
|
1465
|
+
R.warn("No global sources configured. Run: baton source connect <url>");
|
|
1468
1466
|
return;
|
|
1469
1467
|
}
|
|
1470
1468
|
let sourceString;
|
|
1471
1469
|
if (globalSources.length === 1) {
|
|
1472
1470
|
sourceString = globalSources[0].url;
|
|
1473
|
-
|
|
1471
|
+
R.info(`Using source: ${globalSources[0].name} (${sourceString})`);
|
|
1474
1472
|
} else {
|
|
1475
1473
|
const defaultSource = await getDefaultGlobalSource();
|
|
1476
|
-
const selectedUrl = await
|
|
1474
|
+
const selectedUrl = await Je({
|
|
1477
1475
|
message: "Select a source repository:",
|
|
1478
1476
|
options: globalSources.map((s) => ({
|
|
1479
1477
|
value: s.url,
|
|
@@ -1482,26 +1480,26 @@ async function handleAddProfile(cwd) {
|
|
|
1482
1480
|
})),
|
|
1483
1481
|
initialValue: defaultSource?.url
|
|
1484
1482
|
});
|
|
1485
|
-
if (
|
|
1486
|
-
|
|
1483
|
+
if (Ct(selectedUrl)) {
|
|
1484
|
+
R.warn("Cancelled.");
|
|
1487
1485
|
return;
|
|
1488
1486
|
}
|
|
1489
1487
|
sourceString = selectedUrl;
|
|
1490
1488
|
}
|
|
1491
1489
|
const selectedSource = await selectProfileFromSource(sourceString);
|
|
1492
1490
|
if (manifest.profiles.some((pr) => pr.source === selectedSource)) {
|
|
1493
|
-
|
|
1491
|
+
R.warn(`Profile "${selectedSource}" is already installed.`);
|
|
1494
1492
|
return;
|
|
1495
1493
|
}
|
|
1496
1494
|
manifest.profiles.push({ source: selectedSource });
|
|
1497
|
-
await writeFile(manifestPath, stringify(manifest), "utf-8");
|
|
1498
|
-
|
|
1499
|
-
const shouldSync = await
|
|
1495
|
+
await writeFile(manifestPath, (0, import_dist.stringify)(manifest), "utf-8");
|
|
1496
|
+
R.success(`Added profile: ${selectedSource}`);
|
|
1497
|
+
const shouldSync = await Re({
|
|
1500
1498
|
message: "Sync profiles now?",
|
|
1501
1499
|
initialValue: true
|
|
1502
1500
|
});
|
|
1503
|
-
if (
|
|
1504
|
-
|
|
1501
|
+
if (Ct(shouldSync) || !shouldSync) {
|
|
1502
|
+
R.info("Run 'baton sync' later to apply the new profile.");
|
|
1505
1503
|
return;
|
|
1506
1504
|
}
|
|
1507
1505
|
await runBatonSync(cwd);
|
|
@@ -1510,14 +1508,14 @@ async function handleRemoveProfile(cwd) {
|
|
|
1510
1508
|
const manifestPath = join(cwd, "baton.yaml");
|
|
1511
1509
|
const manifest = await loadProjectManifestSafe(cwd);
|
|
1512
1510
|
if (!manifest) {
|
|
1513
|
-
|
|
1511
|
+
R.error("Could not load baton.yaml");
|
|
1514
1512
|
return;
|
|
1515
1513
|
}
|
|
1516
1514
|
if (manifest.profiles.length === 0) {
|
|
1517
|
-
|
|
1515
|
+
R.warn("No profiles installed.");
|
|
1518
1516
|
return;
|
|
1519
1517
|
}
|
|
1520
|
-
const selected = await
|
|
1518
|
+
const selected = await Je({
|
|
1521
1519
|
message: "Which profile do you want to remove?",
|
|
1522
1520
|
options: manifest.profiles.map((pr) => ({
|
|
1523
1521
|
value: pr.source,
|
|
@@ -1525,42 +1523,42 @@ async function handleRemoveProfile(cwd) {
|
|
|
1525
1523
|
hint: pr.version ? `v${pr.version}` : void 0
|
|
1526
1524
|
}))
|
|
1527
1525
|
});
|
|
1528
|
-
if (
|
|
1529
|
-
|
|
1526
|
+
if (Ct(selected)) {
|
|
1527
|
+
R.warn("Cancelled.");
|
|
1530
1528
|
return;
|
|
1531
1529
|
}
|
|
1532
1530
|
const profileSource = selected;
|
|
1533
|
-
const confirmed = await
|
|
1531
|
+
const confirmed = await Re({
|
|
1534
1532
|
message: `Remove profile "${profileSource}"?`,
|
|
1535
1533
|
initialValue: false
|
|
1536
1534
|
});
|
|
1537
|
-
if (
|
|
1538
|
-
|
|
1535
|
+
if (Ct(confirmed) || !confirmed) {
|
|
1536
|
+
R.warn("Cancelled.");
|
|
1539
1537
|
return;
|
|
1540
1538
|
}
|
|
1541
1539
|
const profileIndex = manifest.profiles.findIndex((pr) => pr.source === profileSource);
|
|
1542
1540
|
manifest.profiles.splice(profileIndex, 1);
|
|
1543
|
-
await writeFile(manifestPath, stringify(manifest), "utf-8");
|
|
1544
|
-
|
|
1545
|
-
|
|
1541
|
+
await writeFile(manifestPath, (0, import_dist.stringify)(manifest), "utf-8");
|
|
1542
|
+
R.success(`Removed profile: ${profileSource}`);
|
|
1543
|
+
R.info("Run 'baton sync' to clean up synced files.");
|
|
1546
1544
|
}
|
|
1547
1545
|
async function handleRemoveBaton(cwd) {
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
const confirmed = await
|
|
1546
|
+
R.warn("This will remove Baton from your project:");
|
|
1547
|
+
R.info(" - baton.yaml (project manifest)");
|
|
1548
|
+
R.info(" - baton.lock (lockfile)");
|
|
1549
|
+
const confirmed = await Re({
|
|
1552
1550
|
message: "Are you sure you want to remove Baton from this project?",
|
|
1553
1551
|
initialValue: false
|
|
1554
1552
|
});
|
|
1555
|
-
if (
|
|
1556
|
-
|
|
1553
|
+
if (Ct(confirmed) || !confirmed) {
|
|
1554
|
+
R.warn("Cancelled.");
|
|
1557
1555
|
return false;
|
|
1558
1556
|
}
|
|
1559
1557
|
await rm(join(cwd, "baton.yaml"), { force: true });
|
|
1560
1558
|
await rm(join(cwd, "baton.lock"), { force: true });
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1559
|
+
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.");
|
|
1564
1562
|
return true;
|
|
1565
1563
|
}
|
|
1566
1564
|
const manageCommand = defineCommand({
|
|
@@ -1571,13 +1569,13 @@ const manageCommand = defineCommand({
|
|
|
1571
1569
|
async run() {
|
|
1572
1570
|
const cwd = process.cwd();
|
|
1573
1571
|
if (!await loadProjectManifestSafe(cwd)) {
|
|
1574
|
-
|
|
1575
|
-
|
|
1572
|
+
We("Baton Manage");
|
|
1573
|
+
Ne("baton.yaml not found. Run 'baton init' first.");
|
|
1576
1574
|
process.exit(1);
|
|
1577
1575
|
}
|
|
1578
|
-
|
|
1576
|
+
We("Baton Manage");
|
|
1579
1577
|
while (true) {
|
|
1580
|
-
const action = await
|
|
1578
|
+
const action = await Je({
|
|
1581
1579
|
message: "What would you like to do?",
|
|
1582
1580
|
options: [
|
|
1583
1581
|
{
|
|
@@ -1606,8 +1604,8 @@ const manageCommand = defineCommand({
|
|
|
1606
1604
|
}
|
|
1607
1605
|
]
|
|
1608
1606
|
});
|
|
1609
|
-
if (
|
|
1610
|
-
|
|
1607
|
+
if (Ct(action) || action === "quit") {
|
|
1608
|
+
Le("Goodbye!");
|
|
1611
1609
|
return;
|
|
1612
1610
|
}
|
|
1613
1611
|
if (action === "overview") {
|
|
@@ -1625,7 +1623,7 @@ const manageCommand = defineCommand({
|
|
|
1625
1623
|
} else if (action === "remove-baton") {
|
|
1626
1624
|
console.log("");
|
|
1627
1625
|
if (await handleRemoveBaton(cwd)) {
|
|
1628
|
-
|
|
1626
|
+
Le("Goodbye!");
|
|
1629
1627
|
return;
|
|
1630
1628
|
}
|
|
1631
1629
|
console.log("");
|
|
@@ -1642,9 +1640,9 @@ const profileCommand = defineCommand({
|
|
|
1642
1640
|
description: "Manage profiles (create, list, remove)"
|
|
1643
1641
|
},
|
|
1644
1642
|
subCommands: {
|
|
1645
|
-
create: () => import("./create-
|
|
1646
|
-
list: () => import("./list-
|
|
1647
|
-
remove: () => import("./remove-
|
|
1643
|
+
create: () => import("./create-Diqnd3ci.mjs").then((m) => m.createCommand),
|
|
1644
|
+
list: () => import("./list-B5xUVBTU.mjs").then((m) => m.profileListCommand),
|
|
1645
|
+
remove: () => import("./remove-6S8F9xcE.mjs").then((m) => m.profileRemoveCommand)
|
|
1648
1646
|
}
|
|
1649
1647
|
});
|
|
1650
1648
|
|
|
@@ -1680,14 +1678,14 @@ const connectCommand = defineCommand({
|
|
|
1680
1678
|
const url = args.url;
|
|
1681
1679
|
const customName = args.name;
|
|
1682
1680
|
if (customName && !KEBAB_CASE_REGEX.test(customName)) {
|
|
1683
|
-
|
|
1681
|
+
Ne("Source name must be kebab-case (e.g., my-source)");
|
|
1684
1682
|
process.exit(1);
|
|
1685
1683
|
}
|
|
1686
1684
|
try {
|
|
1687
1685
|
parseSource(url);
|
|
1688
1686
|
} catch (error) {
|
|
1689
1687
|
const message = error instanceof SourceParseError ? error.message : `Invalid source: ${error.message}`;
|
|
1690
|
-
|
|
1688
|
+
Ne(message);
|
|
1691
1689
|
process.exit(1);
|
|
1692
1690
|
}
|
|
1693
1691
|
try {
|
|
@@ -1696,20 +1694,20 @@ const connectCommand = defineCommand({
|
|
|
1696
1694
|
description: args.description
|
|
1697
1695
|
});
|
|
1698
1696
|
const displayName = args.name || url;
|
|
1699
|
-
|
|
1700
|
-
const shouldSync = await
|
|
1701
|
-
if (
|
|
1702
|
-
|
|
1697
|
+
R.success(`Connected source: ${displayName}`);
|
|
1698
|
+
const shouldSync = await Re({ message: "Would you like to sync profiles from this source now?" });
|
|
1699
|
+
if (Ct(shouldSync) || !shouldSync) {
|
|
1700
|
+
Le("Source connected. Run 'baton init' to set up profiles.");
|
|
1703
1701
|
return;
|
|
1704
1702
|
}
|
|
1705
|
-
|
|
1703
|
+
Le("Starting profile sync...");
|
|
1706
1704
|
const profiles = await selectMultipleProfilesFromSource(url);
|
|
1707
1705
|
if (profiles.length > 0) {
|
|
1708
|
-
|
|
1709
|
-
|
|
1706
|
+
R.success(`Selected ${profiles.length} profile(s) for sync.`);
|
|
1707
|
+
Ve("Run 'baton init' in your project directory to install these profiles.", "Next step");
|
|
1710
1708
|
}
|
|
1711
1709
|
} catch (error) {
|
|
1712
|
-
|
|
1710
|
+
Ne(`Failed to connect source: ${error.message}`);
|
|
1713
1711
|
process.exit(1);
|
|
1714
1712
|
}
|
|
1715
1713
|
}
|
|
@@ -1717,13 +1715,14 @@ const connectCommand = defineCommand({
|
|
|
1717
1715
|
|
|
1718
1716
|
//#endregion
|
|
1719
1717
|
//#region src/commands/source/create.ts
|
|
1718
|
+
var import_lib = /* @__PURE__ */ __toESM(require_lib(), 1);
|
|
1720
1719
|
const __dirname$1 = dirname(fileURLToPath(import.meta.url));
|
|
1721
1720
|
async function runInteractiveWizard(overrides = {}) {
|
|
1722
|
-
|
|
1721
|
+
We("Create a new Baton source repository");
|
|
1723
1722
|
let name;
|
|
1724
1723
|
if (overrides.name) name = overrides.name;
|
|
1725
1724
|
else {
|
|
1726
|
-
const result = await
|
|
1725
|
+
const result = await Ze({
|
|
1727
1726
|
message: "What is the name of your source repository?",
|
|
1728
1727
|
placeholder: "my-team-profile",
|
|
1729
1728
|
validate: (value) => {
|
|
@@ -1731,8 +1730,8 @@ async function runInteractiveWizard(overrides = {}) {
|
|
|
1731
1730
|
if (!KEBAB_CASE_REGEX.test(value)) return "Name must be in kebab-case (lowercase, hyphens only)";
|
|
1732
1731
|
}
|
|
1733
1732
|
});
|
|
1734
|
-
if (
|
|
1735
|
-
|
|
1733
|
+
if (Ct(result)) {
|
|
1734
|
+
Ne("Operation cancelled.");
|
|
1736
1735
|
process.exit(0);
|
|
1737
1736
|
}
|
|
1738
1737
|
name = String(result);
|
|
@@ -1740,12 +1739,12 @@ async function runInteractiveWizard(overrides = {}) {
|
|
|
1740
1739
|
let git;
|
|
1741
1740
|
if (overrides.git !== void 0) git = overrides.git;
|
|
1742
1741
|
else {
|
|
1743
|
-
const result = await
|
|
1742
|
+
const result = await Re({
|
|
1744
1743
|
message: "Initialize Git repository?",
|
|
1745
1744
|
initialValue: true
|
|
1746
1745
|
});
|
|
1747
|
-
if (
|
|
1748
|
-
|
|
1746
|
+
if (Ct(result)) {
|
|
1747
|
+
Ne("Operation cancelled.");
|
|
1749
1748
|
process.exit(0);
|
|
1750
1749
|
}
|
|
1751
1750
|
git = result;
|
|
@@ -1753,12 +1752,12 @@ async function runInteractiveWizard(overrides = {}) {
|
|
|
1753
1752
|
let withInitialProfile;
|
|
1754
1753
|
if (overrides.withInitialProfile !== void 0) withInitialProfile = overrides.withInitialProfile;
|
|
1755
1754
|
else {
|
|
1756
|
-
const result = await
|
|
1755
|
+
const result = await Re({
|
|
1757
1756
|
message: "Create initial profile in profiles/default/?",
|
|
1758
1757
|
initialValue: true
|
|
1759
1758
|
});
|
|
1760
|
-
if (
|
|
1761
|
-
|
|
1759
|
+
if (Ct(result)) {
|
|
1760
|
+
Ne("Operation cancelled.");
|
|
1762
1761
|
process.exit(0);
|
|
1763
1762
|
}
|
|
1764
1763
|
withInitialProfile = result;
|
|
@@ -1792,7 +1791,7 @@ async function copyDirectory(src, dest, variables) {
|
|
|
1792
1791
|
".ttf",
|
|
1793
1792
|
".eot"
|
|
1794
1793
|
]).has(entry.name.substring(entry.name.lastIndexOf(".")))) await writeFile(destPath, content);
|
|
1795
|
-
else await writeFile(destPath,
|
|
1794
|
+
else await writeFile(destPath, import_lib.default.compile(content, { noEscape: true })(variables));
|
|
1796
1795
|
}
|
|
1797
1796
|
}
|
|
1798
1797
|
}
|
|
@@ -1800,7 +1799,7 @@ async function copyDirectory(src, dest, variables) {
|
|
|
1800
1799
|
* Initialize Git repository and create initial commit
|
|
1801
1800
|
*/
|
|
1802
1801
|
async function initializeGit(targetDir) {
|
|
1803
|
-
const git =
|
|
1802
|
+
const git = esm_default(targetDir);
|
|
1804
1803
|
await git.init();
|
|
1805
1804
|
await git.add(".");
|
|
1806
1805
|
await git.commit("Initial baton source setup");
|
|
@@ -1929,7 +1928,7 @@ const sourceCreateCommand = defineCommand({
|
|
|
1929
1928
|
overrides.withInitialProfile = false;
|
|
1930
1929
|
}
|
|
1931
1930
|
const options = await runInteractiveWizard(overrides);
|
|
1932
|
-
const spinner =
|
|
1931
|
+
const spinner = bt();
|
|
1933
1932
|
spinner.start("Creating source repository...");
|
|
1934
1933
|
try {
|
|
1935
1934
|
const targetDir = await scaffoldSourceRepo(options);
|
|
@@ -1937,7 +1936,7 @@ const sourceCreateCommand = defineCommand({
|
|
|
1937
1936
|
const features = [];
|
|
1938
1937
|
if (options.withInitialProfile) features.push("Initial Profile: profiles/default/");
|
|
1939
1938
|
if (options.git) features.push("Git: Initialized with initial commit");
|
|
1940
|
-
if (features.length > 0)
|
|
1939
|
+
if (features.length > 0) Ve(features.join("\n"), "Features");
|
|
1941
1940
|
const org = options.name.includes("-") ? options.name.split("-")[0] : options.name;
|
|
1942
1941
|
const nextSteps = [];
|
|
1943
1942
|
nextSteps.push(` cd ${options.name}`);
|
|
@@ -1949,7 +1948,7 @@ const sourceCreateCommand = defineCommand({
|
|
|
1949
1948
|
nextSteps.push("");
|
|
1950
1949
|
nextSteps.push(" # Share with your team:");
|
|
1951
1950
|
nextSteps.push(` baton source connect https://github.com/${org}/${options.name}.git`);
|
|
1952
|
-
|
|
1951
|
+
Le(`Source repository "${options.name}" created successfully!\n\nNext steps:\n${nextSteps.join("\n")}`);
|
|
1953
1952
|
} catch (error) {
|
|
1954
1953
|
spinner.stop("Failed to create source repository");
|
|
1955
1954
|
throw error;
|
|
@@ -1979,24 +1978,24 @@ const disconnectCommand = defineCommand({
|
|
|
1979
1978
|
const sourceIdentifier = args.source;
|
|
1980
1979
|
const matchedSource = (await getGlobalSources()).find((s) => s.name === sourceIdentifier || s.url === sourceIdentifier);
|
|
1981
1980
|
if (!matchedSource) {
|
|
1982
|
-
|
|
1981
|
+
Ne(`Source "${sourceIdentifier}" not found in global configuration.`);
|
|
1983
1982
|
process.exit(1);
|
|
1984
1983
|
}
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
const confirmed = await
|
|
1984
|
+
R.warn(`Disconnecting source "${matchedSource.name}" (${matchedSource.url}) will affect any projects using profiles from this source.`);
|
|
1985
|
+
R.info("Projects that reference this source will no longer be able to sync or update their profiles.");
|
|
1986
|
+
const confirmed = await Re({
|
|
1988
1987
|
message: `Are you sure you want to disconnect source "${matchedSource.name}"?`,
|
|
1989
1988
|
initialValue: false
|
|
1990
1989
|
});
|
|
1991
|
-
if (
|
|
1992
|
-
|
|
1990
|
+
if (Ct(confirmed) || !confirmed) {
|
|
1991
|
+
Ne("Operation cancelled.");
|
|
1993
1992
|
process.exit(0);
|
|
1994
1993
|
}
|
|
1995
1994
|
try {
|
|
1996
1995
|
await removeGlobalSource(sourceIdentifier);
|
|
1997
|
-
|
|
1996
|
+
Le(`Disconnected source: ${matchedSource.name}`);
|
|
1998
1997
|
} catch (error) {
|
|
1999
|
-
|
|
1998
|
+
Ne(`Failed to disconnect source: ${error.message}`);
|
|
2000
1999
|
process.exit(1);
|
|
2001
2000
|
}
|
|
2002
2001
|
}
|
|
@@ -2017,8 +2016,8 @@ const listCommand = defineCommand({
|
|
|
2017
2016
|
async run() {
|
|
2018
2017
|
const sources = await getGlobalSources();
|
|
2019
2018
|
if (sources.length === 0) {
|
|
2020
|
-
|
|
2021
|
-
|
|
2019
|
+
R.info("No global sources configured.");
|
|
2020
|
+
Ve("Add a source with:\n baton source connect <url>", "Tip");
|
|
2022
2021
|
return;
|
|
2023
2022
|
}
|
|
2024
2023
|
console.log("\n🌐 Global Sources\n");
|
|
@@ -2160,25 +2159,25 @@ async function cleanupOrphanedFiles(params) {
|
|
|
2160
2159
|
const orphanedPaths = [...previousPaths].filter((prev) => !currentPaths.has(prev));
|
|
2161
2160
|
if (orphanedPaths.length === 0) return;
|
|
2162
2161
|
if (dryRun) {
|
|
2163
|
-
|
|
2164
|
-
for (const orphanedPath of orphanedPaths)
|
|
2162
|
+
R.warn(`Would remove ${orphanedPaths.length} orphaned file(s):`);
|
|
2163
|
+
for (const orphanedPath of orphanedPaths) R.info(` Removed: ${orphanedPath}`);
|
|
2165
2164
|
return;
|
|
2166
2165
|
}
|
|
2167
|
-
|
|
2168
|
-
for (const orphanedPath of orphanedPaths)
|
|
2166
|
+
R.warn(`Found ${orphanedPaths.length} orphaned file(s) to remove:`);
|
|
2167
|
+
for (const orphanedPath of orphanedPaths) R.info(` Removed: ${orphanedPath}`);
|
|
2169
2168
|
let shouldRemove = autoYes;
|
|
2170
2169
|
if (!autoYes) {
|
|
2171
|
-
const confirmed = await
|
|
2170
|
+
const confirmed = await Re({
|
|
2172
2171
|
message: `Remove ${orphanedPaths.length} orphaned file(s)?`,
|
|
2173
2172
|
initialValue: true
|
|
2174
2173
|
});
|
|
2175
|
-
if (
|
|
2176
|
-
|
|
2174
|
+
if (Ct(confirmed)) {
|
|
2175
|
+
R.info("Skipped orphan removal.");
|
|
2177
2176
|
shouldRemove = false;
|
|
2178
2177
|
} else shouldRemove = confirmed;
|
|
2179
2178
|
}
|
|
2180
2179
|
if (!shouldRemove) {
|
|
2181
|
-
|
|
2180
|
+
R.info("Orphan removal skipped.");
|
|
2182
2181
|
return;
|
|
2183
2182
|
}
|
|
2184
2183
|
spinner.start("Removing orphaned files...");
|
|
@@ -2237,7 +2236,7 @@ const syncCommand = defineCommand({
|
|
|
2237
2236
|
let category;
|
|
2238
2237
|
if (categoryArg) {
|
|
2239
2238
|
if (!validCategories.includes(categoryArg)) {
|
|
2240
|
-
|
|
2239
|
+
Ne(`Invalid category "${categoryArg}". Valid categories: ${validCategories.join(", ")}`);
|
|
2241
2240
|
process.exit(1);
|
|
2242
2241
|
}
|
|
2243
2242
|
category = categoryArg;
|
|
@@ -2245,7 +2244,7 @@ const syncCommand = defineCommand({
|
|
|
2245
2244
|
const syncAi = !category || category === "ai";
|
|
2246
2245
|
const syncFiles = !category || category === "files";
|
|
2247
2246
|
const syncIde = !category || category === "ide";
|
|
2248
|
-
|
|
2247
|
+
We(category ? `🔄 Baton Sync (category: ${category})` : "🔄 Baton Sync");
|
|
2249
2248
|
const stats = {
|
|
2250
2249
|
created: 0,
|
|
2251
2250
|
errors: 0
|
|
@@ -2257,8 +2256,8 @@ const syncCommand = defineCommand({
|
|
|
2257
2256
|
try {
|
|
2258
2257
|
projectManifest = await loadProjectManifest(manifestPath);
|
|
2259
2258
|
} catch (error) {
|
|
2260
|
-
if (error instanceof FileNotFoundError)
|
|
2261
|
-
else
|
|
2259
|
+
if (error instanceof FileNotFoundError) Ne("baton.yaml not found. Run `baton init` first.");
|
|
2260
|
+
else Ne(`Failed to load baton.yaml: ${error instanceof Error ? error.message : String(error)}`);
|
|
2262
2261
|
process.exit(1);
|
|
2263
2262
|
}
|
|
2264
2263
|
const previousPaths = /* @__PURE__ */ new Set();
|
|
@@ -2266,19 +2265,19 @@ const syncCommand = defineCommand({
|
|
|
2266
2265
|
const previousLock = await readLock(resolve(projectRoot, "baton.lock"));
|
|
2267
2266
|
for (const pkg of Object.values(previousLock.packages)) for (const filePath of Object.keys(pkg.integrity)) previousPaths.add(filePath);
|
|
2268
2267
|
} catch {}
|
|
2269
|
-
const spinner =
|
|
2268
|
+
const spinner = bt();
|
|
2270
2269
|
spinner.start("Resolving profile chain...");
|
|
2271
2270
|
const allProfiles = [];
|
|
2272
2271
|
const sourceShas = /* @__PURE__ */ new Map();
|
|
2273
2272
|
for (const profileSource of projectManifest.profiles || []) try {
|
|
2274
|
-
if (verbose)
|
|
2273
|
+
if (verbose) R.info(`Resolving source: ${profileSource.source}`);
|
|
2275
2274
|
const parsed = parseSource(profileSource.source);
|
|
2276
2275
|
let manifestPath;
|
|
2277
2276
|
if (parsed.provider === "local" || parsed.provider === "file") {
|
|
2278
2277
|
const absolutePath = parsed.path.startsWith("/") ? parsed.path : resolve(projectRoot, parsed.path);
|
|
2279
2278
|
manifestPath = resolve(absolutePath, "baton.profile.yaml");
|
|
2280
2279
|
try {
|
|
2281
|
-
const git =
|
|
2280
|
+
const git = esm_default(absolutePath);
|
|
2282
2281
|
await git.checkIsRepo();
|
|
2283
2282
|
const sha = await git.revparse(["HEAD"]);
|
|
2284
2283
|
sourceShas.set(profileSource.source, sha.trim());
|
|
@@ -2307,7 +2306,7 @@ const syncCommand = defineCommand({
|
|
|
2307
2306
|
}
|
|
2308
2307
|
if (allProfiles.length === 0) {
|
|
2309
2308
|
spinner.stop("No profiles configured");
|
|
2310
|
-
|
|
2309
|
+
Le("Nothing to sync. Run `baton manage` to add a profile.");
|
|
2311
2310
|
process.exit(2);
|
|
2312
2311
|
}
|
|
2313
2312
|
spinner.stop(`Resolved ${allProfiles.length} profile(s)`);
|
|
@@ -2389,7 +2388,7 @@ const syncCommand = defineCommand({
|
|
|
2389
2388
|
if (!files) continue;
|
|
2390
2389
|
const targetDir = getIdePlatformTargetDir(ideKey);
|
|
2391
2390
|
if (!targetDir) {
|
|
2392
|
-
if (!isKnownIdePlatform(ideKey))
|
|
2391
|
+
if (!isKnownIdePlatform(ideKey)) R.warn(`Unknown IDE platform "${ideKey}" in profile "${profile.name}" — skipping. Register it in the IDE platform registry.`);
|
|
2393
2392
|
continue;
|
|
2394
2393
|
}
|
|
2395
2394
|
for (const fileName of files) {
|
|
@@ -2419,7 +2418,7 @@ const syncCommand = defineCommand({
|
|
|
2419
2418
|
}
|
|
2420
2419
|
const mergedIdeCount = ideMap.size;
|
|
2421
2420
|
spinner.stop(`Merged: ${mergedSkills.length} skills, ${mergedRules.length} rules, ${mergedMemory.length} memory files, ${mergedCommandCount} commands, ${mergedFileCount} files, ${mergedIdeCount} IDE configs`);
|
|
2422
|
-
if (allWeightWarnings.length > 0) for (const w of allWeightWarnings)
|
|
2421
|
+
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.`);
|
|
2423
2422
|
spinner.start("Computing tool intersection...");
|
|
2424
2423
|
const globalAiTools = await getGlobalAiTools();
|
|
2425
2424
|
const globalIdePlatforms = await getGlobalIdePlatforms();
|
|
@@ -2449,26 +2448,26 @@ const syncCommand = defineCommand({
|
|
|
2449
2448
|
syncedAiTools = detectedAgents;
|
|
2450
2449
|
syncedIdePlatforms = null;
|
|
2451
2450
|
if (detectedAgents.length > 0) {
|
|
2452
|
-
|
|
2453
|
-
|
|
2451
|
+
R.warn("No AI tools configured. Run `baton ai-tools scan` to configure your tools.");
|
|
2452
|
+
R.info(`Falling back to detected tools: ${detectedAgents.join(", ")}`);
|
|
2454
2453
|
}
|
|
2455
2454
|
}
|
|
2456
2455
|
if (syncedAiTools.length === 0 && detectedAgents.length === 0) {
|
|
2457
2456
|
spinner.stop("No AI tools available");
|
|
2458
|
-
|
|
2457
|
+
Ne("No AI tools found. Install an AI coding tool first.");
|
|
2459
2458
|
process.exit(1);
|
|
2460
2459
|
}
|
|
2461
2460
|
if (syncedAiTools.length === 0) {
|
|
2462
2461
|
spinner.stop("No AI tools in intersection");
|
|
2463
|
-
|
|
2462
|
+
Ne("No AI tools match between your configuration and profile support. Run `baton ai-tools scan` or check your profile's supported tools.");
|
|
2464
2463
|
process.exit(1);
|
|
2465
2464
|
}
|
|
2466
2465
|
if (allIntersections) for (const [source, intersection] of allIntersections) if (verbose) {
|
|
2467
|
-
|
|
2466
|
+
R.step(`Intersection for ${source}`);
|
|
2468
2467
|
displayIntersection(intersection);
|
|
2469
2468
|
} else {
|
|
2470
2469
|
const summary = formatIntersectionSummary(intersection);
|
|
2471
|
-
|
|
2470
|
+
R.info(`Syncing for: ${summary}`);
|
|
2472
2471
|
}
|
|
2473
2472
|
const ideSummary = syncedIdePlatforms && syncedIdePlatforms.length > 0 ? ` | IDE platforms: ${syncedIdePlatforms.join(", ")}` : "";
|
|
2474
2473
|
spinner.stop(`Syncing AI tools: ${syncedAiTools.join(", ")}${ideSummary}`);
|
|
@@ -2477,8 +2476,8 @@ const syncCommand = defineCommand({
|
|
|
2477
2476
|
if (legacyFiles.length > 0 && !dryRun) {
|
|
2478
2477
|
spinner.stop(`Found ${legacyFiles.length} legacy file(s)`);
|
|
2479
2478
|
if (!autoYes) {
|
|
2480
|
-
|
|
2481
|
-
|
|
2479
|
+
Ve(`Found legacy configuration files:\n${legacyFiles.map((f) => ` - ${f.legacyPath}`).join("\n")}`, "Legacy Files");
|
|
2480
|
+
R.warn("Run migration manually with appropriate action (migrate/copy/skip)");
|
|
2482
2481
|
}
|
|
2483
2482
|
} else spinner.stop("No legacy files found");
|
|
2484
2483
|
spinner.start("Processing configurations...");
|
|
@@ -2506,7 +2505,7 @@ const syncCommand = defineCommand({
|
|
|
2506
2505
|
}
|
|
2507
2506
|
const contentAccumulator = /* @__PURE__ */ new Map();
|
|
2508
2507
|
if (!dryRun && syncAi) for (const adapter of adapters) {
|
|
2509
|
-
if (verbose)
|
|
2508
|
+
if (verbose) R.step(`[${adapter.key}] Placing memory files...`);
|
|
2510
2509
|
for (const memoryEntry of mergedMemory) try {
|
|
2511
2510
|
const contentParts = [];
|
|
2512
2511
|
for (const contribution of memoryEntry.contributions) {
|
|
@@ -2552,7 +2551,7 @@ const syncCommand = defineCommand({
|
|
|
2552
2551
|
}
|
|
2553
2552
|
}
|
|
2554
2553
|
if (!dryRun && syncAi) for (const adapter of adapters) {
|
|
2555
|
-
if (verbose)
|
|
2554
|
+
if (verbose) R.step(`[${adapter.key}] Placing skills...`);
|
|
2556
2555
|
for (const skillItem of mergedSkills) try {
|
|
2557
2556
|
const profileDir = profileLocalPaths.get(skillItem.profileName);
|
|
2558
2557
|
if (!profileDir) {
|
|
@@ -2586,7 +2585,7 @@ const syncCommand = defineCommand({
|
|
|
2586
2585
|
}
|
|
2587
2586
|
if (verbose) {
|
|
2588
2587
|
const label = placed > 0 ? `${placed} file(s) created` : "unchanged, skipped";
|
|
2589
|
-
|
|
2588
|
+
R.info(` -> ${absoluteTargetDir}/ (${label})`);
|
|
2590
2589
|
}
|
|
2591
2590
|
} catch (error) {
|
|
2592
2591
|
spinner.message(`Error placing skill ${skillItem.name} for ${adapter.name}: ${error}`);
|
|
@@ -2594,7 +2593,7 @@ const syncCommand = defineCommand({
|
|
|
2594
2593
|
}
|
|
2595
2594
|
}
|
|
2596
2595
|
if (!dryRun && syncAi) for (const adapter of adapters) {
|
|
2597
|
-
if (verbose)
|
|
2596
|
+
if (verbose) R.step(`[${adapter.key}] Placing rules...`);
|
|
2598
2597
|
for (const ruleEntry of mergedRules) try {
|
|
2599
2598
|
const isUniversal = ruleEntry.agents.length === 0;
|
|
2600
2599
|
const isForThisAdapter = ruleEntry.agents.includes(adapter.key);
|
|
@@ -2651,14 +2650,14 @@ const syncCommand = defineCommand({
|
|
|
2651
2650
|
}
|
|
2652
2651
|
if (verbose) {
|
|
2653
2652
|
const label = result.action === "skipped" ? "unchanged, skipped" : result.action;
|
|
2654
|
-
|
|
2653
|
+
R.info(` -> ${result.path} (${label})`);
|
|
2655
2654
|
}
|
|
2656
2655
|
} catch (error) {
|
|
2657
2656
|
spinner.message(`Error placing accumulated content to ${absolutePath}: ${error}`);
|
|
2658
2657
|
stats.errors++;
|
|
2659
2658
|
}
|
|
2660
2659
|
if (!dryRun && syncAi) for (const adapter of adapters) {
|
|
2661
|
-
if (verbose)
|
|
2660
|
+
if (verbose) R.step(`[${adapter.key}] Placing commands...`);
|
|
2662
2661
|
for (const profile of allProfiles) {
|
|
2663
2662
|
const profileDir = profileLocalPaths.get(profile.name);
|
|
2664
2663
|
if (!profileDir) continue;
|
|
@@ -2681,7 +2680,7 @@ const syncCommand = defineCommand({
|
|
|
2681
2680
|
};
|
|
2682
2681
|
if (verbose) {
|
|
2683
2682
|
const label = result.action === "skipped" ? "unchanged, skipped" : result.action;
|
|
2684
|
-
|
|
2683
|
+
R.info(` -> ${result.path} (${label})`);
|
|
2685
2684
|
}
|
|
2686
2685
|
} catch (error) {
|
|
2687
2686
|
spinner.message(`Error placing command ${commandName} for ${adapter.name}: ${error}`);
|
|
@@ -2704,8 +2703,8 @@ const syncCommand = defineCommand({
|
|
|
2704
2703
|
if (await readFile(targetPath, "utf-8").catch(() => void 0) !== content) {
|
|
2705
2704
|
await writeFile(targetPath, content, "utf-8");
|
|
2706
2705
|
stats.created++;
|
|
2707
|
-
if (verbose)
|
|
2708
|
-
} else if (verbose)
|
|
2706
|
+
if (verbose) R.info(` -> ${fileEntry.target} (created)`);
|
|
2707
|
+
} else if (verbose) R.info(` -> ${fileEntry.target} (unchanged, skipped)`);
|
|
2709
2708
|
const fpf = getOrCreatePlacedFiles(placedFiles, fileEntry.profileName);
|
|
2710
2709
|
fpf[fileEntry.target] = {
|
|
2711
2710
|
content,
|
|
@@ -2717,7 +2716,7 @@ const syncCommand = defineCommand({
|
|
|
2717
2716
|
}
|
|
2718
2717
|
if (!dryRun && syncIde) for (const ideEntry of ideMap.values()) try {
|
|
2719
2718
|
if (syncedIdePlatforms !== null && !syncedIdePlatforms.includes(ideEntry.ideKey)) {
|
|
2720
|
-
if (verbose)
|
|
2719
|
+
if (verbose) R.info(` -> ${ideEntry.targetDir}/${ideEntry.fileName} (skipped — IDE platform "${ideEntry.ideKey}" not in intersection)`);
|
|
2721
2720
|
continue;
|
|
2722
2721
|
}
|
|
2723
2722
|
const profileDir = profileLocalPaths.get(ideEntry.profileName);
|
|
@@ -2734,8 +2733,8 @@ const syncCommand = defineCommand({
|
|
|
2734
2733
|
if (await readFile(targetPath, "utf-8").catch(() => void 0) !== content) {
|
|
2735
2734
|
await writeFile(targetPath, content, "utf-8");
|
|
2736
2735
|
stats.created++;
|
|
2737
|
-
if (verbose)
|
|
2738
|
-
} else if (verbose)
|
|
2736
|
+
if (verbose) R.info(` -> ${ideEntry.targetDir}/${ideEntry.fileName} (created)`);
|
|
2737
|
+
} else if (verbose) R.info(` -> ${ideEntry.targetDir}/${ideEntry.fileName} (unchanged, skipped)`);
|
|
2739
2738
|
const ideRelPath = `${ideEntry.targetDir}/${ideEntry.fileName}`;
|
|
2740
2739
|
const ipf = getOrCreatePlacedFiles(placedFiles, ideEntry.profileName);
|
|
2741
2740
|
ipf[ideRelPath] = {
|
|
@@ -2789,14 +2788,14 @@ const syncCommand = defineCommand({
|
|
|
2789
2788
|
parts.push(` • ${filteredIdeCount} IDE configs`);
|
|
2790
2789
|
}
|
|
2791
2790
|
const categoryLabel = category ? ` (category: ${category})` : "";
|
|
2792
|
-
|
|
2791
|
+
Le(`[Dry Run${categoryLabel}] Would sync:\n${parts.join("\n")}\n\nFor ${adapters.length} agent(s): ${syncedAiTools.join(", ")}`);
|
|
2793
2792
|
} else {
|
|
2794
2793
|
const categoryLabel = category ? ` (category: ${category})` : "";
|
|
2795
|
-
|
|
2794
|
+
Le(`✅ Sync complete${categoryLabel}! Configurations updated.`);
|
|
2796
2795
|
}
|
|
2797
2796
|
process.exit(stats.errors > 0 ? 1 : 0);
|
|
2798
2797
|
} catch (error) {
|
|
2799
|
-
|
|
2798
|
+
Ne(`Sync failed: ${error}`);
|
|
2800
2799
|
process.exit(1);
|
|
2801
2800
|
}
|
|
2802
2801
|
}
|
|
@@ -2824,18 +2823,18 @@ const updateCommand = defineCommand({
|
|
|
2824
2823
|
async run({ args }) {
|
|
2825
2824
|
const dryRun = args["dry-run"];
|
|
2826
2825
|
const autoConfirm = args.yes;
|
|
2827
|
-
|
|
2826
|
+
We("Baton Update");
|
|
2828
2827
|
const cwd = process.cwd();
|
|
2829
2828
|
const manifestPath = resolve(cwd, "baton.yaml");
|
|
2830
2829
|
const lockfilePath = resolve(cwd, "baton.lock");
|
|
2831
|
-
const spinner =
|
|
2830
|
+
const spinner = bt();
|
|
2832
2831
|
spinner.start("Loading project configuration");
|
|
2833
2832
|
let manifest;
|
|
2834
2833
|
try {
|
|
2835
2834
|
manifest = await loadProjectManifest(manifestPath);
|
|
2836
2835
|
} catch (error) {
|
|
2837
2836
|
spinner.stop("Failed to load baton.yaml");
|
|
2838
|
-
|
|
2837
|
+
Ne(error instanceof Error ? error.message : "Could not load project manifest");
|
|
2839
2838
|
process.exit(1);
|
|
2840
2839
|
}
|
|
2841
2840
|
let lockfile = null;
|
|
@@ -2844,7 +2843,7 @@ const updateCommand = defineCommand({
|
|
|
2844
2843
|
spinner.stop("Configuration loaded");
|
|
2845
2844
|
} catch {
|
|
2846
2845
|
spinner.stop("Configuration loaded (no lockfile found)");
|
|
2847
|
-
|
|
2846
|
+
Ve("No lockfile found. Run 'baton sync' first to create one.");
|
|
2848
2847
|
}
|
|
2849
2848
|
spinner.start("Checking for updates");
|
|
2850
2849
|
const updateCandidates = [];
|
|
@@ -2865,14 +2864,14 @@ const updateCommand = defineCommand({
|
|
|
2865
2864
|
});
|
|
2866
2865
|
}
|
|
2867
2866
|
} catch (error) {
|
|
2868
|
-
if (error instanceof SourceParseError)
|
|
2867
|
+
if (error instanceof SourceParseError) R.warn(`Skipping invalid source: ${profile.source}`);
|
|
2869
2868
|
}
|
|
2870
2869
|
spinner.stop("Update check complete");
|
|
2871
2870
|
if (updateCandidates.length === 0) {
|
|
2872
|
-
|
|
2871
|
+
Le("All packages are up to date!");
|
|
2873
2872
|
process.exit(0);
|
|
2874
2873
|
}
|
|
2875
|
-
|
|
2874
|
+
Ve(`Found ${updateCandidates.length} update${updateCandidates.length > 1 ? "s" : ""}`);
|
|
2876
2875
|
for (const candidate of updateCandidates) {
|
|
2877
2876
|
console.log(`\n📦 ${candidate.name}: ${candidate.currentVersion} → ${candidate.latestVersion}`);
|
|
2878
2877
|
if (candidate.changes.length > 0) {
|
|
@@ -2881,16 +2880,16 @@ const updateCommand = defineCommand({
|
|
|
2881
2880
|
}
|
|
2882
2881
|
}
|
|
2883
2882
|
if (dryRun) {
|
|
2884
|
-
|
|
2883
|
+
Le("Dry-run mode enabled. No changes were made.\nRun 'baton update' without --dry-run to apply updates.");
|
|
2885
2884
|
process.exit(0);
|
|
2886
2885
|
}
|
|
2887
2886
|
if (!autoConfirm) {
|
|
2888
|
-
const confirmed = await
|
|
2887
|
+
const confirmed = await Re({
|
|
2889
2888
|
message: `Apply ${updateCandidates.length} update${updateCandidates.length > 1 ? "s" : ""}?`,
|
|
2890
2889
|
initialValue: true
|
|
2891
2890
|
});
|
|
2892
|
-
if (
|
|
2893
|
-
|
|
2891
|
+
if (Ct(confirmed) || !confirmed) {
|
|
2892
|
+
Ne("Update cancelled");
|
|
2894
2893
|
process.exit(0);
|
|
2895
2894
|
}
|
|
2896
2895
|
}
|
|
@@ -2901,7 +2900,7 @@ const updateCommand = defineCommand({
|
|
|
2901
2900
|
const url = parsed.provider === "github" || parsed.provider === "gitlab" ? parsed.url : parsed.provider === "git" ? parsed.url : "";
|
|
2902
2901
|
if (!url) {
|
|
2903
2902
|
spinner.stop("Update failed");
|
|
2904
|
-
|
|
2903
|
+
Ne(`Cannot update local source: ${candidate.name}`);
|
|
2905
2904
|
process.exit(1);
|
|
2906
2905
|
}
|
|
2907
2906
|
const clonedSource = await cloneGitSource({
|
|
@@ -2919,7 +2918,7 @@ const updateCommand = defineCommand({
|
|
|
2919
2918
|
};
|
|
2920
2919
|
} catch (error) {
|
|
2921
2920
|
spinner.stop("Update failed");
|
|
2922
|
-
|
|
2921
|
+
Ne(`Failed to update ${candidate.name}: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
2923
2922
|
process.exit(1);
|
|
2924
2923
|
}
|
|
2925
2924
|
const newLock = generateLock(updatedPackages);
|
|
@@ -2932,7 +2931,7 @@ const updateCommand = defineCommand({
|
|
|
2932
2931
|
await writeLock(lockfile, lockfilePath);
|
|
2933
2932
|
} else await writeLock(newLock, lockfilePath);
|
|
2934
2933
|
spinner.stop("Updates applied successfully");
|
|
2935
|
-
|
|
2934
|
+
Le(`✅ Updated ${updateCandidates.length} package${updateCandidates.length > 1 ? "s" : ""}!\n\nRun 'baton sync' to apply the updated configurations.`);
|
|
2936
2935
|
process.exit(0);
|
|
2937
2936
|
}
|
|
2938
2937
|
});
|
|
@@ -2958,7 +2957,7 @@ async function getChangeSummary(parsed, fromVersion, toVersion) {
|
|
|
2958
2957
|
ref: toVersion,
|
|
2959
2958
|
subpath: parsed.provider !== "local" && "subpath" in parsed ? parsed.subpath : void 0
|
|
2960
2959
|
});
|
|
2961
|
-
const simpleGit = (await import("
|
|
2960
|
+
const simpleGit = (await import("./esm-CuRZ1S4C.mjs")).default;
|
|
2962
2961
|
return (await simpleGit(clonedSource.localPath).log({
|
|
2963
2962
|
from: fromVersion,
|
|
2964
2963
|
to: toVersion,
|