@baton-dx/cli 0.1.4 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import { c as Ve, h as defineCommand, i as Le, l as We, n as isInSourceRepo } from "./context-detection-mMNLg_4F.mjs";
3
- import { D as discoverProfilesInSourceRepo } from "./src-Dh0ZvHbV.mjs";
2
+ import { c as Ve, h as defineCommand, i as Le, l as We, n as isInSourceRepo } from "./context-detection-DqOTnD6_.mjs";
3
+ import { A as discoverProfilesInSourceRepo } from "./src-BgiJfm14.mjs";
4
+ import "./agent-detection-DTiVeO5W.mjs";
4
5
  import "./esm-BagM-kVd.mjs";
5
6
 
6
7
  //#region src/commands/profile/list.ts
@@ -52,4 +53,4 @@ Note: Must be run from a source repository (directory with baton.source.yaml)`
52
53
 
53
54
  //#endregion
54
55
  export { profileListCommand };
55
- //# sourceMappingURL=list-B5xUVBTU.mjs.map
56
+ //# sourceMappingURL=list-CCzjta6J.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"list-B5xUVBTU.mjs","names":[],"sources":["../src/commands/profile/list.ts"],"sourcesContent":["import { discoverProfilesInSourceRepo } from \"@baton-dx/core\";\nimport * as p from \"@clack/prompts\";\nimport { defineCommand } from \"citty\";\nimport { isInSourceRepo } from \"../../utils/context-detection.js\";\n\nexport const profileListCommand = defineCommand({\n meta: {\n name: \"profile list\",\n description: `List all profiles in the current source repository\n\nShows a table of all profiles with:\n - Profile name (root profile marked with \"(root)\")\n - Version from baton.profile.yaml\n - Description from profile manifest\n\nExamples:\n baton profile list\n\nNote: Must be run from a source repository (directory with baton.source.yaml)`,\n },\n run: async () => {\n p.intro(\"List Profiles\");\n\n // Check if we're in a source repo\n const inSourceRepo = await isInSourceRepo();\n if (!inSourceRepo) {\n p.outro(\n \"Error: Not in a source repository. Run this command from a directory containing baton.source.yaml\",\n );\n process.exit(1);\n }\n\n const cwd = process.cwd();\n\n // Discover all profiles in the profiles/ directory\n const profiles = await discoverProfilesInSourceRepo(cwd);\n\n if (profiles.length === 0) {\n p.outro(\"No profiles found.\");\n process.exit(0);\n }\n\n // Build table output\n const lines: string[] = [];\n lines.push(\"┌─────────────────────┬─────────┬────────────────────────────────────┐\");\n lines.push(\"│ Name │ Version │ Description │\");\n lines.push(\"├─────────────────────┼─────────┼────────────────────────────────────┤\");\n\n for (const profile of profiles) {\n const name = profile.name;\n const version = profile.version || \"-\";\n const description = profile.description || \"-\";\n\n // Pad columns to fixed width\n const namePadded = name.padEnd(19);\n const versionPadded = version.padEnd(7);\n const descPadded = description.padEnd(34);\n\n lines.push(`│ ${namePadded} │ ${versionPadded} │ ${descPadded} │`);\n }\n\n lines.push(\"└─────────────────────┴─────────┴────────────────────────────────────┘\");\n\n p.note(lines.join(\"\\n\"), \"Profiles\");\n p.outro(`Found ${profiles.length} profile${profiles.length === 1 ? \"\" : \"s\"}`);\n process.exit(0);\n },\n});\n"],"mappings":";;;;;;AAKA,MAAa,qBAAqB,cAAc;CAC9C,MAAM;EACJ,MAAM;EACN,aAAa;;;;;;;;;;;EAWd;CACD,KAAK,YAAY;AACf,KAAQ,gBAAgB;AAIxB,MAAI,CADiB,MAAM,gBAAgB,EACxB;AACjB,MACE,oGACD;AACD,WAAQ,KAAK,EAAE;;EAMjB,MAAM,WAAW,MAAM,6BAHX,QAAQ,KAAK,CAG+B;AAExD,MAAI,SAAS,WAAW,GAAG;AACzB,MAAQ,qBAAqB;AAC7B,WAAQ,KAAK,EAAE;;EAIjB,MAAM,QAAkB,EAAE;AAC1B,QAAM,KAAK,yEAAyE;AACpF,QAAM,KAAK,yEAAyE;AACpF,QAAM,KAAK,yEAAyE;AAEpF,OAAK,MAAM,WAAW,UAAU;GAC9B,MAAM,OAAO,QAAQ;GACrB,MAAM,UAAU,QAAQ,WAAW;GACnC,MAAM,cAAc,QAAQ,eAAe;GAG3C,MAAM,aAAa,KAAK,OAAO,GAAG;GAClC,MAAM,gBAAgB,QAAQ,OAAO,EAAE;GACvC,MAAM,aAAa,YAAY,OAAO,GAAG;AAEzC,SAAM,KAAK,KAAK,WAAW,KAAK,cAAc,KAAK,WAAW,IAAI;;AAGpE,QAAM,KAAK,yEAAyE;AAEpF,KAAO,MAAM,KAAK,KAAK,EAAE,WAAW;AACpC,KAAQ,SAAS,SAAS,OAAO,UAAU,SAAS,WAAW,IAAI,KAAK,MAAM;AAC9E,UAAQ,KAAK,EAAE;;CAElB,CAAC"}
1
+ {"version":3,"file":"list-CCzjta6J.mjs","names":[],"sources":["../src/commands/profile/list.ts"],"sourcesContent":["import { discoverProfilesInSourceRepo } from \"@baton-dx/core\";\nimport * as p from \"@clack/prompts\";\nimport { defineCommand } from \"citty\";\nimport { isInSourceRepo } from \"../../utils/context-detection.js\";\n\nexport const profileListCommand = defineCommand({\n meta: {\n name: \"profile list\",\n description: `List all profiles in the current source repository\n\nShows a table of all profiles with:\n - Profile name (root profile marked with \"(root)\")\n - Version from baton.profile.yaml\n - Description from profile manifest\n\nExamples:\n baton profile list\n\nNote: Must be run from a source repository (directory with baton.source.yaml)`,\n },\n run: async () => {\n p.intro(\"List Profiles\");\n\n // Check if we're in a source repo\n const inSourceRepo = await isInSourceRepo();\n if (!inSourceRepo) {\n p.outro(\n \"Error: Not in a source repository. Run this command from a directory containing baton.source.yaml\",\n );\n process.exit(1);\n }\n\n const cwd = process.cwd();\n\n // Discover all profiles in the profiles/ directory\n const profiles = await discoverProfilesInSourceRepo(cwd);\n\n if (profiles.length === 0) {\n p.outro(\"No profiles found.\");\n process.exit(0);\n }\n\n // Build table output\n const lines: string[] = [];\n lines.push(\"┌─────────────────────┬─────────┬────────────────────────────────────┐\");\n lines.push(\"│ Name │ Version │ Description │\");\n lines.push(\"├─────────────────────┼─────────┼────────────────────────────────────┤\");\n\n for (const profile of profiles) {\n const name = profile.name;\n const version = profile.version || \"-\";\n const description = profile.description || \"-\";\n\n // Pad columns to fixed width\n const namePadded = name.padEnd(19);\n const versionPadded = version.padEnd(7);\n const descPadded = description.padEnd(34);\n\n lines.push(`│ ${namePadded} │ ${versionPadded} │ ${descPadded} │`);\n }\n\n lines.push(\"└─────────────────────┴─────────┴────────────────────────────────────┘\");\n\n p.note(lines.join(\"\\n\"), \"Profiles\");\n p.outro(`Found ${profiles.length} profile${profiles.length === 1 ? \"\" : \"s\"}`);\n process.exit(0);\n },\n});\n"],"mappings":";;;;;;;AAKA,MAAa,qBAAqB,cAAc;CAC9C,MAAM;EACJ,MAAM;EACN,aAAa;;;;;;;;;;;EAWd;CACD,KAAK,YAAY;AACf,KAAQ,gBAAgB;AAIxB,MAAI,CADiB,MAAM,gBAAgB,EACxB;AACjB,MACE,oGACD;AACD,WAAQ,KAAK,EAAE;;EAMjB,MAAM,WAAW,MAAM,6BAHX,QAAQ,KAAK,CAG+B;AAExD,MAAI,SAAS,WAAW,GAAG;AACzB,MAAQ,qBAAqB;AAC7B,WAAQ,KAAK,EAAE;;EAIjB,MAAM,QAAkB,EAAE;AAC1B,QAAM,KAAK,yEAAyE;AACpF,QAAM,KAAK,yEAAyE;AACpF,QAAM,KAAK,yEAAyE;AAEpF,OAAK,MAAM,WAAW,UAAU;GAC9B,MAAM,OAAO,QAAQ;GACrB,MAAM,UAAU,QAAQ,WAAW;GACnC,MAAM,cAAc,QAAQ,eAAe;GAG3C,MAAM,aAAa,KAAK,OAAO,GAAG;GAClC,MAAM,gBAAgB,QAAQ,OAAO,EAAE;GACvC,MAAM,aAAa,YAAY,OAAO,GAAG;AAEzC,SAAM,KAAK,KAAK,WAAW,KAAK,cAAc,KAAK,WAAW,IAAI;;AAGpE,QAAM,KAAK,yEAAyE;AAEpF,KAAO,MAAM,KAAK,KAAK,EAAE,WAAW;AACpC,KAAQ,SAAS,SAAS,OAAO,UAAU,SAAS,WAAW,IAAI,KAAK,MAAM;AAC9E,UAAQ,KAAK,EAAE;;CAElB,CAAC"}
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { a as Ne, h as defineCommand, i as Le, l as We, m as require_dist, o as R, p as Ct, s as Re, t as findSourceRoot } from "./context-detection-mMNLg_4F.mjs";
2
+ import { a as Ne, h as defineCommand, i as Le, l as We, m as require_dist, o as R, p as Ct, s as Re, t as findSourceRoot } from "./context-detection-DqOTnD6_.mjs";
3
3
  import { readFile, readdir, rm, writeFile } from "node:fs/promises";
4
4
  import { join, relative } from "node:path";
5
5
 
@@ -87,4 +87,4 @@ const profileRemoveCommand = defineCommand({
87
87
 
88
88
  //#endregion
89
89
  export { profileRemoveCommand };
90
- //# sourceMappingURL=remove-6S8F9xcE.mjs.map
90
+ //# sourceMappingURL=remove-BBs6Mv8t.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"remove-6S8F9xcE.mjs","names":["p.confirm","p.isCancel"],"sources":["../src/commands/profile/remove.ts"],"sourcesContent":["import { readFile, readdir, rm, writeFile } from \"node:fs/promises\";\nimport { join, relative } from \"node:path\";\nimport * as p from \"@clack/prompts\";\nimport { defineCommand } from \"citty\";\nimport { parse as parseYaml, stringify as stringifyYaml } from \"yaml\";\nimport { findSourceRoot } from \"../../utils/context-detection.js\";\n\n/**\n * Recursively collect all file paths in a directory (relative to baseDir)\n */\nasync function collectFiles(dir: string, baseDir: string): Promise<string[]> {\n const files: string[] = [];\n const entries = await readdir(dir, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = join(dir, entry.name);\n if (entry.isDirectory()) {\n files.push(...(await collectFiles(fullPath, baseDir)));\n } else {\n files.push(relative(baseDir, fullPath));\n }\n }\n\n return files;\n}\n\n/**\n * Remove a profile entry from baton.source.yaml if it exists in the profiles array\n */\nasync function removeProfileFromSourceManifest(\n sourceRoot: string,\n profileName: string,\n): Promise<void> {\n const manifestPath = join(sourceRoot, \"baton.source.yaml\");\n\n try {\n const content = await readFile(manifestPath, \"utf-8\");\n const manifest = parseYaml(content);\n\n if (manifest?.profiles && Array.isArray(manifest.profiles)) {\n const originalLength = manifest.profiles.length;\n manifest.profiles = manifest.profiles.filter(\n (profile: { name?: string; path?: string }) =>\n profile.name !== profileName && profile.path !== `profiles/${profileName}`,\n );\n\n if (manifest.profiles.length < originalLength) {\n // Remove empty profiles array\n if (manifest.profiles.length === 0) {\n manifest.profiles = undefined;\n }\n await writeFile(manifestPath, stringifyYaml(manifest), \"utf-8\");\n }\n }\n } catch {\n // If manifest can't be read/written, skip — the directory removal is the primary action\n }\n}\n\nexport const profileRemoveCommand = defineCommand({\n meta: {\n name: \"remove\",\n description: \"Remove a profile from the source repository\",\n },\n args: {\n name: {\n type: \"positional\",\n description: \"Profile name to remove\",\n required: true,\n },\n },\n async run({ args }) {\n p.intro(\"Remove 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 const profileName = args.name as string;\n const profileDir = join(sourceRoot, \"profiles\", profileName);\n\n // Check if profile directory exists\n let files: string[];\n try {\n files = await collectFiles(profileDir, sourceRoot);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n p.cancel(`Profile \"${profileName}\" does not exist in profiles/${profileName}/`);\n process.exit(1);\n }\n throw error;\n }\n\n // Warn about files that will be deleted\n if (files.length > 0) {\n p.log.warn(\"The following files will be deleted:\");\n for (const file of files) {\n p.log.info(` ${file}`);\n }\n } else {\n p.log.warn(`Directory profiles/${profileName}/ will be deleted.`);\n }\n\n // Confirm before removing\n const confirmed = await p.confirm({\n message: `Are you sure you want to remove profile \"${profileName}\"?`,\n initialValue: false,\n });\n\n if (p.isCancel(confirmed) || !confirmed) {\n p.cancel(\"Operation cancelled.\");\n process.exit(0);\n }\n\n // Remove the profile directory\n await rm(profileDir, { recursive: true, force: true });\n\n // Remove entry from baton.source.yaml if present\n await removeProfileFromSourceManifest(sourceRoot, profileName);\n\n p.outro(`Profile \"${profileName}\" removed from profiles/${profileName}/`);\n },\n});\n"],"mappings":";;;;;;;;;;AAUA,eAAe,aAAa,KAAa,SAAoC;CAC3E,MAAM,QAAkB,EAAE;CAC1B,MAAM,UAAU,MAAM,QAAQ,KAAK,EAAE,eAAe,MAAM,CAAC;AAE3D,MAAK,MAAM,SAAS,SAAS;EAC3B,MAAM,WAAW,KAAK,KAAK,MAAM,KAAK;AACtC,MAAI,MAAM,aAAa,CACrB,OAAM,KAAK,GAAI,MAAM,aAAa,UAAU,QAAQ,CAAE;MAEtD,OAAM,KAAK,SAAS,SAAS,SAAS,CAAC;;AAI3C,QAAO;;;;;AAMT,eAAe,gCACb,YACA,aACe;CACf,MAAM,eAAe,KAAK,YAAY,oBAAoB;AAE1D,KAAI;EAEF,MAAM,kCADU,MAAM,SAAS,cAAc,QAAQ,CAClB;AAEnC,MAAI,UAAU,YAAY,MAAM,QAAQ,SAAS,SAAS,EAAE;GAC1D,MAAM,iBAAiB,SAAS,SAAS;AACzC,YAAS,WAAW,SAAS,SAAS,QACnC,YACC,QAAQ,SAAS,eAAe,QAAQ,SAAS,YAAY,cAChE;AAED,OAAI,SAAS,SAAS,SAAS,gBAAgB;AAE7C,QAAI,SAAS,SAAS,WAAW,EAC/B,UAAS,WAAW;AAEtB,UAAM,UAAU,yCAA4B,SAAS,EAAE,QAAQ;;;SAG7D;;AAKV,MAAa,uBAAuB,cAAc;CAChD,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;;EAGjB,MAAM,cAAc,KAAK;EACzB,MAAM,aAAa,KAAK,YAAY,YAAY,YAAY;EAG5D,IAAI;AACJ,MAAI;AACF,WAAQ,MAAM,aAAa,YAAY,WAAW;WAC3C,OAAO;AACd,OAAK,MAAgC,SAAS,UAAU;AACtD,OAAS,YAAY,YAAY,+BAA+B,YAAY,GAAG;AAC/E,YAAQ,KAAK,EAAE;;AAEjB,SAAM;;AAIR,MAAI,MAAM,SAAS,GAAG;AACpB,KAAM,KAAK,uCAAuC;AAClD,QAAK,MAAM,QAAQ,MACjB,GAAM,KAAK,KAAK,OAAO;QAGzB,GAAM,KAAK,sBAAsB,YAAY,oBAAoB;EAInE,MAAM,YAAY,MAAMA,GAAU;GAChC,SAAS,4CAA4C,YAAY;GACjE,cAAc;GACf,CAAC;AAEF,MAAIC,GAAW,UAAU,IAAI,CAAC,WAAW;AACvC,MAAS,uBAAuB;AAChC,WAAQ,KAAK,EAAE;;AAIjB,QAAM,GAAG,YAAY;GAAE,WAAW;GAAM,OAAO;GAAM,CAAC;AAGtD,QAAM,gCAAgC,YAAY,YAAY;AAE9D,KAAQ,YAAY,YAAY,0BAA0B,YAAY,GAAG;;CAE5E,CAAC"}
1
+ {"version":3,"file":"remove-BBs6Mv8t.mjs","names":["p.confirm","p.isCancel"],"sources":["../src/commands/profile/remove.ts"],"sourcesContent":["import { readFile, readdir, rm, writeFile } from \"node:fs/promises\";\nimport { join, relative } from \"node:path\";\nimport * as p from \"@clack/prompts\";\nimport { defineCommand } from \"citty\";\nimport { parse as parseYaml, stringify as stringifyYaml } from \"yaml\";\nimport { findSourceRoot } from \"../../utils/context-detection.js\";\n\n/**\n * Recursively collect all file paths in a directory (relative to baseDir)\n */\nasync function collectFiles(dir: string, baseDir: string): Promise<string[]> {\n const files: string[] = [];\n const entries = await readdir(dir, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = join(dir, entry.name);\n if (entry.isDirectory()) {\n files.push(...(await collectFiles(fullPath, baseDir)));\n } else {\n files.push(relative(baseDir, fullPath));\n }\n }\n\n return files;\n}\n\n/**\n * Remove a profile entry from baton.source.yaml if it exists in the profiles array\n */\nasync function removeProfileFromSourceManifest(\n sourceRoot: string,\n profileName: string,\n): Promise<void> {\n const manifestPath = join(sourceRoot, \"baton.source.yaml\");\n\n try {\n const content = await readFile(manifestPath, \"utf-8\");\n const manifest = parseYaml(content);\n\n if (manifest?.profiles && Array.isArray(manifest.profiles)) {\n const originalLength = manifest.profiles.length;\n manifest.profiles = manifest.profiles.filter(\n (profile: { name?: string; path?: string }) =>\n profile.name !== profileName && profile.path !== `profiles/${profileName}`,\n );\n\n if (manifest.profiles.length < originalLength) {\n // Remove empty profiles array\n if (manifest.profiles.length === 0) {\n manifest.profiles = undefined;\n }\n await writeFile(manifestPath, stringifyYaml(manifest), \"utf-8\");\n }\n }\n } catch {\n // If manifest can't be read/written, skip — the directory removal is the primary action\n }\n}\n\nexport const profileRemoveCommand = defineCommand({\n meta: {\n name: \"remove\",\n description: \"Remove a profile from the source repository\",\n },\n args: {\n name: {\n type: \"positional\",\n description: \"Profile name to remove\",\n required: true,\n },\n },\n async run({ args }) {\n p.intro(\"Remove 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 const profileName = args.name as string;\n const profileDir = join(sourceRoot, \"profiles\", profileName);\n\n // Check if profile directory exists\n let files: string[];\n try {\n files = await collectFiles(profileDir, sourceRoot);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n p.cancel(`Profile \"${profileName}\" does not exist in profiles/${profileName}/`);\n process.exit(1);\n }\n throw error;\n }\n\n // Warn about files that will be deleted\n if (files.length > 0) {\n p.log.warn(\"The following files will be deleted:\");\n for (const file of files) {\n p.log.info(` ${file}`);\n }\n } else {\n p.log.warn(`Directory profiles/${profileName}/ will be deleted.`);\n }\n\n // Confirm before removing\n const confirmed = await p.confirm({\n message: `Are you sure you want to remove profile \"${profileName}\"?`,\n initialValue: false,\n });\n\n if (p.isCancel(confirmed) || !confirmed) {\n p.cancel(\"Operation cancelled.\");\n process.exit(0);\n }\n\n // Remove the profile directory\n await rm(profileDir, { recursive: true, force: true });\n\n // Remove entry from baton.source.yaml if present\n await removeProfileFromSourceManifest(sourceRoot, profileName);\n\n p.outro(`Profile \"${profileName}\" removed from profiles/${profileName}/`);\n },\n});\n"],"mappings":";;;;;;;;;;AAUA,eAAe,aAAa,KAAa,SAAoC;CAC3E,MAAM,QAAkB,EAAE;CAC1B,MAAM,UAAU,MAAM,QAAQ,KAAK,EAAE,eAAe,MAAM,CAAC;AAE3D,MAAK,MAAM,SAAS,SAAS;EAC3B,MAAM,WAAW,KAAK,KAAK,MAAM,KAAK;AACtC,MAAI,MAAM,aAAa,CACrB,OAAM,KAAK,GAAI,MAAM,aAAa,UAAU,QAAQ,CAAE;MAEtD,OAAM,KAAK,SAAS,SAAS,SAAS,CAAC;;AAI3C,QAAO;;;;;AAMT,eAAe,gCACb,YACA,aACe;CACf,MAAM,eAAe,KAAK,YAAY,oBAAoB;AAE1D,KAAI;EAEF,MAAM,kCADU,MAAM,SAAS,cAAc,QAAQ,CAClB;AAEnC,MAAI,UAAU,YAAY,MAAM,QAAQ,SAAS,SAAS,EAAE;GAC1D,MAAM,iBAAiB,SAAS,SAAS;AACzC,YAAS,WAAW,SAAS,SAAS,QACnC,YACC,QAAQ,SAAS,eAAe,QAAQ,SAAS,YAAY,cAChE;AAED,OAAI,SAAS,SAAS,SAAS,gBAAgB;AAE7C,QAAI,SAAS,SAAS,WAAW,EAC/B,UAAS,WAAW;AAEtB,UAAM,UAAU,yCAA4B,SAAS,EAAE,QAAQ;;;SAG7D;;AAKV,MAAa,uBAAuB,cAAc;CAChD,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;;EAGjB,MAAM,cAAc,KAAK;EACzB,MAAM,aAAa,KAAK,YAAY,YAAY,YAAY;EAG5D,IAAI;AACJ,MAAI;AACF,WAAQ,MAAM,aAAa,YAAY,WAAW;WAC3C,OAAO;AACd,OAAK,MAAgC,SAAS,UAAU;AACtD,OAAS,YAAY,YAAY,+BAA+B,YAAY,GAAG;AAC/E,YAAQ,KAAK,EAAE;;AAEjB,SAAM;;AAIR,MAAI,MAAM,SAAS,GAAG;AACpB,KAAM,KAAK,uCAAuC;AAClD,QAAK,MAAM,QAAQ,MACjB,GAAM,KAAK,KAAK,OAAO;QAGzB,GAAM,KAAK,sBAAsB,YAAY,oBAAoB;EAInE,MAAM,YAAY,MAAMA,GAAU;GAChC,SAAS,4CAA4C,YAAY;GACjE,cAAc;GACf,CAAC;AAEF,MAAIC,GAAW,UAAU,IAAI,CAAC,WAAW;AACvC,MAAS,uBAAuB;AAChC,WAAQ,KAAK,EAAE;;AAIjB,QAAM,GAAG,YAAY;GAAE,WAAW;GAAM,OAAO;GAAM,CAAC;AAGtD,QAAM,gCAAgC,YAAY,YAAY;AAE9D,KAAQ,YAAY,YAAY,0BAA0B,YAAY,GAAG;;CAE5E,CAAC"}
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import { n as __require, r as __toESM, t as __commonJSMin } from "./chunk-BbwQpWto.mjs";
3
- import { m as require_dist } from "./context-detection-mMNLg_4F.mjs";
4
- import { r as AGENT_PATHS } from "./agent-detection-C5gaTtah.mjs";
3
+ import { m as require_dist } from "./context-detection-DqOTnD6_.mjs";
4
+ import { i as AGENT_PATHS, r as evaluateDetection } from "./agent-detection-DTiVeO5W.mjs";
5
5
  import { d as esm_default, m as simpleGit } from "./esm-BagM-kVd.mjs";
6
- import { access, constants, mkdir, readFile, readdir, rm, stat, symlink, writeFile } from "node:fs/promises";
7
- import path, { dirname, isAbsolute, join, relative, resolve } from "node:path";
6
+ import { access, mkdir, readFile, readdir, rm, stat, symlink, writeFile } from "node:fs/promises";
7
+ import { dirname, isAbsolute, join, relative, resolve } from "node:path";
8
8
  import { promisify } from "node:util";
9
9
  import { homedir } from "node:os";
10
10
  import { createHash } from "node:crypto";
@@ -4106,7 +4106,7 @@ var BaseAdapter = class {
4106
4106
  memoryFilename = "AGENTS.md";
4107
4107
  async isInstalled() {
4108
4108
  try {
4109
- const { detectInstalledAgents } = await import("./agent-detection-BW5-jGuR.mjs");
4109
+ const { detectInstalledAgents } = await import("./agent-detection-l61K-AbU.mjs");
4110
4110
  return (await detectInstalledAgents()).includes(this.key);
4111
4111
  } catch {
4112
4112
  return false;
@@ -4535,27 +4535,111 @@ function getAdaptersForKeys(keys) {
4535
4535
  const idePlatformRegistry = {
4536
4536
  vscode: {
4537
4537
  targetDir: ".vscode",
4538
- detection: ["code", "~/.vscode/"]
4538
+ detectionConfig: { groups: [
4539
+ [{
4540
+ type: "binary",
4541
+ name: "code"
4542
+ }],
4543
+ [{
4544
+ type: "app",
4545
+ name: "Visual Studio Code.app"
4546
+ }],
4547
+ [{
4548
+ type: "directory",
4549
+ path: "~/.vscode/",
4550
+ markerFile: "extensions"
4551
+ }]
4552
+ ] }
4539
4553
  },
4540
4554
  jetbrains: {
4541
4555
  targetDir: ".idea",
4542
- detection: ["idea", "~/.config/JetBrains/"]
4556
+ detectionConfig: { groups: [
4557
+ [{
4558
+ type: "binary",
4559
+ name: "idea"
4560
+ }],
4561
+ [{
4562
+ type: "directory",
4563
+ path: "~/.config/JetBrains/",
4564
+ platforms: ["linux"]
4565
+ }],
4566
+ [{
4567
+ type: "directory",
4568
+ path: "~/Library/Application Support/JetBrains/",
4569
+ platforms: ["darwin"]
4570
+ }]
4571
+ ] }
4543
4572
  },
4544
4573
  cursor: {
4545
4574
  targetDir: ".cursor",
4546
- detection: ["cursor", "~/.cursor/"]
4575
+ detectionConfig: { groups: [
4576
+ [{
4577
+ type: "binary",
4578
+ name: "cursor"
4579
+ }],
4580
+ [{
4581
+ type: "app",
4582
+ name: "Cursor.app"
4583
+ }],
4584
+ [{
4585
+ type: "directory",
4586
+ path: "~/.cursor/",
4587
+ markerFile: "extensions"
4588
+ }]
4589
+ ] }
4547
4590
  },
4548
4591
  windsurf: {
4549
4592
  targetDir: ".windsurf",
4550
- detection: ["windsurf", "~/.windsurf/"]
4593
+ detectionConfig: { groups: [
4594
+ [{
4595
+ type: "binary",
4596
+ name: "windsurf"
4597
+ }],
4598
+ [{
4599
+ type: "app",
4600
+ name: "Windsurf.app"
4601
+ }],
4602
+ [{
4603
+ type: "directory",
4604
+ path: "~/.windsurf/",
4605
+ markerFile: "extensions"
4606
+ }]
4607
+ ] }
4551
4608
  },
4552
4609
  antigravity: {
4553
4610
  targetDir: ".antigravity",
4554
- detection: ["antigravity", "~/.antigravity/"]
4611
+ detectionConfig: { groups: [
4612
+ [{
4613
+ type: "binary",
4614
+ name: "agy"
4615
+ }],
4616
+ [{
4617
+ type: "binary",
4618
+ name: "antigravity"
4619
+ }],
4620
+ [{
4621
+ type: "app",
4622
+ name: "Antigravity.app"
4623
+ }]
4624
+ ] }
4555
4625
  },
4556
4626
  zed: {
4557
4627
  targetDir: ".config/zed",
4558
- detection: ["zed", "~/.config/zed/"]
4628
+ detectionConfig: { groups: [
4629
+ [{
4630
+ type: "binary",
4631
+ name: "zed"
4632
+ }],
4633
+ [{
4634
+ type: "app",
4635
+ name: "Zed.app"
4636
+ }],
4637
+ [{
4638
+ type: "directory",
4639
+ path: "~/.config/zed/",
4640
+ markerFile: "settings.json"
4641
+ }]
4642
+ ] }
4559
4643
  }
4560
4644
  };
4561
4645
  /**
@@ -4633,6 +4717,21 @@ function addPathPattern(patterns, path) {
4633
4717
  if (lastSlash > 0) patterns.add(path.substring(0, lastSlash + 1));
4634
4718
  else if (path) patterns.add(path);
4635
4719
  }
4720
+ /**
4721
+ * Ensures `.baton/` is listed in the project's .gitignore.
4722
+ *
4723
+ * Uses the same "# Baton cache" format as `baton init`.
4724
+ * Idempotent: no-op if `.baton/` is already present (by any mechanism).
4725
+ */
4726
+ async function ensureBatonDirGitignored(projectRoot) {
4727
+ const gitignorePath = join(projectRoot, ".gitignore");
4728
+ let content = "";
4729
+ try {
4730
+ content = await readFile(gitignorePath, "utf-8");
4731
+ } catch {}
4732
+ if (content.includes(".baton/")) return;
4733
+ await writeFile(gitignorePath, content ? `${content.trimEnd()}\n\n# Baton cache\n.baton/\n` : "# Baton cache\n.baton/\n", "utf-8");
4734
+ }
4636
4735
  const BATON_SECTION_START = "# Baton managed";
4637
4736
  const BATON_SECTION_END = "# End Baton managed";
4638
4737
  /**
@@ -6555,15 +6654,17 @@ async function resolveProfileChain(manifest, source, baseDir) {
6555
6654
  * @param chain - Accumulator for resolved profiles
6556
6655
  * @param visited - Set of visited sources (for circular detection)
6557
6656
  * @param path - Current path (for error messages)
6657
+ * @param localPath - Resolved local directory path for this profile
6558
6658
  */
6559
- async function resolveChainRecursive(manifest, source, baseDir, chain, visited, path) {
6659
+ async function resolveChainRecursive(manifest, source, baseDir, chain, visited, path, localPath) {
6560
6660
  if (path.length >= MAX_CHAIN_DEPTH) throw new Error(`Profile inheritance chain exceeds maximum depth of ${MAX_CHAIN_DEPTH}. Chain: ${path.join(" -> ")} -> ${manifest.name}`);
6561
6661
  if (visited.has(source)) throw new CircularInheritanceError(`Circular profile inheritance detected: ${[...path, manifest.name].join(" -> ")}`);
6562
6662
  const currentVisited = new Set(visited);
6563
6663
  currentVisited.add(source);
6564
6664
  path.push(manifest.name);
6565
6665
  if (manifest.extends && manifest.extends.length > 0) for (const extendSource of manifest.extends) try {
6566
- await resolveChainRecursive(await loadProfileFromSource(extendSource, baseDir), extendSource, baseDir, chain, currentVisited, [...path]);
6666
+ const { manifest: parentManifest, localPath: parentLocalPath } = await loadProfileFromSource(extendSource, baseDir);
6667
+ await resolveChainRecursive(parentManifest, extendSource, baseDir, chain, currentVisited, [...path], parentLocalPath);
6567
6668
  } catch (error) {
6568
6669
  if (error instanceof CircularInheritanceError) throw error;
6569
6670
  if (error instanceof Error && error.message.includes("exceeds maximum depth")) throw error;
@@ -6571,7 +6672,8 @@ async function resolveChainRecursive(manifest, source, baseDir, chain, visited,
6571
6672
  chain.push({
6572
6673
  manifest,
6573
6674
  source,
6574
- name: manifest.name
6675
+ name: manifest.name,
6676
+ localPath
6575
6677
  });
6576
6678
  path.pop();
6577
6679
  }
@@ -6580,19 +6682,35 @@ async function resolveChainRecursive(manifest, source, baseDir, chain, visited,
6580
6682
  *
6581
6683
  * @param source - Source URL or path
6582
6684
  * @param baseDir - Base directory for resolving relative paths
6583
- * @returns Loaded profile manifest
6685
+ * @returns Loaded profile manifest and resolved local directory path
6584
6686
  */
6585
6687
  async function loadProfileFromSource(source, baseDir) {
6586
6688
  const parsed = parseSource(source);
6587
- if (parsed.provider === "local") return await loadProfileManifest(resolve(resolve(baseDir, parsed.path), "baton.profile.yaml"));
6588
- if (parsed.provider === "file") return await loadProfileManifest(resolve(parsed.path.startsWith("/") ? parsed.path : resolve(baseDir, parsed.path), "baton.profile.yaml"));
6689
+ if (parsed.provider === "local") {
6690
+ const absolutePath = resolve(baseDir, parsed.path);
6691
+ return {
6692
+ manifest: await loadProfileManifest(resolve(absolutePath, "baton.profile.yaml")),
6693
+ localPath: absolutePath
6694
+ };
6695
+ }
6696
+ if (parsed.provider === "file") {
6697
+ const absolutePath = parsed.path.startsWith("/") ? parsed.path : resolve(baseDir, parsed.path);
6698
+ return {
6699
+ manifest: await loadProfileManifest(resolve(absolutePath, "baton.profile.yaml")),
6700
+ localPath: absolutePath
6701
+ };
6702
+ }
6589
6703
  if (parsed.provider === "npm") throw new Error("NPM sources are not yet implemented in inheritance chain");
6590
6704
  const subpath = parsed.provider !== "git" ? parsed.subpath : void 0;
6591
- return await loadProfileManifest(resolve((await cloneGitSource({
6705
+ const cloned = await cloneGitSource({
6592
6706
  url: parsed.url,
6593
6707
  ref: parsed.ref,
6594
6708
  subpath
6595
- })).localPath, "baton.profile.yaml"));
6709
+ });
6710
+ return {
6711
+ manifest: await loadProfileManifest(resolve(cloned.localPath, "baton.profile.yaml")),
6712
+ localPath: cloned.localPath
6713
+ };
6596
6714
  }
6597
6715
 
6598
6716
  //#endregion
@@ -14329,40 +14447,12 @@ function inferNameFromUrl(url) {
14329
14447
  */
14330
14448
  let cachedIdes = null;
14331
14449
  /**
14332
- * Check if a command exists in PATH
14333
- */
14334
- async function commandExists(command) {
14335
- try {
14336
- const { execa } = await import("./execa-RdtdAT4S.mjs");
14337
- await execa("which", [command]);
14338
- return true;
14339
- } catch {
14340
- return false;
14341
- }
14342
- }
14343
- /**
14344
- * Check if a directory exists
14345
- */
14346
- async function directoryExists(path) {
14347
- try {
14348
- await access(path, constants.R_OK);
14349
- return true;
14350
- } catch {
14351
- return false;
14352
- }
14353
- }
14354
- /**
14355
- * Detect if a specific IDE platform is installed
14450
+ * Detect if a specific IDE platform is installed using structured detectionConfig.
14356
14451
  */
14357
14452
  async function isIdeInstalled(ideKey) {
14358
14453
  const ideConfig = idePlatformRegistry[ideKey];
14359
14454
  if (!ideConfig) return false;
14360
- for (const detection of ideConfig.detection) if (detection.startsWith("~/")) {
14361
- if (await directoryExists(join(homedir(), detection.slice(2)))) return true;
14362
- } else if (detection.startsWith(".")) {
14363
- if (await directoryExists(join(homedir(), detection))) return true;
14364
- } else if (await commandExists(detection)) return true;
14365
- return false;
14455
+ return evaluateDetection(ideConfig.detectionConfig);
14366
14456
  }
14367
14457
  /**
14368
14458
  * Detect all installed IDE platforms.
@@ -14430,5 +14520,125 @@ function computeDimensionIntersection(developerItems, profileItems) {
14430
14520
  }
14431
14521
 
14432
14522
  //#endregion
14433
- export { readLock as A, getAdaptersForKeys as B, resolveProfileSupport as C, discoverProfilesInSourceRepo as D, placeFile as E, updateGitignore as F, loadProjectManifest as G, parseSource as H, getIdePlatformTargetDir as I, SourceParseError as J, KEBAB_CASE_REGEX as K, getRegisteredIdePlatforms as L, resolveVersion as M, cloneGitSource as N, findSourceManifest as O, collectProfileSupportPatterns as P, idePlatformRegistry as R, mergeContentParts as S, detectLegacyPaths as T, loadLockfile as U, parseFrontmatter as V, loadProfileManifest as W, getAgentPath as X, getAgentConfig as Y, getAllAgentKeys as Z, mergeSkills as _, getDefaultGlobalSource as a, isLockedProfile as b, getGlobalSources as c, setGlobalIdePlatforms as d, require_lib as f, mergeRulesWithWarnings as g, mergeRules as h, addGlobalSource as i, writeLock as j, generateLock as k, removeGlobalSource as l, mergeMemoryWithWarnings as m, clearIdeCache as n, getGlobalAiTools as o, mergeMemory as p, FileNotFoundError as q, detectInstalledIdes as r, getGlobalIdePlatforms as s, computeIntersection as t, setGlobalAiTools as u, mergeSkillsWithWarnings as v, resolveProfileChain as w, sortProfilesByWeight as x, getProfileWeight as y, isKnownIdePlatform as z };
14434
- //# sourceMappingURL=src-Dh0ZvHbV.mjs.map
14523
+ //#region ../core/src/preferences/preferences-schema.ts
14524
+ /**
14525
+ * Schema for .baton/preferences.yaml - project-level tool and IDE preferences
14526
+ *
14527
+ * Controls which AI tools and IDE platforms Baton configures for this project.
14528
+ * Resolution chain: Detection -> Global Config -> Project Preferences
14529
+ */
14530
+ const projectPreferencesSchema = objectType({
14531
+ version: literalType("1.0"),
14532
+ ai: objectType({
14533
+ useGlobal: booleanType(),
14534
+ tools: arrayType(stringType()).default([])
14535
+ }).default({
14536
+ useGlobal: true,
14537
+ tools: []
14538
+ }),
14539
+ ide: objectType({
14540
+ useGlobal: booleanType(),
14541
+ platforms: arrayType(stringType()).default([])
14542
+ }).default({
14543
+ useGlobal: true,
14544
+ platforms: []
14545
+ })
14546
+ });
14547
+
14548
+ //#endregion
14549
+ //#region ../core/src/preferences/preferences-io.ts
14550
+ /**
14551
+ * Returns the path to the project preferences file.
14552
+ *
14553
+ * @param projectRoot - Absolute path to the project root
14554
+ * @returns Absolute path to .baton/preferences.yaml
14555
+ */
14556
+ function getPreferencesPath(projectRoot) {
14557
+ return join(projectRoot, ".baton", "preferences.yaml");
14558
+ }
14559
+ /**
14560
+ * Reads project preferences from .baton/preferences.yaml
14561
+ *
14562
+ * @param projectRoot - Absolute path to the project root
14563
+ * @returns Parsed ProjectPreferences, or null if the file doesn't exist
14564
+ * @throws {ManifestValidationError} If the file exists but contains invalid data
14565
+ */
14566
+ async function readProjectPreferences(projectRoot) {
14567
+ const prefsPath = getPreferencesPath(projectRoot);
14568
+ try {
14569
+ const parsed = (0, import_dist.parse)(await readFile(prefsPath, "utf-8"));
14570
+ return projectPreferencesSchema.parse(parsed);
14571
+ } catch (error) {
14572
+ if (error.code === "ENOENT") return null;
14573
+ throw new ManifestValidationError(`Invalid project preferences at ${prefsPath}: ${error.message}`, { cause: error });
14574
+ }
14575
+ }
14576
+ /**
14577
+ * Writes project preferences to .baton/preferences.yaml
14578
+ *
14579
+ * Creates the .baton/ directory if it doesn't exist.
14580
+ *
14581
+ * @param projectRoot - Absolute path to the project root
14582
+ * @param prefs - The preferences to write (will be validated)
14583
+ * @throws {ManifestValidationError} If preferences validation fails
14584
+ */
14585
+ async function writeProjectPreferences(projectRoot, prefs) {
14586
+ const validated = projectPreferencesSchema.parse(prefs);
14587
+ const prefsPath = getPreferencesPath(projectRoot);
14588
+ await mkdir(dirname(prefsPath), { recursive: true });
14589
+ await writeFile(prefsPath, (0, import_dist.stringify)(validated), "utf-8");
14590
+ await ensureBatonDirGitignored(projectRoot);
14591
+ }
14592
+
14593
+ //#endregion
14594
+ //#region ../core/src/preferences/preferences-resolver.ts
14595
+ /**
14596
+ * Resolves the effective AI tools and IDE platforms for a project.
14597
+ *
14598
+ * Resolution chain:
14599
+ * 1. If no .baton/preferences.yaml exists → use global config
14600
+ * 2. If useGlobal: true → use global config for that dimension
14601
+ * 3. If useGlobal: false → use project-level preferences
14602
+ *
14603
+ * AI and IDE dimensions are resolved independently, allowing mixed configs
14604
+ * (e.g., AI from project, IDE from global).
14605
+ *
14606
+ * @param projectRoot - Absolute path to the project root
14607
+ * @returns Resolved preferences with source attribution
14608
+ */
14609
+ async function resolvePreferences(projectRoot) {
14610
+ const prefs = await readProjectPreferences(projectRoot);
14611
+ if (!prefs) {
14612
+ const [tools, platforms] = await Promise.all([getGlobalAiTools(), getGlobalIdePlatforms()]);
14613
+ return {
14614
+ ai: {
14615
+ source: "global",
14616
+ tools
14617
+ },
14618
+ ide: {
14619
+ source: "global",
14620
+ platforms
14621
+ }
14622
+ };
14623
+ }
14624
+ return {
14625
+ ai: prefs.ai.useGlobal ? {
14626
+ source: "global",
14627
+ tools: await getGlobalAiTools()
14628
+ } : {
14629
+ source: "project",
14630
+ tools: prefs.ai.tools
14631
+ },
14632
+ ide: prefs.ide.useGlobal ? {
14633
+ source: "global",
14634
+ platforms: await getGlobalIdePlatforms()
14635
+ } : {
14636
+ source: "project",
14637
+ platforms: prefs.ide.platforms
14638
+ }
14639
+ };
14640
+ }
14641
+
14642
+ //#endregion
14643
+ export { getAgentConfig as $, discoverProfilesInSourceRepo as A, getRegisteredIdePlatforms as B, isLockedProfile as C, resolveProfileChain as D, resolveProfileSupport as E, resolveVersion as F, parseFrontmatter as G, isKnownIdePlatform as H, cloneGitSource as I, loadProfileManifest as J, parseSource as K, collectProfileSupportPatterns as L, generateLock as M, readLock as N, detectLegacyPaths as O, writeLock as P, SourceParseError as Q, updateGitignore as R, getProfileWeight as S, mergeContentParts as T, getAdaptersForKeys as U, idePlatformRegistry as V, getAllAdapters as W, KEBAB_CASE_REGEX as X, loadProjectManifest as Y, FileNotFoundError as Z, mergeMemoryWithWarnings as _, clearIdeCache as a, mergeSkills as b, getDefaultGlobalSource as c, getGlobalSources as d, getAgentPath as et, removeGlobalSource as f, mergeMemory as g, require_lib as h, computeIntersection as i, findSourceManifest as j, placeFile as k, getGlobalAiTools as l, setGlobalIdePlatforms as m, readProjectPreferences as n, detectInstalledIdes as o, setGlobalAiTools as p, loadLockfile as q, writeProjectPreferences as r, addGlobalSource as s, resolvePreferences as t, getAllAgentKeys as tt, getGlobalIdePlatforms as u, mergeRules as v, sortProfilesByWeight as w, mergeSkillsWithWarnings as x, mergeRulesWithWarnings as y, getIdePlatformTargetDir as z };
14644
+ //# sourceMappingURL=src-BgiJfm14.mjs.map