faf-cli 7.12.1 → 7.13.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 +39 -32
- package/dist/cli.js +364 -337
- package/dist/cli.js.map +70 -59
- package/dist/core/cwd-guard.d.ts +8 -0
- package/dist/core/faf-dna.d.ts +147 -0
- package/dist/core/faf-source.d.ts +27 -0
- package/dist/core/json-edit.d.ts +77 -0
- package/dist/core/render-hash.d.ts +55 -0
- package/dist/core/safe-write.d.ts +228 -0
- package/dist/core/scorer.d.ts +7 -2
- package/dist/core/shape.d.ts +11 -0
- package/dist/core/slots.d.ts +30 -0
- package/dist/core/typed-none.d.ts +25 -0
- package/dist/core/types.d.ts +8 -0
- package/dist/core/yaml-edit.d.ts +136 -0
- package/dist/detect/assemble.d.ts +49 -5
- package/dist/detect/git-repo.d.ts +50 -0
- package/dist/detect/scanner.d.ts +9 -4
- package/dist/detect/stack.d.ts +14 -1
- package/dist/detect/turbo-cat-knowledge.d.ts +10 -0
- package/dist/detect/turbo-cat.d.ts +6 -2
- package/dist/fafm/index.d.ts +1 -0
- package/dist/fafm/soul.d.ts +108 -6
- package/dist/index.d.ts +24 -3
- package/dist/index.js +203 -163
- package/dist/index.js.map +35 -23
- package/dist/interop/cards.d.ts +52 -3
- package/dist/interop/claude-memory.d.ts +134 -0
- package/dist/interop/claude.d.ts +5 -1
- package/dist/interop/commonmark.d.ts +144 -0
- package/dist/interop/copilot-instructions.d.ts +1 -0
- package/dist/interop/faf.d.ts +123 -11
- package/dist/interop/inject.d.ts +112 -22
- package/dist/interop/projecthtml.d.ts +19 -2
- package/dist/interop/servercard.d.ts +51 -2
- package/dist/wasm/kernel.d.ts +7 -4
- package/package.json +5 -4
- package/project.faf +11 -4
package/dist/interop/inject.d.ts
CHANGED
|
@@ -6,40 +6,130 @@
|
|
|
6
6
|
*/
|
|
7
7
|
export declare const FAF_START = "<!-- faf:start -->";
|
|
8
8
|
export declare const FAF_END = "<!-- faf:end -->";
|
|
9
|
+
/** Split into lines keeping each line's own terminator (\r\n, \r or \n). */
|
|
10
|
+
export declare function linesWithEnds(text: string): string[];
|
|
11
|
+
export declare const stripEnd: (line: string) => string;
|
|
9
12
|
/**
|
|
10
|
-
*
|
|
11
|
-
* first END
|
|
12
|
-
*
|
|
13
|
-
*
|
|
13
|
+
* faf's marked range in `text`: in each reading, the last START line before
|
|
14
|
+
* the first END line after it (the innermost pair — text between an earlier
|
|
15
|
+
* START and the last one is kept), both outside every Markdown region. The
|
|
16
|
+
* two readings must find the same pair; otherwise there is no range. Returns
|
|
17
|
+
* the char range covering both lines (the END line's terminator excluded), or
|
|
18
|
+
* null. A leading BOM on line 1 stays outside the range. `isStart` and
|
|
19
|
+
* `isEnd` are asked about each line shown as text in at least one reading,
|
|
20
|
+
* once, in order, on the line with its terminator and that BOM removed. Past
|
|
21
|
+
* MAX_OPEN_CONTAINERS open containers the CommonMark reading stops, and there
|
|
22
|
+
* is no range (a pair found before that point still counts). Time grows with
|
|
23
|
+
* the text's length, not with how deep it nests.
|
|
24
|
+
*/
|
|
25
|
+
export declare function findMarkedRange(text: string, isStart: (line: string) => boolean, isEnd: (line: string) => boolean): {
|
|
26
|
+
start: number;
|
|
27
|
+
end: number;
|
|
28
|
+
} | null;
|
|
29
|
+
/**
|
|
30
|
+
* Locate the faf-managed block in `text`: a START marker line and the first
|
|
31
|
+
* END marker line after it, both outside fenced and indented code, raw HTML
|
|
32
|
+
* blocks and multi-line HTML comments, under both readings (see
|
|
33
|
+
* {@link findMarkedRange}). Returns the char range covering both marker lines
|
|
34
|
+
* (terminator of the END line excluded), or null when there is no complete
|
|
35
|
+
* block both readings agree on.
|
|
14
36
|
*
|
|
15
|
-
* Markers are matched as WHOLE LINES at column 0
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
37
|
+
* Markers are matched as WHOLE LINES at column 0 — exactly the marker text,
|
|
38
|
+
* nothing after it — never as substrings. Substring search was a real bug
|
|
39
|
+
* (7.1.4–7.11.0): renderAgentsMd quoted the marker tokens in its own
|
|
40
|
+
* blockquote, so on every re-run `indexOf(end)` hit the quote, cut the old
|
|
41
|
+
* block in half and appended its stale tail below the new block — `faf
|
|
42
|
+
* export --agents` grew AGENTS.md by ~49 lines per run. The same happened to
|
|
43
|
+
* users who documented the markers in a code fence above the block.
|
|
21
44
|
*
|
|
22
|
-
* Two
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
45
|
+
* Two START lines before an END: the pair is the last START and that END.
|
|
46
|
+
* faf's own body never holds a column-0 START (see {@link wrapFafBlock}), so
|
|
47
|
+
* the text between the two STARTs is the user's and stays. A START with no
|
|
48
|
+
* END is not a block: the caller treats "no block" as a user file and
|
|
49
|
+
* prefixes — it never reclaims. faf's own block is always found again where
|
|
50
|
+
* faf put it (see {@link placeFafBlock}).
|
|
28
51
|
*/
|
|
29
52
|
export declare function findFafBlock(text: string, start?: string, end?: string): {
|
|
30
53
|
start: number;
|
|
31
54
|
end: number;
|
|
32
55
|
} | null;
|
|
56
|
+
/** No line of faf's own body leaves more block quotes, lists and list items
|
|
57
|
+
* open than this (as CommonMark reads it) — far below the reader's cap,
|
|
58
|
+
* MAX_OPEN_CONTAINERS, so the next run always reads the block to its end,
|
|
59
|
+
* quoted (one quote more) or not. See {@link capDepth}. */
|
|
60
|
+
export declare const BODY_MAX_CONTAINERS = 16;
|
|
61
|
+
/**
|
|
62
|
+
* `head`, faf's block (`wrapped`, from {@link wrapFafBlock}) and `tail` as one
|
|
63
|
+
* text in which {@link findFafBlock} finds the block exactly where it was put
|
|
64
|
+
* — so the next write updates it in place and never stacks a second one. A
|
|
65
|
+
* body that would not be found there (it leaves a region open in only one
|
|
66
|
+
* reading, or the text before it holds a raw HTML block the body continues)
|
|
67
|
+
* is quoted line by line instead. If even that is not found, it throws a
|
|
68
|
+
* SafePathError (`unplaceable`) naming `path` (the file being written):
|
|
69
|
+
* "<file>: faf could not place its block where the next run finds it again —
|
|
70
|
+
* faf left it unchanged" — one line, and nothing is written.
|
|
71
|
+
*/
|
|
72
|
+
export declare function placeFafBlock(head: string, wrapped: string, tail: string, start?: string, end?: string, path?: string): string;
|
|
73
|
+
/** The managed block: START, the body (guarded — see guardBody), END. */
|
|
74
|
+
export declare function wrapFafBlock(block: string, start?: string, end?: string): string;
|
|
75
|
+
/** The file's new text: `existing` (null when there is no file) with `wrapped`
|
|
76
|
+
* as its managed block, placed where the next scan finds it again (see
|
|
77
|
+
* {@link placeFafBlock}; `path` names the file in its refusal). */
|
|
78
|
+
export declare function withFafBlock(existing: string | null, wrapped: string, start?: string, end?: string, path?: string): string;
|
|
79
|
+
/** Read a resolved path, or null when nothing is there yet. Only ENOENT reads
|
|
80
|
+
* as "no file"; any other error is thrown, so a file faf could not read is
|
|
81
|
+
* never treated as empty and written fresh. The text is decoded strictly: a
|
|
82
|
+
* file that is not UTF-8 (a UTF-16 file, cp1252 bytes) is refused
|
|
83
|
+
* (SafePathError `not-utf8`) and left as it is — see readUtf8. */
|
|
84
|
+
export declare function readIfPresent(path: string): string | null;
|
|
85
|
+
export interface InjectOptions {
|
|
86
|
+
/** The project folder the file must stay inside. Default: the file's own
|
|
87
|
+
* folder. Pass it when the file sits in a subfolder (`.github/…`), so a
|
|
88
|
+
* linked subfolder cannot carry the write out of the project. */
|
|
89
|
+
root?: string;
|
|
90
|
+
}
|
|
33
91
|
/**
|
|
34
92
|
* Non-destructively write a faf-managed block into a file.
|
|
35
93
|
*
|
|
36
94
|
* - file does not exist → create it containing just the block
|
|
37
95
|
* - file has the markers → replace ONLY the content between them (update in place)
|
|
38
|
-
* -
|
|
39
|
-
*
|
|
96
|
+
* - anything else → PREFIX the block; everything already there is preserved
|
|
97
|
+
* (a leading BOM stays at byte 0)
|
|
98
|
+
*
|
|
99
|
+
* faf replaces only text it can prove it wrote: what sits between its own
|
|
100
|
+
* marker lines. A file with no marker lines is never reclaimed, whatever it
|
|
101
|
+
* starts or ends with. Idempotent: re-running updates the managed block in
|
|
102
|
+
* place and never duplicates it. Enhance, never replace.
|
|
40
103
|
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
104
|
+
* The file is resolved inside its project first: a link that leads outside,
|
|
105
|
+
* a dangling link, a link to a file with another name (CLAUDE.md → README.md)
|
|
106
|
+
* and anything in `.git` are refused (SafePathError) and nothing is written; a
|
|
107
|
+
* link to a file of the same name, or between AI context files (CLAUDE.md →
|
|
108
|
+
* AGENTS.md), is written through and stays a link. A file that is not UTF-8
|
|
109
|
+
* is refused and left as it is. The write is atomic — a failure leaves the
|
|
110
|
+
* original exactly as it was — and is refused if the file changed on disk
|
|
111
|
+
* after faf read it. A block faf cannot place where its next run finds it
|
|
112
|
+
* again is refused too (SafePathError `unplaceable`), and nothing is written.
|
|
113
|
+
*/
|
|
114
|
+
export declare function injectFafBlock(path: string, block: string, start?: string, end?: string, opts?: InjectOptions): void;
|
|
115
|
+
/**
|
|
116
|
+
* The one line the CLI prints when faf's block goes on top of a file that has
|
|
117
|
+
* no block of its own but holds older faf text — or null:
|
|
118
|
+
* - the file's first line is faf's old metastamp (`<!-- faf: … -->`): faf
|
|
119
|
+
* never reclaims such a file (it cannot prove it wrote the text), so the
|
|
120
|
+
* old faf text stays below the new block;
|
|
121
|
+
* - a whole-line START marker sits inside a code fence, a raw HTML block
|
|
122
|
+
* or an HTML comment (in either reading — see {@link findFafBlock}): the
|
|
123
|
+
* older block there is an example to faf, and stays below the new one;
|
|
124
|
+
* - a whole-line START marker sits past more than MAX_OPEN_CONTAINERS
|
|
125
|
+
* nested lists or quotes: faf did not read that far, and the older block
|
|
126
|
+
* stays below the new one.
|
|
127
|
+
* `label` names the file (`CLAUDE.md`). `existing` is the file's text before
|
|
128
|
+
* the write (null when there was none). faf-mcp and claude-faf-mcp print the
|
|
129
|
+
* same line through this export.
|
|
44
130
|
*/
|
|
45
|
-
export declare function
|
|
131
|
+
export declare function legacyStampNote(label: string, existing: string | null, start?: string, end?: string): string | null;
|
|
132
|
+
/** {@link legacyStampNote} for the file at `path`, read the way
|
|
133
|
+
* injectFafBlock reads it — before the write. Null when there is no note, or
|
|
134
|
+
* when the file cannot be read (the write itself then says why). */
|
|
135
|
+
export declare function legacyStampNoteAt(path: string, label: string, start?: string, end?: string, opts?: InjectOptions): string | null;
|
|
@@ -1,7 +1,24 @@
|
|
|
1
1
|
import type { FafData, ScoreResult } from '../core/types.js';
|
|
2
2
|
/** Render project.faf data + its score into a self-contained HTML string. */
|
|
3
3
|
export declare function renderProjectHtml(data: FafData, result: ScoreResult, fafPath?: string): string;
|
|
4
|
-
/**
|
|
5
|
-
|
|
4
|
+
/** True when `bytes` are a project.html faf rendered: a line that is faf's
|
|
5
|
+
* description meta (every faf release has written it). */
|
|
6
|
+
export declare function hasProjectHtmlMark(bytes: Uint8Array): boolean;
|
|
7
|
+
/** Options for {@link writeProjectHtml}. */
|
|
8
|
+
export interface ProjectHtmlWriteOptions {
|
|
9
|
+
/** Replace a project.html faf cannot prove it wrote — no faf mark, edited
|
|
10
|
+
* since faf wrote it, or from before 7.13 — the explicit overwrite
|
|
11
|
+
* (`--force`). Default: it is refused and left as it is. */
|
|
12
|
+
force?: boolean;
|
|
13
|
+
}
|
|
14
|
+
/** Write project.html beside project.faf (repo root) — atomically, and never
|
|
15
|
+
* through a link that leaves `dir` or dangles (SafePathError). The page
|
|
16
|
+
* carries faf's render hash (`<meta name="faf-render" content="sha256:…">`,
|
|
17
|
+
* the hash of the page without that line). A project.html already there is
|
|
18
|
+
* replaced only when it is byte for byte what faf last wrote (its hash still
|
|
19
|
+
* fits); a page edited since, a hand-written page, or a page from before 7.13
|
|
20
|
+
* that is not exactly faf's render of `data` is refused (SafePathError
|
|
21
|
+
* `not-owned`) and left byte for byte, unless `force`. */
|
|
22
|
+
export declare function writeProjectHtml(dir: string, data: FafData, result: ScoreResult, fafPath?: string, write?: ProjectHtmlWriteOptions): void;
|
|
6
23
|
/** @deprecated Use {@link renderProjectHtml}. Removed in the next major. */
|
|
7
24
|
export declare const generateProjectHtml: typeof renderProjectHtml;
|
|
@@ -63,10 +63,59 @@ export declare function registryName(data: FafData): string;
|
|
|
63
63
|
* omitted rather than shipped invalid — GitHub's registry then derives a name
|
|
64
64
|
* from the namespace, so set `project.title` to control the display. */
|
|
65
65
|
export declare function registryTitle(data: FafData): string | undefined;
|
|
66
|
+
/** True when `bytes` are a Server Card faf wrote: JSON carrying the FAF
|
|
67
|
+
* context-block at `_meta["one.faf/context"]`, as every card faf has
|
|
68
|
+
* written does. */
|
|
69
|
+
export declare function hasServerCardMark(bytes: Uint8Array): boolean;
|
|
70
|
+
/** True when `bytes` are a registry `server.json` carrying faf's identity:
|
|
71
|
+
* `_meta[REGISTRY_PUBLISHER_KEY]["one.faf/context"]`. */
|
|
72
|
+
export declare function hasRegistryMark(bytes: Uint8Array): boolean;
|
|
73
|
+
/** Options for the card writers ({@link writeServerCard}, `faf cards`). */
|
|
74
|
+
export interface CardWriteOptions {
|
|
75
|
+
/** Replace a card faf cannot prove it wrote — no faf mark, edited since
|
|
76
|
+
* faf wrote it, or from before 7.13 — the explicit overwrite (`--force`).
|
|
77
|
+
* Default: such a file is refused and left as it is. */
|
|
78
|
+
force?: boolean;
|
|
79
|
+
}
|
|
66
80
|
/** Write the Server Card to a `server-card` file. Returns the path.
|
|
67
81
|
* Per experimental-ext-server-card#22 the reserved location is
|
|
68
82
|
* `<streamable-http-url>/server-card` (no longer `.well-known`); serve the
|
|
69
|
-
* emitted file there as `application/mcp-server-card+json`.
|
|
70
|
-
|
|
83
|
+
* emitted file there as `application/mcp-server-card+json`.
|
|
84
|
+
*
|
|
85
|
+
* The card carries faf's render hash at `_meta["one.faf/render"]` (the hash
|
|
86
|
+
* of the card without that key; the Server Card schema leaves `_meta` open
|
|
87
|
+
* for namespaced keys). A `server-card` already there is replaced only when
|
|
88
|
+
* it is byte for byte what faf last wrote (its hash still fits); a card edited
|
|
89
|
+
* since, a hand-written card, or a card from before 7.13 that is not exactly
|
|
90
|
+
* faf's render of `data` is refused (SafePathError `not-owned`) and left byte
|
|
91
|
+
* for byte, unless `force`. The write is atomic and never goes through a link
|
|
92
|
+
* that leaves `dir` or dangles. */
|
|
93
|
+
export declare function writeServerCard(dir: string, data: FafData, opts?: ServerCardOptions, write?: CardWriteOptions): string;
|
|
94
|
+
/** The identity faf owns in a registry `server.json`. */
|
|
95
|
+
export interface ServerJsonIdentity {
|
|
96
|
+
/** The reverse-DNS registry name ({@link registryName}). */
|
|
97
|
+
name: string;
|
|
98
|
+
/** The display title ({@link registryTitle}); when undefined the file's own title is kept. */
|
|
99
|
+
title?: string;
|
|
100
|
+
/** A version to set (`--set-version`); when undefined the file's own is kept. */
|
|
101
|
+
version?: string;
|
|
102
|
+
/** The `_meta` faf writes ({@link registryMeta}). */
|
|
103
|
+
meta: Record<string, unknown>;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Put faf's identity into the text of a registry `server.json`, changing
|
|
107
|
+
* nothing else: the `name` value, the `title` (only when faf has one — the
|
|
108
|
+
* file's own title is kept otherwise), a `version` asked for, and the keys of
|
|
109
|
+
* faf's context-block under `_meta[REGISTRY_PUBLISHER_KEY]["one.faf/context"]`.
|
|
110
|
+
* Each is a text edit of that one value, or the key added when missing; no
|
|
111
|
+
* field is deleted, and every other byte — key order, a 20-digit number, an
|
|
112
|
+
* array on one line, spacing, CRLF — stays as it was. Throws a JsonEditError,
|
|
113
|
+
* changing nothing, when the text cannot be edited that way (not valid JSON,
|
|
114
|
+
* not an object, a key repeated on the way, a `_meta` that is not an object).
|
|
115
|
+
*/
|
|
116
|
+
export declare function patchServerJson(text: string, identity: ServerJsonIdentity): {
|
|
117
|
+
text: string;
|
|
118
|
+
changed: boolean;
|
|
119
|
+
};
|
|
71
120
|
/** @deprecated Use {@link buildServerCard}. Removed in the next major. */
|
|
72
121
|
export declare const generateServerCard: typeof buildServerCard;
|
package/dist/wasm/kernel.d.ts
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import type { KernelScoreResult, FafbInfo } from '../core/types.js';
|
|
2
|
-
/** Score a .faf YAML string (21 base slots)
|
|
2
|
+
/** Score a .faf YAML string (21 base slots), as the file is. A typed None /
|
|
3
|
+
* N/A / not applicable at a slot is an empty slot, as in every engine. A
|
|
4
|
+
* leading BOM is not scored (`faf auto` and `faf score` read a BOM file). */
|
|
3
5
|
export declare function score(yaml: string): KernelScoreResult;
|
|
4
|
-
/** Score a .faf YAML string (33 enterprise slots) */
|
|
6
|
+
/** Score a .faf YAML string (33 enterprise slots), as the file is (a leading BOM is not scored). */
|
|
5
7
|
export declare function scoreEnterprise(yaml: string): KernelScoreResult;
|
|
6
|
-
/** Validate .faf YAML */
|
|
8
|
+
/** Validate .faf YAML (a leading BOM is left out, as in {@link score}: `faf check` reads a BOM file). */
|
|
7
9
|
export declare function validate(yaml: string): boolean;
|
|
8
|
-
/** Compile .faf YAML to FAFb binary
|
|
10
|
+
/** Compile .faf YAML to FAFb binary (a leading BOM is not compiled, as in
|
|
11
|
+
* {@link score}: `faf compile` and `faf refresh` read a BOM file). */
|
|
9
12
|
export declare function compile(yaml: string): Uint8Array;
|
|
10
13
|
/** Decompile FAFb binary to JSON info */
|
|
11
14
|
export declare function decompile(bytes: Uint8Array): FafbInfo;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "faf-cli",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.13.0",
|
|
4
4
|
"description": "Persistent AI context + memory — .faf and .fafm, IANA-registered. Anthropic-merged.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"icon": "https://faf.one/orange-smiley.svg",
|
|
@@ -31,10 +31,11 @@
|
|
|
31
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",
|
|
32
32
|
"test": "bun test --timeout=120000",
|
|
33
33
|
"test:watch": "bun test --watch --timeout=120000",
|
|
34
|
-
"lint": "eslint src/**/*.ts",
|
|
34
|
+
"lint": "eslint 'src/**/*.ts'",
|
|
35
35
|
"format": "prettier --write 'src/**/*.ts'",
|
|
36
36
|
"check:no-hardcode": "! grep -rEln '/(Users|home/runner|private/var)/' dist/ || (echo '❌ Hardcoded build-machine path leaked into dist — see grep output above. Externalize the offending dep in the build script.'; exit 1)",
|
|
37
|
-
"
|
|
37
|
+
"check:engines": "node scripts/check-engines.mjs",
|
|
38
|
+
"prepublishOnly": "bun run check:engines && bun run build && bun run check:no-hardcode"
|
|
38
39
|
},
|
|
39
40
|
"keywords": [
|
|
40
41
|
"faf",
|
|
@@ -94,7 +95,7 @@
|
|
|
94
95
|
"picomatch": "^4.0.5"
|
|
95
96
|
},
|
|
96
97
|
"engines": {
|
|
97
|
-
"node": ">=
|
|
98
|
+
"node": ">=22.0.0"
|
|
98
99
|
},
|
|
99
100
|
"publishConfig": {
|
|
100
101
|
"access": "public"
|
package/project.faf
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
faf_version: "3.0"
|
|
2
2
|
project:
|
|
3
3
|
name: faf-cli
|
|
4
|
-
version: "7.
|
|
5
|
-
goal: "CLI for IANA-registered `.faf` + `.fafm` — context DNA and portable agent memory. TypeScript, Bun-native since v6. package faf-cli v7.
|
|
4
|
+
version: "7.13.0"
|
|
5
|
+
goal: "CLI for IANA-registered `.faf` + `.fafm` — context DNA and portable agent memory. TypeScript, Bun-native since v6. package faf-cli v7.13.0 The Co-Author Edition."
|
|
6
6
|
main_language: TypeScript
|
|
7
7
|
type: cli # found: package.json bin
|
|
8
8
|
# Commands + key_files feed `faf export --agents` (hand values win over detection).
|
|
@@ -58,7 +58,7 @@ human_context:
|
|
|
58
58
|
what: Persistent AI Context Standard — project DNA for AI. IANA-registered. Anthropic-merged.
|
|
59
59
|
why: Eliminates 91% context re-discovery tax — define once, AI remembers forever
|
|
60
60
|
where: npm registry, Homebrew, GitHub
|
|
61
|
-
when: Production since September 2025; Bun-native since v6; current package 7.
|
|
61
|
+
when: Production since September 2025; Bun-native since v6; current package 7.13.0
|
|
62
62
|
how: bunx faf-cli auto, then project.faf versions with your code — faf show renders it human-visible
|
|
63
63
|
monorepo:
|
|
64
64
|
packages_count: slotignored
|
|
@@ -68,7 +68,7 @@ monorepo:
|
|
|
68
68
|
remote_cache: slotignored
|
|
69
69
|
surfaces:
|
|
70
70
|
# Distribution map — the machine-checkable form of human_context.where. Score-neutral.
|
|
71
|
-
auto: # derived from manifests;
|
|
71
|
+
auto: # derived from manifests; re-derive with the /surface skill
|
|
72
72
|
- { type: npm, id: faf-cli, source: "package.json#name" }
|
|
73
73
|
- { type: github, id: Wolfe-Jam/faf-cli, source: "package.json#repository" }
|
|
74
74
|
declared: # hand-authored: surfaces the manifests don't carry + targets
|
|
@@ -89,3 +89,10 @@ ai_instructions:
|
|
|
89
89
|
testing: WJTTC — zero errors always; bun test green before any ship.
|
|
90
90
|
runtime: Bun-native since v6, TypeScript strict, Rust→WASM scoring kernel.
|
|
91
91
|
releases: Atomic via /pubpro — bump, verify, tag, publish in one motion.
|
|
92
|
+
tech_stack:
|
|
93
|
+
- TypeScript
|
|
94
|
+
- Bun
|
|
95
|
+
- commander
|
|
96
|
+
- faf-scoring-kernel
|
|
97
|
+
- open
|
|
98
|
+
- yaml
|