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.
- devcoach-0.1.0/.github/dependabot.yml +40 -0
- devcoach-0.1.0/.github/workflows/ci.yml +110 -0
- devcoach-0.1.0/.github/workflows/ruff-autofix.yml +44 -0
- devcoach-0.1.0/.gitignore +211 -0
- devcoach-0.1.0/CLAUDE.md +273 -0
- devcoach-0.1.0/LICENSE +201 -0
- devcoach-0.1.0/NOTICE +20 -0
- devcoach-0.1.0/PKG-INFO +443 -0
- devcoach-0.1.0/README.md +211 -0
- devcoach-0.1.0/SKILL.md +185 -0
- devcoach-0.1.0/docs/PLAN.md +144 -0
- devcoach-0.1.0/docs/cli.md +235 -0
- devcoach-0.1.0/docs/configuration.md +127 -0
- devcoach-0.1.0/docs/getting-started.md +142 -0
- devcoach-0.1.0/docs/mcp-server.md +274 -0
- devcoach-0.1.0/docs/web-ui.md +88 -0
- devcoach-0.1.0/pyproject.toml +65 -0
- devcoach-0.1.0/src/devcoach/SKILL.md +288 -0
- devcoach-0.1.0/src/devcoach/__init__.py +3 -0
- devcoach-0.1.0/src/devcoach/cli/__init__.py +0 -0
- devcoach-0.1.0/src/devcoach/cli/commands.py +793 -0
- devcoach-0.1.0/src/devcoach/core/__init__.py +0 -0
- devcoach-0.1.0/src/devcoach/core/coach.py +141 -0
- devcoach-0.1.0/src/devcoach/core/db.py +768 -0
- devcoach-0.1.0/src/devcoach/core/detect.py +132 -0
- devcoach-0.1.0/src/devcoach/core/git.py +97 -0
- devcoach-0.1.0/src/devcoach/core/models.py +104 -0
- devcoach-0.1.0/src/devcoach/core/prompts.py +52 -0
- devcoach-0.1.0/src/devcoach/mcp/__init__.py +0 -0
- devcoach-0.1.0/src/devcoach/mcp/server.py +545 -0
- devcoach-0.1.0/src/devcoach/web/__init__.py +0 -0
- devcoach-0.1.0/src/devcoach/web/app.py +319 -0
- devcoach-0.1.0/src/devcoach/web/static/favicon.svg +3 -0
- devcoach-0.1.0/src/devcoach/web/static/relative-time.js +24 -0
- devcoach-0.1.0/src/devcoach/web/static/style.css +163 -0
- devcoach-0.1.0/src/devcoach/web/static/vendor/alpinejs.min.js +5 -0
- devcoach-0.1.0/src/devcoach/web/static/vendor/flatpickr-dark.min.css +795 -0
- devcoach-0.1.0/src/devcoach/web/static/vendor/flatpickr.min.css +13 -0
- devcoach-0.1.0/src/devcoach/web/static/vendor/flatpickr.min.js +2 -0
- devcoach-0.1.0/src/devcoach/web/static/vendor/highlight.min.js +1232 -0
- devcoach-0.1.0/src/devcoach/web/static/vendor/hljs-dark.min.css +1 -0
- devcoach-0.1.0/src/devcoach/web/static/vendor/hljs-light.min.css +1 -0
- devcoach-0.1.0/src/devcoach/web/static/vendor/htmx.min.js +1 -0
- devcoach-0.1.0/src/devcoach/web/static/vendor/icons/bitbucket.svg +1 -0
- devcoach-0.1.0/src/devcoach/web/static/vendor/icons/github.svg +1 -0
- devcoach-0.1.0/src/devcoach/web/static/vendor/icons/gitlab.svg +1 -0
- devcoach-0.1.0/src/devcoach/web/static/vendor/icons/vscode.svg +41 -0
- devcoach-0.1.0/src/devcoach/web/static/vendor/marked.min.js +6 -0
- devcoach-0.1.0/src/devcoach/web/static/vendor/tailwind.js +83 -0
- devcoach-0.1.0/src/devcoach/web/templates/base.html +80 -0
- devcoach-0.1.0/src/devcoach/web/templates/lesson_detail.html +215 -0
- devcoach-0.1.0/src/devcoach/web/templates/lessons.html +546 -0
- devcoach-0.1.0/src/devcoach/web/templates/profile.html +240 -0
- devcoach-0.1.0/src/devcoach/web/templates/settings.html +144 -0
- devcoach-0.1.0/tests/__init__.py +0 -0
- devcoach-0.1.0/tests/conftest.py +94 -0
- devcoach-0.1.0/tests/test_cli.py +343 -0
- devcoach-0.1.0/tests/test_web.py +351 -0
- 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__/
|
devcoach-0.1.0/CLAUDE.md
ADDED
|
@@ -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`
|