faf-python-sdk 1.1.2__tar.gz → 1.3.0__tar.gz

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.
Files changed (39) hide show
  1. faf_python_sdk-1.3.0/.github/workflows/testpypi.yml +45 -0
  2. faf_python_sdk-1.3.0/CHANGELOG.md +76 -0
  3. faf_python_sdk-1.3.0/CLAUDE.md +33 -0
  4. faf_python_sdk-1.3.0/CONTRIBUTING.md +152 -0
  5. {faf_python_sdk-1.1.2 → faf_python_sdk-1.3.0}/PKG-INFO +36 -5
  6. {faf_python_sdk-1.1.2 → faf_python_sdk-1.3.0}/README.md +34 -3
  7. {faf_python_sdk-1.1.2 → faf_python_sdk-1.3.0}/faf_sdk/__init__.py +10 -1
  8. faf_python_sdk-1.3.0/faf_sdk/dart_detection.json +30 -0
  9. faf_python_sdk-1.3.0/faf_sdk/detect.py +167 -0
  10. faf_python_sdk-1.3.0/faf_sdk/interop.py +403 -0
  11. faf_python_sdk-1.3.0/faf_sdk/py.typed +0 -0
  12. {faf_python_sdk-1.1.2 → faf_python_sdk-1.3.0}/project.faf +2 -2
  13. {faf_python_sdk-1.1.2 → faf_python_sdk-1.3.0}/pyproject.toml +11 -2
  14. faf_python_sdk-1.3.0/scripts/sync-dart-spec.sh +52 -0
  15. faf_python_sdk-1.3.0/tests/dart_parity_fixtures.json +310 -0
  16. faf_python_sdk-1.3.0/tests/test_dart_parity.py +40 -0
  17. faf_python_sdk-1.3.0/tests/test_interop.py +178 -0
  18. faf_python_sdk-1.1.2/CHANGELOG.md +0 -39
  19. {faf_python_sdk-1.1.2 → faf_python_sdk-1.3.0}/.github/workflows/pypi.yml +0 -0
  20. {faf_python_sdk-1.1.2 → faf_python_sdk-1.3.0}/.gitignore +0 -0
  21. {faf_python_sdk-1.1.2 → faf_python_sdk-1.3.0}/CODE_OF_CONDUCT.md +0 -0
  22. {faf_python_sdk-1.1.2 → faf_python_sdk-1.3.0}/LICENSE +0 -0
  23. {faf_python_sdk-1.1.2 → faf_python_sdk-1.3.0}/SECURITY.md +0 -0
  24. {faf_python_sdk-1.1.2 → faf_python_sdk-1.3.0}/docs/GROK-INTEGRATION.md +0 -0
  25. {faf_python_sdk-1.1.2 → faf_python_sdk-1.3.0}/docs/TECHNICAL-SPEC.md +0 -0
  26. {faf_python_sdk-1.1.2 → faf_python_sdk-1.3.0}/examples/basic_usage.py +0 -0
  27. {faf_python_sdk-1.1.2 → faf_python_sdk-1.3.0}/examples/grok_integration.py +0 -0
  28. {faf_python_sdk-1.1.2 → faf_python_sdk-1.3.0}/faf_sdk/discovery.py +0 -0
  29. {faf_python_sdk-1.1.2 → faf_python_sdk-1.3.0}/faf_sdk/mk4.py +0 -0
  30. {faf_python_sdk-1.1.2 → faf_python_sdk-1.3.0}/faf_sdk/parser.py +0 -0
  31. {faf_python_sdk-1.1.2 → faf_python_sdk-1.3.0}/faf_sdk/types.py +0 -0
  32. {faf_python_sdk-1.1.2 → faf_python_sdk-1.3.0}/faf_sdk/validator.py +0 -0
  33. {faf_python_sdk-1.1.2 → faf_python_sdk-1.3.0}/tests/__init__.py +0 -0
  34. {faf_python_sdk-1.1.2 → faf_python_sdk-1.3.0}/tests/stress_test.py +0 -0
  35. {faf_python_sdk-1.1.2 → faf_python_sdk-1.3.0}/tests/test_discovery.py +0 -0
  36. {faf_python_sdk-1.1.2 → faf_python_sdk-1.3.0}/tests/test_mk4.py +0 -0
  37. {faf_python_sdk-1.1.2 → faf_python_sdk-1.3.0}/tests/test_parser.py +0 -0
  38. {faf_python_sdk-1.1.2 → faf_python_sdk-1.3.0}/tests/test_validator.py +0 -0
  39. {faf_python_sdk-1.1.2 → faf_python_sdk-1.3.0}/tests/test_wjttc.py +0 -0
@@ -0,0 +1,45 @@
1
+ name: Publish to TestPyPI
2
+
3
+ # Fires on every version tag push (e.g. `git push origin v0.1.5`).
4
+ # Stage 1 of the publish flow: stage to TestPyPI for verification BEFORE
5
+ # creating a GitHub Release (which then fires pypi.yml for production).
6
+ #
7
+ # Flow:
8
+ # 1. git tag v0.1.5 && git push origin v0.1.5 → this workflow runs
9
+ # 2. verify install from TestPyPI → manual / via verify_install.py
10
+ # 3. gh release create v0.1.5 → triggers pypi.yml (production)
11
+
12
+ on:
13
+ push:
14
+ tags:
15
+ - 'v*'
16
+ workflow_dispatch: # Manual trigger
17
+
18
+ jobs:
19
+ publish-testpypi:
20
+ name: Publish to TestPyPI
21
+ runs-on: ubuntu-latest
22
+ environment: testpypi
23
+ permissions:
24
+ id-token: write # Required for OIDC trusted publishing
25
+
26
+ steps:
27
+ - uses: actions/checkout@v6
28
+
29
+ - name: Set up Python
30
+ uses: actions/setup-python@v6
31
+ with:
32
+ python-version: '3.12'
33
+
34
+ - name: Install build tools
35
+ run: pip install build
36
+
37
+ - name: Build package
38
+ run: python -m build
39
+
40
+ - name: Publish to TestPyPI
41
+ uses: pypa/gh-action-pypi-publish@release/v1
42
+ with:
43
+ repository-url: https://test.pypi.org/legacy/
44
+ # No token — uses OIDC trusted publisher configured at
45
+ # https://test.pypi.org/manage/project/faf-python-sdk/settings/publishing/
@@ -0,0 +1,76 @@
1
+ # Changelog
2
+
3
+ All notable changes to faf-python-sdk are documented here.
4
+ Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
5
+
6
+ ## [1.3.0] - 2026-09-06 — The Interop Edition
7
+
8
+ The SDK can now author AI-context files, not just parse and score them.
9
+
10
+ ### Added
11
+ - `faf_sdk.interop` — `generate_agents_md(faf)` and `generate_gemini_md(faf)`,
12
+ Python ports of faf-cli's `src/interop/agents.ts` + `gemini.ts`, kept in
13
+ parity with the canonical TypeScript. Deterministic BETTER-shaped projection:
14
+ `## Setup & build` (install→build→dev ordered) · `## Run the tests` ·
15
+ `## Where things live` · `## Conventions` · three-tier `## Guardrails` ·
16
+ `## Definition of Done` · `## When stuck` · `## Security & secrets` ·
17
+ `## Commit & PR` · `## Stack`. Human Context (who/why marketing) is
18
+ intentionally omitted from AGENTS.md — it belongs in the README / .faf DNA.
19
+ - `faf_meta_tag(faf)`, `title_label(key)`, `slot_label(path)` — the shared
20
+ label + metastamp helpers, also from `src/interop`.
21
+ - Both generators take the **raw parsed dict** (`FafFile.data.raw`) — the .faf
22
+ format carries top-level `commands` / `key_files` / `security` that the typed
23
+ model doesn't surface.
24
+ - 17 tests, including deterministic-output and human-context-omission guards.
25
+
26
+ ### Fixed
27
+ - `[tool.mypy] python_version` was `"3.9"` — rejected by modern mypy
28
+ (`must be 3.10 or higher`). Set to `"3.10"`.
29
+
30
+ ## [1.2.0] - 2026-06-16 — The Dart Edition
31
+
32
+ Adds `detect_dart_project()`: content-aware Dart/Flutter detection from a `pubspec.yaml` (Flutter app vs package · Dart MCP / backend / CLI / library), reproducing faf-cli's engine byte-for-byte — 20 shared fixtures, parity-tested.
33
+
34
+ ### Added
35
+ - `detect_dart_project(dir)` → `DartProject` — the SDK's first detection capability. Reads `pubspec.yaml` and classifies: Flutter app vs reusable package, Dart MCP server, Dart backend (Serverpod / Dart Frog / Shelf / …), Dart CLI, or library. Exported from `faf_sdk`.
36
+ - `faf_sdk/dart_detection.json` — the detection KNOWLEDGE spec, vendored byte-identical from faf-cli (the single source); ships in the wheel, loaded at runtime.
37
+ - `tests/test_dart_parity.py` — 20 shared fixtures run identically by faf-cli and this SDK; parity proven by test, not by eye.
38
+ - `scripts/sync-dart-spec.sh` — vendor + `--check` (byte-identity) the spec & fixtures from faf-cli.
39
+
40
+ ### Notes
41
+ - Mirrors faf-cli `src/detect/dart.ts` exactly (A+B hybrid). To bolster Dart support, edit the spec in faf-cli (the Truth) and re-sync. No new runtime dependencies.
42
+
43
+ ## [1.1.2] - 2026-04-26
44
+
45
+ ### Changed
46
+ - Package description aligned with the canonical "Persistent project context for Python" framing on PyPI catalog and GitHub repo metadata.
47
+ - README lede sharpened — leads with the value proposition and the audience (MCP server / CI validator / tool authors), no longer feature-list framing.
48
+
49
+ ### Added
50
+ - `CHANGELOG.md` (this file) — versioned release history, separate from the README's "What's New" section.
51
+ - Brand mantra `FAF defines. MD instructs. AI codes.` anchored in the README and `__init__.py` module docstring.
52
+
53
+ ### Notes
54
+ No runtime code changes. Patch release to surface description alignment in the PyPI catalog and tighten positioning copy. The catalog only updates on a new publish.
55
+
56
+ ## [1.1.1] - 2026-04-18
57
+
58
+ ### Fixed
59
+ - Tier alignment to match faf-cli v6 — clean geometric symbols, no emoji. v1.1.0 mistakenly returned emoji tiers; this patch normalizes them to plain uppercase strings (`TROPHY`, `GOLD`, `SILVER`, `BRONZE`, `GREEN`, `YELLOW`, `RED`).
60
+
61
+ ## [1.1.0] - 2026-03-29
62
+
63
+ ### Added
64
+ - **Mk4 Championship Scoring Engine** — the same 33-slot scoring algorithm used by the Rust compiler and TypeScript CLI, now in Python. Same slots, same formula, same scores.
65
+ - `score_faf()` — Mk4 scoring with 21-slot Base or 33-slot Enterprise tiers.
66
+ - 100% parity with `faf-wasm-sdk` (Rust) and `faf-cli` (TypeScript).
67
+ - 88 new WJTTC championship-grade tests (concurrency, adversarial input, security).
68
+ - Total test count: 175 (was 87).
69
+
70
+ ### Fixed
71
+ - 3 crash bugs in malformed YAML and null project field handling.
72
+
73
+ ## [1.0.2] - earlier
74
+
75
+ ### Added
76
+ - Initial public release with `parse`, `parse_file`, `stringify`, `validate`, `find_faf_file`, `find_project_root`, and the typed `FafData` model.
@@ -0,0 +1,33 @@
1
+ <!-- faf:start -->
2
+ <!-- faf: faf-python-sdk | Python | | Python SDK for parsing, validating, scoring, and authoring .faf project context — the Mk4 engine + interop generators other Python FAF tools build on -->
3
+ <!-- faf: claim=project.faf | family=FAF -->
4
+
5
+ # CLAUDE.md — faf-python-sdk
6
+
7
+ ## What This Is
8
+
9
+ Python SDK for parsing, validating, scoring, and authoring .faf project context — the Mk4 engine + interop generators other Python FAF tools build on
10
+
11
+ ## Stack
12
+
13
+ - **Language:** Python
14
+ - **Backend:** Python
15
+ - **API:** SDK (library)
16
+ - **Runtime:** Python 3.8+
17
+ - **Hosting:** PyPI
18
+ - **Build:** hatchling
19
+ - **CI/CD:** GitHub Actions
20
+
21
+ ## Context
22
+
23
+ - **Who:** Python developers building AI tools, MCP servers, and CI pipelines
24
+ - **What:** Parse, validate, and score FAF project DNA files with 100% Mk4 parity
25
+ - **Why:** Universal scoring across all FAF tools — same slots, same formula, same scores
26
+ - **Where:** PyPI (pip install faf-python-sdk)
27
+ - **When:** Every AI session that needs project context
28
+ - **How:** Import faf_sdk, call score_faf() or parse_file()
29
+
30
+ ---
31
+
32
+ *STATUS: BI-SYNC ACTIVE — 2026-09-06T18:19:41.228Z*
33
+ <!-- faf:end -->
@@ -0,0 +1,152 @@
1
+ # Contributing
2
+
3
+ Contributions are welcome. Bug fixes, doc improvements, new validators,
4
+ new scoring tactics in `mk4`, new discovery sources — all useful.
5
+
6
+ This file describes **how to land a change cleanly**.
7
+
8
+ ---
9
+
10
+ ## Setup
11
+
12
+ ```bash
13
+ git clone https://github.com/Wolfe-Jam/faf-python-sdk
14
+ cd faf-python-sdk
15
+ python3 -m venv .venv
16
+ source .venv/bin/activate
17
+ pip install -e ".[dev]"
18
+ ```
19
+
20
+ That's it. You're ready to run tests and ship a fix.
21
+
22
+ ---
23
+
24
+ ## Before opening a PR
25
+
26
+ Run the full check pass:
27
+
28
+ ```bash
29
+ pytest tests/ -v # all tests must pass
30
+ mypy faf_sdk/ # strict typing — no untyped defs
31
+ ```
32
+
33
+ If both come back clean, you're good to push. PRs that don't pass tests
34
+ will not be reviewed.
35
+
36
+ ---
37
+
38
+ ## PR conventions
39
+
40
+ | Type of change | Required |
41
+ |---|---|
42
+ | Bug fix | A regression test that fails on the bug, passes after the fix. The test comes **with** the fix, not after. |
43
+ | New feature | Tests for the new surface. If it touches the `.faf` format itself, see "Architecture decisions" below — the SDK doesn't extend the format unilaterally. |
44
+ | Doc-only | No tests required. README + CHANGELOG entries still expected. |
45
+ | Refactor | Existing tests must pass unchanged. Coverage must stay ≥ existing baseline. |
46
+
47
+ `mypy faf_sdk/` must be clean. Strict mode is on (`disallow_untyped_defs = true`).
48
+ The `[tool.mypy]` section in `pyproject.toml` is authoritative.
49
+
50
+ ---
51
+
52
+ ## Branch model
53
+
54
+ - `main` is always shippable. Tagged releases come from `main`.
55
+ - Work on feature branches. PR → squash-merge into `main`.
56
+ - Don't open PRs against tagged commits. Tags are immutable; any
57
+ fix lands on `main` and gets a new tag if it's release-worthy.
58
+
59
+ ---
60
+
61
+ ## Commit messages
62
+
63
+ - Imperative mood: "fix: handle empty .faf gracefully" not "fixed" / "fixes".
64
+ - Conventional-commits prefix is appreciated but not enforced
65
+ (`fix:` / `feat:` / `refactor:` / `chore:` / `docs:` / `test:`).
66
+ - Body explains the **why** when it's non-obvious. The diff explains
67
+ the what.
68
+ - No marketing language in commit subjects. Commit messages are
69
+ technical, not promotional.
70
+
71
+ ---
72
+
73
+ ## Code style
74
+
75
+ - **Names over comments.** A well-named function or variable doesn't
76
+ need a comment explaining what it does.
77
+ - **WHY-comments are welcome** where the *why* isn't obvious — a
78
+ hidden constraint, a workaround for a specific bug, a non-obvious
79
+ invariant.
80
+ - **No marketing prose in code comments.** Internal docs are
81
+ documentation, not pitch material.
82
+ - Type hints on all public signatures. `disallow_untyped_defs` is on.
83
+ - `from __future__ import annotations` at the top of any new module
84
+ using `|` union types — keeps Python 3.8/3.9 compatibility.
85
+
86
+ ---
87
+
88
+ ## Adding a new validator / scorer / parser
89
+
90
+ The SDK's modules are deliberately narrow:
91
+
92
+ | Module | Job |
93
+ |---|---|
94
+ | `parser` | Read `.faf` (YAML) into typed dicts |
95
+ | `validator` | Check `.faf` against the schema |
96
+ | `mk4` | Score `.faf` (the FAF "ECU" — championship tiers) |
97
+ | `discovery` | Find `.faf` files in a project |
98
+ | `types` | Shared dataclasses + protocols |
99
+
100
+ When adding to one of these, **stay in lane**. A scoring change goes in
101
+ `mk4`, not `validator`. A new file-finder goes in `discovery`. Cross-cutting
102
+ features need a small RFC in the PR description before code lands.
103
+
104
+ ---
105
+
106
+ ## Architecture decisions
107
+
108
+ The SDK has firm design rules that aren't up for debate in PRs:
109
+
110
+ 1. **The `.faf` format spec lives in the FAF organization, not the SDK.**
111
+ PRs that change parser/validator behavior to "support a new format
112
+ feature" before the format spec adds it will be closed. The SDK
113
+ *implements* the spec; it doesn't *extend* it.
114
+ 2. **MIT-licensed and dependency-light.** The SDK has one runtime
115
+ dependency (`pyyaml`) and that's the floor we want to defend. PRs
116
+ that add runtime deps need strong justification.
117
+ 3. **The SDK is the foundation other Python FAF tools build on**
118
+ (gemini-faf-mcp, custom MCP servers, CI validators). Breaking
119
+ changes to public surfaces go behind a major version bump.
120
+
121
+ ---
122
+
123
+ ## CI doctrine
124
+
125
+ Two rules from the project's CI philosophy:
126
+
127
+ 1. **Red means real.** A red `test` job is a real, actionable failure.
128
+ We don't tolerate flaky tests — if a test fails intermittently,
129
+ that's a bug in the test, fix it before merging anything else.
130
+ 2. **Type-check is observability, not a gate.** mypy failures show up
131
+ but tests are the only hard gate. Type cleanup PRs are welcome but
132
+ never urgent.
133
+
134
+ If CI goes red after a merge, the breaking change owns the
135
+ fix — revert is on the table, no shame.
136
+
137
+ ---
138
+
139
+ ## Where to file issues
140
+
141
+ [github.com/Wolfe-Jam/faf-python-sdk/issues](https://github.com/Wolfe-Jam/faf-python-sdk/issues)
142
+
143
+ For security issues: don't open a public issue. Email
144
+ **team@faf.one** with details.
145
+
146
+ ---
147
+
148
+ ## License
149
+
150
+ MIT. Fork it, ship it, embed it, enjoy it.
151
+
152
+ **Don't copy FAF brand. Do your own.**
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: faf-python-sdk
3
- Version: 1.1.2
3
+ Version: 1.3.0
4
4
  Summary: Persistent project context for Python — parse, validate, and score `.faf` files. The foundation other Python FAF tools (gemini-faf-mcp, custom MCP servers, CI validators) build on. IANA-registered application/vnd.faf+yaml.
5
5
  Project-URL: Homepage, https://faf.one
6
6
  Project-URL: Documentation, https://github.com/Wolfe-Jam/faf-python-sdk
@@ -32,17 +32,46 @@ Description-Content-Type: text/markdown
32
32
 
33
33
  # faf-python-sdk
34
34
 
35
- **Persistent project context for Python.** Parse, validate, and score `.faf` files — the foundation other Python FAF tools build on.
35
+ **Persistent Project Context for Python. Parse, validate, score.**
36
36
 
37
- If you're building MCP servers, CI validators, or any Python tool that needs to understand project context, start here.
37
+ **FAF defines. MD instructs. AI codes.**
38
38
 
39
+ The foundation other Python FAF tools build on. If you're building MCP servers, CI validators, or any Python tool that needs to understand project context, start here.
40
+
41
+ [![FAF](https://mcpaas.live/badge/Wolfe-Jam/faf-python-sdk.svg)](https://builder.faf.one)
39
42
  [![PyPI](https://img.shields.io/pypi/v/faf-python-sdk?style=for-the-badge&logo=pypi&logoColor=white)](https://pypi.org/project/faf-python-sdk/)
40
43
  [![Downloads](https://img.shields.io/pypi/dm/faf-python-sdk?style=for-the-badge&color=blue)](https://pypi.org/project/faf-python-sdk/)
41
- [![Tests](https://img.shields.io/badge/tests-175%20passing-brightgreen?style=for-the-badge)](https://github.com/Wolfe-Jam/faf-python-sdk)
44
+ [![Tests](https://img.shields.io/badge/tests-213%20passing-brightgreen?style=for-the-badge)](https://github.com/Wolfe-Jam/faf-python-sdk)
42
45
  [![IANA](https://img.shields.io/badge/IANA-registered-informational?style=for-the-badge)](https://www.iana.org/assignments/media-types/application/vnd.faf+yaml)
43
46
 
44
47
  **Media Type:** `application/vnd.faf+yaml` (IANA registered)
45
48
 
49
+ ## What's New in v1.3.0 — The Interop Edition
50
+
51
+ The SDK can now author AI-context files, not just parse and score them.
52
+
53
+ `faf_sdk.interop` — `generate_agents_md(faf)` and `generate_gemini_md(faf)`, Python ports of faf-cli's `src/interop/agents.ts` + `gemini.ts`, in parity with the canonical TypeScript. Deterministic BETTER-shaped projection: setup (install→build→dev ordered) · tests · layout · conventions · three-tier guardrails · definition of done · security · commit · stack. Human Context (who/why marketing) is intentionally omitted from AGENTS.md — it belongs in the README / .faf DNA, not agent ops.
54
+
55
+ ```python
56
+ from faf_sdk import parse_file, generate_agents_md
57
+
58
+ faf = parse_file("project.faf")
59
+ print(generate_agents_md(faf.data.raw)) # takes the raw dict — carries top-level commands / key_files / security
60
+ ```
61
+
62
+ Any Python FAF tool that authors an AI-context file wraps this now — never hand-roll a Markdown generator. `gemini-faf-mcp` 2.7.0's `faf_agents` / `faf_gemini` are the reference wrappers.
63
+
64
+ ## What's New in v1.2.0 — The Dart Edition
65
+
66
+ Adds `detect_dart_project()`: content-aware Dart/Flutter detection from a `pubspec.yaml` (Flutter app vs package · Dart MCP / backend / CLI / library), reproducing faf-cli's engine byte-for-byte — 20 shared fixtures, parity-tested.
67
+
68
+ ```python
69
+ from faf_sdk import detect_dart_project
70
+
71
+ d = detect_dart_project(".")
72
+ print(d.app_type, d.framework) # e.g. "mobile" "Flutter"
73
+ ```
74
+
46
75
  ## What's New in v1.1.0
47
76
 
48
77
  **Mk4 Championship Scoring Engine** — the same 33-slot scoring algorithm used by the Rust compiler and TypeScript CLI, now in Python. Same slots, same formula, same scores. Every FAF tool in every language now agrees on what 100% means.
@@ -175,6 +204,8 @@ root = find_project_root()
175
204
  | [grok-faf-mcp](https://npmjs.com/package/grok-faf-mcp) | xAI | npm |
176
205
  | [faf-cli](https://npmjs.com/package/faf-cli) | CLI | npm |
177
206
 
207
+ If `faf-python-sdk` has been useful, consider starring the repo — it helps others find it.
208
+
178
209
  ## Links
179
210
 
180
211
  - **Site:** [faf.one](https://faf.one)
@@ -1,16 +1,45 @@
1
1
  # faf-python-sdk
2
2
 
3
- **Persistent project context for Python.** Parse, validate, and score `.faf` files — the foundation other Python FAF tools build on.
3
+ **Persistent Project Context for Python. Parse, validate, score.**
4
4
 
5
- If you're building MCP servers, CI validators, or any Python tool that needs to understand project context, start here.
5
+ **FAF defines. MD instructs. AI codes.**
6
6
 
7
+ The foundation other Python FAF tools build on. If you're building MCP servers, CI validators, or any Python tool that needs to understand project context, start here.
8
+
9
+ [![FAF](https://mcpaas.live/badge/Wolfe-Jam/faf-python-sdk.svg)](https://builder.faf.one)
7
10
  [![PyPI](https://img.shields.io/pypi/v/faf-python-sdk?style=for-the-badge&logo=pypi&logoColor=white)](https://pypi.org/project/faf-python-sdk/)
8
11
  [![Downloads](https://img.shields.io/pypi/dm/faf-python-sdk?style=for-the-badge&color=blue)](https://pypi.org/project/faf-python-sdk/)
9
- [![Tests](https://img.shields.io/badge/tests-175%20passing-brightgreen?style=for-the-badge)](https://github.com/Wolfe-Jam/faf-python-sdk)
12
+ [![Tests](https://img.shields.io/badge/tests-213%20passing-brightgreen?style=for-the-badge)](https://github.com/Wolfe-Jam/faf-python-sdk)
10
13
  [![IANA](https://img.shields.io/badge/IANA-registered-informational?style=for-the-badge)](https://www.iana.org/assignments/media-types/application/vnd.faf+yaml)
11
14
 
12
15
  **Media Type:** `application/vnd.faf+yaml` (IANA registered)
13
16
 
17
+ ## What's New in v1.3.0 — The Interop Edition
18
+
19
+ The SDK can now author AI-context files, not just parse and score them.
20
+
21
+ `faf_sdk.interop` — `generate_agents_md(faf)` and `generate_gemini_md(faf)`, Python ports of faf-cli's `src/interop/agents.ts` + `gemini.ts`, in parity with the canonical TypeScript. Deterministic BETTER-shaped projection: setup (install→build→dev ordered) · tests · layout · conventions · three-tier guardrails · definition of done · security · commit · stack. Human Context (who/why marketing) is intentionally omitted from AGENTS.md — it belongs in the README / .faf DNA, not agent ops.
22
+
23
+ ```python
24
+ from faf_sdk import parse_file, generate_agents_md
25
+
26
+ faf = parse_file("project.faf")
27
+ print(generate_agents_md(faf.data.raw)) # takes the raw dict — carries top-level commands / key_files / security
28
+ ```
29
+
30
+ Any Python FAF tool that authors an AI-context file wraps this now — never hand-roll a Markdown generator. `gemini-faf-mcp` 2.7.0's `faf_agents` / `faf_gemini` are the reference wrappers.
31
+
32
+ ## What's New in v1.2.0 — The Dart Edition
33
+
34
+ Adds `detect_dart_project()`: content-aware Dart/Flutter detection from a `pubspec.yaml` (Flutter app vs package · Dart MCP / backend / CLI / library), reproducing faf-cli's engine byte-for-byte — 20 shared fixtures, parity-tested.
35
+
36
+ ```python
37
+ from faf_sdk import detect_dart_project
38
+
39
+ d = detect_dart_project(".")
40
+ print(d.app_type, d.framework) # e.g. "mobile" "Flutter"
41
+ ```
42
+
14
43
  ## What's New in v1.1.0
15
44
 
16
45
  **Mk4 Championship Scoring Engine** — the same 33-slot scoring algorithm used by the Rust compiler and TypeScript CLI, now in Python. Same slots, same formula, same scores. Every FAF tool in every language now agrees on what 100% means.
@@ -143,6 +172,8 @@ root = find_project_root()
143
172
  | [grok-faf-mcp](https://npmjs.com/package/grok-faf-mcp) | xAI | npm |
144
173
  | [faf-cli](https://npmjs.com/package/faf-cli) | CLI | npm |
145
174
 
175
+ If `faf-python-sdk` has been useful, consider starring the repo — it helps others find it.
176
+
146
177
  ## Links
147
178
 
148
179
  - **Site:** [faf.one](https://faf.one)
@@ -24,6 +24,8 @@ from .parser import parse, parse_file, stringify, FafFile
24
24
  from .validator import validate, ValidationResult
25
25
  from .mk4 import score_faf, Mk4Result, SlotState, LicenseTier
26
26
  from .discovery import find_faf_file, find_project_root, load_fafignore
27
+ from .detect import detect_dart_project, DartProject
28
+ from .interop import generate_agents_md, generate_gemini_md, faf_meta_tag
27
29
  from .types import (
28
30
  FafData,
29
31
  ProjectInfo,
@@ -34,7 +36,7 @@ from .types import (
34
36
  AIScoring
35
37
  )
36
38
 
37
- __version__ = "1.1.2"
39
+ __version__ = "1.3.0"
38
40
  __all__ = [
39
41
  # Parser
40
42
  "parse",
@@ -53,6 +55,13 @@ __all__ = [
53
55
  "find_faf_file",
54
56
  "find_project_root",
55
57
  "load_fafignore",
58
+ # Detection (Dart/Flutter — A+B hybrid, parity with faf-cli)
59
+ "detect_dart_project",
60
+ "DartProject",
61
+ # Interop — AGENTS.md / GEMINI.md generators (parity with faf-cli src/interop)
62
+ "generate_agents_md",
63
+ "generate_gemini_md",
64
+ "faf_meta_tag",
56
65
  # Types
57
66
  "FafData",
58
67
  "ProjectInfo",
@@ -0,0 +1,30 @@
1
+ {
2
+ "version": 1,
3
+ "_doc": "SINGLE SOURCE of Dart/Flutter detection KNOWLEDGE (A+B hybrid, the Truth-is-faf-cli spec). faf-cli imports this directly; faf-python-sdk vendors a synced copy (phase 2); MCPs consume the SDK (phase 3). To bolster Dart/Flutter support — a new state library, server framework, MCP dep — edit THIS file and every engine inherits it. The thin per-language LOGIC (pubspec parse, app-vs-package heuristic, priority branching) lives in code, not here.",
4
+ "flutterDeps": ["flutter"],
5
+ "mcpDeps": ["dart_mcp", "mcp_server", "mcp_dart", "dart_mcp_server", "mcp"],
6
+ "serverFrameworks": [
7
+ ["serverpod", "Serverpod"],
8
+ ["dart_frog", "Dart Frog"],
9
+ ["conduit", "Conduit"],
10
+ ["angel3_framework", "Angel3"],
11
+ ["alfred", "Alfred"],
12
+ ["shelf", "Shelf"]
13
+ ],
14
+ "stateManagement": [
15
+ ["flutter_riverpod", "Riverpod"],
16
+ ["hooks_riverpod", "Riverpod"],
17
+ ["riverpod", "Riverpod"],
18
+ ["flutter_bloc", "Bloc"],
19
+ ["bloc", "Bloc"],
20
+ ["provider", "Provider"],
21
+ ["get", "GetX"],
22
+ ["flutter_mobx", "MobX"],
23
+ ["mobx", "MobX"],
24
+ ["signals", "Signals"]
25
+ ],
26
+ "routing": [
27
+ ["go_router", "go_router"],
28
+ ["auto_route", "auto_route"]
29
+ ]
30
+ }
@@ -0,0 +1,167 @@
1
+ """
2
+ Dart/Flutter detection — CONTENT-AWARE pubspec classification (Python).
3
+
4
+ PARITY: this mirrors faf-cli src/detect/dart.ts EXACTLY. A pubspec.yaml alone
5
+ does NOT mean Flutter — the same manifest backs Flutter apps, pure-Dart CLIs,
6
+ packages, servers (Dart Frog / Shelf / Serverpod) and MCP servers
7
+ (dart_mcp / mcp_server). We read the dependencies and branch.
8
+
9
+ The detection KNOWLEDGE (which deps mean what) lives in dart_detection.json —
10
+ a byte-identical, synced copy of faf-cli's src/detect/dart-detection.json (the
11
+ single source, A+B hybrid). To bolster Dart support, edit the spec in faf-cli
12
+ (the Truth) and re-run scripts/sync-dart-spec.sh. The thin per-language LOGIC
13
+ (pubspec parse, app-vs-package heuristic, priority branching) lives here.
14
+
15
+ Behavior parity with faf-cli is PROVEN by tests/test_dart_parity.py, which runs
16
+ the SAME shared fixtures faf-cli runs in tests/detect/dart-parity.test.ts.
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ import json
22
+ import os
23
+ import re
24
+ from dataclasses import dataclass
25
+ from pathlib import Path
26
+ from typing import Any, Dict, List, Optional, Set
27
+
28
+ _SPEC: Dict[str, Any] = json.loads(
29
+ (Path(__file__).parent / "dart_detection.json").read_text(encoding="utf-8")
30
+ )
31
+
32
+ # Detection KNOWLEDGE — sourced from dart_detection.json, never hand-listed here.
33
+ FLUTTER_DEPS: List[str] = list(_SPEC["flutterDeps"])
34
+ MCP_DEPS: List[str] = list(_SPEC["mcpDeps"])
35
+ SERVER_FRAMEWORKS: List[List[str]] = [list(e) for e in _SPEC["serverFrameworks"]]
36
+ STATE_MGMT: List[List[str]] = [list(e) for e in _SPEC["stateManagement"]]
37
+ ROUTING: List[List[str]] = [list(e) for e in _SPEC["routing"]]
38
+
39
+
40
+ @dataclass
41
+ class DartProject:
42
+ """Classification of a Dart/Flutter project from its pubspec.yaml."""
43
+
44
+ app_type: str # 'mobile' | 'mcp' | 'backend' | 'cli' | 'library'
45
+ is_flutter: bool
46
+ framework: str # 'Flutter' | 'Dart Frog' | 'Serverpod' | 'Shelf' | ... | ''
47
+ state_management: str # 'Riverpod' | 'Bloc' | 'Provider' | 'GetX' | ... | ''
48
+ routing: str # 'go_router' | 'auto_route' | ''
49
+ testing: str # 'flutter_test' | 'test' | ''
50
+ found: str # human-readable rationale for the .faf `# found:` comment
51
+
52
+ def to_dict(self) -> Dict[str, object]:
53
+ """Project to the faf-cli DartProject shape (camelCase) for parity checks."""
54
+ return {
55
+ "appType": self.app_type,
56
+ "isFlutter": self.is_flutter,
57
+ "framework": self.framework,
58
+ "stateManagement": self.state_management,
59
+ "routing": self.routing,
60
+ "testing": self.testing,
61
+ "found": self.found,
62
+ }
63
+
64
+
65
+ def _pubspec_deps(content: str) -> Set[str]:
66
+ """Collect dependency names from dependencies / dev_dependencies sections."""
67
+ deps: Set[str] = set()
68
+ in_deps = False
69
+ for line in content.split("\n"):
70
+ if re.match(r"^(dependencies|dev_dependencies|dependency_overrides):\s*$", line):
71
+ in_deps = True
72
+ continue
73
+ # A new top-level key (no leading whitespace) ends the dependency section.
74
+ if re.match(r"^\S", line):
75
+ in_deps = False
76
+ if in_deps:
77
+ m = re.match(r"^\s{2}([a-zA-Z0-9_]+):", line)
78
+ if m:
79
+ deps.add(m.group(1).lower())
80
+ return deps
81
+
82
+
83
+ def detect_dart_project(directory: str) -> Optional[DartProject]:
84
+ """Classify a Dart/Flutter project from its pubspec.yaml. None if not Dart."""
85
+ path = Path(directory) / "pubspec.yaml"
86
+ if not path.exists():
87
+ return None
88
+ try:
89
+ content = path.read_text(encoding="utf-8")
90
+ except OSError:
91
+ return None
92
+
93
+ deps = _pubspec_deps(content)
94
+
95
+ def has(dep: str) -> bool:
96
+ return dep.lower() in deps
97
+
98
+ # Flutter: the `flutter` SDK dep, a top-level `flutter:` section, or `sdk: flutter`.
99
+ is_flutter = (
100
+ any(has(d) for d in FLUTTER_DEPS)
101
+ or re.search(r"^flutter:\s*$", content, re.MULTILINE) is not None
102
+ or re.search(r"\bsdk:\s*flutter\b", content) is not None
103
+ )
104
+
105
+ mcp_dep = next((d for d in MCP_DEPS if has(d)), None)
106
+ server = next((e for e in SERVER_FRAMEWORKS if has(e[0])), None)
107
+ state_entry = next((e for e in STATE_MGMT if has(e[0])), None)
108
+ state_management = state_entry[1] if state_entry else ""
109
+ route_entry = next((e for e in ROUTING if has(e[0])), None)
110
+ routing = route_entry[1] if route_entry else ""
111
+ testing = "flutter_test" if has("flutter_test") else ("test" if has("test") else "")
112
+
113
+ # CLI: a top-level `executables:` section, or bin/*.dart entry points.
114
+ has_executables = re.search(r"^executables:\s*$", content, re.MULTILINE) is not None
115
+ has_bin_dart = False
116
+ bin_dir = Path(directory) / "bin"
117
+ if bin_dir.is_dir():
118
+ try:
119
+ has_bin_dart = any(f.endswith(".dart") for f in os.listdir(bin_dir))
120
+ except OSError:
121
+ has_bin_dart = False
122
+ is_cli = has_executables or has_bin_dart
123
+
124
+ framework = ""
125
+ app_type: str
126
+ found: str
127
+
128
+ if is_flutter:
129
+ framework = "Flutter"
130
+ # App vs package: an app has lib/main.dart (the entry) or `publish_to: none`;
131
+ # a reusable Flutter package has neither — it's publishable, lib/ exports only.
132
+ is_app = (Path(directory) / "lib" / "main.dart").exists() or (
133
+ re.search(r"^publish_to:\s*['\"]?none\b", content, re.MULTILINE) is not None
134
+ )
135
+ if is_app:
136
+ app_type = "mobile"
137
+ found = "pubspec.yaml (Flutter app)"
138
+ else:
139
+ app_type = "library"
140
+ found = "pubspec.yaml (Flutter package)"
141
+ elif mcp_dep:
142
+ app_type = "mcp"
143
+ found = f"pubspec.yaml + {mcp_dep} (Dart MCP server)"
144
+ elif server:
145
+ app_type = "backend"
146
+ framework = server[1]
147
+ found = f"pubspec.yaml + {server[0]} (Dart backend)"
148
+ elif is_cli:
149
+ app_type = "cli"
150
+ found = (
151
+ "pubspec.yaml executables: (Dart CLI)"
152
+ if has_executables
153
+ else "pubspec.yaml + bin/*.dart (Dart CLI)"
154
+ )
155
+ else:
156
+ app_type = "library"
157
+ found = "pubspec.yaml (Dart package)"
158
+
159
+ return DartProject(
160
+ app_type=app_type,
161
+ is_flutter=is_flutter,
162
+ framework=framework,
163
+ state_management=state_management,
164
+ routing=routing,
165
+ testing=testing,
166
+ found=found,
167
+ )