fitdocs 0.1.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 (123) hide show
  1. fitdocs-0.1.0/.gitignore +44 -0
  2. fitdocs-0.1.0/CHANGELOG.md +54 -0
  3. fitdocs-0.1.0/LICENSE +21 -0
  4. fitdocs-0.1.0/PKG-INFO +249 -0
  5. fitdocs-0.1.0/README.md +212 -0
  6. fitdocs-0.1.0/pyproject.toml +168 -0
  7. fitdocs-0.1.0/src/fitdocs/__init__.py +125 -0
  8. fitdocs-0.1.0/src/fitdocs/agentskill.py +105 -0
  9. fitdocs-0.1.0/src/fitdocs/athlete.py +183 -0
  10. fitdocs-0.1.0/src/fitdocs/audit.py +491 -0
  11. fitdocs-0.1.0/src/fitdocs/benchmarks.py +692 -0
  12. fitdocs-0.1.0/src/fitdocs/citation.py +199 -0
  13. fitdocs-0.1.0/src/fitdocs/cli.py +1627 -0
  14. fitdocs-0.1.0/src/fitdocs/config.py +146 -0
  15. fitdocs-0.1.0/src/fitdocs/contract.py +1155 -0
  16. fitdocs-0.1.0/src/fitdocs/declaration.py +593 -0
  17. fitdocs-0.1.0/src/fitdocs/docio.py +95 -0
  18. fitdocs-0.1.0/src/fitdocs/docmerge.py +220 -0
  19. fitdocs-0.1.0/src/fitdocs/history/__init__.py +68 -0
  20. fitdocs-0.1.0/src/fitdocs/history/documents.py +223 -0
  21. fitdocs-0.1.0/src/fitdocs/history/engine.py +462 -0
  22. fitdocs-0.1.0/src/fitdocs/history/model.py +135 -0
  23. fitdocs-0.1.0/src/fitdocs/history/page.py +669 -0
  24. fitdocs-0.1.0/src/fitdocs/history/series.py +738 -0
  25. fitdocs-0.1.0/src/fitdocs/history/settings.py +268 -0
  26. fitdocs-0.1.0/src/fitdocs/history/sources.py +463 -0
  27. fitdocs-0.1.0/src/fitdocs/inbox.py +845 -0
  28. fitdocs-0.1.0/src/fitdocs/ingest/__init__.py +133 -0
  29. fitdocs-0.1.0/src/fitdocs/ingest/_fields.py +89 -0
  30. fitdocs-0.1.0/src/fitdocs/ingest/decode.py +105 -0
  31. fitdocs-0.1.0/src/fitdocs/ingest/errors.py +29 -0
  32. fitdocs-0.1.0/src/fitdocs/ingest/laps.py +171 -0
  33. fitdocs-0.1.0/src/fitdocs/ingest/records.py +164 -0
  34. fitdocs-0.1.0/src/fitdocs/ingest/sets.py +183 -0
  35. fitdocs-0.1.0/src/fitdocs/ingest/sport.py +115 -0
  36. fitdocs-0.1.0/src/fitdocs/ingest/summary.py +418 -0
  37. fitdocs-0.1.0/src/fitdocs/layout.py +453 -0
  38. fitdocs-0.1.0/src/fitdocs/load/__init__.py +108 -0
  39. fitdocs-0.1.0/src/fitdocs/load/arbitrate.py +187 -0
  40. fitdocs-0.1.0/src/fitdocs/load/channels/__init__.py +113 -0
  41. fitdocs-0.1.0/src/fitdocs/load/channels/grade.py +267 -0
  42. fitdocs-0.1.0/src/fitdocs/load/channels/heart_rate.py +292 -0
  43. fitdocs-0.1.0/src/fitdocs/load/channels/pace.py +277 -0
  44. fitdocs-0.1.0/src/fitdocs/load/channels/power.py +169 -0
  45. fitdocs-0.1.0/src/fitdocs/load/channels/sources.py +436 -0
  46. fitdocs-0.1.0/src/fitdocs/load/channels/sufficiency.py +150 -0
  47. fitdocs-0.1.0/src/fitdocs/load/channels/types.py +214 -0
  48. fitdocs-0.1.0/src/fitdocs/load/channels/weighting.py +165 -0
  49. fitdocs-0.1.0/src/fitdocs/load/docedit.py +407 -0
  50. fitdocs-0.1.0/src/fitdocs/load/engine.py +677 -0
  51. fitdocs-0.1.0/src/fitdocs/load/priority.py +66 -0
  52. fitdocs-0.1.0/src/fitdocs/load/profile.py +1008 -0
  53. fitdocs-0.1.0/src/fitdocs/load/prompts.py +460 -0
  54. fitdocs-0.1.0/src/fitdocs/load/qa/__init__.py +57 -0
  55. fitdocs-0.1.0/src/fitdocs/load/qa/cadence.py +348 -0
  56. fitdocs-0.1.0/src/fitdocs/load/qa/divergence.py +187 -0
  57. fitdocs-0.1.0/src/fitdocs/load/qa/drift.py +113 -0
  58. fitdocs-0.1.0/src/fitdocs/load/qa/flags.py +281 -0
  59. fitdocs-0.1.0/src/fitdocs/load/qa/sources.py +324 -0
  60. fitdocs-0.1.0/src/fitdocs/load/qa/staleness.py +147 -0
  61. fitdocs-0.1.0/src/fitdocs/load/qa/types.py +141 -0
  62. fitdocs-0.1.0/src/fitdocs/load/registry.py +196 -0
  63. fitdocs-0.1.0/src/fitdocs/load/render.py +468 -0
  64. fitdocs-0.1.0/src/fitdocs/load/settings.py +546 -0
  65. fitdocs-0.1.0/src/fitdocs/load/threshold/__init__.py +23 -0
  66. fitdocs-0.1.0/src/fitdocs/load/threshold/anchors.py +168 -0
  67. fitdocs-0.1.0/src/fitdocs/load/threshold/calculator.py +768 -0
  68. fitdocs-0.1.0/src/fitdocs/load/threshold/discipline.py +117 -0
  69. fitdocs-0.1.0/src/fitdocs/load/threshold/selection.py +140 -0
  70. fitdocs-0.1.0/src/fitdocs/load/types.py +451 -0
  71. fitdocs-0.1.0/src/fitdocs/metrics/__init__.py +160 -0
  72. fitdocs-0.1.0/src/fitdocs/metrics/aggregates.py +393 -0
  73. fitdocs-0.1.0/src/fitdocs/metrics/power.py +364 -0
  74. fitdocs-0.1.0/src/fitdocs/metrics/sources.py +745 -0
  75. fitdocs-0.1.0/src/fitdocs/metrics/stress.py +203 -0
  76. fitdocs-0.1.0/src/fitdocs/metrics/types.py +164 -0
  77. fitdocs-0.1.0/src/fitdocs/metrics/zones.py +75 -0
  78. fitdocs-0.1.0/src/fitdocs/model.py +241 -0
  79. fitdocs-0.1.0/src/fitdocs/performance/__init__.py +26 -0
  80. fitdocs-0.1.0/src/fitdocs/performance/derive.py +787 -0
  81. fitdocs-0.1.0/src/fitdocs/performance/engine.py +556 -0
  82. fitdocs-0.1.0/src/fitdocs/performance/models.py +159 -0
  83. fitdocs-0.1.0/src/fitdocs/performance/sources.py +665 -0
  84. fitdocs-0.1.0/src/fitdocs/performance/types.py +108 -0
  85. fitdocs-0.1.0/src/fitdocs/plans/__init__.py +221 -0
  86. fitdocs-0.1.0/src/fitdocs/plans/aggregate.py +163 -0
  87. fitdocs-0.1.0/src/fitdocs/plans/block_page.py +351 -0
  88. fitdocs-0.1.0/src/fitdocs/plans/corpus.py +206 -0
  89. fitdocs-0.1.0/src/fitdocs/plans/engine.py +577 -0
  90. fitdocs-0.1.0/src/fitdocs/plans/matching.py +395 -0
  91. fitdocs-0.1.0/src/fitdocs/plans/model.py +750 -0
  92. fitdocs-0.1.0/src/fitdocs/plans/page.py +324 -0
  93. fitdocs-0.1.0/src/fitdocs/plans/placement.py +521 -0
  94. fitdocs-0.1.0/src/fitdocs/plans/planned_page.py +113 -0
  95. fitdocs-0.1.0/src/fitdocs/plans/reconcile.py +228 -0
  96. fitdocs-0.1.0/src/fitdocs/plans/resolution.py +94 -0
  97. fitdocs-0.1.0/src/fitdocs/plans/settings.py +175 -0
  98. fitdocs-0.1.0/src/fitdocs/plans/source.py +1130 -0
  99. fitdocs-0.1.0/src/fitdocs/plugins.py +657 -0
  100. fitdocs-0.1.0/src/fitdocs/py.typed +0 -0
  101. fitdocs-0.1.0/src/fitdocs/quarantine.py +245 -0
  102. fitdocs-0.1.0/src/fitdocs/render/__init__.py +153 -0
  103. fitdocs-0.1.0/src/fitdocs/render/charts/__init__.py +9 -0
  104. fitdocs-0.1.0/src/fitdocs/render/charts/calendar.py +482 -0
  105. fitdocs-0.1.0/src/fitdocs/render/charts/hero.py +421 -0
  106. fitdocs-0.1.0/src/fitdocs/render/charts/map.py +462 -0
  107. fitdocs-0.1.0/src/fitdocs/render/charts/palette.py +190 -0
  108. fitdocs-0.1.0/src/fitdocs/render/charts/series.py +155 -0
  109. fitdocs-0.1.0/src/fitdocs/render/charts/svg.py +130 -0
  110. fitdocs-0.1.0/src/fitdocs/render/charts/zones.py +201 -0
  111. fitdocs-0.1.0/src/fitdocs/render/format.py +106 -0
  112. fitdocs-0.1.0/src/fitdocs/render/frontmatter.py +159 -0
  113. fitdocs-0.1.0/src/fitdocs/render/sections.py +618 -0
  114. fitdocs-0.1.0/src/fitdocs/render/splits.py +340 -0
  115. fitdocs-0.1.0/src/fitdocs/render/strength.py +180 -0
  116. fitdocs-0.1.0/src/fitdocs/render/views.py +245 -0
  117. fitdocs-0.1.0/src/fitdocs/settings.py +71 -0
  118. fitdocs-0.1.0/src/fitdocs/skills/build-training-block/SKILL.md +249 -0
  119. fitdocs-0.1.0/src/fitdocs/skills/build-training-block/example-block.toml +105 -0
  120. fitdocs-0.1.0/src/fitdocs/skills/fitdocs-workouts/SKILL.md +89 -0
  121. fitdocs-0.1.0/src/fitdocs/sync.py +1515 -0
  122. fitdocs-0.1.0/src/fitdocs/tiles.py +438 -0
  123. fitdocs-0.1.0/src/fitdocs/version.py +64 -0
@@ -0,0 +1,44 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ .venv/
5
+ venv/
6
+ dist/
7
+ build/
8
+ *.egg-info/
9
+ .pytest_cache/
10
+ .ruff_cache/
11
+ .mypy_cache/
12
+
13
+ # OS / IDE
14
+ .DS_Store
15
+ .idea/
16
+
17
+ # Machine-local config (mirrors pkm's data-root contract)
18
+ .fitdocs/data-root
19
+
20
+ # Personal fitness data never lives in the repo (hard rule; see CLAUDE.md /
21
+ # tech.md data-root contract). Local .fit/.gpx exports kept for real-data
22
+ # verification stay on disk but are never committed.
23
+ data/
24
+ *.fit
25
+ *.gpx
26
+
27
+ # Source spreadsheets for the withdrawn methodology, kept out of the repo
28
+ # pending redistribution permission from the third party.
29
+ *.xlsx
30
+
31
+ # Copyrighted third-party reference texts, read to source the training-load
32
+ # formulas and never redistributable: page scans of Banister (1991), "Modeling
33
+ # Elite Athletic Performance", ch. 9 of Physiological Testing of the
34
+ # High-Performance Athlete, and the publisher PDF of Morton, Fitz-Clarke &
35
+ # Banister (1990), J Appl Physiol 69(3):1171-1177 (© American Physiological
36
+ # Society). They stay on disk for verification, like the source workbooks above,
37
+ # but are committed nowhere — and these rules are also what keeps them out of
38
+ # the sdist, which ships untracked files that git does not ignore. What was
39
+ # extracted from them: docs/reference/banister-trimp-primary-sources.md.
40
+ bannister_physiological_testing_of_the_high_performance_athlete/
41
+ Morton*Modeling*human*performance*in*running*.pdf
42
+
43
+ # local secrets (Final Surge creds etc.) -- never committed
44
+ .env
@@ -0,0 +1,54 @@
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
+ **Convention:** an entry that touches a governed contract -- the generated
9
+ document and ownership contract, the inbox interface, the plugin API, or the
10
+ `fitdocs.toml` settings schema -- names the contract and the action a user or
11
+ plugin author must take, inline in the entry that changes it. Entries
12
+ describe user-observable behavior, not commit subjects; removing or adding
13
+ the built-in `threshold` calculator is itself a contract change and is
14
+ recorded as one.
15
+
16
+ ## [Unreleased]
17
+
18
+ ## [0.1.0] - 2026-09-19
19
+
20
+ ### Added
21
+
22
+ - `fitdocs` is installable as a Python package with a single console
23
+ command.
24
+ - `fitdocs sync` ingests `.fit` files from the configured inbox, archives
25
+ each source, and writes one markdown document per workout under the data
26
+ root's `workouts/` directory, with charts and computed training load;
27
+ `fitdocs regen` rebuilds every workout document from the data root alone
28
+ (the archive, the athlete profile, and settings) -- the original files
29
+ and the inbox are not needed.
30
+ - The data root's owned paths, the overwrite rules, and the user-owned
31
+ regions of every generated document are published as the ownership
32
+ contract; the four owned directories a human or agent browses
33
+ (`workouts/`, `fit-archive/`, `history/`, `blocks/`) each carry a generated
34
+ `AGENTS.md` restating it, and every generated document carries a matching
35
+ provenance banner.
36
+ - The inbox is configured through the `[inbox]` table in
37
+ `<data-root>/fitdocs.toml`: a location fitdocs drains on `sync`, with a
38
+ disposition policy that never deletes a source file.
39
+ - Training-load calculation is pluggable through the `LoadCalculator`
40
+ interface: a calculator is discovered as an installed distribution
41
+ advertising the `fitdocs.load_calculators` entry point, or as a local
42
+ plugin file/directory named in `[plugins]`. fitdocs ships one built-in
43
+ calculator, `threshold`.
44
+ - `fitdocs plugins` lists every registered calculator (built-in, packaged,
45
+ and local) with its id, display name, version, origin, and supported
46
+ modalities, and every plugin load error; it works with or without a
47
+ configured data root.
48
+ - `fitdocs check` reports every place a data root diverges from the
49
+ installed ownership contract.
50
+ - Every packaged agent skill ships inside the distribution and is
51
+ discoverable through `fitdocs skill`, which prints a packaged skill's
52
+ directory and a one-line copy recipe without resolving a data root or
53
+ writing anything.
54
+ - `fitdocs --version` reports the installed package version.
fitdocs-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Josh Stauffer
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
fitdocs-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,249 @@
1
+ Metadata-Version: 2.5
2
+ Name: fitdocs
3
+ Version: 0.1.0
4
+ Summary: Turn .fit files into rich per-workout markdown documents, with pluggable training-load calculation.
5
+ Project-URL: Source, https://github.com/joshua-stauffer/fitdocs
6
+ Project-URL: Documentation, https://github.com/joshua-stauffer/fitdocs/blob/main/docs/index.md
7
+ Project-URL: Changelog, https://github.com/joshua-stauffer/fitdocs/blob/main/CHANGELOG.md
8
+ Project-URL: Issues, https://github.com/joshua-stauffer/fitdocs/issues
9
+ Project-URL: Ownership Contract, https://github.com/joshua-stauffer/fitdocs/blob/main/docs/ownership-contract.md
10
+ Project-URL: Plugin Platform, https://github.com/joshua-stauffer/fitdocs/blob/main/docs/plugins.md
11
+ Project-URL: Inbox, https://github.com/joshua-stauffer/fitdocs/blob/main/docs/inbox.md
12
+ Project-URL: Configuration, https://github.com/joshua-stauffer/fitdocs/blob/main/docs/configuration.md
13
+ Project-URL: Install, https://github.com/joshua-stauffer/fitdocs/blob/main/docs/install.md
14
+ Project-URL: Compatibility, https://github.com/joshua-stauffer/fitdocs/blob/main/docs/compatibility.md
15
+ Project-URL: Wiki Integration, https://github.com/joshua-stauffer/fitdocs/blob/main/docs/wiki-integration.md
16
+ Author-email: Josh Stauffer <66793731+joshua-stauffer@users.noreply.github.com>
17
+ License-Expression: MIT
18
+ License-File: LICENSE
19
+ Keywords: cycling,fit-file,fitness,garmin,markdown,running,training-load
20
+ Classifier: Development Status :: 3 - Alpha
21
+ Classifier: Environment :: Console
22
+ Classifier: Intended Audience :: End Users/Desktop
23
+ Classifier: License :: OSI Approved :: MIT License
24
+ Classifier: Programming Language :: Python :: 3
25
+ Classifier: Programming Language :: Python :: 3.11
26
+ Classifier: Programming Language :: Python :: 3.12
27
+ Classifier: Programming Language :: Python :: 3.13
28
+ Classifier: Topic :: Software Development :: Libraries
29
+ Classifier: Typing :: Typed
30
+ Requires-Python: >=3.11
31
+ Requires-Dist: garmin-fit-sdk>=21.208.0
32
+ Requires-Dist: pyyaml>=6.0
33
+ Requires-Dist: rich>=13
34
+ Requires-Dist: tomli-w>=1.0
35
+ Requires-Dist: typer>=0.12
36
+ Description-Content-Type: text/markdown
37
+
38
+ # fitdocs
39
+
40
+ An installable personal knowledge manager for fitness. Consumes `.fit` files and
41
+ writes one markdown document per workout — rich, readable activity pages for
42
+ running, cycling, and weight training, with charts and computed training load.
43
+
44
+ Built to plug into a larger markdown PKM (wiki-style, Obsidian-compatible) as a
45
+ first-class path, while remaining feature-complete standalone.
46
+
47
+ ## Install
48
+
49
+ fitdocs installs as a single console entry point with either standard
50
+ isolated-application installer:
51
+
52
+ ```sh
53
+ uv tool install fitdocs
54
+ ```
55
+
56
+ ```sh
57
+ pipx install fitdocs
58
+ ```
59
+
60
+ See [Install](https://github.com/joshua-stauffer/fitdocs/blob/main/docs/install.md)
61
+ for the full first-run path — installing from a checkout before the package
62
+ is published, choosing a data root, getting `.fit` files where fitdocs finds
63
+ them, the first run, and what differs between a standalone data root and one
64
+ inside an existing markdown wiki.
65
+
66
+ ## What it does
67
+
68
+ 1. **Ingest** `.fit` files from a target directory or the standing inbox (user drops files in; fitdocs never watches for them).
69
+ 2. **Render** one markdown doc per activity — summary metrics, laps/splits or
70
+ sets/reps, and embedded charts (e.g. the power-vs-heart-rate hero graph) —
71
+ modeled on [fitdocs.ai](https://github.com/joshua-stauffer/fitdocs.ai)'s
72
+ activity views.
73
+ 3. **Compute training load** through a pluggable `LoadCalculator` interface.
74
+ fitdocs ships one built-in methodology, `threshold`, and is otherwise the
75
+ carrier: any other calculator is a plugin, installed or dropped in
76
+ locally, and contributions are welcome. If an activity is missing data a
77
+ calculator needs (e.g. max heart rate, a threshold pace), fitdocs prompts
78
+ for it and completes the calculation; when the activity predates the
79
+ prompt, fitdocs also asks whether the answer covers earlier activities
80
+ too.
81
+ 4. **Show training history.** `fitdocs history` renders one longitudinal
82
+ page — a fitness/fatigue/form model and chart built from the training
83
+ load already recorded on your workout documents — with no new data to
84
+ enter and no `.fit` file read.
85
+ 5. **Render training blocks.** `fitdocs plan` renders one block page per
86
+ source in your plan directory, with one planned-workout page per row
87
+ underneath it — rebuilt from your plan sources and reconciled against
88
+ your logged workout pages; the same pass also runs at the end of
89
+ `fitdocs sync` and `fitdocs regen` (see [Training blocks](#training-blocks)).
90
+
91
+ Organizing workouts into cycles/training blocks was deliberately deferred
92
+ through the first releases; Phase 7 of the roadmap (2026-09-15) adds training
93
+ blocks — a plan document rendered from an athlete-owned source and reconciled
94
+ against logged workouts — as the `training-blocks` (shipped: the plan
95
+ source, the two page types and `fitdocs plan`) and `plan-resolution`
96
+ (shipped: the reconciling pass chained after `fitdocs sync`/`fitdocs regen`
97
+ and run standalone through `fitdocs plan`) specs; only `build-training-block`
98
+ is not yet shipped.
99
+
100
+ ## Route maps
101
+
102
+ Outdoor activities get a **Map** section drawn over real basemap tiles —
103
+ the only time fitdocs touches the network. What leaves your machine and
104
+ when, the persistent opt-out, choosing a provider, attribution, and the tile
105
+ cache all now live in
106
+ [Configuration](https://github.com/joshua-stauffer/fitdocs/blob/main/docs/configuration.md#tiles-the-map-tile-provider-and-the-offline-opt-out).
107
+
108
+ ## Plugins
109
+
110
+ fitdocs ships one built-in training-load methodology, `threshold`, and
111
+ discovers any other calculator as a plugin, from two channels: an installed
112
+ Python distribution advertising the `fitdocs.load_calculators` entry point
113
+ (`pip install fitdocs-mycalc` is the whole install), or a local plugin
114
+ file/directory named by `[plugins].path` in `<data-root>/fitdocs.toml`. Run
115
+ `fitdocs plugins` at any time to see every registered calculator (id, version,
116
+ origin, modalities) and any plugin load errors — a plugin failure is always a
117
+ warning, never a run failure.
118
+
119
+ **Local plugin files are executed as ordinary code with your own user
120
+ privileges — fitdocs applies no sandboxing.** Only point `[plugins].path` at
121
+ code you trust.
122
+
123
+ See [`docs/plugins.md`](https://github.com/joshua-stauffer/fitdocs/blob/main/docs/plugins.md)
124
+ for the full guide — the worked packaged example, the local-file alternative,
125
+ the public API surface, the compatibility policy, diagnosis of every
126
+ rejection reason, and (linked from there) how to write the calculator itself.
127
+
128
+ ## Agent skills
129
+
130
+ fitdocs packages agent skills inside the wheel. Run `fitdocs skill` to list
131
+ the packaged skills and their installed directories, and
132
+ `fitdocs skill <name>` to print one skill's installed directory and a
133
+ recipe for putting it where your agent can use it.
134
+
135
+ **Install** a packaged skill by copying its directory into your agent's
136
+ skills directory — the open SKILL.md standard fixes no location, so your
137
+ agent's own documentation is where you find it.
138
+
139
+ **Verify** a packaged skill is active by asking the agent to list its
140
+ skills; the packaged skill appears under its frontmatter `name`.
141
+
142
+ **Update** a packaged skill once you have upgraded fitdocs: run
143
+ `fitdocs skill <name>` again and replace the old directory with the new
144
+ one; the copied `SKILL.md`'s `metadata.version` says which fitdocs release
145
+ it came from.
146
+
147
+ Two packaged skills ship today: `build-training-block` walks an agent
148
+ through building a training block from the athlete's answers as a plan
149
+ source, rendering it with `fitdocs plan`, amending it, and settling
150
+ ambiguous matches; `fitdocs-workouts` drains the inbox into workout
151
+ documents and reads the drain report. See
152
+ [Wiki Integration](https://github.com/joshua-stauffer/fitdocs/blob/main/docs/wiki-integration.md)
153
+ for where each skill lands, how to confirm it is active, how to update it,
154
+ and an end-to-end recipe for adopting fitdocs into an existing wiki.
155
+
156
+ ## Inbox
157
+
158
+ fitdocs defines a standing inbox: get `.fit` files there however you like,
159
+ and `fitdocs sync`, run with no arguments, drains it — performing no
160
+ watching and no scheduling of any kind. See
161
+ [Inbox](https://github.com/joshua-stauffer/fitdocs/blob/main/docs/inbox.md)
162
+ for the full interface: every settings key and its default, drain semantics,
163
+ the safeguards, and the disposition policy's never-delete guarantee.
164
+
165
+ ## Training blocks
166
+
167
+ `fitdocs plan` renders every plan source in your plan directory into a block
168
+ page under `blocks/`, plus one planned-workout page per row in a subdirectory
169
+ per block. The plan directory itself is **user-owned and read-only to
170
+ fitdocs**: fitdocs never creates, writes, renames, or deletes anything
171
+ there. It is configured in the `[plans]` table of
172
+ `<data-root>/fitdocs.toml`, the same settings file `[tiles]`, `[inbox]`, and
173
+ `[plugins]` share, which is itself read-only to fitdocs.
174
+
175
+ | Key | Meaning | Default |
176
+ |-----|---------|---------|
177
+ | `path` | The plan-source location. An absolute path is used as given; a relative path resolves against the data root. **Never created** by fitdocs. Must lie outside the owned paths — configuring it inside `blocks/` or another owned path is a configuration error. | `"plans"` (i.e. `<data-root>/plans/`) |
178
+
179
+ `fitdocs plan`, and the same pass chained at the end of `fitdocs sync` and
180
+ `fitdocs regen`, also reconciles each row against the logged workout pages,
181
+ resolving it to one of five states — `matched`, `overridden`, `skipped`,
182
+ `not logged`, or `upcoming` — a `matched` row further labeled `exact` (one
183
+ candidate), `absorbed` (several candidates, all taken by that one row — a
184
+ split session), or `ambiguous` when two or more planned workouts on the
185
+ same day compete for the same candidates, in which case the proposed
186
+ pairing is never presented as settled. An `[[override]]` entry in a plan
187
+ source pins a row to named logged workouts, or marks it skipped, ahead of
188
+ that automatic matching. Standalone or chained, the resolution shown is
189
+ re-derived from whatever the logged workout pages currently record —
190
+ nothing about the match is stored, and nothing is written back into a
191
+ workout page or a plan source.
192
+
193
+ ## Benchmarks
194
+
195
+ `fitdocs derive-benchmarks [--out PATH] [--dry-run]` turns your tagged
196
+ efforts (races, tests, and hard efforts you marked with the `effort`
197
+ frontmatter key) into dated benchmarks — threshold pace, lactate-threshold
198
+ heart rate, and functional threshold power — written to `athlete.toml`
199
+ alongside anything you typed in by hand. It never touches a benchmark you
200
+ recorded yourself, reports exactly why any tagged page was declined or
201
+ failed, and `--dry-run` prints the identical report without writing
202
+ anything.
203
+
204
+ ## Ownership
205
+
206
+ fitdocs writes into a wiki it does not control, so what it owns and what
207
+ you own is a published, versioned contract, not an implicit arrangement:
208
+ see the
209
+ [fitdocs Ownership Contract](https://github.com/joshua-stauffer/fitdocs/blob/main/docs/ownership-contract.md).
210
+
211
+ In short: fitdocs owns `workouts/`, `workouts/assets/`, `history/`,
212
+ `history/assets/`, `blocks/`, `fit-archive/`, `.cache/`, and `.fitdocs/`
213
+ under your data root (plus any location your `fitdocs.toml` settings
214
+ configure it to write into — the [Inbox](#inbox)'s `path` and
215
+ `processed_dir` are the first example), and writes nowhere else. `history/`
216
+ and `history/assets/` hold a second, generated document type — the
217
+ training-history page `fitdocs history` writes, alongside the per-workout
218
+ documents under `workouts/` — and carry no user-owned content of their own.
219
+ `blocks/` holds two further generated document types — one block page per
220
+ plan source and one planned-workout page per row, written by `fitdocs plan`
221
+ — of which the block page carries one user-owned region, `notes`, preserved
222
+ the same way a workout document's `notes` region is; the planned-workout
223
+ page carries no user-owned content. You own every workout document's
224
+ `notes` and `workout` regions, and its effort-tag frontmatter keys (`effort`,
225
+ `effort_distance_m`, `effort_time_s`, `effort_event`) — see the published
226
+ contract for the full detail. And `athlete.toml`'s hand-added keys survive
227
+ every fitdocs write. Every generated document carries a provenance stamp,
228
+ and the four owned directories a human or agent browses — `workouts/`,
229
+ `history/`, `blocks/`, and `fit-archive/` — each carry an `AGENTS.md`
230
+ restating the same contract for LLM agents maintaining the wiki.
231
+
232
+ ## Development workflow
233
+
234
+ All feature work follows Kiro-style Spec-Driven Development via
235
+ [cc-sdd](https://github.com/gotalab/cc-sdd) (`/kiro-*` skills in Claude Code).
236
+ Start with `.kiro/steering/roadmap.md` for the current plan; `.kiro/specs/`
237
+ holds every feature spec (requirements → design → tasks → implementation),
238
+ and `docs/reference/` holds the research and methodology references backing
239
+ them.
240
+
241
+ ## Learn more
242
+
243
+ - [Documentation index](https://github.com/joshua-stauffer/fitdocs/blob/main/docs/index.md) — the single entry point into every page below.
244
+ - [Install](https://github.com/joshua-stauffer/fitdocs/blob/main/docs/install.md) — the full first-run path, standalone or wiki-hosted.
245
+ - [Configuration](https://github.com/joshua-stauffer/fitdocs/blob/main/docs/configuration.md) — the data-root contract, the settings file, and the network/offline behavior.
246
+ - [Inbox](https://github.com/joshua-stauffer/fitdocs/blob/main/docs/inbox.md) — the full inbox interface.
247
+ - [Compatibility policy](https://github.com/joshua-stauffer/fitdocs/blob/main/docs/compatibility.md) — what a version number promises.
248
+ - [Plugin platform](https://github.com/joshua-stauffer/fitdocs/blob/main/docs/plugins.md) — the calculator plugin API.
249
+ - [Ownership contract](https://github.com/joshua-stauffer/fitdocs/blob/main/docs/ownership-contract.md) — what fitdocs owns in your data root and what you own.
@@ -0,0 +1,212 @@
1
+ # fitdocs
2
+
3
+ An installable personal knowledge manager for fitness. Consumes `.fit` files and
4
+ writes one markdown document per workout — rich, readable activity pages for
5
+ running, cycling, and weight training, with charts and computed training load.
6
+
7
+ Built to plug into a larger markdown PKM (wiki-style, Obsidian-compatible) as a
8
+ first-class path, while remaining feature-complete standalone.
9
+
10
+ ## Install
11
+
12
+ fitdocs installs as a single console entry point with either standard
13
+ isolated-application installer:
14
+
15
+ ```sh
16
+ uv tool install fitdocs
17
+ ```
18
+
19
+ ```sh
20
+ pipx install fitdocs
21
+ ```
22
+
23
+ See [Install](https://github.com/joshua-stauffer/fitdocs/blob/main/docs/install.md)
24
+ for the full first-run path — installing from a checkout before the package
25
+ is published, choosing a data root, getting `.fit` files where fitdocs finds
26
+ them, the first run, and what differs between a standalone data root and one
27
+ inside an existing markdown wiki.
28
+
29
+ ## What it does
30
+
31
+ 1. **Ingest** `.fit` files from a target directory or the standing inbox (user drops files in; fitdocs never watches for them).
32
+ 2. **Render** one markdown doc per activity — summary metrics, laps/splits or
33
+ sets/reps, and embedded charts (e.g. the power-vs-heart-rate hero graph) —
34
+ modeled on [fitdocs.ai](https://github.com/joshua-stauffer/fitdocs.ai)'s
35
+ activity views.
36
+ 3. **Compute training load** through a pluggable `LoadCalculator` interface.
37
+ fitdocs ships one built-in methodology, `threshold`, and is otherwise the
38
+ carrier: any other calculator is a plugin, installed or dropped in
39
+ locally, and contributions are welcome. If an activity is missing data a
40
+ calculator needs (e.g. max heart rate, a threshold pace), fitdocs prompts
41
+ for it and completes the calculation; when the activity predates the
42
+ prompt, fitdocs also asks whether the answer covers earlier activities
43
+ too.
44
+ 4. **Show training history.** `fitdocs history` renders one longitudinal
45
+ page — a fitness/fatigue/form model and chart built from the training
46
+ load already recorded on your workout documents — with no new data to
47
+ enter and no `.fit` file read.
48
+ 5. **Render training blocks.** `fitdocs plan` renders one block page per
49
+ source in your plan directory, with one planned-workout page per row
50
+ underneath it — rebuilt from your plan sources and reconciled against
51
+ your logged workout pages; the same pass also runs at the end of
52
+ `fitdocs sync` and `fitdocs regen` (see [Training blocks](#training-blocks)).
53
+
54
+ Organizing workouts into cycles/training blocks was deliberately deferred
55
+ through the first releases; Phase 7 of the roadmap (2026-09-15) adds training
56
+ blocks — a plan document rendered from an athlete-owned source and reconciled
57
+ against logged workouts — as the `training-blocks` (shipped: the plan
58
+ source, the two page types and `fitdocs plan`) and `plan-resolution`
59
+ (shipped: the reconciling pass chained after `fitdocs sync`/`fitdocs regen`
60
+ and run standalone through `fitdocs plan`) specs; only `build-training-block`
61
+ is not yet shipped.
62
+
63
+ ## Route maps
64
+
65
+ Outdoor activities get a **Map** section drawn over real basemap tiles —
66
+ the only time fitdocs touches the network. What leaves your machine and
67
+ when, the persistent opt-out, choosing a provider, attribution, and the tile
68
+ cache all now live in
69
+ [Configuration](https://github.com/joshua-stauffer/fitdocs/blob/main/docs/configuration.md#tiles-the-map-tile-provider-and-the-offline-opt-out).
70
+
71
+ ## Plugins
72
+
73
+ fitdocs ships one built-in training-load methodology, `threshold`, and
74
+ discovers any other calculator as a plugin, from two channels: an installed
75
+ Python distribution advertising the `fitdocs.load_calculators` entry point
76
+ (`pip install fitdocs-mycalc` is the whole install), or a local plugin
77
+ file/directory named by `[plugins].path` in `<data-root>/fitdocs.toml`. Run
78
+ `fitdocs plugins` at any time to see every registered calculator (id, version,
79
+ origin, modalities) and any plugin load errors — a plugin failure is always a
80
+ warning, never a run failure.
81
+
82
+ **Local plugin files are executed as ordinary code with your own user
83
+ privileges — fitdocs applies no sandboxing.** Only point `[plugins].path` at
84
+ code you trust.
85
+
86
+ See [`docs/plugins.md`](https://github.com/joshua-stauffer/fitdocs/blob/main/docs/plugins.md)
87
+ for the full guide — the worked packaged example, the local-file alternative,
88
+ the public API surface, the compatibility policy, diagnosis of every
89
+ rejection reason, and (linked from there) how to write the calculator itself.
90
+
91
+ ## Agent skills
92
+
93
+ fitdocs packages agent skills inside the wheel. Run `fitdocs skill` to list
94
+ the packaged skills and their installed directories, and
95
+ `fitdocs skill <name>` to print one skill's installed directory and a
96
+ recipe for putting it where your agent can use it.
97
+
98
+ **Install** a packaged skill by copying its directory into your agent's
99
+ skills directory — the open SKILL.md standard fixes no location, so your
100
+ agent's own documentation is where you find it.
101
+
102
+ **Verify** a packaged skill is active by asking the agent to list its
103
+ skills; the packaged skill appears under its frontmatter `name`.
104
+
105
+ **Update** a packaged skill once you have upgraded fitdocs: run
106
+ `fitdocs skill <name>` again and replace the old directory with the new
107
+ one; the copied `SKILL.md`'s `metadata.version` says which fitdocs release
108
+ it came from.
109
+
110
+ Two packaged skills ship today: `build-training-block` walks an agent
111
+ through building a training block from the athlete's answers as a plan
112
+ source, rendering it with `fitdocs plan`, amending it, and settling
113
+ ambiguous matches; `fitdocs-workouts` drains the inbox into workout
114
+ documents and reads the drain report. See
115
+ [Wiki Integration](https://github.com/joshua-stauffer/fitdocs/blob/main/docs/wiki-integration.md)
116
+ for where each skill lands, how to confirm it is active, how to update it,
117
+ and an end-to-end recipe for adopting fitdocs into an existing wiki.
118
+
119
+ ## Inbox
120
+
121
+ fitdocs defines a standing inbox: get `.fit` files there however you like,
122
+ and `fitdocs sync`, run with no arguments, drains it — performing no
123
+ watching and no scheduling of any kind. See
124
+ [Inbox](https://github.com/joshua-stauffer/fitdocs/blob/main/docs/inbox.md)
125
+ for the full interface: every settings key and its default, drain semantics,
126
+ the safeguards, and the disposition policy's never-delete guarantee.
127
+
128
+ ## Training blocks
129
+
130
+ `fitdocs plan` renders every plan source in your plan directory into a block
131
+ page under `blocks/`, plus one planned-workout page per row in a subdirectory
132
+ per block. The plan directory itself is **user-owned and read-only to
133
+ fitdocs**: fitdocs never creates, writes, renames, or deletes anything
134
+ there. It is configured in the `[plans]` table of
135
+ `<data-root>/fitdocs.toml`, the same settings file `[tiles]`, `[inbox]`, and
136
+ `[plugins]` share, which is itself read-only to fitdocs.
137
+
138
+ | Key | Meaning | Default |
139
+ |-----|---------|---------|
140
+ | `path` | The plan-source location. An absolute path is used as given; a relative path resolves against the data root. **Never created** by fitdocs. Must lie outside the owned paths — configuring it inside `blocks/` or another owned path is a configuration error. | `"plans"` (i.e. `<data-root>/plans/`) |
141
+
142
+ `fitdocs plan`, and the same pass chained at the end of `fitdocs sync` and
143
+ `fitdocs regen`, also reconciles each row against the logged workout pages,
144
+ resolving it to one of five states — `matched`, `overridden`, `skipped`,
145
+ `not logged`, or `upcoming` — a `matched` row further labeled `exact` (one
146
+ candidate), `absorbed` (several candidates, all taken by that one row — a
147
+ split session), or `ambiguous` when two or more planned workouts on the
148
+ same day compete for the same candidates, in which case the proposed
149
+ pairing is never presented as settled. An `[[override]]` entry in a plan
150
+ source pins a row to named logged workouts, or marks it skipped, ahead of
151
+ that automatic matching. Standalone or chained, the resolution shown is
152
+ re-derived from whatever the logged workout pages currently record —
153
+ nothing about the match is stored, and nothing is written back into a
154
+ workout page or a plan source.
155
+
156
+ ## Benchmarks
157
+
158
+ `fitdocs derive-benchmarks [--out PATH] [--dry-run]` turns your tagged
159
+ efforts (races, tests, and hard efforts you marked with the `effort`
160
+ frontmatter key) into dated benchmarks — threshold pace, lactate-threshold
161
+ heart rate, and functional threshold power — written to `athlete.toml`
162
+ alongside anything you typed in by hand. It never touches a benchmark you
163
+ recorded yourself, reports exactly why any tagged page was declined or
164
+ failed, and `--dry-run` prints the identical report without writing
165
+ anything.
166
+
167
+ ## Ownership
168
+
169
+ fitdocs writes into a wiki it does not control, so what it owns and what
170
+ you own is a published, versioned contract, not an implicit arrangement:
171
+ see the
172
+ [fitdocs Ownership Contract](https://github.com/joshua-stauffer/fitdocs/blob/main/docs/ownership-contract.md).
173
+
174
+ In short: fitdocs owns `workouts/`, `workouts/assets/`, `history/`,
175
+ `history/assets/`, `blocks/`, `fit-archive/`, `.cache/`, and `.fitdocs/`
176
+ under your data root (plus any location your `fitdocs.toml` settings
177
+ configure it to write into — the [Inbox](#inbox)'s `path` and
178
+ `processed_dir` are the first example), and writes nowhere else. `history/`
179
+ and `history/assets/` hold a second, generated document type — the
180
+ training-history page `fitdocs history` writes, alongside the per-workout
181
+ documents under `workouts/` — and carry no user-owned content of their own.
182
+ `blocks/` holds two further generated document types — one block page per
183
+ plan source and one planned-workout page per row, written by `fitdocs plan`
184
+ — of which the block page carries one user-owned region, `notes`, preserved
185
+ the same way a workout document's `notes` region is; the planned-workout
186
+ page carries no user-owned content. You own every workout document's
187
+ `notes` and `workout` regions, and its effort-tag frontmatter keys (`effort`,
188
+ `effort_distance_m`, `effort_time_s`, `effort_event`) — see the published
189
+ contract for the full detail. And `athlete.toml`'s hand-added keys survive
190
+ every fitdocs write. Every generated document carries a provenance stamp,
191
+ and the four owned directories a human or agent browses — `workouts/`,
192
+ `history/`, `blocks/`, and `fit-archive/` — each carry an `AGENTS.md`
193
+ restating the same contract for LLM agents maintaining the wiki.
194
+
195
+ ## Development workflow
196
+
197
+ All feature work follows Kiro-style Spec-Driven Development via
198
+ [cc-sdd](https://github.com/gotalab/cc-sdd) (`/kiro-*` skills in Claude Code).
199
+ Start with `.kiro/steering/roadmap.md` for the current plan; `.kiro/specs/`
200
+ holds every feature spec (requirements → design → tasks → implementation),
201
+ and `docs/reference/` holds the research and methodology references backing
202
+ them.
203
+
204
+ ## Learn more
205
+
206
+ - [Documentation index](https://github.com/joshua-stauffer/fitdocs/blob/main/docs/index.md) — the single entry point into every page below.
207
+ - [Install](https://github.com/joshua-stauffer/fitdocs/blob/main/docs/install.md) — the full first-run path, standalone or wiki-hosted.
208
+ - [Configuration](https://github.com/joshua-stauffer/fitdocs/blob/main/docs/configuration.md) — the data-root contract, the settings file, and the network/offline behavior.
209
+ - [Inbox](https://github.com/joshua-stauffer/fitdocs/blob/main/docs/inbox.md) — the full inbox interface.
210
+ - [Compatibility policy](https://github.com/joshua-stauffer/fitdocs/blob/main/docs/compatibility.md) — what a version number promises.
211
+ - [Plugin platform](https://github.com/joshua-stauffer/fitdocs/blob/main/docs/plugins.md) — the calculator plugin API.
212
+ - [Ownership contract](https://github.com/joshua-stauffer/fitdocs/blob/main/docs/ownership-contract.md) — what fitdocs owns in your data root and what you own.