faf-cli 6.8.0 → 6.10.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.
@@ -0,0 +1,104 @@
1
+ /**
2
+ * faf bench — the AI-grounding benchmark (P1, agent-native).
3
+ *
4
+ * Measures the thing FAF sells, falsifiably: AI works better and faster WITH
5
+ * structured context. Two numbers, one harness — grounding ACCURACY (N
6
+ * project questions, cold vs with-faf) and grounding COST (tokens to get
7
+ * grounded, as reported by the runner).
8
+ *
9
+ * The unfair advantage: the .faf IS the answer key. Questions derive from the
10
+ * populated active slots; grading is mechanical (normalize + versioned alias
11
+ * map + significant-token containment — no judge, no rubric drift). The
12
+ * question-set hash rides a ✪ receipt, parity-hash discipline.
13
+ *
14
+ * DOCTRINE (wolfejam 2026-06-11):
15
+ * - "A low score is an ALARM BELL — you are hemorrhaging tokens and the AI
16
+ * is pretty much clueless about what you are doing, even trying to do."
17
+ * Headline: "Trouble ahead, expensive trouble."
18
+ * - NEVER render a low score alone — always the pair (cold → with-faf), or
19
+ * the alarm framing with headroom. The delta is the product; the cold
20
+ * number belongs to the ABSENCE of context ("without context"), never to
21
+ * FAF. Output always ends in a prescription, never a verdict.
22
+ * - The 6Ws are UNDERIVABLE from code: cold exploration can dig out a stack
23
+ * (you pay for the dig); it can never find intent.
24
+ */
25
+ /** Bump when phrasing, aliases, or grading rules change — part of the qset hash. */
26
+ export declare const BENCH_VERSION = "faf-bench/1";
27
+ export interface BenchQuestion {
28
+ n: number;
29
+ path: string;
30
+ question: string;
31
+ }
32
+ /**
33
+ * Versioned alias groups — mechanical equivalences, no fuzzy scoring.
34
+ * Members are compared post-normalization. Extend deliberately; every change
35
+ * bumps the qset hash via BENCH_VERSION.
36
+ */
37
+ export declare const ALIAS_GROUPS: string[][];
38
+ export declare function normalizeAnswer(s: string): string;
39
+ /**
40
+ * Mechanical match: exact normalized equality, alias-group equality, or
41
+ * every significant token of the EXPECTED value present in the answer
42
+ * (deterministic set containment — handles sentence-shaped 6W answers
43
+ * without a judge). A miss is a miss.
44
+ */
45
+ export declare function answersMatch(expected: string, given: string): boolean;
46
+ export interface QuestionSet {
47
+ version: string;
48
+ qsetHash: string;
49
+ questions: BenchQuestion[];
50
+ /** Internal answer key — NEVER printed by `questions`; used by `grade`. */
51
+ answers: Record<number, string>;
52
+ }
53
+ /** Derive the question set + answer key from a project.faf (active populated slots only). */
54
+ export declare function deriveQuestionSet(yaml: string): QuestionSet;
55
+ /**
56
+ * The answer-key-safe projection of a QuestionSet — version + qsetHash +
57
+ * questions, NEVER `answers`. Any "give me the questions" surface (MCP tools,
58
+ * UIs) MUST hand out THIS, not the raw QuestionSet: a tool that prints the
59
+ * answer key makes the benchmark a lie. The CLI's `bench questions` follows
60
+ * the same rule.
61
+ */
62
+ export declare function publicQuestions(qset: QuestionSet): {
63
+ version: string;
64
+ qsetHash: string;
65
+ questions: BenchQuestion[];
66
+ };
67
+ export interface GradeResult {
68
+ correct: number;
69
+ total: number;
70
+ misses: BenchQuestion[];
71
+ perQuestion: {
72
+ n: number;
73
+ path: string;
74
+ ok: boolean;
75
+ }[];
76
+ }
77
+ export declare function gradeAnswers(qset: QuestionSet, given: Record<string, string>): GradeResult;
78
+ export interface RunRecord {
79
+ score: number;
80
+ total: number;
81
+ tokens?: number;
82
+ model?: string;
83
+ }
84
+ export interface BenchState {
85
+ version: string;
86
+ qsetHash: string;
87
+ protocol: 'in-session';
88
+ cold?: RunRecord;
89
+ faf?: RunRecord;
90
+ }
91
+ /** ✪ receipt — sha256 over the canonical projection; third-party verifiable. */
92
+ export declare function buildReceipt(state: BenchState): {
93
+ projection: string;
94
+ hash: string;
95
+ };
96
+ export interface BenchOptions {
97
+ json?: boolean;
98
+ cold?: boolean;
99
+ faf?: boolean;
100
+ tokens?: string;
101
+ model?: string;
102
+ file?: string;
103
+ }
104
+ export declare function benchCommand(action?: string, answersFile?: string, options?: BenchOptions): void;
@@ -0,0 +1,72 @@
1
+ /**
2
+ * The 6Ws Interview — SINGLE SOURCE.
3
+ *
4
+ * The canonical question registry for filling a .faf by asking a human (or an
5
+ * agent asking a human). faf-cli OWNS it; consumers (claude-faf-mcp's faf_go,
6
+ * siblings, UIs) IMPORT it — never reimplement, never copy. Decided
7
+ * 2026-06-10: the CLI exports, CFM consumes, zero drift. (CFM's previous
8
+ * hand-maintained registry had already drifted — e.g. its `how` asked "How
9
+ * should AI assist?" while the canonical slot semantic is "How is it built /
10
+ * used?". This file is the arbiter; slots.ts is its spine.)
11
+ *
12
+ * Shape carries what interactive consumers need: question text, a short
13
+ * header chip, input type, required flag, select options where a closed-ish
14
+ * vocabulary exists. Question SEMANTICS align to core/slots.ts descriptions —
15
+ * the slot is the meaning, the question is just its interview voice.
16
+ *
17
+ * Authoring doctrine baked into the prompts: 6Ws answers are terse LABELS
18
+ * (3-4 words, hard cap <6) — a scannable spec card, not prose.
19
+ */
20
+ /** Bump when questions/options change — consumers can pin or report it. */
21
+ export declare const INTERVIEW_VERSION = "faf-interview/1";
22
+ export interface InterviewOption {
23
+ label: string;
24
+ value: string;
25
+ description: string;
26
+ }
27
+ export interface InterviewQuestion {
28
+ /** Canonical slot path (core/slots.ts is the spine). */
29
+ path: string;
30
+ question: string;
31
+ /** Short chip/header label (max ~12 chars) for option-UI consumers. */
32
+ header: string;
33
+ type: 'text' | 'select';
34
+ required: boolean;
35
+ options?: InterviewOption[];
36
+ }
37
+ /**
38
+ * THE 8-Q 6Ws INTERVIEW — the core. Project identity (name + goal) plus the
39
+ * six Ws. Language is deliberately NOT here: detection finds it; humans are
40
+ * only asked what machines cannot derive. The 6Ws are the underivable half.
41
+ */
42
+ export declare const SIX_WS_INTERVIEW: InterviewQuestion[];
43
+ /**
44
+ * Stack interview — asked only for slots ACTIVE for the app_type and still
45
+ * empty. Selects where a common vocabulary exists; every select includes
46
+ * Other (specify) and None where absence is legitimate.
47
+ */
48
+ export declare const STACK_INTERVIEW: InterviewQuestion[];
49
+ /** The full ordered registry: the 8-Q core first, then stack. */
50
+ export declare const INTERVIEW: InterviewQuestion[];
51
+ /** Lookup by slot path. */
52
+ export declare const INTERVIEW_BY_PATH: Map<string, InterviewQuestion>;
53
+ /**
54
+ * Plain-object companion to INTERVIEW_BY_PATH. A Map JSON-serializes to `{}`,
55
+ * which reads as "the export shipped empty" to any consumer that crosses a
56
+ * serialization boundary (caught by the CFM compose handoff, 2026-06-12).
57
+ * Bridge consumers that serialize should use THIS; in-process consumers can
58
+ * use either.
59
+ */
60
+ export declare const INTERVIEW_PATHS: Record<string, InterviewQuestion>;
61
+ /**
62
+ * Interview voice for ANY slot: the registry question when one exists,
63
+ * otherwise derived from the slot's canonical description — so slot-driven
64
+ * flows (faf go) and registry-driven flows (faf_go) speak the same language.
65
+ */
66
+ export declare function questionForSlot(path: string): string;
67
+ /**
68
+ * The questions still worth asking for a given .faf: registry order, empty
69
+ * (or placeholder) slots only, slotignored skipped — the What-Not is never
70
+ * interviewed. `data` is the parsed .faf object.
71
+ */
72
+ export declare function interviewForMissing(data: Record<string, unknown>, isEmpty: (value: unknown) => boolean): InterviewQuestion[];
@@ -28,9 +28,10 @@ export declare const ENTERPRISE_SLOTS: SlotDef[];
28
28
  export declare const PLACEHOLDERS: Set<string>;
29
29
  /** Check if a value is a placeholder (empty) */
30
30
  export declare function isPlaceholder(value: unknown): boolean;
31
- /** App-type to active category mapping. The canonical 22-type ladder —
32
- * 20 detectable apps + `about` (non-app, 0 slots, owner-attested)
33
- * + `encyclopedia` (curated knowledge surface, FAFipedia and similar).
31
+ /** App-type to active category mapping. The canonical 24-type ladder —
32
+ * 21 detectable apps + `about` (non-app, 0 slots, owner-attested)
33
+ * + `encyclopedia` (curated knowledge surface, FAFipedia and similar)
34
+ * + `intent` (human-intent seed from builder.faf.one, not auto-detected).
34
35
  * (See v6.6.md doctrine memory + fafipedia-vs-grokipedia-architecture.)
35
36
  * Sorted ascending by active slot count for readability.
36
37
  *
@@ -0,0 +1,104 @@
1
+ /**
2
+ * 🔺 TURBO-CAT™ FORMAT PYRAMID v3.1.1
3
+ *
4
+ * ROW 20 IN PROGRESS - 199 FORMATS CATALOGUED!
5
+ *
6
+ * Like precious metals in a catalytic converter - each format triggers
7
+ * a specific intelligence reaction. We CAN catalyze ANY format, but only
8
+ * the WORTHY earn a pyramid stone!
9
+ *
10
+ * Pyramid Evolution:
11
+ * ├── Row 17: Sum(1..17) + 1 = 154 (THE SACRED - ACHIEVED 2024)
12
+ * ├── Row 18: Sum(1..18) + 1 = 172 (BEYOND SACRED - ACHIEVED)
13
+ * ├── Row 19: Sum(1..19) + 1 = 190 (ACHIEVED 2025-12-17)
14
+ * └── Row 20: Sum(1..20) + 1 = 211 (IN PROGRESS - 9/21 added)
15
+ *
16
+ * Row 20 Tier 1 Added (2025-12-17):
17
+ * - build.zig.zon (Zig packages)
18
+ * - gleam.toml (Gleam - 2nd most admired 2025)
19
+ * - bunfig.toml (Bun config)
20
+ * - mise.toml / .mise.toml (mise version manager)
21
+ * - manifest.toml (Flox/Nix environments)
22
+ * - justfile (Just command runner - 10k+ stars)
23
+ * - .pre-commit-config.yaml (Git hooks framework)
24
+ * - CLAUDE.md (Claude Code AI context)
25
+ *
26
+ * THE DOCTRINE: To add a new format, PROVE it's better than existing ones!
27
+ *
28
+ * 😽 We are the FORMAT FREAKS - Quality over Quantity!
29
+ * 🏆 199 formats - CHAMPIONSHIP GRADE FORMAT DETECTION
30
+ */
31
+ export interface FormatKnowledge {
32
+ frameworks: string[];
33
+ slots: Partial<ContextSlots>;
34
+ priority: number;
35
+ intelligence: 'ultra-high' | 'high' | 'medium' | 'low';
36
+ confirmWith?: string[];
37
+ }
38
+ export interface ContextSlots {
39
+ framework: string;
40
+ mainLanguage: string;
41
+ buildTool: string;
42
+ packageManager: string;
43
+ hosting: string;
44
+ backend: string;
45
+ apiType: string;
46
+ cicd: string;
47
+ database: string;
48
+ cssFramework: string;
49
+ uiLibrary: string;
50
+ stateManagement: string;
51
+ server: string;
52
+ connection: string;
53
+ runtime: string;
54
+ build: string;
55
+ main_language: string;
56
+ build_tool: string;
57
+ pkg_manager: string;
58
+ api: string;
59
+ css: string;
60
+ ui_library: string;
61
+ state: string;
62
+ db: string;
63
+ targetUser: string;
64
+ coreProblem: string;
65
+ missionPurpose: string;
66
+ deploymentMarket: string;
67
+ timeline: string;
68
+ approach: string;
69
+ }
70
+ /**
71
+ * THE KNOWLEDGE BASE
72
+ *
73
+ * Add any file format here and map it to:
74
+ * 1. Frameworks it indicates
75
+ * 2. Context slots it can fill
76
+ * 3. Priority/intelligence score
77
+ *
78
+ * The fab-formats engine will automatically use this knowledge!
79
+ */
80
+ export declare const KNOWLEDGE_BASE: Record<string, FormatKnowledge>;
81
+ /**
82
+ * Get knowledge for a specific format
83
+ */
84
+ export declare function getFormatKnowledge(format: string): FormatKnowledge | undefined;
85
+ /**
86
+ * Get all format keys
87
+ */
88
+ export declare function getAllFormats(): string[];
89
+ /**
90
+ * Get high-value formats (priority >= 30)
91
+ */
92
+ export declare function getHighValueFormats(): string[];
93
+ /**
94
+ * Get formats by intelligence level
95
+ */
96
+ export declare function getFormatsByIntelligence(level: FormatKnowledge['intelligence']): string[];
97
+ /**
98
+ * Get formats that can fill a specific slot
99
+ */
100
+ export declare function getFormatsForSlot(slotName: keyof ContextSlots): string[];
101
+ /**
102
+ * Calculate total possible intelligence score
103
+ */
104
+ export declare function getMaxIntelligenceScore(): number;
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Turbo-Cat™ (= Format-finder) — fills .faf slots from file-format evidence
3
+ * across ~200 formats (the KNOWLEDGE_BASE). Restores the multi-ecosystem
4
+ * manifest interrogation the v6.0 rewrite narrowed to README+Cargo+package.json.
5
+ *
6
+ * v6-native sync port (the v5 engine was async; sync integrates cleanly with
7
+ * `auto`). Two-layer: (1A) config files walking up to the monorepo .git
8
+ * boundary, (1B) source extensions; priority-wins slot recommendations.
9
+ *
10
+ * Used as the LOWEST-precedence filler in `auto`: v6's specific detection wins;
11
+ * Turbo-Cat fills only the slots still empty (esp. non-npm stacks).
12
+ */
13
+ /** A format actually discovered on disk (config-file layer; real files only). */
14
+ export interface DiscoveredFormat {
15
+ fileName: string;
16
+ /** Derived from the entry's primary slot family (deterministic — real
17
+ * knowledge surfaced, not invented): package-manager, language, framework,
18
+ * backend, database, ci-cd, hosting, build, … */
19
+ category: string;
20
+ priority: number;
21
+ }
22
+ export interface TurboCatResult {
23
+ slotFills: Record<string, string>;
24
+ frameworks: string[];
25
+ confirmedCount: number;
26
+ /** Option B (compose spec 2026-06-12): the per-format breakdown consumers
27
+ * display — surfaced so MCPs can DELETE their local format maps entirely. */
28
+ discoveredFormats: DiscoveredFormat[];
29
+ /** Deterministic lowercase signature, e.g. "typescript-react" ('unknown-stack' when bare). */
30
+ stackSignature: string;
31
+ }
32
+ /** Scan formats → priority-wins slot recommendations. */
33
+ export declare function turboCatScan(projectDir: string): TurboCatResult;
34
+ /** Turbo-Cat slot fills shaped as a v6 .faf partial ({ project, stack }) for fillEmpties. */
35
+ export declare function turboCatSlots(projectDir: string): {
36
+ project?: Record<string, string>;
37
+ stack?: Record<string, string>;
38
+ };
package/dist/index.d.ts CHANGED
@@ -6,4 +6,10 @@ export { enrichScore, scoreFafYaml } from './core/scorer.js';
6
6
  export { validateFaf } from './core/schema.js';
7
7
  export { findFafFile, readFaf, readFafRaw } from './interop/faf.js';
8
8
  export { generateProjectHtml, writeProjectHtml } from './interop/projecthtml.js';
9
+ export type { InterviewQuestion, InterviewOption } from './core/interview.js';
10
+ export { INTERVIEW, SIX_WS_INTERVIEW, STACK_INTERVIEW, INTERVIEW_BY_PATH, INTERVIEW_PATHS, INTERVIEW_VERSION, questionForSlot, interviewForMissing, } from './core/interview.js';
11
+ export { BENCH_VERSION, deriveQuestionSet, publicQuestions, gradeAnswers, buildReceipt, normalizeAnswer, answersMatch, ALIAS_GROUPS, } from './commands/bench.js';
12
+ export type { BenchQuestion, QuestionSet, GradeResult, BenchState, RunRecord } from './commands/bench.js';
13
+ export { turboCatScan, turboCatSlots } from './detect/turbo-cat.js';
14
+ export type { TurboCatResult, DiscoveredFormat } from './detect/turbo-cat.js';
9
15
  export * as kernel from './wasm/kernel.js';