slxd-coursekit 0.1.2__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.
- slxd_coursekit-0.1.2/.github/workflows/ci.yml +23 -0
- slxd_coursekit-0.1.2/.github/workflows/release.yml +54 -0
- slxd_coursekit-0.1.2/.gitignore +7 -0
- slxd_coursekit-0.1.2/.python-version +1 -0
- slxd_coursekit-0.1.2/AGENTS.md +61 -0
- slxd_coursekit-0.1.2/CLAUDE.md +1 -0
- slxd_coursekit-0.1.2/CONTRIBUTING.es.md +180 -0
- slxd_coursekit-0.1.2/CONTRIBUTING.md +180 -0
- slxd_coursekit-0.1.2/LICENSE +21 -0
- slxd_coursekit-0.1.2/PKG-INFO +115 -0
- slxd_coursekit-0.1.2/README.es.md +99 -0
- slxd_coursekit-0.1.2/README.md +98 -0
- slxd_coursekit-0.1.2/dev/plan.md +387 -0
- slxd_coursekit-0.1.2/docs/en/01-getting-started.md +270 -0
- slxd_coursekit-0.1.2/docs/en/02-workflow.md +496 -0
- slxd_coursekit-0.1.2/docs/en/03-commands.md +977 -0
- slxd_coursekit-0.1.2/docs/en/04-configuration.md +516 -0
- slxd_coursekit-0.1.2/docs/en/05-agents.md +264 -0
- slxd_coursekit-0.1.2/docs/en/06-content.md +518 -0
- slxd_coursekit-0.1.2/docs/en/07-media.md +442 -0
- slxd_coursekit-0.1.2/docs/en/08-assembly-and-delivery.md +616 -0
- slxd_coursekit-0.1.2/docs/en/09-troubleshooting.md +427 -0
- slxd_coursekit-0.1.2/docs/en/index.md +56 -0
- slxd_coursekit-0.1.2/docs/es/01-getting-started.md +270 -0
- slxd_coursekit-0.1.2/docs/es/02-workflow.md +496 -0
- slxd_coursekit-0.1.2/docs/es/03-commands.md +977 -0
- slxd_coursekit-0.1.2/docs/es/04-configuration.md +516 -0
- slxd_coursekit-0.1.2/docs/es/05-agents.md +264 -0
- slxd_coursekit-0.1.2/docs/es/06-content.md +518 -0
- slxd_coursekit-0.1.2/docs/es/07-media.md +442 -0
- slxd_coursekit-0.1.2/docs/es/08-assembly-and-delivery.md +616 -0
- slxd_coursekit-0.1.2/docs/es/09-troubleshooting.md +427 -0
- slxd_coursekit-0.1.2/docs/es/index.md +56 -0
- slxd_coursekit-0.1.2/pyproject.toml +45 -0
- slxd_coursekit-0.1.2/src/coursekit/__init__.py +8 -0
- slxd_coursekit-0.1.2/src/coursekit/__main__.py +5 -0
- slxd_coursekit-0.1.2/src/coursekit/agentkit/__init__.py +0 -0
- slxd_coursekit-0.1.2/src/coursekit/agentkit/agents/reviewer.md +29 -0
- slxd_coursekit-0.1.2/src/coursekit/agentkit/agents/writer.md +26 -0
- slxd_coursekit-0.1.2/src/coursekit/agentkit/commands/approve-design.md +19 -0
- slxd_coursekit-0.1.2/src/coursekit/agentkit/commands/approve-unit.md +18 -0
- slxd_coursekit-0.1.2/src/coursekit/agentkit/commands/assemble.md +10 -0
- slxd_coursekit-0.1.2/src/coursekit/agentkit/commands/client-feedback.md +9 -0
- slxd_coursekit-0.1.2/src/coursekit/agentkit/commands/course-status.md +7 -0
- slxd_coursekit-0.1.2/src/coursekit/agentkit/commands/define-theme.md +10 -0
- slxd_coursekit-0.1.2/src/coursekit/agentkit/commands/deliver.md +11 -0
- slxd_coursekit-0.1.2/src/coursekit/agentkit/commands/design-change.md +14 -0
- slxd_coursekit-0.1.2/src/coursekit/agentkit/commands/new-course.md +29 -0
- slxd_coursekit-0.1.2/src/coursekit/agentkit/commands/produce-media.md +10 -0
- slxd_coursekit-0.1.2/src/coursekit/agentkit/commands/review-unit.md +12 -0
- slxd_coursekit-0.1.2/src/coursekit/agentkit/commands/sync-directives.md +19 -0
- slxd_coursekit-0.1.2/src/coursekit/agentkit/commands/write-unit.md +25 -0
- slxd_coursekit-0.1.2/src/coursekit/agentkit/docs/content-format.md +126 -0
- slxd_coursekit-0.1.2/src/coursekit/agentkit/docs/slxd-mcp.md +41 -0
- slxd_coursekit-0.1.2/src/coursekit/agentkit/skills/_slxd-tools.md +10 -0
- slxd_coursekit-0.1.2/src/coursekit/agentkit/skills/client-review/SKILL.md +50 -0
- slxd_coursekit-0.1.2/src/coursekit/agentkit/skills/content-review/SKILL.md +78 -0
- slxd_coursekit-0.1.2/src/coursekit/agentkit/skills/content-writing/SKILL.md +70 -0
- slxd_coursekit-0.1.2/src/coursekit/agentkit/skills/creator-assembly/SKILL.md +101 -0
- slxd_coursekit-0.1.2/src/coursekit/agentkit/skills/delivery/SKILL.md +48 -0
- slxd_coursekit-0.1.2/src/coursekit/agentkit/skills/html-assembly/SKILL.md +51 -0
- slxd_coursekit-0.1.2/src/coursekit/agentkit/skills/instructional-design/SKILL.md +117 -0
- slxd_coursekit-0.1.2/src/coursekit/agentkit/skills/media-production/SKILL.md +97 -0
- slxd_coursekit-0.1.2/src/coursekit/agentkit/skills/theme-definition/SKILL.md +67 -0
- slxd_coursekit-0.1.2/src/coursekit/agents.py +363 -0
- slxd_coursekit-0.1.2/src/coursekit/approve.py +206 -0
- slxd_coursekit-0.1.2/src/coursekit/assemble.py +794 -0
- slxd_coursekit-0.1.2/src/coursekit/brief.py +268 -0
- slxd_coursekit-0.1.2/src/coursekit/catalog.py +189 -0
- slxd_coursekit-0.1.2/src/coursekit/cli.py +318 -0
- slxd_coursekit-0.1.2/src/coursekit/clientreview.py +139 -0
- slxd_coursekit-0.1.2/src/coursekit/commands.py +895 -0
- slxd_coursekit-0.1.2/src/coursekit/config.py +130 -0
- slxd_coursekit-0.1.2/src/coursekit/course.py +114 -0
- slxd_coursekit-0.1.2/src/coursekit/defaults/__init__.py +0 -0
- slxd_coursekit-0.1.2/src/coursekit/defaults/agents.yaml +21 -0
- slxd_coursekit-0.1.2/src/coursekit/defaults/delivery.yaml +13 -0
- slxd_coursekit-0.1.2/src/coursekit/defaults/directives.yaml +79 -0
- slxd_coursekit-0.1.2/src/coursekit/defaults/media.yaml +59 -0
- slxd_coursekit-0.1.2/src/coursekit/defaults/rules.yaml +54 -0
- slxd_coursekit-0.1.2/src/coursekit/delivery.py +130 -0
- slxd_coursekit-0.1.2/src/coursekit/directives.py +56 -0
- slxd_coursekit-0.1.2/src/coursekit/doctor.py +144 -0
- slxd_coursekit-0.1.2/src/coursekit/envfile.py +38 -0
- slxd_coursekit-0.1.2/src/coursekit/fingerprint.py +59 -0
- slxd_coursekit-0.1.2/src/coursekit/handoff.py +472 -0
- slxd_coursekit-0.1.2/src/coursekit/htmlkit/__init__.py +5 -0
- slxd_coursekit-0.1.2/src/coursekit/htmlkit/build.py +249 -0
- slxd_coursekit-0.1.2/src/coursekit/htmlkit/check.py +106 -0
- slxd_coursekit-0.1.2/src/coursekit/htmlkit/manifest.py +52 -0
- slxd_coursekit-0.1.2/src/coursekit/htmlkit/render.py +374 -0
- slxd_coursekit-0.1.2/src/coursekit/htmlkit/sanitize.py +96 -0
- slxd_coursekit-0.1.2/src/coursekit/i18n.py +96 -0
- slxd_coursekit-0.1.2/src/coursekit/identity.py +37 -0
- slxd_coursekit-0.1.2/src/coursekit/init.py +314 -0
- slxd_coursekit-0.1.2/src/coursekit/lang/__init__.py +47 -0
- slxd_coursekit-0.1.2/src/coursekit/lang/en.yaml +83 -0
- slxd_coursekit-0.1.2/src/coursekit/lang/es.yaml +83 -0
- slxd_coursekit-0.1.2/src/coursekit/lang/messages/__init__.py +0 -0
- slxd_coursekit-0.1.2/src/coursekit/lang/messages/agents.yaml +10 -0
- slxd_coursekit-0.1.2/src/coursekit/lang/messages/approve.yaml +57 -0
- slxd_coursekit-0.1.2/src/coursekit/lang/messages/assemble.yaml +73 -0
- slxd_coursekit-0.1.2/src/coursekit/lang/messages/brief.yaml +57 -0
- slxd_coursekit-0.1.2/src/coursekit/lang/messages/cli.yaml +129 -0
- slxd_coursekit-0.1.2/src/coursekit/lang/messages/clientreview.yaml +24 -0
- slxd_coursekit-0.1.2/src/coursekit/lang/messages/commands.yaml +314 -0
- slxd_coursekit-0.1.2/src/coursekit/lang/messages/config.yaml +4 -0
- slxd_coursekit-0.1.2/src/coursekit/lang/messages/course.yaml +12 -0
- slxd_coursekit-0.1.2/src/coursekit/lang/messages/delivery.yaml +18 -0
- slxd_coursekit-0.1.2/src/coursekit/lang/messages/directives.yaml +14 -0
- slxd_coursekit-0.1.2/src/coursekit/lang/messages/doctor.yaml +57 -0
- slxd_coursekit-0.1.2/src/coursekit/lang/messages/handoff.yaml +92 -0
- slxd_coursekit-0.1.2/src/coursekit/lang/messages/htmlkit.yaml +20 -0
- slxd_coursekit-0.1.2/src/coursekit/lang/messages/init.yaml +30 -0
- slxd_coursekit-0.1.2/src/coursekit/lang/messages/launch.yaml +31 -0
- slxd_coursekit-0.1.2/src/coursekit/lang/messages/media.yaml +54 -0
- slxd_coursekit-0.1.2/src/coursekit/lang/messages/mediatools.yaml +35 -0
- slxd_coursekit-0.1.2/src/coursekit/lang/messages/new.yaml +2 -0
- slxd_coursekit-0.1.2/src/coursekit/lang/messages/outline.yaml +11 -0
- slxd_coursekit-0.1.2/src/coursekit/lang/messages/project.yaml +4 -0
- slxd_coursekit-0.1.2/src/coursekit/lang/messages/publish.yaml +29 -0
- slxd_coursekit-0.1.2/src/coursekit/lang/messages/refresh.yaml +10 -0
- slxd_coursekit-0.1.2/src/coursekit/lang/messages/setup.yaml +115 -0
- slxd_coursekit-0.1.2/src/coursekit/lang/messages/states.yaml +9 -0
- slxd_coursekit-0.1.2/src/coursekit/lang/messages/status.yaml +63 -0
- slxd_coursekit-0.1.2/src/coursekit/lang/messages/sync.yaml +25 -0
- slxd_coursekit-0.1.2/src/coursekit/lang/messages/theme.yaml +32 -0
- slxd_coursekit-0.1.2/src/coursekit/lang/messages/uninstall.yaml +34 -0
- slxd_coursekit-0.1.2/src/coursekit/lang/messages/verify.yaml +75 -0
- slxd_coursekit-0.1.2/src/coursekit/lang/messages/voices.yaml +42 -0
- slxd_coursekit-0.1.2/src/coursekit/launch.py +386 -0
- slxd_coursekit-0.1.2/src/coursekit/media.py +341 -0
- slxd_coursekit-0.1.2/src/coursekit/mediatools.py +223 -0
- slxd_coursekit-0.1.2/src/coursekit/new.py +85 -0
- slxd_coursekit-0.1.2/src/coursekit/outline.py +65 -0
- slxd_coursekit-0.1.2/src/coursekit/project.py +88 -0
- slxd_coursekit-0.1.2/src/coursekit/publish.py +210 -0
- slxd_coursekit-0.1.2/src/coursekit/refresh.py +80 -0
- slxd_coursekit-0.1.2/src/coursekit/setup.py +371 -0
- slxd_coursekit-0.1.2/src/coursekit/states.py +123 -0
- slxd_coursekit-0.1.2/src/coursekit/status.py +68 -0
- slxd_coursekit-0.1.2/src/coursekit/store.py +153 -0
- slxd_coursekit-0.1.2/src/coursekit/sync.py +264 -0
- slxd_coursekit-0.1.2/src/coursekit/templates/__init__.py +0 -0
- slxd_coursekit-0.1.2/src/coursekit/templates/brief/__init__.py +0 -0
- slxd_coursekit-0.1.2/src/coursekit/templates/brief/links.md +4 -0
- slxd_coursekit-0.1.2/src/coursekit/templates/brief/notes.md +13 -0
- slxd_coursekit-0.1.2/src/coursekit/templates/course/__init__.py +0 -0
- slxd_coursekit-0.1.2/src/coursekit/templates/course/course.yaml +40 -0
- slxd_coursekit-0.1.2/src/coursekit/templates/course/en/__init__.py +0 -0
- slxd_coursekit-0.1.2/src/coursekit/templates/course/en/assessment.md +12 -0
- slxd_coursekit-0.1.2/src/coursekit/templates/course/en/content.md +9 -0
- slxd_coursekit-0.1.2/src/coursekit/templates/course/es/__init__.py +0 -0
- slxd_coursekit-0.1.2/src/coursekit/templates/course/es/assessment.md +12 -0
- slxd_coursekit-0.1.2/src/coursekit/templates/course/es/content.md +9 -0
- slxd_coursekit-0.1.2/src/coursekit/templates/html/__init__.py +0 -0
- slxd_coursekit-0.1.2/src/coursekit/templates/html/package.json +9 -0
- slxd_coursekit-0.1.2/src/coursekit/templates/html/player/entry.js +123 -0
- slxd_coursekit-0.1.2/src/coursekit/templates/html/player/questions.js +265 -0
- slxd_coursekit-0.1.2/src/coursekit/templates/html/player/scorm.js +92 -0
- slxd_coursekit-0.1.2/src/coursekit/templates/html/player/widgets.js +126 -0
- slxd_coursekit-0.1.2/src/coursekit/templates/html/styles/base.css +162 -0
- slxd_coursekit-0.1.2/src/coursekit/templates/project/AGENTS.md +44 -0
- slxd_coursekit-0.1.2/src/coursekit/templates/project/CLAUDE.md +1 -0
- slxd_coursekit-0.1.2/src/coursekit/templates/project/__init__.py +0 -0
- slxd_coursekit-0.1.2/src/coursekit/templates/project/brief/links.md +4 -0
- slxd_coursekit-0.1.2/src/coursekit/templates/project/brief/notes.md +12 -0
- slxd_coursekit-0.1.2/src/coursekit/templates/project/env.example +37 -0
- slxd_coursekit-0.1.2/src/coursekit/templates/project/githooks/__init__.py +0 -0
- slxd_coursekit-0.1.2/src/coursekit/templates/project/githooks/post-checkout +4 -0
- slxd_coursekit-0.1.2/src/coursekit/templates/project/githooks/post-merge +7 -0
- slxd_coursekit-0.1.2/src/coursekit/templates/project/gitignore +19 -0
- slxd_coursekit-0.1.2/src/coursekit/templates/project/project.yaml +42 -0
- slxd_coursekit-0.1.2/src/coursekit/templates/remotion/__init__.py +0 -0
- slxd_coursekit-0.1.2/src/coursekit/templates/remotion/package.json +19 -0
- slxd_coursekit-0.1.2/src/coursekit/templates/remotion/src/Root.tsx +32 -0
- slxd_coursekit-0.1.2/src/coursekit/templates/remotion/src/index.ts +4 -0
- slxd_coursekit-0.1.2/src/coursekit/templates/remotion/src/theme.ts +7 -0
- slxd_coursekit-0.1.2/src/coursekit/templates/remotion/tsconfig.json +13 -0
- slxd_coursekit-0.1.2/src/coursekit/theme.py +454 -0
- slxd_coursekit-0.1.2/src/coursekit/tools/__init__.py +0 -0
- slxd_coursekit-0.1.2/src/coursekit/tools/web2md.js +513 -0
- slxd_coursekit-0.1.2/src/coursekit/uninstall.py +205 -0
- slxd_coursekit-0.1.2/src/coursekit/util.py +76 -0
- slxd_coursekit-0.1.2/src/coursekit/verify.py +355 -0
- slxd_coursekit-0.1.2/src/coursekit/voices.py +131 -0
- slxd_coursekit-0.1.2/tests/__init__.py +0 -0
- slxd_coursekit-0.1.2/tests/conftest.py +36 -0
- slxd_coursekit-0.1.2/tests/fixtures/creator_theme.json +100 -0
- slxd_coursekit-0.1.2/tests/fixtures/html_assessment.md +27 -0
- slxd_coursekit-0.1.2/tests/fixtures/html_content.md +176 -0
- slxd_coursekit-0.1.2/tests/fixtures/matrix.json +19 -0
- slxd_coursekit-0.1.2/tests/test_agents.py +143 -0
- slxd_coursekit-0.1.2/tests/test_assemble.py +186 -0
- slxd_coursekit-0.1.2/tests/test_audit_fixes.py +285 -0
- slxd_coursekit-0.1.2/tests/test_cli.py +16 -0
- slxd_coursekit-0.1.2/tests/test_config.py +130 -0
- slxd_coursekit-0.1.2/tests/test_content_words.py +183 -0
- slxd_coursekit-0.1.2/tests/test_course_flow.py +217 -0
- slxd_coursekit-0.1.2/tests/test_delivery.py +170 -0
- slxd_coursekit-0.1.2/tests/test_docs.py +109 -0
- slxd_coursekit-0.1.2/tests/test_handoff.py +446 -0
- slxd_coursekit-0.1.2/tests/test_html_backend.py +352 -0
- slxd_coursekit-0.1.2/tests/test_i18n.py +80 -0
- slxd_coursekit-0.1.2/tests/test_init.py +331 -0
- slxd_coursekit-0.1.2/tests/test_launch.py +185 -0
- slxd_coursekit-0.1.2/tests/test_mcp_blocker.py +192 -0
- slxd_coursekit-0.1.2/tests/test_media.py +128 -0
- slxd_coursekit-0.1.2/tests/test_polish.py +116 -0
- slxd_coursekit-0.1.2/tests/test_refresh.py +79 -0
- slxd_coursekit-0.1.2/tests/test_review_modes.py +106 -0
- slxd_coursekit-0.1.2/tests/test_setup.py +134 -0
- slxd_coursekit-0.1.2/tests/test_states_flow.py +295 -0
- slxd_coursekit-0.1.2/tests/test_store.py +320 -0
- slxd_coursekit-0.1.2/tests/test_theme.py +221 -0
- slxd_coursekit-0.1.2/tests/test_theme_choice.py +135 -0
- slxd_coursekit-0.1.2/uv.lock +924 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
name: ci
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
strategy:
|
|
11
|
+
matrix:
|
|
12
|
+
os: [ubuntu-latest, macos-latest, windows-latest]
|
|
13
|
+
runs-on: ${{ matrix.os }}
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
- uses: astral-sh/setup-uv@v6
|
|
17
|
+
- run: uv sync --group dev
|
|
18
|
+
- run: uv run ruff check .
|
|
19
|
+
- run: uv run pytest -q
|
|
20
|
+
- name: install as a tool
|
|
21
|
+
run: |
|
|
22
|
+
uv tool install .
|
|
23
|
+
coursekit --version
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
name: release
|
|
2
|
+
|
|
3
|
+
# A tag `vX.Y.Z` publishes that version: the package is built, attached to a GitHub release and uploaded to PyPI (trusted publishing:
|
|
4
|
+
# the `pypi` environment of this repository, registered as publisher of the project on PyPI).
|
|
5
|
+
on:
|
|
6
|
+
push:
|
|
7
|
+
tags: ["v*"]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
build:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
- uses: astral-sh/setup-uv@v6
|
|
15
|
+
- name: the tag is the version of the package
|
|
16
|
+
run: |
|
|
17
|
+
version=$(python3 -c "import tomllib;print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")
|
|
18
|
+
test "v$version" = "${GITHUB_REF_NAME}" || { echo "tag ${GITHUB_REF_NAME} does not match version $version"; exit 1; }
|
|
19
|
+
- run: uv sync --group dev
|
|
20
|
+
- run: uv run ruff check .
|
|
21
|
+
- run: uv run pytest -q
|
|
22
|
+
- run: uv build
|
|
23
|
+
- uses: actions/upload-artifact@v4
|
|
24
|
+
with:
|
|
25
|
+
name: dist
|
|
26
|
+
path: dist/
|
|
27
|
+
|
|
28
|
+
github-release:
|
|
29
|
+
needs: build
|
|
30
|
+
runs-on: ubuntu-latest
|
|
31
|
+
permissions:
|
|
32
|
+
contents: write
|
|
33
|
+
steps:
|
|
34
|
+
- uses: actions/download-artifact@v4
|
|
35
|
+
with:
|
|
36
|
+
name: dist
|
|
37
|
+
path: dist/
|
|
38
|
+
- uses: softprops/action-gh-release@v2
|
|
39
|
+
with:
|
|
40
|
+
files: dist/*
|
|
41
|
+
generate_release_notes: true
|
|
42
|
+
|
|
43
|
+
pypi:
|
|
44
|
+
needs: build
|
|
45
|
+
runs-on: ubuntu-latest
|
|
46
|
+
environment: pypi
|
|
47
|
+
permissions:
|
|
48
|
+
id-token: write
|
|
49
|
+
steps:
|
|
50
|
+
- uses: actions/download-artifact@v4
|
|
51
|
+
with:
|
|
52
|
+
name: dist
|
|
53
|
+
path: dist/
|
|
54
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.12
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# AGENTS.md — coursekit (development)
|
|
2
|
+
|
|
3
|
+
Instructions for any coding agent (Claude Code, opencode, Codex) working on the coursekit package
|
|
4
|
+
itself. The instructions that coursekit generates for course projects live in
|
|
5
|
+
`src/coursekit/templates/project/AGENTS.md`, not here.
|
|
6
|
+
|
|
7
|
+
## Read first
|
|
8
|
+
|
|
9
|
+
- `dev/plan.md`: the plan, its phases and the state of each step (maintainers' notes, in Spanish; not user documentation).
|
|
10
|
+
- `CONTRIBUTING.md` (English) and `CONTRIBUTING.es.md` (Spanish): environment, tests, how to contribute; keep both in step.
|
|
11
|
+
|
|
12
|
+
## Public repository: nothing about clients or origin
|
|
13
|
+
|
|
14
|
+
- This repository is public. Never write the name of a client, a person, a tenant, an internal
|
|
15
|
+
URL, a course of a real project or any data of a real project: not in code, tests, fixtures,
|
|
16
|
+
docs, comments or commit messages.
|
|
17
|
+
- Never say or imply where coursekit comes from ("a previous pipeline", "the reference project",
|
|
18
|
+
"migrated from", "as before"…). Write the package, its docs and its history as a standalone
|
|
19
|
+
product.
|
|
20
|
+
- Examples and test fixtures are generic and invented (e.g. a course on strong passwords, client
|
|
21
|
+
"ACME").
|
|
22
|
+
|
|
23
|
+
## Languages
|
|
24
|
+
|
|
25
|
+
- Code, identifiers, comments, file names, YAML keys, skills, commands and agents: **English**.
|
|
26
|
+
- CLI messages (output, errors, `--help`) speak the language of the person (`es` or `en`: `COURSEKIT_LANG`
|
|
27
|
+
in the `.env`, else `project.yaml › ui_language`, else the system's). Never write one in Python: add it
|
|
28
|
+
to `src/coursekit/lang/messages/<module>.yaml` with its `es` and `en` text and use
|
|
29
|
+
`t("<module>", "<key>", value=...)` from `coursekit.i18n` (`tests/test_i18n.py` checks both languages
|
|
30
|
+
and the `{values}`). The tests run with `COURSEKIT_LANG=en`.
|
|
31
|
+
- `docs/` is the user documentation, in **English** (`docs/en/`) and **Spanish (Spain)** (`docs/es/`) with the
|
|
32
|
+
same files, and `README.md` / `README.es.md`. Commit messages and conversation with the maintainer:
|
|
33
|
+
**Spanish (Spain)**.
|
|
34
|
+
- Course content formats are per language (`src/coursekit/lang/es.yaml`, `en.yaml`); never
|
|
35
|
+
hard-code a content token (section heading, objective tag, placeholder) in Python. Resource types
|
|
36
|
+
and the keys and words of the directives (`question:`, `answer: true`...) have an English **id** in the
|
|
37
|
+
code, the configuration and the manifest (`image`, `infographic`...) and the word each language writes in
|
|
38
|
+
`media_types`, `directive_keys` and `directive_words` of its `lang/<code>.yaml`: a new type or key is added to
|
|
39
|
+
every language file (`tests/test_content_words.py` checks it). The defaults and their texts are in English.
|
|
40
|
+
|
|
41
|
+
## Working rules
|
|
42
|
+
|
|
43
|
+
- `uv sync --group dev`, then `uv run ruff check .` and `uv run pytest -q` before every commit;
|
|
44
|
+
CI runs both on Linux, macOS and Windows (paths in messages with `as_posix()`; read git output as
|
|
45
|
+
UTF-8).
|
|
46
|
+
- Production rules and numbers live in `src/coursekit/defaults/*.yaml` and reach skills as
|
|
47
|
+
`{{tokens}}` (`coursekit agents`): never write a number in a skill.
|
|
48
|
+
- Every rule a project can override lives in `src/coursekit/defaults/<name>.yaml`, commented. `coursekit init`
|
|
49
|
+
copies each file to the project as `config/<name>.example.yaml` (complete, with every value; not read;
|
|
50
|
+
`init --update` refreshes it), so a new rule or a changed default reaches the examples by itself: just keep
|
|
51
|
+
its comment up to date. A new configuration file needs its name in `config.NAMES` **and** `init.CONFIG_NAMES`
|
|
52
|
+
(`tests/test_init.py` fails otherwise), and its documentation in `docs/en` and `docs/es`.
|
|
53
|
+
- Documentation travels with the change: a new or changed command, option, process, configuration or rule is
|
|
54
|
+
documented in `docs/en` and `docs/es` (same files, same structure) in the same change.
|
|
55
|
+
`tests/test_docs.py` checks that both trees have the same files, that links resolve, and that
|
|
56
|
+
`docs/*/03-commands.md` mentions every command and every option of the CLI.
|
|
57
|
+
- Skills, commands and agents are edited only in `src/coursekit/agentkit/`; every `{{token}}` must
|
|
58
|
+
exist in `agents.context()` (a test fails otherwise).
|
|
59
|
+
- Do not commit or push unless the maintainer asks. Never force-push without explicit permission.
|
|
60
|
+
- No emojis anywhere (only `★ ✔ ✘ · — ‹ ›`).
|
|
61
|
+
- Do not install software on the maintainer's machine; say what to run.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@AGENTS.md
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# Contribuir a coursekit
|
|
2
|
+
|
|
3
|
+
English: [CONTRIBUTING.md](CONTRIBUTING.md)
|
|
4
|
+
|
|
5
|
+
## Bienvenida
|
|
6
|
+
|
|
7
|
+
coursekit (paquete `slxd-coursekit`, comando `coursekit`) es una herramienta de línea de comandos que guía la producción de cursos e-learning asistida por IA: brief, diseño instruccional, redacción, revisión, multimedia, ensamblado y entrega SCORM, con personas que firman en cada punto de control. Consulta el [README](README.es.md) para la visión general y la documentación de usuario ([English](docs/en/index.md), [Español](docs/es/index.md)) para ver cómo funciona desde el lado de quien lo usa.
|
|
8
|
+
|
|
9
|
+
Se agradecen contribuciones de cualquier tamaño: informes de errores, correcciones, documentación, traducciones y nuevas funciones. Esta guía explica cómo preparar el proyecto, qué debe pasar antes de dar un cambio por bueno y dónde está cada cosa.
|
|
10
|
+
|
|
11
|
+
El repositorio es público. No incluyas nunca el nombre de un cliente, de una persona, de un tenant, una URL interna ni datos de un proyecto real en código, tests, fixtures, documentación, comentarios o mensajes de commit. Los ejemplos y fixtures son genéricos e inventados (por ejemplo, un curso sobre contraseñas seguras para un cliente llamado "ACME").
|
|
12
|
+
|
|
13
|
+
## Preparar el entorno
|
|
14
|
+
|
|
15
|
+
Necesitas Python 3.12 o 3.13 (`requires-python = ">=3.12,<3.14"`; `.python-version` fija 3.12) y [uv](https://docs.astral.sh/uv/).
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
git clone https://github.com/studiolxd/coursekit
|
|
19
|
+
cd coursekit
|
|
20
|
+
uv sync --group dev
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
`uv sync --group dev` crea `.venv` e instala el paquete y las herramientas de desarrollo (`pytest`, `ruff`). Ejecuta coursekit desde el código fuente, sin instalarlo:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
uv run coursekit --version
|
|
27
|
+
uv run coursekit help
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Para probarlo como lo haría una persona usuaria, instálalo como herramienta (CI hace lo mismo con `uv tool install .`) y trabaja en una carpeta desechable fuera del repositorio:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
uv tool install --editable .
|
|
34
|
+
mkdir -p ~/tmp/coursekit-try && cd ~/tmp/coursekit-try
|
|
35
|
+
coursekit init demo --yes --no-git --client ACME
|
|
36
|
+
cd demo
|
|
37
|
+
coursekit status
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
`--editable` hace que el comando instalado siga tus cambios. Al terminar: `uv tool uninstall slxd-coursekit`. Si la terminal no encuentra `coursekit` tras instalarlo, ejecuta `uv tool update-shell` y abre una terminal nueva.
|
|
41
|
+
|
|
42
|
+
### Windows, macOS y Linux
|
|
43
|
+
|
|
44
|
+
CI se ejecuta en los tres (consulta `.github/workflows/ci.yml`), así que un cambio tiene que funcionar en los tres:
|
|
45
|
+
|
|
46
|
+
- Las rutas que se muestran en los mensajes pasan por `Path.as_posix()`, para que la salida sea igual en todas partes.
|
|
47
|
+
- Lee la salida de `git` (y de cualquier otra herramienta) como UTF-8.
|
|
48
|
+
- Los ficheros que genera coursekit se escriben en UTF-8 con saltos de línea `\n` (`write_text(..., encoding="utf-8", newline="\n")`).
|
|
49
|
+
- Construye las rutas con `pathlib`, no concatenando cadenas con `/` o `\`.
|
|
50
|
+
|
|
51
|
+
## Comprobaciones antes de cada cambio
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
uv run ruff check .
|
|
55
|
+
uv run pytest -q
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Ambas deben pasar; CI ejecuta las dos (más `uv tool install .` y `coursekit --version`) en Linux, macOS y Windows con cada push a `main` y cada pull request. Ruff se configura en `pyproject.toml`: longitud de línea 140, Python 3.12 como objetivo y las reglas `E`, `F`, `I` (imports), `UP` y `B`. `uv run ruff check . --fix` corrige la mayoría de los avisos de orden de imports y de estilo.
|
|
59
|
+
|
|
60
|
+
`tests/conftest.py` fija `COURSEKIT_LANG=en` en todos los tests y restaura el entorno después, así que los mensajes en los tests están en inglés. Un test que necesite español lo indica de forma explícita (`monkeypatch.setenv("COURSEKIT_LANG", "es")` o `i18n.use("es")`).
|
|
61
|
+
|
|
62
|
+
Además de los tests de comportamiento (`test_cli.py`, `test_course_flow.py`, `test_assemble.py`, `test_delivery.py`, `test_media.py`, `test_launch.py`, `test_setup.py`, `test_config.py`, ...), algunos tests vigilan las convenciones del proyecto y fallan si un cambio olvida una parte:
|
|
63
|
+
|
|
64
|
+
| Test | Qué comprueba |
|
|
65
|
+
|------|---------------|
|
|
66
|
+
| `tests/test_i18n.py` | Cada mensaje de `src/coursekit/lang/messages/*.yaml` existe en `es` y `en` con los mismos `{valores}`; cada `t("módulo", "clave")` usado en el código existe en su catálogo y recibe los valores que necesita; no hay emojis en los catálogos. |
|
|
67
|
+
| `tests/test_docs.py` | `docs/en` y `docs/es` tienen los mismos ficheros y la misma estructura de encabezados; los enlaces se resuelven; no hay emojis; `docs/*/03-commands.md` menciona cada comando y cada opción de la CLI; los dos README se enlazan entre sí. |
|
|
68
|
+
| `tests/test_init.py` | `init.CONFIG_NAMES` es igual a `config.NAMES`; `config/<nombre>.example.yaml` es el valor por defecto completo del paquete para cada configuración. |
|
|
69
|
+
| `tests/test_agents.py` | Cada skill, comando y agente se genera para cada herramienta, sin ningún `{{token}}` sin sustituir. |
|
|
70
|
+
|
|
71
|
+
## Mapa del repositorio
|
|
72
|
+
|
|
73
|
+
| Ruta | Qué hay |
|
|
74
|
+
|------|---------|
|
|
75
|
+
| `src/coursekit/cli.py` | Punto de entrada (`coursekit = "coursekit.cli:main"`): el parser, `init`, `config`, `rules`, `agents` y la gestión de errores de nivel superior. |
|
|
76
|
+
| `src/coursekit/commands.py` | El resto de comandos: `register(sub)` añade cada subcomando y sus opciones; las funciones `cmd_*` llaman a los módulos de abajo. |
|
|
77
|
+
| `src/coursekit/project.py`, `envfile.py`, `config.py` | Localizar el proyecto, cargar `.env` y la configuración por capas (valores del paquete, `config/`, `project.yaml`, `course.yaml`). |
|
|
78
|
+
| `src/coursekit/init.py`, `setup.py`, `doctor.py` | `coursekit init` (estructura del proyecto y ficheros que mantiene al día), `setup` (herramientas de la máquina) y `doctor` (diagnóstico). |
|
|
79
|
+
| `src/coursekit/course.py`, `new.py`, `sync.py`, `outline.py`, `status.py`, `states.py` | El modelo del curso, crear un curso, sincronizar el diseño aprobado con el esqueleto del contenido, leer el diseño y el estado del curso. |
|
|
80
|
+
| `src/coursekit/verify.py`, `approve.py`, `fingerprint.py`, `identity.py` | Comprobar el contenido contra las reglas de producción, las firmas y las huellas que detectan cambios posteriores. |
|
|
81
|
+
| `src/coursekit/agents.py`, `launch.py` | Generar skills, comandos y agentes para cada herramienta de IA (`context()` guarda los `{{tokens}}`) y lanzar las herramientas. |
|
|
82
|
+
| `src/coursekit/brief.py`, `media.py`, `mediatools.py`, `voices.py`, `theme.py` | Conversión del brief, planificación y producción multimedia, voces y subtítulos, tokens del theme. |
|
|
83
|
+
| `src/coursekit/assemble.py`, `directives.py`, `delivery.py`, `publish.py`, `catalog.py` | Ensamblado en la plataforma de autoría, directivas, entrega SCORM, publicación y catálogo de cursos. |
|
|
84
|
+
| `src/coursekit/i18n.py` | Mensajes para personas: `t("módulo", "clave", valor=...)` y la elección del idioma. |
|
|
85
|
+
| `src/coursekit/lang/` | `es.yaml` y `en.yaml`: tokens del formato de contenido del curso por idioma. `lang/messages/<módulo>.yaml`: los catálogos de mensajes de la CLI (`es` y `en`). |
|
|
86
|
+
| `src/coursekit/defaults/` | Valores por defecto del paquete, comentados: `rules.yaml`, `directives.yaml`, `media.yaml`, `delivery.yaml` (un proyecto puede sobrescribirlos) y `agents.yaml` (herramienta y modelo por defecto de cada rol). |
|
|
87
|
+
| `src/coursekit/agentkit/` | Origen de las skills, comandos, agentes y documentos de referencia que se generan en los proyectos de curso. |
|
|
88
|
+
| `src/coursekit/templates/` | Ficheros que se copian en proyectos y cursos (`project/`, `course/`, `brief/`, `remotion/`). |
|
|
89
|
+
| `src/coursekit/tools/` | Scripts auxiliares que se distribuyen con el paquete (`web2md.js`). |
|
|
90
|
+
| `tests/` | La batería de tests; `tests/fixtures/` contiene datos inventados. |
|
|
91
|
+
| `docs/en/`, `docs/es/` | Documentación de usuario en inglés y en español (España), con los mismos ficheros en ambos idiomas. |
|
|
92
|
+
|
|
93
|
+
## Convenciones
|
|
94
|
+
|
|
95
|
+
- **Idiomas.** El código, los identificadores, los comentarios, los nombres de fichero, las claves YAML, las skills, los comandos y los agentes están en inglés. Los mensajes para personas (salida, errores, `--help`) no se escriben nunca en Python: viven en los catálogos, en español e inglés. La documentación se escribe en ambos idiomas. Los mensajes de commit se escriben en español (España), como en el historial existente.
|
|
96
|
+
- **Tokens de contenido.** Las palabras del formato de contenido del curso (encabezado de apartado, etiqueta de objetivo, marcador de recurso, ...) pertenecen a `src/coursekit/lang/<código>.yaml`. No las escribas fijas en Python.
|
|
97
|
+
- **Sin números en las skills.** Las reglas y cifras de producción viven en `src/coursekit/defaults/*.yaml` y llegan a las skills como `{{tokens}}`.
|
|
98
|
+
- **Sin emojis** en ningún sitio (código, documentación, mensajes, commits). Los únicos símbolos permitidos son `★ ✔ ✘ · — ‹ ›`.
|
|
99
|
+
- **Comentarios.** Explican el porqué, no el qué; mantenlos breves, en inglés, y actualízalos junto con el código. Los valores comentados de `src/coursekit/defaults/` hacen también de documentación de cada regla para quien usa el paquete.
|
|
100
|
+
- **Ejemplos y fixtures** genéricos e inventados. No copies nunca material de un proyecto real, ni siquiera en parte.
|
|
101
|
+
- **Producto independiente.** Escribe el código, la documentación y el historial como un producto con entidad propia; no describas de dónde viene coursekit ni ninguna de sus partes.
|
|
102
|
+
|
|
103
|
+
## Cómo hacer
|
|
104
|
+
|
|
105
|
+
### Añadir un comando o una opción
|
|
106
|
+
|
|
107
|
+
1. Pon la lógica en el módulo que corresponda (o en uno nuevo) y añade una función `cmd_<nombre>(args)` en `src/coursekit/commands.py`. Los comandos sobre el propio proyecto (`init`, `config`, `rules`, `agents`) están en `cli.py`.
|
|
108
|
+
2. Registra el subcomando y sus opciones en `register(sub)` (`commands.py`) o en `build_parser()` (`cli.py`). Los textos de ayuda también son mensajes: `help=t("commands", "help_<nombre>")`.
|
|
109
|
+
3. Para los errores que la persona puede corregir, lanza la excepción del módulo y asegúrate de que `cli.main` la captura (las excepciones de `commands.ERRORS`): escribe `coursekit: <mensaje>` en stderr y sale con código 1.
|
|
110
|
+
4. Añade los mensajes (más abajo) y un test; los tests de la CLI llaman a `main([...])` de `coursekit.cli` y leen la salida con `capsys`.
|
|
111
|
+
5. Documéntalo en `docs/en/03-commands.md` y `docs/es/03-commands.md`: una sección `### \`coursekit <nombre>\``, la entrada en las tablas de comandos y cada cadena de opción. `tests/test_docs.py` falla si falta un comando o una opción en alguno de los dos ficheros.
|
|
112
|
+
|
|
113
|
+
### Añadir o cambiar un mensaje
|
|
114
|
+
|
|
115
|
+
Los mensajes viven en `src/coursekit/lang/messages/<módulo>.yaml`, un catálogo por módulo, cada clave con los dos idiomas:
|
|
116
|
+
|
|
117
|
+
```yaml
|
|
118
|
+
created_env:
|
|
119
|
+
es: "ok: .env creado"
|
|
120
|
+
en: "ok: created .env"
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Se usa como `t("setup", "created_env")`; con valores, `t("módulo", "clave", path=...)` y `{path}` en ambos textos (una llave literal se escribe `{{`). Los dos textos deben usar los mismos `{valores}`; lo comprueba `tests/test_i18n.py`, que comprueba también que cada clave usada en el código existe. Un módulo nuevo es un fichero `<módulo>.yaml` nuevo en esa carpeta.
|
|
124
|
+
|
|
125
|
+
### Añadir o cambiar una regla de producción o un fichero de configuración
|
|
126
|
+
|
|
127
|
+
Una regla que un proyecto puede sobrescribir vive en `src/coursekit/defaults/<nombre>.yaml`, con un comentario que diga qué hace. `coursekit init` copia cada uno de esos ficheros al proyecto como `config/<nombre>.example.yaml` (completo, coursekit no lo lee; `init --update` lo refresca), de modo que una regla nueva o un valor por defecto cambiado llegan solos a los ejemplos: basta con mantener al día su comentario.
|
|
128
|
+
|
|
129
|
+
- Una regla nueva dentro de un fichero existente: añade la clave con su valor por defecto y su comentario, úsala desde el código a través de `config` y documéntala en `docs/en/04-configuration.md` y `docs/es/04-configuration.md`. Si una skill necesita el valor, añade un token a `agents.context()` (ver más abajo).
|
|
130
|
+
- Un fichero de configuración nuevo: crea `defaults/<nombre>.yaml` y añade su nombre a **ambos** `config.NAMES` (`src/coursekit/config.py`) e `init.CONFIG_NAMES` (`src/coursekit/init.py`); `tests/test_init.py` falla si no. Documéntalo en las dos páginas de configuración y en `03-commands.md` si `coursekit config` debe mencionarlo.
|
|
131
|
+
|
|
132
|
+
### Editar skills, comandos y agentes
|
|
133
|
+
|
|
134
|
+
Edítalos solo en `src/coursekit/agentkit/` (`skills/<nombre>/SKILL.md`, `commands/<nombre>.md`, `agents/<nombre>.md`, `docs/`). No edites nunca las copias generadas dentro de un proyecto de curso. Cada `{{token}}` que uses debe existir en `agents.context()` de `src/coursekit/agents.py`; un token desconocido lanza un error y `tests/test_agents.py` falla. Los parciales empiezan por `_` y se incluyen con `{{> _nombre}}`. Para ver el resultado, ejecuta `coursekit agents` en un proyecto desechable y lee los ficheros generados.
|
|
135
|
+
|
|
136
|
+
### Añadir un idioma de contenido
|
|
137
|
+
|
|
138
|
+
El formato de contenido es por idioma. Como mínimo:
|
|
139
|
+
|
|
140
|
+
- `src/coursekit/lang/<código>.yaml` con todos los tokens (copia `en.yaml`), incluidos `media_types` (la palabra de cada id de tipo de recurso), `directive_keys` y `directive_words`, y el código añadido a `LANGUAGES` en `src/coursekit/lang/__init__.py`. Un tipo de recurso o una clave de directiva nuevos reciben un id y una palabra en cada fichero de idioma (`tests/test_content_words.py` lo comprueba).
|
|
141
|
+
- El código en `init.LANGUAGES` y sus formas de tratamiento en `init.ADDRESS`; su nombre en `agents.LANGUAGE_NAMES`; el tratamiento por defecto en `agents.context()`.
|
|
142
|
+
- Una plantilla de curso: `src/coursekit/templates/course/<código>/` (`content.md`, `assessment.md`).
|
|
143
|
+
- Valores por defecto que dependen del idioma: `catalog.CATALOG_NAME` y los textos de sus columnas, `setup.PIPER_DEFAULT`, `voices.LOCALES` y las voces por defecto de `voices.py`.
|
|
144
|
+
- Documentación en `docs/en` y `docs/es`, y tests.
|
|
145
|
+
|
|
146
|
+
El idioma de la interfaz (`ui_language`, `COURSEKIT_LANG`) es independiente: es `es` o `en` (`i18n.LANGUAGES`). Añadir uno supone añadir su texto a cada entrada de cada catálogo, y `tests/test_i18n.py` las exige todas.
|
|
147
|
+
|
|
148
|
+
### Añadir documentación
|
|
149
|
+
|
|
150
|
+
La documentación de usuario vive en `docs/en/` y `docs/es/`, con los mismos nombres de fichero y la misma estructura de encabezados (número y nivel). Un comando, opción, proceso, configuración o regla nuevos o modificados se documentan en los dos idiomas en el mismo cambio. Los enlaces son relativos y deben resolverse; los bloques de código no se comprueban, así que mantén exactos los comandos que contienen. Si añades una página, añádela a los dos árboles y enlázala desde `index.md`.
|
|
151
|
+
|
|
152
|
+
## Pull requests y commits
|
|
153
|
+
|
|
154
|
+
- Mantén los cambios pequeños y centrados: una corrección o función por pull request, sin reformateos ajenos al cambio.
|
|
155
|
+
- Código, tests y documentación van juntos. Un pull request que cambia el comportamiento incluye el test que lo cubre y las actualizaciones de `docs/en` y `docs/es`.
|
|
156
|
+
- Ejecuta antes `uv run ruff check .` y `uv run pytest -q`. Comprueba que la salida se lee bien en los dos idiomas en todo lo que ve una persona (`COURSEKIT_LANG=es` y `COURSEKIT_LANG=en`).
|
|
157
|
+
- Los mensajes de commit se escriben en español (España): una primera línea breve que diga qué cambia y, si hace falta, los detalles después. Ejemplo: `Rutas con barras normales en los mensajes de error`.
|
|
158
|
+
- No hagas force-push en ramas compartidas ni reescribas historial que otras personas ya hayan descargado.
|
|
159
|
+
- No subas ficheros generados o locales: `.venv/`, `dist/`, `build/`, cachés ni ningún `.env`.
|
|
160
|
+
|
|
161
|
+
## Versiones
|
|
162
|
+
|
|
163
|
+
Una etiqueta `vX.Y.Z` que coincida con la versión de `pyproject.toml` ejecuta `.github/workflows/release.yml`: comprueba, construye, adjunta el wheel y la distribución de código fuente a una release de GitHub y sube a PyPI (publicación de confianza: el entorno `pypi` del repositorio es el publicador registrado en PyPI). Para publicar: sube `version`, commit, `git tag vX.Y.Z`, `git push --tags`. Los proyectos siguen la versión instalada por sí solos (el primer comando tras actualizar regenera sus ficheros gestionados y los de los agentes). La versión es el campo `version` de `pyproject.toml` (ahora `0.1.2`); `coursekit --version` la lee de los metadatos del paquete instalado y muestra `0.0.0` si se ejecuta desde un checkout sin instalar. El paquete se construye con hatchling:
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
uv build
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
que escribe la distribución de código fuente y el wheel en `dist/` (ignorado por git). Los cambios de versión y las publicaciones los decide el equipo mantenedor.
|
|
170
|
+
|
|
171
|
+
## Informar de problemas
|
|
172
|
+
|
|
173
|
+
Abre una issue en el [repositorio](https://github.com/studiolxd/coursekit). Incluye:
|
|
174
|
+
|
|
175
|
+
- La salida de `coursekit --version` y cómo lo instalaste (`uv tool install`, `uv run` desde un checkout).
|
|
176
|
+
- Tu sistema operativo y su versión, y la versión de Python si ejecutas desde el código fuente.
|
|
177
|
+
- El comando exacto y su salida completa (añade `COURSEKIT_LANG=en` para obtener los mensajes en inglés), y la salida de `coursekit doctor` si el problema tiene que ver con las herramientas de la máquina.
|
|
178
|
+
- Qué esperabas y qué ocurrió, y los pasos mínimos para reproducirlo, a ser posible en un proyecto nuevo creado con `coursekit init`.
|
|
179
|
+
|
|
180
|
+
No pegues secretos ni material de cursos reales: elimina claves de API, tokens, el contenido de `.env`, URLs de sistemas internos y cualquier texto del curso de un cliente. Sustitúyelos por ejemplos inventados.
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# Contributing to coursekit
|
|
2
|
+
|
|
3
|
+
Español: [CONTRIBUTING.es.md](CONTRIBUTING.es.md)
|
|
4
|
+
|
|
5
|
+
## Welcome
|
|
6
|
+
|
|
7
|
+
coursekit (package `slxd-coursekit`, command `coursekit`) is a command-line tool that drives the production of AI-assisted e-learning courses: brief, instructional design, writing, review, media, assembly and SCORM delivery, with people signing off at every control point. See the [README](README.md) for the overview and the user documentation ([English](docs/en/index.md), [Español](docs/es/index.md)) for how it works from the user's side.
|
|
8
|
+
|
|
9
|
+
Contributions of any size are welcome: bug reports, fixes, documentation, translations and new features. This guide explains how to set up the project, what must pass before a change is ready and where things live.
|
|
10
|
+
|
|
11
|
+
The repository is public. Never include the name of a client, a person, a tenant, an internal URL or any data of a real project in code, tests, fixtures, docs, comments or commit messages. Examples and fixtures are generic and invented (for instance, a course on strong passwords for a client called "ACME").
|
|
12
|
+
|
|
13
|
+
## Set up the environment
|
|
14
|
+
|
|
15
|
+
You need Python 3.12 or 3.13 (`requires-python = ">=3.12,<3.14"`; `.python-version` pins 3.12) and [uv](https://docs.astral.sh/uv/).
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
git clone https://github.com/studiolxd/coursekit
|
|
19
|
+
cd coursekit
|
|
20
|
+
uv sync --group dev
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
`uv sync --group dev` creates `.venv` and installs the package and the development tools (`pytest`, `ruff`). Run coursekit from the source tree, without installing it:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
uv run coursekit --version
|
|
27
|
+
uv run coursekit help
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
To try it the way a user would, install it as a tool (CI does the same with `uv tool install .`) and work in a throwaway folder outside the repository:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
uv tool install --editable .
|
|
34
|
+
mkdir -p ~/tmp/coursekit-try && cd ~/tmp/coursekit-try
|
|
35
|
+
coursekit init demo --yes --no-git --client ACME
|
|
36
|
+
cd demo
|
|
37
|
+
coursekit status
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
`--editable` makes the installed command follow your changes. When you are done: `uv tool uninstall slxd-coursekit`. If the shell does not find `coursekit` after installing, run `uv tool update-shell` and open a new terminal.
|
|
41
|
+
|
|
42
|
+
### Windows, macOS and Linux
|
|
43
|
+
|
|
44
|
+
CI runs on all three (see `.github/workflows/ci.yml`), so a change must work on all three:
|
|
45
|
+
|
|
46
|
+
- Paths shown in messages go through `Path.as_posix()`, so the output is the same everywhere.
|
|
47
|
+
- Read the output of `git` (and any other tool) as UTF-8.
|
|
48
|
+
- Files coursekit generates are written as UTF-8 with `\n` line endings (`write_text(..., encoding="utf-8", newline="\n")`).
|
|
49
|
+
- Build paths with `pathlib`, not by concatenating strings with `/` or `\`.
|
|
50
|
+
|
|
51
|
+
## Checks before every change
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
uv run ruff check .
|
|
55
|
+
uv run pytest -q
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Both must pass; CI runs both (plus `uv tool install .` and `coursekit --version`) on Linux, macOS and Windows for every push to `main` and every pull request. Ruff is configured in `pyproject.toml`: line length 140, target Python 3.12, rules `E`, `F`, `I` (imports), `UP` and `B`. `uv run ruff check . --fix` fixes most import-order and style findings.
|
|
59
|
+
|
|
60
|
+
`tests/conftest.py` sets `COURSEKIT_LANG=en` for every test and restores the environment afterwards, so messages in tests are in English. A test that needs Spanish says so explicitly (`monkeypatch.setenv("COURSEKIT_LANG", "es")` or `i18n.use("es")`).
|
|
61
|
+
|
|
62
|
+
Besides the behaviour tests (`test_cli.py`, `test_course_flow.py`, `test_assemble.py`, `test_delivery.py`, `test_media.py`, `test_launch.py`, `test_setup.py`, `test_config.py`, ...), some tests guard the project's conventions and will fail if a change forgets a part:
|
|
63
|
+
|
|
64
|
+
| Test | What it checks |
|
|
65
|
+
|------|----------------|
|
|
66
|
+
| `tests/test_i18n.py` | Every message of `src/coursekit/lang/messages/*.yaml` exists in `es` and `en` with the same `{values}`; every `t("module", "key")` used in the code exists in its catalog and receives the values it needs; no emojis in the catalogs. |
|
|
67
|
+
| `tests/test_docs.py` | `docs/en` and `docs/es` have the same files and the same heading structure; links resolve; no emojis; `docs/*/03-commands.md` mentions every command and every option of the CLI; the two READMEs point to each other. |
|
|
68
|
+
| `tests/test_init.py` | `init.CONFIG_NAMES` equals `config.NAMES`; `config/<name>.example.yaml` is the complete package default of each configuration. |
|
|
69
|
+
| `tests/test_agents.py` | Every skill, command and agent is generated for every tool, with no `{{token}}` left unrendered. |
|
|
70
|
+
|
|
71
|
+
## Repository map
|
|
72
|
+
|
|
73
|
+
| Path | What is there |
|
|
74
|
+
|------|---------------|
|
|
75
|
+
| `src/coursekit/cli.py` | Entry point (`coursekit = "coursekit.cli:main"`): the parser, `init`, `config`, `rules`, `agents` and the top-level error handling. |
|
|
76
|
+
| `src/coursekit/commands.py` | The rest of the commands: `register(sub)` adds each sub-command and its options; `cmd_*` functions call the modules below. |
|
|
77
|
+
| `src/coursekit/project.py`, `envfile.py`, `config.py` | Finding the project, loading `.env`, and the layered configuration (package defaults, `config/`, `project.yaml`, `course.yaml`). |
|
|
78
|
+
| `src/coursekit/init.py`, `setup.py`, `doctor.py` | `coursekit init` (project scaffolding and the files it keeps up to date), `setup` (machine tools) and `doctor` (diagnosis). |
|
|
79
|
+
| `src/coursekit/course.py`, `new.py`, `sync.py`, `outline.py`, `status.py`, `states.py` | The course model, creating a course, syncing the approved design into the content skeleton, reading the design and the course status. |
|
|
80
|
+
| `src/coursekit/verify.py`, `approve.py`, `fingerprint.py`, `identity.py` | Checking content against the production rules, sign-offs and the fingerprints that detect later changes. |
|
|
81
|
+
| `src/coursekit/agents.py`, `launch.py` | Rendering skills, commands and agents for each AI tool (`context()` holds the `{{tokens}}`), and launching the tools. |
|
|
82
|
+
| `src/coursekit/brief.py`, `media.py`, `mediatools.py`, `voices.py`, `theme.py` | Brief conversion, media planning and production, voices and subtitles, theme tokens. |
|
|
83
|
+
| `src/coursekit/assemble.py`, `directives.py`, `delivery.py`, `publish.py`, `catalog.py` | Assembly into the authoring platform, directives, SCORM delivery, publishing and the course catalog. |
|
|
84
|
+
| `src/coursekit/i18n.py` | Messages for people: `t("module", "key", value=...)` and the choice of language. |
|
|
85
|
+
| `src/coursekit/lang/` | `es.yaml` and `en.yaml`: tokens of the course content format per language. `lang/messages/<module>.yaml`: the CLI message catalogs (`es` and `en`). |
|
|
86
|
+
| `src/coursekit/defaults/` | Package defaults, commented: `rules.yaml`, `directives.yaml`, `media.yaml`, `delivery.yaml` (overridable by a project) and `agents.yaml` (default tool and model per role). |
|
|
87
|
+
| `src/coursekit/agentkit/` | Source of the skills, commands, agents and reference docs generated into course projects. |
|
|
88
|
+
| `src/coursekit/templates/` | Files copied into projects and courses (`project/`, `course/`, `brief/`, `remotion/`). |
|
|
89
|
+
| `src/coursekit/tools/` | Helper scripts shipped with the package (`web2md.js`). |
|
|
90
|
+
| `tests/` | The test suite; `tests/fixtures/` holds invented data. |
|
|
91
|
+
| `docs/en/`, `docs/es/` | User documentation in English and Spanish (Spain), same files in both. |
|
|
92
|
+
|
|
93
|
+
## Conventions
|
|
94
|
+
|
|
95
|
+
- **Languages.** Code, identifiers, comments, file names, YAML keys, skills, commands and agents are in English. Messages for people (output, errors, `--help`) are never written in Python: they live in the catalogs, in Spanish and English. Documentation is written in both languages. Commit messages are written in Spanish (Spain), as in the existing history.
|
|
96
|
+
- **Content tokens.** The words of the course content format (section heading, objective tag, placeholder, ...) belong to `src/coursekit/lang/<code>.yaml`. Never hard-code one in Python.
|
|
97
|
+
- **No numbers in skills.** Production rules and numbers live in `src/coursekit/defaults/*.yaml` and reach skills as `{{tokens}}`.
|
|
98
|
+
- **No emojis** anywhere (code, docs, messages, commit messages). The only symbols allowed are `★ ✔ ✘ · — ‹ ›`.
|
|
99
|
+
- **Comments** explain why, not what; keep them short, in English, and update them with the code. The commented defaults in `src/coursekit/defaults/` double as user-facing documentation of each rule.
|
|
100
|
+
- **Examples and fixtures** are generic and invented. Never copy material from a real project, not even partially.
|
|
101
|
+
- **Standalone product.** Write code, docs and history as a product on its own; do not describe where coursekit or any of its parts come from.
|
|
102
|
+
|
|
103
|
+
## How to
|
|
104
|
+
|
|
105
|
+
### Add a command or an option
|
|
106
|
+
|
|
107
|
+
1. Put the logic in the module it belongs to (or a new module) and add a `cmd_<name>(args)` function in `src/coursekit/commands.py`. Commands about the project itself (`init`, `config`, `rules`, `agents`) are in `cli.py`.
|
|
108
|
+
2. Register the sub-command and its options in `register(sub)` (`commands.py`) or `build_parser()` (`cli.py`). Help texts are messages too: `help=t("commands", "help_<name>")`.
|
|
109
|
+
3. For errors the person can fix, raise the module's exception and make sure it is caught in `cli.main` (the exceptions listed in `commands.ERRORS`): it prints `coursekit: <message>` to stderr and exits with 1.
|
|
110
|
+
4. Add the messages (below) and a test; the CLI tests call `main([...])` from `coursekit.cli` and read output with `capsys`.
|
|
111
|
+
5. Document it in `docs/en/03-commands.md` and `docs/es/03-commands.md`: a `### \`coursekit <name>\`` section, the entry in the command tables and every option string. `tests/test_docs.py` fails if a command or an option is missing from either file.
|
|
112
|
+
|
|
113
|
+
### Add or change a message
|
|
114
|
+
|
|
115
|
+
Messages live in `src/coursekit/lang/messages/<module>.yaml`, one catalog per module, each key with both languages:
|
|
116
|
+
|
|
117
|
+
```yaml
|
|
118
|
+
created_env:
|
|
119
|
+
es: "ok: .env creado"
|
|
120
|
+
en: "ok: created .env"
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Use it as `t("setup", "created_env")`; with values, `t("module", "key", path=...)` and `{path}` in both texts (a literal brace is written `{{`). Both texts must use the same `{values}`; `tests/test_i18n.py` checks it, and also that every key used in the code exists. A new module is a new `<module>.yaml` file in that folder.
|
|
124
|
+
|
|
125
|
+
### Add or change a production rule or a configuration file
|
|
126
|
+
|
|
127
|
+
A rule a project can override lives in `src/coursekit/defaults/<name>.yaml`, with a comment saying what it does. `coursekit init` copies each of those files to the project as `config/<name>.example.yaml` (complete, not read by coursekit; `init --update` refreshes it), so a new rule or a changed default reaches the examples by itself: just keep its comment up to date.
|
|
128
|
+
|
|
129
|
+
- A new rule inside an existing file: add the key with its default and comment, use it from the code through `config`, and document it in `docs/en/04-configuration.md` and `docs/es/04-configuration.md`. If a skill needs the value, add a token to `agents.context()` (see below).
|
|
130
|
+
- A new configuration file: create `defaults/<name>.yaml` and add its name to **both** `config.NAMES` (`src/coursekit/config.py`) and `init.CONFIG_NAMES` (`src/coursekit/init.py`); `tests/test_init.py` fails otherwise. Document it in both configuration pages and in `03-commands.md` if `coursekit config` should mention it.
|
|
131
|
+
|
|
132
|
+
### Edit skills, commands and agents
|
|
133
|
+
|
|
134
|
+
Edit them only in `src/coursekit/agentkit/` (`skills/<name>/SKILL.md`, `commands/<name>.md`, `agents/<name>.md`, `docs/`). Never edit the generated copies inside a course project. Every `{{token}}` you use must exist in `agents.context()` in `src/coursekit/agents.py`; an unknown token raises an error and `tests/test_agents.py` fails. Partials start with `_` and are included with `{{> _name}}`. To see the result, run `coursekit agents` in a throwaway project and read the generated files.
|
|
135
|
+
|
|
136
|
+
### Add a content language
|
|
137
|
+
|
|
138
|
+
The content format is per language. At least:
|
|
139
|
+
|
|
140
|
+
- `src/coursekit/lang/<code>.yaml` with all the tokens (copy `en.yaml`), including `media_types` (the word of each resource type id), `directive_keys` and `directive_words`, and the code added to `LANGUAGES` in `src/coursekit/lang/__init__.py`. A new resource type or directive key gets an id and a word in every language file (`tests/test_content_words.py` checks it).
|
|
141
|
+
- The code in `init.LANGUAGES` and its address forms in `init.ADDRESS`; its name in `agents.LANGUAGE_NAMES`; the default address in `agents.context()`.
|
|
142
|
+
- A course template: `src/coursekit/templates/course/<code>/` (`content.md`, `assessment.md`).
|
|
143
|
+
- Language-dependent defaults: `catalog.CATALOG_NAME` and its column texts, `setup.PIPER_DEFAULT`, `voices.LOCALES` and the default voices in `voices.py`.
|
|
144
|
+
- Documentation in both `docs/en` and `docs/es`, and tests.
|
|
145
|
+
|
|
146
|
+
The language of the interface (`ui_language`, `COURSEKIT_LANG`) is separate: it is `es` or `en` (`i18n.LANGUAGES`). Adding one means adding its text to every catalog entry, and `tests/test_i18n.py` requires all of them.
|
|
147
|
+
|
|
148
|
+
### Add documentation
|
|
149
|
+
|
|
150
|
+
User documentation lives in `docs/en/` and `docs/es/`, with the same file names and the same heading structure (numbers and levels). A new or changed command, option, process, configuration or rule is documented in both languages in the same change. Links are relative and must resolve; fenced code blocks are not checked, so keep commands in them accurate. If you add a page, add it to both trees and link it from `index.md`.
|
|
151
|
+
|
|
152
|
+
## Pull requests and commits
|
|
153
|
+
|
|
154
|
+
- Keep changes small and focused: one fix or feature per pull request, without unrelated reformatting.
|
|
155
|
+
- Code, tests and documentation travel together. A pull request that changes behaviour includes the test that covers it and the updates in `docs/en` and `docs/es`.
|
|
156
|
+
- Run `uv run ruff check .` and `uv run pytest -q` first. Check that the output reads well in both languages for anything a person sees (`COURSEKIT_LANG=es` and `COURSEKIT_LANG=en`).
|
|
157
|
+
- Commit messages are written in Spanish (Spain): a short first line saying what changes, then details if needed. Example: `Rutas con barras normales en los mensajes de error`.
|
|
158
|
+
- Do not force-push shared branches, and do not rewrite history that others have pulled.
|
|
159
|
+
- Do not commit generated or local files: `.venv/`, `dist/`, `build/`, caches and any `.env`.
|
|
160
|
+
|
|
161
|
+
## Releases
|
|
162
|
+
|
|
163
|
+
A tag `vX.Y.Z` that matches the version of `pyproject.toml` runs `.github/workflows/release.yml`: it checks, builds, attaches the wheel and the source distribution to a GitHub release and uploads to PyPI (trusted publishing: the `pypi` environment of the repository is the publisher registered on PyPI). To release: bump `version`, commit, `git tag vX.Y.Z`, `git push --tags`. Projects follow the installed version by themselves (the first command after an update regenerates their managed and agent files). The version is the `version` field in `pyproject.toml` (currently `0.1.2`); `coursekit --version` reads it from the installed package metadata and prints `0.0.0` when run from a checkout that is not installed. The package builds with hatchling:
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
uv build
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
which writes the source distribution and the wheel to `dist/` (ignored by git). Version bumps and releases are decided by the maintainers.
|
|
170
|
+
|
|
171
|
+
## Reporting problems
|
|
172
|
+
|
|
173
|
+
Open an issue in the [repository](https://github.com/studiolxd/coursekit). Include:
|
|
174
|
+
|
|
175
|
+
- The output of `coursekit --version`, and how you installed it (`uv tool install`, `uv run` from a checkout).
|
|
176
|
+
- Your operating system and version, and the Python version if you run from source.
|
|
177
|
+
- The exact command and its complete output (add `COURSEKIT_LANG=en` to get messages in English), plus the output of `coursekit doctor` when the problem is about the machine's tools.
|
|
178
|
+
- What you expected and what happened, and the smallest steps that reproduce it, ideally in a new project made with `coursekit init`.
|
|
179
|
+
|
|
180
|
+
Do not paste secrets or real course material: remove API keys, tokens, `.env` contents, URLs of internal systems and any text from a client's course. Replace them with invented examples.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Studio LXD
|
|
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.
|