facult 2.7.0 → 2.7.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.
- package/README.md +141 -337
- package/package.json +1 -1
- package/src/adapters/codex.ts +1 -1
- package/src/ai-state.ts +131 -1
- package/src/builtin.ts +7 -1
- package/src/doctor.ts +327 -0
- package/src/global-docs.ts +43 -2
- package/src/index-builder.ts +79 -8
- package/src/index.ts +60 -53
- package/src/manage.ts +880 -37
- package/src/project-sync.ts +288 -0
package/src/index-builder.ts
CHANGED
|
@@ -69,6 +69,12 @@ export interface AgentEntry {
|
|
|
69
69
|
path: string;
|
|
70
70
|
description?: string;
|
|
71
71
|
lastModifiedAt?: string;
|
|
72
|
+
enabledFor?: string[];
|
|
73
|
+
trusted?: boolean;
|
|
74
|
+
trustedAt?: string;
|
|
75
|
+
trustedBy?: string;
|
|
76
|
+
auditStatus?: "pending" | "passed" | "flagged";
|
|
77
|
+
lastAuditAt?: string;
|
|
72
78
|
}
|
|
73
79
|
|
|
74
80
|
export interface SnippetEntry {
|
|
@@ -197,6 +203,53 @@ function extractIndexMeta(entry: unknown): {
|
|
|
197
203
|
};
|
|
198
204
|
}
|
|
199
205
|
|
|
206
|
+
function findPreviousEntryByCanonicalRef(
|
|
207
|
+
previous: Record<string, unknown> | undefined,
|
|
208
|
+
canonicalRef: string | undefined,
|
|
209
|
+
fallbackName: string
|
|
210
|
+
): unknown {
|
|
211
|
+
if (!previous) {
|
|
212
|
+
return undefined;
|
|
213
|
+
}
|
|
214
|
+
if (typeof canonicalRef === "string") {
|
|
215
|
+
for (const value of Object.values(previous)) {
|
|
216
|
+
if (!isPlainObject(value)) {
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
if (value.canonicalRef === canonicalRef) {
|
|
220
|
+
return value;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
const legacyFallback = previous[fallbackName];
|
|
225
|
+
if (
|
|
226
|
+
isPlainObject(legacyFallback) &&
|
|
227
|
+
typeof legacyFallback.canonicalRef !== "string"
|
|
228
|
+
) {
|
|
229
|
+
return legacyFallback;
|
|
230
|
+
}
|
|
231
|
+
return undefined;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function findPreviousMcpEntry(
|
|
235
|
+
previous: Record<string, unknown> | undefined,
|
|
236
|
+
canonicalRef: string | undefined,
|
|
237
|
+
name: string
|
|
238
|
+
): unknown {
|
|
239
|
+
if (!previous) {
|
|
240
|
+
return undefined;
|
|
241
|
+
}
|
|
242
|
+
const candidate = previous[name];
|
|
243
|
+
if (!isPlainObject(candidate)) {
|
|
244
|
+
return undefined;
|
|
245
|
+
}
|
|
246
|
+
return typeof candidate.canonicalRef !== "string" ||
|
|
247
|
+
(typeof canonicalRef === "string" &&
|
|
248
|
+
candidate.canonicalRef === canonicalRef)
|
|
249
|
+
? candidate
|
|
250
|
+
: undefined;
|
|
251
|
+
}
|
|
252
|
+
|
|
200
253
|
function stripQuotes(s: string): string {
|
|
201
254
|
const t = s.trim();
|
|
202
255
|
if (
|
|
@@ -489,8 +542,12 @@ async function indexSkills(
|
|
|
489
542
|
const md = await Bun.file(skillMd).text();
|
|
490
543
|
const { description, tags } = parseSkillMarkdown(md);
|
|
491
544
|
const name = basename(d);
|
|
492
|
-
|
|
493
|
-
const prev =
|
|
545
|
+
const canonicalRef = canonicalRefForPath(source, "skills", d);
|
|
546
|
+
const prev = findPreviousEntryByCanonicalRef(
|
|
547
|
+
previous,
|
|
548
|
+
canonicalRef,
|
|
549
|
+
name
|
|
550
|
+
);
|
|
494
551
|
const meta = extractIndexMeta(prev);
|
|
495
552
|
|
|
496
553
|
out[name] = {
|
|
@@ -498,7 +555,7 @@ async function indexSkills(
|
|
|
498
555
|
path: d,
|
|
499
556
|
description,
|
|
500
557
|
tags,
|
|
501
|
-
canonicalRef
|
|
558
|
+
canonicalRef,
|
|
502
559
|
lastModifiedAt: await statIsoTime(skillMd),
|
|
503
560
|
enabledFor: meta.enabledFor,
|
|
504
561
|
trusted: meta.trusted ?? false,
|
|
@@ -550,12 +607,13 @@ async function indexMcpServers(
|
|
|
550
607
|
|
|
551
608
|
const lm = await statIsoTime(mcpConfigPath);
|
|
552
609
|
for (const name of Object.keys(serversObj).sort()) {
|
|
553
|
-
const
|
|
610
|
+
const canonicalRef = canonicalRefForPath(source, "mcp", mcpConfigPath);
|
|
611
|
+
const prev = findPreviousMcpEntry(previous, canonicalRef, name);
|
|
554
612
|
const meta = extractIndexMeta(prev);
|
|
555
613
|
out[name] = {
|
|
556
614
|
name,
|
|
557
615
|
path: mcpConfigPath,
|
|
558
|
-
canonicalRef
|
|
616
|
+
canonicalRef,
|
|
559
617
|
lastModifiedAt: lm,
|
|
560
618
|
definition: serversObj[name],
|
|
561
619
|
enabledFor: meta.enabledFor,
|
|
@@ -576,7 +634,8 @@ async function indexMcpServers(
|
|
|
576
634
|
|
|
577
635
|
async function indexAgents(
|
|
578
636
|
agentsDir: string,
|
|
579
|
-
source: IndexedSource
|
|
637
|
+
source: IndexedSource,
|
|
638
|
+
previous?: Record<string, unknown>
|
|
580
639
|
): Promise<Record<string, AgentEntry>> {
|
|
581
640
|
const out: Record<string, AgentEntry> = {};
|
|
582
641
|
const files: string[] = [];
|
|
@@ -596,6 +655,9 @@ async function indexAgents(
|
|
|
596
655
|
for (const p of files) {
|
|
597
656
|
const name =
|
|
598
657
|
basename(p) === "agent.toml" ? basename(dirname(p)) : basename(p);
|
|
658
|
+
const canonicalRef = canonicalRefForPath(source, "agents", p);
|
|
659
|
+
const prev = findPreviousEntryByCanonicalRef(previous, canonicalRef, name);
|
|
660
|
+
const meta = extractIndexMeta(prev);
|
|
599
661
|
let description: string | undefined;
|
|
600
662
|
try {
|
|
601
663
|
const raw = await Bun.file(p).text();
|
|
@@ -611,8 +673,14 @@ async function indexAgents(
|
|
|
611
673
|
name,
|
|
612
674
|
path: p,
|
|
613
675
|
description,
|
|
614
|
-
canonicalRef
|
|
676
|
+
canonicalRef,
|
|
615
677
|
lastModifiedAt: await statIsoTime(p),
|
|
678
|
+
enabledFor: meta.enabledFor,
|
|
679
|
+
trusted: meta.trusted ?? false,
|
|
680
|
+
trustedAt: meta.trustedAt,
|
|
681
|
+
trustedBy: meta.trustedBy,
|
|
682
|
+
auditStatus: meta.auditStatus ?? "pending",
|
|
683
|
+
lastAuditAt: meta.lastAuditAt,
|
|
616
684
|
...entryScopeMeta(source),
|
|
617
685
|
};
|
|
618
686
|
}
|
|
@@ -782,6 +850,9 @@ async function indexSourceAssets(
|
|
|
782
850
|
const prevSkills = isPlainObject(previousIndex?.skills)
|
|
783
851
|
? (previousIndex?.skills as Record<string, unknown>)
|
|
784
852
|
: undefined;
|
|
853
|
+
const prevAgents = isPlainObject(previousIndex?.agents)
|
|
854
|
+
? (previousIndex?.agents as Record<string, unknown>)
|
|
855
|
+
: undefined;
|
|
785
856
|
const prevMcpMap =
|
|
786
857
|
isPlainObject(previousIndex?.mcp) &&
|
|
787
858
|
isPlainObject((previousIndex.mcp as Record<string, unknown>).servers)
|
|
@@ -795,7 +866,7 @@ async function indexSourceAssets(
|
|
|
795
866
|
await Promise.all([
|
|
796
867
|
indexSkills(skillsDir, source, prevSkills),
|
|
797
868
|
indexMcpServers(canonicalMcpPath, source, prevMcpMap),
|
|
798
|
-
indexAgents(agentsDir, source),
|
|
869
|
+
indexAgents(agentsDir, source, prevAgents),
|
|
799
870
|
indexSnippets(snippetsDir, source),
|
|
800
871
|
indexInstructions(instructionsDir, source),
|
|
801
872
|
indexToolAssets(toolsDir, source),
|
package/src/index.ts
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
|
|
3
3
|
import { join } from "node:path";
|
|
4
|
-
import { getAllAdapters } from "./adapters";
|
|
5
|
-
import { aiCommand } from "./ai";
|
|
6
|
-
import { auditCommand } from "./audit";
|
|
7
|
-
import { autosyncCommand } from "./autosync";
|
|
8
4
|
import {
|
|
9
5
|
type CapabilityScopeMode,
|
|
10
6
|
parseCliContextArgs,
|
|
@@ -20,9 +16,6 @@ import {
|
|
|
20
16
|
renderPage,
|
|
21
17
|
renderTable,
|
|
22
18
|
} from "./cli-ui";
|
|
23
|
-
import { consolidateCommand } from "./consolidate";
|
|
24
|
-
import { doctorCommand } from "./doctor";
|
|
25
|
-
import { disableCommand, enableCommand } from "./enable-disable";
|
|
26
19
|
import type { AssetScope, AssetSourceKind } from "./graph";
|
|
27
20
|
import {
|
|
28
21
|
graphDependencies,
|
|
@@ -38,14 +31,6 @@ import type {
|
|
|
38
31
|
SkillEntry,
|
|
39
32
|
SnippetEntry,
|
|
40
33
|
} from "./index-builder";
|
|
41
|
-
import { indexCommand } from "./index-builder";
|
|
42
|
-
import {
|
|
43
|
-
manageCommand,
|
|
44
|
-
managedCommand,
|
|
45
|
-
syncCommand,
|
|
46
|
-
unmanageCommand,
|
|
47
|
-
} from "./manage";
|
|
48
|
-
import { migrateCommand } from "./migrate";
|
|
49
34
|
import type { QueryFilters } from "./query";
|
|
50
35
|
import {
|
|
51
36
|
filterAgents,
|
|
@@ -56,18 +41,6 @@ import {
|
|
|
56
41
|
findCapabilities,
|
|
57
42
|
loadIndex,
|
|
58
43
|
} from "./query";
|
|
59
|
-
import {
|
|
60
|
-
installCommand,
|
|
61
|
-
searchCommand,
|
|
62
|
-
sourcesCommand,
|
|
63
|
-
templatesCommand,
|
|
64
|
-
updateCommand,
|
|
65
|
-
verifySourceCommand,
|
|
66
|
-
} from "./remote";
|
|
67
|
-
import { scanCommand } from "./scan";
|
|
68
|
-
import { selfUpdateCommand } from "./self-update";
|
|
69
|
-
import { snippetsCommand } from "./snippets-cli";
|
|
70
|
-
import { trustCommand, untrustCommand } from "./trust";
|
|
71
44
|
import { parseJsonLenient } from "./util/json";
|
|
72
45
|
|
|
73
46
|
type ListKind = "skills" | "mcp" | "agents" | "snippets" | "instructions";
|
|
@@ -1131,7 +1104,7 @@ async function graphCommand(argv: string[]) {
|
|
|
1131
1104
|
}
|
|
1132
1105
|
}
|
|
1133
1106
|
|
|
1134
|
-
function adaptersCommand(argv: string[]) {
|
|
1107
|
+
async function adaptersCommand(argv: string[]) {
|
|
1135
1108
|
if (argv.includes("--help") || argv.includes("-h") || argv[0] === "help") {
|
|
1136
1109
|
console.log(
|
|
1137
1110
|
renderPage({
|
|
@@ -1147,6 +1120,7 @@ function adaptersCommand(argv: string[]) {
|
|
|
1147
1120
|
);
|
|
1148
1121
|
return;
|
|
1149
1122
|
}
|
|
1123
|
+
const { getAllAdapters } = await import("./adapters");
|
|
1150
1124
|
const adapters = getAllAdapters();
|
|
1151
1125
|
if (!adapters.length) {
|
|
1152
1126
|
console.log(
|
|
@@ -1186,28 +1160,35 @@ async function main(argv: string[]) {
|
|
|
1186
1160
|
|
|
1187
1161
|
// Convenience: allow `fclt --show-duplicates` as shorthand for `fclt scan --show-duplicates`.
|
|
1188
1162
|
if (cmd === "--show-duplicates") {
|
|
1163
|
+
const { scanCommand } = await import("./scan");
|
|
1189
1164
|
await scanCommand([cmd, ...rest]);
|
|
1190
1165
|
return;
|
|
1191
1166
|
}
|
|
1192
1167
|
|
|
1193
1168
|
switch (cmd) {
|
|
1194
1169
|
case "scan":
|
|
1195
|
-
await scanCommand(rest);
|
|
1170
|
+
await import("./scan").then(({ scanCommand }) => scanCommand(rest));
|
|
1196
1171
|
return;
|
|
1197
1172
|
case "audit":
|
|
1198
|
-
await auditCommand(rest);
|
|
1173
|
+
await import("./audit").then(({ auditCommand }) => auditCommand(rest));
|
|
1199
1174
|
return;
|
|
1200
1175
|
case "migrate":
|
|
1201
|
-
await
|
|
1176
|
+
await import("./migrate").then(({ migrateCommand }) =>
|
|
1177
|
+
migrateCommand(rest)
|
|
1178
|
+
);
|
|
1202
1179
|
return;
|
|
1203
1180
|
case "doctor":
|
|
1204
|
-
await doctorCommand(rest);
|
|
1181
|
+
await import("./doctor").then(({ doctorCommand }) => doctorCommand(rest));
|
|
1205
1182
|
return;
|
|
1206
1183
|
case "consolidate":
|
|
1207
|
-
await
|
|
1184
|
+
await import("./consolidate").then(({ consolidateCommand }) =>
|
|
1185
|
+
consolidateCommand(rest)
|
|
1186
|
+
);
|
|
1208
1187
|
return;
|
|
1209
1188
|
case "index":
|
|
1210
|
-
await
|
|
1189
|
+
await import("./index-builder").then(({ indexCommand }) =>
|
|
1190
|
+
indexCommand(rest)
|
|
1191
|
+
);
|
|
1211
1192
|
return;
|
|
1212
1193
|
case "list":
|
|
1213
1194
|
await listCommand(rest);
|
|
@@ -1222,65 +1203,91 @@ async function main(argv: string[]) {
|
|
|
1222
1203
|
await graphCommand(rest);
|
|
1223
1204
|
return;
|
|
1224
1205
|
case "ai":
|
|
1225
|
-
await aiCommand(rest);
|
|
1206
|
+
await import("./ai").then(({ aiCommand }) => aiCommand(rest));
|
|
1226
1207
|
return;
|
|
1227
1208
|
case "adapters":
|
|
1228
1209
|
await adaptersCommand(rest);
|
|
1229
1210
|
return;
|
|
1230
1211
|
case "trust":
|
|
1231
|
-
await trustCommand(rest);
|
|
1212
|
+
await import("./trust").then(({ trustCommand }) => trustCommand(rest));
|
|
1232
1213
|
return;
|
|
1233
1214
|
case "untrust":
|
|
1234
|
-
await
|
|
1215
|
+
await import("./trust").then(({ untrustCommand }) =>
|
|
1216
|
+
untrustCommand(rest)
|
|
1217
|
+
);
|
|
1235
1218
|
return;
|
|
1236
1219
|
case "manage":
|
|
1237
|
-
await manageCommand(rest);
|
|
1220
|
+
await import("./manage").then(({ manageCommand }) => manageCommand(rest));
|
|
1238
1221
|
return;
|
|
1239
1222
|
case "unmanage":
|
|
1240
|
-
await
|
|
1223
|
+
await import("./manage").then(({ unmanageCommand }) =>
|
|
1224
|
+
unmanageCommand(rest)
|
|
1225
|
+
);
|
|
1241
1226
|
return;
|
|
1242
1227
|
case "managed":
|
|
1243
|
-
await
|
|
1228
|
+
await import("./manage").then(({ managedCommand }) =>
|
|
1229
|
+
managedCommand(rest)
|
|
1230
|
+
);
|
|
1244
1231
|
return;
|
|
1245
1232
|
case "enable":
|
|
1246
|
-
await
|
|
1233
|
+
await import("./enable-disable").then(({ enableCommand }) =>
|
|
1234
|
+
enableCommand(rest)
|
|
1235
|
+
);
|
|
1247
1236
|
return;
|
|
1248
1237
|
case "disable":
|
|
1249
|
-
await
|
|
1238
|
+
await import("./enable-disable").then(({ disableCommand }) =>
|
|
1239
|
+
disableCommand(rest)
|
|
1240
|
+
);
|
|
1250
1241
|
return;
|
|
1251
1242
|
case "sync":
|
|
1252
|
-
await syncCommand(rest);
|
|
1243
|
+
await import("./manage").then(({ syncCommand }) => syncCommand(rest));
|
|
1253
1244
|
return;
|
|
1254
1245
|
case "autosync":
|
|
1255
|
-
await
|
|
1246
|
+
await import("./autosync").then(({ autosyncCommand }) =>
|
|
1247
|
+
autosyncCommand(rest)
|
|
1248
|
+
);
|
|
1256
1249
|
return;
|
|
1257
1250
|
case "search":
|
|
1258
|
-
await searchCommand(rest);
|
|
1251
|
+
await import("./remote").then(({ searchCommand }) => searchCommand(rest));
|
|
1259
1252
|
return;
|
|
1260
1253
|
case "install":
|
|
1261
|
-
await
|
|
1254
|
+
await import("./remote").then(({ installCommand }) =>
|
|
1255
|
+
installCommand(rest)
|
|
1256
|
+
);
|
|
1262
1257
|
return;
|
|
1263
1258
|
case "update":
|
|
1264
1259
|
if (rest.includes("--self")) {
|
|
1265
|
-
await
|
|
1260
|
+
await import("./self-update").then(({ selfUpdateCommand }) =>
|
|
1261
|
+
selfUpdateCommand(rest.filter((arg) => arg !== "--self"))
|
|
1262
|
+
);
|
|
1266
1263
|
return;
|
|
1267
1264
|
}
|
|
1268
|
-
await updateCommand(rest);
|
|
1265
|
+
await import("./remote").then(({ updateCommand }) => updateCommand(rest));
|
|
1269
1266
|
return;
|
|
1270
1267
|
case "self-update":
|
|
1271
|
-
await
|
|
1268
|
+
await import("./self-update").then(({ selfUpdateCommand }) =>
|
|
1269
|
+
selfUpdateCommand(rest)
|
|
1270
|
+
);
|
|
1272
1271
|
return;
|
|
1273
1272
|
case "verify-source":
|
|
1274
|
-
await
|
|
1273
|
+
await import("./remote").then(({ verifySourceCommand }) =>
|
|
1274
|
+
verifySourceCommand(rest)
|
|
1275
|
+
);
|
|
1275
1276
|
return;
|
|
1276
1277
|
case "templates":
|
|
1277
|
-
await
|
|
1278
|
+
await import("./remote").then(({ templatesCommand }) =>
|
|
1279
|
+
templatesCommand(rest)
|
|
1280
|
+
);
|
|
1278
1281
|
return;
|
|
1279
1282
|
case "sources":
|
|
1280
|
-
await
|
|
1283
|
+
await import("./remote").then(({ sourcesCommand }) =>
|
|
1284
|
+
sourcesCommand(rest)
|
|
1285
|
+
);
|
|
1281
1286
|
return;
|
|
1282
1287
|
case "snippets":
|
|
1283
|
-
await
|
|
1288
|
+
await import("./snippets-cli").then(({ snippetsCommand }) =>
|
|
1289
|
+
snippetsCommand(rest)
|
|
1290
|
+
);
|
|
1284
1291
|
return;
|
|
1285
1292
|
default:
|
|
1286
1293
|
console.error(`Unknown command: ${cmd}`);
|