@baton-dx/cli 0.3.0 → 0.3.2
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-BG_VVOTI.mjs → create-BkpEXaht.mjs} +2 -2
- package/dist/{create-BG_VVOTI.mjs.map → create-BkpEXaht.mjs.map} +1 -1
- package/dist/index.mjs +113 -73
- package/dist/index.mjs.map +1 -1
- package/dist/{list-CCzjta6J.mjs → list-DSLwzhBG.mjs} +2 -2
- package/dist/{list-CCzjta6J.mjs.map → list-DSLwzhBG.mjs.map} +1 -1
- package/dist/{src-BgiJfm14.mjs → src-BCGnnv5D.mjs} +130 -48
- package/dist/src-BCGnnv5D.mjs.map +1 -0
- package/package.json +1 -1
- package/dist/src-BgiJfm14.mjs.map +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { r as __toESM } from "./chunk-BbwQpWto.mjs";
|
|
3
3
|
import { a as Ne, h as defineCommand, i as Le, l as We, p as Ct, t as findSourceRoot, u as Ze } from "./context-detection-DqOTnD6_.mjs";
|
|
4
|
-
import {
|
|
4
|
+
import { _ as require_lib, tt as KEBAB_CASE_REGEX } from "./src-BCGnnv5D.mjs";
|
|
5
5
|
import "./agent-detection-DTiVeO5W.mjs";
|
|
6
6
|
import "./esm-BagM-kVd.mjs";
|
|
7
7
|
import { mkdir, readFile, readdir, writeFile } from "node:fs/promises";
|
|
@@ -79,4 +79,4 @@ async function copyProfileTemplate(sourceDir, targetDir, variables) {
|
|
|
79
79
|
|
|
80
80
|
//#endregion
|
|
81
81
|
export { createCommand };
|
|
82
|
-
//# sourceMappingURL=create-
|
|
82
|
+
//# sourceMappingURL=create-BkpEXaht.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-
|
|
1
|
+
{"version":3,"file":"create-BkpEXaht.mjs","names":["p.text","p.isCancel","Handlebars"],"sources":["../src/commands/profile/create.ts"],"sourcesContent":["import { mkdir, readFile, readdir, writeFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { KEBAB_CASE_REGEX } from \"@baton-dx/core\";\nimport * as p from \"@clack/prompts\";\nimport { defineCommand } from \"citty\";\nimport Handlebars from \"handlebars\";\nimport { findSourceRoot } from \"../../utils/context-detection.js\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\nexport const createCommand = defineCommand({\n meta: {\n name: \"create\",\n description: \"Create a new profile in your source repository\",\n },\n args: {\n name: {\n type: \"positional\",\n description: \"Profile name (kebab-case)\",\n required: false,\n },\n },\n async run({ args }) {\n p.intro(\"Create Profile\");\n\n // Check for baton.source.yaml in current or parent directories\n const sourceRoot = await findSourceRoot();\n if (!sourceRoot) {\n p.cancel(\"This command must be run inside a source directory (baton.source.yaml not found)\");\n process.exit(1);\n }\n\n // Get profile name — from argument or wizard prompt\n let name = args.name as string | undefined;\n\n if (!name) {\n const nameInput = await p.text({\n message: \"Profile name (kebab-case)\",\n placeholder: \"e.g., backend, frontend, my-profile\",\n validate(value) {\n if (!value || value.trim().length === 0) {\n return \"Profile name is required\";\n }\n if (!KEBAB_CASE_REGEX.test(value.trim())) {\n return \"Profile name must be in kebab-case (e.g., my-profile, backend, frontend)\";\n }\n },\n });\n\n if (p.isCancel(nameInput)) {\n p.cancel(\"Cancelled.\");\n process.exit(0);\n }\n\n name = (nameInput as string).trim();\n }\n\n // Validate name format (kebab-case)\n if (!KEBAB_CASE_REGEX.test(name)) {\n p.cancel(\"Profile name must be in kebab-case (e.g., my-profile, backend, frontend)\");\n process.exit(1);\n }\n\n // Check if profile already exists in profiles/ directory\n const targetDir = join(sourceRoot, \"profiles\", name);\n try {\n await readdir(targetDir);\n p.cancel(`Profile '${name}' already exists in profiles/${name}/`);\n process.exit(1);\n } catch {\n // Directory doesn't exist - good to proceed\n }\n\n // Create profile directory\n await mkdir(targetDir, { recursive: true });\n\n // Copy minimal template files\n const templateDir = join(__dirname, \"templates\", \"profile\", \"minimal\");\n await copyProfileTemplate(templateDir, targetDir, { name });\n\n p.outro(`Profile '${name}' created in profiles/${name}/`);\n },\n});\n\n/**\n * Recursively copy profile template with variable substitution\n */\nasync function copyProfileTemplate(\n sourceDir: string,\n targetDir: string,\n variables: { name: string },\n): Promise<void> {\n const entries = await readdir(sourceDir, { withFileTypes: true });\n\n for (const entry of entries) {\n const sourcePath = join(sourceDir, entry.name);\n const targetPath = join(targetDir, entry.name);\n\n if (entry.isDirectory()) {\n await mkdir(targetPath, { recursive: true });\n await copyProfileTemplate(sourcePath, targetPath, variables);\n } else {\n // Read file content\n const content = await readFile(sourcePath, \"utf-8\");\n\n // Apply Handlebars substitution for text files\n const processed = Handlebars.compile(content, { noEscape: true })(variables);\n\n // Write processed content\n await writeFile(targetPath, processed, \"utf-8\");\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;AASA,MAAM,YAAY,QAAQ,cAAc,OAAO,KAAK,IAAI,CAAC;AAEzD,MAAa,gBAAgB,cAAc;CACzC,MAAM;EACJ,MAAM;EACN,aAAa;EACd;CACD,MAAM,EACJ,MAAM;EACJ,MAAM;EACN,aAAa;EACb,UAAU;EACX,EACF;CACD,MAAM,IAAI,EAAE,QAAQ;AAClB,KAAQ,iBAAiB;EAGzB,MAAM,aAAa,MAAM,gBAAgB;AACzC,MAAI,CAAC,YAAY;AACf,MAAS,mFAAmF;AAC5F,WAAQ,KAAK,EAAE;;EAIjB,IAAI,OAAO,KAAK;AAEhB,MAAI,CAAC,MAAM;GACT,MAAM,YAAY,MAAMA,GAAO;IAC7B,SAAS;IACT,aAAa;IACb,SAAS,OAAO;AACd,SAAI,CAAC,SAAS,MAAM,MAAM,CAAC,WAAW,EACpC,QAAO;AAET,SAAI,CAAC,iBAAiB,KAAK,MAAM,MAAM,CAAC,CACtC,QAAO;;IAGZ,CAAC;AAEF,OAAIC,GAAW,UAAU,EAAE;AACzB,OAAS,aAAa;AACtB,YAAQ,KAAK,EAAE;;AAGjB,UAAQ,UAAqB,MAAM;;AAIrC,MAAI,CAAC,iBAAiB,KAAK,KAAK,EAAE;AAChC,MAAS,2EAA2E;AACpF,WAAQ,KAAK,EAAE;;EAIjB,MAAM,YAAY,KAAK,YAAY,YAAY,KAAK;AACpD,MAAI;AACF,SAAM,QAAQ,UAAU;AACxB,MAAS,YAAY,KAAK,+BAA+B,KAAK,GAAG;AACjE,WAAQ,KAAK,EAAE;UACT;AAKR,QAAM,MAAM,WAAW,EAAE,WAAW,MAAM,CAAC;AAI3C,QAAM,oBADc,KAAK,WAAW,aAAa,WAAW,UAAU,EAC/B,WAAW,EAAE,MAAM,CAAC;AAE3D,KAAQ,YAAY,KAAK,wBAAwB,KAAK,GAAG;;CAE5D,CAAC;;;;AAKF,eAAe,oBACb,WACA,WACA,WACe;CACf,MAAM,UAAU,MAAM,QAAQ,WAAW,EAAE,eAAe,MAAM,CAAC;AAEjE,MAAK,MAAM,SAAS,SAAS;EAC3B,MAAM,aAAa,KAAK,WAAW,MAAM,KAAK;EAC9C,MAAM,aAAa,KAAK,WAAW,MAAM,KAAK;AAE9C,MAAI,MAAM,aAAa,EAAE;AACvB,SAAM,MAAM,YAAY,EAAE,WAAW,MAAM,CAAC;AAC5C,SAAM,oBAAoB,YAAY,YAAY,UAAU;SACvD;GAEL,MAAM,UAAU,MAAM,SAAS,YAAY,QAAQ;AAMnD,SAAM,UAAU,YAHEC,mBAAW,QAAQ,SAAS,EAAE,UAAU,MAAM,CAAC,CAAC,UAAU,EAGrC,QAAQ"}
|
package/dist/index.mjs
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { r as __toESM } from "./chunk-BbwQpWto.mjs";
|
|
3
3
|
import { a as Ne, c as Ve, d as bt, f as je, g as runMain, h as defineCommand, i as Le, l as We, m as require_dist, o as R, p as Ct, r as Je, s as Re, t as findSourceRoot, u as Ze } from "./context-detection-DqOTnD6_.mjs";
|
|
4
|
-
import { $ as
|
|
4
|
+
import { $ as loadProfileManifest, A as detectLegacyPaths, B as collectComprehensivePatterns, C as mergeSkillsWithWarnings, D as mergeContentParts, E as sortProfilesByWeight, F as generateLock, G as getRegisteredIdePlatforms, H as removeGitignoreManagedSection, I as readLock, J as getAdaptersForKeys, K as idePlatformRegistry, L as writeLock, M as discoverProfilesInSourceRepo, N as findSourceManifest, O as resolveProfileSupport, P as removePlacedFiles, Q as loadLockfile, R as resolveVersion, S as mergeSkills, T as isLockedProfile, U as updateGitignore, V as ensureBatonDirGitignored, W as getIdePlatformTargetDir, X as parseFrontmatter, Y as getAllAdapters, Z as parseSource, _ as require_lib, a as clearIdeCache, at as getAgentPath, b as mergeRules, c as getBatonHome, d as getGlobalConfigPath, et as loadProjectManifest, f as getGlobalIdePlatforms, g as setGlobalIdePlatforms, h as setGlobalAiTools, i as computeIntersection, it as getAgentConfig, j as placeFile, k as resolveProfileChain, l as getDefaultGlobalSource, m as removeGlobalSource, n as readProjectPreferences, nt as FileNotFoundError, o as detectInstalledIdes, ot as getAllAgentKeys, p as getGlobalSources, q as isKnownIdePlatform, r as writeProjectPreferences, rt as SourceParseError, s as addGlobalSource, t as resolvePreferences, tt as KEBAB_CASE_REGEX, u as getGlobalAiTools, v as mergeMemory, w as getProfileWeight, x as mergeRulesWithWarnings, y as mergeMemoryWithWarnings, z as cloneGitSource } from "./src-BCGnnv5D.mjs";
|
|
5
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
|
-
import { homedir } from "node:os";
|
|
11
10
|
|
|
12
11
|
//#region src/commands/ai-tools/configure.ts
|
|
13
12
|
const aiToolsConfigureCommand = defineCommand({
|
|
@@ -421,7 +420,6 @@ function formatIntersectionSummary(intersection) {
|
|
|
421
420
|
|
|
422
421
|
//#endregion
|
|
423
422
|
//#region src/commands/config.ts
|
|
424
|
-
const CONFIG_FILE = join(homedir(), ".baton", "config.yaml");
|
|
425
423
|
const VALID_KEYS = [
|
|
426
424
|
"cache-dir",
|
|
427
425
|
"default-scope",
|
|
@@ -429,16 +427,18 @@ const VALID_KEYS = [
|
|
|
429
427
|
"default-tools"
|
|
430
428
|
];
|
|
431
429
|
async function loadConfig() {
|
|
430
|
+
const configFile = getGlobalConfigPath();
|
|
432
431
|
try {
|
|
433
|
-
await access(
|
|
432
|
+
await access(configFile);
|
|
434
433
|
} catch {
|
|
435
434
|
return {};
|
|
436
435
|
}
|
|
437
|
-
return (0, import_dist.parse)(await readFile(
|
|
436
|
+
return (0, import_dist.parse)(await readFile(configFile, "utf-8"));
|
|
438
437
|
}
|
|
439
438
|
async function saveConfig(config) {
|
|
440
|
-
|
|
441
|
-
await
|
|
439
|
+
const configFile = getGlobalConfigPath();
|
|
440
|
+
await mkdir(getBatonHome(), { recursive: true });
|
|
441
|
+
await writeFile(configFile, (0, import_dist.stringify)(config), "utf-8");
|
|
442
442
|
}
|
|
443
443
|
async function showDashboard() {
|
|
444
444
|
We("Baton Dashboard");
|
|
@@ -1650,7 +1650,22 @@ const initCommand = defineCommand({
|
|
|
1650
1650
|
}
|
|
1651
1651
|
}
|
|
1652
1652
|
await showProfileIntersections(profileSources);
|
|
1653
|
-
|
|
1653
|
+
let gitignoreSetting = true;
|
|
1654
|
+
if (isInteractive) {
|
|
1655
|
+
const shouldGitignore = await Re({
|
|
1656
|
+
message: "Add synced AI tool and IDE config files to .gitignore?",
|
|
1657
|
+
initialValue: true
|
|
1658
|
+
});
|
|
1659
|
+
if (Ct(shouldGitignore)) {
|
|
1660
|
+
Ne("Setup cancelled.");
|
|
1661
|
+
process.exit(0);
|
|
1662
|
+
}
|
|
1663
|
+
gitignoreSetting = shouldGitignore;
|
|
1664
|
+
}
|
|
1665
|
+
const yamlContent = (0, import_dist.stringify)({
|
|
1666
|
+
profiles: profileSources.map((source) => ({ source })),
|
|
1667
|
+
gitignore: gitignoreSetting
|
|
1668
|
+
});
|
|
1654
1669
|
spinner.start("Creating baton.yaml...");
|
|
1655
1670
|
await writeFile(join(cwd, "baton.yaml"), yamlContent, "utf-8");
|
|
1656
1671
|
spinner.stop("✅ Created baton.yaml");
|
|
@@ -1660,10 +1675,11 @@ const initCommand = defineCommand({
|
|
|
1660
1675
|
try {
|
|
1661
1676
|
gitignoreContent = await readFile(gitignorePath, "utf-8");
|
|
1662
1677
|
} catch (_error) {}
|
|
1663
|
-
if (!gitignoreContent.includes(".baton/")) {
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1678
|
+
if (!gitignoreContent.includes(".baton/")) await writeFile(gitignorePath, gitignoreContent ? `${gitignoreContent}\n\n# Baton cache\n.baton/\n` : "# Baton cache\n.baton/\n", "utf-8");
|
|
1679
|
+
if (gitignoreSetting) {
|
|
1680
|
+
await updateGitignore(cwd, collectComprehensivePatterns({ fileTargets: [] }));
|
|
1681
|
+
spinner.stop("✅ Updated .gitignore with managed file patterns");
|
|
1682
|
+
} else spinner.stop("✅ Added .baton/ to .gitignore");
|
|
1667
1683
|
spinner.start("Creating .baton directory...");
|
|
1668
1684
|
try {
|
|
1669
1685
|
await mkdir(join(cwd, ".baton"), { recursive: true });
|
|
@@ -1965,13 +1981,33 @@ async function handleRemoveBaton(cwd) {
|
|
|
1965
1981
|
R.warn("Cancelled.");
|
|
1966
1982
|
return false;
|
|
1967
1983
|
}
|
|
1984
|
+
const lockPath = join(cwd, "baton.lock");
|
|
1985
|
+
await cleanupPlacedFilesFromLock(lockPath, cwd);
|
|
1968
1986
|
await rm(join(cwd, "baton.yaml"), { force: true });
|
|
1969
|
-
await rm(
|
|
1987
|
+
await rm(lockPath, { force: true });
|
|
1970
1988
|
R.success("Baton has been removed from this project.");
|
|
1971
|
-
R.info("Note: Synced files (rules, skills, memory) were not removed.");
|
|
1972
|
-
R.info("Run 'baton sync' before removing to clean up, or delete them manually.");
|
|
1973
1989
|
return true;
|
|
1974
1990
|
}
|
|
1991
|
+
async function cleanupPlacedFilesFromLock(lockPath, projectRoot) {
|
|
1992
|
+
let placedPaths;
|
|
1993
|
+
try {
|
|
1994
|
+
const lockfile = await readLock(lockPath);
|
|
1995
|
+
placedPaths = Object.values(lockfile.packages).flatMap((pkg) => Object.keys(pkg.integrity));
|
|
1996
|
+
} catch (error) {
|
|
1997
|
+
if (error instanceof FileNotFoundError) return;
|
|
1998
|
+
return;
|
|
1999
|
+
}
|
|
2000
|
+
if (placedPaths.length === 0) return;
|
|
2001
|
+
R.info(`Found ${placedPaths.length} placed file(s):`);
|
|
2002
|
+
for (const filePath of placedPaths) R.info(` ${filePath}`);
|
|
2003
|
+
const shouldClean = await Re({
|
|
2004
|
+
message: `Also remove ${placedPaths.length} placed file(s)?`,
|
|
2005
|
+
initialValue: false
|
|
2006
|
+
});
|
|
2007
|
+
if (Ct(shouldClean) || !shouldClean) return;
|
|
2008
|
+
const removedCount = await removePlacedFiles(placedPaths, projectRoot);
|
|
2009
|
+
R.success(`Removed ${removedCount} placed file(s).`);
|
|
2010
|
+
}
|
|
1975
2011
|
function formatIdeName(ideKey) {
|
|
1976
2012
|
return {
|
|
1977
2013
|
vscode: "VS Code",
|
|
@@ -2112,6 +2148,31 @@ async function handleConfigureIdes(cwd) {
|
|
|
2112
2148
|
});
|
|
2113
2149
|
R.success(`Project configured with ${selectedKeys.length} IDE platform(s).`);
|
|
2114
2150
|
}
|
|
2151
|
+
async function handleConfigureGitignore(cwd) {
|
|
2152
|
+
const manifestPath = join(cwd, "baton.yaml");
|
|
2153
|
+
const manifest = await loadProjectManifestSafe(cwd);
|
|
2154
|
+
if (!manifest) {
|
|
2155
|
+
R.error("Could not load baton.yaml");
|
|
2156
|
+
return;
|
|
2157
|
+
}
|
|
2158
|
+
const currentSetting = manifest.gitignore !== false;
|
|
2159
|
+
R.info(currentSetting ? "Currently: synced files ARE gitignored" : "Currently: synced files are NOT gitignored (committed to repo)");
|
|
2160
|
+
const newSetting = await Re({
|
|
2161
|
+
message: "Add synced AI tool and IDE config files to .gitignore?",
|
|
2162
|
+
initialValue: currentSetting
|
|
2163
|
+
});
|
|
2164
|
+
if (Ct(newSetting)) {
|
|
2165
|
+
R.warn("Cancelled.");
|
|
2166
|
+
return;
|
|
2167
|
+
}
|
|
2168
|
+
if (newSetting === currentSetting) {
|
|
2169
|
+
R.info("No change.");
|
|
2170
|
+
return;
|
|
2171
|
+
}
|
|
2172
|
+
manifest.gitignore = newSetting;
|
|
2173
|
+
await writeFile(manifestPath, (0, import_dist.stringify)(manifest), "utf-8");
|
|
2174
|
+
R.success(newSetting ? "Enabled .gitignore management. Run 'baton sync' to update." : "Disabled .gitignore management. Run 'baton sync' to clean up.");
|
|
2175
|
+
}
|
|
2115
2176
|
const manageCommand = defineCommand({
|
|
2116
2177
|
meta: {
|
|
2117
2178
|
name: "manage",
|
|
@@ -2154,6 +2215,11 @@ const manageCommand = defineCommand({
|
|
|
2154
2215
|
label: "Configure IDEs for this project",
|
|
2155
2216
|
hint: "Choose which IDEs to sync"
|
|
2156
2217
|
},
|
|
2218
|
+
{
|
|
2219
|
+
value: "configure-gitignore",
|
|
2220
|
+
label: "Configure .gitignore",
|
|
2221
|
+
hint: "Choose whether synced files are gitignored"
|
|
2222
|
+
},
|
|
2157
2223
|
{
|
|
2158
2224
|
value: "remove-baton",
|
|
2159
2225
|
label: "Remove Baton",
|
|
@@ -2189,6 +2255,10 @@ const manageCommand = defineCommand({
|
|
|
2189
2255
|
console.log("");
|
|
2190
2256
|
await handleConfigureIdes(cwd);
|
|
2191
2257
|
console.log("");
|
|
2258
|
+
} else if (action === "configure-gitignore") {
|
|
2259
|
+
console.log("");
|
|
2260
|
+
await handleConfigureGitignore(cwd);
|
|
2261
|
+
console.log("");
|
|
2192
2262
|
} else if (action === "remove-baton") {
|
|
2193
2263
|
console.log("");
|
|
2194
2264
|
if (await handleRemoveBaton(cwd)) {
|
|
@@ -2209,8 +2279,8 @@ const profileCommand = defineCommand({
|
|
|
2209
2279
|
description: "Manage profiles (create, list, remove)"
|
|
2210
2280
|
},
|
|
2211
2281
|
subCommands: {
|
|
2212
|
-
create: () => import("./create-
|
|
2213
|
-
list: () => import("./list-
|
|
2282
|
+
create: () => import("./create-BkpEXaht.mjs").then((m) => m.createCommand),
|
|
2283
|
+
list: () => import("./list-DSLwzhBG.mjs").then((m) => m.profileListCommand),
|
|
2214
2284
|
remove: () => import("./remove-BBs6Mv8t.mjs").then((m) => m.profileRemoveCommand)
|
|
2215
2285
|
}
|
|
2216
2286
|
});
|
|
@@ -2668,35 +2738,25 @@ async function copyDirectoryRecursive(sourceDir, targetDir) {
|
|
|
2668
2738
|
return placed;
|
|
2669
2739
|
}
|
|
2670
2740
|
/**
|
|
2671
|
-
*
|
|
2672
|
-
*
|
|
2673
|
-
*
|
|
2741
|
+
* Handle .gitignore update based on the project manifest's gitignore setting.
|
|
2742
|
+
*
|
|
2743
|
+
* When gitignore is enabled (default): writes comprehensive patterns for ALL
|
|
2744
|
+
* known AI tools and IDE platforms to ensure stable, dev-independent content.
|
|
2745
|
+
* When disabled: removes any existing managed section.
|
|
2746
|
+
* Always ensures .baton/ is gitignored regardless of setting.
|
|
2674
2747
|
*/
|
|
2675
|
-
async function
|
|
2676
|
-
const {
|
|
2677
|
-
const
|
|
2678
|
-
|
|
2679
|
-
if (
|
|
2680
|
-
for (const tool of intersection.aiTools.synced) profileSupportedAiTools.add(tool);
|
|
2681
|
-
for (const tool of intersection.aiTools.unavailable) profileSupportedAiTools.add(tool);
|
|
2682
|
-
for (const plat of intersection.idePlatforms.synced) profileSupportedIdePlatforms.add(plat);
|
|
2683
|
-
for (const plat of intersection.idePlatforms.unavailable) profileSupportedIdePlatforms.add(plat);
|
|
2684
|
-
}
|
|
2685
|
-
else {
|
|
2686
|
-
for (const adapter of adapters) profileSupportedAiTools.add(adapter.key);
|
|
2687
|
-
for (const entry of ideMap.values()) profileSupportedIdePlatforms.add(entry.ideKey);
|
|
2688
|
-
}
|
|
2689
|
-
const hasContent = mergedSkills.length > 0 || mergedRules.length > 0 || mergedMemory.length > 0 || mergedCommandCount > 0;
|
|
2690
|
-
const gitignorePatterns = collectProfileSupportPatterns({
|
|
2691
|
-
profileAiTools: [...profileSupportedAiTools],
|
|
2692
|
-
profileIdePlatforms: [...profileSupportedIdePlatforms],
|
|
2693
|
-
fileTargets: [...fileMap.values()].map((f) => f.target),
|
|
2694
|
-
hasContent
|
|
2695
|
-
});
|
|
2696
|
-
if (gitignorePatterns.length > 0) {
|
|
2748
|
+
async function handleGitignoreUpdate(params) {
|
|
2749
|
+
const { projectManifest, fileMap, projectRoot, spinner } = params;
|
|
2750
|
+
const gitignoreEnabled = projectManifest.gitignore !== false;
|
|
2751
|
+
await ensureBatonDirGitignored(projectRoot);
|
|
2752
|
+
if (gitignoreEnabled) {
|
|
2697
2753
|
spinner.start("Updating .gitignore...");
|
|
2698
|
-
const updated = await updateGitignore(projectRoot,
|
|
2699
|
-
spinner.stop(updated ? "Updated .gitignore with
|
|
2754
|
+
const updated = await updateGitignore(projectRoot, collectComprehensivePatterns({ fileTargets: [...fileMap.values()].map((f) => f.target) }));
|
|
2755
|
+
spinner.stop(updated ? "Updated .gitignore with managed patterns" : ".gitignore already up to date");
|
|
2756
|
+
} else {
|
|
2757
|
+
spinner.start("Checking .gitignore...");
|
|
2758
|
+
const removed = await removeGitignoreManagedSection(projectRoot);
|
|
2759
|
+
spinner.stop(removed ? "Removed managed section from .gitignore" : ".gitignore unchanged");
|
|
2700
2760
|
}
|
|
2701
2761
|
}
|
|
2702
2762
|
/**
|
|
@@ -2750,23 +2810,7 @@ async function cleanupOrphanedFiles(params) {
|
|
|
2750
2810
|
return;
|
|
2751
2811
|
}
|
|
2752
2812
|
spinner.start("Removing orphaned files...");
|
|
2753
|
-
|
|
2754
|
-
for (const orphanedPath of orphanedPaths) {
|
|
2755
|
-
const absolutePath = orphanedPath.startsWith("/") ? orphanedPath : resolve(projectRoot, orphanedPath);
|
|
2756
|
-
try {
|
|
2757
|
-
await unlink(absolutePath);
|
|
2758
|
-
removedCount++;
|
|
2759
|
-
let dir = dirname(absolutePath);
|
|
2760
|
-
while (dir !== projectRoot && dir.startsWith(projectRoot)) try {
|
|
2761
|
-
if ((await readdir(dir)).length === 0) {
|
|
2762
|
-
await rmdir(dir);
|
|
2763
|
-
dir = dirname(dir);
|
|
2764
|
-
} else break;
|
|
2765
|
-
} catch {
|
|
2766
|
-
break;
|
|
2767
|
-
}
|
|
2768
|
-
} catch {}
|
|
2769
|
-
}
|
|
2813
|
+
const removedCount = await removePlacedFiles(orphanedPaths, projectRoot);
|
|
2770
2814
|
spinner.stop(`Removed ${removedCount} orphaned file(s)`);
|
|
2771
2815
|
}
|
|
2772
2816
|
const syncCommand = defineCommand({
|
|
@@ -3214,9 +3258,10 @@ const syncCommand = defineCommand({
|
|
|
3214
3258
|
const combinedContent = entry.parts.join("\n\n");
|
|
3215
3259
|
const result = await placeFile(combinedContent, entry.adapter, entry.type, "project", entry.name, placementConfig);
|
|
3216
3260
|
if (result.action !== "skipped") stats.created++;
|
|
3261
|
+
const relPath = isAbsolute(result.path) ? relative(projectRoot, result.path) : result.path;
|
|
3217
3262
|
for (const profileName of entry.profiles) {
|
|
3218
3263
|
const pf = getOrCreatePlacedFiles(placedFiles, profileName);
|
|
3219
|
-
pf[
|
|
3264
|
+
pf[relPath] = {
|
|
3220
3265
|
content: combinedContent,
|
|
3221
3266
|
tool: entry.adapter.key,
|
|
3222
3267
|
category: "ai"
|
|
@@ -3246,8 +3291,9 @@ const syncCommand = defineCommand({
|
|
|
3246
3291
|
}
|
|
3247
3292
|
const result = await placeFile(content, adapter, "commands", "project", commandName, placementConfig);
|
|
3248
3293
|
if (result.action !== "skipped") stats.created++;
|
|
3294
|
+
const cmdRelPath = isAbsolute(result.path) ? relative(projectRoot, result.path) : result.path;
|
|
3249
3295
|
const pf = getOrCreatePlacedFiles(placedFiles, profile.name);
|
|
3250
|
-
pf[
|
|
3296
|
+
pf[cmdRelPath] = {
|
|
3251
3297
|
content,
|
|
3252
3298
|
tool: adapter.key,
|
|
3253
3299
|
category: "ai"
|
|
@@ -3321,14 +3367,8 @@ const syncCommand = defineCommand({
|
|
|
3321
3367
|
stats.errors++;
|
|
3322
3368
|
}
|
|
3323
3369
|
spinner.stop(dryRun ? `Would place files for ${adapters.length} agent(s)` : `Placed ${stats.created} file(s) for ${adapters.length} agent(s)`);
|
|
3324
|
-
if (!dryRun) await
|
|
3325
|
-
|
|
3326
|
-
adapters,
|
|
3327
|
-
ideMap,
|
|
3328
|
-
mergedSkills,
|
|
3329
|
-
mergedRules,
|
|
3330
|
-
mergedMemory,
|
|
3331
|
-
mergedCommandCount,
|
|
3370
|
+
if (!dryRun) await handleGitignoreUpdate({
|
|
3371
|
+
projectManifest,
|
|
3332
3372
|
fileMap,
|
|
3333
3373
|
projectRoot,
|
|
3334
3374
|
spinner
|