faf-cli 6.10.1 → 6.12.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 +29 -4
- package/dist/cli.js +228 -228
- package/dist/cli.js.map +10 -10
- package/dist/commands/bench.d.ts +7 -2
- package/dist/core/interview.d.ts +38 -0
- package/dist/detect/assemble.d.ts +16 -0
- package/dist/detect/frameworks.d.ts +3 -0
- package/dist/detect/relentless.d.ts +22 -0
- package/dist/detect/scanner.d.ts +70 -0
- package/dist/detect/stack.d.ts +3 -0
- package/dist/index.d.ts +7 -2
- package/dist/index.js +138 -129
- package/dist/index.js.map +16 -6
- package/dist/interop/servercard.d.ts +61 -0
- package/dist/interrogate/cargo.d.ts +10 -0
- package/dist/interrogate/index.d.ts +16 -0
- package/dist/interrogate/readme.d.ts +25 -0
- package/dist/interrogate/types.d.ts +49 -0
- package/package.json +4 -3
- package/project.faf +1 -1
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { FafData } from '../core/types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Generate an MCP Server Card (SEP-2127) from a .faf.
|
|
4
|
+
*
|
|
5
|
+
* The card is a published discovery manifest. By design it carries the FAF
|
|
6
|
+
* context-block in `_meta["one.faf/context"]` — so every Server Card produced
|
|
7
|
+
* through FAF ships FAF context by default. This emitter is the SINGLE SOURCE of
|
|
8
|
+
* the block — faf-server-card-ref and `.fafa` provenance compose it (never
|
|
9
|
+
* hand-roll), so every surface is byte-identical by construction: one context,
|
|
10
|
+
* one source, every door.
|
|
11
|
+
*
|
|
12
|
+
* Honest-first: no score is baked (it would go stale on disk); the block points
|
|
13
|
+
* to the .faf and asserts the score is deterministic. The card omits `remotes`
|
|
14
|
+
* unless a deployment URL is supplied — it must not claim an endpoint it lacks.
|
|
15
|
+
*/
|
|
16
|
+
export interface ServerCardOptions {
|
|
17
|
+
/** Pointer to the .faf context (default: ./project.faf). Pass an absolute URL
|
|
18
|
+
* for a remote/served card (e.g. faf-server-card-ref at context.faf.one). */
|
|
19
|
+
fafPointer?: string;
|
|
20
|
+
/** Optional live endpoint; adds a streamable-http remote when set. */
|
|
21
|
+
remoteUrl?: string;
|
|
22
|
+
/** Optional "verify the score here" URL — makes verify-don't-trust actionable
|
|
23
|
+
* (faf-server-card-ref uses https://faf.one). Omitted from the block if unset. */
|
|
24
|
+
scoreEndpoint?: string;
|
|
25
|
+
/** Override timestamp (tests); otherwise .faf `generated`, else now. */
|
|
26
|
+
now?: string;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* The canonical FAF context-block — the value of `_meta["one.faf/context"]`,
|
|
30
|
+
* identical across every surface (Server Card, registry `server.json`, `.fafa`):
|
|
31
|
+
* one context, every door. Honest-first: no score baked (it would go stale on
|
|
32
|
+
* disk); it points to the .faf and asserts the score is deterministic.
|
|
33
|
+
*/
|
|
34
|
+
export declare function fafContextBlock(data: FafData, opts?: ServerCardOptions): Record<string, unknown>;
|
|
35
|
+
/** Build the Server Card object from .faf data. */
|
|
36
|
+
export declare function generateServerCard(data: FafData, opts?: ServerCardOptions): Record<string, unknown>;
|
|
37
|
+
export declare const REGISTRY_PUBLISHER_KEY = "io.modelcontextprotocol.registry/publisher-provided";
|
|
38
|
+
/**
|
|
39
|
+
* Build the `_meta` for an MCP Registry `server.json`.
|
|
40
|
+
*
|
|
41
|
+
* The SAME canonical context-block as the Server Card, but nested under
|
|
42
|
+
* `io.modelcontextprotocol.registry/publisher-provided` — the ONLY `_meta` key
|
|
43
|
+
* the official registry preserves on publish. Top-level keys (the way the card
|
|
44
|
+
* carries `one.faf/context`) are silently dropped by the registry. Throws if the
|
|
45
|
+
* block exceeds the registry's 4KB cap. Merge the result into an existing
|
|
46
|
+
* `server.json` `_meta`; don't regenerate the manifest (packages/mcpb are tuned).
|
|
47
|
+
*/
|
|
48
|
+
export declare function registryMeta(data: FafData, opts?: ServerCardOptions): Record<string, unknown>;
|
|
49
|
+
/** The canonical reverse-DNS registry name, e.g. `one.faf/claude-faf-mcp`.
|
|
50
|
+
*
|
|
51
|
+
* HOMEPAGE REQUIRED: the namespace is derived from `project.homepage`'s host
|
|
52
|
+
* (faf.one -> one.faf). With no homepage/website/url the namespace falls back
|
|
53
|
+
* to `local/<name>`. This can't silently ship — the migration is guarded
|
|
54
|
+
* (`rewrite-server-json.ts` refuses any name that isn't `one.faf/*`) — but set
|
|
55
|
+
* `homepage: https://faf.one` in the .faf to get the correct `one.faf/<name>`. */
|
|
56
|
+
export declare function registryName(data: FafData): string;
|
|
57
|
+
/** Write the Server Card to a `server-card` file. Returns the path.
|
|
58
|
+
* Per experimental-ext-server-card#22 the reserved location is
|
|
59
|
+
* `<streamable-http-url>/server-card` (no longer `.well-known`); serve the
|
|
60
|
+
* emitted file there as `application/mcp-server-card+json`. */
|
|
61
|
+
export declare function writeServerCard(dir: string, data: FafData, opts?: ServerCardOptions): string;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cargo.toml interrogation — extract Rust project metadata.
|
|
3
|
+
*
|
|
4
|
+
* The [package].description field is the Rust ecosystem's equivalent of
|
|
5
|
+
* package.json's `description` — a one-line elevator pitch. It maps to
|
|
6
|
+
* `project.goal` (overarching/use-case framing).
|
|
7
|
+
*/
|
|
8
|
+
import { type ExtractedContext } from './types.js';
|
|
9
|
+
/** Read Cargo.toml and extract per-slot content. Returns {} if no Cargo.toml. */
|
|
10
|
+
export declare function interrogateCargo(dir: string): ExtractedContext;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Repo interrogation orchestrator — runs all extractors and merges results.
|
|
3
|
+
*
|
|
4
|
+
* Order of precedence: extractors that run first WIN (their non-empty values
|
|
5
|
+
* are kept; later extractors fill only empties). README is highest-precedence
|
|
6
|
+
* because its prose is most likely to be hand-curated; manifest fields
|
|
7
|
+
* (Cargo.toml description, package.json description) are fallbacks.
|
|
8
|
+
*
|
|
9
|
+
* The orchestrator never overwrites with empty — only fills empties.
|
|
10
|
+
*/
|
|
11
|
+
import type { ExtractedContext } from './types.js';
|
|
12
|
+
export type { ExtractedContext } from './types.js';
|
|
13
|
+
export { interrogateReadme } from './readme.js';
|
|
14
|
+
export { interrogateCargo } from './cargo.js';
|
|
15
|
+
/** Run all repo interrogators and return merged context. */
|
|
16
|
+
export declare function interrogateRepo(dir: string): ExtractedContext;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* README.md interrogation — extract slot content from semantic markdown anchors.
|
|
3
|
+
*
|
|
4
|
+
* Per faf-auto-no-guess-no-slop:
|
|
5
|
+
* Each extractor looks for a specific anchor (heading, structural pattern).
|
|
6
|
+
* If the anchor is missing or the content fails validation → return undefined.
|
|
7
|
+
* No slot is synthesized from another slot's content.
|
|
8
|
+
*/
|
|
9
|
+
import { type ExtractedContext } from './types.js';
|
|
10
|
+
/** Extract `project.goal` — first prose paragraph or ## Use Case / ## Goal */
|
|
11
|
+
export declare function extractGoal(content: string): string | undefined;
|
|
12
|
+
/** Extract `human_context.what` — ## About / ## Description / ## Overview */
|
|
13
|
+
export declare function extractWhat(content: string): string | undefined;
|
|
14
|
+
/** Extract `human_context.who` — ## Audience / ## For Whom / ## Users */
|
|
15
|
+
export declare function extractWho(content: string): string | undefined;
|
|
16
|
+
/** Extract `human_context.why` — ## Why / ## Motivation / ## Problem */
|
|
17
|
+
export declare function extractWhy(content: string): string | undefined;
|
|
18
|
+
/** Extract `human_context.where` — ## Deployment / ## Where it runs / ## Install */
|
|
19
|
+
export declare function extractWhere(content: string): string | undefined;
|
|
20
|
+
/** Extract `human_context.when` — ## Status / ## History / "Production since" */
|
|
21
|
+
export declare function extractWhen(content: string): string | undefined;
|
|
22
|
+
/** Extract `human_context.how` — ## Architecture / ## How it works / ## Approach */
|
|
23
|
+
export declare function extractHow(content: string): string | undefined;
|
|
24
|
+
/** Read README.md and extract per-slot content. Returns {} if no README. */
|
|
25
|
+
export declare function interrogateReadme(dir: string): ExtractedContext;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Repo interrogation — extract slot content from README, Cargo.toml,
|
|
3
|
+
* package.json, etc., without guessing or substituting generic slop.
|
|
4
|
+
*
|
|
5
|
+
* Contract (per faf-auto-no-guess-no-slop doctrine):
|
|
6
|
+
* - Each slot has a per-slot evidence anchor
|
|
7
|
+
* - If the anchor is missing → leave empty (do NOT synthesize from other slots)
|
|
8
|
+
* - If the anchor is present but content is generic / too long / structural →
|
|
9
|
+
* leave empty (do NOT settle for slop)
|
|
10
|
+
* - Empty is honest; wrong is a lie
|
|
11
|
+
*
|
|
12
|
+
* Per goal-is-not-a-6w doctrine:
|
|
13
|
+
* - `project.goal` is the elevator pitch / use case — its own anchor
|
|
14
|
+
* - Each `human_context.*` 6W has its own anchor
|
|
15
|
+
* - No slot inherits from / is split-extracted from another
|
|
16
|
+
*/
|
|
17
|
+
/** Content extracted from a single source file (README, Cargo.toml, etc.) */
|
|
18
|
+
export interface ExtractedContext {
|
|
19
|
+
project?: {
|
|
20
|
+
/** One-line elevator pitch / use case. README first prose paragraph,
|
|
21
|
+
* ## Use Case section, or Cargo.toml description. */
|
|
22
|
+
goal?: string;
|
|
23
|
+
};
|
|
24
|
+
human_context?: {
|
|
25
|
+
/** Audience — "## Audience" / "for {audience}" patterns */
|
|
26
|
+
who?: string;
|
|
27
|
+
/** What's being built — "## About" / "## Description" / "X is..." */
|
|
28
|
+
what?: string;
|
|
29
|
+
/** Motivation — "## Why" / "## Motivation" / "Problem:" */
|
|
30
|
+
why?: string;
|
|
31
|
+
/** Where it runs — "## Deployment" / "## Where" / install-target */
|
|
32
|
+
where?: string;
|
|
33
|
+
/** Status / timeline — "## Status" / "Production since" / version line */
|
|
34
|
+
when?: string;
|
|
35
|
+
/** Approach — "## Architecture" / "## How it works" */
|
|
36
|
+
how?: string;
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/** Confidence threshold rules for extracted text — empty if violated */
|
|
40
|
+
export declare const EXTRACTION_LIMITS: {
|
|
41
|
+
/** Minimum length for a slot to be considered worth filling (in chars) */
|
|
42
|
+
readonly MIN_LENGTH: 8;
|
|
43
|
+
/** Maximum length — anything longer is documentation, not a slot fill */
|
|
44
|
+
readonly MAX_LENGTH: 280;
|
|
45
|
+
};
|
|
46
|
+
/** Generic slop patterns — extracted text matching these is rejected */
|
|
47
|
+
export declare const SLOP_PATTERNS: readonly RegExp[];
|
|
48
|
+
/** Validate extracted text against the no-guess/no-slop doctrine */
|
|
49
|
+
export declare function isValidExtraction(text: string): boolean;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "faf-cli",
|
|
3
|
-
"version": "6.
|
|
4
|
-
"description": "Persistent AI Context Standard — project DNA for AI. IANA-registered. Anthropic-
|
|
3
|
+
"version": "6.12.0",
|
|
4
|
+
"description": "Persistent AI Context Standard — project DNA for AI. IANA-registered. Anthropic-merged.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"icon": "https://faf.one/orange-smiley.svg",
|
|
7
7
|
"logo": "https://faf.one/orange-smiley.svg",
|
|
@@ -25,7 +25,8 @@
|
|
|
25
25
|
],
|
|
26
26
|
"scripts": {
|
|
27
27
|
"dev": "bun src/cli.ts",
|
|
28
|
-
"
|
|
28
|
+
"clean": "rm -rf dist",
|
|
29
|
+
"build": "bun run clean && bun build src/cli.ts --outdir dist --target=node --minify --sourcemap=linked --external faf-scoring-kernel --external open && bun build src/index.ts --outdir dist --target=node --minify --sourcemap=linked --external faf-scoring-kernel --external open && tsc -p tsconfig.build.json",
|
|
29
30
|
"compile": "bun build src/cli.ts --compile --bytecode --minify --outfile faf",
|
|
30
31
|
"compile:all": "bun build src/cli.ts --compile --bytecode --minify --target=bun-darwin-arm64 --outfile faf-darwin-arm64 && bun build src/cli.ts --compile --bytecode --minify --target=bun-darwin-x64 --outfile faf-darwin-x64 && bun build src/cli.ts --compile --bytecode --minify --target=bun-linux-x64 --outfile faf-linux-x64 && bun build src/cli.ts --compile --bytecode --minify --target=bun-windows-x64 --outfile faf-windows-x64.exe",
|
|
31
32
|
"test": "bun test --timeout=120000",
|
package/project.faf
CHANGED
|
@@ -26,7 +26,7 @@ stack:
|
|
|
26
26
|
storage: slotignored
|
|
27
27
|
human_context:
|
|
28
28
|
who: Developers and teams using AI coding assistants
|
|
29
|
-
what: Persistent AI Context Standard — project DNA for AI. IANA-registered. Anthropic-
|
|
29
|
+
what: Persistent AI Context Standard — project DNA for AI. IANA-registered. Anthropic-merged.
|
|
30
30
|
why: Eliminates 91% context re-discovery tax — define once, AI remembers forever
|
|
31
31
|
where: npm registry, Homebrew, GitHub
|
|
32
32
|
when: Production since September 2025, v6.0 March 2026
|