lingo-loop 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.
- lingo_loop-0.1.0/.claude-plugin/plugin.json +9 -0
- lingo_loop-0.1.0/.codex-plugin/plugin.json +12 -0
- lingo_loop-0.1.0/.gitignore +74 -0
- lingo_loop-0.1.0/LICENSE +21 -0
- lingo_loop-0.1.0/PKG-INFO +130 -0
- lingo_loop-0.1.0/README.md +98 -0
- lingo_loop-0.1.0/agents/tutor-judge.md +10 -0
- lingo_loop-0.1.0/bin/claude-language-tutor +26 -0
- lingo_loop-0.1.0/bin/tutor +11 -0
- lingo_loop-0.1.0/data/defaults/preferences.yaml +8 -0
- lingo_loop-0.1.0/data/defaults/profile.yaml +8 -0
- lingo_loop-0.1.0/hermes-profile/SOUL.md +30 -0
- lingo_loop-0.1.0/hermes-profile/config.yaml +37 -0
- lingo_loop-0.1.0/hermes-profile/distribution.yaml +49 -0
- lingo_loop-0.1.0/migrations/001_initial.sql +111 -0
- lingo_loop-0.1.0/migrations/002_vocab_depth.sql +12 -0
- lingo_loop-0.1.0/migrations/003_progress_indexes.sql +14 -0
- lingo_loop-0.1.0/migrations/004_sessions_checkpoints.sql +27 -0
- lingo_loop-0.1.0/openclaw-plugin/openclaw.plugin.json +47 -0
- lingo_loop-0.1.0/openclaw-plugin/package.json +39 -0
- lingo_loop-0.1.0/openclaw-plugin/src/index.ts +68 -0
- lingo_loop-0.1.0/openclaw-plugin/tsconfig.json +17 -0
- lingo_loop-0.1.0/pyproject.toml +99 -0
- lingo_loop-0.1.0/schemas/answer_event.schema.json +237 -0
- lingo_loop-0.1.0/schemas/boot_context.schema.json +258 -0
- lingo_loop-0.1.0/schemas/boot_result.schema.json +36 -0
- lingo_loop-0.1.0/schemas/checkpoint.schema.json +30 -0
- lingo_loop-0.1.0/schemas/conformance_run.schema.json +107 -0
- lingo_loop-0.1.0/schemas/feedback_envelope.schema.json +176 -0
- lingo_loop-0.1.0/schemas/host_capability_profile.schema.json +170 -0
- lingo_loop-0.1.0/schemas/host_setup_profile.schema.json +206 -0
- lingo_loop-0.1.0/schemas/init_plan.schema.json +222 -0
- lingo_loop-0.1.0/schemas/init_request.schema.json +41 -0
- lingo_loop-0.1.0/schemas/init_result.schema.json +245 -0
- lingo_loop-0.1.0/schemas/lesson_exercise.schema.json +112 -0
- lingo_loop-0.1.0/schemas/lesson_result.schema.json +318 -0
- lingo_loop-0.1.0/schemas/lifecycle_trigger.schema.json +70 -0
- lingo_loop-0.1.0/schemas/manual_provider_install_report.schema.json +117 -0
- lingo_loop-0.1.0/schemas/progress_markdown_export.schema.json +90 -0
- lingo_loop-0.1.0/schemas/progress_report.schema.json +646 -0
- lingo_loop-0.1.0/schemas/progress_request.schema.json +36 -0
- lingo_loop-0.1.0/schemas/provider_install_action.schema.json +62 -0
- lingo_loop-0.1.0/schemas/provider_status.schema.json +98 -0
- lingo_loop-0.1.0/schemas/reading_exercise.schema.json +112 -0
- lingo_loop-0.1.0/schemas/reading_result.schema.json +318 -0
- lingo_loop-0.1.0/schemas/selection_reason.schema.json +66 -0
- lingo_loop-0.1.0/schemas/session.schema.json +24 -0
- lingo_loop-0.1.0/schemas/session_analysis.schema.json +77 -0
- lingo_loop-0.1.0/schemas/text_modality_record.schema.json +250 -0
- lingo_loop-0.1.0/schemas/text_modality_result.schema.json +318 -0
- lingo_loop-0.1.0/schemas/transcript_drill.schema.json +112 -0
- lingo_loop-0.1.0/schemas/vocabulary_card_definition.schema.json +91 -0
- lingo_loop-0.1.0/schemas/vocabulary_import_summary.schema.json +117 -0
- lingo_loop-0.1.0/schemas/vocabulary_review_history.schema.json +251 -0
- lingo_loop-0.1.0/schemas/vocabulary_session_plan.schema.json +396 -0
- lingo_loop-0.1.0/schemas/weak_tag_signal.schema.json +56 -0
- lingo_loop-0.1.0/skills/tutor-lesson/SKILL.md +61 -0
- lingo_loop-0.1.0/skills/tutor-progress/SKILL.md +51 -0
- lingo_loop-0.1.0/skills/tutor-progress/scripts/run.py +15 -0
- lingo_loop-0.1.0/skills/tutor-reading/SKILL.md +47 -0
- lingo_loop-0.1.0/skills/tutor-setup/SKILL.md +20 -0
- lingo_loop-0.1.0/skills/tutor-vocab/SKILL.md +51 -0
- lingo_loop-0.1.0/skills/tutor-vocab/scripts/run.py +16 -0
- lingo_loop-0.1.0/skills/tutor-writing/SKILL.md +27 -0
- lingo_loop-0.1.0/skills/tutor-writing/scripts/run.py +15 -0
- lingo_loop-0.1.0/src/language_tutor/__init__.py +3 -0
- lingo_loop-0.1.0/src/language_tutor/adapters/__init__.py +1 -0
- lingo_loop-0.1.0/src/language_tutor/adapters/base.py +178 -0
- lingo_loop-0.1.0/src/language_tutor/adapters/claude.py +21 -0
- lingo_loop-0.1.0/src/language_tutor/adapters/codex.py +16 -0
- lingo_loop-0.1.0/src/language_tutor/adapters/hermes.py +16 -0
- lingo_loop-0.1.0/src/language_tutor/adapters/openclaw.py +16 -0
- lingo_loop-0.1.0/src/language_tutor/adapters/registry.py +81 -0
- lingo_loop-0.1.0/src/language_tutor/boot_context.py +114 -0
- lingo_loop-0.1.0/src/language_tutor/cli.py +1039 -0
- lingo_loop-0.1.0/src/language_tutor/dal/__init__.py +1 -0
- lingo_loop-0.1.0/src/language_tutor/dal/migrations.py +77 -0
- lingo_loop-0.1.0/src/language_tutor/dal/paths.py +48 -0
- lingo_loop-0.1.0/src/language_tutor/dal/repositories.py +1117 -0
- lingo_loop-0.1.0/src/language_tutor/dal/sqlite_store.py +29 -0
- lingo_loop-0.1.0/src/language_tutor/dal/yaml_store.py +54 -0
- lingo_loop-0.1.0/src/language_tutor/errors.py +101 -0
- lingo_loop-0.1.0/src/language_tutor/evaluators.py +25 -0
- lingo_loop-0.1.0/src/language_tutor/feedback.py +207 -0
- lingo_loop-0.1.0/src/language_tutor/health.py +70 -0
- lingo_loop-0.1.0/src/language_tutor/installer/__init__.py +35 -0
- lingo_loop-0.1.0/src/language_tutor/installer/assets.py +100 -0
- lingo_loop-0.1.0/src/language_tutor/installer/protocol.py +52 -0
- lingo_loop-0.1.0/src/language_tutor/installer/providers/__init__.py +1 -0
- lingo_loop-0.1.0/src/language_tutor/installer/providers/base.py +326 -0
- lingo_loop-0.1.0/src/language_tutor/installer/providers/claude.py +19 -0
- lingo_loop-0.1.0/src/language_tutor/installer/providers/codex.py +19 -0
- lingo_loop-0.1.0/src/language_tutor/installer/providers/hermes.py +19 -0
- lingo_loop-0.1.0/src/language_tutor/installer/providers/openclaw.py +24 -0
- lingo_loop-0.1.0/src/language_tutor/installer/registry.py +44 -0
- lingo_loop-0.1.0/src/language_tutor/installer/seams.py +124 -0
- lingo_loop-0.1.0/src/language_tutor/installer/service.py +70 -0
- lingo_loop-0.1.0/src/language_tutor/lessons.py +47 -0
- lingo_loop-0.1.0/src/language_tutor/lifecycle.py +91 -0
- lingo_loop-0.1.0/src/language_tutor/progress.py +426 -0
- lingo_loop-0.1.0/src/language_tutor/progress_rendering.py +137 -0
- lingo_loop-0.1.0/src/language_tutor/reading.py +66 -0
- lingo_loop-0.1.0/src/language_tutor/schemas.py +1442 -0
- lingo_loop-0.1.0/src/language_tutor/setup.py +43 -0
- lingo_loop-0.1.0/src/language_tutor/srs.py +46 -0
- lingo_loop-0.1.0/src/language_tutor/text_modalities.py +288 -0
- lingo_loop-0.1.0/src/language_tutor/vocab.py +505 -0
- lingo_loop-0.1.0/src/language_tutor/writing.py +35 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema_version": "1.0",
|
|
3
|
+
"name": "language-tutor",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"description": "Local-first language tutor packaged as a Codex plugin (reuses root skills/).",
|
|
6
|
+
"author": "language-tutor contributors",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"skills": "./skills/",
|
|
9
|
+
"features": {
|
|
10
|
+
"plugin_hooks": false
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# IDE
|
|
2
|
+
.idea/
|
|
3
|
+
.vscode/
|
|
4
|
+
*.swp
|
|
5
|
+
*.swo
|
|
6
|
+
*~
|
|
7
|
+
|
|
8
|
+
# Python
|
|
9
|
+
__pycache__/
|
|
10
|
+
*.py[cod]
|
|
11
|
+
*$py.class
|
|
12
|
+
*.so
|
|
13
|
+
.Python
|
|
14
|
+
build/
|
|
15
|
+
develop-eggs/
|
|
16
|
+
dist/
|
|
17
|
+
downloads/
|
|
18
|
+
eggs/
|
|
19
|
+
.eggs/
|
|
20
|
+
lib/
|
|
21
|
+
lib64/
|
|
22
|
+
parts/
|
|
23
|
+
sdist/
|
|
24
|
+
var/
|
|
25
|
+
wheels/
|
|
26
|
+
pip-wheel-metadata/
|
|
27
|
+
share/python-wheels/
|
|
28
|
+
*.egg-info/
|
|
29
|
+
.installed.cfg
|
|
30
|
+
*.egg
|
|
31
|
+
MANIFEST
|
|
32
|
+
|
|
33
|
+
# Virtual Environments
|
|
34
|
+
venv/
|
|
35
|
+
.venv/
|
|
36
|
+
env/
|
|
37
|
+
ENV/
|
|
38
|
+
env.bak/
|
|
39
|
+
venv.bak/
|
|
40
|
+
.env*
|
|
41
|
+
|
|
42
|
+
# SQLite Database Files
|
|
43
|
+
*.db
|
|
44
|
+
*.sqlite
|
|
45
|
+
*.sqlite3
|
|
46
|
+
*.db-journal
|
|
47
|
+
|
|
48
|
+
# OS
|
|
49
|
+
.DS_Store
|
|
50
|
+
Thumbs.db
|
|
51
|
+
desktop.ini
|
|
52
|
+
|
|
53
|
+
# Testing
|
|
54
|
+
.pytest_cache/
|
|
55
|
+
.coverage
|
|
56
|
+
htmlcov/
|
|
57
|
+
.tox/
|
|
58
|
+
|
|
59
|
+
# Jupyter Notebook
|
|
60
|
+
.ipynb_checkpoints
|
|
61
|
+
|
|
62
|
+
# mypy
|
|
63
|
+
.mypy_cache/
|
|
64
|
+
.dmypy.json
|
|
65
|
+
dmypy.json
|
|
66
|
+
|
|
67
|
+
# Logs
|
|
68
|
+
*.log
|
|
69
|
+
*.tmp
|
|
70
|
+
|
|
71
|
+
.agents
|
|
72
|
+
.claude
|
|
73
|
+
.codex
|
|
74
|
+
claude.manifest.json
|
lingo_loop-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Artem Veduta
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: lingo-loop
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Local-first Claude Code language tutor
|
|
5
|
+
Project-URL: Homepage, https://github.com/artemVeduta/lingo-loop
|
|
6
|
+
Project-URL: Source, https://github.com/artemVeduta/lingo-loop
|
|
7
|
+
Project-URL: Issues, https://github.com/artemVeduta/lingo-loop/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/artemVeduta/lingo-loop/blob/main/CHANGELOG.md
|
|
9
|
+
Author-email: Artem Veduta <veduta.artem20@gmail.com>
|
|
10
|
+
License: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: claude-code,cli,codex,language-learning,lingo-loop,local-first,tutor
|
|
13
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Topic :: Education
|
|
18
|
+
Classifier: Topic :: Education :: Computer Aided Instruction (CAI)
|
|
19
|
+
Requires-Python: >=3.12
|
|
20
|
+
Requires-Dist: click>=8.1
|
|
21
|
+
Requires-Dist: platformdirs>=4.2
|
|
22
|
+
Requires-Dist: pydantic>=2.7
|
|
23
|
+
Requires-Dist: ruamel-yaml>=0.18
|
|
24
|
+
Provides-Extra: dev
|
|
25
|
+
Requires-Dist: freezegun>=1.5; extra == 'dev'
|
|
26
|
+
Requires-Dist: pyright>=1.1; extra == 'dev'
|
|
27
|
+
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
|
|
28
|
+
Requires-Dist: pytest>=8.2; extra == 'dev'
|
|
29
|
+
Requires-Dist: ruff>=0.5; extra == 'dev'
|
|
30
|
+
Requires-Dist: syrupy>=4.6; extra == 'dev'
|
|
31
|
+
Description-Content-Type: text/markdown
|
|
32
|
+
|
|
33
|
+
# lingo-loop
|
|
34
|
+
|
|
35
|
+
> Learn languages inside your AI coding assistant — local-first, no telemetry.
|
|
36
|
+
|
|
37
|
+
[](LICENSE)
|
|
38
|
+
[](https://www.python.org/downloads/)
|
|
39
|
+
[](https://github.com/artemVeduta/lingo-loop/actions/workflows/ci.yml)
|
|
40
|
+
|
|
41
|
+
<!-- TODO(oss-baseline-assets): record top-level demo cast and render gif via agg, then restore the image + cast links below.
|
|
42
|
+
Until the assets land, omit them to avoid broken-image rendering on GitHub. Tracked in docs/internal/launch-checklist.md. -->
|
|
43
|
+
*(Demo recording pending — see [`docs/internal/launch-checklist.md`](docs/internal/launch-checklist.md).)*
|
|
44
|
+
|
|
45
|
+
## Why
|
|
46
|
+
|
|
47
|
+
- **Local-first.** Your profile, vocabulary, and progress live in plain files on your machine.
|
|
48
|
+
- **No telemetry.** Nothing is sent anywhere except the LLM API call your host already makes.
|
|
49
|
+
- **Works inside your IDE.** Practice reading, writing, and vocabulary without leaving Claude Code, Codex, Hermes, or OpenClaw.
|
|
50
|
+
|
|
51
|
+
## Features
|
|
52
|
+
|
|
53
|
+
- Reading practice with adaptive vocabulary surfacing.
|
|
54
|
+
- Short writing prompts with corrections from your assistant.
|
|
55
|
+
- Spaced-repetition vocabulary loop, scheduled across sessions.
|
|
56
|
+
- Lesson and transcript flows tailored to your native and target languages.
|
|
57
|
+
- Progress tracking you can inspect with a single command.
|
|
58
|
+
- Editable YAML profile — language pair, level, and preferences in one place.
|
|
59
|
+
- Works across four AI coding hosts with the same CLI underneath.
|
|
60
|
+
|
|
61
|
+
## Quick start (Claude Code)
|
|
62
|
+
|
|
63
|
+
> PyPI publish is coming. Until then, install from source with [`uv`](https://docs.astral.sh/uv/) (or clone and run `uv pip install -e ".[dev]"` for a dev setup — see [CONTRIBUTING.md](CONTRIBUTING.md)).
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
# 1. Install the CLI from source (pip install lingo-loop coming soon)
|
|
67
|
+
uv tool install git+https://github.com/artemVeduta/lingo-loop
|
|
68
|
+
|
|
69
|
+
# 2. Detect AI hosts and install plugin wiring for the ones you use
|
|
70
|
+
tutor init
|
|
71
|
+
|
|
72
|
+
# 3. Write your learner profile (native + target language)
|
|
73
|
+
tutor setup write --json '{"profile":{"native_language":"en","target_language":"uk"},"preferences":{}}'
|
|
74
|
+
|
|
75
|
+
# 4. Inside Claude, run /reload-plugins, then ask: "start a reading session"
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
`tutor init` detects Claude, Codex, Hermes, and OpenClaw and shows a keyboard
|
|
79
|
+
menu: arrow keys move, Space toggles providers, and Enter continues/applies.
|
|
80
|
+
Rerun any time to repair drift — it never touches your learner profile, history,
|
|
81
|
+
or secrets.
|
|
82
|
+
Non-interactive form: `tutor init --provider claude --yes` (also `--dry-run`,
|
|
83
|
+
`--json`).
|
|
84
|
+
|
|
85
|
+
Full Claude install doc: [`docs/install/claude.md`](docs/install/claude.md)
|
|
86
|
+
|
|
87
|
+
## Other hosts
|
|
88
|
+
|
|
89
|
+
- Codex — [`docs/install/codex.md`](docs/install/codex.md)
|
|
90
|
+
- Hermes — [`docs/install/hermes.md`](docs/install/hermes.md)
|
|
91
|
+
- OpenClaw — [`docs/install/openclaw.md`](docs/install/openclaw.md)
|
|
92
|
+
|
|
93
|
+
## How it works
|
|
94
|
+
|
|
95
|
+
```
|
|
96
|
+
┌──────────────┐ ┌────────────────┐ ┌─────────────────┐
|
|
97
|
+
│ AI Host │──────▶│ Tutor skill │──────▶│ tutor CLI │
|
|
98
|
+
│ (Claude/ │ │ (markdown + │ │ (Python) │
|
|
99
|
+
│ Codex/...) │ │ prompts) │ │ │
|
|
100
|
+
└──────────────┘ └────────────────┘ └─────────────────┘
|
|
101
|
+
│
|
|
102
|
+
┌───────────────┼────────────────┐
|
|
103
|
+
▼ ▼ ▼
|
|
104
|
+
┌──────────┐ ┌──────────────┐ ┌─────────┐
|
|
105
|
+
│ profile │ │ learning │ │ data/ │
|
|
106
|
+
│ YAML │ │ SQLite │ │ defaults│
|
|
107
|
+
└──────────┘ └──────────────┘ └─────────┘
|
|
108
|
+
|
|
109
|
+
All state local. No network calls except the host's own LLM API.
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Privacy
|
|
113
|
+
|
|
114
|
+
Your learner profile and preferences live in editable YAML files. Transactional learning state (vocabulary intervals, session history, progress) stays in a local SQLite database under your user data directory. There is no telemetry, no account, no cloud sync, and no remote storage. The only network traffic is the LLM API call your AI host already makes on your behalf.
|
|
115
|
+
|
|
116
|
+
## Roadmap
|
|
117
|
+
|
|
118
|
+
Tracked publicly on GitHub:
|
|
119
|
+
|
|
120
|
+
- [Milestones](https://github.com/artemVeduta/lingo-loop/milestones) — versioned scope.
|
|
121
|
+
- [Projects](https://github.com/artemVeduta/lingo-loop/projects) — in-flight work.
|
|
122
|
+
- [Discussions](https://github.com/artemVeduta/lingo-loop/discussions) — propose ideas before they become issues.
|
|
123
|
+
|
|
124
|
+
## Contributing
|
|
125
|
+
|
|
126
|
+
Pull requests welcome. See [CONTRIBUTING.md](CONTRIBUTING.md) for dev setup, branch naming, commit conventions, and the DCO sign-off policy.
|
|
127
|
+
|
|
128
|
+
## License
|
|
129
|
+
|
|
130
|
+
[MIT](LICENSE) © 2026 Artem Veduta
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# lingo-loop
|
|
2
|
+
|
|
3
|
+
> Learn languages inside your AI coding assistant — local-first, no telemetry.
|
|
4
|
+
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
[](https://www.python.org/downloads/)
|
|
7
|
+
[](https://github.com/artemVeduta/lingo-loop/actions/workflows/ci.yml)
|
|
8
|
+
|
|
9
|
+
<!-- TODO(oss-baseline-assets): record top-level demo cast and render gif via agg, then restore the image + cast links below.
|
|
10
|
+
Until the assets land, omit them to avoid broken-image rendering on GitHub. Tracked in docs/internal/launch-checklist.md. -->
|
|
11
|
+
*(Demo recording pending — see [`docs/internal/launch-checklist.md`](docs/internal/launch-checklist.md).)*
|
|
12
|
+
|
|
13
|
+
## Why
|
|
14
|
+
|
|
15
|
+
- **Local-first.** Your profile, vocabulary, and progress live in plain files on your machine.
|
|
16
|
+
- **No telemetry.** Nothing is sent anywhere except the LLM API call your host already makes.
|
|
17
|
+
- **Works inside your IDE.** Practice reading, writing, and vocabulary without leaving Claude Code, Codex, Hermes, or OpenClaw.
|
|
18
|
+
|
|
19
|
+
## Features
|
|
20
|
+
|
|
21
|
+
- Reading practice with adaptive vocabulary surfacing.
|
|
22
|
+
- Short writing prompts with corrections from your assistant.
|
|
23
|
+
- Spaced-repetition vocabulary loop, scheduled across sessions.
|
|
24
|
+
- Lesson and transcript flows tailored to your native and target languages.
|
|
25
|
+
- Progress tracking you can inspect with a single command.
|
|
26
|
+
- Editable YAML profile — language pair, level, and preferences in one place.
|
|
27
|
+
- Works across four AI coding hosts with the same CLI underneath.
|
|
28
|
+
|
|
29
|
+
## Quick start (Claude Code)
|
|
30
|
+
|
|
31
|
+
> PyPI publish is coming. Until then, install from source with [`uv`](https://docs.astral.sh/uv/) (or clone and run `uv pip install -e ".[dev]"` for a dev setup — see [CONTRIBUTING.md](CONTRIBUTING.md)).
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
# 1. Install the CLI from source (pip install lingo-loop coming soon)
|
|
35
|
+
uv tool install git+https://github.com/artemVeduta/lingo-loop
|
|
36
|
+
|
|
37
|
+
# 2. Detect AI hosts and install plugin wiring for the ones you use
|
|
38
|
+
tutor init
|
|
39
|
+
|
|
40
|
+
# 3. Write your learner profile (native + target language)
|
|
41
|
+
tutor setup write --json '{"profile":{"native_language":"en","target_language":"uk"},"preferences":{}}'
|
|
42
|
+
|
|
43
|
+
# 4. Inside Claude, run /reload-plugins, then ask: "start a reading session"
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
`tutor init` detects Claude, Codex, Hermes, and OpenClaw and shows a keyboard
|
|
47
|
+
menu: arrow keys move, Space toggles providers, and Enter continues/applies.
|
|
48
|
+
Rerun any time to repair drift — it never touches your learner profile, history,
|
|
49
|
+
or secrets.
|
|
50
|
+
Non-interactive form: `tutor init --provider claude --yes` (also `--dry-run`,
|
|
51
|
+
`--json`).
|
|
52
|
+
|
|
53
|
+
Full Claude install doc: [`docs/install/claude.md`](docs/install/claude.md)
|
|
54
|
+
|
|
55
|
+
## Other hosts
|
|
56
|
+
|
|
57
|
+
- Codex — [`docs/install/codex.md`](docs/install/codex.md)
|
|
58
|
+
- Hermes — [`docs/install/hermes.md`](docs/install/hermes.md)
|
|
59
|
+
- OpenClaw — [`docs/install/openclaw.md`](docs/install/openclaw.md)
|
|
60
|
+
|
|
61
|
+
## How it works
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
┌──────────────┐ ┌────────────────┐ ┌─────────────────┐
|
|
65
|
+
│ AI Host │──────▶│ Tutor skill │──────▶│ tutor CLI │
|
|
66
|
+
│ (Claude/ │ │ (markdown + │ │ (Python) │
|
|
67
|
+
│ Codex/...) │ │ prompts) │ │ │
|
|
68
|
+
└──────────────┘ └────────────────┘ └─────────────────┘
|
|
69
|
+
│
|
|
70
|
+
┌───────────────┼────────────────┐
|
|
71
|
+
▼ ▼ ▼
|
|
72
|
+
┌──────────┐ ┌──────────────┐ ┌─────────┐
|
|
73
|
+
│ profile │ │ learning │ │ data/ │
|
|
74
|
+
│ YAML │ │ SQLite │ │ defaults│
|
|
75
|
+
└──────────┘ └──────────────┘ └─────────┘
|
|
76
|
+
|
|
77
|
+
All state local. No network calls except the host's own LLM API.
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Privacy
|
|
81
|
+
|
|
82
|
+
Your learner profile and preferences live in editable YAML files. Transactional learning state (vocabulary intervals, session history, progress) stays in a local SQLite database under your user data directory. There is no telemetry, no account, no cloud sync, and no remote storage. The only network traffic is the LLM API call your AI host already makes on your behalf.
|
|
83
|
+
|
|
84
|
+
## Roadmap
|
|
85
|
+
|
|
86
|
+
Tracked publicly on GitHub:
|
|
87
|
+
|
|
88
|
+
- [Milestones](https://github.com/artemVeduta/lingo-loop/milestones) — versioned scope.
|
|
89
|
+
- [Projects](https://github.com/artemVeduta/lingo-loop/projects) — in-flight work.
|
|
90
|
+
- [Discussions](https://github.com/artemVeduta/lingo-loop/discussions) — propose ideas before they become issues.
|
|
91
|
+
|
|
92
|
+
## Contributing
|
|
93
|
+
|
|
94
|
+
Pull requests welcome. See [CONTRIBUTING.md](CONTRIBUTING.md) for dev setup, branch naming, commit conventions, and the DCO sign-off policy.
|
|
95
|
+
|
|
96
|
+
## License
|
|
97
|
+
|
|
98
|
+
[MIT](LICENSE) © 2026 Artem Veduta
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: tutor-judge
|
|
3
|
+
description: Grades a learner answer and returns only a FeedbackEnvelope JSON object using the supplied allowed_error_tags.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# tutor-judge
|
|
7
|
+
|
|
8
|
+
Return only a `FeedbackEnvelope` JSON object. No prose outside JSON.
|
|
9
|
+
|
|
10
|
+
Use the `allowed_error_tags` supplied in input. Do not invent tags. Keep `confidence` one of `high`, `medium`, or `low`. Low confidence cannot be definitive high-severity correction. Include `next_drill_hint`.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
4
|
+
cd "${ROOT}"
|
|
5
|
+
|
|
6
|
+
if ! command -v claude &>/dev/null; then
|
|
7
|
+
echo "error: claude CLI not found. Install Claude Code first." >&2
|
|
8
|
+
exit 1
|
|
9
|
+
fi
|
|
10
|
+
|
|
11
|
+
if ! command -v uv &>/dev/null; then
|
|
12
|
+
echo "error: uv not found. Install uv first: https://docs.astral.sh/uv/" >&2
|
|
13
|
+
exit 1
|
|
14
|
+
fi
|
|
15
|
+
|
|
16
|
+
if [[ ! -x .venv/bin/python ]]; then
|
|
17
|
+
echo "==> Creating virtual environment and installing dependencies..."
|
|
18
|
+
uv venv .venv
|
|
19
|
+
uv pip install -e ".[dev]"
|
|
20
|
+
fi
|
|
21
|
+
|
|
22
|
+
echo "==> Validating plugin..."
|
|
23
|
+
claude plugin validate . ## --strict
|
|
24
|
+
|
|
25
|
+
echo "==> Launching Claude Code with language-tutor plugin..."
|
|
26
|
+
exec claude --plugin-dir "${ROOT}" "$@"
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
4
|
+
PYTHON="${ROOT}/.venv/bin/python"
|
|
5
|
+
|
|
6
|
+
if [[ -x "${PYTHON}" ]]; then
|
|
7
|
+
exec "${PYTHON}" -m language_tutor.cli "$@"
|
|
8
|
+
fi
|
|
9
|
+
|
|
10
|
+
export PYTHONPATH="${ROOT}/src${PYTHONPATH:+:${PYTHONPATH}}"
|
|
11
|
+
exec python -m language_tutor.cli "$@"
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Language Tutor (Hermes Profile Prompt)
|
|
2
|
+
|
|
3
|
+
You are a patient, source-backed language tutor running inside the Hermes
|
|
4
|
+
host. You operate text-only: reading, lessons, transcripts, vocabulary,
|
|
5
|
+
writing, and progress reporting. You do not produce or consume audio or
|
|
6
|
+
images.
|
|
7
|
+
|
|
8
|
+
## Lifecycle
|
|
9
|
+
|
|
10
|
+
Hermes does not provide SessionStart/SessionEnd hooks for this profile.
|
|
11
|
+
Boot context is built on demand via an explicit tutor command (the
|
|
12
|
+
operator runs the tutor's boot command after `hermes profile install`).
|
|
13
|
+
There is no automatic session-end hook; end-of-session work is invoked
|
|
14
|
+
explicitly when requested.
|
|
15
|
+
|
|
16
|
+
## Boot context
|
|
17
|
+
|
|
18
|
+
When the explicit tutor boot command runs, load the learner profile and
|
|
19
|
+
preferences through the tutor core and render the boot context sections.
|
|
20
|
+
Never invent learner state; rely on the core's persisted data, which lives
|
|
21
|
+
outside this distribution.
|
|
22
|
+
|
|
23
|
+
## Behavior contract
|
|
24
|
+
|
|
25
|
+
- Keep all interaction text-only and terminal-readable.
|
|
26
|
+
- Defer pedagogy, feedback, progress, and persistence to the tutor core.
|
|
27
|
+
- Never echo, package, or export learner secrets, memories, sessions,
|
|
28
|
+
database state, logs, or local overrides.
|
|
29
|
+
- Stay within the declared capability profile: no audio, no images, no
|
|
30
|
+
host-specific side effects beyond the documented text flows.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Hermes profile configuration defaults for the language-tutor agent.
|
|
2
|
+
# Defaults only. No learner data, no secrets, no machine-local state.
|
|
3
|
+
schema_version: 1
|
|
4
|
+
|
|
5
|
+
# Capability declaration mirrors src/language_tutor/adapters/registry.py
|
|
6
|
+
# (HostId.HERMES). Source of truth stays in the registry; this is a
|
|
7
|
+
# documentation echo for operators inspecting the distribution.
|
|
8
|
+
capabilities:
|
|
9
|
+
text_support: supported
|
|
10
|
+
audio_support: unsupported
|
|
11
|
+
image_support: unsupported
|
|
12
|
+
lifecycle_start: explicit_command
|
|
13
|
+
lifecycle_end: not_available
|
|
14
|
+
boot_context_trigger: explicit_tutor_command
|
|
15
|
+
|
|
16
|
+
# Tutor flows exposed by this profile. All text-only.
|
|
17
|
+
flows:
|
|
18
|
+
- reading
|
|
19
|
+
- lesson
|
|
20
|
+
- transcript
|
|
21
|
+
- vocab
|
|
22
|
+
- writing
|
|
23
|
+
- progress
|
|
24
|
+
|
|
25
|
+
# Hermes profile lifecycle commands (documentation for operators).
|
|
26
|
+
commands:
|
|
27
|
+
install: hermes profile install
|
|
28
|
+
update: hermes profile update
|
|
29
|
+
inspect: hermes profile info language-tutor
|
|
30
|
+
list: hermes profile list
|
|
31
|
+
remove: hermes profile delete language-tutor
|
|
32
|
+
|
|
33
|
+
# Operator-supplied environment is read from the operator's own untracked
|
|
34
|
+
# .env at runtime. This distribution ships no values.
|
|
35
|
+
env:
|
|
36
|
+
source: operator_local_env
|
|
37
|
+
bundled_values: false
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Hermes profile distribution manifest for the language-tutor agent.
|
|
2
|
+
# Distribution metadata and defaults only. Contains NO learner-owned data.
|
|
3
|
+
# Installed/updated via `hermes profile install` / `hermes profile update`.
|
|
4
|
+
schema_version: 1
|
|
5
|
+
name: language-tutor
|
|
6
|
+
display_name: Language Tutor
|
|
7
|
+
version: 0.1.0
|
|
8
|
+
description: >-
|
|
9
|
+
Source-backed language-learning tutor profile for Hermes. Provides
|
|
10
|
+
text-only reading, lesson, transcript, vocab, writing, and progress
|
|
11
|
+
flows backed by the host-independent tutor core. No audio or image
|
|
12
|
+
capabilities.
|
|
13
|
+
license: MIT
|
|
14
|
+
source_url: https://github.com/artemVeduta/lingo-loop
|
|
15
|
+
|
|
16
|
+
# Whole-agent package contents (git-backed distribution).
|
|
17
|
+
profile:
|
|
18
|
+
prompt: SOUL.md
|
|
19
|
+
config: config.yaml
|
|
20
|
+
# Tutor skills are reused from the repository root `skills/` surface; this
|
|
21
|
+
# distribution does not ship its own skills/ tree.
|
|
22
|
+
skills: ../skills
|
|
23
|
+
|
|
24
|
+
# Environment variables the operator must supply locally before launch.
|
|
25
|
+
# Declared here for documentation only; values are never bundled. The actual
|
|
26
|
+
# secrets live in the operator's own untracked .env (NOT shipped).
|
|
27
|
+
env_requires:
|
|
28
|
+
- name: ANTHROPIC_API_KEY
|
|
29
|
+
required: true
|
|
30
|
+
description: API key for the tutor model provider. Operator-supplied; never bundled.
|
|
31
|
+
|
|
32
|
+
# Hard exclusions: data that must never be packaged, copied, or published.
|
|
33
|
+
exclude:
|
|
34
|
+
- ".env"
|
|
35
|
+
- "*.env"
|
|
36
|
+
- secrets
|
|
37
|
+
- memories
|
|
38
|
+
- sessions
|
|
39
|
+
- "*.sqlite"
|
|
40
|
+
- "*.sqlite3"
|
|
41
|
+
- "*.db"
|
|
42
|
+
- "*.db-wal"
|
|
43
|
+
- "*.db-shm"
|
|
44
|
+
- logs
|
|
45
|
+
- "*.log"
|
|
46
|
+
- caches
|
|
47
|
+
- "*.cache"
|
|
48
|
+
- local
|
|
49
|
+
- local_overrides
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
CREATE TABLE IF NOT EXISTS migration_records (
|
|
2
|
+
version INTEGER PRIMARY KEY,
|
|
3
|
+
name TEXT NOT NULL,
|
|
4
|
+
checksum TEXT NOT NULL,
|
|
5
|
+
applied_at TEXT NOT NULL
|
|
6
|
+
);
|
|
7
|
+
|
|
8
|
+
CREATE TABLE IF NOT EXISTS lifecycle_events (
|
|
9
|
+
id TEXT PRIMARY KEY,
|
|
10
|
+
session_id TEXT NOT NULL,
|
|
11
|
+
event_type TEXT NOT NULL,
|
|
12
|
+
payload_json TEXT NOT NULL,
|
|
13
|
+
occurred_at TEXT NOT NULL,
|
|
14
|
+
source TEXT NOT NULL
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
CREATE TABLE IF NOT EXISTS vocabulary_items (
|
|
18
|
+
id TEXT PRIMARY KEY,
|
|
19
|
+
target_language TEXT NOT NULL,
|
|
20
|
+
prompt TEXT NOT NULL,
|
|
21
|
+
lemma TEXT,
|
|
22
|
+
accepted_answers_json TEXT NOT NULL,
|
|
23
|
+
hint TEXT,
|
|
24
|
+
tags_json TEXT NOT NULL,
|
|
25
|
+
state TEXT NOT NULL,
|
|
26
|
+
ease_factor REAL NOT NULL CHECK (ease_factor >= 1.3),
|
|
27
|
+
repetition_count INTEGER NOT NULL CHECK (repetition_count >= 0),
|
|
28
|
+
interval_days INTEGER NOT NULL CHECK (interval_days >= 0),
|
|
29
|
+
due_at TEXT NOT NULL,
|
|
30
|
+
created_at TEXT NOT NULL,
|
|
31
|
+
updated_at TEXT NOT NULL,
|
|
32
|
+
dedupe_key TEXT NOT NULL UNIQUE
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
CREATE TABLE IF NOT EXISTS answer_events (
|
|
36
|
+
id TEXT PRIMARY KEY,
|
|
37
|
+
idempotency_key TEXT UNIQUE,
|
|
38
|
+
session_id TEXT NOT NULL,
|
|
39
|
+
skill TEXT NOT NULL,
|
|
40
|
+
prompt_ref TEXT NOT NULL,
|
|
41
|
+
learner_answer TEXT NOT NULL,
|
|
42
|
+
outcome TEXT NOT NULL,
|
|
43
|
+
feedback_envelope_json TEXT,
|
|
44
|
+
recorded_at TEXT NOT NULL
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
CREATE TABLE IF NOT EXISTS vocabulary_reviews (
|
|
48
|
+
id TEXT PRIMARY KEY,
|
|
49
|
+
session_id TEXT NOT NULL,
|
|
50
|
+
vocabulary_item_id TEXT NOT NULL REFERENCES vocabulary_items(id),
|
|
51
|
+
answer_event_id TEXT NOT NULL UNIQUE REFERENCES answer_events(id),
|
|
52
|
+
verdict TEXT NOT NULL,
|
|
53
|
+
quality INTEGER NOT NULL CHECK (quality BETWEEN 0 AND 5),
|
|
54
|
+
previous_state_json TEXT NOT NULL,
|
|
55
|
+
next_state_json TEXT NOT NULL,
|
|
56
|
+
reviewed_at TEXT NOT NULL
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
CREATE TABLE IF NOT EXISTS mistake_events (
|
|
60
|
+
id TEXT PRIMARY KEY,
|
|
61
|
+
session_id TEXT NOT NULL,
|
|
62
|
+
answer_event_id TEXT REFERENCES answer_events(id),
|
|
63
|
+
skill TEXT NOT NULL,
|
|
64
|
+
span_start INTEGER,
|
|
65
|
+
span_end INTEGER,
|
|
66
|
+
span_text TEXT,
|
|
67
|
+
severity TEXT NOT NULL,
|
|
68
|
+
tag TEXT NOT NULL,
|
|
69
|
+
explanation TEXT NOT NULL,
|
|
70
|
+
confidence TEXT NOT NULL,
|
|
71
|
+
created_at TEXT NOT NULL
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
CREATE TABLE IF NOT EXISTS session_summaries (
|
|
75
|
+
id TEXT PRIMARY KEY,
|
|
76
|
+
session_id TEXT NOT NULL UNIQUE,
|
|
77
|
+
summary_for_user TEXT NOT NULL,
|
|
78
|
+
summary_for_next_boot TEXT NOT NULL,
|
|
79
|
+
weak_tags_json TEXT NOT NULL,
|
|
80
|
+
next_focus TEXT NOT NULL,
|
|
81
|
+
cost_snapshot_json TEXT NOT NULL,
|
|
82
|
+
created_at TEXT NOT NULL
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
CREATE TABLE IF NOT EXISTS skill_metrics (
|
|
86
|
+
id TEXT PRIMARY KEY,
|
|
87
|
+
metric_date TEXT NOT NULL,
|
|
88
|
+
metric_name TEXT NOT NULL,
|
|
89
|
+
metric_value REAL NOT NULL,
|
|
90
|
+
dimensions_json TEXT NOT NULL,
|
|
91
|
+
created_at TEXT NOT NULL
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
CREATE TABLE IF NOT EXISTS cost_events (
|
|
95
|
+
id TEXT PRIMARY KEY,
|
|
96
|
+
session_id TEXT NOT NULL,
|
|
97
|
+
operation TEXT NOT NULL,
|
|
98
|
+
model TEXT NOT NULL,
|
|
99
|
+
input_tokens INTEGER NOT NULL CHECK (input_tokens >= 0),
|
|
100
|
+
output_tokens INTEGER NOT NULL CHECK (output_tokens >= 0),
|
|
101
|
+
cache_read_tokens INTEGER NOT NULL CHECK (cache_read_tokens >= 0),
|
|
102
|
+
estimated_cost_usd REAL CHECK (estimated_cost_usd IS NULL OR estimated_cost_usd >= 0),
|
|
103
|
+
pricing_source TEXT NOT NULL,
|
|
104
|
+
source_event_id TEXT,
|
|
105
|
+
created_at TEXT NOT NULL
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
CREATE INDEX IF NOT EXISTS idx_vocabulary_due ON vocabulary_items(due_at, state);
|
|
109
|
+
CREATE INDEX IF NOT EXISTS idx_answers_session ON answer_events(session_id, recorded_at);
|
|
110
|
+
CREATE INDEX IF NOT EXISTS idx_mistakes_tag ON mistake_events(tag, created_at);
|
|
111
|
+
CREATE INDEX IF NOT EXISTS idx_cost_month ON cost_events(created_at);
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
ALTER TABLE vocabulary_items
|
|
2
|
+
ADD COLUMN card_type TEXT NOT NULL DEFAULT 'standard'
|
|
3
|
+
CHECK (card_type IN ('standard', 'cloze'));
|
|
4
|
+
|
|
5
|
+
ALTER TABLE vocabulary_items
|
|
6
|
+
ADD COLUMN notes_json TEXT NOT NULL DEFAULT '[]';
|
|
7
|
+
|
|
8
|
+
ALTER TABLE vocabulary_items
|
|
9
|
+
ADD COLUMN sources_json TEXT NOT NULL DEFAULT '[]';
|
|
10
|
+
|
|
11
|
+
UPDATE vocabulary_items
|
|
12
|
+
SET dedupe_key = 'standard:' || lower(trim(coalesce(lemma, prompt))) || ':' || lower(trim(prompt));
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
CREATE INDEX IF NOT EXISTS idx_progress_sessions_created
|
|
2
|
+
ON session_summaries(created_at DESC, session_id);
|
|
3
|
+
|
|
4
|
+
CREATE INDEX IF NOT EXISTS idx_progress_reviews_session_time
|
|
5
|
+
ON vocabulary_reviews(session_id, reviewed_at);
|
|
6
|
+
|
|
7
|
+
CREATE INDEX IF NOT EXISTS idx_progress_mistakes_session_time
|
|
8
|
+
ON mistake_events(session_id, created_at);
|
|
9
|
+
|
|
10
|
+
CREATE INDEX IF NOT EXISTS idx_progress_answers_session_skill_time
|
|
11
|
+
ON answer_events(session_id, skill, recorded_at);
|
|
12
|
+
|
|
13
|
+
CREATE INDEX IF NOT EXISTS idx_progress_vocab_due_state
|
|
14
|
+
ON vocabulary_items(due_at, state);
|