learningfoundry 0.2.0__tar.gz → 0.27.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 (137) hide show
  1. {learningfoundry-0.2.0 → learningfoundry-0.27.0}/.gitignore +3 -4
  2. learningfoundry-0.27.0/CHANGELOG.md +313 -0
  3. learningfoundry-0.27.0/PKG-INFO +348 -0
  4. learningfoundry-0.27.0/README.md +319 -0
  5. learningfoundry-0.27.0/pyproject.toml +90 -0
  6. {learningfoundry-0.2.0 → learningfoundry-0.27.0}/src/learningfoundry/__init__.py +1 -1
  7. learningfoundry-0.27.0/src/learningfoundry/cli.py +218 -0
  8. learningfoundry-0.27.0/src/learningfoundry/config.py +95 -0
  9. learningfoundry-0.27.0/src/learningfoundry/exceptions.py +40 -0
  10. learningfoundry-0.27.0/src/learningfoundry/generator.py +99 -0
  11. learningfoundry-0.27.0/src/learningfoundry/integrations/d3foundry_stub.py +28 -0
  12. learningfoundry-0.27.0/src/learningfoundry/integrations/nbfoundry_stub.py +31 -0
  13. learningfoundry-0.27.0/src/learningfoundry/integrations/protocols.py +36 -0
  14. learningfoundry-0.27.0/src/learningfoundry/integrations/quizazz.py +50 -0
  15. learningfoundry-0.27.0/src/learningfoundry/logging_config.py +34 -0
  16. learningfoundry-0.27.0/src/learningfoundry/parser.py +116 -0
  17. learningfoundry-0.27.0/src/learningfoundry/pipeline.py +180 -0
  18. learningfoundry-0.27.0/src/learningfoundry/py.typed +0 -0
  19. learningfoundry-0.27.0/src/learningfoundry/resolver.py +293 -0
  20. learningfoundry-0.27.0/src/learningfoundry/schema_v1.py +141 -0
  21. learningfoundry-0.27.0/src/learningfoundry/sveltekit_template/package.json +35 -0
  22. learningfoundry-0.27.0/src/learningfoundry/sveltekit_template/pnpm-lock.yaml +2046 -0
  23. learningfoundry-0.27.0/src/learningfoundry/sveltekit_template/src/app.css +3 -0
  24. learningfoundry-0.27.0/src/learningfoundry/sveltekit_template/src/app.html +12 -0
  25. learningfoundry-0.27.0/src/learningfoundry/sveltekit_template/src/lib/components/ContentBlock.svelte +44 -0
  26. learningfoundry-0.27.0/src/learningfoundry/sveltekit_template/src/lib/components/ExerciseBlock.svelte +35 -0
  27. learningfoundry-0.27.0/src/learningfoundry/sveltekit_template/src/lib/components/LessonList.svelte +45 -0
  28. learningfoundry-0.27.0/src/learningfoundry/sveltekit_template/src/lib/components/LessonView.svelte +44 -0
  29. learningfoundry-0.27.0/src/learningfoundry/sveltekit_template/src/lib/components/ModuleList.svelte +67 -0
  30. learningfoundry-0.27.0/src/learningfoundry/sveltekit_template/src/lib/components/Navigation.svelte +48 -0
  31. learningfoundry-0.27.0/src/learningfoundry/sveltekit_template/src/lib/components/PlaceholderBlock.svelte +13 -0
  32. learningfoundry-0.27.0/src/learningfoundry/sveltekit_template/src/lib/components/ProgressBar.svelte +29 -0
  33. learningfoundry-0.27.0/src/learningfoundry/sveltekit_template/src/lib/components/ProgressDashboard.svelte +85 -0
  34. learningfoundry-0.27.0/src/learningfoundry/sveltekit_template/src/lib/components/QuizBlock.svelte +53 -0
  35. learningfoundry-0.27.0/src/learningfoundry/sveltekit_template/src/lib/components/TextBlock.svelte +16 -0
  36. learningfoundry-0.27.0/src/learningfoundry/sveltekit_template/src/lib/components/VideoBlock.svelte +28 -0
  37. learningfoundry-0.27.0/src/learningfoundry/sveltekit_template/src/lib/components/VisualizationBlock.svelte +42 -0
  38. learningfoundry-0.27.0/src/learningfoundry/sveltekit_template/src/lib/db/database.ts +118 -0
  39. learningfoundry-0.27.0/src/learningfoundry/sveltekit_template/src/lib/db/index.ts +12 -0
  40. learningfoundry-0.27.0/src/learningfoundry/sveltekit_template/src/lib/db/progress.ts +165 -0
  41. learningfoundry-0.27.0/src/learningfoundry/sveltekit_template/src/lib/stores/curriculum.ts +128 -0
  42. learningfoundry-0.27.0/src/learningfoundry/sveltekit_template/src/lib/types/index.ts +133 -0
  43. learningfoundry-0.27.0/src/learningfoundry/sveltekit_template/src/lib/utils/markdown.ts +13 -0
  44. learningfoundry-0.27.0/src/learningfoundry/sveltekit_template/src/routes/+layout.svelte +51 -0
  45. learningfoundry-0.27.0/src/learningfoundry/sveltekit_template/src/routes/+page.svelte +44 -0
  46. learningfoundry-0.27.0/src/learningfoundry/sveltekit_template/src/routes/[module]/[lesson]/+page.svelte +43 -0
  47. learningfoundry-0.27.0/src/learningfoundry/sveltekit_template/static/.gitkeep +0 -0
  48. learningfoundry-0.27.0/src/learningfoundry/sveltekit_template/svelte.config.js +23 -0
  49. learningfoundry-0.27.0/src/learningfoundry/sveltekit_template/tsconfig.json +13 -0
  50. learningfoundry-0.27.0/src/learningfoundry/sveltekit_template/vite.config.ts +9 -0
  51. learningfoundry-0.27.0/sveltekit_template/src/app.css +3 -0
  52. learningfoundry-0.27.0/sveltekit_template/src/app.html +12 -0
  53. learningfoundry-0.27.0/sveltekit_template/src/lib/components/ContentBlock.svelte +44 -0
  54. learningfoundry-0.27.0/sveltekit_template/src/lib/components/ExerciseBlock.svelte +35 -0
  55. learningfoundry-0.27.0/sveltekit_template/src/lib/components/LessonList.svelte +45 -0
  56. learningfoundry-0.27.0/sveltekit_template/src/lib/components/LessonView.svelte +44 -0
  57. learningfoundry-0.27.0/sveltekit_template/src/lib/components/ModuleList.svelte +67 -0
  58. learningfoundry-0.27.0/sveltekit_template/src/lib/components/Navigation.svelte +48 -0
  59. learningfoundry-0.27.0/sveltekit_template/src/lib/components/PlaceholderBlock.svelte +13 -0
  60. learningfoundry-0.27.0/sveltekit_template/src/lib/components/ProgressBar.svelte +29 -0
  61. learningfoundry-0.27.0/sveltekit_template/src/lib/components/ProgressDashboard.svelte +85 -0
  62. learningfoundry-0.27.0/sveltekit_template/src/lib/components/QuizBlock.svelte +53 -0
  63. learningfoundry-0.27.0/sveltekit_template/src/lib/components/TextBlock.svelte +16 -0
  64. learningfoundry-0.27.0/sveltekit_template/src/lib/components/VideoBlock.svelte +28 -0
  65. learningfoundry-0.27.0/sveltekit_template/src/lib/components/VisualizationBlock.svelte +42 -0
  66. learningfoundry-0.27.0/sveltekit_template/src/lib/db/database.ts +118 -0
  67. learningfoundry-0.27.0/sveltekit_template/src/lib/db/index.ts +12 -0
  68. learningfoundry-0.27.0/sveltekit_template/src/lib/db/progress.ts +165 -0
  69. learningfoundry-0.27.0/sveltekit_template/src/lib/stores/curriculum.ts +128 -0
  70. learningfoundry-0.27.0/sveltekit_template/src/lib/types/index.ts +133 -0
  71. learningfoundry-0.27.0/sveltekit_template/src/lib/utils/markdown.ts +13 -0
  72. learningfoundry-0.27.0/sveltekit_template/src/routes/+layout.svelte +51 -0
  73. learningfoundry-0.27.0/sveltekit_template/src/routes/+page.svelte +44 -0
  74. learningfoundry-0.27.0/sveltekit_template/src/routes/[module]/[lesson]/+page.svelte +43 -0
  75. learningfoundry-0.2.0/.github/workflows/publish.yml +0 -49
  76. learningfoundry-0.2.0/.project-guide.yml +0 -5
  77. learningfoundry-0.2.0/.pyve/config +0 -6
  78. learningfoundry-0.2.0/.tool-versions +0 -1
  79. learningfoundry-0.2.0/CHANGELOG.md +0 -23
  80. learningfoundry-0.2.0/PKG-INFO +0 -13
  81. learningfoundry-0.2.0/README.md +0 -19
  82. learningfoundry-0.2.0/docs/project-guide/.metadata.yml +0 -195
  83. learningfoundry-0.2.0/docs/project-guide/developer/best-practices-guide.md +0 -342
  84. learningfoundry-0.2.0/docs/project-guide/developer/brand-descriptions-guide.md +0 -461
  85. learningfoundry-0.2.0/docs/project-guide/developer/codecov-setup-guide.md +0 -181
  86. learningfoundry-0.2.0/docs/project-guide/developer/debug-guide.md +0 -469
  87. learningfoundry-0.2.0/docs/project-guide/developer/landing-page-guide.md +0 -808
  88. learningfoundry-0.2.0/docs/project-guide/developer/production-github-guide.md +0 -437
  89. learningfoundry-0.2.0/docs/project-guide/developer/project-guide.md +0 -452
  90. learningfoundry-0.2.0/docs/project-guide/go.md +0 -233
  91. learningfoundry-0.2.0/docs/project-guide/templates/artifacts/brand-descriptions.md +0 -54
  92. learningfoundry-0.2.0/docs/project-guide/templates/artifacts/concept.md +0 -37
  93. learningfoundry-0.2.0/docs/project-guide/templates/artifacts/features.md +0 -77
  94. learningfoundry-0.2.0/docs/project-guide/templates/artifacts/project-essentials.md +0 -43
  95. learningfoundry-0.2.0/docs/project-guide/templates/artifacts/stories.md +0 -21
  96. learningfoundry-0.2.0/docs/project-guide/templates/artifacts/tech-spec.md +0 -77
  97. learningfoundry-0.2.0/docs/project-guide/templates/llm_entry_point.md +0 -5
  98. learningfoundry-0.2.0/docs/project-guide/templates/modes/_header-common.md +0 -46
  99. learningfoundry-0.2.0/docs/project-guide/templates/modes/_header-cycle.md +0 -4
  100. learningfoundry-0.2.0/docs/project-guide/templates/modes/_header-sequence.md +0 -7
  101. learningfoundry-0.2.0/docs/project-guide/templates/modes/_phase-letters.md +0 -27
  102. learningfoundry-0.2.0/docs/project-guide/templates/modes/archive-stories-mode.md +0 -80
  103. learningfoundry-0.2.0/docs/project-guide/templates/modes/code-test-first-mode.md +0 -58
  104. learningfoundry-0.2.0/docs/project-guide/templates/modes/code-velocity-mode.md +0 -58
  105. learningfoundry-0.2.0/docs/project-guide/templates/modes/debug-mode.md +0 -462
  106. learningfoundry-0.2.0/docs/project-guide/templates/modes/default-mode.md +0 -100
  107. learningfoundry-0.2.0/docs/project-guide/templates/modes/document-brand-mode.md +0 -484
  108. learningfoundry-0.2.0/docs/project-guide/templates/modes/document-landing-mode.md +0 -808
  109. learningfoundry-0.2.0/docs/project-guide/templates/modes/plan-concept-mode.md +0 -50
  110. learningfoundry-0.2.0/docs/project-guide/templates/modes/plan-features-mode.md +0 -57
  111. learningfoundry-0.2.0/docs/project-guide/templates/modes/plan-phase-mode.md +0 -75
  112. learningfoundry-0.2.0/docs/project-guide/templates/modes/plan-stories-mode.md +0 -73
  113. learningfoundry-0.2.0/docs/project-guide/templates/modes/plan-tech-spec-mode.md +0 -74
  114. learningfoundry-0.2.0/docs/project-guide/templates/modes/production-mode.md +0 -16
  115. learningfoundry-0.2.0/docs/project-guide/templates/modes/project-scaffold-mode.md +0 -139
  116. learningfoundry-0.2.0/docs/project-guide/templates/modes/refactor-document-mode.md +0 -104
  117. learningfoundry-0.2.0/docs/project-guide/templates/modes/refactor-plan-mode.md +0 -123
  118. learningfoundry-0.2.0/docs/specs/concept.md +0 -140
  119. learningfoundry-0.2.0/docs/specs/d3foundry/dependency-spec.md +0 -334
  120. learningfoundry-0.2.0/docs/specs/d802-curriculum-idea-statement.md +0 -250
  121. learningfoundry-0.2.0/docs/specs/features.md +0 -344
  122. learningfoundry-0.2.0/docs/specs/idea.md +0 -33
  123. learningfoundry-0.2.0/docs/specs/lmentry/features.md +0 -282
  124. learningfoundry-0.2.0/docs/specs/nbfoundry/dependency-spec.md +0 -356
  125. learningfoundry-0.2.0/docs/specs/project-essentials.md +0 -75
  126. learningfoundry-0.2.0/docs/specs/quizazz/concept.md +0 -118
  127. learningfoundry-0.2.0/docs/specs/quizazz/dependency-spec.md +0 -191
  128. learningfoundry-0.2.0/docs/specs/quizazz/features.md +0 -382
  129. learningfoundry-0.2.0/docs/specs/quizazz/tech_spec.md +0 -641
  130. learningfoundry-0.2.0/docs/specs/stories.md +0 -394
  131. learningfoundry-0.2.0/docs/specs/tech-spec.md +0 -1099
  132. learningfoundry-0.2.0/pyproject.toml +0 -43
  133. learningfoundry-0.2.0/src/learningfoundry/cli.py +0 -12
  134. {learningfoundry-0.2.0 → learningfoundry-0.27.0}/LICENSE +0 -0
  135. {learningfoundry-0.2.0 → learningfoundry-0.27.0}/docs/project-guide/README.md +0 -0
  136. {learningfoundry-0.2.0 → learningfoundry-0.27.0}/src/learningfoundry/__main__.py +0 -0
  137. /learningfoundry-0.2.0/src/learningfoundry/py.typed → /learningfoundry-0.27.0/src/learningfoundry/integrations/__init__.py +0 -0
@@ -20,13 +20,11 @@ build/
20
20
  *.ipynb_checkpoints
21
21
 
22
22
  # Pyve virtual environment
23
+ .pyve/envs
23
24
  .pyve/testenv
24
25
  .envrc
25
26
  .env
26
- .venv
27
-
28
-
29
-
27
+ .vscode/settings.json
30
28
 
31
29
  # Node / SvelteKit
32
30
  node_modules/
@@ -34,6 +32,7 @@ node_modules/
34
32
  .output/
35
33
  .vercel/
36
34
  .netlify/
35
+ **/sveltekit_template/static/sql-wasm.wasm
37
36
 
38
37
  # TypeScript
39
38
  *.tsbuildinfo
@@ -0,0 +1,313 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.27.0] - 2026-04-15
11
+
12
+ ### Added
13
+
14
+ - `requirements-dev.txt` — added `pytest-cov>=7.0`
15
+ - `pyproject.toml` — `[tool.coverage.run]` (source, omit sveltekit_template) and `[tool.coverage.report]` (exclude_lines)
16
+ - `.github/workflows/ci.yml` — test job now runs `--cov=src/learningfoundry --cov-report=xml`; uploads `coverage.xml` to Codecov via `codecov/codecov-action@v4` (`fail_ci_if_error: false`)
17
+ - `README.md` — CI status badge and Codecov coverage badge
18
+
19
+ ### Verified
20
+
21
+ - Local coverage run: **95%** (458 statements, 25 missed)
22
+
23
+ ## [0.26.0] - 2026-04-15
24
+
25
+ ### Added
26
+
27
+ - `.github/workflows/ci.yml` — CI workflow triggered on push/PR to `main`:
28
+ - `lint` job: Python 3.12, installs `requirements-dev.txt`, runs `ruff check .` then `mypy src/`
29
+ - `test` job: Python 3.12, installs package + dev deps, runs `pytest` (smoke tests excluded)
30
+ - Jobs run in parallel; standard `actions/setup-python` used (pyve is local-only tooling)
31
+
32
+ ## [0.25.0] - 2026-04-15
33
+
34
+ ### Added
35
+
36
+ - `pyproject.toml` — `readme`, `keywords`, `classifiers` (Beta, Apache, Python 3.12, Education, Code Generators, Typed), `[project.urls]` (Homepage, Repository, Bug Tracker, Changelog), `[tool.hatch.build.targets.sdist]` include list
37
+ - `src/learningfoundry/sveltekit_template/` — template copied into package so it ships in the wheel
38
+
39
+ ### Fixed
40
+
41
+ - `src/learningfoundry/generator.py` — `_TEMPLATE_DIR` now uses `Path(__file__).parent / "sveltekit_template"` (was `../../../sveltekit_template`); template now resolves correctly in installed environments
42
+
43
+ ### Verified
44
+
45
+ - `pyve run hatch build` — `dist/learningfoundry-0.25.0-py3-none-any.whl` and `.tar.gz` produced
46
+ - Wheel contains 31 `sveltekit_template/` files
47
+ - `pip install dist/learningfoundry-0.25.0-py3-none-any.whl` in clean venv — `learningfoundry --version` → `0.25.0`; `_TEMPLATE_DIR.exists()` → `True`
48
+ - `pyve test -q` — 195 passed
49
+
50
+ ## [0.24.0] - 2026-04-15
51
+
52
+ ### Added
53
+
54
+ - `README.md` — full user-facing documentation: overview, installation, quick start, CLI reference (build/validate/preview with all flags and exit codes), curriculum YAML format with all 5 block types, configuration file reference, development setup, project structure
55
+
56
+ ## [0.23.0] - 2026-04-15
57
+
58
+ ### Added
59
+
60
+ - `tests/test_smoke_sveltekit.py` — 6 end-to-end smoke tests (marked `smoke`, excluded from default `pyve test` run):
61
+ - `test_pnpm_install_succeeds` — `node_modules/` created
62
+ - `test_pnpm_build_produces_build_dir` — `build/` directory exists
63
+ - `test_build_produces_index_html` — `build/index.html` present
64
+ - `test_curriculum_json_present_in_build` — `build/curriculum.json` copied by vite
65
+ - `test_curriculum_json_valid_in_build` — JSON is valid with 2 modules
66
+ - `test_build_contains_js_assets` — at least one `.js` file in build output
67
+ - `pyproject.toml` — registered `smoke` marker; smoke file excluded from `addopts` so `pyve test` stays fast
68
+ - Smoke tests use `scope="module"` fixtures so `pnpm install` + `pnpm build` run once per session
69
+
70
+ ### Verified
71
+
72
+ - `pyve test tests/test_smoke_sveltekit.py -v` — 6 passed in ~13 s
73
+ - `pyve test -q` — 195 passed (smoke excluded, fast)
74
+ - `ruff check .` and `mypy src/` — clean
75
+
76
+ ## [0.22.0] - 2026-04-15
77
+
78
+ ### Added
79
+
80
+ - `pyproject.toml [tool.mypy]` — `strict = true`, `python_version = "3.12"`, `[[tool.mypy.overrides]]` for `quizazz_builder` (`ignore_missing_imports = true`)
81
+ - `pyproject.toml [tool.ruff.lint]` — expanded select to `["E", "F", "I", "UP", "W", "B"]` (adds pycodestyle warnings + flake8-bugbear)
82
+ - Installed `mypy` and `types-PyYAML` into testenv
83
+
84
+ ### Fixed
85
+
86
+ - `src/learningfoundry/integrations/quizazz.py` — removed stale `# type: ignore[import-untyped]`; now covered by mypy overrides
87
+ - `scripts/spike_e2e.py` — removed unused `shutil` import
88
+
89
+ ### Verified
90
+
91
+ - `pyve testenv run ruff check .` — 0 errors (with W + B rules)
92
+ - `pyve testenv run mypy src/` — 0 errors (16 source files, strict)
93
+ - `pyve test -q` — 195 passed
94
+
95
+ ## [0.21.0] - 2026-04-15
96
+
97
+ ### Added
98
+
99
+ - `tests/test_edge_cases.py` — 22 new tests across 6 classes:
100
+ - `TestEmptyCurriculum` — schema rejects empty modules/lessons; generator handles zero-module `ResolvedCurriculum`; `run_validate` returns False for empty-module YAML; lesson-with-no-blocks resolves fine
101
+ - `TestAllBlockTypesTogether` — all 5 block types resolved in order; all are `ResolvedContentBlock`; `curriculum.json` contains all 5 types
102
+ - `TestLargeCurriculum` — 5 modules × 4 lessons; all modules/lessons resolved; generated JSON counts correct; spot-check text content
103
+ - `TestIntegrationRunBuild` — full `run_build` with fixture curriculum (all block types) through real generator; `curriculum.json` has 2 modules; mod-01 has all 5 block types; `package.json` present
104
+ - `TestValidateResolutionErrors` — missing text-block file returns False with error; error message includes location context
105
+ - `TestOptionalFields` — missing `description` defaults to `""`; missing assessments resolve to `None`
106
+
107
+ ### Verified
108
+
109
+ - `pyve test` — 195 passed, 0 failed
110
+
111
+ ## [0.20.0] - 2026-04-15
112
+
113
+ ### Added
114
+
115
+ - `src/learningfoundry/cli.py` — `preview` subcommand: calls `run_preview()`, accepts `--port` (default 5173), prints `http://localhost:{port}` on success; same error/exit-code handling as `build`
116
+ - `tests/test_cli.py` — 6 new preview tests: help, delegation to `run_preview`, URL output, default port, validation error exit, generation error exit (21 total CLI tests)
117
+
118
+ ## [0.19.0] - 2026-04-15
119
+
120
+ ### Added
121
+
122
+ - `src/learningfoundry/cli.py` — `build` subcommand (parse→resolve→generate, `--config`, `--output`, `--base-dir`, `--log-level`); `validate` subcommand (parse→resolve only, reports OK/errors); exit codes 1=validation, 2=resolution, 3=generation, 4=config
123
+ - `tests/test_cli.py` — 15 tests: `--help`/`--version`, build success/error paths, validate OK/invalid/missing/config-error
124
+ - `tests/conftest.py` — `reset_learningfoundry_logger` autouse fixture; fixes caplog isolation across all test modules
125
+
126
+ ### Fixed
127
+
128
+ - Cross-module `caplog` interference caused by `setup_logging()` leaving handlers on the `learningfoundry` logger — now reset after every test via `conftest.py`
129
+
130
+ ## [0.18.0] - 2026-04-15
131
+
132
+ ### Added
133
+
134
+ - `sveltekit_template/src/lib/components/LessonView.svelte` — renders all content blocks for a lesson, marks lesson in-progress on mount, marks complete on nav-next, propagates quiz scores
135
+ - `sveltekit_template/src/routes/+layout.svelte` — app shell: sidebar (`ModuleList`) + main content slot; loads module progress from SQLite reactively
136
+ - `sveltekit_template/src/routes/+page.svelte` — landing page with `ProgressDashboard`
137
+ - `sveltekit_template/src/routes/[module]/[lesson]/+page.svelte` — lesson route; syncs URL params to curriculum store; renders `LessonView`
138
+
139
+ ### Verified
140
+
141
+ - `pnpm exec svelte-check` — 0 errors, 0 warnings
142
+ - `pnpm build` — full adapter-static build succeeds; all routes compiled
143
+
144
+ ## [0.17.0] - 2026-04-15
145
+
146
+ ### Added
147
+
148
+ - `sveltekit_template/src/lib/components/ProgressBar.svelte` — accessible progress bar with clamped percent and optional label
149
+ - `sveltekit_template/src/lib/components/LessonList.svelte` — lesson list with status icons (✓/…/○) and active highlight
150
+ - `sveltekit_template/src/lib/components/ModuleList.svelte` — collapsible module sidebar with per-module `ProgressBar`; auto-expands active module via `$effect`
151
+ - `sveltekit_template/src/lib/components/Navigation.svelte` — prev/next lesson buttons using `lucide-svelte` chevrons; "Finish" on last lesson; fires `onComplete`
152
+ - `sveltekit_template/src/lib/components/ProgressDashboard.svelte` — overall + per-module progress bars, pre/post assessment scores, start/continue/complete actions
153
+
154
+ ### Verified
155
+
156
+ - `pnpm exec svelte-check` — 0 errors, 0 warnings
157
+
158
+ ## [0.16.0] - 2026-04-15
159
+
160
+ ### Added
161
+
162
+ - `sveltekit_template/src/lib/utils/markdown.ts` — `renderMarkdown()` using `marked`
163
+ - `sveltekit_template/src/lib/components/PlaceholderBlock.svelte` — generic "coming soon" placeholder
164
+ - `sveltekit_template/src/lib/components/TextBlock.svelte` — renders markdown via `{@html}` with `$derived`
165
+ - `sveltekit_template/src/lib/components/VideoBlock.svelte` — YouTube embed (converts watch/youtu.be URLs to embed URLs)
166
+ - `sveltekit_template/src/lib/components/QuizBlock.svelte` — quizazz manifest placeholder; writes score to SQLite on complete
167
+ - `sveltekit_template/src/lib/components/ExerciseBlock.svelte` — renders exercise content or stub placeholder
168
+ - `sveltekit_template/src/lib/components/VisualizationBlock.svelte` — renders SVG/image or stub placeholder
169
+ - `sveltekit_template/src/lib/components/ContentBlock.svelte` — type dispatcher for all block types
170
+ - `sveltekit_template/package.json` — added `marked ^18.0.0`
171
+
172
+ ### Verified
173
+
174
+ - `pnpm exec svelte-check` — 0 errors, 0 warnings
175
+
176
+ ## [0.15.0] - 2026-04-15
177
+
178
+ ### Added
179
+
180
+ - `sveltekit_template/src/lib/db/database.ts` — sql.js init with WASM locator, IndexedDB persistence (`getDb()`, `persistDb()`), DDL for `lesson_progress`, `quiz_scores`, `exercise_status`
181
+ - `sveltekit_template/src/lib/db/progress.ts` — `markLessonComplete()`, `markLessonInProgress()`, `getLessonProgress()`, `saveQuizScore()`, `getQuizScore()`, `updateExerciseStatus()`, `getModuleProgress()`
182
+ - `sveltekit_template/src/lib/db/index.ts` — barrel re-export
183
+ - `sveltekit_template/package.json` — `postinstall` script copies `sql-wasm.wasm` to `static/`; added `@types/sql.js ^1.4.11`
184
+ - `sveltekit_template/static/.gitkeep` — tracks static dir in git
185
+ - `.gitignore` — ignores `sveltekit_template/static/sql-wasm.wasm`
186
+
187
+ ### Verified
188
+
189
+ - `pnpm exec svelte-check` — 0 errors
190
+
191
+ ## [0.14.0] - 2026-04-15
192
+
193
+ ### Added
194
+
195
+ - `sveltekit_template/src/lib/types/index.ts` — all TypeScript interfaces: `TextContent`, `VideoContent`, `QuizManifest`, `QuizQuestion`, `QuizAnswer`, `ExerciseContent`, `VisualizationContent`, `ContentBlock`, `Lesson`, `Module`, `Curriculum`, `LessonProgress`, `QuizScore`, `ModuleProgress`, `CurriculumProgress`
196
+ - `sveltekit_template/src/lib/stores/curriculum.ts` — `curriculum` readable (loads `curriculum.json`), `currentPosition` writable, derived stores `modules`, `currentModule`, `currentLesson`, `lessonSequence`, `currentIndex`, `previousLesson`, `nextLesson`, and `navigateTo/navigateNext/navigatePrev` helpers
197
+
198
+ ### Verified
199
+
200
+ - `pnpm exec svelte-kit sync && pnpm exec svelte-check` — 0 errors
201
+
202
+ ## [0.13.0] - 2026-04-15
203
+
204
+ ### Added
205
+
206
+ - `sveltekit_template/package.json` — full deps: `svelte@^5`, `@sveltejs/kit@^2`, `@sveltejs/adapter-static@^3`, `sql.js`, `lucide-svelte`; devDeps: `typescript`, `tailwindcss@^4`, `@tailwindcss/vite`, `vite@^8`, `@sveltejs/vite-plugin-svelte@^7`, `vitest@^3`, `prettier`, `prettier-plugin-svelte`, `svelte-check`
207
+ - `sveltekit_template/svelte.config.js` — `adapter-static` with `vitePreprocess()`, `fallback: 'index.html'`
208
+ - `sveltekit_template/vite.config.ts` — `tailwindcss()` + `sveltekit()` plugins
209
+ - `sveltekit_template/tsconfig.json` — strict TypeScript config extending `.svelte-kit/tsconfig.json`
210
+ - `sveltekit_template/src/app.html` — SvelteKit shell with `%sveltekit.head%` and `%sveltekit.body%`
211
+ - `sveltekit_template/src/app.css` — Tailwind v4 `@import 'tailwindcss'`
212
+
213
+ ### Verified
214
+
215
+ - `pnpm install && pnpm build` succeeds in `sveltekit_template/` (vite 8.0.8, adapter-static output to `build/`)
216
+
217
+ ## [0.12.0] - 2026-04-15
218
+
219
+ ### Added
220
+
221
+ - `src/learningfoundry/generator.py` — `generate_app()`: atomically copies `sveltekit_template/` to output dir, writes `curriculum.json` to `static/`; overwrites with warning; raises `GenerationError` if template is missing
222
+ - `sveltekit_template/package.json`, `sveltekit_template/svelte.config.js` — minimal template stubs (expanded in D.a)
223
+ - `tests/test_generator.py` — 11 tests covering output structure, `curriculum.json` content, overwrite behavior, and missing template
224
+
225
+ ### Fixed
226
+
227
+ - `tests/test_exceptions.py` — added `teardown_method` to `TestLoggingSetup` to restore `learningfoundry` logger state, fixing `caplog` interference in cross-module test runs
228
+
229
+ ## [0.11.0] - 2026-04-15
230
+
231
+ ### Added
232
+
233
+ - `src/learningfoundry/pipeline.py` — `run_build()`, `run_validate()`, `run_preview()` orchestrating parse → resolve → generate; `run_validate()` returns `(bool, list[str])` without generating; `run_preview()` runs `pnpm install` + `pnpm run dev`
234
+ - `tests/test_pipeline.py` — 11 tests covering end-to-end build, generator injection, error propagation, validate-only mode, and error capture
235
+ - `tests/fixtures/content/mod-01/lesson-01.md`, `tests/fixtures/content/mod-02/lesson-02.md` — stub markdown content for fixture curriculum
236
+
237
+ ## [0.10.0] - 2026-04-15
238
+
239
+ ### Added
240
+
241
+ - `src/learningfoundry/resolver.py` — `resolve_curriculum()` with `ResolvedCurriculum`, `ResolvedModule`, `ResolvedLesson`, `ResolvedContentBlock` dataclasses; resolves text (markdown read), video (URL pass-through), quiz/exercise/visualization (provider delegation), and pre/post assessments; raises `ContentResolutionError` with block location context
242
+ - `tests/test_resolver.py` — 16 tests covering all block types, missing files, empty markdown warning, provider delegation, error wrapping with location, and assessment resolution
243
+
244
+ ## [0.9.0] - 2026-04-15
245
+
246
+ ### Added
247
+
248
+ - `src/learningfoundry/integrations/quizazz.py` — `QuizazzProvider` delegating to `quizazz_builder.compile_assessment()`; wraps all errors in `IntegrationError`; raises `ImportError` with install instructions if `quizazz-builder` is not installed
249
+ - `tests/test_integrations/test_quizazz.py` — 8 tests covering delegation, return value, error wrapping, error chaining, and missing package
250
+
251
+ ## [0.8.0] - 2026-04-15
252
+
253
+ ### Added
254
+
255
+ - `src/learningfoundry/integrations/__init__.py` — integrations package
256
+ - `src/learningfoundry/integrations/protocols.py` — `QuizProvider`, `ExerciseProvider`, `VisualizationProvider` Protocol classes
257
+ - `src/learningfoundry/integrations/nbfoundry_stub.py` — `NbfoundryStub` returning placeholder `ExerciseContent` dict with `"status": "stub"`
258
+ - `src/learningfoundry/integrations/d3foundry_stub.py` — `D3foundryStub` returning placeholder `VisualizationContent` dict with `"status": "stub"`
259
+ - `tests/test_integrations/test_nbfoundry_stub.py` — 12 tests verifying stub structure matches `ExerciseContent` TypeScript interface
260
+ - `tests/test_integrations/test_d3foundry_stub.py` — 13 tests verifying stub structure matches `VisualizationContent` TypeScript interface
261
+
262
+ ## [0.7.0] - 2026-04-15
263
+
264
+ ### Added
265
+
266
+ - `src/learningfoundry/parser.py` — `parse_curriculum()` and `_dispatch_parser()`: loads YAML, extracts version, dispatches to schema, raises `CurriculumVersionError` / `CurriculumValidationError` on failure
267
+ - `tests/test_parser.py` — 13 tests covering valid parsing, missing/null/unsupported/malformed version, malformed YAML, schema errors, and missing file
268
+
269
+ ## [0.6.0] - 2026-04-15
270
+
271
+ ### Added
272
+
273
+ - `src/learningfoundry/schema_v1.py` — Pydantic v1 curriculum schema: all block types (`TextBlock`, `VideoBlock`, `QuizBlock`, `ExerciseBlock`, `VisualizationBlock`), `Lesson`, `Module`, `CurriculumDef`, `CurriculumV1` with validators for IDs, YouTube URLs, uniqueness, and minimum counts
274
+ - `tests/fixtures/valid-curriculum.yml` — full fixture curriculum exercising all block types and assessments
275
+ - `tests/test_schema_v1.py` — 35 tests covering valid parsing, all block types, invalid URLs, ID format, duplicate IDs, and missing required fields
276
+
277
+ ## [0.5.0] - 2026-04-15
278
+
279
+ ### Added
280
+
281
+ - `src/learningfoundry/config.py` — `LoggingConfig`, `AppConfig` dataclasses and `load_config()` with CLI > config file > defaults precedence
282
+ - `tests/test_config.py` — 16 tests covering defaults, file overrides, CLI overrides, malformed YAML, and unknown key warnings
283
+
284
+ ## [0.4.0] - 2026-04-15
285
+
286
+ ### Added
287
+
288
+ - `src/learningfoundry/exceptions.py` — full exception hierarchy: `LearningFoundryError`, `ConfigError`, `CurriculumVersionError`, `CurriculumValidationError`, `ContentResolutionError`, `IntegrationError`, `GenerationError`
289
+ - `src/learningfoundry/logging_config.py` — `setup_logging(level, output)` using stdlib `logging`
290
+ - `tests/test_exceptions.py` — 17 tests covering hierarchy, string representations, and logging setup
291
+
292
+ ## [0.3.0] - 2026-04-15
293
+
294
+ ### Added
295
+
296
+ - `scripts/spike_e2e.py` — throwaway end-to-end spike: YAML parse → content resolve → SvelteKit skeleton generation
297
+ - `scripts/fixtures/spike-curriculum.yml` — minimal 1-module/1-lesson fixture curriculum
298
+ - `scripts/fixtures/content/lesson-01.md` — stub markdown content for the spike
299
+
300
+ ## [0.2.0] - 2026-04-15
301
+
302
+ ### Added
303
+
304
+ - `.github/workflows/publish.yml` — publishes sdist + wheel to PyPI on `v*` tag push via OIDC trusted publishing
305
+
306
+ ## [0.1.0] - 2026-04-15
307
+
308
+ ### Added
309
+
310
+ - `src/learningfoundry/__init__.py` with `__version__ = "0.1.0"`
311
+ - `src/learningfoundry/py.typed` PEP 561 marker
312
+ - `src/learningfoundry/cli.py` Click entry point with `--version` flag
313
+ - `src/learningfoundry/__main__.py` enabling `python -m learningfoundry`
@@ -0,0 +1,348 @@
1
+ Metadata-Version: 2.4
2
+ Name: learningfoundry
3
+ Version: 0.27.0
4
+ Summary: A curriculum engine that turns a YAML curriculum definition into a deployable SvelteKit learning application.
5
+ Project-URL: Homepage, https://github.com/pointmatic/learningfoundry
6
+ Project-URL: Repository, https://github.com/pointmatic/learningfoundry
7
+ Project-URL: Bug Tracker, https://github.com/pointmatic/learningfoundry/issues
8
+ Project-URL: Changelog, https://github.com/pointmatic/learningfoundry/blob/main/CHANGELOG.md
9
+ Author: Pointmatic
10
+ License-Expression: Apache-2.0
11
+ License-File: LICENSE
12
+ Keywords: curriculum,education,elearning,lms,sveltekit
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Intended Audience :: Education
16
+ Classifier: License :: OSI Approved :: Apache Software License
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Topic :: Education
20
+ Classifier: Topic :: Software Development :: Code Generators
21
+ Classifier: Typing :: Typed
22
+ Requires-Python: >=3.12
23
+ Requires-Dist: click>=8.1
24
+ Requires-Dist: pydantic>=2.0
25
+ Requires-Dist: pyyaml>=6.0
26
+ Provides-Extra: quizazz
27
+ Requires-Dist: quizazz-builder>=0.1; extra == 'quizazz'
28
+ Description-Content-Type: text/markdown
29
+
30
+ # learningfoundry
31
+
32
+ [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE)
33
+ [![Python](https://img.shields.io/badge/python-3.12%2B-blue)](https://www.python.org)
34
+ [![CI](https://github.com/pointmatic/learningfoundry/actions/workflows/ci.yml/badge.svg)](https://github.com/pointmatic/learningfoundry/actions/workflows/ci.yml)
35
+ [![codecov](https://codecov.io/gh/pointmatic/learningfoundry/branch/main/graph/badge.svg)](https://codecov.io/gh/pointmatic/learningfoundry)
36
+
37
+ A curriculum engine that turns a YAML curriculum definition into a deployable SvelteKit learning application — with interactive assessments, executable notebooks, and data visualizations — in a single pipeline.
38
+
39
+ ---
40
+
41
+ ## Table of Contents
42
+
43
+ - [Overview](#overview)
44
+ - [Installation](#installation)
45
+ - [Quick Start](#quick-start)
46
+ - [CLI Reference](#cli-reference)
47
+ - [Curriculum YAML Format](#curriculum-yaml-format)
48
+ - [Configuration File](#configuration-file)
49
+ - [Development Setup](#development-setup)
50
+
51
+ ---
52
+
53
+ ## Overview
54
+
55
+ `learningfoundry` takes a single `curriculum.yml` file and generates a fully self-contained [SvelteKit](https://kit.svelte.dev/) learning application. The generated app supports:
56
+
57
+ - **Text** — Markdown content rendered in the browser
58
+ - **Video** — YouTube embeds
59
+ - **Quiz** — Interactive assessments via [quizazz-builder](https://github.com/pointmatic/quizazz-builder) (optional)
60
+ - **Exercise** — Executable notebooks via nbfoundry (stub provided)
61
+ - **Visualization** — D3-based charts via d3foundry (stub provided)
62
+
63
+ Learner progress is persisted locally in SQLite (via sql.js) — no backend required.
64
+
65
+ ---
66
+
67
+ ## Installation
68
+
69
+ ```bash
70
+ pip install learningfoundry
71
+ ```
72
+
73
+ **With optional quizazz support:**
74
+
75
+ ```bash
76
+ pip install "learningfoundry[quizazz]"
77
+ ```
78
+
79
+ **Requirements:**
80
+
81
+ - Python 3.12+
82
+ - [pnpm](https://pnpm.io) (for `preview` command and generated app development)
83
+ - Node.js 18+ (for the generated SvelteKit app)
84
+
85
+ ---
86
+
87
+ ## Quick Start
88
+
89
+ 1. **Create a curriculum file** (see [Curriculum YAML Format](#curriculum-yaml-format)):
90
+
91
+ ```bash
92
+ cat > curriculum.yml << 'EOF'
93
+ version: "1.0.0"
94
+ curriculum:
95
+ title: "My Course"
96
+ description: "A short description."
97
+ modules:
98
+ - id: mod-01
99
+ title: "Module One"
100
+ lessons:
101
+ - id: lesson-01
102
+ title: "Getting Started"
103
+ content_blocks:
104
+ - type: text
105
+ ref: content/lesson-01.md
106
+ - type: video
107
+ url: "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
108
+ EOF
109
+ ```
110
+
111
+ 2. **Validate** the curriculum:
112
+
113
+ ```bash
114
+ learningfoundry validate
115
+ # OK — curriculum is valid.
116
+ ```
117
+
118
+ 3. **Build** the SvelteKit app:
119
+
120
+ ```bash
121
+ learningfoundry build
122
+ # Build complete → dist/
123
+ ```
124
+
125
+ 4. **Preview** locally (builds then starts a dev server):
126
+
127
+ ```bash
128
+ learningfoundry preview
129
+ # Preview server started at http://localhost:5173
130
+ ```
131
+
132
+ ---
133
+
134
+ ## CLI Reference
135
+
136
+ ### `learningfoundry build`
137
+
138
+ Parse → resolve → generate a SvelteKit project.
139
+
140
+ ```
141
+ Usage: learningfoundry build [OPTIONS]
142
+
143
+ Options:
144
+ -c, --config PATH Path to the curriculum YAML file. [default: curriculum.yml]
145
+ --log-level LEVEL Logging verbosity. [default: INFO]
146
+ Choices: DEBUG, INFO, WARNING, ERROR
147
+ -o, --output PATH Output directory for the generated SvelteKit project.
148
+ [default: dist]
149
+ --base-dir PATH Base directory for content refs.
150
+ (default: curriculum file's parent directory)
151
+ --help Show this message and exit.
152
+ ```
153
+
154
+ **Exit codes:**
155
+
156
+ | Code | Meaning |
157
+ |------|---------|
158
+ | 0 | Success |
159
+ | 1 | Curriculum validation error |
160
+ | 2 | Content resolution error (missing file, bad URL, etc.) |
161
+ | 3 | SvelteKit generation error |
162
+ | 4 | Configuration file error |
163
+
164
+ ---
165
+
166
+ ### `learningfoundry validate`
167
+
168
+ Validate a curriculum YAML without generating any output.
169
+
170
+ ```
171
+ Usage: learningfoundry validate [OPTIONS]
172
+
173
+ Options:
174
+ -c, --config PATH Path to the curriculum YAML file. [default: curriculum.yml]
175
+ --log-level LEVEL Logging verbosity. [default: INFO]
176
+ --base-dir PATH Base directory for resolving content refs.
177
+ --help Show this message and exit.
178
+ ```
179
+
180
+ Prints `OK — curriculum is valid.` on success, or a list of errors and exits with code 1.
181
+
182
+ ---
183
+
184
+ ### `learningfoundry preview`
185
+
186
+ Build then launch a local Vite dev server.
187
+
188
+ ```
189
+ Usage: learningfoundry preview [OPTIONS]
190
+
191
+ Options:
192
+ -c, --config PATH Path to the curriculum YAML file. [default: curriculum.yml]
193
+ --log-level LEVEL Logging verbosity. [default: INFO]
194
+ -o, --output PATH Output directory for the generated SvelteKit project.
195
+ [default: dist]
196
+ --base-dir PATH Base directory for content refs.
197
+ --port INTEGER Port for the local dev server. [default: 5173]
198
+ --help Show this message and exit.
199
+ ```
200
+
201
+ Runs `pnpm install` and `pnpm run dev` in the generated project directory. Requires `pnpm` on `PATH`.
202
+
203
+ ---
204
+
205
+ ## Curriculum YAML Format
206
+
207
+ ```yaml
208
+ version: "1.0.0"
209
+
210
+ curriculum:
211
+ title: "Course Title" # required
212
+ description: "Course overview." # optional
213
+
214
+ modules:
215
+ - id: mod-01 # required, kebab-case
216
+ title: "Module One" # required
217
+ description: "..." # optional
218
+
219
+ # Optional pre/post assessments (requires quizazz-builder)
220
+ pre_assessment:
221
+ source: quizazz
222
+ ref: assessments/mod-01-pre.yml
223
+
224
+ post_assessment:
225
+ source: quizazz
226
+ ref: assessments/mod-01-post.yml
227
+
228
+ lessons:
229
+ - id: lesson-01 # required, kebab-case; unique within module
230
+ title: "Lesson One" # required
231
+
232
+ content_blocks:
233
+
234
+ # Text block — Markdown file
235
+ - type: text
236
+ ref: content/mod-01/lesson-01.md
237
+
238
+ # Video block — YouTube URL only
239
+ - type: video
240
+ url: "https://www.youtube.com/watch?v=XXXXXXXXXXX"
241
+
242
+ # Quiz block — requires learningfoundry[quizazz]
243
+ - type: quiz
244
+ source: quizazz
245
+ ref: assessments/mod-01-quiz.yml
246
+
247
+ # Exercise block — requires nbfoundry (stub included)
248
+ - type: exercise
249
+ source: nbfoundry
250
+ ref: exercises/mod-01-exercise.yml
251
+
252
+ # Visualization block — requires d3foundry (stub included)
253
+ - type: visualization
254
+ source: d3foundry
255
+ ref: visualizations/mod-01-vis.yml
256
+ ```
257
+
258
+ **Rules:**
259
+
260
+ - Module and lesson `id` values must be unique within their scope, and match the pattern `[a-z0-9][a-z0-9-]*`.
261
+ - Every curriculum must have at least one module; every module at least one lesson.
262
+ - All `ref` paths are resolved relative to `--base-dir` (default: directory containing the curriculum YAML).
263
+ - Only YouTube URLs are accepted for `video` blocks (`youtube.com/watch?v=` or `youtu.be/`).
264
+
265
+ ---
266
+
267
+ ## Configuration File
268
+
269
+ An optional config file can set defaults for logging. The CLI always takes precedence.
270
+
271
+ **Default location:** `~/.config/learningfoundry/config.yml`
272
+
273
+ ```yaml
274
+ logging:
275
+ level: INFO # DEBUG | INFO | WARNING | ERROR
276
+ output: stdout # stdout | stderr
277
+ ```
278
+
279
+ Pass a custom config location with `-c / --config`.
280
+
281
+ ---
282
+
283
+ ## Development Setup
284
+
285
+ ### Prerequisites
286
+
287
+ - Python 3.12+
288
+ - [pyve](https://github.com/pointmatic/pyve) (virtual env manager used in this project)
289
+ - pnpm 9+ and Node.js 18+
290
+
291
+ ### Setup
292
+
293
+ ```bash
294
+ git clone https://github.com/pointmatic/learningfoundry.git
295
+ cd learningfoundry
296
+
297
+ # Create the Python environment and install the package in editable mode
298
+ pyve init
299
+ pip install -e .
300
+
301
+ # Create the test runner environment and install dev dependencies
302
+ pyve testenv --init
303
+ pyve testenv --install -r requirements-dev.txt
304
+ ```
305
+
306
+ ### Running Tests
307
+
308
+ ```bash
309
+ # Fast unit + integration tests (~2 min)
310
+ pyve test
311
+
312
+ # End-to-end SvelteKit smoke tests (requires pnpm, ~15 s extra)
313
+ pyve test tests/test_smoke_sveltekit.py -v
314
+ ```
315
+
316
+ ### Linting and Type Checking
317
+
318
+ ```bash
319
+ pyve testenv run ruff check .
320
+ pyve testenv run mypy src/
321
+ ```
322
+
323
+ ### Project Structure
324
+
325
+ ```
326
+ learningfoundry/
327
+ ├── src/learningfoundry/
328
+ │ ├── cli.py # Click CLI entry point
329
+ │ ├── config.py # Configuration loading
330
+ │ ├── exceptions.py # Exception hierarchy
331
+ │ ├── generator.py # SvelteKit project generator
332
+ │ ├── integrations/ # Quiz / exercise / visualization providers
333
+ │ ├── logging_config.py # Logging setup
334
+ │ ├── parser.py # YAML parser + version dispatch
335
+ │ ├── pipeline.py # run_build / run_validate / run_preview
336
+ │ ├── resolver.py # Content reference resolver
337
+ │ └── schema_v1.py # Pydantic v1 curriculum schema
338
+ ├── sveltekit_template/ # SvelteKit app template (copied on build)
339
+ ├── tests/ # pytest test suite
340
+ ├── requirements-dev.txt # Dev dependencies
341
+ └── pyproject.toml # Build config, ruff, mypy, pytest settings
342
+ ```
343
+
344
+ ---
345
+
346
+ ## License
347
+
348
+ Apache 2.0 — see [LICENSE](LICENSE).