@baton-dx/cli 0.2.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { c as Ve, h as defineCommand, i as Le, l as We, n as isInSourceRepo } from "./context-detection-DqOTnD6_.mjs";
3
- import { D as discoverProfilesInSourceRepo } from "./src-DBbk6iAs.mjs";
3
+ import { A as discoverProfilesInSourceRepo } from "./src-C3-Vz-R7.mjs";
4
4
  import "./agent-detection-DTiVeO5W.mjs";
5
5
  import "./esm-BagM-kVd.mjs";
6
6
 
@@ -53,4 +53,4 @@ Note: Must be run from a source repository (directory with baton.source.yaml)`
53
53
 
54
54
  //#endregion
55
55
  export { profileListCommand };
56
- //# sourceMappingURL=list-CGmYHSHW.mjs.map
56
+ //# sourceMappingURL=list-DmzVXCNF.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"list-CGmYHSHW.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-DmzVXCNF.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"}
@@ -3,7 +3,7 @@ import { n as __require, r as __toESM, t as __commonJSMin } from "./chunk-BbwQpW
3
3
  import { m as require_dist } from "./context-detection-DqOTnD6_.mjs";
4
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, mkdir, readFile, readdir, rm, stat, symlink, writeFile } from "node:fs/promises";
6
+ import { access, mkdir, readFile, readdir, rm, rmdir, stat, symlink, unlink, writeFile } from "node:fs/promises";
7
7
  import { dirname, isAbsolute, join, relative, resolve } from "node:path";
8
8
  import { promisify } from "node:util";
9
9
  import { homedir } from "node:os";
@@ -4717,6 +4717,21 @@ function addPathPattern(patterns, path) {
4717
4717
  if (lastSlash > 0) patterns.add(path.substring(0, lastSlash + 1));
4718
4718
  else if (path) patterns.add(path);
4719
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
+ }
4720
4735
  const BATON_SECTION_START = "# Baton managed";
4721
4736
  const BATON_SECTION_END = "# End Baton managed";
4722
4737
  /**
@@ -6399,6 +6414,47 @@ async function readLock(filePath) {
6399
6414
  }
6400
6415
  }
6401
6416
 
6417
+ //#endregion
6418
+ //#region ../core/src/lockfile/cleanup.ts
6419
+ /**
6420
+ * Removes placed files and directories, then cleans up empty parent directories.
6421
+ *
6422
+ * Handles both files and directories (fixes EISDIR bug where unlink fails on dirs).
6423
+ * Already-deleted paths (ENOENT) are silently skipped.
6424
+ * After each removal, walks up and removes empty parent directories up to projectRoot.
6425
+ *
6426
+ * @param filePaths - Paths to remove (relative to projectRoot or absolute)
6427
+ * @param projectRoot - Project root directory (parent cleanup stops here)
6428
+ * @returns Count of successfully removed items
6429
+ */
6430
+ async function removePlacedFiles(filePaths, projectRoot) {
6431
+ let removedCount = 0;
6432
+ for (const filePath of filePaths) {
6433
+ const absolutePath = isAbsolute(filePath) ? filePath : resolve(projectRoot, filePath);
6434
+ try {
6435
+ if ((await stat(absolutePath)).isDirectory()) await rm(absolutePath, {
6436
+ recursive: true,
6437
+ force: true
6438
+ });
6439
+ else await unlink(absolutePath);
6440
+ removedCount++;
6441
+ let dir = dirname(absolutePath);
6442
+ while (dir !== projectRoot && dir.startsWith(projectRoot)) try {
6443
+ if ((await readdir(dir)).length === 0) {
6444
+ await rmdir(dir);
6445
+ dir = dirname(dir);
6446
+ } else break;
6447
+ } catch {
6448
+ break;
6449
+ }
6450
+ } catch (error) {
6451
+ if (error instanceof Error && "code" in error && error.code === "ENOENT") continue;
6452
+ throw error;
6453
+ }
6454
+ }
6455
+ return removedCount;
6456
+ }
6457
+
6402
6458
  //#endregion
6403
6459
  //#region ../core/src/sources/load-profile-safe.ts
6404
6460
  /**
@@ -14505,5 +14561,125 @@ function computeDimensionIntersection(developerItems, profileItems) {
14505
14561
  }
14506
14562
 
14507
14563
  //#endregion
14508
- 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 };
14509
- //# sourceMappingURL=src-DBbk6iAs.mjs.map
14564
+ //#region ../core/src/preferences/preferences-schema.ts
14565
+ /**
14566
+ * Schema for .baton/preferences.yaml - project-level tool and IDE preferences
14567
+ *
14568
+ * Controls which AI tools and IDE platforms Baton configures for this project.
14569
+ * Resolution chain: Detection -> Global Config -> Project Preferences
14570
+ */
14571
+ const projectPreferencesSchema = objectType({
14572
+ version: literalType("1.0"),
14573
+ ai: objectType({
14574
+ useGlobal: booleanType(),
14575
+ tools: arrayType(stringType()).default([])
14576
+ }).default({
14577
+ useGlobal: true,
14578
+ tools: []
14579
+ }),
14580
+ ide: objectType({
14581
+ useGlobal: booleanType(),
14582
+ platforms: arrayType(stringType()).default([])
14583
+ }).default({
14584
+ useGlobal: true,
14585
+ platforms: []
14586
+ })
14587
+ });
14588
+
14589
+ //#endregion
14590
+ //#region ../core/src/preferences/preferences-io.ts
14591
+ /**
14592
+ * Returns the path to the project preferences file.
14593
+ *
14594
+ * @param projectRoot - Absolute path to the project root
14595
+ * @returns Absolute path to .baton/preferences.yaml
14596
+ */
14597
+ function getPreferencesPath(projectRoot) {
14598
+ return join(projectRoot, ".baton", "preferences.yaml");
14599
+ }
14600
+ /**
14601
+ * Reads project preferences from .baton/preferences.yaml
14602
+ *
14603
+ * @param projectRoot - Absolute path to the project root
14604
+ * @returns Parsed ProjectPreferences, or null if the file doesn't exist
14605
+ * @throws {ManifestValidationError} If the file exists but contains invalid data
14606
+ */
14607
+ async function readProjectPreferences(projectRoot) {
14608
+ const prefsPath = getPreferencesPath(projectRoot);
14609
+ try {
14610
+ const parsed = (0, import_dist.parse)(await readFile(prefsPath, "utf-8"));
14611
+ return projectPreferencesSchema.parse(parsed);
14612
+ } catch (error) {
14613
+ if (error.code === "ENOENT") return null;
14614
+ throw new ManifestValidationError(`Invalid project preferences at ${prefsPath}: ${error.message}`, { cause: error });
14615
+ }
14616
+ }
14617
+ /**
14618
+ * Writes project preferences to .baton/preferences.yaml
14619
+ *
14620
+ * Creates the .baton/ directory if it doesn't exist.
14621
+ *
14622
+ * @param projectRoot - Absolute path to the project root
14623
+ * @param prefs - The preferences to write (will be validated)
14624
+ * @throws {ManifestValidationError} If preferences validation fails
14625
+ */
14626
+ async function writeProjectPreferences(projectRoot, prefs) {
14627
+ const validated = projectPreferencesSchema.parse(prefs);
14628
+ const prefsPath = getPreferencesPath(projectRoot);
14629
+ await mkdir(dirname(prefsPath), { recursive: true });
14630
+ await writeFile(prefsPath, (0, import_dist.stringify)(validated), "utf-8");
14631
+ await ensureBatonDirGitignored(projectRoot);
14632
+ }
14633
+
14634
+ //#endregion
14635
+ //#region ../core/src/preferences/preferences-resolver.ts
14636
+ /**
14637
+ * Resolves the effective AI tools and IDE platforms for a project.
14638
+ *
14639
+ * Resolution chain:
14640
+ * 1. If no .baton/preferences.yaml exists → use global config
14641
+ * 2. If useGlobal: true → use global config for that dimension
14642
+ * 3. If useGlobal: false → use project-level preferences
14643
+ *
14644
+ * AI and IDE dimensions are resolved independently, allowing mixed configs
14645
+ * (e.g., AI from project, IDE from global).
14646
+ *
14647
+ * @param projectRoot - Absolute path to the project root
14648
+ * @returns Resolved preferences with source attribution
14649
+ */
14650
+ async function resolvePreferences(projectRoot) {
14651
+ const prefs = await readProjectPreferences(projectRoot);
14652
+ if (!prefs) {
14653
+ const [tools, platforms] = await Promise.all([getGlobalAiTools(), getGlobalIdePlatforms()]);
14654
+ return {
14655
+ ai: {
14656
+ source: "global",
14657
+ tools
14658
+ },
14659
+ ide: {
14660
+ source: "global",
14661
+ platforms
14662
+ }
14663
+ };
14664
+ }
14665
+ return {
14666
+ ai: prefs.ai.useGlobal ? {
14667
+ source: "global",
14668
+ tools: await getGlobalAiTools()
14669
+ } : {
14670
+ source: "project",
14671
+ tools: prefs.ai.tools
14672
+ },
14673
+ ide: prefs.ide.useGlobal ? {
14674
+ source: "global",
14675
+ platforms: await getGlobalIdePlatforms()
14676
+ } : {
14677
+ source: "project",
14678
+ platforms: prefs.ide.platforms
14679
+ }
14680
+ };
14681
+ }
14682
+
14683
+ //#endregion
14684
+ export { SourceParseError as $, discoverProfilesInSourceRepo as A, getIdePlatformTargetDir as B, isLockedProfile as C, resolveProfileChain as D, resolveProfileSupport as E, writeLock as F, getAllAdapters as G, idePlatformRegistry as H, resolveVersion as I, loadLockfile as J, parseFrontmatter as K, cloneGitSource as L, removePlacedFiles as M, generateLock as N, detectLegacyPaths as O, readLock as P, FileNotFoundError as Q, collectProfileSupportPatterns as R, getProfileWeight as S, mergeContentParts as T, isKnownIdePlatform as U, getRegisteredIdePlatforms as V, getAdaptersForKeys as W, loadProjectManifest as X, loadProfileManifest as Y, KEBAB_CASE_REGEX as Z, mergeMemoryWithWarnings as _, clearIdeCache as a, mergeSkills as b, getDefaultGlobalSource as c, getGlobalSources as d, getAgentConfig 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, getAllAgentKeys as nt, detectInstalledIdes as o, setGlobalAiTools as p, parseSource as q, writeProjectPreferences as r, addGlobalSource as s, resolvePreferences as t, getAgentPath as tt, getGlobalIdePlatforms as u, mergeRules as v, sortProfilesByWeight as w, mergeSkillsWithWarnings as x, mergeRulesWithWarnings as y, updateGitignore as z };
14685
+ //# sourceMappingURL=src-C3-Vz-R7.mjs.map