facult 2.8.5 → 2.8.7

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
@@ -441,13 +441,13 @@ fclt ai evolve apply EV-00001
441
441
  fclt ai evolve promote EV-00003 --to global --project
442
442
  ```
443
443
 
444
- Runtime state stays generated and local inside the active canonical root:
445
- - global writeback state: `~/.ai/.facult/ai/global/...`
444
+ Runtime writeback and evolution state stays generated and machine-local:
445
+ - global writeback state: machine-local Facult state under `.../global/ai/global/...`
446
446
  - project writeback state: machine-local per-project Facult state under `.../projects/<slug-hash>/ai/project/...`
447
447
 
448
448
  That split is intentional:
449
449
  - canonical source remains in `~/.ai` or `<repo>/.ai`
450
- - global generated state stays inside `~/.ai/.facult/`; project generated state stays outside the repo in machine-local state
450
+ - global generated index and graph state stays inside `~/.ai/.facult/`; writebacks, journals, proposals, drafts, and managed runtime state stay outside canonical source in machine-local state
451
451
  - those records let agents inspect what changed, why it changed, and how it was reviewed
452
452
 
453
453
  Use writeback when:
@@ -698,10 +698,13 @@ Under canonical generated AI state (`~/.ai/.facult/` or `<repo>/.ai/.facult/`):
698
698
  Under machine-local Facult state:
699
699
  - `install.json` (machine-local install metadata)
700
700
  - `global/managed.json` or `projects/<slug-hash>/managed.json` (managed tool state)
701
+ - `global/ai/global/writeback/queue.jsonl` and `projects/<slug-hash>/ai/project/writeback/queue.jsonl` (writeback queues)
702
+ - `global/ai/global/evolution/` and `projects/<slug-hash>/ai/project/evolution/` (proposal metadata, markdown drafts, and patch artifacts)
701
703
  - `.../autosync/services/*.json` (autosync service configs)
702
704
  - `.../autosync/state/*.json` (autosync runtime state)
703
705
  - `.../autosync/logs/*` (autosync service logs)
704
- - `runtime/<version>/<platform-arch>/...` under the machine-local cache root (npm launcher binary cache)
706
+ - `cache/runtime/<version>/<platform-arch>/...` under the macOS machine-local state root, or `runtime/<version>/<platform-arch>/...` under `FACULT_CACHE_DIR`/XDG cache roots on other platforms (npm launcher binary cache)
707
+ - if that cache root is unavailable, the npm launcher falls back to a temp-dir runtime cache before using the bundled source fallback
705
708
 
706
709
  ### Config reference
707
710
 
package/bin/fclt.cjs CHANGED
@@ -45,7 +45,7 @@ function localCacheRoot(home) {
45
45
  return path.resolve(override);
46
46
  }
47
47
  if (process.platform === "darwin") {
48
- return path.join(home, "Library", "Caches", "fclt");
48
+ return path.join(localStateRoot(home), "cache");
49
49
  }
50
50
  const xdg = String(process.env.XDG_CACHE_HOME || "").trim();
51
51
  return xdg
@@ -67,7 +67,7 @@ async function main() {
67
67
  }
68
68
 
69
69
  const home = os.homedir();
70
- const cacheRoot = path.join(localCacheRoot(home), "runtime");
70
+ const cacheRoot = await runtimeCacheRoot(home);
71
71
  const installDir = path.join(
72
72
  cacheRoot,
73
73
  version,
@@ -174,6 +174,22 @@ async function main() {
174
174
  process.exit(1);
175
175
  }
176
176
 
177
+ async function runtimeCacheRoot(home) {
178
+ const primary = path.join(localCacheRoot(home), "runtime");
179
+ try {
180
+ await fsp.mkdir(primary, { recursive: true });
181
+ return primary;
182
+ } catch {
183
+ const fallback = path.join(os.tmpdir(), "fclt", "runtime-cache");
184
+ try {
185
+ await fsp.mkdir(fallback, { recursive: true });
186
+ return fallback;
187
+ } catch {
188
+ return primary;
189
+ }
190
+ }
191
+ }
192
+
177
193
  async function canUseSourceFallback(sourceEntry) {
178
194
  if (!(await fileExists(sourceEntry))) {
179
195
  return false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "facult",
3
- "version": "2.8.5",
3
+ "version": "2.8.7",
4
4
  "description": "Manage canonical AI capabilities, sync surfaces, and evolution state.",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/ai.ts CHANGED
@@ -9,6 +9,7 @@ import {
9
9
  facultAiDraftDir,
10
10
  facultAiJournalPath,
11
11
  facultAiProposalDir,
12
+ facultAiStateDir,
12
13
  facultAiWritebackQueuePath,
13
14
  facultRootDir,
14
15
  legacyFacultAiStateDirs,
@@ -233,9 +234,10 @@ function aiRuntimeScopeName(rootDir: string, homeDir: string): AssetScope {
233
234
 
234
235
  function legacyAiRuntimeScopeDirs(homeDir: string, rootDir: string): string[] {
235
236
  const scope = aiRuntimeScopeName(rootDir, homeDir);
236
- return legacyFacultAiStateDirs(homeDir, rootDir).map((dir) =>
237
- join(dir, scope)
238
- );
237
+ return uniqueStrings([
238
+ join(facultAiStateDir(homeDir, rootDir), scope),
239
+ ...legacyFacultAiStateDirs(homeDir, rootDir).map((dir) => join(dir, scope)),
240
+ ]);
239
241
  }
240
242
 
241
243
  function aiWritebackQueueReadPaths(homeDir: string, rootDir: string): string[] {
@@ -1604,15 +1606,15 @@ function parseEvidence(argv: string[]): WritebackEvidence[] {
1604
1606
  }
1605
1607
 
1606
1608
  async function writebackCommand(argv: string[]) {
1607
- const [sub, ...rest] = argv;
1608
- const parsed = parseCliContextArgs(rest);
1609
+ const parsed = parseCliContextArgs(argv);
1610
+ const [sub, ...commandArgs] = parsed.argv;
1609
1611
 
1610
1612
  if (!sub || sub === "--help" || sub === "-h" || sub === "help") {
1611
1613
  console.log(writebackHelp());
1612
1614
  return;
1613
1615
  }
1614
1616
 
1615
- if (parsed.argv.includes("--help") || parsed.argv.includes("-h")) {
1617
+ if (commandArgs.includes("--help") || commandArgs.includes("-h")) {
1616
1618
  console.log(writebackHelp());
1617
1619
  return;
1618
1620
  }
@@ -1625,8 +1627,8 @@ async function writebackCommand(argv: string[]) {
1625
1627
 
1626
1628
  try {
1627
1629
  if (sub === "add") {
1628
- const kind = parseStringFlag(parsed.argv, "--kind");
1629
- const summary = parseStringFlag(parsed.argv, "--summary");
1630
+ const kind = parseStringFlag(commandArgs, "--kind");
1631
+ const summary = parseStringFlag(commandArgs, "--summary");
1630
1632
  if (!(kind && summary)) {
1631
1633
  throw new Error("writeback add requires --kind and --summary");
1632
1634
  }
@@ -1634,18 +1636,18 @@ async function writebackCommand(argv: string[]) {
1634
1636
  rootDir,
1635
1637
  kind,
1636
1638
  summary,
1637
- asset: parseStringFlag(parsed.argv, "--asset"),
1638
- allowEmptyEvidence: parsed.argv.includes("--allow-empty-evidence"),
1639
+ asset: parseStringFlag(commandArgs, "--asset"),
1640
+ allowEmptyEvidence: commandArgs.includes("--allow-empty-evidence"),
1639
1641
  confidence:
1640
- (parseStringFlag(parsed.argv, "--confidence") as
1642
+ (parseStringFlag(commandArgs, "--confidence") as
1641
1643
  | ConfidenceLevel
1642
1644
  | undefined) ?? undefined,
1643
1645
  suggestedDestination: parseStringFlag(
1644
- parsed.argv,
1646
+ commandArgs,
1645
1647
  "--suggested-destination"
1646
1648
  ),
1647
- tags: parseRepeatedFlag(parsed.argv, "--tag"),
1648
- evidence: parseEvidence(parsed.argv),
1649
+ tags: parseRepeatedFlag(commandArgs, "--tag"),
1650
+ evidence: parseEvidence(commandArgs),
1649
1651
  });
1650
1652
  console.log(`Recorded writeback ${record.id}`);
1651
1653
  console.log(JSON.stringify(record, null, 2));
@@ -1654,7 +1656,7 @@ async function writebackCommand(argv: string[]) {
1654
1656
 
1655
1657
  if (sub === "list") {
1656
1658
  const rows = await listWritebacks({ rootDir });
1657
- if (parsed.argv.includes("--json")) {
1659
+ if (commandArgs.includes("--json")) {
1658
1660
  console.log(JSON.stringify(rows, null, 2));
1659
1661
  return;
1660
1662
  }
@@ -1665,7 +1667,7 @@ async function writebackCommand(argv: string[]) {
1665
1667
  }
1666
1668
 
1667
1669
  if (sub === "group" || sub === "summarize") {
1668
- const byValue = parseStringFlag(parsed.argv, "--by") ?? "asset";
1670
+ const byValue = parseStringFlag(commandArgs, "--by") ?? "asset";
1669
1671
  if (byValue !== "asset" && byValue !== "kind" && byValue !== "domain") {
1670
1672
  throw new Error(`Unsupported writeback grouping: ${byValue}`);
1671
1673
  }
@@ -1673,7 +1675,7 @@ async function writebackCommand(argv: string[]) {
1673
1675
  sub === "group"
1674
1676
  ? await groupWritebacks({ rootDir, by: byValue })
1675
1677
  : await summarizeWritebacks({ rootDir, by: byValue });
1676
- if (parsed.argv.includes("--json")) {
1678
+ if (commandArgs.includes("--json")) {
1677
1679
  console.log(JSON.stringify(rows, null, 2));
1678
1680
  return;
1679
1681
  }
@@ -1686,7 +1688,7 @@ async function writebackCommand(argv: string[]) {
1686
1688
  }
1687
1689
 
1688
1690
  if (sub === "show") {
1689
- const id = parsed.argv.find((arg) => !arg.startsWith("-"));
1691
+ const id = commandArgs.find((arg) => !arg.startsWith("-"));
1690
1692
  if (!id) {
1691
1693
  throw new Error("writeback show requires an id");
1692
1694
  }
@@ -1699,7 +1701,7 @@ async function writebackCommand(argv: string[]) {
1699
1701
  }
1700
1702
 
1701
1703
  if (sub === "dismiss" || sub === "promote") {
1702
- const id = parsed.argv.find((arg) => !arg.startsWith("-"));
1704
+ const id = commandArgs.find((arg) => !arg.startsWith("-"));
1703
1705
  if (!id) {
1704
1706
  throw new Error(`writeback ${sub} requires an id`);
1705
1707
  }
@@ -1720,15 +1722,15 @@ async function writebackCommand(argv: string[]) {
1720
1722
  }
1721
1723
 
1722
1724
  async function evolveCommand(argv: string[]) {
1723
- const [sub, ...rest] = argv;
1724
- const parsed = parseCliContextArgs(rest);
1725
+ const parsed = parseCliContextArgs(argv);
1726
+ const [sub, ...commandArgs] = parsed.argv;
1725
1727
 
1726
1728
  if (!sub || sub === "--help" || sub === "-h" || sub === "help") {
1727
1729
  console.log(evolveHelp());
1728
1730
  return;
1729
1731
  }
1730
1732
 
1731
- if (parsed.argv.includes("--help") || parsed.argv.includes("-h")) {
1733
+ if (commandArgs.includes("--help") || commandArgs.includes("-h")) {
1732
1734
  console.log(evolveHelp());
1733
1735
  return;
1734
1736
  }
@@ -1743,9 +1745,9 @@ async function evolveCommand(argv: string[]) {
1743
1745
  if (sub === "propose") {
1744
1746
  const proposals = await proposeEvolution({
1745
1747
  rootDir,
1746
- asset: parseStringFlag(parsed.argv, "--asset"),
1748
+ asset: parseStringFlag(commandArgs, "--asset"),
1747
1749
  });
1748
- if (parsed.argv.includes("--json")) {
1750
+ if (commandArgs.includes("--json")) {
1749
1751
  console.log(JSON.stringify(proposals, null, 2));
1750
1752
  return;
1751
1753
  }
@@ -1759,7 +1761,7 @@ async function evolveCommand(argv: string[]) {
1759
1761
 
1760
1762
  if (sub === "list") {
1761
1763
  const rows = await listProposals({ rootDir });
1762
- if (parsed.argv.includes("--json")) {
1764
+ if (commandArgs.includes("--json")) {
1763
1765
  console.log(JSON.stringify(rows, null, 2));
1764
1766
  return;
1765
1767
  }
@@ -1770,7 +1772,7 @@ async function evolveCommand(argv: string[]) {
1770
1772
  }
1771
1773
 
1772
1774
  if (sub === "show") {
1773
- const id = parsed.argv.find((arg) => !arg.startsWith("-"));
1775
+ const id = commandArgs.find((arg) => !arg.startsWith("-"));
1774
1776
  if (!id) {
1775
1777
  throw new Error("evolve show requires an id");
1776
1778
  }
@@ -1791,7 +1793,7 @@ async function evolveCommand(argv: string[]) {
1791
1793
  sub === "apply" ||
1792
1794
  sub === "promote"
1793
1795
  ) {
1794
- const id = parsed.argv.find((arg) => !arg.startsWith("-"));
1796
+ const id = commandArgs.find((arg) => !arg.startsWith("-"));
1795
1797
  if (!id) {
1796
1798
  throw new Error(`evolve ${sub} requires an id`);
1797
1799
  }
@@ -1799,7 +1801,7 @@ async function evolveCommand(argv: string[]) {
1799
1801
  sub === "draft"
1800
1802
  ? await draftProposal(id, {
1801
1803
  rootDir,
1802
- append: parseStringFlag(parsed.argv, "--append"),
1804
+ append: parseStringFlag(commandArgs, "--append"),
1803
1805
  })
1804
1806
  : sub === "review"
1805
1807
  ? await reviewProposal(id, { rootDir })
@@ -1809,7 +1811,7 @@ async function evolveCommand(argv: string[]) {
1809
1811
  ? await rejectProposal(id, {
1810
1812
  rootDir,
1811
1813
  reason:
1812
- parseStringFlag(parsed.argv, "--reason") ??
1814
+ parseStringFlag(commandArgs, "--reason") ??
1813
1815
  (() => {
1814
1816
  throw new Error("evolve reject requires --reason");
1815
1817
  })(),
@@ -1817,7 +1819,7 @@ async function evolveCommand(argv: string[]) {
1817
1819
  : sub === "supersede"
1818
1820
  ? await supersedeProposal(
1819
1821
  id,
1820
- parseStringFlag(parsed.argv, "--by") ??
1822
+ parseStringFlag(commandArgs, "--by") ??
1821
1823
  (() => {
1822
1824
  throw new Error("evolve supersede requires --by");
1823
1825
  })(),
@@ -1827,7 +1829,7 @@ async function evolveCommand(argv: string[]) {
1827
1829
  ? await promoteProposal(id, {
1828
1830
  rootDir,
1829
1831
  to:
1830
- (parseStringFlag(parsed.argv, "--to") as
1832
+ (parseStringFlag(commandArgs, "--to") as
1831
1833
  | "global"
1832
1834
  | undefined) ??
1833
1835
  (() => {
package/src/paths.ts CHANGED
@@ -114,7 +114,7 @@ export function facultLocalCacheRoot(home: string = defaultHomeDir()): string {
114
114
  return resolvePath(override, home);
115
115
  }
116
116
  if (process.platform === "darwin") {
117
- return join(home, "Library", "Caches", "fclt");
117
+ return join(facultLocalStateRoot(home), "cache");
118
118
  }
119
119
  const xdg = process.env.XDG_CACHE_HOME?.trim();
120
120
  return xdg
@@ -369,7 +369,8 @@ export function facultAiRuntimeScopeDir(
369
369
  rootDir?: string
370
370
  ): string {
371
371
  return join(
372
- facultAiStateDir(home, rootDir),
372
+ facultMachineStateDir(home, rootDir),
373
+ "ai",
373
374
  projectRootFromAiRoot(rootDir ?? facultRootDir(home), home)
374
375
  ? "project"
375
376
  : "global"