facult 2.9.0 → 2.10.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.
Files changed (3) hide show
  1. package/README.md +13 -6
  2. package/package.json +1 -1
  3. package/src/manage.ts +40 -20
package/README.md CHANGED
@@ -127,7 +127,7 @@ fclt index
127
127
 
128
128
  Why `keep-current`: it is deterministic and non-interactive for duplicate sources.
129
129
 
130
- ### 4. Manage a tool and sync
130
+ ### 4. Optional: manage a tool and sync
131
131
 
132
132
  ```bash
133
133
  fclt manage codex --dry-run
@@ -140,7 +140,11 @@ fclt enable requesting-code-review receiving-code-review brainstorming systemati
140
140
  fclt sync
141
141
  ```
142
142
 
143
- Use `--dry-run` first if the live tool already has local content. If the tool already contains skills, agents, rules, docs, config, or MCP definitions, rerun with `--adopt-existing` and add `--existing-conflicts keep-canonical|keep-existing` if names collide.
143
+ Managed rendering is an advanced mode. Prefer `fclt inventory`, `fclt list`, `fclt consolidate`, and explicit writeback/evolution when you mainly want visibility and normalization across Codex, Claude, and other tool homes. Use `manage` only when you want fclt to write rendered files back into a tool.
144
+
145
+ Use `--dry-run` first if the live tool already has local content. If the tool already contains skills, agents, rules, docs, config, or MCP definitions, rerun `manage` with `--adopt-existing` and add `--existing-conflicts keep-canonical|keep-existing` if names collide.
146
+
147
+ Ordinary `fclt sync` does not import live tool edits into canonical state. If you intentionally edited skills, agents, docs, rules, config, or MCP entries in Codex/Claude and want fclt to pick them up, run `fclt sync --adopt-live`.
144
148
 
145
149
  Codex path policy:
146
150
  - skills render to `.agents/skills`
@@ -200,6 +204,8 @@ Useful AI behavior is composable. You need small reusable parts, a clean way to
200
204
  - discovery and graph views for dependencies, provenance, and rendered targets
201
205
  - writeback and evolution flows for improving canonical assets over time
202
206
 
207
+ The renderer is optional. The low-friction default is to let tools keep their native files, use `fclt inventory`/`scan`/`list` to see the full global set, and use `fclt consolidate` or `fclt sync --adopt-live` only when you explicitly want to promote live tool edits into canonical `~/.ai`.
208
+
203
209
  ## Built-in Defaults
204
210
 
205
211
  `fclt` includes a built-in layer for writeback and evolution. By default, that layer provides:
@@ -231,9 +237,10 @@ Put that in `config.toml` or `config.local.toml` under the active canonical root
231
237
 
232
238
  `fclt` is CLI-first. The practical setup is:
233
239
  1. Install `fclt` globally so any agent runtime can execute it.
234
- 2. Manage each agent tool with `fclt manage <tool>` and `fclt sync`.
235
- 3. Let the built-in operating-model layer render global writeback/evolution instructions into the tool.
236
- 4. Optionally scaffold MCP wrappers if you want an MCP entry that delegates to `fclt`.
240
+ 2. Use `fclt inventory`, `fclt list`, and `fclt consolidate` to inspect and normalize existing tool-native state.
241
+ 3. If you want fclt-owned rendered outputs, manage each agent tool with `fclt manage <tool>` and `fclt sync`.
242
+ 4. Let the built-in operating-model layer render global writeback/evolution instructions into the tool only where managed rendering is worth the ownership tradeoff.
243
+ 5. Optionally scaffold MCP wrappers if you want an MCP entry that delegates to `fclt`.
237
244
 
238
245
  ```bash
239
246
  # Scaffold reusable templates in the canonical store
@@ -592,7 +599,7 @@ fclt disable <name> [--for <tool1,tool2,...>]
592
599
  fclt trust --all
593
600
  fclt trust skills --all
594
601
  fclt untrust mcp --all
595
- fclt sync [tool] [--dry-run] [--builtin-conflicts overwrite]
602
+ fclt sync [tool] [--dry-run] [--adopt-live] [--builtin-conflicts overwrite]
596
603
  fclt autosync install [tool] [--git-remote <name>] [--git-branch <name>] [--git-interval-minutes <n>] [--git-disable]
597
604
  fclt autosync status [tool]
598
605
  fclt autosync restart [tool]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "facult",
3
- "version": "2.9.0",
3
+ "version": "2.10.0",
4
4
  "description": "Manage canonical AI capabilities, sync surfaces, and evolution state.",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/manage.ts CHANGED
@@ -137,6 +137,7 @@ export interface SyncOptions {
137
137
  tool?: string;
138
138
  dryRun?: boolean;
139
139
  builtinConflictMode?: "warn" | "overwrite";
140
+ adoptLive?: boolean;
140
141
  }
141
142
 
142
143
  const MANAGED_VERSION = 1 as const;
@@ -2421,12 +2422,14 @@ async function syncSkillSymlinks({
2421
2422
  rootDir,
2422
2423
  tool,
2423
2424
  dryRun,
2425
+ adoptLive,
2424
2426
  }: {
2425
2427
  homeDir: string;
2426
2428
  toolSkillsDir: string;
2427
2429
  rootDir: string;
2428
2430
  tool: string;
2429
2431
  dryRun?: boolean;
2432
+ adoptLive?: boolean;
2430
2433
  }): Promise<SkillSymlinkPlan> {
2431
2434
  const plan = await planSkillSymlinkChanges({
2432
2435
  homeDir,
@@ -2438,10 +2441,12 @@ async function syncSkillSymlinks({
2438
2441
  return plan;
2439
2442
  }
2440
2443
 
2441
- const adoptedConflicts = await adoptSkillSymlinkConflictsIntoCanonical({
2442
- rootDir,
2443
- conflicts: plan.conflicts,
2444
- });
2444
+ const adoptedConflicts = adoptLive
2445
+ ? await adoptSkillSymlinkConflictsIntoCanonical({
2446
+ rootDir,
2447
+ conflicts: plan.conflicts,
2448
+ })
2449
+ : [];
2445
2450
  const adoptedNames = new Set(
2446
2451
  adoptedConflicts.map((conflict) => conflict.name)
2447
2452
  );
@@ -3646,7 +3651,8 @@ function logRenderedConflicts(
3646
3651
  function logSkillSymlinkConflicts(
3647
3652
  tool: string,
3648
3653
  conflicts: SkillSymlinkConflict[],
3649
- dryRun?: boolean
3654
+ dryRun?: boolean,
3655
+ mode: "adopt" | "skip" = "skip"
3650
3656
  ) {
3651
3657
  for (const conflict of conflicts) {
3652
3658
  const verb =
@@ -3654,9 +3660,13 @@ function logSkillSymlinkConflicts(
3654
3660
  ? dryRun
3655
3661
  ? "would preserve"
3656
3662
  : "preserved"
3657
- : dryRun
3658
- ? "would adopt"
3659
- : "adopted";
3663
+ : mode === "adopt"
3664
+ ? dryRun
3665
+ ? "would adopt"
3666
+ : "adopted"
3667
+ : dryRun
3668
+ ? "would skip"
3669
+ : "skipped";
3660
3670
  const state =
3661
3671
  conflict.reason === "unmanaged"
3662
3672
  ? "it is not in canonical skill state"
@@ -3667,7 +3677,7 @@ function logSkillSymlinkConflicts(
3667
3677
  ? ` (canonical ${conflict.canonicalPath})`
3668
3678
  : "";
3669
3679
  console.warn(
3670
- `${tool}: ${verb} skill ${conflict.name} from ${conflict.livePath}${canonical} because ${state}.`
3680
+ `${tool}: ${verb} skill ${conflict.name} from ${conflict.livePath}${canonical} because ${state}.${mode === "skip" && conflict.reason !== "disabled" ? ' Rerun with "--adopt-live" to import it into canonical state.' : ""}`
3671
3681
  );
3672
3682
  }
3673
3683
  }
@@ -4470,6 +4480,7 @@ async function syncManagedToolEntry({
4470
4480
  rootDir,
4471
4481
  dryRun,
4472
4482
  builtinConflictMode,
4483
+ adoptLive,
4473
4484
  }: {
4474
4485
  homeDir: string;
4475
4486
  tool: string;
@@ -4477,6 +4488,7 @@ async function syncManagedToolEntry({
4477
4488
  rootDir: string;
4478
4489
  dryRun?: boolean;
4479
4490
  builtinConflictMode?: "warn" | "overwrite";
4491
+ adoptLive?: boolean;
4480
4492
  }) {
4481
4493
  const correlationId = randomUUID();
4482
4494
  const baseLedger = syncLedgerBase({
@@ -4515,14 +4527,15 @@ async function syncManagedToolEntry({
4515
4527
  automationDir: entry.automationDir,
4516
4528
  });
4517
4529
 
4518
- const adoptedSkills = dryRun
4519
- ? []
4520
- : await repairManagedCanonicalContent({
4521
- homeDir,
4522
- rootDir,
4523
- tool,
4524
- entry,
4525
- });
4530
+ const adoptedSkills =
4531
+ dryRun || !adoptLive
4532
+ ? []
4533
+ : await repairManagedCanonicalContent({
4534
+ homeDir,
4535
+ rootDir,
4536
+ tool,
4537
+ entry,
4538
+ });
4526
4539
 
4527
4540
  const skillPlan = entry.skillsDir
4528
4541
  ? await syncSkillSymlinks({
@@ -4531,6 +4544,7 @@ async function syncManagedToolEntry({
4531
4544
  rootDir,
4532
4545
  tool,
4533
4546
  dryRun,
4547
+ adoptLive,
4534
4548
  })
4535
4549
  : { add: [], remove: [], conflicts: [], adopted: [] };
4536
4550
  const skillLedgerEvents = collectSkillLedgerEvents({
@@ -4895,8 +4909,8 @@ async function syncManagedToolEntry({
4895
4909
  logRenderedConflicts(tool, configRendered.conflicts);
4896
4910
  logRenderedConflicts(tool, mcpRendered.conflicts);
4897
4911
  logRenderedConflicts(tool, pluginRendered.conflicts);
4898
- logSkillSymlinkConflicts(tool, skillPlan.adopted);
4899
- logSkillSymlinkConflicts(tool, skillPlan.conflicts);
4912
+ logSkillSymlinkConflicts(tool, skillPlan.adopted, false, "adopt");
4913
+ logSkillSymlinkConflicts(tool, skillPlan.conflicts, false, "skip");
4900
4914
 
4901
4915
  updateRenderedTargetState({
4902
4916
  entry,
@@ -5004,6 +5018,7 @@ export async function syncManagedTools(opts: SyncOptions = {}) {
5004
5018
  rootDir,
5005
5019
  dryRun: opts.dryRun,
5006
5020
  builtinConflictMode: opts.builtinConflictMode,
5021
+ adoptLive: opts.adoptLive,
5007
5022
  });
5008
5023
  }
5009
5024
 
@@ -5196,16 +5211,20 @@ export async function syncCommand(argv: string[]) {
5196
5211
  console.log(`fclt sync — sync managed tools with canonical state
5197
5212
 
5198
5213
  Usage:
5199
- fclt sync [tool] [--dry-run] [--builtin-conflicts overwrite] [--root PATH|--global|--project]
5214
+ fclt sync [tool] [--dry-run] [--adopt-live] [--builtin-conflicts overwrite] [--root PATH|--global|--project]
5200
5215
 
5201
5216
  Options:
5202
5217
  --dry-run Show what would change
5218
+ --adopt-live Import live tool content into canonical state before rendering
5203
5219
  --builtin-conflicts overwrite Replace locally modified builtin-backed rendered files
5204
5220
  `);
5205
5221
  return;
5206
5222
  }
5207
5223
  const tool = parsed.argv.find((arg) => !arg.startsWith("-"));
5208
5224
  const dryRun = parsed.argv.includes("--dry-run");
5225
+ const adoptLive =
5226
+ parsed.argv.includes("--adopt-live") ||
5227
+ parsed.argv.includes("--adopt-live-skills");
5209
5228
  const builtinConflictIndex = parsed.argv.indexOf("--builtin-conflicts");
5210
5229
  let builtinConflictMode: "warn" | "overwrite" | undefined;
5211
5230
  if (builtinConflictIndex !== -1) {
@@ -5221,6 +5240,7 @@ Options:
5221
5240
  await syncManagedTools({
5222
5241
  tool,
5223
5242
  dryRun,
5243
+ adoptLive,
5224
5244
  builtinConflictMode,
5225
5245
  rootDir: resolveCliContextRoot({
5226
5246
  rootArg: parsed.rootArg,