devcoach 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 (59) hide show
  1. devcoach-0.1.0/.github/dependabot.yml +40 -0
  2. devcoach-0.1.0/.github/workflows/ci.yml +110 -0
  3. devcoach-0.1.0/.github/workflows/ruff-autofix.yml +44 -0
  4. devcoach-0.1.0/.gitignore +211 -0
  5. devcoach-0.1.0/CLAUDE.md +273 -0
  6. devcoach-0.1.0/LICENSE +201 -0
  7. devcoach-0.1.0/NOTICE +20 -0
  8. devcoach-0.1.0/PKG-INFO +443 -0
  9. devcoach-0.1.0/README.md +211 -0
  10. devcoach-0.1.0/SKILL.md +185 -0
  11. devcoach-0.1.0/docs/PLAN.md +144 -0
  12. devcoach-0.1.0/docs/cli.md +235 -0
  13. devcoach-0.1.0/docs/configuration.md +127 -0
  14. devcoach-0.1.0/docs/getting-started.md +142 -0
  15. devcoach-0.1.0/docs/mcp-server.md +274 -0
  16. devcoach-0.1.0/docs/web-ui.md +88 -0
  17. devcoach-0.1.0/pyproject.toml +65 -0
  18. devcoach-0.1.0/src/devcoach/SKILL.md +288 -0
  19. devcoach-0.1.0/src/devcoach/__init__.py +3 -0
  20. devcoach-0.1.0/src/devcoach/cli/__init__.py +0 -0
  21. devcoach-0.1.0/src/devcoach/cli/commands.py +793 -0
  22. devcoach-0.1.0/src/devcoach/core/__init__.py +0 -0
  23. devcoach-0.1.0/src/devcoach/core/coach.py +141 -0
  24. devcoach-0.1.0/src/devcoach/core/db.py +768 -0
  25. devcoach-0.1.0/src/devcoach/core/detect.py +132 -0
  26. devcoach-0.1.0/src/devcoach/core/git.py +97 -0
  27. devcoach-0.1.0/src/devcoach/core/models.py +104 -0
  28. devcoach-0.1.0/src/devcoach/core/prompts.py +52 -0
  29. devcoach-0.1.0/src/devcoach/mcp/__init__.py +0 -0
  30. devcoach-0.1.0/src/devcoach/mcp/server.py +545 -0
  31. devcoach-0.1.0/src/devcoach/web/__init__.py +0 -0
  32. devcoach-0.1.0/src/devcoach/web/app.py +319 -0
  33. devcoach-0.1.0/src/devcoach/web/static/favicon.svg +3 -0
  34. devcoach-0.1.0/src/devcoach/web/static/relative-time.js +24 -0
  35. devcoach-0.1.0/src/devcoach/web/static/style.css +163 -0
  36. devcoach-0.1.0/src/devcoach/web/static/vendor/alpinejs.min.js +5 -0
  37. devcoach-0.1.0/src/devcoach/web/static/vendor/flatpickr-dark.min.css +795 -0
  38. devcoach-0.1.0/src/devcoach/web/static/vendor/flatpickr.min.css +13 -0
  39. devcoach-0.1.0/src/devcoach/web/static/vendor/flatpickr.min.js +2 -0
  40. devcoach-0.1.0/src/devcoach/web/static/vendor/highlight.min.js +1232 -0
  41. devcoach-0.1.0/src/devcoach/web/static/vendor/hljs-dark.min.css +1 -0
  42. devcoach-0.1.0/src/devcoach/web/static/vendor/hljs-light.min.css +1 -0
  43. devcoach-0.1.0/src/devcoach/web/static/vendor/htmx.min.js +1 -0
  44. devcoach-0.1.0/src/devcoach/web/static/vendor/icons/bitbucket.svg +1 -0
  45. devcoach-0.1.0/src/devcoach/web/static/vendor/icons/github.svg +1 -0
  46. devcoach-0.1.0/src/devcoach/web/static/vendor/icons/gitlab.svg +1 -0
  47. devcoach-0.1.0/src/devcoach/web/static/vendor/icons/vscode.svg +41 -0
  48. devcoach-0.1.0/src/devcoach/web/static/vendor/marked.min.js +6 -0
  49. devcoach-0.1.0/src/devcoach/web/static/vendor/tailwind.js +83 -0
  50. devcoach-0.1.0/src/devcoach/web/templates/base.html +80 -0
  51. devcoach-0.1.0/src/devcoach/web/templates/lesson_detail.html +215 -0
  52. devcoach-0.1.0/src/devcoach/web/templates/lessons.html +546 -0
  53. devcoach-0.1.0/src/devcoach/web/templates/profile.html +240 -0
  54. devcoach-0.1.0/src/devcoach/web/templates/settings.html +144 -0
  55. devcoach-0.1.0/tests/__init__.py +0 -0
  56. devcoach-0.1.0/tests/conftest.py +94 -0
  57. devcoach-0.1.0/tests/test_cli.py +343 -0
  58. devcoach-0.1.0/tests/test_web.py +351 -0
  59. devcoach-0.1.0/uv.lock +1573 -0
@@ -0,0 +1,40 @@
1
+ version: 2
2
+
3
+ updates:
4
+ # Python dependencies (uv / pyproject.toml)
5
+ - package-ecosystem: uv
6
+ directory: /
7
+ schedule:
8
+ interval: weekly
9
+ day: monday
10
+ time: "06:00"
11
+ timezone: "Europe/Rome"
12
+ groups:
13
+ # Group all non-breaking updates together to minimise PR noise.
14
+ # Breaking updates (major bumps) are kept separate per package.
15
+ non-breaking:
16
+ update-types:
17
+ - minor
18
+ - patch
19
+ open-pull-requests-limit: 10
20
+ commit-message:
21
+ prefix: "deps"
22
+ labels:
23
+ - dependencies
24
+
25
+ # GitHub Actions
26
+ - package-ecosystem: github-actions
27
+ directory: /
28
+ schedule:
29
+ interval: weekly
30
+ day: monday
31
+ time: "06:00"
32
+ timezone: "Europe/Rome"
33
+ groups:
34
+ github-actions:
35
+ patterns: ["*"]
36
+ commit-message:
37
+ prefix: "ci"
38
+ labels:
39
+ - dependencies
40
+ - ci
@@ -0,0 +1,110 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ tags: ["v*"]
7
+ pull_request:
8
+
9
+ concurrency:
10
+ group: ${{ github.workflow }}-${{ github.ref }}
11
+ cancel-in-progress: true
12
+
13
+ jobs:
14
+ # ── Lint ────────────────────────────────────────────────────────────────
15
+ lint:
16
+ name: Lint (ruff)
17
+ runs-on: ubuntu-latest
18
+ steps:
19
+ - uses: actions/checkout@v6
20
+ - uses: astral-sh/setup-uv@v7
21
+ with:
22
+ python-version: "3.12"
23
+ - run: uv sync --group dev
24
+ - run: uv run ruff check src/ tests/
25
+ - run: uv run ruff format --check src/ tests/
26
+
27
+ # ── Tests ───────────────────────────────────────────────────────────────
28
+ test:
29
+ name: Test (Python ${{ matrix.python-version }})
30
+ runs-on: ubuntu-latest
31
+ strategy:
32
+ fail-fast: false
33
+ matrix:
34
+ python-version: ["3.11", "3.12", "3.13"]
35
+ steps:
36
+ - uses: actions/checkout@v6
37
+ - uses: astral-sh/setup-uv@v7
38
+ with:
39
+ python-version: ${{ matrix.python-version }}
40
+ - run: uv sync --group dev
41
+ - run: uv run pytest tests/ -v --tb=short
42
+
43
+ # ── Build ───────────────────────────────────────────────────────────────
44
+ build:
45
+ name: Build distribution
46
+ needs: [lint, test]
47
+ runs-on: ubuntu-latest
48
+ steps:
49
+ - uses: actions/checkout@v6
50
+ - uses: astral-sh/setup-uv@v7
51
+ with:
52
+ python-version: "3.12"
53
+ - run: uv build
54
+ - uses: actions/upload-artifact@v7
55
+ with:
56
+ name: dist
57
+ path: dist/
58
+ retention-days: 7
59
+
60
+ # ── Publish ─────────────────────────────────────────────────────────────
61
+ # Runs only on v* tags (e.g. v0.2.0). Uses PyPI Trusted Publishing (OIDC)
62
+ # — no API tokens stored in repository secrets.
63
+ #
64
+ # One-time setup required on PyPI:
65
+ # 1. Go to https://pypi.org/manage/account/publishing/
66
+ # 2. Add a new publisher:
67
+ # PyPI project name : devcoach
68
+ # Owner : UltimaPhoenix
69
+ # Repository name : dev-coach
70
+ # Workflow filename : ci.yml
71
+ # Environment name : pypi
72
+ publish:
73
+ name: Publish to PyPI
74
+ if: startsWith(github.ref, 'refs/tags/v')
75
+ needs: build
76
+ runs-on: ubuntu-latest
77
+ environment:
78
+ name: pypi
79
+ url: https://pypi.org/p/devcoach
80
+ permissions:
81
+ id-token: write # required for OIDC trusted publishing
82
+ steps:
83
+ - uses: actions/download-artifact@v8
84
+ with:
85
+ name: dist
86
+ path: dist/
87
+ - name: Publish to PyPI
88
+ uses: pypa/gh-action-pypi-publish@release/v1
89
+
90
+ # ── GitHub Release ──────────────────────────────────────────────────────
91
+ # Creates a GitHub Release with the built distribution attached.
92
+ release:
93
+ name: Create GitHub Release
94
+ if: startsWith(github.ref, 'refs/tags/v')
95
+ needs: publish
96
+ runs-on: ubuntu-latest
97
+ permissions:
98
+ contents: write
99
+ steps:
100
+ - uses: actions/checkout@v6
101
+ - uses: actions/download-artifact@v8
102
+ with:
103
+ name: dist
104
+ path: dist/
105
+ - name: Create release
106
+ uses: softprops/action-gh-release@v3
107
+ with:
108
+ files: dist/*
109
+ generate_release_notes: true
110
+ fail_on_unmatched_files: true
@@ -0,0 +1,44 @@
1
+ name: Ruff auto-fix
2
+
3
+ on:
4
+ schedule:
5
+ - cron: "0 6 * * 1" # every Monday at 06:00 UTC, same cadence as Dependabot
6
+ workflow_dispatch: # allow manual trigger from the Actions tab
7
+
8
+ permissions:
9
+ contents: write
10
+ pull-requests: write
11
+
12
+ jobs:
13
+ autofix:
14
+ name: ruff check --fix + ruff format
15
+ runs-on: ubuntu-latest
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+
19
+ - uses: astral-sh/setup-uv@v5
20
+ with:
21
+ python-version: "3.12"
22
+
23
+ - name: Install dev dependencies
24
+ run: uv sync --group dev
25
+
26
+ - name: Run ruff fix
27
+ run: uv run ruff check --fix src/ tests/
28
+
29
+ - name: Run ruff format
30
+ run: uv run ruff format src/ tests/
31
+
32
+ - name: Open PR if there are changes
33
+ uses: peter-evans/create-pull-request@v7
34
+ with:
35
+ commit-message: "style: apply ruff auto-fixes"
36
+ title: "style: ruff auto-fix"
37
+ body: |
38
+ Automated style fixes applied by `ruff check --fix` and `ruff format`.
39
+
40
+ This PR was opened automatically by the **Ruff auto-fix** workflow.
41
+ Review the diff, then merge or close.
42
+ branch: ruff/autofix
43
+ delete-branch: true
44
+ labels: style
@@ -0,0 +1,211 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[codz]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ # Usually these files are written by a python script from a template
31
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
+ *.manifest
33
+ *.spec
34
+
35
+ # Installer logs
36
+ pip-log.txt
37
+ pip-delete-this-directory.txt
38
+
39
+ # Unit test / coverage reports
40
+ htmlcov/
41
+ .tox/
42
+ .nox/
43
+ .coverage
44
+ .coverage.*
45
+ .cache
46
+ nosetests.xml
47
+ coverage.xml
48
+ *.cover
49
+ *.py.cover
50
+ .hypothesis/
51
+ .pytest_cache/
52
+ cover/
53
+
54
+ # Translations
55
+ *.mo
56
+ *.pot
57
+
58
+ # Django stuff:
59
+ *.log
60
+ local_settings.py
61
+ db.sqlite3
62
+ db.sqlite3-journal
63
+
64
+ # Flask stuff:
65
+ instance/
66
+ .webassets-cache
67
+
68
+ # Scrapy stuff:
69
+ .scrapy
70
+
71
+ # Sphinx documentation
72
+ docs/_build/
73
+
74
+ # PyBuilder
75
+ .pybuilder/
76
+ target/
77
+
78
+ # Jupyter Notebook
79
+ .ipynb_checkpoints
80
+
81
+ # IPython
82
+ profile_default/
83
+ ipython_config.py
84
+
85
+ # pyenv
86
+ # For a library or package, you might want to ignore these files since the code is
87
+ # intended to run in multiple environments; otherwise, check them in:
88
+ # .python-version
89
+
90
+ # pipenv
91
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
93
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
94
+ # install all needed dependencies.
95
+ #Pipfile.lock
96
+
97
+ # UV
98
+ # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
99
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
100
+ # commonly ignored for libraries.
101
+ #uv.lock
102
+
103
+ # poetry
104
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
105
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
106
+ # commonly ignored for libraries.
107
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
108
+ #poetry.lock
109
+ #poetry.toml
110
+
111
+ # pdm
112
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
113
+ # pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
114
+ # https://pdm-project.org/en/latest/usage/project/#working-with-version-control
115
+ #pdm.lock
116
+ #pdm.toml
117
+ .pdm-python
118
+ .pdm-build/
119
+
120
+ # pixi
121
+ # Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
122
+ #pixi.lock
123
+ # Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
124
+ # in the .venv directory. It is recommended not to include this directory in version control.
125
+ .pixi
126
+
127
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
128
+ __pypackages__/
129
+
130
+ # Celery stuff
131
+ celerybeat-schedule
132
+ celerybeat.pid
133
+
134
+ # SageMath parsed files
135
+ *.sage.py
136
+
137
+ # Environments
138
+ .env
139
+ .envrc
140
+ .venv
141
+ env/
142
+ venv/
143
+ ENV/
144
+ env.bak/
145
+ venv.bak/
146
+
147
+ # Spyder project settings
148
+ .spyderproject
149
+ .spyproject
150
+
151
+ # Rope project settings
152
+ .ropeproject
153
+
154
+ # mkdocs documentation
155
+ /site
156
+
157
+ # mypy
158
+ .mypy_cache/
159
+ .dmypy.json
160
+ dmypy.json
161
+
162
+ # Pyre type checker
163
+ .pyre/
164
+
165
+ # pytype static type analyzer
166
+ .pytype/
167
+
168
+ # Cython debug symbols
169
+ cython_debug/
170
+
171
+ # PyCharm
172
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
173
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
174
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
175
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
176
+ #.idea/
177
+
178
+ # Abstra
179
+ # Abstra is an AI-powered process automation framework.
180
+ # Ignore directories containing user credentials, local state, and settings.
181
+ # Learn more at https://abstra.io/docs
182
+ .abstra/
183
+
184
+ # Visual Studio Code
185
+ # Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
186
+ # that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
187
+ # and can be added to the global gitignore or merged into this file. However, if you prefer,
188
+ # you could uncomment the following to ignore the entire vscode folder
189
+ # .vscode/
190
+
191
+ # Ruff stuff:
192
+ .ruff_cache/
193
+
194
+ # PyPI configuration file
195
+ .pypirc
196
+
197
+ # Claude Code
198
+ .claude/
199
+ .playwright-mcp/
200
+
201
+ # Cursor
202
+ # Cursor is an AI-powered code editor. `.cursorignore` specifies files/directories to
203
+ # exclude from AI features like autocomplete and code analysis. Recommended for sensitive data
204
+ # refer to https://docs.cursor.com/context/ignore-files
205
+ .cursorignore
206
+ .cursorindexingignore
207
+
208
+ # Marimo
209
+ marimo/_static/
210
+ marimo/_lsp/
211
+ __marimo__/
@@ -0,0 +1,273 @@
1
+ # devcoach — CLAUDE.md
2
+
3
+ ## What is this project
4
+
5
+ `devcoach` is a local MCP server written in Python that acts as a progressive technical
6
+ coach. It integrates with Claude Code and Claude Desktop via stdio transport.
7
+ Every time Claude completes a technical task, devcoach decides whether to deliver a lesson
8
+ based on the user's knowledge map, the rate limit, and what has already been taught.
9
+
10
+ Repo: https://github.com/UltimaPhoenix/dev-coach
11
+ PyPI package name: `devcoach`
12
+ End-user command: `uvx devcoach`
13
+
14
+ ---
15
+
16
+ ## Stack
17
+
18
+ - **Python** 3.11+
19
+ - **FastMCP** — MCP framework (not the raw SDK)
20
+ - **SQLite** — stdlib `sqlite3`, zero server, single file at `~/.devcoach/coaching.db`
21
+ - **Pydantic v2** — model validation
22
+ - **uv / uvx** — distribution and launch tool
23
+
24
+ ---
25
+
26
+ ## Project structure
27
+
28
+ ```
29
+ dev-coach/
30
+ ├── CLAUDE.md
31
+ ├── README.md
32
+ ├── .gitignore
33
+ ├── pyproject.toml
34
+ └── src/devcoach/
35
+ ├── __init__.py
36
+ ├── server.py # FastMCP app, tool definitions, MCP prompt, entry point
37
+ ├── db.py # SQLite schema, migrations, query helpers
38
+ ├── coach.py # rate limit, lesson selection, knowledge map logic
39
+ ├── models.py # Pydantic: Lesson, Profile, Settings, KnowledgeUpdate
40
+ ├── prompts.py # lesson template builder by level (junior/mid/senior)
41
+ └── SKILL.md # coaching instructions — single source of truth
42
+ ```
43
+
44
+ ---
45
+
46
+ ## Exposed MCP tools
47
+
48
+ | Tool | Input | Output | Purpose |
49
+ |---|---|---|---|
50
+ | `log_lesson` | `Lesson` JSON | `ok` | Save a lesson to the DB |
51
+ | `get_profile` | — | `Profile` JSON | Return the current knowledge map |
52
+ | `update_knowledge` | `topic: str, delta: int` | Updated `Profile` | Adjust confidence for a topic |
53
+ | `check_rate_limit` | — | `{allowed: bool, reason?: str}` | Check whether a lesson can be delivered now |
54
+ | `get_lessons` | `period?: today\|week\|month\|year\|all` | `Lesson[]` | Query the lesson history |
55
+ | `get_taught_topics` | — | `str[]` | List of already-taught topic IDs |
56
+
57
+ ---
58
+
59
+ ## MCP prompt — auto-injected coaching instructions
60
+
61
+ The server also exposes a **MCP prompt** named `devcoach_instructions`. Clients that
62
+ support MCP prompts (Claude Code, Claude Desktop) load it automatically when connecting
63
+ to the server — no separate SKILL.md installation needed.
64
+
65
+ The prompt is read at runtime directly from `SKILL.md` bundled inside the package.
66
+ `SKILL.md` is therefore the **single source of truth** for coaching behaviour: update
67
+ it once and both the MCP prompt and any separately installed skill reflect the change.
68
+
69
+ ```python
70
+ # server.py
71
+ import importlib.resources
72
+
73
+ @mcp.prompt()
74
+ def devcoach_instructions() -> str:
75
+ return importlib.resources.read_text("devcoach", "SKILL.md")
76
+ ```
77
+
78
+ `SKILL.md` must be declared as package data in `pyproject.toml`:
79
+
80
+ ```toml
81
+ [tool.hatch.build.targets.wheel]
82
+ packages = ["src/devcoach"]
83
+
84
+ [tool.hatch.build.targets.wheel.shared-data]
85
+ "src/devcoach/SKILL.md" = "devcoach/SKILL.md"
86
+ ```
87
+
88
+ For clients that do **not** support MCP prompts (claude.ai web), install the
89
+ `.skill` file generated from the same `SKILL.md` via the Claude.ai Skills settings.
90
+
91
+ ---
92
+
93
+ ## DB schema
94
+
95
+ ```sql
96
+ -- Delivered lessons
97
+ CREATE TABLE lessons (
98
+ id TEXT PRIMARY KEY,
99
+ timestamp TEXT NOT NULL,
100
+ topic_id TEXT NOT NULL,
101
+ category TEXT NOT NULL,
102
+ title TEXT NOT NULL,
103
+ level TEXT NOT NULL, -- junior|mid|senior
104
+ summary TEXT NOT NULL,
105
+ task_context TEXT
106
+ );
107
+
108
+ -- Per-topic knowledge map
109
+ CREATE TABLE knowledge (
110
+ topic TEXT PRIMARY KEY,
111
+ confidence INTEGER NOT NULL DEFAULT 5, -- 0-10
112
+ updated_at TEXT NOT NULL
113
+ );
114
+
115
+ -- User settings
116
+ CREATE TABLE settings (
117
+ key TEXT PRIMARY KEY,
118
+ value TEXT NOT NULL
119
+ );
120
+ ```
121
+
122
+ ---
123
+
124
+ ## Default profile
125
+
126
+ Loaded automatically on first run if the `knowledge` table is empty:
127
+
128
+ ```python
129
+ DEFAULT_PROFILE = {
130
+ "general_engineering": 8, "software_architecture": 8,
131
+ "design_patterns": 7, "debugging_mindset": 8,
132
+ "node_js": 7, "javascript": 7, "typescript": 6,
133
+ "python": 4, "django": 3, "fastapi": 4,
134
+ "docker": 8, "docker_compose": 8, "traefik": 7,
135
+ "coolify": 7, "postgresql": 6, "redis": 6,
136
+ "git": 7, "ci_cd": 6, "security": 5,
137
+ "performance_optimization": 6, "testing": 5,
138
+ "linux_cli": 7, "networking": 6, "react": 5, "html_css": 5,
139
+ }
140
+ ```
141
+
142
+ ---
143
+
144
+ ## Default settings
145
+
146
+ ```python
147
+ DEFAULT_SETTINGS = {
148
+ "max_per_day": "2",
149
+ "min_hours_between": "4",
150
+ }
151
+ ```
152
+
153
+ ---
154
+
155
+ ## Rate limit logic (coach.py)
156
+
157
+ ```
158
+ 1. Count lessons with a timestamp in the last 24h → if >= max_per_day: denied
159
+ 2. Find the timestamp of the last lesson → if < min_hours_between ago: denied
160
+ 3. Otherwise: allowed
161
+ ```
162
+
163
+ ---
164
+
165
+ ## Lesson selection logic (coach.py)
166
+
167
+ Descending priority:
168
+ 1. Pitfall or antipattern in the current task + low confidence on that topic
169
+ 2. Interesting pattern used + confidence < 6
170
+ 3. Related concept not yet mastered + confidence < 5
171
+ 4. Deep-dive on a topic the user is building toward + confidence 4–6
172
+
173
+ Never teach a `topic_id` already present in `get_taught_topics()`.
174
+
175
+ ---
176
+
177
+ ## Lesson format
178
+
179
+ The `log_lesson` tool accepts this schema:
180
+
181
+ ```json
182
+ {
183
+ "id": "uuid-or-random-string",
184
+ "timestamp": "2025-01-15T20:30:00Z",
185
+ "topic_id": "python_generators",
186
+ "category": "python",
187
+ "title": "Generator expressions vs list comprehensions",
188
+ "level": "mid",
189
+ "summary": "Generators are lazy: they yield one item at a time without loading everything into memory.",
190
+ "task_context": "Optimising a loop over a large dataset"
191
+ }
192
+ ```
193
+
194
+ ---
195
+
196
+ ## pyproject.toml — key points
197
+
198
+ ```toml
199
+ [project]
200
+ name = "devcoach"
201
+ version = "0.1.0"
202
+ requires-python = ">=3.11"
203
+ dependencies = [
204
+ "fastmcp>=2.0",
205
+ "pydantic>=2.0",
206
+ ]
207
+
208
+ [project.scripts]
209
+ devcoach = "devcoach.server:main"
210
+
211
+ [build-system]
212
+ requires = ["hatchling"]
213
+ build-backend = "hatchling.build"
214
+ ```
215
+
216
+ ---
217
+
218
+ ## Local testing
219
+
220
+ ```bash
221
+ # Install in dev mode
222
+ uv pip install -e .
223
+
224
+ # Start the server (stdio)
225
+ devcoach
226
+
227
+ # Or with uvx directly from the repo
228
+ uvx --from . devcoach
229
+
230
+ # Test with MCP Inspector
231
+ npx @modelcontextprotocol/inspector devcoach
232
+ ```
233
+
234
+ ---
235
+
236
+ ## Claude Code / Claude Desktop configuration
237
+
238
+ ```json
239
+ {
240
+ "mcpServers": {
241
+ "devcoach": {
242
+ "command": "uvx",
243
+ "args": ["devcoach"]
244
+ }
245
+ }
246
+ }
247
+ ```
248
+
249
+ ---
250
+
251
+ ## Development conventions
252
+
253
+ - All code is **type-annotated** (mypy-compatible)
254
+ - No external dependencies beyond `fastmcp` and `pydantic`
255
+ - `db.py` exposes only pure functions — no business logic
256
+ - `coach.py` never imports from `server.py` (one-way dependency)
257
+ - DB errors: always `try/except` with graceful fallback, never crash the server
258
+ - DB path: always `Path.home() / ".devcoach" / "coaching.db"`, never hardcoded
259
+
260
+ ---
261
+
262
+ ## Recommended development order
263
+
264
+ 1. `pyproject.toml` + `.gitignore` + folder structure
265
+ 2. `models.py` — Pydantic models
266
+ 3. `db.py` — schema + init + query helpers
267
+ 4. `coach.py` — rate limit + lesson selection
268
+ 5. `server.py` — FastMCP app + tool wiring + MCP prompt (`devcoach_instructions`)
269
+ 6. `SKILL.md` — coaching instructions (read by the MCP prompt at runtime)
270
+ 7. `prompts.py` — lesson templates (optional for MVP)
271
+ 8. Test with MCP Inspector
272
+ 9. README.md
273
+ 10. Publish to PyPI with `uv publish`