phrase-gym 0.2.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.
- phrase_gym-0.2.0/.coveragerc +17 -0
- phrase_gym-0.2.0/.github/workflows/ci.yml +117 -0
- phrase_gym-0.2.0/.github/workflows/docs.yml +43 -0
- phrase_gym-0.2.0/.github/workflows/pip_publish.yml +63 -0
- phrase_gym-0.2.0/.github/workflows/static.yml +24 -0
- phrase_gym-0.2.0/.gitignore +40 -0
- phrase_gym-0.2.0/.nvmrc +1 -0
- phrase_gym-0.2.0/.pre-commit-config.yaml +60 -0
- phrase_gym-0.2.0/.python-version +1 -0
- phrase_gym-0.2.0/AGENTS.md +102 -0
- phrase_gym-0.2.0/CLAUDE.md +1 -0
- phrase_gym-0.2.0/LICENSE +20 -0
- phrase_gym-0.2.0/PKG-INFO +139 -0
- phrase_gym-0.2.0/README.md +97 -0
- phrase_gym-0.2.0/activate.sh +14 -0
- phrase_gym-0.2.0/docs/common/reference.md +8 -0
- phrase_gym-0.2.0/docs/development.md +75 -0
- phrase_gym-0.2.0/docs/importing-phrases.md +104 -0
- phrase_gym-0.2.0/docs/mkdocs.yml +33 -0
- phrase_gym-0.2.0/docs/src/en/index.md +14 -0
- phrase_gym-0.2.0/docs/src/en/installation.md +32 -0
- phrase_gym-0.2.0/examples/meeting-phrases.json +44 -0
- phrase_gym-0.2.0/frontend/.prettierignore +6 -0
- phrase_gym-0.2.0/frontend/.prettierrc.json +6 -0
- phrase_gym-0.2.0/frontend/index.html +2 -0
- phrase_gym-0.2.0/frontend/openapi.json +1335 -0
- phrase_gym-0.2.0/frontend/package-lock.json +2332 -0
- phrase_gym-0.2.0/frontend/package.json +34 -0
- phrase_gym-0.2.0/frontend/playwright.config.ts +25 -0
- phrase_gym-0.2.0/frontend/src/App.vue +165 -0
- phrase_gym-0.2.0/frontend/src/api.generated.ts +923 -0
- phrase_gym-0.2.0/frontend/src/api.ts +27 -0
- phrase_gym-0.2.0/frontend/src/audioSession.ts +33 -0
- phrase_gym-0.2.0/frontend/src/captureOwners.ts +35 -0
- phrase_gym-0.2.0/frontend/src/components/AddMaterial.vue +166 -0
- phrase_gym-0.2.0/frontend/src/components/SavedHistory.vue +28 -0
- phrase_gym-0.2.0/frontend/src/components/StudyCard.vue +118 -0
- phrase_gym-0.2.0/frontend/src/composables/useCapture.ts +214 -0
- phrase_gym-0.2.0/frontend/src/composables/usePlayback.ts +118 -0
- phrase_gym-0.2.0/frontend/src/composables/useStudy.ts +49 -0
- phrase_gym-0.2.0/frontend/src/discardedLearnerData.ts +29 -0
- phrase_gym-0.2.0/frontend/src/main.ts +7 -0
- phrase_gym-0.2.0/frontend/src/practice.css +71 -0
- phrase_gym-0.2.0/frontend/src/practiceJournal.ts +128 -0
- phrase_gym-0.2.0/frontend/src/practiceTypes.ts +13 -0
- phrase_gym-0.2.0/frontend/src/study.css +97 -0
- phrase_gym-0.2.0/frontend/src/studyTypes.ts +6 -0
- phrase_gym-0.2.0/frontend/src/style.css +299 -0
- phrase_gym-0.2.0/frontend/src/takeJournalRecovery.ts +32 -0
- phrase_gym-0.2.0/frontend/src/takeTransfers.ts +82 -0
- phrase_gym-0.2.0/frontend/tests/audio-session.test.ts +59 -0
- phrase_gym-0.2.0/frontend/tests/browser/fixtures.ts +61 -0
- phrase_gym-0.2.0/frontend/tests/browser/offline-import.spec.ts +41 -0
- phrase_gym-0.2.0/frontend/tests/browser/silent-playback.ts +18 -0
- phrase_gym-0.2.0/frontend/tests/browser/study.spec.ts +774 -0
- phrase_gym-0.2.0/frontend/tests/journal-reset.test.ts +58 -0
- phrase_gym-0.2.0/frontend/tests/silent-contexts.test.ts +36 -0
- phrase_gym-0.2.0/frontend/tests/study.test.ts +51 -0
- phrase_gym-0.2.0/frontend/tests/take-transfers.test.ts +77 -0
- phrase_gym-0.2.0/frontend/tsconfig.json +15 -0
- phrase_gym-0.2.0/frontend/vite.config.ts +8 -0
- phrase_gym-0.2.0/frontend/vitest.config.ts +2 -0
- phrase_gym-0.2.0/invoke.yml +5 -0
- phrase_gym-0.2.0/pyproject.toml +63 -0
- phrase_gym-0.2.0/pytest.ini +4 -0
- phrase_gym-0.2.0/scripts/__init__.py +0 -0
- phrase_gym-0.2.0/scripts/generate_api.py +13 -0
- phrase_gym-0.2.0/scripts/release.py +90 -0
- phrase_gym-0.2.0/scripts/verup.sh +4 -0
- phrase_gym-0.2.0/scripts/verup_action.sh +4 -0
- phrase_gym-0.2.0/specs/START-HERE.md +17 -0
- phrase_gym-0.2.0/specs/architecture.md +63 -0
- phrase_gym-0.2.0/specs/decision-core-trainer.md +46 -0
- phrase_gym-0.2.0/specs/decision-external-preparation-agent.md +35 -0
- phrase_gym-0.2.0/specs/decision-online-only.md +9 -0
- phrase_gym-0.2.0/specs/decision-personal-preferences.md +21 -0
- phrase_gym-0.2.0/specs/decision-recording-and-comparison.md +31 -0
- phrase_gym-0.2.0/specs/decision-targeted-drilling.md +47 -0
- phrase_gym-0.2.0/specs/decision-transfer-and-engagement.md +39 -0
- phrase_gym-0.2.0/specs/open-problems.md +12 -0
- phrase_gym-0.2.0/specs/plan/implementation-plan.md +107 -0
- phrase_gym-0.2.0/specs/plan/orchestration-handout.md +38 -0
- phrase_gym-0.2.0/specs/research/README.md +37 -0
- phrase_gym-0.2.0/specs/research/automatic-use.md +33 -0
- phrase_gym-0.2.0/specs/research/motivation.md +18 -0
- phrase_gym-0.2.0/specs/research/phrase-prompts.md +63 -0
- phrase_gym-0.2.0/specs/research/spacing-and-grading.md +23 -0
- phrase_gym-0.2.0/specs/research/words-in-context.md +47 -0
- phrase_gym-0.2.0/src/phrase_gym/__about__.py +1 -0
- phrase_gym-0.2.0/src/phrase_gym/__init__.py +5 -0
- phrase_gym-0.2.0/src/phrase_gym/coach/__init__.py +0 -0
- phrase_gym-0.2.0/src/phrase_gym/coach/bounded_process.py +81 -0
- phrase_gym-0.2.0/src/phrase_gym/coach/main.py +66 -0
- phrase_gym-0.2.0/src/phrase_gym/coach/speech.py +174 -0
- phrase_gym-0.2.0/src/phrase_gym/deploy/__init__.py +0 -0
- phrase_gym-0.2.0/src/phrase_gym/server/__init__.py +0 -0
- phrase_gym-0.2.0/src/phrase_gym/server/api/__init__.py +0 -0
- phrase_gym-0.2.0/src/phrase_gym/server/api/coach.py +34 -0
- phrase_gym-0.2.0/src/phrase_gym/server/api/imports.py +41 -0
- phrase_gym-0.2.0/src/phrase_gym/server/api/recordings.py +92 -0
- phrase_gym-0.2.0/src/phrase_gym/server/api/study.py +38 -0
- phrase_gym-0.2.0/src/phrase_gym/server/app.py +43 -0
- phrase_gym-0.2.0/src/phrase_gym/server/boundary.py +94 -0
- phrase_gym-0.2.0/src/phrase_gym/server/contracts.py +147 -0
- phrase_gym-0.2.0/src/phrase_gym/server/importer.py +171 -0
- phrase_gym-0.2.0/src/phrase_gym/server/main.py +51 -0
- phrase_gym-0.2.0/src/phrase_gym/server/publication.py +61 -0
- phrase_gym-0.2.0/src/phrase_gym/server/reference_audio.py +48 -0
- phrase_gym-0.2.0/src/phrase_gym/server/static/assets/index-BtTv3vAH.js +19 -0
- phrase_gym-0.2.0/src/phrase_gym/server/static/assets/index-CKTwL26h.css +1 -0
- phrase_gym-0.2.0/src/phrase_gym/server/static/index.html +4 -0
- phrase_gym-0.2.0/src/phrase_gym/server/storage.py +101 -0
- phrase_gym-0.2.0/src/phrase_gym/server/study.py +187 -0
- phrase_gym-0.2.0/tasks.py +163 -0
- phrase_gym-0.2.0/tests/browser_server.py +29 -0
- phrase_gym-0.2.0/tests/conftest.py +41 -0
- phrase_gym-0.2.0/tests/test_boundary.py +35 -0
- phrase_gym-0.2.0/tests/test_cli.py +44 -0
- phrase_gym-0.2.0/tests/test_imports.py +203 -0
- phrase_gym-0.2.0/tests/test_release.py +133 -0
- phrase_gym-0.2.0/tests/test_speech.py +161 -0
- phrase_gym-0.2.0/tests/test_tailscale.py +62 -0
- phrase_gym-0.2.0/tests/test_tasks.py +98 -0
- phrase_gym-0.2.0/tests/test_workflow.py +124 -0
- phrase_gym-0.2.0/uv.lock +887 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
workflow_call:
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: write
|
|
11
|
+
pull-requests: write
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
server:
|
|
15
|
+
name: Server (Linux)
|
|
16
|
+
runs-on: ubuntu-24.04
|
|
17
|
+
concurrency:
|
|
18
|
+
group: github-pages
|
|
19
|
+
cancel-in-progress: false
|
|
20
|
+
steps:
|
|
21
|
+
- uses: actions/checkout@v4
|
|
22
|
+
- uses: astral-sh/setup-uv@v6
|
|
23
|
+
with:
|
|
24
|
+
version: 0.11.7
|
|
25
|
+
- uses: actions/setup-python@v5
|
|
26
|
+
with:
|
|
27
|
+
python-version: '3.13'
|
|
28
|
+
- run: uv sync --frozen
|
|
29
|
+
- run: >-
|
|
30
|
+
uv run pytest --junitxml=pytest.xml --alluredir=allure-results
|
|
31
|
+
--cov=phrase_gym --cov-report=term-missing --cov-report=xml --cov-report=html
|
|
32
|
+
- name: Upload test and coverage results
|
|
33
|
+
if: always()
|
|
34
|
+
uses: actions/upload-artifact@v4
|
|
35
|
+
with:
|
|
36
|
+
name: server-test-results
|
|
37
|
+
path: |
|
|
38
|
+
pytest.xml
|
|
39
|
+
coverage.xml
|
|
40
|
+
htmlcov/
|
|
41
|
+
allure-results/
|
|
42
|
+
- name: Load Allure history
|
|
43
|
+
if: always() && github.event_name == 'push' && github.ref == 'refs/heads/main'
|
|
44
|
+
uses: actions/checkout@v4
|
|
45
|
+
continue-on-error: true
|
|
46
|
+
with:
|
|
47
|
+
ref: gh-pages
|
|
48
|
+
path: gh-pages-dir
|
|
49
|
+
- name: Generate Allure report
|
|
50
|
+
if: always() && github.event_name == 'push' && github.ref == 'refs/heads/main'
|
|
51
|
+
uses: andgineer/allure-report@v3.6
|
|
52
|
+
id: allure-report
|
|
53
|
+
with:
|
|
54
|
+
allure-results: allure-results
|
|
55
|
+
website: gh-pages-dir
|
|
56
|
+
reports-site-path: builds/tests
|
|
57
|
+
- name: Publish Allure report
|
|
58
|
+
if: always() && steps.allure-report.outcome == 'success'
|
|
59
|
+
uses: peaceiris/actions-gh-pages@v4
|
|
60
|
+
with:
|
|
61
|
+
github_token: ${{ secrets.GITHUB_TOKEN }}
|
|
62
|
+
publish_branch: gh-pages
|
|
63
|
+
publish_dir: ${{ steps.allure-report.outputs.reports-site }}
|
|
64
|
+
destination_dir: ${{ steps.allure-report.outputs.reports-site-path }}
|
|
65
|
+
- name: Coverage comment
|
|
66
|
+
if: always() && (github.event_name == 'pull_request' || github.ref == 'refs/heads/main')
|
|
67
|
+
id: coverage-comment
|
|
68
|
+
uses: py-cov-action/python-coverage-comment-action@v3
|
|
69
|
+
with:
|
|
70
|
+
GITHUB_TOKEN: ${{ github.token }}
|
|
71
|
+
MINIMUM_GREEN: 85
|
|
72
|
+
MINIMUM_ORANGE: 70
|
|
73
|
+
- name: Store coverage comment
|
|
74
|
+
if: steps.coverage-comment.outputs.COMMENT_FILE_WRITTEN == 'true'
|
|
75
|
+
uses: actions/upload-artifact@v4
|
|
76
|
+
with:
|
|
77
|
+
name: python-coverage-comment-action
|
|
78
|
+
path: python-coverage-comment-action.txt
|
|
79
|
+
- name: Coveralls
|
|
80
|
+
if: always() && github.event_name != 'workflow_call'
|
|
81
|
+
continue-on-error: true
|
|
82
|
+
uses: coverallsapp/github-action@v2
|
|
83
|
+
- name: Codecov
|
|
84
|
+
if: always() && github.event_name != 'workflow_call'
|
|
85
|
+
continue-on-error: true
|
|
86
|
+
uses: codecov/codecov-action@v4.0.1
|
|
87
|
+
with:
|
|
88
|
+
fail_ci_if_error: true
|
|
89
|
+
token: ${{ secrets.CODECOV_TOKEN }}
|
|
90
|
+
checks:
|
|
91
|
+
name: Full checks (macOS)
|
|
92
|
+
runs-on: macos-15
|
|
93
|
+
steps:
|
|
94
|
+
- uses: actions/checkout@v4
|
|
95
|
+
- uses: astral-sh/setup-uv@v6
|
|
96
|
+
with:
|
|
97
|
+
version: 0.11.7
|
|
98
|
+
- uses: actions/setup-python@v5
|
|
99
|
+
with:
|
|
100
|
+
python-version: '3.13'
|
|
101
|
+
- uses: actions/setup-node@v4
|
|
102
|
+
with:
|
|
103
|
+
node-version-file: .nvmrc
|
|
104
|
+
cache: npm
|
|
105
|
+
cache-dependency-path: frontend/package-lock.json
|
|
106
|
+
- run: uv sync --frozen
|
|
107
|
+
- run: npm --prefix frontend ci
|
|
108
|
+
- run: npx playwright install --with-deps chromium webkit
|
|
109
|
+
working-directory: frontend
|
|
110
|
+
- run: uv run inv pre
|
|
111
|
+
- run: uv run inv test
|
|
112
|
+
- uses: actions/upload-artifact@v4
|
|
113
|
+
if: failure()
|
|
114
|
+
with:
|
|
115
|
+
name: browser-test-results
|
|
116
|
+
path: frontend/test-results/
|
|
117
|
+
if-no-files-found: ignore
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
name: docs
|
|
2
|
+
on:
|
|
3
|
+
push:
|
|
4
|
+
branches: [main]
|
|
5
|
+
pull_request:
|
|
6
|
+
permissions:
|
|
7
|
+
contents: write
|
|
8
|
+
jobs:
|
|
9
|
+
deploy:
|
|
10
|
+
runs-on: ubuntu-24.04
|
|
11
|
+
concurrency:
|
|
12
|
+
group: github-pages
|
|
13
|
+
cancel-in-progress: false
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
- uses: astral-sh/setup-uv@v6
|
|
17
|
+
with:
|
|
18
|
+
version: 0.11.7
|
|
19
|
+
- uses: actions/setup-python@v5
|
|
20
|
+
with:
|
|
21
|
+
python-version: '3.13'
|
|
22
|
+
- run: uv sync --frozen --all-extras
|
|
23
|
+
- run: uv run inv build-docs
|
|
24
|
+
- name: Load existing reports
|
|
25
|
+
if: github.event_name == 'push'
|
|
26
|
+
uses: actions/checkout@v4
|
|
27
|
+
continue-on-error: true
|
|
28
|
+
with:
|
|
29
|
+
ref: gh-pages
|
|
30
|
+
path: gh-pages
|
|
31
|
+
- name: Preserve test reports
|
|
32
|
+
if: github.event_name == 'push'
|
|
33
|
+
run: |
|
|
34
|
+
if [ -d gh-pages/builds ]; then
|
|
35
|
+
cp -r gh-pages/builds build/docs/site/builds
|
|
36
|
+
fi
|
|
37
|
+
- name: Publish documentation
|
|
38
|
+
if: github.event_name == 'push'
|
|
39
|
+
uses: peaceiris/actions-gh-pages@v4
|
|
40
|
+
with:
|
|
41
|
+
github_token: ${{ secrets.GITHUB_TOKEN }}
|
|
42
|
+
publish_branch: gh-pages
|
|
43
|
+
publish_dir: build/docs/site
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
name: Upload Python Package
|
|
2
|
+
on:
|
|
3
|
+
push:
|
|
4
|
+
tags: ['v*.*.*']
|
|
5
|
+
permissions:
|
|
6
|
+
contents: write
|
|
7
|
+
pull-requests: write
|
|
8
|
+
jobs:
|
|
9
|
+
check-tag:
|
|
10
|
+
runs-on: ubuntu-24.04
|
|
11
|
+
outputs:
|
|
12
|
+
version: ${{ steps.version.outputs.version }}
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
- uses: actions/setup-python@v5
|
|
16
|
+
with:
|
|
17
|
+
python-version: '3.13'
|
|
18
|
+
- name: Validate release tag and package version
|
|
19
|
+
id: version
|
|
20
|
+
env:
|
|
21
|
+
RELEASE_TAG: ${{ github.ref_name }}
|
|
22
|
+
run: |
|
|
23
|
+
python - <<'PY'
|
|
24
|
+
import os
|
|
25
|
+
from scripts.release import validate_tag
|
|
26
|
+
version = validate_tag(os.environ['RELEASE_TAG'])
|
|
27
|
+
with open(os.environ['GITHUB_OUTPUT'], 'a') as output:
|
|
28
|
+
output.write(f'version={version}\n')
|
|
29
|
+
PY
|
|
30
|
+
checks:
|
|
31
|
+
needs: check-tag
|
|
32
|
+
uses: ./.github/workflows/ci.yml
|
|
33
|
+
secrets: inherit
|
|
34
|
+
publish:
|
|
35
|
+
needs: [check-tag, checks]
|
|
36
|
+
runs-on: ubuntu-24.04
|
|
37
|
+
steps:
|
|
38
|
+
- uses: actions/checkout@v4
|
|
39
|
+
- uses: astral-sh/setup-uv@v6
|
|
40
|
+
with:
|
|
41
|
+
version: 0.11.7
|
|
42
|
+
- uses: actions/setup-python@v5
|
|
43
|
+
with:
|
|
44
|
+
python-version: '3.13'
|
|
45
|
+
- uses: actions/setup-node@v4
|
|
46
|
+
with:
|
|
47
|
+
node-version-file: .nvmrc
|
|
48
|
+
cache: npm
|
|
49
|
+
cache-dependency-path: frontend/package-lock.json
|
|
50
|
+
- run: uv run inv build
|
|
51
|
+
- name: Publish to PyPI
|
|
52
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
53
|
+
with:
|
|
54
|
+
user: ${{ secrets.PYPI_USERNAME }}
|
|
55
|
+
password: ${{ secrets.PYPI_PASSWORD }}
|
|
56
|
+
packages-dir: dist
|
|
57
|
+
- name: Create release
|
|
58
|
+
uses: softprops/action-gh-release@v2
|
|
59
|
+
with:
|
|
60
|
+
name: Release ${{ needs.check-tag.outputs.version }}
|
|
61
|
+
tag_name: ${{ github.ref_name }}
|
|
62
|
+
target_commitish: ${{ github.sha }}
|
|
63
|
+
body: https://pypi.org/project/phrase-gym/${{ needs.check-tag.outputs.version }}/
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
name: static source code checks
|
|
2
|
+
on:
|
|
3
|
+
push:
|
|
4
|
+
branches: [main]
|
|
5
|
+
pull_request:
|
|
6
|
+
jobs:
|
|
7
|
+
static:
|
|
8
|
+
runs-on: ubuntu-24.04
|
|
9
|
+
steps:
|
|
10
|
+
- uses: actions/checkout@v4
|
|
11
|
+
- uses: astral-sh/setup-uv@v6
|
|
12
|
+
with:
|
|
13
|
+
version: 0.11.7
|
|
14
|
+
- uses: actions/setup-python@v5
|
|
15
|
+
with:
|
|
16
|
+
python-version: '3.13'
|
|
17
|
+
- uses: actions/setup-node@v4
|
|
18
|
+
with:
|
|
19
|
+
node-version-file: .nvmrc
|
|
20
|
+
cache: npm
|
|
21
|
+
cache-dependency-path: frontend/package-lock.json
|
|
22
|
+
- run: uv sync --frozen --all-extras
|
|
23
|
+
- run: npm --prefix frontend ci
|
|
24
|
+
- run: uv run inv pre
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
.DS_Store
|
|
2
|
+
.env
|
|
3
|
+
.idea/
|
|
4
|
+
|
|
5
|
+
# Local tool environments and caches
|
|
6
|
+
.venv/
|
|
7
|
+
**/__pycache__
|
|
8
|
+
**/.pytest_cache
|
|
9
|
+
.ruff_cache/
|
|
10
|
+
.coverage
|
|
11
|
+
build/
|
|
12
|
+
dist/
|
|
13
|
+
site/
|
|
14
|
+
**/*.egg-info
|
|
15
|
+
pytest.xml
|
|
16
|
+
pytest-coverage.txt
|
|
17
|
+
.dmypy.json
|
|
18
|
+
*.py-e
|
|
19
|
+
**/.setup-scripts/
|
|
20
|
+
allure-results/
|
|
21
|
+
htmlcov/
|
|
22
|
+
coverage.xml
|
|
23
|
+
|
|
24
|
+
# Client dependencies
|
|
25
|
+
**/node_modules/
|
|
26
|
+
|
|
27
|
+
# Production client build and local application data
|
|
28
|
+
frontend/dist/
|
|
29
|
+
frontend/test-results/
|
|
30
|
+
frontend/playwright-report/
|
|
31
|
+
.data/
|
|
32
|
+
*.sqlite3
|
|
33
|
+
*.sqlite3-wal
|
|
34
|
+
*.sqlite3-shm
|
|
35
|
+
|
|
36
|
+
# Rust build output
|
|
37
|
+
/target/
|
|
38
|
+
|
|
39
|
+
# Private local Coach attempts and diagnostics
|
|
40
|
+
.phrase-gym-coach/
|
phrase_gym-0.2.0/.nvmrc
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
22
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
exclude: ^(src/phrase_gym/server/static/|frontend/src/api.generated.ts|frontend/openapi.json|build/|dist/)
|
|
2
|
+
repos:
|
|
3
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
4
|
+
rev: v5.0.0
|
|
5
|
+
hooks:
|
|
6
|
+
- id: check-yaml
|
|
7
|
+
- id: end-of-file-fixer
|
|
8
|
+
- id: trailing-whitespace
|
|
9
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
10
|
+
rev: v0.14.2
|
|
11
|
+
hooks:
|
|
12
|
+
- id: ruff-check
|
|
13
|
+
exclude: ^tests/
|
|
14
|
+
args:
|
|
15
|
+
- --fix
|
|
16
|
+
- --line-length=100
|
|
17
|
+
- --target-version=py313
|
|
18
|
+
- --select=PL,A001,ARG,B,BLE,C,COM,E,F,G,I,ISC,N,PIE,Q,RET,S,SIM,SLF,TID,TRY,UP,W
|
|
19
|
+
- --ignore=B019,S101,G004,TRY003,TRY300,TRY301,TRY400
|
|
20
|
+
# Development commands resolve trusted tools from the operator's PATH.
|
|
21
|
+
- --per-file-ignores=scripts/**:S603,scripts/**:S607
|
|
22
|
+
- id: ruff-check
|
|
23
|
+
name: ruff check tests
|
|
24
|
+
files: ^tests/
|
|
25
|
+
args: [--fix, --line-length=100, --target-version=py313, '--select=E,F,I,UP,B,SIM']
|
|
26
|
+
- id: ruff-format
|
|
27
|
+
args: [--line-length=100, --target-version=py313]
|
|
28
|
+
- repo: https://github.com/facebook/pyrefly-pre-commit
|
|
29
|
+
rev: 0.50.0
|
|
30
|
+
hooks:
|
|
31
|
+
- id: pyrefly-check
|
|
32
|
+
language: system
|
|
33
|
+
entry: uv run pyrefly check
|
|
34
|
+
pass_filenames: false
|
|
35
|
+
- repo: local
|
|
36
|
+
hooks:
|
|
37
|
+
- id: uv-lock
|
|
38
|
+
name: Dependency lock
|
|
39
|
+
entry: uv lock --check
|
|
40
|
+
language: system
|
|
41
|
+
pass_filenames: false
|
|
42
|
+
always_run: true
|
|
43
|
+
- id: module-sizes
|
|
44
|
+
name: Production module sizes
|
|
45
|
+
entry: uv run inv check-sizes
|
|
46
|
+
language: system
|
|
47
|
+
pass_filenames: false
|
|
48
|
+
always_run: true
|
|
49
|
+
- id: client-format
|
|
50
|
+
name: Client formatting
|
|
51
|
+
entry: npm --prefix frontend run format:check
|
|
52
|
+
language: system
|
|
53
|
+
pass_filenames: false
|
|
54
|
+
always_run: true
|
|
55
|
+
- id: api-contract
|
|
56
|
+
name: Generated API contract
|
|
57
|
+
entry: uv run inv check-api
|
|
58
|
+
language: system
|
|
59
|
+
pass_filenames: false
|
|
60
|
+
always_run: true
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.13.13
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# Phrase Gym — agent instructions
|
|
2
|
+
|
|
3
|
+
Read [specs/START-HERE.md](specs/START-HERE.md) and [remaining work](specs/plan/implementation-plan.md). Specs retain current decisions and hard-to-infer design constraints; code and tests describe implemented behavior. The application is a small local Python service with the retained Vue client in `frontend/`. Earlier Python and Rust code in Git are prototypes, not parity requirements.
|
|
4
|
+
|
|
5
|
+
For a root-session request to implement the next stage, follow the [orchestration handout](specs/plan/orchestration-handout.md) before making changes or delegating. Delegated agents follow their assigned scope and the regular rules below.
|
|
6
|
+
|
|
7
|
+
`CLAUDE.md` links to this file; keep one instruction source.
|
|
8
|
+
|
|
9
|
+
## Architecture and file structure
|
|
10
|
+
|
|
11
|
+
- Keep architecture under active review in every batch. Each module and service has one coherent responsibility. Split unrelated workflows before adding more methods; do not grow a catch-all service or application component.
|
|
12
|
+
- Aim for no more than 250 lines of handwritten production code per file; review and split before 300. Generated contracts and migrations may be longer. A shorter file does not justify circular dependencies, meaningless wrappers or fragmented invariants.
|
|
13
|
+
- Dependencies point inward: transport and UI adapters use contracts/domain services; domain services never import HTTP routers, application startup or UI modules. Python owns the generated API contract. Construct services at the application boundary. Keep Phrase Gym lightweight and Coach separate under the [application boundary](specs/architecture.md#application-boundary); do not recreate the prototype's general distributed worker or native Coach GUI.
|
|
14
|
+
- Keep validation, publication transactions and library reads separate. One workflow owns each transaction; helpers receive that transaction instead of opening hidden writes. Keep all package publication atomic.
|
|
15
|
+
- Vue views render state. Route orchestration, preferences and audio effects belong in focused composables/adapters; the practice reducer stays pure. Generated API types are the client contract.
|
|
16
|
+
- Preserve the original Vue shell, practice card, microphone control and responsive styling as the Python backend grows. Adapt or hide controls for unavailable workflows; do not replace the interface with a new design.
|
|
17
|
+
- When moving a symbol, update all importers and tests. No re-export compatibility shims. Shared helpers are preferable to duplicated logic.
|
|
18
|
+
|
|
19
|
+
## Commands and environment
|
|
20
|
+
|
|
21
|
+
| Task | Command |
|
|
22
|
+
|---|---|
|
|
23
|
+
| Python lint, format, types, module size, client format and contract drift | `uv run inv pre` |
|
|
24
|
+
| Python tests, client typecheck/unit/build and both browser engines | `uv run inv test` |
|
|
25
|
+
| Python iteration | `uv run pytest` |
|
|
26
|
+
| Client iteration | `npm --prefix frontend test` |
|
|
27
|
+
| Browser iteration | `npm --prefix frontend run test:browser` |
|
|
28
|
+
| Refresh OpenAPI and generated client types | `uv run python scripts/generate_api.py && npm --prefix frontend run api-types` |
|
|
29
|
+
| Build the retained client | `npm --prefix frontend run build` |
|
|
30
|
+
| Run the local application on 127.0.0.1:8000 | `uv run phrase-gym run` |
|
|
31
|
+
| Build the frontend and run the local application | `uv run inv run --build` |
|
|
32
|
+
| Install the pre-commit hooks (once per clone) | `uv run pre-commit install` |
|
|
33
|
+
| Scaffold tasks and release commands | `uv run inv --list` |
|
|
34
|
+
| Build documentation | `uv run inv build-docs` |
|
|
35
|
+
| Build the client and Python distributions | `uv run inv build` |
|
|
36
|
+
|
|
37
|
+
Use `uv sync --frozen` and `npm --prefix frontend ci`; install browser binaries from `frontend/` with `npx playwright install chromium webkit`. Python 3.13 and application dependencies are pinned in `uv.lock`. Use Node 22.12+ on an LTS release (`.nvmrc`). The pre-commit framework runs the pre gate and needs uv, Python, Node and the client dependencies; `inv pre` runs the same hooks manually. Dependency changes update their committed lockfiles. Piper uses its separately pinned private environment described in [the import guide](docs/importing-phrases.md#piper-voice). Missing dependencies or browsers are environment failures to fix, not reasons to skip suites.
|
|
38
|
+
|
|
39
|
+
Preserve the repository scaffold: pre-commit, Invoke tasks, version/tag helpers, package metadata, environment/build scripts, documentation publishing, test/coverage reports and PyPI release workflows. Extend these for the application instead of replacing or deleting them. Only application/documentation boilerplate should be replaced with real implementation and documentation. Follow [development and release operations](docs/development.md).
|
|
40
|
+
|
|
41
|
+
Pre-commit owns the static checks and all Ruff settings. Use Pyrefly for Python type checking; do not add a duplicate mypy gate or a wrapper that reruns the same tools. Invoke owns repository development, build, test and release tasks; the packaged CLI owns user runtime and deployment commands, which Invoke may wrap. Keep root-level executable scripts limited to the retained environment activation script.
|
|
42
|
+
|
|
43
|
+
Run the full browser/audio gate on macOS and retain the separate Linux Python server job in CI, following the [platform boundary](specs/architecture.md#application-boundary). Do not skip native recorder tests to accommodate Linux WebKit's missing API.
|
|
44
|
+
|
|
45
|
+
## Done gate
|
|
46
|
+
|
|
47
|
+
Choose the gate from the complete change, including staged and unstaged files.
|
|
48
|
+
|
|
49
|
+
**CI results are always part of verification:** inspect GitHub Actions for the current remote commit before making changes. After the local gate passes, commit and push under the standing authorization below, then wait for the workflows on the exact pushed commit to finish and inspect every result before delivery. Inspect failed jobs and their logs, fix failures, and repeat the applicable checks and push until CI passes. Local checks do not establish that CI passed. Report the commit, run link and outcome separately from local checks. Missing, pending, cancelled or inaccessible runs are unverified, never passing; report any actual block explicitly.
|
|
50
|
+
|
|
51
|
+
**Documentation-only changes:** when the change contains only prose in specs, plans, README, docs or agent instructions, do not run application builds or Python/client/browser suites; the installed pre-commit hooks still run. Run `git diff --check` and `git diff --cached --check`, inspect the full diff, verify changed local links/anchors and check consistency with related decisions and plans. Report these documentation checks without claiming application tests passed. Documentation describing future behavior does not require implementing or testing that behavior.
|
|
52
|
+
|
|
53
|
+
**Implementation or mixed changes:** changes to production code, tests, runtime prompts, generated contracts, migrations, dependencies, executable scripts, build configuration, hooks or CI require the full gate, even when accompanied by specs. Before describing such a milestone as done, ready, fixed, landed or green, run these in order and inspect their complete results:
|
|
54
|
+
|
|
55
|
+
1. `uv run inv pre`: pre-commit hygiene, Python formatting/lint/types, module-size, client formatting and generated-contract drift checks all pass.
|
|
56
|
+
2. `uv run inv test`: Python, client and both real-backend browser suites pass; frontend checks must run rather than skip.
|
|
57
|
+
|
|
58
|
+
Use focused checks during iteration. Run the full ordered gate once per coherent implementation deliverable before commit or delivery, rather than after every internal edit. Rerun relevant checks for review fixes and the full gate after substantive changes; a narrow subset cannot replace it. Use the documentation-only gate for prose-only changes. Never bypass commit hooks with `--no-verify`.
|
|
59
|
+
|
|
60
|
+
Fix findings even when they appear pre-existing. Deferral requires evidence that the same failure exists on the committed baseline and the user's agreement in the current turn. Never silently report a failing tree as passing. If execution or approval infrastructure blocks a check, describe that block explicitly and do not claim the gate passed.
|
|
61
|
+
|
|
62
|
+
## Tests
|
|
63
|
+
|
|
64
|
+
- New modules/functions ship meaningful behavioral coverage in the same commit. Keep tests with the responsibility they verify; do not grow one catch-all test file.
|
|
65
|
+
- Behavior that only exists in the browser needs a real-browser test against the real server and built client. Assert provisional/error states deterministically; do not race an artificial delay.
|
|
66
|
+
- For implementation changes, iterate with subsets if useful, then verify the full suite. Documentation-only changes use the done gate above.
|
|
67
|
+
- Never launch system dialogs or native popup paths automatically. If setup, login, permission or a native check needs attended interaction, ask the user directly and report the unresolved acceptance instead of opening it.
|
|
68
|
+
- Before a full browser suite in a restricted environment, run one existing test per browser engine with `--max-failures=1` to verify startup. Stop immediately if browser launch is denied or crashes; do not let the suite repeatedly launch failing browsers and flood the desktop with crash dialogs. Report the environment block and leave the full gate incomplete until it is resolved. Do not rerun browser checks after the user asks to stop them.
|
|
69
|
+
- Automated tests must emit no sound on the host. Mute every browser context, including manually created contexts, while preserving native decoding, playback clocks and events. Synthetic capture feeds a media stream, never the speaker destination. Live listening and physical microphone checks require attended learner interaction and are separate from automated tests.
|
|
70
|
+
- Tests make no real provider, TTS, source mutation or external delivery calls. Fake those boundaries; never read the operator's `.env` or private recordings into fixtures or logs.
|
|
71
|
+
- Never leave a failing test. Diagnose clock, ordering, race and environment causes; do not skip, xfail, delete or weaken the assertion to hide a failure.
|
|
72
|
+
- Software checks are not language-content, speech-quality or actual-device acceptance. Keep Safari/headset, iPhone and content gaps explicit.
|
|
73
|
+
- Prototype fixtures in Git are not replacement compatibility requirements ([architecture](specs/architecture.md#releases-and-data-compatibility)). Preserve meaningful coverage, and replace obsolete expectations only for an intended spec change. The Python application uses library integrations with focused tests rather than carrying forward numerical-port equivalence.
|
|
74
|
+
|
|
75
|
+
## Code conventions
|
|
76
|
+
|
|
77
|
+
- Python passes Ruff and Pyrefly; Vue passes Prettier and TypeScript checking. The executable pre gate enforces the handwritten production module-size boundary and generated-contract drift.
|
|
78
|
+
- English only in comments, docstrings, plans and repository documentation. Follow the project's English communication rule. Learning data, language names and voice IDs retain their original language/script as quoted, searchable literals.
|
|
79
|
+
- All imports at module top level, none inside functions and none to break cycles; fix the dependency structure.
|
|
80
|
+
- No re-export shims when moving symbols. Import shared helpers directly from their owner.
|
|
81
|
+
- Comments/docstrings explain a non-obvious why in one or two lines: a hidden constraint, library workaround or invariant. Do not restate the code, argue architectural decisions there, or cite phase/plan names; decisions belong in `specs/`.
|
|
82
|
+
|
|
83
|
+
## Specs and plans
|
|
84
|
+
|
|
85
|
+
- `specs/` contains current decisions, their rationale and substantial design constraints that are difficult to infer from code. Code, tests and generated contracts document implemented behavior; usage and operational commands belong in README or `docs/`.
|
|
86
|
+
- Keep one authoritative home per decision. Do not retain historical references, superseded sketches, review logs, implementation inventories or completed plans in specs. Git preserves earlier versions.
|
|
87
|
+
- The implementation plan contains only remaining work. Delete completed entries and plans; retain any lasting decision in its focused spec. Correct contradictory text in the same commit and keep acceptance gaps concise.
|
|
88
|
+
|
|
89
|
+
## Model integration
|
|
90
|
+
|
|
91
|
+
- When local AI preparation is used, Codex uses the normal user home and Phrase Gym repository as its working directory, as requested by the learner. Never copy login tokens. Existing private attempt files stay ignored under `.phrase-gym-coach/`; retain subscription-only login, bounded execution and an allowlisted child environment. Deny repository `.env` reads and keep application stores/recordings outside the permitted model workspace. Normal-home instructions/skills may load; do not claim customization isolation. The first MVP imports externally prepared content and needs no internal LLM adapter.
|
|
92
|
+
|
|
93
|
+
Follow the [standing local English demo authorization](specs/plan/implementation-plan.md#standing-authorization-for-the-local-english-demo): routine demo runs and continuations require no separate budget, temporary authentication workspace or login approval. Existing runtime bounds govern prototype runs; they do not require reproducing its distributed infrastructure. Preserve learner data and recordings during replacement.
|
|
94
|
+
|
|
95
|
+
When changing prompts, provider payloads or interpretation of real model answers, mocks alone cannot establish model behavior. Plan representative fixtures and real-provider measurements before execution; inspect availability and every concrete result. Record semantic review separately from automated conformance. A fresh reviewer is required for those model-result measurements, not for ordinary refactors. Do not create learner effectiveness experiments or unrequested general review loops.
|
|
96
|
+
|
|
97
|
+
## Git, secrets and deployment
|
|
98
|
+
|
|
99
|
+
- Commit authorized work directly to `main` and push it to `origin/main` after the applicable local gate passes, then verify CI as required by the done gate. This is standing authorization; do not ask for separate push approval unless the user explicitly requests local-only work. Do not create branches or PRs by default, and never force-push.
|
|
100
|
+
- Publishing releases and deployment still require the user's explicit request for that action.
|
|
101
|
+
- Keep `.env`, provider keys, learner data and recordings ignored, private and server-side. Never bundle or print secrets.
|
|
102
|
+
- Source repositories, vaults, echo-words, legacy trainers and Anki stay read-only unless separately authorized. Real deployment must be reproducible from committed code and must verify its actual health/access boundary; no one-off server edits.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
AGENTS.md
|
phrase_gym-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright (c) 2026 Andrey Sorokin (andrey@sorokin.engineer)
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
4
|
+
this software and associated documentation files (the “Software”), to deal in
|
|
5
|
+
the Software without restriction, including without limitation the rights to use,
|
|
6
|
+
copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
|
7
|
+
Software, and to permit persons to whom the Software is furnished to do so,
|
|
8
|
+
subject to the following conditions:
|
|
9
|
+
|
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
|
11
|
+
copies or substantial portions of the Software.
|
|
12
|
+
|
|
13
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
|
|
14
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|
15
|
+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
16
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
17
|
+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
18
|
+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
19
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
20
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: phrase-gym
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: A small local trainer for bringing useful phrases into speech.
|
|
5
|
+
Project-URL: Homepage, https://andgineer.github.io/phrase-gym/
|
|
6
|
+
Project-URL: Documentation, https://andgineer.github.io/phrase-gym/
|
|
7
|
+
Project-URL: Repository, https://github.com/andgineer/phrase-gym
|
|
8
|
+
Author-email: Andrey Sorokin <andrey@sorokin.engineer>
|
|
9
|
+
License: Copyright (c) 2026 Andrey Sorokin (andrey@sorokin.engineer)
|
|
10
|
+
|
|
11
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
12
|
+
this software and associated documentation files (the “Software”), to deal in
|
|
13
|
+
the Software without restriction, including without limitation the rights to use,
|
|
14
|
+
copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
|
15
|
+
Software, and to permit persons to whom the Software is furnished to do so,
|
|
16
|
+
subject to the following conditions:
|
|
17
|
+
|
|
18
|
+
The above copyright notice and this permission notice shall be included in all
|
|
19
|
+
copies or substantial portions of the Software.
|
|
20
|
+
|
|
21
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
|
|
22
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|
23
|
+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
24
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
25
|
+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
26
|
+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
27
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
28
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
|
29
|
+
License-File: LICENSE
|
|
30
|
+
Keywords: language-learning,phrases,spoken-retrieval
|
|
31
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
32
|
+
Classifier: Operating System :: OS Independent
|
|
33
|
+
Classifier: Programming Language :: Python :: 3
|
|
34
|
+
Requires-Python: <3.14,>=3.13
|
|
35
|
+
Requires-Dist: fastapi==0.119.1
|
|
36
|
+
Requires-Dist: platformdirs==4.5.0
|
|
37
|
+
Requires-Dist: pydantic==2.12.3
|
|
38
|
+
Requires-Dist: uvicorn==0.38.0
|
|
39
|
+
Provides-Extra: coach
|
|
40
|
+
Provides-Extra: deploy
|
|
41
|
+
Description-Content-Type: text/markdown
|
|
42
|
+
|
|
43
|
+
# Phrase Gym
|
|
44
|
+
|
|
45
|
+
[](https://github.com/andgineer/phrase-gym/actions)
|
|
46
|
+
[](https://htmlpreview.github.io/?https://github.com/andgineer/phrase-gym/blob/python-coverage-comment-action-data/htmlcov/index.html)
|
|
47
|
+
|
|
48
|
+
Phrase Gym is a small local Python/Vue application for studying useful English phrases and
|
|
49
|
+
keeping private learner recordings. This checkpoint supports package import, ungraded first
|
|
50
|
+
study, reference playback, optional recording, comparison, and saved replay. Later recall and
|
|
51
|
+
scheduling remain the next stage.
|
|
52
|
+
|
|
53
|
+
## Install and launch
|
|
54
|
+
|
|
55
|
+
The server runs on macOS and Linux. Use a macOS browser for the MVP media player and recording;
|
|
56
|
+
Linux browser audio support is planned after the MVP.
|
|
57
|
+
|
|
58
|
+
For a source checkout, use Python 3.13, [uv](https://docs.astral.sh/uv/), Node 22.12+ and a
|
|
59
|
+
configured English Piper voice for Coach synthesis. Install the pinned environments and build the client once:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
uv sync --frozen
|
|
63
|
+
npm --prefix frontend ci
|
|
64
|
+
npm --prefix frontend run build
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Start Phrase Gym without any Piper installation:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
uv run phrase-gym run
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
For private iPhone access on your existing tailnet, use `uv run inv run --build --tailscale`. This points Tailscale Serve at the new server
|
|
74
|
+
and allows its exact HTTPS machine hostname. Open the HTTPS URL printed by Tailscale on the
|
|
75
|
+
iPhone with Tailscale connected.
|
|
76
|
+
|
|
77
|
+
The built wheel contains the Vue client. A lean installed package runs `phrase-gym run`.
|
|
78
|
+
Coach runs separately: set `PHRASE_GYM_PIPER_CONFIG` to the private voice file in
|
|
79
|
+
[the import guide](docs/importing-phrases.md#piper-voice), then run
|
|
80
|
+
`uv run phrase-gym coach` in another terminal. The private Piper environment belongs to Coach;
|
|
81
|
+
the server needs only core dependencies. Service registration and deployment commands remain
|
|
82
|
+
unavailable.
|
|
83
|
+
|
|
84
|
+
Open <http://127.0.0.1:8000> on the Mac. The app listens only on loopback and stores its database,
|
|
85
|
+
references, and recordings in the operating system's Phrase Gym application-data directory.
|
|
86
|
+
For a separate data directory, add `--data-dir /absolute/path`.
|
|
87
|
+
|
|
88
|
+
## Add and study material
|
|
89
|
+
|
|
90
|
+
Open **Add material**, paste a package or choose its UTF-8 JSON file, and select **Import**.
|
|
91
|
+
The app saves the request before acknowledging it. A text-only package stays pending while Coach
|
|
92
|
+
is offline; Coach prepares reference audio and returns it through the API. An external agent can
|
|
93
|
+
submit the same package with complete WAV references in one API request, without Coach. The server
|
|
94
|
+
validates and publishes all cards and references together. A preparation error remains visible
|
|
95
|
+
for explicit retry. Exact package
|
|
96
|
+
retries return the existing result; a changed package ID or repeated source/item identity is
|
|
97
|
+
rejected without overwriting learned material.
|
|
98
|
+
|
|
99
|
+
Study shows the full phrase and meaning/use help. Reference playback and recording are optional.
|
|
100
|
+
Every stopped take is retained separately and saved automatically; pending uploads remain in the
|
|
101
|
+
browser for retry. **Next** records ungraded study completion. The finish screen deliberately does
|
|
102
|
+
not invent a recall grade or schedule before the later-recall stage exists.
|
|
103
|
+
|
|
104
|
+
See [the package schema, example, and external-agent instruction](docs/importing-phrases.md).
|
|
105
|
+
|
|
106
|
+
## Development
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
. ./activate.sh
|
|
110
|
+
uv run pre-commit install
|
|
111
|
+
inv --list
|
|
112
|
+
uv run inv pre
|
|
113
|
+
uv run inv test
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
`inv test` runs Python tests, client typechecking/unit/build checks, and silent real-backend
|
|
117
|
+
browser tests in Chromium and WebKit on macOS. CI runs the full gate on macOS and separately
|
|
118
|
+
runs all Python server tests on Linux. For server-only iteration, use `uv run pytest`.
|
|
119
|
+
Install browser binaries from `frontend/` with
|
|
120
|
+
`npx playwright install chromium webkit`. If migrating from the temporary custom hook, run
|
|
121
|
+
`git config --unset core.hooksPath` before `pre-commit install`.
|
|
122
|
+
|
|
123
|
+
The scaffold's `inv pre`, `inv reqs`, `inv docs-en`, `inv build-docs`, `inv version`, and
|
|
124
|
+
`inv ver-bug` / `inv ver-feature` / `inv ver-release` commands are available.
|
|
125
|
+
`uv run inv build` builds the client and Python distributions. A version command checks and commits the
|
|
126
|
+
version update, then pushes its release tag; the tag workflow publishes to PyPI only after CI passes.
|
|
127
|
+
See [development and release operations](docs/development.md) for setup and dry-run commands.
|
|
128
|
+
|
|
129
|
+
Use `uv run inv run --build` to rebuild the frontend and start the server, or `uv run inv run`
|
|
130
|
+
to start it with the existing client build.
|
|
131
|
+
|
|
132
|
+
[Documentation](https://andgineer.github.io/phrase-gym/),
|
|
133
|
+
[Allure reports](https://andgineer.github.io/phrase-gym/builds/tests/),
|
|
134
|
+
[Codecov](https://app.codecov.io/gh/andgineer/phrase-gym), and
|
|
135
|
+
[Coveralls](https://coveralls.io/github/andgineer/phrase-gym) retain the scaffold's reporting links.
|
|
136
|
+
|
|
137
|
+
Created using [cookiecutter-python-package](https://github.com/andgineer/cookiecutter-python-package).
|
|
138
|
+
|
|
139
|
+
Current decisions and remaining work start at [specs/START-HERE.md](specs/START-HERE.md).
|