facult 2.5.1 → 2.5.2

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 (2) hide show
  1. package/package.json +1 -1
  2. package/src/manage.ts +66 -13
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "facult",
3
- "version": "2.5.1",
3
+ "version": "2.5.2",
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
@@ -586,15 +586,34 @@ async function loadCanonicalAutomations(
586
586
  return await loadAutomationEntries(join(rootDir, "automations"));
587
587
  }
588
588
 
589
+ function isAutomationRuntimeRelativePath(relPath: string): boolean {
590
+ return relPath === "memory.md";
591
+ }
592
+
593
+ function isAutomationRuntimeTargetPath(targetPath: string): boolean {
594
+ return basename(targetPath) === "memory.md";
595
+ }
596
+
589
597
  function automationEntriesEqual(
590
598
  left: AutomationEntry,
591
599
  right: AutomationEntry
592
600
  ): boolean {
593
- if (left.files.size !== right.files.size) {
601
+ const leftFiles = new Map(
602
+ [...left.files.entries()].filter(
603
+ ([relPath]) => !isAutomationRuntimeRelativePath(relPath)
604
+ )
605
+ );
606
+ const rightFiles = new Map(
607
+ [...right.files.entries()].filter(
608
+ ([relPath]) => !isAutomationRuntimeRelativePath(relPath)
609
+ )
610
+ );
611
+
612
+ if (leftFiles.size !== rightFiles.size) {
594
613
  return false;
595
614
  }
596
- for (const [relPath, leftRaw] of left.files.entries()) {
597
- if (right.files.get(relPath) !== leftRaw) {
615
+ for (const [relPath, leftRaw] of leftFiles.entries()) {
616
+ if (rightFiles.get(relPath) !== leftRaw) {
598
617
  return false;
599
618
  }
600
619
  }
@@ -816,23 +835,27 @@ async function planAutomationFileChanges(args: {
816
835
  const contents = new Map<string, string>();
817
836
  const sources = new Map<string, string>();
818
837
  const desiredPaths = new Set<string>();
838
+ const add = new Set<string>();
819
839
 
820
840
  for (const automation of automations) {
821
841
  for (const [relPath, raw] of automation.files.entries()) {
822
842
  const targetPath = join(args.automationDir, automation.name, relPath);
823
843
  const sourcePath = join(automation.sourceDir, relPath);
824
- desiredPaths.add(targetPath);
825
844
  contents.set(targetPath, raw);
826
- sources.set(targetPath, sourcePath);
827
- }
828
- }
829
845
 
830
- const add = new Set<string>();
831
- for (const targetPath of desiredPaths) {
832
- const current = await readTextIfExists(targetPath);
833
- const desired = contents.get(targetPath);
834
- if (desired != null && current !== desired) {
835
- add.add(targetPath);
846
+ if (isAutomationRuntimeRelativePath(relPath)) {
847
+ if ((await readTextIfExists(targetPath)) == null) {
848
+ add.add(targetPath);
849
+ }
850
+ continue;
851
+ }
852
+
853
+ desiredPaths.add(targetPath);
854
+ sources.set(targetPath, sourcePath);
855
+ const current = await readTextIfExists(targetPath);
856
+ if (current !== raw) {
857
+ add.add(targetPath);
858
+ }
836
859
  }
837
860
  }
838
861
 
@@ -841,6 +864,7 @@ async function planAutomationFileChanges(args: {
841
864
  (args.previouslyManagedTargets ?? []).filter(
842
865
  (targetPath) =>
843
866
  targetPath.startsWith(join(args.automationDir, "")) &&
867
+ !isAutomationRuntimeTargetPath(targetPath) &&
844
868
  !desiredPaths.has(targetPath)
845
869
  )
846
870
  )
@@ -2973,6 +2997,30 @@ function updateRenderedTargetState(args: {
2973
2997
  args.entry.renderedTargets = next;
2974
2998
  }
2975
2999
 
3000
+ function pruneAutomationRuntimeRenderedTargets(args: {
3001
+ entry: ManagedToolState;
3002
+ automationDir?: string;
3003
+ }) {
3004
+ if (!(args.automationDir && args.entry.renderedTargets)) {
3005
+ return;
3006
+ }
3007
+ const prefix = join(args.automationDir, "");
3008
+ const next = { ...args.entry.renderedTargets };
3009
+ let changed = false;
3010
+ for (const targetPath of Object.keys(next)) {
3011
+ if (
3012
+ targetPath.startsWith(prefix) &&
3013
+ isAutomationRuntimeTargetPath(targetPath)
3014
+ ) {
3015
+ delete next[targetPath];
3016
+ changed = true;
3017
+ }
3018
+ }
3019
+ if (changed) {
3020
+ args.entry.renderedTargets = next;
3021
+ }
3022
+ }
3023
+
2976
3024
  function logSyncDryRun({
2977
3025
  tool,
2978
3026
  entry,
@@ -3177,6 +3225,11 @@ async function syncManagedToolEntry({
3177
3225
  dryRun?: boolean;
3178
3226
  builtinConflictMode?: "warn" | "overwrite";
3179
3227
  }) {
3228
+ pruneAutomationRuntimeRenderedTargets({
3229
+ entry,
3230
+ automationDir: entry.automationDir,
3231
+ });
3232
+
3180
3233
  const adoptedSkills = dryRun
3181
3234
  ? []
3182
3235
  : await repairManagedCanonicalContent({