@baton-dx/cli 0.4.2 → 0.4.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.
@@ -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-DdbrKid3.mjs";
4
- import { et as KEBAB_CASE_REGEX, h as require_lib } from "./src-BPYdPWlV.mjs";
4
+ import { _ as require_lib, nt as KEBAB_CASE_REGEX } from "./src-YrWWPWNR.mjs";
5
5
  import "./ai-tool-detection-CMsBNa9e.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-W7AYGROv.mjs.map
82
+ //# sourceMappingURL=create-C1zm03eE.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"create-W7AYGROv.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"}
1
+ {"version":3,"file":"create-C1zm03eE.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,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, 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-DdbrKid3.mjs";
4
- import { $ as loadProjectManifest, A as placeFile, B as ensureBatonDirGitignored, C as getProfileWeight, D as resolveProfileSupport, E as mergeContentParts, F as readLock, G as idePlatformRegistry, H as updateGitignore, I as writeLock, J as getAllAIToolAdapters, K as isKnownIdePlatform, L as resolveVersion, M as findSourceManifest, N as removePlacedFiles, O as resolveProfileChain, P as generateLock, Q as loadProfileManifest, R as cloneGitSource, S as mergeSkillsWithWarnings, T as sortProfilesByWeight, U as getIdePlatformTargetDir, V as removeGitignoreManagedSection, W as getRegisteredIdePlatforms, X as parseSource, Y as parseFrontmatter, Z as loadLockfile, _ as mergeMemoryWithWarnings, a as clearIdeCache, at as getAllAIToolKeys, b as mergeRulesWithWarnings, c as getDefaultGlobalSource, d as getGlobalSources, et as KEBAB_CASE_REGEX, f as removeGlobalSource, g as mergeMemory, h as require_lib, i as computeIntersection, it as getAIToolPath, j as discoverProfilesInSourceRepo, k as detectLegacyPaths, l as getGlobalAiTools, m as setGlobalIdePlatforms, n as readProjectPreferences, nt as SourceParseError, o as detectInstalledIdes, p as setGlobalAiTools, q as getAIToolAdaptersForKeys, r as writeProjectPreferences, rt as getAIToolConfig, s as addGlobalSource, t as resolvePreferences, tt as FileNotFoundError, u as getGlobalIdePlatforms, v as mergeAgentsWithWarnings, w as isLockedProfile, x as mergeSkills, y as mergeRules, z as collectComprehensivePatterns } from "./src-BPYdPWlV.mjs";
4
+ import { $ as loadLockfile, A as resolveProfileChain, B as cloneGitSource, C as mergeSkills, D as sortProfilesByWeight, E as isLockedProfile, F as removePlacedFiles, G as getIdePlatformTargetDir, H as ensureBatonDirGitignored, I as generateLock, J as isKnownIdePlatform, K as getRegisteredIdePlatforms, L as readLock, M as placeFile, N as discoverProfilesInSourceRepo, O as mergeContentParts, P as findSourceManifest, Q as parseSource, R as writeLock, S as mergeRulesWithWarnings, T as getProfileWeight, U as removeGitignoreManagedSection, V as collectComprehensivePatterns, W as updateGitignore, X as getAllAIToolAdapters, Y as getAIToolAdaptersForKeys, Z as parseFrontmatter, _ as require_lib, a as clearIdeCache, at as getAIToolConfig, b as mergeAgentsWithWarnings, c as getDefaultGlobalSource, d as getGlobalSources, et as loadProfileManifest, f as loadGlobalConfig, g as setGlobalIdePlatforms, h as setGlobalAiTools, i as computeIntersection, it as SourceParseError, j as detectLegacyPaths, k as resolveProfileSupport, l as getGlobalAiTools, m as saveGlobalConfig, n as readProjectPreferences, nt as KEBAB_CASE_REGEX, o as detectInstalledIdes, ot as getAIToolPath, p as removeGlobalSource, q as idePlatformRegistry, r as writeProjectPreferences, rt as FileNotFoundError, s as addGlobalSource, st as getAllAIToolKeys, t as resolvePreferences, tt as loadProjectManifest, u as getGlobalIdePlatforms, v as mergeMemory, w as mergeSkillsWithWarnings, x as mergeRules, y as mergeMemoryWithWarnings, z as resolveVersion } from "./src-YrWWPWNR.mjs";
5
5
  import { n as detectInstalledAITools, t as clearAIToolCache } from "./ai-tool-detection-CMsBNa9e.mjs";
6
6
  import { d as esm_default } from "./esm-BagM-kVd.mjs";
7
7
  import { access, mkdir, readFile, readdir, rm, stat, writeFile } from "node:fs/promises";
@@ -418,7 +418,70 @@ function formatIntersectionSummary(intersection) {
418
418
  }
419
419
 
420
420
  //#endregion
421
- //#region src/commands/config.ts
421
+ //#region src/commands/config/set.ts
422
+ /**
423
+ * Set a value in the global Baton config (~/.baton/config.yaml).
424
+ *
425
+ * Supports dotted paths like "sync.cacheTtlHours" to set nested values.
426
+ * Usage: baton config set <key> <value>
427
+ */
428
+ const configSetCommand = defineCommand({
429
+ meta: {
430
+ name: "set",
431
+ description: "Set a global config value (e.g., baton config set sync.cacheTtlHours 1)"
432
+ },
433
+ args: {
434
+ key: {
435
+ type: "positional",
436
+ description: "Config key using dot notation (e.g., sync.cacheTtlHours)",
437
+ required: true
438
+ },
439
+ value: {
440
+ type: "positional",
441
+ description: "Value to set",
442
+ required: true
443
+ }
444
+ },
445
+ async run({ args }) {
446
+ const { key, value } = args;
447
+ const segments = key.split(".");
448
+ if (segments.length === 0 || segments.some((s) => s === "")) {
449
+ Ne(`Invalid config key: "${key}"`);
450
+ process.exit(1);
451
+ }
452
+ const config = await loadGlobalConfig();
453
+ const parsed = parseValue(value);
454
+ setNestedValue(config, segments, parsed);
455
+ try {
456
+ await saveGlobalConfig(config);
457
+ } catch (error) {
458
+ Ne(`Invalid config value: ${error instanceof Error ? error.message : String(error)}`);
459
+ process.exit(1);
460
+ }
461
+ R.success(`Set ${key} = ${JSON.stringify(parsed)}`);
462
+ }
463
+ });
464
+ /** Parse a string value into a number, boolean, or string. */
465
+ function parseValue(raw) {
466
+ if (raw === "true") return true;
467
+ if (raw === "false") return false;
468
+ const num = Number(raw);
469
+ if (!Number.isNaN(num) && raw.trim() !== "") return num;
470
+ return raw;
471
+ }
472
+ /** Set a value at a dotted path on an object, creating intermediate objects as needed. */
473
+ function setNestedValue(obj, segments, value) {
474
+ let current = obj;
475
+ for (let i = 0; i < segments.length - 1; i++) {
476
+ const seg = segments[i];
477
+ if (current[seg] === void 0 || current[seg] === null) current[seg] = {};
478
+ current = current[seg];
479
+ }
480
+ current[segments[segments.length - 1]] = value;
481
+ }
482
+
483
+ //#endregion
484
+ //#region src/commands/config/index.ts
422
485
  async function showDashboard() {
423
486
  We("Baton Dashboard");
424
487
  const [sources, aiTools, idePlatforms, projectManifest] = await Promise.all([
@@ -511,8 +574,9 @@ async function loadProjectManifestSafe$1() {
511
574
  const configCommand = defineCommand({
512
575
  meta: {
513
576
  name: "config",
514
- description: "Show Baton dashboard overview"
577
+ description: "Show Baton dashboard overview or configure settings"
515
578
  },
579
+ subCommands: { set: configSetCommand },
516
580
  async run() {
517
581
  await showDashboard();
518
582
  }
@@ -2168,8 +2232,8 @@ const profileCommand = defineCommand({
2168
2232
  description: "Manage profiles (create, list, remove)"
2169
2233
  },
2170
2234
  subCommands: {
2171
- create: () => import("./create-W7AYGROv.mjs").then((m) => m.createCommand),
2172
- list: () => import("./list-D5O_m1e5.mjs").then((m) => m.profileListCommand),
2235
+ create: () => import("./create-C1zm03eE.mjs").then((m) => m.createCommand),
2236
+ list: () => import("./list-BHFeSiTO.mjs").then((m) => m.profileListCommand),
2173
2237
  remove: () => import("./remove-iaf_gkie.mjs").then((m) => m.profileRemoveCommand)
2174
2238
  }
2175
2239
  });
@@ -2728,6 +2792,11 @@ const syncCommand = defineCommand({
2728
2792
  alias: "v",
2729
2793
  description: "Show detailed output for each placed file",
2730
2794
  default: false
2795
+ },
2796
+ fresh: {
2797
+ type: "boolean",
2798
+ description: "Force an immediate source refresh (ignore cache TTL)",
2799
+ default: false
2731
2800
  }
2732
2801
  },
2733
2802
  async run({ args }) {
@@ -2735,6 +2804,7 @@ const syncCommand = defineCommand({
2735
2804
  const categoryArg = args.category;
2736
2805
  const autoYes = args.yes;
2737
2806
  const verbose = args.verbose;
2807
+ const fresh = args.fresh;
2738
2808
  let category;
2739
2809
  if (categoryArg) {
2740
2810
  if (!validCategories.includes(categoryArg)) {
@@ -2763,6 +2833,11 @@ const syncCommand = defineCommand({
2763
2833
  process.exit(1);
2764
2834
  }
2765
2835
  await promptFirstRunPreferences(projectRoot, !!args.yes);
2836
+ let cacheTtlHours = 24;
2837
+ try {
2838
+ cacheTtlHours = (await loadGlobalConfig()).sync?.cacheTtlHours ?? 24;
2839
+ } catch {}
2840
+ const maxCacheAgeMs = fresh ? 0 : cacheTtlHours * 60 * 60 * 1e3;
2766
2841
  const previousPaths = /* @__PURE__ */ new Set();
2767
2842
  try {
2768
2843
  const previousLock = await readLock(resolve(projectRoot, "baton.lock"));
@@ -2776,6 +2851,7 @@ const syncCommand = defineCommand({
2776
2851
  if (verbose) R.info(`Resolving source: ${profileSource.source}`);
2777
2852
  const parsed = parseSource(profileSource.source);
2778
2853
  let manifestPath;
2854
+ let cloneContext;
2779
2855
  if (parsed.provider === "local" || parsed.provider === "file") {
2780
2856
  const absolutePath = parsed.path.startsWith("/") ? parsed.path : resolve(projectRoot, parsed.path);
2781
2857
  manifestPath = resolve(absolutePath, "baton.profile.yaml");
@@ -2794,14 +2870,19 @@ const syncCommand = defineCommand({
2794
2870
  url,
2795
2871
  ref: profileSource.version,
2796
2872
  subpath: "subpath" in parsed ? parsed.subpath : void 0,
2797
- useCache: true
2873
+ useCache: true,
2874
+ maxCacheAgeMs
2798
2875
  });
2799
2876
  manifestPath = resolve(cloned.localPath, "baton.profile.yaml");
2800
2877
  sourceShas.set(profileSource.source, cloned.sha);
2878
+ cloneContext = {
2879
+ cachePath: cloned.cachePath,
2880
+ sparseCheckout: cloned.sparseCheckout
2881
+ };
2801
2882
  }
2802
2883
  const manifest = await loadProfileManifest(manifestPath);
2803
2884
  const profileDir = dirname(manifestPath);
2804
- const chain = await resolveProfileChain(manifest, profileSource.source, profileDir);
2885
+ const chain = await resolveProfileChain(manifest, profileSource.source, profileDir, cloneContext);
2805
2886
  allProfiles.push(...chain);
2806
2887
  } catch (error) {
2807
2888
  spinner.stop(`Failed to resolve profile ${profileSource.source}: ${error}`);
@@ -3007,7 +3088,8 @@ const syncCommand = defineCommand({
3007
3088
  url: parsed.provider === "git" ? parsed.url : parsed.url,
3008
3089
  ref: profileSource.version,
3009
3090
  subpath: "subpath" in parsed ? parsed.subpath : void 0,
3010
- useCache: true
3091
+ useCache: true,
3092
+ maxCacheAgeMs
3011
3093
  });
3012
3094
  for (const prof of allProfiles) if (prof.source === profileSource.source) profileLocalPaths.set(prof.name, cloned.localPath);
3013
3095
  }