facult 2.5.2 → 2.7.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 +7 -0
- package/package.json +1 -1
- package/src/audit/agent.ts +26 -24
- package/src/audit/fix.ts +875 -0
- package/src/audit/index.ts +51 -2
- package/src/audit/safe.ts +596 -0
- package/src/audit/static.ts +151 -34
- package/src/audit/status.ts +21 -0
- package/src/audit/suppressions.ts +266 -0
- package/src/audit/tui.ts +784 -174
- package/src/audit/update-index.ts +4 -17
- package/src/cli-ui.ts +375 -0
- package/src/consolidate.ts +151 -55
- package/src/global-docs.ts +38 -12
- package/src/index.ts +511 -239
- package/src/manage.ts +51 -51
- package/src/mcp-config.ts +132 -0
- package/src/remote.ts +387 -117
- package/src/trust.ts +119 -11
- package/src/util/git.ts +95 -0
package/src/index.ts
CHANGED
|
@@ -10,6 +10,16 @@ import {
|
|
|
10
10
|
parseCliContextArgs,
|
|
11
11
|
resolveCliContextRoot,
|
|
12
12
|
} from "./cli-context";
|
|
13
|
+
import {
|
|
14
|
+
renderBadge,
|
|
15
|
+
renderBullets,
|
|
16
|
+
renderCatalog,
|
|
17
|
+
renderCode,
|
|
18
|
+
renderJsonBlock,
|
|
19
|
+
renderKeyValue,
|
|
20
|
+
renderPage,
|
|
21
|
+
renderTable,
|
|
22
|
+
} from "./cli-ui";
|
|
13
23
|
import { consolidateCommand } from "./consolidate";
|
|
14
24
|
import { doctorCommand } from "./doctor";
|
|
15
25
|
import { disableCommand, enableCommand } from "./enable-disable";
|
|
@@ -96,182 +106,232 @@ interface GraphCommandOptions {
|
|
|
96
106
|
}
|
|
97
107
|
|
|
98
108
|
function printHelp() {
|
|
99
|
-
console.log(
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
--enabled-for Filter list to entries enabled for a specific tool
|
|
186
|
-
--untrusted Filter list to entries that are not trusted
|
|
187
|
-
--flagged Filter list to entries flagged by audit
|
|
188
|
-
--pending Filter list to entries pending audit
|
|
189
|
-
--for Comma-separated list of tools for enable/disable
|
|
190
|
-
--dry-run Show what sync would change
|
|
191
|
-
--as Install/scaffold target name override
|
|
192
|
-
--limit Max results for search
|
|
193
|
-
--apply Apply updates (update command)
|
|
194
|
-
--self (update) run self-update flow instead of remote item updates
|
|
195
|
-
--strict-source-trust Enforce trust-only remote install/update actions
|
|
196
|
-
--show-secrets (show) Print raw secret values (unsafe)
|
|
197
|
-
--root Select a canonical .ai root explicitly
|
|
198
|
-
--global Force the global canonical root
|
|
199
|
-
--project Force the nearest repo-local .ai root
|
|
200
|
-
--scope Capability view scope: merged|global|project
|
|
201
|
-
--source Filter to builtin|global|project asset provenance
|
|
202
|
-
`);
|
|
109
|
+
console.log(
|
|
110
|
+
renderPage({
|
|
111
|
+
title: "fclt",
|
|
112
|
+
subtitle:
|
|
113
|
+
"Manage canonical AI capability, rendered tool surfaces, and evolution state.",
|
|
114
|
+
sections: [
|
|
115
|
+
{
|
|
116
|
+
title: "Usage",
|
|
117
|
+
lines: renderBullets([
|
|
118
|
+
`${renderCode("fclt list")} defaults to ${renderCode("skills")} when you do not specify a type.`,
|
|
119
|
+
`${renderCode("fclt graph <asset>")} is shorthand for ${renderCode("fclt graph show <asset>")}.`,
|
|
120
|
+
`${renderCode("fclt templates init ...")} is the main entry for scaffolding new canonical capability.`,
|
|
121
|
+
]),
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
title: "Core Commands",
|
|
125
|
+
lines: renderTable({
|
|
126
|
+
headers: ["Command", "Purpose"],
|
|
127
|
+
rows: [
|
|
128
|
+
["scan", "Scan local tool configs and discovered assets"],
|
|
129
|
+
[
|
|
130
|
+
"audit",
|
|
131
|
+
"Run security audits with interactive or scripted flows",
|
|
132
|
+
],
|
|
133
|
+
[
|
|
134
|
+
"consolidate",
|
|
135
|
+
"Import existing skills and MCP configs into canonical state",
|
|
136
|
+
],
|
|
137
|
+
["index", "Rebuild the generated capability index"],
|
|
138
|
+
[
|
|
139
|
+
"list",
|
|
140
|
+
"List indexed skills, MCP, agents, snippets, or instructions",
|
|
141
|
+
],
|
|
142
|
+
["show", "Inspect one indexed asset and its source contents"],
|
|
143
|
+
["find", "Search indexed capability across asset types"],
|
|
144
|
+
["graph", "Inspect capability graph nodes, deps, and dependents"],
|
|
145
|
+
[
|
|
146
|
+
"templates",
|
|
147
|
+
"Scaffold skills, MCP, agents, snippets, and automations",
|
|
148
|
+
],
|
|
149
|
+
["search/install/update", "Work with remote capability indices"],
|
|
150
|
+
[
|
|
151
|
+
"manage/sync",
|
|
152
|
+
"Enter managed mode and render tool-native output",
|
|
153
|
+
],
|
|
154
|
+
["ai", "Capture writeback and evolve canonical assets"],
|
|
155
|
+
],
|
|
156
|
+
}),
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
title: "Common Options",
|
|
160
|
+
lines: renderTable({
|
|
161
|
+
headers: ["Option", "Meaning"],
|
|
162
|
+
rows: [
|
|
163
|
+
[
|
|
164
|
+
"--json",
|
|
165
|
+
"Machine-readable output instead of formatted terminal UI",
|
|
166
|
+
],
|
|
167
|
+
["--dry-run", "Show intended writes without mutating files"],
|
|
168
|
+
[
|
|
169
|
+
"--root / --global / --project",
|
|
170
|
+
"Pick the canonical root explicitly",
|
|
171
|
+
],
|
|
172
|
+
[
|
|
173
|
+
"--scope / --source",
|
|
174
|
+
"Narrow merged views by scope or provenance",
|
|
175
|
+
],
|
|
176
|
+
[
|
|
177
|
+
"--non-interactive / --yes",
|
|
178
|
+
"Suppress prompts where the command supports inferred defaults",
|
|
179
|
+
],
|
|
180
|
+
],
|
|
181
|
+
}),
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
title: "Examples",
|
|
185
|
+
lines: renderBullets([
|
|
186
|
+
renderCode("fclt list"),
|
|
187
|
+
renderCode("fclt graph skills:capability-evolution"),
|
|
188
|
+
renderCode("fclt templates init skill review-checklist"),
|
|
189
|
+
renderCode("fclt templates init agent writeback-curator"),
|
|
190
|
+
]),
|
|
191
|
+
},
|
|
192
|
+
],
|
|
193
|
+
})
|
|
194
|
+
);
|
|
203
195
|
}
|
|
204
196
|
|
|
205
197
|
function printListHelp() {
|
|
206
|
-
console.log(
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
198
|
+
console.log(
|
|
199
|
+
renderPage({
|
|
200
|
+
title: "fclt list",
|
|
201
|
+
subtitle: "List indexed entries from the canonical store.",
|
|
202
|
+
sections: [
|
|
203
|
+
{
|
|
204
|
+
title: "Usage",
|
|
205
|
+
lines: renderBullets([
|
|
206
|
+
renderCode(
|
|
207
|
+
"fclt list [skills|mcp|agents|snippets|instructions] [options]"
|
|
208
|
+
),
|
|
209
|
+
renderCode("fclt list"),
|
|
210
|
+
]),
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
title: "Options",
|
|
214
|
+
lines: renderTable({
|
|
215
|
+
headers: ["Option", "Meaning"],
|
|
216
|
+
rows: [
|
|
217
|
+
[
|
|
218
|
+
"--enabled-for TOOL",
|
|
219
|
+
"Only include entries enabled for one tool",
|
|
220
|
+
],
|
|
221
|
+
["--untrusted", "Only include entries without trust approval"],
|
|
222
|
+
["--flagged", "Only include entries flagged by audit"],
|
|
223
|
+
["--pending", "Only include entries still pending audit"],
|
|
224
|
+
["--root / --global / --project", "Choose the canonical root"],
|
|
225
|
+
["--scope", "merged, global, or project"],
|
|
226
|
+
["--source", "builtin, global, or project provenance"],
|
|
227
|
+
["--json", "Print the raw JSON array"],
|
|
228
|
+
],
|
|
229
|
+
}),
|
|
230
|
+
},
|
|
231
|
+
],
|
|
232
|
+
})
|
|
233
|
+
);
|
|
223
234
|
}
|
|
224
235
|
|
|
225
236
|
function printShowHelp() {
|
|
226
|
-
console.log(
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
237
|
+
console.log(
|
|
238
|
+
renderPage({
|
|
239
|
+
title: "fclt show",
|
|
240
|
+
subtitle: "Inspect one indexed entry and the source file behind it.",
|
|
241
|
+
sections: [
|
|
242
|
+
{
|
|
243
|
+
title: "Usage",
|
|
244
|
+
lines: renderBullets([
|
|
245
|
+
renderCode("fclt show <name>"),
|
|
246
|
+
renderCode("fclt show mcp:<name> [--show-secrets]"),
|
|
247
|
+
renderCode("fclt show instruction:<name>"),
|
|
248
|
+
]),
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
title: "Options",
|
|
252
|
+
lines: renderTable({
|
|
253
|
+
headers: ["Option", "Meaning"],
|
|
254
|
+
rows: [
|
|
255
|
+
[
|
|
256
|
+
"--show-secrets",
|
|
257
|
+
"For MCP configs, print raw secrets instead of redacting",
|
|
258
|
+
],
|
|
259
|
+
["--root / --global / --project", "Choose the canonical root"],
|
|
260
|
+
["--scope", "merged, global, or project"],
|
|
261
|
+
["--source", "builtin, global, or project provenance"],
|
|
262
|
+
],
|
|
263
|
+
}),
|
|
264
|
+
},
|
|
265
|
+
],
|
|
266
|
+
})
|
|
267
|
+
);
|
|
241
268
|
}
|
|
242
269
|
|
|
243
270
|
function printFindHelp() {
|
|
244
|
-
console.log(
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
271
|
+
console.log(
|
|
272
|
+
renderPage({
|
|
273
|
+
title: "fclt find",
|
|
274
|
+
subtitle:
|
|
275
|
+
"Search indexed capability across skills, MCP, agents, snippets, and instructions.",
|
|
276
|
+
sections: [
|
|
277
|
+
{
|
|
278
|
+
title: "Usage",
|
|
279
|
+
lines: renderBullets([renderCode("fclt find <query> [--json]")]),
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
title: "Options",
|
|
283
|
+
lines: renderTable({
|
|
284
|
+
headers: ["Option", "Meaning"],
|
|
285
|
+
rows: [
|
|
286
|
+
["--root / --global / --project", "Choose the canonical root"],
|
|
287
|
+
["--scope", "merged, global, or project"],
|
|
288
|
+
["--source", "builtin, global, or project provenance"],
|
|
289
|
+
["--json", "Print the raw JSON array"],
|
|
290
|
+
],
|
|
291
|
+
}),
|
|
292
|
+
},
|
|
293
|
+
],
|
|
294
|
+
})
|
|
295
|
+
);
|
|
257
296
|
}
|
|
258
297
|
|
|
259
298
|
function printGraphHelp() {
|
|
260
|
-
console.log(
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
299
|
+
console.log(
|
|
300
|
+
renderPage({
|
|
301
|
+
title: "fclt graph",
|
|
302
|
+
subtitle: "Inspect explicit capability graph nodes and relations.",
|
|
303
|
+
sections: [
|
|
304
|
+
{
|
|
305
|
+
title: "Usage",
|
|
306
|
+
lines: renderBullets([
|
|
307
|
+
renderCode("fclt graph <asset> [--json]"),
|
|
308
|
+
renderCode("fclt graph show <asset> [--json]"),
|
|
309
|
+
renderCode("fclt graph deps <asset> [--json]"),
|
|
310
|
+
renderCode("fclt graph dependents <asset> [--json]"),
|
|
311
|
+
]),
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
title: "Notes",
|
|
315
|
+
lines: renderBullets([
|
|
316
|
+
`${renderCode("fclt graph <asset>")} defaults to ${renderCode("show")}.`,
|
|
317
|
+
"Selectors can be canonical refs, names, or graph node ids.",
|
|
318
|
+
]),
|
|
319
|
+
},
|
|
320
|
+
{
|
|
321
|
+
title: "Options",
|
|
322
|
+
lines: renderTable({
|
|
323
|
+
headers: ["Option", "Meaning"],
|
|
324
|
+
rows: [
|
|
325
|
+
["--root / --global / --project", "Choose the canonical root"],
|
|
326
|
+
["--scope", "merged, global, or project"],
|
|
327
|
+
["--source", "builtin, global, or project provenance"],
|
|
328
|
+
["--json", "Print raw graph JSON"],
|
|
329
|
+
],
|
|
330
|
+
}),
|
|
331
|
+
},
|
|
332
|
+
],
|
|
333
|
+
})
|
|
334
|
+
);
|
|
275
335
|
}
|
|
276
336
|
|
|
277
337
|
function parseListKind(argv: string[]): { kind: ListKind; startIndex: number } {
|
|
@@ -371,15 +431,16 @@ export function parseFindArgs(argv: string[]): FindCommandOptions {
|
|
|
371
431
|
return { text, json };
|
|
372
432
|
}
|
|
373
433
|
|
|
374
|
-
function parseGraphArgs(argv: string[]): GraphCommandOptions {
|
|
375
|
-
const [
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
434
|
+
export function parseGraphArgs(argv: string[]): GraphCommandOptions {
|
|
435
|
+
const [first, ...rest] = argv;
|
|
436
|
+
const hasExplicitKind =
|
|
437
|
+
first === "show" || first === "deps" || first === "dependents";
|
|
438
|
+
const kind: GraphCommandKind = hasExplicitKind ? first : "show";
|
|
439
|
+
const args = hasExplicitKind ? rest : argv;
|
|
379
440
|
|
|
380
441
|
let json = false;
|
|
381
442
|
let target: string | null = null;
|
|
382
|
-
for (const arg of
|
|
443
|
+
for (const arg of args) {
|
|
383
444
|
if (!arg) {
|
|
384
445
|
continue;
|
|
385
446
|
}
|
|
@@ -409,6 +470,68 @@ function scopeFilterForMode(
|
|
|
409
470
|
return scopeMode === "project" ? "project" : undefined;
|
|
410
471
|
}
|
|
411
472
|
|
|
473
|
+
function sourceLabel(entry: { sourceKind?: string; scope?: string }): string {
|
|
474
|
+
const source = entry.sourceKind?.trim();
|
|
475
|
+
const scope = entry.scope?.trim();
|
|
476
|
+
if (source && scope) {
|
|
477
|
+
return `${source}/${scope}`;
|
|
478
|
+
}
|
|
479
|
+
return source ?? scope ?? "merged";
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
function describeFilters(filters: QueryFilters): string {
|
|
483
|
+
const parts: string[] = [];
|
|
484
|
+
|
|
485
|
+
if (filters.enabledFor) {
|
|
486
|
+
parts.push(`enabled for ${filters.enabledFor}`);
|
|
487
|
+
}
|
|
488
|
+
if (filters.untrusted) {
|
|
489
|
+
parts.push("untrusted only");
|
|
490
|
+
}
|
|
491
|
+
if (filters.flagged) {
|
|
492
|
+
parts.push("flagged only");
|
|
493
|
+
}
|
|
494
|
+
if (filters.pending) {
|
|
495
|
+
parts.push("pending only");
|
|
496
|
+
}
|
|
497
|
+
if (filters.sourceKind) {
|
|
498
|
+
parts.push(`source ${filters.sourceKind}`);
|
|
499
|
+
}
|
|
500
|
+
if (filters.scope) {
|
|
501
|
+
parts.push(`scope ${filters.scope}`);
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
return parts.join(" • ");
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
function trustBadge(trusted?: boolean): string {
|
|
508
|
+
return trusted
|
|
509
|
+
? renderBadge("trusted", "success")
|
|
510
|
+
: renderBadge("untrusted", "warn");
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
function auditBadge(status?: string): string {
|
|
514
|
+
const normalized = (status ?? "pending").trim().toLowerCase();
|
|
515
|
+
if (normalized === "passed") {
|
|
516
|
+
return renderBadge("audit passed", "success");
|
|
517
|
+
}
|
|
518
|
+
if (normalized === "flagged") {
|
|
519
|
+
return renderBadge("audit flagged", "danger");
|
|
520
|
+
}
|
|
521
|
+
return renderBadge("audit pending", "warn");
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
function displayDescription(value?: string): string {
|
|
525
|
+
const normalized = value
|
|
526
|
+
?.trim()
|
|
527
|
+
.replaceAll('\\"', '"')
|
|
528
|
+
.replace(INLINE_NAME_DESCRIPTION_RE, "");
|
|
529
|
+
if (!normalized || normalized === ">") {
|
|
530
|
+
return "No description.";
|
|
531
|
+
}
|
|
532
|
+
return normalized;
|
|
533
|
+
}
|
|
534
|
+
|
|
412
535
|
function resolveContextualOptions(
|
|
413
536
|
argv: string[],
|
|
414
537
|
opts?: { allowSource?: boolean }
|
|
@@ -504,33 +627,70 @@ async function listCommand(argv: string[]) {
|
|
|
504
627
|
return;
|
|
505
628
|
}
|
|
506
629
|
|
|
507
|
-
|
|
630
|
+
if (entries.length === 0) {
|
|
631
|
+
console.log(
|
|
632
|
+
renderPage({
|
|
633
|
+
title: `fclt list ${opts.kind}`,
|
|
634
|
+
subtitle: "No matching entries.",
|
|
635
|
+
sections: [
|
|
636
|
+
{
|
|
637
|
+
title: "Next Steps",
|
|
638
|
+
lines: renderBullets([
|
|
639
|
+
renderCode("fclt index --force"),
|
|
640
|
+
renderCode("fclt templates list"),
|
|
641
|
+
]),
|
|
642
|
+
},
|
|
643
|
+
],
|
|
644
|
+
footer: describeFilters(opts.filters)
|
|
645
|
+
? [describeFilters(opts.filters)]
|
|
646
|
+
: undefined,
|
|
647
|
+
})
|
|
648
|
+
);
|
|
649
|
+
return;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
const items = entries.map((entry) => {
|
|
508
653
|
if (opts.kind === "skills") {
|
|
509
654
|
const skill = entry as SkillEntry;
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
auditStatus
|
|
655
|
+
return {
|
|
656
|
+
title: skill.name,
|
|
657
|
+
meta: sourceLabel(skill),
|
|
658
|
+
badges: [trustBadge(skill.trusted), auditBadge(skill.auditStatus)],
|
|
659
|
+
description: displayDescription(skill.description),
|
|
514
660
|
};
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
trusted
|
|
523
|
-
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
if (opts.kind === "mcp") {
|
|
664
|
+
const server = entry as McpEntry;
|
|
665
|
+
return {
|
|
666
|
+
title: server.name,
|
|
667
|
+
meta: sourceLabel(server),
|
|
668
|
+
badges: [trustBadge(server.trusted), auditBadge(server.auditStatus)],
|
|
669
|
+
description:
|
|
670
|
+
Array.isArray(server.enabledFor) && server.enabledFor.length > 0
|
|
671
|
+
? `Enabled for ${server.enabledFor.join(", ")}.`
|
|
672
|
+
: "No enabled-for restrictions recorded.",
|
|
524
673
|
};
|
|
525
|
-
const trustedLabel = meta.trusted === true ? "trusted" : "untrusted";
|
|
526
|
-
const auditLabel = (meta.auditStatus ?? "pending").trim().toLowerCase();
|
|
527
|
-
console.log(
|
|
528
|
-
`${entry.name}\t[${trustedLabel}; audit=${auditLabel}]${formatSourceMeta(entry)}`
|
|
529
|
-
);
|
|
530
|
-
} else {
|
|
531
|
-
console.log(`${entry.name}${formatSourceMeta(entry)}`);
|
|
532
674
|
}
|
|
533
|
-
|
|
675
|
+
|
|
676
|
+
const detailEntry = entry as AgentEntry | SnippetEntry | InstructionEntry;
|
|
677
|
+
return {
|
|
678
|
+
title: entry.name,
|
|
679
|
+
meta: sourceLabel(entry),
|
|
680
|
+
description: displayDescription(detailEntry.description),
|
|
681
|
+
};
|
|
682
|
+
});
|
|
683
|
+
|
|
684
|
+
console.log(
|
|
685
|
+
renderPage({
|
|
686
|
+
title: `fclt list ${opts.kind}`,
|
|
687
|
+
subtitle: `${entries.length} matching entr${entries.length === 1 ? "y" : "ies"}`,
|
|
688
|
+
sections: [{ title: "Entries", lines: renderCatalog(items) }],
|
|
689
|
+
footer: describeFilters(opts.filters)
|
|
690
|
+
? [describeFilters(opts.filters)]
|
|
691
|
+
: undefined,
|
|
692
|
+
})
|
|
693
|
+
);
|
|
534
694
|
}
|
|
535
695
|
|
|
536
696
|
async function readEntryContents(entryPath: string): Promise<string> {
|
|
@@ -589,35 +749,55 @@ async function findCommand(argv: string[]) {
|
|
|
589
749
|
return;
|
|
590
750
|
}
|
|
591
751
|
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
752
|
+
if (matches.length === 0) {
|
|
753
|
+
console.log(
|
|
754
|
+
renderPage({
|
|
755
|
+
title: "fclt find",
|
|
756
|
+
subtitle: `No matches for "${opts.text}".`,
|
|
757
|
+
sections: [
|
|
758
|
+
{
|
|
759
|
+
title: "Try",
|
|
760
|
+
lines: renderBullets([
|
|
761
|
+
renderCode("fclt list"),
|
|
762
|
+
renderCode("fclt index --force"),
|
|
763
|
+
]),
|
|
764
|
+
},
|
|
765
|
+
],
|
|
766
|
+
})
|
|
767
|
+
);
|
|
768
|
+
return;
|
|
595
769
|
}
|
|
770
|
+
|
|
771
|
+
console.log(
|
|
772
|
+
renderPage({
|
|
773
|
+
title: "fclt find",
|
|
774
|
+
subtitle: `${matches.length} match${matches.length === 1 ? "" : "es"} for "${opts.text}"`,
|
|
775
|
+
sections: [
|
|
776
|
+
{
|
|
777
|
+
title: "Results",
|
|
778
|
+
lines: renderCatalog(
|
|
779
|
+
matches.map((entry) => ({
|
|
780
|
+
title: `${entry.kind}:${entry.name}`,
|
|
781
|
+
meta: sourceLabel(entry),
|
|
782
|
+
description: displayDescription(entry.description),
|
|
783
|
+
}))
|
|
784
|
+
),
|
|
785
|
+
},
|
|
786
|
+
],
|
|
787
|
+
})
|
|
788
|
+
);
|
|
596
789
|
}
|
|
597
790
|
|
|
598
791
|
const SECRET_KEY_RE = /(TOKEN|KEY|SECRET|PASSWORD|PASS|BEARER)/i;
|
|
599
792
|
const SECRETY_STRING_RE =
|
|
600
793
|
/\b(sk-[A-Za-z0-9]{10,}|ghp_[A-Za-z0-9]{10,}|github_pat_[A-Za-z0-9_]{10,})\b/g;
|
|
794
|
+
const INLINE_NAME_DESCRIPTION_RE = /^name:\s+\S+\s+description:\s*/i;
|
|
795
|
+
const TRAILING_NEWLINE_RE = /\n$/;
|
|
601
796
|
|
|
602
797
|
function redactPossibleSecrets(value: string): string {
|
|
603
798
|
return value.replace(SECRETY_STRING_RE, "<redacted>");
|
|
604
799
|
}
|
|
605
800
|
|
|
606
|
-
function formatSourceMeta(entry: {
|
|
607
|
-
sourceKind?: string;
|
|
608
|
-
scope?: string;
|
|
609
|
-
}): string {
|
|
610
|
-
const source = entry.sourceKind?.trim();
|
|
611
|
-
const scope = entry.scope?.trim();
|
|
612
|
-
if (!(source || scope)) {
|
|
613
|
-
return "";
|
|
614
|
-
}
|
|
615
|
-
if (source && scope) {
|
|
616
|
-
return `\t[${source}/${scope}]`;
|
|
617
|
-
}
|
|
618
|
-
return `\t[${source ?? scope}]`;
|
|
619
|
-
}
|
|
620
|
-
|
|
621
801
|
function sanitizeForDisplay(value: unknown): unknown {
|
|
622
802
|
if (typeof value === "string") {
|
|
623
803
|
return redactPossibleSecrets(value);
|
|
@@ -796,10 +976,28 @@ async function showCommand(argv: string[]) {
|
|
|
796
976
|
}
|
|
797
977
|
}
|
|
798
978
|
|
|
799
|
-
console.log(
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
979
|
+
console.log(
|
|
980
|
+
renderPage({
|
|
981
|
+
title: `fclt show ${kind}:${entry.name}`,
|
|
982
|
+
subtitle: contentPath,
|
|
983
|
+
sections: [
|
|
984
|
+
{
|
|
985
|
+
title: "Metadata",
|
|
986
|
+
lines: renderJsonBlock(displayEntry),
|
|
987
|
+
},
|
|
988
|
+
{
|
|
989
|
+
title: "Contents",
|
|
990
|
+
lines: displayContents.replace(TRAILING_NEWLINE_RE, "").split("\n"),
|
|
991
|
+
},
|
|
992
|
+
],
|
|
993
|
+
footer:
|
|
994
|
+
kind === "mcp" && !showSecrets
|
|
995
|
+
? [
|
|
996
|
+
"Secrets are redacted. Re-run with --show-secrets only when you need raw values.",
|
|
997
|
+
]
|
|
998
|
+
: undefined,
|
|
999
|
+
})
|
|
1000
|
+
);
|
|
803
1001
|
}
|
|
804
1002
|
|
|
805
1003
|
async function graphCommand(argv: string[]) {
|
|
@@ -858,29 +1056,75 @@ async function graphCommand(argv: string[]) {
|
|
|
858
1056
|
}
|
|
859
1057
|
|
|
860
1058
|
if (opts.kind === "show") {
|
|
861
|
-
console.log(
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
1059
|
+
console.log(
|
|
1060
|
+
renderPage({
|
|
1061
|
+
title: `fclt graph ${node.id}`,
|
|
1062
|
+
subtitle: `${node.kind} • ${sourceLabel(node)}`,
|
|
1063
|
+
sections: [
|
|
1064
|
+
{
|
|
1065
|
+
title: "Node",
|
|
1066
|
+
lines: renderKeyValue([
|
|
1067
|
+
["id", node.id],
|
|
1068
|
+
["kind", node.kind],
|
|
1069
|
+
["name", node.name],
|
|
1070
|
+
["path", node.path ?? "—"],
|
|
1071
|
+
["canonicalRef", node.canonicalRef ?? "—"],
|
|
1072
|
+
]),
|
|
1073
|
+
},
|
|
1074
|
+
{
|
|
1075
|
+
title: "Dependencies",
|
|
1076
|
+
lines:
|
|
1077
|
+
deps.length > 0
|
|
1078
|
+
? renderCatalog(
|
|
1079
|
+
deps.map((dep) => ({
|
|
1080
|
+
title: dep.node.id,
|
|
1081
|
+
meta: dep.edge.kind,
|
|
1082
|
+
details: [dep.edge.locator],
|
|
1083
|
+
}))
|
|
1084
|
+
)
|
|
1085
|
+
: ["No dependencies."],
|
|
1086
|
+
},
|
|
1087
|
+
{
|
|
1088
|
+
title: "Dependents",
|
|
1089
|
+
lines:
|
|
1090
|
+
dependents.length > 0
|
|
1091
|
+
? renderCatalog(
|
|
1092
|
+
dependents.map((dependent) => ({
|
|
1093
|
+
title: dependent.node.id,
|
|
1094
|
+
meta: dependent.edge.kind,
|
|
1095
|
+
details: [dependent.edge.locator],
|
|
1096
|
+
}))
|
|
1097
|
+
)
|
|
1098
|
+
: ["No dependents."],
|
|
1099
|
+
},
|
|
1100
|
+
],
|
|
1101
|
+
})
|
|
1102
|
+
);
|
|
875
1103
|
return;
|
|
876
1104
|
}
|
|
877
1105
|
|
|
878
1106
|
const relations = opts.kind === "deps" ? deps : dependents;
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
1107
|
+
console.log(
|
|
1108
|
+
renderPage({
|
|
1109
|
+
title: `fclt graph ${opts.kind}`,
|
|
1110
|
+
subtitle: `${relations.length} relation${relations.length === 1 ? "" : "s"} for ${node.id}`,
|
|
1111
|
+
sections: [
|
|
1112
|
+
{
|
|
1113
|
+
title: opts.kind === "deps" ? "Dependencies" : "Dependents",
|
|
1114
|
+
lines:
|
|
1115
|
+
relations.length > 0
|
|
1116
|
+
? renderCatalog(
|
|
1117
|
+
relations.map((relation) => ({
|
|
1118
|
+
title: relation.node.id,
|
|
1119
|
+
meta: relation.edge.kind,
|
|
1120
|
+
details: [relation.edge.locator],
|
|
1121
|
+
}))
|
|
1122
|
+
)
|
|
1123
|
+
: ["No relations found."],
|
|
1124
|
+
},
|
|
1125
|
+
],
|
|
1126
|
+
})
|
|
1127
|
+
);
|
|
884
1128
|
} catch (err) {
|
|
885
1129
|
console.error(err instanceof Error ? err.message : String(err));
|
|
886
1130
|
process.exitCode = 1;
|
|
@@ -890,19 +1134,47 @@ async function graphCommand(argv: string[]) {
|
|
|
890
1134
|
function adaptersCommand(argv: string[]) {
|
|
891
1135
|
if (argv.includes("--help") || argv.includes("-h") || argv[0] === "help") {
|
|
892
1136
|
console.log(
|
|
893
|
-
|
|
1137
|
+
renderPage({
|
|
1138
|
+
title: "fclt adapters",
|
|
1139
|
+
subtitle: "List registered tool adapters.",
|
|
1140
|
+
sections: [
|
|
1141
|
+
{
|
|
1142
|
+
title: "Usage",
|
|
1143
|
+
lines: renderBullets([renderCode("fclt adapters")]),
|
|
1144
|
+
},
|
|
1145
|
+
],
|
|
1146
|
+
})
|
|
894
1147
|
);
|
|
895
1148
|
return;
|
|
896
1149
|
}
|
|
897
1150
|
const adapters = getAllAdapters();
|
|
898
1151
|
if (!adapters.length) {
|
|
899
|
-
console.log(
|
|
1152
|
+
console.log(
|
|
1153
|
+
renderPage({
|
|
1154
|
+
title: "fclt adapters",
|
|
1155
|
+
subtitle: "No adapters registered.",
|
|
1156
|
+
sections: [],
|
|
1157
|
+
})
|
|
1158
|
+
);
|
|
900
1159
|
return;
|
|
901
1160
|
}
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
1161
|
+
console.log(
|
|
1162
|
+
renderPage({
|
|
1163
|
+
title: "fclt adapters",
|
|
1164
|
+
subtitle: `${adapters.length} registered adapter${adapters.length === 1 ? "" : "s"}`,
|
|
1165
|
+
sections: [
|
|
1166
|
+
{
|
|
1167
|
+
title: "Adapters",
|
|
1168
|
+
lines: renderCatalog(
|
|
1169
|
+
adapters.map((adapter) => ({
|
|
1170
|
+
title: adapter.id,
|
|
1171
|
+
description: `Versions: ${adapter.versions.join(", ")}`,
|
|
1172
|
+
}))
|
|
1173
|
+
),
|
|
1174
|
+
},
|
|
1175
|
+
],
|
|
1176
|
+
})
|
|
1177
|
+
);
|
|
906
1178
|
}
|
|
907
1179
|
|
|
908
1180
|
async function main(argv: string[]) {
|