devcoach 0.3.5__tar.gz → 0.3.8__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.3.8/.github/scripts/fixtures/devcoach-backup.zip +0 -0
- devcoach-0.3.8/.github/scripts/take_screenshots.py +110 -0
- {devcoach-0.3.5 → devcoach-0.3.8}/.github/workflows/ci.yml +102 -6
- {devcoach-0.3.5 → devcoach-0.3.8}/.github/workflows/ruff-autofix.yml +1 -1
- devcoach-0.3.8/.github/workflows/update-screenshots.yml +39 -0
- {devcoach-0.3.5 → devcoach-0.3.8}/CLAUDE.md +4 -3
- {devcoach-0.3.5 → devcoach-0.3.8}/PKG-INFO +23 -10
- {devcoach-0.3.5 → devcoach-0.3.8}/README.md +21 -7
- {devcoach-0.3.5 → devcoach-0.3.8}/docs/cli.md +38 -4
- devcoach-0.3.8/docs/favicon.svg +1 -0
- {devcoach-0.3.5 → devcoach-0.3.8}/docs/getting-started.md +14 -4
- devcoach-0.3.8/docs/index.md +55 -0
- {devcoach-0.3.5 → devcoach-0.3.8}/docs/mcp-server.md +9 -4
- devcoach-0.3.8/docs/screenshots/knowledge-map-dark.png +0 -0
- devcoach-0.3.8/docs/screenshots/knowledge-map-light.png +0 -0
- devcoach-0.3.8/docs/screenshots/lessons-dark.png +0 -0
- devcoach-0.3.8/docs/screenshots/lessons-light.png +0 -0
- devcoach-0.3.8/docs/screenshots/settings-dark.png +0 -0
- devcoach-0.3.8/docs/screenshots/settings-light.png +0 -0
- devcoach-0.3.8/mkdocs.yml +54 -0
- {devcoach-0.3.5 → devcoach-0.3.8}/pyproject.toml +15 -4
- devcoach-0.3.8/sonar-project.properties +11 -0
- {devcoach-0.3.5 → devcoach-0.3.8}/src/devcoach/SKILL.md +65 -68
- {devcoach-0.3.5 → devcoach-0.3.8}/src/devcoach/cli/commands.py +174 -26
- {devcoach-0.3.5 → devcoach-0.3.8}/src/devcoach/core/coach.py +2 -2
- {devcoach-0.3.5 → devcoach-0.3.8}/src/devcoach/core/db.py +21 -6
- {devcoach-0.3.5 → devcoach-0.3.8}/src/devcoach/core/git.py +11 -9
- {devcoach-0.3.5 → devcoach-0.3.8}/src/devcoach/core/models.py +44 -2
- {devcoach-0.3.5 → devcoach-0.3.8}/src/devcoach/mcp/server.py +79 -146
- {devcoach-0.3.5 → devcoach-0.3.8}/src/devcoach/web/app.py +37 -6
- {devcoach-0.3.5 → devcoach-0.3.8}/src/devcoach/web/templates/base.html +16 -5
- {devcoach-0.3.5 → devcoach-0.3.8}/src/devcoach/web/templates/lesson_detail.html +1 -0
- {devcoach-0.3.5 → devcoach-0.3.8}/src/devcoach/web/templates/lessons.html +31 -28
- {devcoach-0.3.5 → devcoach-0.3.8}/src/devcoach/web/templates/profile.html +8 -8
- {devcoach-0.3.5 → devcoach-0.3.8}/src/devcoach/web/templates/settings.html +26 -4
- {devcoach-0.3.5 → devcoach-0.3.8}/tests/test_cli.py +30 -9
- devcoach-0.3.8/tests/test_cli_commands.py +754 -0
- devcoach-0.3.8/tests/test_coach.py +159 -0
- devcoach-0.3.8/tests/test_db_extra.py +413 -0
- devcoach-0.3.8/tests/test_detect.py +111 -0
- devcoach-0.3.8/tests/test_git.py +140 -0
- devcoach-0.3.8/tests/test_mcp_server.py +532 -0
- devcoach-0.3.8/tests/test_prompts.py +126 -0
- {devcoach-0.3.5 → devcoach-0.3.8}/tests/test_web.py +24 -8
- devcoach-0.3.8/tests/test_web_extra.py +388 -0
- {devcoach-0.3.5 → devcoach-0.3.8}/uv.lock +412 -141
- {devcoach-0.3.5 → devcoach-0.3.8}/.github/dependabot.yml +0 -0
- {devcoach-0.3.5 → devcoach-0.3.8}/.gitignore +0 -0
- {devcoach-0.3.5 → devcoach-0.3.8}/LICENSE +0 -0
- {devcoach-0.3.5 → devcoach-0.3.8}/NOTICE +0 -0
- {devcoach-0.3.5 → devcoach-0.3.8}/SKILL.md +0 -0
- {devcoach-0.3.5 → devcoach-0.3.8}/docs/PLAN.md +0 -0
- {devcoach-0.3.5 → devcoach-0.3.8}/docs/configuration.md +0 -0
- {devcoach-0.3.5 → devcoach-0.3.8}/docs/web-ui.md +0 -0
- {devcoach-0.3.5 → devcoach-0.3.8}/src/devcoach/__init__.py +0 -0
- {devcoach-0.3.5 → devcoach-0.3.8}/src/devcoach/cli/__init__.py +0 -0
- {devcoach-0.3.5 → devcoach-0.3.8}/src/devcoach/core/__init__.py +0 -0
- {devcoach-0.3.5 → devcoach-0.3.8}/src/devcoach/core/detect.py +0 -0
- {devcoach-0.3.5 → devcoach-0.3.8}/src/devcoach/core/prompts.py +0 -0
- {devcoach-0.3.5 → devcoach-0.3.8}/src/devcoach/mcp/__init__.py +0 -0
- {devcoach-0.3.5 → devcoach-0.3.8}/src/devcoach/web/__init__.py +0 -0
- {devcoach-0.3.5 → devcoach-0.3.8}/src/devcoach/web/static/favicon.svg +0 -0
- {devcoach-0.3.5 → devcoach-0.3.8}/src/devcoach/web/static/relative-time.js +0 -0
- {devcoach-0.3.5 → devcoach-0.3.8}/src/devcoach/web/static/style.css +0 -0
- {devcoach-0.3.5 → devcoach-0.3.8}/src/devcoach/web/static/vendor/alpinejs.min.js +0 -0
- {devcoach-0.3.5 → devcoach-0.3.8}/src/devcoach/web/static/vendor/flatpickr-dark.min.css +0 -0
- {devcoach-0.3.5 → devcoach-0.3.8}/src/devcoach/web/static/vendor/flatpickr.min.css +0 -0
- {devcoach-0.3.5 → devcoach-0.3.8}/src/devcoach/web/static/vendor/flatpickr.min.js +0 -0
- {devcoach-0.3.5 → devcoach-0.3.8}/src/devcoach/web/static/vendor/highlight.min.js +0 -0
- {devcoach-0.3.5 → devcoach-0.3.8}/src/devcoach/web/static/vendor/hljs-dark.min.css +0 -0
- {devcoach-0.3.5 → devcoach-0.3.8}/src/devcoach/web/static/vendor/hljs-light.min.css +0 -0
- {devcoach-0.3.5 → devcoach-0.3.8}/src/devcoach/web/static/vendor/htmx.min.js +0 -0
- {devcoach-0.3.5 → devcoach-0.3.8}/src/devcoach/web/static/vendor/icons/bitbucket.svg +0 -0
- {devcoach-0.3.5 → devcoach-0.3.8}/src/devcoach/web/static/vendor/icons/github.svg +0 -0
- {devcoach-0.3.5 → devcoach-0.3.8}/src/devcoach/web/static/vendor/icons/gitlab.svg +0 -0
- {devcoach-0.3.5 → devcoach-0.3.8}/src/devcoach/web/static/vendor/icons/vscode.svg +0 -0
- {devcoach-0.3.5 → devcoach-0.3.8}/src/devcoach/web/static/vendor/marked.min.js +0 -0
- {devcoach-0.3.5 → devcoach-0.3.8}/src/devcoach/web/static/vendor/tailwind.js +0 -0
- {devcoach-0.3.5 → devcoach-0.3.8}/tests/__init__.py +0 -0
- {devcoach-0.3.5 → devcoach-0.3.8}/tests/conftest.py +0 -0
|
Binary file
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"""Take devcoach UI screenshots for documentation.
|
|
2
|
+
|
|
3
|
+
Flow:
|
|
4
|
+
1. Restore DB from .github/scripts/fixtures/devcoach-backup.zip via `devcoach restore`
|
|
5
|
+
2. Start `devcoach ui` on a fixed port
|
|
6
|
+
3. Capture light + dark screenshots of each page
|
|
7
|
+
4. Stop the server
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import signal
|
|
11
|
+
import subprocess
|
|
12
|
+
import sys
|
|
13
|
+
import time
|
|
14
|
+
import urllib.request
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
|
|
17
|
+
SCREENSHOTS_DIR = Path("docs/screenshots")
|
|
18
|
+
FIXTURES_DIR = Path(".github/scripts/fixtures")
|
|
19
|
+
BACKUP_ZIP = FIXTURES_DIR / "devcoach-backup.zip"
|
|
20
|
+
PORT = 7862
|
|
21
|
+
BASE_URL = f"http://localhost:{PORT}"
|
|
22
|
+
VIEWPORT = {"width": 1440, "height": 900}
|
|
23
|
+
|
|
24
|
+
PAGES = [
|
|
25
|
+
("knowledge-map", "/"),
|
|
26
|
+
("lessons", "/lessons"),
|
|
27
|
+
("settings", "/settings"),
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
def restore_db() -> None:
|
|
31
|
+
result = subprocess.run(
|
|
32
|
+
["devcoach", "restore", str(BACKUP_ZIP)],
|
|
33
|
+
check=True,
|
|
34
|
+
capture_output=True,
|
|
35
|
+
text=True,
|
|
36
|
+
)
|
|
37
|
+
print(result.stdout.strip())
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def wait_for_server(url: str, timeout: int = 30) -> None:
|
|
41
|
+
for _ in range(timeout):
|
|
42
|
+
try:
|
|
43
|
+
urllib.request.urlopen(url, timeout=1)
|
|
44
|
+
return
|
|
45
|
+
except Exception:
|
|
46
|
+
time.sleep(1)
|
|
47
|
+
raise TimeoutError(f"Server at {url} did not start within {timeout}s")
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def take_screenshots(server_proc: subprocess.Popen) -> None:
|
|
51
|
+
from playwright.sync_api import sync_playwright
|
|
52
|
+
|
|
53
|
+
SCREENSHOTS_DIR.mkdir(parents=True, exist_ok=True)
|
|
54
|
+
|
|
55
|
+
with sync_playwright() as pw:
|
|
56
|
+
browser = pw.chromium.launch()
|
|
57
|
+
|
|
58
|
+
ctx = browser.new_context(viewport=VIEWPORT, color_scheme="light")
|
|
59
|
+
page = ctx.new_page()
|
|
60
|
+
for name, path in PAGES:
|
|
61
|
+
page.goto(f"{BASE_URL}{path}")
|
|
62
|
+
page.wait_for_load_state("networkidle")
|
|
63
|
+
out = SCREENSHOTS_DIR / f"{name}-light.png"
|
|
64
|
+
page.screenshot(path=str(out))
|
|
65
|
+
print(f" saved {out}")
|
|
66
|
+
ctx.close()
|
|
67
|
+
|
|
68
|
+
ctx = browser.new_context(viewport=VIEWPORT, color_scheme="dark")
|
|
69
|
+
page = ctx.new_page()
|
|
70
|
+
for name, path in PAGES:
|
|
71
|
+
page.goto(f"{BASE_URL}{path}")
|
|
72
|
+
page.wait_for_load_state("networkidle")
|
|
73
|
+
out = SCREENSHOTS_DIR / f"{name}-dark.png"
|
|
74
|
+
page.screenshot(path=str(out))
|
|
75
|
+
print(f" saved {out}")
|
|
76
|
+
ctx.close()
|
|
77
|
+
|
|
78
|
+
browser.close()
|
|
79
|
+
|
|
80
|
+
server_proc.send_signal(signal.SIGTERM)
|
|
81
|
+
server_proc.wait(timeout=10)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def main() -> None:
|
|
85
|
+
if not BACKUP_ZIP.exists():
|
|
86
|
+
sys.exit(f"Backup not found: {BACKUP_ZIP}")
|
|
87
|
+
|
|
88
|
+
print(f"Restoring DB from {BACKUP_ZIP}…")
|
|
89
|
+
restore_db()
|
|
90
|
+
|
|
91
|
+
print(f"Starting devcoach UI on port {PORT}…")
|
|
92
|
+
proc = subprocess.Popen(
|
|
93
|
+
["devcoach", "ui", "--port", str(PORT)],
|
|
94
|
+
stdout=subprocess.DEVNULL,
|
|
95
|
+
stderr=subprocess.DEVNULL,
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
try:
|
|
99
|
+
wait_for_server(BASE_URL)
|
|
100
|
+
print("Taking screenshots…")
|
|
101
|
+
take_screenshots(proc)
|
|
102
|
+
except Exception:
|
|
103
|
+
proc.terminate()
|
|
104
|
+
raise
|
|
105
|
+
|
|
106
|
+
print("Done.")
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
if __name__ == "__main__":
|
|
110
|
+
main()
|
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
name: CI
|
|
2
2
|
|
|
3
|
+
run-name: >-
|
|
4
|
+
${{ github.event_name == 'workflow_dispatch'
|
|
5
|
+
&& format('CI — New release ({0})', inputs.version != '' && inputs.version || inputs.bump)
|
|
6
|
+
|| (startsWith(github.ref, 'refs/tags/v')
|
|
7
|
+
&& format('CI — New release ({0})', github.ref_name)
|
|
8
|
+
|| (github.event_name == 'pull_request'
|
|
9
|
+
&& format('CI - PR {0}', github.event.pull_request.title)
|
|
10
|
+
|| format('CI — {0}', github.event.head_commit.message))) }}
|
|
11
|
+
|
|
3
12
|
# Covers three scenarios:
|
|
4
13
|
#
|
|
5
|
-
# 1. Push to main / pull request → lint + test + build
|
|
14
|
+
# 1. Push to main / pull request → lint + test + build + sonar + pages
|
|
6
15
|
# 2. Tag push (v*) → lint + test + build + publish + GitHub Release
|
|
7
16
|
# 3. workflow_dispatch (Release) → bump version → tag → lint + test + build + publish + GitHub Release
|
|
8
17
|
#
|
|
@@ -70,12 +79,14 @@ jobs:
|
|
|
70
79
|
echo "version=$NEXT" >> "$GITHUB_OUTPUT"
|
|
71
80
|
echo "tag=v$NEXT" >> "$GITHUB_OUTPUT"
|
|
72
81
|
|
|
73
|
-
- name: Bump version in pyproject.toml
|
|
74
|
-
run:
|
|
82
|
+
- name: Bump version in pyproject.toml and sonar-project.properties
|
|
83
|
+
run: |
|
|
84
|
+
sed -i 's/^version = ".*"/version = "${{ steps.ver.outputs.version }}"/' pyproject.toml
|
|
85
|
+
sed -i 's/^sonar.projectVersion=.*/sonar.projectVersion=${{ steps.ver.outputs.version }}/' sonar-project.properties
|
|
75
86
|
|
|
76
87
|
- name: Commit and tag
|
|
77
88
|
run: |
|
|
78
|
-
git add pyproject.toml
|
|
89
|
+
git add pyproject.toml sonar-project.properties
|
|
79
90
|
git commit -m "chore: bump version to ${{ steps.ver.outputs.version }}"
|
|
80
91
|
git tag "${{ steps.ver.outputs.tag }}"
|
|
81
92
|
git push --atomic origin main "${{ steps.ver.outputs.tag }}"
|
|
@@ -106,7 +117,7 @@ jobs:
|
|
|
106
117
|
strategy:
|
|
107
118
|
fail-fast: false
|
|
108
119
|
matrix:
|
|
109
|
-
python-version: ["3.
|
|
120
|
+
python-version: ["3.12", "3.13"]
|
|
110
121
|
steps:
|
|
111
122
|
- uses: actions/checkout@v6
|
|
112
123
|
with:
|
|
@@ -115,7 +126,8 @@ jobs:
|
|
|
115
126
|
with:
|
|
116
127
|
python-version: ${{ matrix.python-version }}
|
|
117
128
|
- run: uv sync --group dev
|
|
118
|
-
-
|
|
129
|
+
- name: Run tests
|
|
130
|
+
run: uv run pytest tests/ -v --tb=short
|
|
119
131
|
|
|
120
132
|
# ── Build ───────────────────────────────────────────────────────────────
|
|
121
133
|
build:
|
|
@@ -137,6 +149,90 @@ jobs:
|
|
|
137
149
|
path: dist/
|
|
138
150
|
retention-days: 7
|
|
139
151
|
|
|
152
|
+
# ── Coverage comment on PR ──────────────────────────────────────────────
|
|
153
|
+
# Posts (and updates) a coverage summary comment on every PR push.
|
|
154
|
+
# Reads the .coverage file generated by pytest-cov — no extra deps needed.
|
|
155
|
+
coverage:
|
|
156
|
+
name: Coverage comment
|
|
157
|
+
needs: [bump]
|
|
158
|
+
if: >
|
|
159
|
+
github.event_name == 'pull_request'
|
|
160
|
+
&& (needs.bump.result == 'success' || needs.bump.result == 'skipped')
|
|
161
|
+
runs-on: ubuntu-latest
|
|
162
|
+
permissions:
|
|
163
|
+
pull-requests: write
|
|
164
|
+
steps:
|
|
165
|
+
- uses: actions/checkout@v6
|
|
166
|
+
- uses: astral-sh/setup-uv@v7
|
|
167
|
+
with:
|
|
168
|
+
python-version: "3.12"
|
|
169
|
+
- run: uv sync --group dev
|
|
170
|
+
- name: Run tests with coverage
|
|
171
|
+
run: uv run pytest tests/ --cov=src/devcoach --cov-report=xml
|
|
172
|
+
- uses: py-cov-action/python-coverage-comment-action@v3
|
|
173
|
+
with:
|
|
174
|
+
GITHUB_TOKEN: ${{ github.token }}
|
|
175
|
+
MINIMUM_GREEN: 90
|
|
176
|
+
MINIMUM_ORANGE: 80
|
|
177
|
+
|
|
178
|
+
# ── SonarCloud scan + quality gate ─────────────────────────────────────
|
|
179
|
+
# CI-based analysis: feeds coverage.xml to SonarCloud.
|
|
180
|
+
# Skipped on pull_request events — SONAR_TOKEN is not available to PR
|
|
181
|
+
# workflows (GitHub restricts secret access). Runs on main / tags / dispatch
|
|
182
|
+
# where the token is available.
|
|
183
|
+
# Requires Automatic Analysis to be DISABLED on sonarcloud.io
|
|
184
|
+
# (Administration → Analysis Method).
|
|
185
|
+
sonar:
|
|
186
|
+
name: SonarCloud scan
|
|
187
|
+
needs: [bump, test]
|
|
188
|
+
if: >
|
|
189
|
+
always()
|
|
190
|
+
&& github.event_name != 'pull_request'
|
|
191
|
+
&& (needs.bump.result == 'success' || needs.bump.result == 'skipped')
|
|
192
|
+
&& needs.test.result == 'success'
|
|
193
|
+
runs-on: ubuntu-latest
|
|
194
|
+
steps:
|
|
195
|
+
- uses: actions/checkout@v6
|
|
196
|
+
with:
|
|
197
|
+
ref: ${{ needs.bump.outputs.tag || github.ref }}
|
|
198
|
+
fetch-depth: 0
|
|
199
|
+
- uses: astral-sh/setup-uv@v7
|
|
200
|
+
with:
|
|
201
|
+
python-version: "3.12"
|
|
202
|
+
- run: uv sync --group dev
|
|
203
|
+
- name: Run tests with coverage
|
|
204
|
+
run: uv run pytest tests/ -v --tb=short --cov=src/devcoach --cov-report=xml
|
|
205
|
+
- uses: sonarsource/sonarqube-scan-action@v7
|
|
206
|
+
env:
|
|
207
|
+
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
|
208
|
+
|
|
209
|
+
# ── GitHub Pages ────────────────────────────────────────────────────────
|
|
210
|
+
# Builds MkDocs site and deploys to GitHub Pages on every push to main.
|
|
211
|
+
pages:
|
|
212
|
+
name: Deploy docs to GitHub Pages
|
|
213
|
+
needs: [bump, build]
|
|
214
|
+
if: always() && needs.build.result == 'success' && github.ref == 'refs/heads/main' && github.event_name == 'push'
|
|
215
|
+
runs-on: ubuntu-latest
|
|
216
|
+
permissions:
|
|
217
|
+
pages: write
|
|
218
|
+
id-token: write
|
|
219
|
+
environment:
|
|
220
|
+
name: github-pages
|
|
221
|
+
url: ${{ steps.deploy.outputs.page_url }}
|
|
222
|
+
steps:
|
|
223
|
+
- uses: actions/checkout@v6
|
|
224
|
+
- uses: astral-sh/setup-uv@v7
|
|
225
|
+
with:
|
|
226
|
+
python-version: "3.12"
|
|
227
|
+
- run: uv sync --group docs
|
|
228
|
+
- run: uv run mkdocs build
|
|
229
|
+
- uses: actions/upload-pages-artifact@v5
|
|
230
|
+
with:
|
|
231
|
+
path: site/
|
|
232
|
+
- name: Deploy to GitHub Pages
|
|
233
|
+
id: deploy
|
|
234
|
+
uses: actions/deploy-pages@v5
|
|
235
|
+
|
|
140
236
|
# ── Publish ─────────────────────────────────────────────────────────────
|
|
141
237
|
# Runs when a tag is present: either created by the bump job (workflow_dispatch)
|
|
142
238
|
# or pushed directly (git push origin v1.2.3).
|
|
@@ -30,7 +30,7 @@ jobs:
|
|
|
30
30
|
run: uv run ruff format src/ tests/
|
|
31
31
|
|
|
32
32
|
- name: Open PR if there are changes
|
|
33
|
-
uses: peter-evans/create-pull-request@
|
|
33
|
+
uses: peter-evans/create-pull-request@v8
|
|
34
34
|
with:
|
|
35
35
|
commit-message: "style: apply ruff auto-fixes"
|
|
36
36
|
title: "style: ruff auto-fix"
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
name: Update documentation screenshots (GitHub Pages)
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
|
|
6
|
+
jobs:
|
|
7
|
+
screenshots:
|
|
8
|
+
name: Take and commit screenshots
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
permissions:
|
|
11
|
+
contents: write
|
|
12
|
+
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v6
|
|
15
|
+
|
|
16
|
+
- uses: astral-sh/setup-uv@v7
|
|
17
|
+
with:
|
|
18
|
+
python-version: "3.12"
|
|
19
|
+
|
|
20
|
+
- name: Install devcoach
|
|
21
|
+
run: uv sync
|
|
22
|
+
|
|
23
|
+
- name: Install Playwright + Chromium
|
|
24
|
+
run: |
|
|
25
|
+
uv pip install playwright
|
|
26
|
+
uv run playwright install chromium --with-deps
|
|
27
|
+
|
|
28
|
+
- name: Restore DB and take screenshots
|
|
29
|
+
run: uv run python .github/scripts/take_screenshots.py
|
|
30
|
+
|
|
31
|
+
- name: Commit screenshots
|
|
32
|
+
run: |
|
|
33
|
+
git config user.name "github-actions[bot]"
|
|
34
|
+
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
35
|
+
git add docs/screenshots/
|
|
36
|
+
git diff --staged --quiet && echo "No changes." || (
|
|
37
|
+
git commit -m "docs: update screenshots [skip ci]" &&
|
|
38
|
+
git push
|
|
39
|
+
)
|
|
@@ -9,7 +9,7 @@ based on the user's knowledge map, the rate limit, and what has already been tau
|
|
|
9
9
|
|
|
10
10
|
Repo: https://github.com/UltimaPhoenix/dev-coach
|
|
11
11
|
PyPI package name: `devcoach`
|
|
12
|
-
End-user command: `uvx devcoach`
|
|
12
|
+
End-user command: `uvx devcoach` (CLI) / `uvx devcoach mcp` (MCP server)
|
|
13
13
|
|
|
14
14
|
---
|
|
15
15
|
|
|
@@ -240,7 +240,7 @@ npx @modelcontextprotocol/inspector devcoach
|
|
|
240
240
|
"mcpServers": {
|
|
241
241
|
"devcoach": {
|
|
242
242
|
"command": "uvx",
|
|
243
|
-
"args": ["devcoach"]
|
|
243
|
+
"args": ["devcoach", "mcp"]
|
|
244
244
|
}
|
|
245
245
|
}
|
|
246
246
|
}
|
|
@@ -252,7 +252,8 @@ npx @modelcontextprotocol/inspector devcoach
|
|
|
252
252
|
|
|
253
253
|
- Follow **Uncle Bob's Clean Code** principles
|
|
254
254
|
- Follow **PEP** standards
|
|
255
|
-
- Linting and formatting enforced by **ruff** — run `uv run ruff check src/ tests/` and `uv run ruff format src/ tests/` before committing
|
|
255
|
+
- Linting and formatting enforced by **ruff** — run `uv run ruff check src/ tests/` and `uv run ruff format src/ tests/` before committing. **All ruff checks must pass before committing.**
|
|
256
|
+
- Test coverage must stay **at or above 80%** — run `uv run pytest --cov=src/devcoach --cov-fail-under=80` to verify. Do not merge code that drops total coverage below this threshold.
|
|
256
257
|
- No external dependencies beyond `fastmcp` and `pydantic`
|
|
257
258
|
- `db.py` exposes only pure functions — no business logic
|
|
258
259
|
- `coach.py` never imports from `server.py` (one-way dependency)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: devcoach
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.8
|
|
4
4
|
Summary: A local MCP server that acts as a progressive technical coach for Claude Code and Claude Desktop
|
|
5
5
|
Project-URL: Homepage, https://github.com/UltimaPhoenix/dev-coach
|
|
6
6
|
Project-URL: Repository, https://github.com/UltimaPhoenix/dev-coach
|
|
@@ -216,12 +216,11 @@ Classifier: Intended Audience :: Developers
|
|
|
216
216
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
217
217
|
Classifier: Operating System :: OS Independent
|
|
218
218
|
Classifier: Programming Language :: Python :: 3
|
|
219
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
220
219
|
Classifier: Programming Language :: Python :: 3.12
|
|
221
220
|
Classifier: Programming Language :: Python :: 3.13
|
|
222
221
|
Classifier: Topic :: Education
|
|
223
222
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
224
|
-
Requires-Python: >=3.
|
|
223
|
+
Requires-Python: >=3.12
|
|
225
224
|
Requires-Dist: fastapi>=0.110
|
|
226
225
|
Requires-Dist: fastmcp>=2.0
|
|
227
226
|
Requires-Dist: jinja2>=3.1
|
|
@@ -233,8 +232,11 @@ Description-Content-Type: text/markdown
|
|
|
233
232
|
# devcoach
|
|
234
233
|
|
|
235
234
|
[](https://pypi.org/project/devcoach/)
|
|
236
|
-
[](https://pypi.org/project/devcoach/)
|
|
237
236
|
[](https://github.com/UltimaPhoenix/dev-coach/actions/workflows/ci.yml)
|
|
237
|
+
[](https://sonarcloud.io/summary/new_code?id=UltimaPhoenix_dev-coach)
|
|
238
|
+
[](https://sonarcloud.io/summary/new_code?id=UltimaPhoenix_dev-coach)
|
|
239
|
+
[](https://ultimaphoenix.github.io/dev-coach/)
|
|
238
240
|
[](LICENSE)
|
|
239
241
|
|
|
240
242
|
**Progressive technical coaching, directly in Claude.** After every task you complete with Claude Code or Claude Desktop, devcoach delivers a short, targeted lesson based on what you already know — no generic tutorials, no repeated topics.
|
|
@@ -254,12 +256,20 @@ Everything runs **locally**. No data leaves your machine. One SQLite file at `~/
|
|
|
254
256
|
|
|
255
257
|
---
|
|
256
258
|
|
|
259
|
+
## Screenshots
|
|
260
|
+
|
|
261
|
+
| Knowledge map | Lesson history | Settings |
|
|
262
|
+
|:---------------------------------------------------------:|:---:|:---:|
|
|
263
|
+
|  |  |  |
|
|
264
|
+
|
|
265
|
+
---
|
|
266
|
+
|
|
257
267
|
## Installation
|
|
258
268
|
|
|
259
269
|
### Recommended — no permanent install needed
|
|
260
270
|
|
|
261
271
|
```bash
|
|
262
|
-
uvx devcoach
|
|
272
|
+
uvx devcoach mcp # starts the MCP server directly
|
|
263
273
|
```
|
|
264
274
|
|
|
265
275
|
### Permanent install
|
|
@@ -276,7 +286,7 @@ devcoach install
|
|
|
276
286
|
|
|
277
287
|
Restart Claude Code or Claude Desktop after installing.
|
|
278
288
|
|
|
279
|
-
> **Requirements:** [uv](https://docs.astral.sh/uv/) · Python 3.
|
|
289
|
+
> **Requirements:** [uv](https://docs.astral.sh/uv/) · Python 3.12+ · Claude Code or Claude Desktop
|
|
280
290
|
|
|
281
291
|
---
|
|
282
292
|
|
|
@@ -311,7 +321,7 @@ Claude: [does the work]
|
|
|
311
321
|
|
|
312
322
|
**Structured concurrency with asyncio.TaskGroup**
|
|
313
323
|
|
|
314
|
-
TaskGroup (Python 3.
|
|
324
|
+
TaskGroup (Python 3.12+) is the modern replacement for bare gather() calls.
|
|
315
325
|
Unlike gather(), it cancels sibling tasks automatically when one raises...
|
|
316
326
|
```
|
|
317
327
|
|
|
@@ -330,13 +340,16 @@ devcoach feedback lesson-python-taskgroup-001 dont_know # need to revisit —
|
|
|
330
340
|
|
|
331
341
|
| Command | Description |
|
|
332
342
|
|---------|-------------|
|
|
343
|
+
| `devcoach` | Show all available commands |
|
|
344
|
+
| `devcoach mcp` | Start the MCP server (stdio) for Claude Code / Claude Desktop |
|
|
333
345
|
| `devcoach setup` | Run the onboarding wizard in the terminal |
|
|
334
346
|
| `devcoach install` | Register with Claude Code / Claude Desktop |
|
|
335
347
|
| `devcoach profile` | Show your knowledge map with confidence bars |
|
|
336
348
|
| `devcoach stats` | Overview: lesson counts, weakest/strongest topics |
|
|
337
349
|
| `devcoach lessons` | Browse lesson history with filters |
|
|
338
350
|
| `devcoach lesson <id>` | Show a single lesson in full |
|
|
339
|
-
| `devcoach star <id>` |
|
|
351
|
+
| `devcoach star <id>` | Mark a lesson as starred (favourite) |
|
|
352
|
+
| `devcoach unstar <id>` | Remove the starred mark from a lesson |
|
|
340
353
|
| `devcoach feedback <id> <know\|dont_know\|clear>` | Record comprehension |
|
|
341
354
|
| `devcoach set max_per_day <n>` | Max lessons in a 24-hour window (default 2) |
|
|
342
355
|
| `devcoach set min_gap_minutes <n>` | Minimum minutes between lessons (default 240) |
|
|
@@ -376,7 +389,7 @@ devcoach implements the [MCP 2025-11-25 spec](https://modelcontextprotocol.io/sp
|
|
|
376
389
|
"devcoach": {
|
|
377
390
|
"type": "stdio",
|
|
378
391
|
"command": "uvx",
|
|
379
|
-
"args": ["devcoach"]
|
|
392
|
+
"args": ["devcoach", "mcp"]
|
|
380
393
|
}
|
|
381
394
|
}
|
|
382
395
|
}
|
|
@@ -418,7 +431,7 @@ git tag v1.2.3
|
|
|
418
431
|
git push origin v1.2.3
|
|
419
432
|
```
|
|
420
433
|
|
|
421
|
-
The pipeline will lint, test across Python 3.
|
|
434
|
+
The pipeline will lint, test across Python 3.12–3.13, build, publish to PyPI via OIDC Trusted Publishing, and create a GitHub Release automatically.
|
|
422
435
|
|
|
423
436
|
> **First-time PyPI setup:** configure a Trusted Publisher on PyPI for `UltimaPhoenix/dev-coach` (environment: `pypi`, workflow: `ci.yml`). No API token required after that.
|
|
424
437
|
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
# devcoach
|
|
2
2
|
|
|
3
3
|
[](https://pypi.org/project/devcoach/)
|
|
4
|
-
[](https://pypi.org/project/devcoach/)
|
|
5
5
|
[](https://github.com/UltimaPhoenix/dev-coach/actions/workflows/ci.yml)
|
|
6
|
+
[](https://sonarcloud.io/summary/new_code?id=UltimaPhoenix_dev-coach)
|
|
7
|
+
[](https://sonarcloud.io/summary/new_code?id=UltimaPhoenix_dev-coach)
|
|
8
|
+
[](https://ultimaphoenix.github.io/dev-coach/)
|
|
6
9
|
[](LICENSE)
|
|
7
10
|
|
|
8
11
|
**Progressive technical coaching, directly in Claude.** After every task you complete with Claude Code or Claude Desktop, devcoach delivers a short, targeted lesson based on what you already know — no generic tutorials, no repeated topics.
|
|
@@ -22,12 +25,20 @@ Everything runs **locally**. No data leaves your machine. One SQLite file at `~/
|
|
|
22
25
|
|
|
23
26
|
---
|
|
24
27
|
|
|
28
|
+
## Screenshots
|
|
29
|
+
|
|
30
|
+
| Knowledge map | Lesson history | Settings |
|
|
31
|
+
|:---------------------------------------------------------:|:---:|:---:|
|
|
32
|
+
|  |  |  |
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
25
36
|
## Installation
|
|
26
37
|
|
|
27
38
|
### Recommended — no permanent install needed
|
|
28
39
|
|
|
29
40
|
```bash
|
|
30
|
-
uvx devcoach
|
|
41
|
+
uvx devcoach mcp # starts the MCP server directly
|
|
31
42
|
```
|
|
32
43
|
|
|
33
44
|
### Permanent install
|
|
@@ -44,7 +55,7 @@ devcoach install
|
|
|
44
55
|
|
|
45
56
|
Restart Claude Code or Claude Desktop after installing.
|
|
46
57
|
|
|
47
|
-
> **Requirements:** [uv](https://docs.astral.sh/uv/) · Python 3.
|
|
58
|
+
> **Requirements:** [uv](https://docs.astral.sh/uv/) · Python 3.12+ · Claude Code or Claude Desktop
|
|
48
59
|
|
|
49
60
|
---
|
|
50
61
|
|
|
@@ -79,7 +90,7 @@ Claude: [does the work]
|
|
|
79
90
|
|
|
80
91
|
**Structured concurrency with asyncio.TaskGroup**
|
|
81
92
|
|
|
82
|
-
TaskGroup (Python 3.
|
|
93
|
+
TaskGroup (Python 3.12+) is the modern replacement for bare gather() calls.
|
|
83
94
|
Unlike gather(), it cancels sibling tasks automatically when one raises...
|
|
84
95
|
```
|
|
85
96
|
|
|
@@ -98,13 +109,16 @@ devcoach feedback lesson-python-taskgroup-001 dont_know # need to revisit —
|
|
|
98
109
|
|
|
99
110
|
| Command | Description |
|
|
100
111
|
|---------|-------------|
|
|
112
|
+
| `devcoach` | Show all available commands |
|
|
113
|
+
| `devcoach mcp` | Start the MCP server (stdio) for Claude Code / Claude Desktop |
|
|
101
114
|
| `devcoach setup` | Run the onboarding wizard in the terminal |
|
|
102
115
|
| `devcoach install` | Register with Claude Code / Claude Desktop |
|
|
103
116
|
| `devcoach profile` | Show your knowledge map with confidence bars |
|
|
104
117
|
| `devcoach stats` | Overview: lesson counts, weakest/strongest topics |
|
|
105
118
|
| `devcoach lessons` | Browse lesson history with filters |
|
|
106
119
|
| `devcoach lesson <id>` | Show a single lesson in full |
|
|
107
|
-
| `devcoach star <id>` |
|
|
120
|
+
| `devcoach star <id>` | Mark a lesson as starred (favourite) |
|
|
121
|
+
| `devcoach unstar <id>` | Remove the starred mark from a lesson |
|
|
108
122
|
| `devcoach feedback <id> <know\|dont_know\|clear>` | Record comprehension |
|
|
109
123
|
| `devcoach set max_per_day <n>` | Max lessons in a 24-hour window (default 2) |
|
|
110
124
|
| `devcoach set min_gap_minutes <n>` | Minimum minutes between lessons (default 240) |
|
|
@@ -144,7 +158,7 @@ devcoach implements the [MCP 2025-11-25 spec](https://modelcontextprotocol.io/sp
|
|
|
144
158
|
"devcoach": {
|
|
145
159
|
"type": "stdio",
|
|
146
160
|
"command": "uvx",
|
|
147
|
-
"args": ["devcoach"]
|
|
161
|
+
"args": ["devcoach", "mcp"]
|
|
148
162
|
}
|
|
149
163
|
}
|
|
150
164
|
}
|
|
@@ -186,7 +200,7 @@ git tag v1.2.3
|
|
|
186
200
|
git push origin v1.2.3
|
|
187
201
|
```
|
|
188
202
|
|
|
189
|
-
The pipeline will lint, test across Python 3.
|
|
203
|
+
The pipeline will lint, test across Python 3.12–3.13, build, publish to PyPI via OIDC Trusted Publishing, and create a GitHub Release automatically.
|
|
190
204
|
|
|
191
205
|
> **First-time PyPI setup:** configure a Trusted Publisher on PyPI for `UltimaPhoenix/dev-coach` (environment: `pypi`, workflow: `ci.yml`). No API token required after that.
|
|
192
206
|
|
|
@@ -1,15 +1,38 @@
|
|
|
1
1
|
# CLI reference
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
Running `devcoach` with no arguments prints a help panel listing every available command:
|
|
4
6
|
|
|
5
7
|
```
|
|
6
|
-
devcoach
|
|
8
|
+
devcoach
|
|
7
9
|
```
|
|
8
10
|
|
|
9
11
|
All commands operate on `~/.devcoach/coaching.db`. No network access required.
|
|
10
12
|
|
|
11
13
|
---
|
|
12
14
|
|
|
15
|
+
## MCP server
|
|
16
|
+
|
|
17
|
+
### `devcoach mcp`
|
|
18
|
+
|
|
19
|
+
Start the stdio MCP server for Claude Code or Claude Desktop. This is what you put in your MCP config:
|
|
20
|
+
|
|
21
|
+
```json
|
|
22
|
+
{
|
|
23
|
+
"mcpServers": {
|
|
24
|
+
"devcoach": {
|
|
25
|
+
"command": "uvx",
|
|
26
|
+
"args": ["devcoach", "mcp"]
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
13
36
|
## Knowledge map
|
|
14
37
|
|
|
15
38
|
### `devcoach profile`
|
|
@@ -117,13 +140,24 @@ devcoach lesson lesson-python-generators-001
|
|
|
117
140
|
|
|
118
141
|
### `devcoach star <id>`
|
|
119
142
|
|
|
120
|
-
|
|
143
|
+
Mark a lesson as starred (favourite).
|
|
121
144
|
|
|
122
145
|
```bash
|
|
123
146
|
devcoach star lesson-python-generators-001
|
|
124
147
|
# → Lesson lesson-python-generators-001 → ★ starred
|
|
125
148
|
```
|
|
126
149
|
|
|
150
|
+
### `devcoach unstar <id>`
|
|
151
|
+
|
|
152
|
+
Remove the starred mark from a lesson.
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
devcoach unstar lesson-python-generators-001
|
|
156
|
+
# → Lesson lesson-python-generators-001 → ☆ unstarred
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Both commands are idempotent — calling them when the lesson is already in the target state is safe.
|
|
160
|
+
|
|
127
161
|
### `devcoach feedback <id> <value>`
|
|
128
162
|
|
|
129
163
|
Record whether you understood a lesson. Adjusts knowledge confidence.
|
|
@@ -191,7 +225,7 @@ Followed by optional group assignment and rate-limit settings.
|
|
|
191
225
|
|
|
192
226
|
### `devcoach install`
|
|
193
227
|
|
|
194
|
-
Register the devcoach MCP server in Claude's config files.
|
|
228
|
+
Register the devcoach MCP server (`devcoach mcp`) in Claude's config files.
|
|
195
229
|
|
|
196
230
|
```bash
|
|
197
231
|
devcoach install # both Claude Code + Claude Desktop
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
../src/devcoach/web/static/favicon.svg
|
|
@@ -3,25 +3,35 @@
|
|
|
3
3
|
## Prerequisites
|
|
4
4
|
|
|
5
5
|
- [uv](https://docs.astral.sh/uv/) (recommended) or pip
|
|
6
|
-
- Python 3.
|
|
6
|
+
- Python 3.12+
|
|
7
7
|
- Claude Code or Claude Desktop
|
|
8
8
|
|
|
9
9
|
---
|
|
10
10
|
|
|
11
11
|
## 1. Install
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
devcoach is published on [PyPI](https://pypi.org/project/devcoach/) and can be installed with any Python package manager.
|
|
14
|
+
|
|
15
|
+
### One-time run — no install needed
|
|
14
16
|
|
|
15
17
|
```bash
|
|
16
|
-
uvx devcoach
|
|
18
|
+
uvx devcoach mcp
|
|
17
19
|
```
|
|
18
20
|
|
|
19
|
-
|
|
21
|
+
`uvx` fetches the latest release from PyPI and runs it in an isolated environment. Nothing is left behind.
|
|
22
|
+
|
|
23
|
+
### Permanent install with uv (recommended)
|
|
20
24
|
|
|
21
25
|
```bash
|
|
22
26
|
uv tool install devcoach
|
|
23
27
|
```
|
|
24
28
|
|
|
29
|
+
### Permanent install with pip
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
pip install devcoach
|
|
33
|
+
```
|
|
34
|
+
|
|
25
35
|
---
|
|
26
36
|
|
|
27
37
|
## 2. Register with Claude
|