facult 2.5.2 → 2.6.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.
package/README.md CHANGED
@@ -445,6 +445,7 @@ The canonical store can contain several distinct asset classes:
445
445
  - `skills/`: workflow-specific capability folders
446
446
  - `mcp/`: canonical MCP server definitions
447
447
  - `tools/<tool>/config.toml`: canonical tool config
448
+ - `tools/<tool>/config.local.toml`: machine-local tool config overlay
448
449
  - `tools/<tool>/rules/*.rules`: canonical tool rules
449
450
  - global docs such as `AGENTS.global.md` and `AGENTS.override.global.md`
450
451
 
@@ -479,6 +480,8 @@ Built-ins currently include:
479
480
  Recommended split:
480
481
  - `~/.ai/config.toml` or `<repo>/.ai/config.toml`: tracked, portable, non-secret refs/defaults
481
482
  - `~/.ai/config.local.toml` or `<repo>/.ai/config.local.toml`: ignored, machine-local paths and secrets
483
+ - `~/.ai/tools/<tool>/config.toml` or `<repo>/.ai/tools/<tool>/config.toml`: tracked tool defaults
484
+ - `~/.ai/tools/<tool>/config.local.toml` or `<repo>/.ai/tools/<tool>/config.local.toml`: ignored, machine-local tool overrides merged after tracked tool config during sync
482
485
  - `[builtin].sync_defaults = false`: disable builtin default sync/materialization for this root
483
486
  - `fclt sync --builtin-conflicts overwrite`: allow packaged builtin defaults to overwrite locally modified generated targets
484
487
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "facult",
3
- "version": "2.5.2",
3
+ "version": "2.6.0",
4
4
  "description": "Manage canonical AI capabilities, sync surfaces, and evolution state.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -426,7 +426,16 @@ export async function planToolConfigSync(args: {
426
426
  previouslyManaged?: boolean;
427
427
  }): Promise<ToolConfigPlan> {
428
428
  const sourcePath = join(args.rootDir, "tools", args.tool, "config.toml");
429
- if (!(await fileExists(sourcePath))) {
429
+ const localSourcePath = join(
430
+ args.rootDir,
431
+ "tools",
432
+ args.tool,
433
+ "config.local.toml"
434
+ );
435
+ const hasTrackedSource = await fileExists(sourcePath);
436
+ const hasLocalSource = await fileExists(localSourcePath);
437
+
438
+ if (!(hasTrackedSource || hasLocalSource)) {
430
439
  return {
431
440
  targetPath: args.toolConfigPath,
432
441
  write: false,
@@ -437,14 +446,28 @@ export async function planToolConfigSync(args: {
437
446
  };
438
447
  }
439
448
 
440
- const rendered = await renderSourceTarget({
441
- homeDir: args.homeDir,
442
- rootDir: args.rootDir,
443
- sourcePath,
444
- targetPath: args.toolConfigPath,
445
- tool: args.tool,
446
- });
447
- const canonicalConfig = Bun.TOML.parse(rendered);
449
+ const trackedRendered = hasTrackedSource
450
+ ? await renderSourceTarget({
451
+ homeDir: args.homeDir,
452
+ rootDir: args.rootDir,
453
+ sourcePath,
454
+ targetPath: args.toolConfigPath,
455
+ tool: args.tool,
456
+ })
457
+ : null;
458
+ const localRendered = hasLocalSource
459
+ ? await renderSourceTarget({
460
+ homeDir: args.homeDir,
461
+ rootDir: args.rootDir,
462
+ sourcePath: localSourcePath,
463
+ targetPath: args.toolConfigPath,
464
+ tool: args.tool,
465
+ })
466
+ : null;
467
+ const canonicalConfig = trackedRendered
468
+ ? Bun.TOML.parse(trackedRendered)
469
+ : {};
470
+ const localConfig = localRendered ? Bun.TOML.parse(localRendered) : {};
448
471
  const existingConfig =
449
472
  (await readTomlFile(args.toolConfigPath)) ??
450
473
  (args.existingConfigPath
@@ -452,8 +475,11 @@ export async function planToolConfigSync(args: {
452
475
  : null) ??
453
476
  ({} as Record<string, unknown>);
454
477
  const merged = mergeTomlObjects(
455
- existingConfig,
456
- isPlainObject(canonicalConfig) ? canonicalConfig : {}
478
+ mergeTomlObjects(
479
+ existingConfig,
480
+ isPlainObject(canonicalConfig) ? canonicalConfig : {}
481
+ ),
482
+ isPlainObject(localConfig) ? localConfig : {}
457
483
  );
458
484
  const nextContents = stringifyTomlObject(merged);
459
485
  const current = await readTextIfExists(args.toolConfigPath);
@@ -462,7 +488,7 @@ export async function planToolConfigSync(args: {
462
488
  write: current !== `${nextContents}\n`,
463
489
  remove: false,
464
490
  contents: nextContents,
465
- sourcePath,
491
+ sourcePath: hasLocalSource ? localSourcePath : sourcePath,
466
492
  managedConfig: true,
467
493
  };
468
494
  }