learn-cli 0.5.0__tar.gz → 0.5.1__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.
Files changed (237) hide show
  1. {learn_cli-0.5.0 → learn_cli-0.5.1}/CHANGELOG.md +9 -0
  2. {learn_cli-0.5.0 → learn_cli-0.5.1}/PKG-INFO +1 -1
  3. {learn_cli-0.5.0 → learn_cli-0.5.1}/learn/profile/_ledger.py +26 -3
  4. {learn_cli-0.5.0 → learn_cli-0.5.1}/pyproject.toml +1 -1
  5. {learn_cli-0.5.0 → learn_cli-0.5.1}/tests/test_profile_store.py +17 -0
  6. {learn_cli-0.5.0 → learn_cli-0.5.1}/uv.lock +1 -1
  7. {learn_cli-0.5.0 → learn_cli-0.5.1}/workers/learn-api/src/index.js +8 -2
  8. {learn_cli-0.5.0 → learn_cli-0.5.1}/workers/learn-api/test/worker.test.js +44 -0
  9. {learn_cli-0.5.0 → learn_cli-0.5.1}/.claude/skills/agent-config/SKILL.md +0 -0
  10. {learn_cli-0.5.0 → learn_cli-0.5.1}/.claude/skills/agent-config/data/backend-fingerprints.yaml +0 -0
  11. {learn_cli-0.5.0 → learn_cli-0.5.1}/.claude/skills/agent-config/scripts/show.sh +0 -0
  12. {learn_cli-0.5.0 → learn_cli-0.5.1}/.claude/skills/ask-colleague/SKILL.md +0 -0
  13. {learn_cli-0.5.0 → learn_cli-0.5.1}/.claude/skills/ask-colleague/prompts/explore.md +0 -0
  14. {learn_cli-0.5.0 → learn_cli-0.5.1}/.claude/skills/ask-colleague/prompts/review.md +0 -0
  15. {learn_cli-0.5.0 → learn_cli-0.5.1}/.claude/skills/ask-colleague/prompts/write.md +0 -0
  16. {learn_cli-0.5.0 → learn_cli-0.5.1}/.claude/skills/ask-colleague/scripts/ask-colleague.sh +0 -0
  17. {learn_cli-0.5.0 → learn_cli-0.5.1}/.claude/skills/assign-to-workforce/SKILL.md +0 -0
  18. {learn_cli-0.5.0 → learn_cli-0.5.1}/.claude/skills/assign-to-workforce/scripts/assign-to-workforce.sh +0 -0
  19. {learn_cli-0.5.0 → learn_cli-0.5.1}/.claude/skills/cicd/SKILL.md +0 -0
  20. {learn_cli-0.5.0 → learn_cli-0.5.1}/.claude/skills/cicd/scripts/_resolve-nick.sh +0 -0
  21. {learn_cli-0.5.0 → learn_cli-0.5.1}/.claude/skills/cicd/scripts/portability-lint.sh +0 -0
  22. {learn_cli-0.5.0 → learn_cli-0.5.1}/.claude/skills/cicd/scripts/pr-reply.sh +0 -0
  23. {learn_cli-0.5.0 → learn_cli-0.5.1}/.claude/skills/cicd/scripts/pr-status.sh +0 -0
  24. {learn_cli-0.5.0 → learn_cli-0.5.1}/.claude/skills/cicd/scripts/workflow.sh +0 -0
  25. {learn_cli-0.5.0 → learn_cli-0.5.1}/.claude/skills/communicate/SKILL.md +0 -0
  26. {learn_cli-0.5.0 → learn_cli-0.5.1}/.claude/skills/communicate/scripts/fetch-issues.sh +0 -0
  27. {learn_cli-0.5.0 → learn_cli-0.5.1}/.claude/skills/communicate/scripts/mesh-message.sh +0 -0
  28. {learn_cli-0.5.0 → learn_cli-0.5.1}/.claude/skills/communicate/scripts/post-comment.sh +0 -0
  29. {learn_cli-0.5.0 → learn_cli-0.5.1}/.claude/skills/communicate/scripts/post-issue.sh +0 -0
  30. {learn_cli-0.5.0 → learn_cli-0.5.1}/.claude/skills/communicate/scripts/templates/skill-new-brief.md +0 -0
  31. {learn_cli-0.5.0 → learn_cli-0.5.1}/.claude/skills/communicate/scripts/templates/skill-update-brief.md +0 -0
  32. {learn_cli-0.5.0 → learn_cli-0.5.1}/.claude/skills/doc-test-alignment/SKILL.md +0 -0
  33. {learn_cli-0.5.0 → learn_cli-0.5.1}/.claude/skills/doc-test-alignment/scripts/check.sh +0 -0
  34. {learn_cli-0.5.0 → learn_cli-0.5.1}/.claude/skills/pypi-maintainer/SKILL.md +0 -0
  35. {learn_cli-0.5.0 → learn_cli-0.5.1}/.claude/skills/pypi-maintainer/scripts/switch-source.sh +0 -0
  36. {learn_cli-0.5.0 → learn_cli-0.5.1}/.claude/skills/recall/SKILL.md +0 -0
  37. {learn_cli-0.5.0 → learn_cli-0.5.1}/.claude/skills/recall/scripts/recall.sh +0 -0
  38. {learn_cli-0.5.0 → learn_cli-0.5.1}/.claude/skills/remember/SKILL.md +0 -0
  39. {learn_cli-0.5.0 → learn_cli-0.5.1}/.claude/skills/remember/scripts/remember.sh +0 -0
  40. {learn_cli-0.5.0 → learn_cli-0.5.1}/.claude/skills/run-tests/SKILL.md +0 -0
  41. {learn_cli-0.5.0 → learn_cli-0.5.1}/.claude/skills/run-tests/scripts/test.sh +0 -0
  42. {learn_cli-0.5.0 → learn_cli-0.5.1}/.claude/skills/sonarclaude/SKILL.md +0 -0
  43. {learn_cli-0.5.0 → learn_cli-0.5.1}/.claude/skills/sonarclaude/scripts/sonar.sh +0 -0
  44. {learn_cli-0.5.0 → learn_cli-0.5.1}/.claude/skills/spec-to-plan/SKILL.md +0 -0
  45. {learn_cli-0.5.0 → learn_cli-0.5.1}/.claude/skills/spec-to-plan/scripts/spec-to-plan.sh +0 -0
  46. {learn_cli-0.5.0 → learn_cli-0.5.1}/.claude/skills/think/SKILL.md +0 -0
  47. {learn_cli-0.5.0 → learn_cli-0.5.1}/.claude/skills/think/scripts/think.sh +0 -0
  48. {learn_cli-0.5.0 → learn_cli-0.5.1}/.claude/skills/version-bump/SKILL.md +0 -0
  49. {learn_cli-0.5.0 → learn_cli-0.5.1}/.claude/skills/version-bump/scripts/bump.py +0 -0
  50. {learn_cli-0.5.0 → learn_cli-0.5.1}/.claude/skills.local.yaml.example +0 -0
  51. {learn_cli-0.5.0 → learn_cli-0.5.1}/.devague/current +0 -0
  52. {learn_cli-0.5.0 → learn_cli-0.5.1}/.devague/current_plan +0 -0
  53. {learn_cli-0.5.0 → learn_cli-0.5.1}/.devague/frames/agentculture-org-learn-is-live-one-learning-hub-in.json +0 -0
  54. {learn_cli-0.5.0 → learn_cli-0.5.1}/.devague/plans/agentculture-org-learn-is-live-one-learning-hub-in.json +0 -0
  55. {learn_cli-0.5.0 → learn_cli-0.5.1}/.flake8 +0 -0
  56. {learn_cli-0.5.0 → learn_cli-0.5.1}/.github/workflows/deploy-site.yml +0 -0
  57. {learn_cli-0.5.0 → learn_cli-0.5.1}/.github/workflows/publish.yml +0 -0
  58. {learn_cli-0.5.0 → learn_cli-0.5.1}/.github/workflows/tests.yml +0 -0
  59. {learn_cli-0.5.0 → learn_cli-0.5.1}/.gitignore +0 -0
  60. {learn_cli-0.5.0 → learn_cli-0.5.1}/.markdownlint-cli2.yaml +0 -0
  61. {learn_cli-0.5.0 → learn_cli-0.5.1}/AGENTS.colleague.md +0 -0
  62. {learn_cli-0.5.0 → learn_cli-0.5.1}/CLAUDE.md +0 -0
  63. {learn_cli-0.5.0 → learn_cli-0.5.1}/LICENSE +0 -0
  64. {learn_cli-0.5.0 → learn_cli-0.5.1}/README.md +0 -0
  65. {learn_cli-0.5.0 → learn_cli-0.5.1}/culture.yaml +0 -0
  66. {learn_cli-0.5.0 → learn_cli-0.5.1}/docs/hosting.md +0 -0
  67. {learn_cli-0.5.0 → learn_cli-0.5.1}/docs/plans/.markdownlint-cli2.yaml +0 -0
  68. {learn_cli-0.5.0 → learn_cli-0.5.1}/docs/plans/2026-07-10-agentculture-org-learn-is-live-one-learning-hub-in.md +0 -0
  69. {learn_cli-0.5.0 → learn_cli-0.5.1}/docs/skill-sources.md +0 -0
  70. {learn_cli-0.5.0 → learn_cli-0.5.1}/docs/specs/.markdownlint-cli2.yaml +0 -0
  71. {learn_cli-0.5.0 → learn_cli-0.5.1}/docs/specs/2026-07-10-agentculture-org-learn-is-live-one-learning-hub-in.md +0 -0
  72. {learn_cli-0.5.0 → learn_cli-0.5.1}/docs/specs/subject-plugin-contract.md +0 -0
  73. {learn_cli-0.5.0 → learn_cli-0.5.1}/learn/__init__.py +0 -0
  74. {learn_cli-0.5.0 → learn_cli-0.5.1}/learn/__main__.py +0 -0
  75. {learn_cli-0.5.0 → learn_cli-0.5.1}/learn/cli/__init__.py +0 -0
  76. {learn_cli-0.5.0 → learn_cli-0.5.1}/learn/cli/_commands/__init__.py +0 -0
  77. {learn_cli-0.5.0 → learn_cli-0.5.1}/learn/cli/_commands/auth.py +0 -0
  78. {learn_cli-0.5.0 → learn_cli-0.5.1}/learn/cli/_commands/cli.py +0 -0
  79. {learn_cli-0.5.0 → learn_cli-0.5.1}/learn/cli/_commands/doctor.py +0 -0
  80. {learn_cli-0.5.0 → learn_cli-0.5.1}/learn/cli/_commands/explain.py +0 -0
  81. {learn_cli-0.5.0 → learn_cli-0.5.1}/learn/cli/_commands/learn.py +0 -0
  82. {learn_cli-0.5.0 → learn_cli-0.5.1}/learn/cli/_commands/mcp.py +0 -0
  83. {learn_cli-0.5.0 → learn_cli-0.5.1}/learn/cli/_commands/next_cmd.py +0 -0
  84. {learn_cli-0.5.0 → learn_cli-0.5.1}/learn/cli/_commands/overview.py +0 -0
  85. {learn_cli-0.5.0 → learn_cli-0.5.1}/learn/cli/_commands/progress.py +0 -0
  86. {learn_cli-0.5.0 → learn_cli-0.5.1}/learn/cli/_commands/record.py +0 -0
  87. {learn_cli-0.5.0 → learn_cli-0.5.1}/learn/cli/_commands/site.py +0 -0
  88. {learn_cli-0.5.0 → learn_cli-0.5.1}/learn/cli/_commands/subject.py +0 -0
  89. {learn_cli-0.5.0 → learn_cli-0.5.1}/learn/cli/_commands/subjects.py +0 -0
  90. {learn_cli-0.5.0 → learn_cli-0.5.1}/learn/cli/_commands/whoami.py +0 -0
  91. {learn_cli-0.5.0 → learn_cli-0.5.1}/learn/cli/_errors.py +0 -0
  92. {learn_cli-0.5.0 → learn_cli-0.5.1}/learn/cli/_output.py +0 -0
  93. {learn_cli-0.5.0 → learn_cli-0.5.1}/learn/contract/__init__.py +0 -0
  94. {learn_cli-0.5.0 → learn_cli-0.5.1}/learn/contract/_validate.py +0 -0
  95. {learn_cli-0.5.0 → learn_cli-0.5.1}/learn/contract/schemas/advice.json +0 -0
  96. {learn_cli-0.5.0 → learn_cli-0.5.1}/learn/contract/schemas/doctor.json +0 -0
  97. {learn_cli-0.5.0 → learn_cli-0.5.1}/learn/contract/schemas/error.json +0 -0
  98. {learn_cli-0.5.0 → learn_cli-0.5.1}/learn/contract/schemas/lesson.json +0 -0
  99. {learn_cli-0.5.0 → learn_cli-0.5.1}/learn/contract/schemas/overview.json +0 -0
  100. {learn_cli-0.5.0 → learn_cli-0.5.1}/learn/contract/schemas/practice.json +0 -0
  101. {learn_cli-0.5.0 → learn_cli-0.5.1}/learn/contract/schemas/progress.json +0 -0
  102. {learn_cli-0.5.0 → learn_cli-0.5.1}/learn/contract/schemas/record.json +0 -0
  103. {learn_cli-0.5.0 → learn_cli-0.5.1}/learn/contract/schemas/story.json +0 -0
  104. {learn_cli-0.5.0 → learn_cli-0.5.1}/learn/contract/schemas/story_list.json +0 -0
  105. {learn_cli-0.5.0 → learn_cli-0.5.1}/learn/contract/schemas/story_read.json +0 -0
  106. {learn_cli-0.5.0 → learn_cli-0.5.1}/learn/explain/__init__.py +0 -0
  107. {learn_cli-0.5.0 → learn_cli-0.5.1}/learn/explain/catalog.py +0 -0
  108. {learn_cli-0.5.0 → learn_cli-0.5.1}/learn/front/__init__.py +0 -0
  109. {learn_cli-0.5.0 → learn_cli-0.5.1}/learn/front/_docs.py +0 -0
  110. {learn_cli-0.5.0 → learn_cli-0.5.1}/learn/front/_driver.py +0 -0
  111. {learn_cli-0.5.0 → learn_cli-0.5.1}/learn/front/_export.py +0 -0
  112. {learn_cli-0.5.0 → learn_cli-0.5.1}/learn/motivation/__init__.py +0 -0
  113. {learn_cli-0.5.0 → learn_cli-0.5.1}/learn/motivation/_constants.py +0 -0
  114. {learn_cli-0.5.0 → learn_cli-0.5.1}/learn/motivation/_decay.py +0 -0
  115. {learn_cli-0.5.0 → learn_cli-0.5.1}/learn/motivation/_models.py +0 -0
  116. {learn_cli-0.5.0 → learn_cli-0.5.1}/learn/motivation/_review.py +0 -0
  117. {learn_cli-0.5.0 → learn_cli-0.5.1}/learn/motivation/_scoring.py +0 -0
  118. {learn_cli-0.5.0 → learn_cli-0.5.1}/learn/motivation/_state.py +0 -0
  119. {learn_cli-0.5.0 → learn_cli-0.5.1}/learn/motivation/_streaks.py +0 -0
  120. {learn_cli-0.5.0 → learn_cli-0.5.1}/learn/motivation/_whatnext.py +0 -0
  121. {learn_cli-0.5.0 → learn_cli-0.5.1}/learn/profile/__init__.py +0 -0
  122. {learn_cli-0.5.0 → learn_cli-0.5.1}/learn/profile/_api.py +0 -0
  123. {learn_cli-0.5.0 → learn_cli-0.5.1}/learn/profile/_auth.py +0 -0
  124. {learn_cli-0.5.0 → learn_cli-0.5.1}/learn/profile/_paths.py +0 -0
  125. {learn_cli-0.5.0 → learn_cli-0.5.1}/learn/profile/_sync.py +0 -0
  126. {learn_cli-0.5.0 → learn_cli-0.5.1}/learn/profile/_syncstate.py +0 -0
  127. {learn_cli-0.5.0 → learn_cli-0.5.1}/learn/subjects/__init__.py +0 -0
  128. {learn_cli-0.5.0 → learn_cli-0.5.1}/learn/subjects/conformance.py +0 -0
  129. {learn_cli-0.5.0 → learn_cli-0.5.1}/learn/subjects/driver.py +0 -0
  130. {learn_cli-0.5.0 → learn_cli-0.5.1}/learn/subjects/registry.json +0 -0
  131. {learn_cli-0.5.0 → learn_cli-0.5.1}/site-astro/.gitignore +0 -0
  132. {learn_cli-0.5.0 → learn_cli-0.5.1}/site-astro/.nvmrc +0 -0
  133. {learn_cli-0.5.0 → learn_cli-0.5.1}/site-astro/README.md +0 -0
  134. {learn_cli-0.5.0 → learn_cli-0.5.1}/site-astro/astro.config.mjs +0 -0
  135. {learn_cli-0.5.0 → learn_cli-0.5.1}/site-astro/package-lock.json +0 -0
  136. {learn_cli-0.5.0 → learn_cli-0.5.1}/site-astro/package.json +0 -0
  137. {learn_cli-0.5.0 → learn_cli-0.5.1}/site-astro/public/apple-touch-icon.png +0 -0
  138. {learn_cli-0.5.0 → learn_cli-0.5.1}/site-astro/public/favicon.ico +0 -0
  139. {learn_cli-0.5.0 → learn_cli-0.5.1}/site-astro/public/favicon.svg +0 -0
  140. {learn_cli-0.5.0 → learn_cli-0.5.1}/site-astro/scripts/check-export-pages.mjs +0 -0
  141. {learn_cli-0.5.0 → learn_cli-0.5.1}/site-astro/scripts/check-static-auth.mjs +0 -0
  142. {learn_cli-0.5.0 → learn_cli-0.5.1}/site-astro/src/components/Footer.astro +0 -0
  143. {learn_cli-0.5.0 → learn_cli-0.5.1}/site-astro/src/components/Header.astro +0 -0
  144. {learn_cli-0.5.0 → learn_cli-0.5.1}/site-astro/src/components/LearnerPanelOverview.astro +0 -0
  145. {learn_cli-0.5.0 → learn_cli-0.5.1}/site-astro/src/components/LearnerPanelSubject.astro +0 -0
  146. {learn_cli-0.5.0 → learn_cli-0.5.1}/site-astro/src/components/Mark.astro +0 -0
  147. {learn_cli-0.5.0 → learn_cli-0.5.1}/site-astro/src/components/PageHero.astro +0 -0
  148. {learn_cli-0.5.0 → learn_cli-0.5.1}/site-astro/src/content-export/docs/portal.md +0 -0
  149. {learn_cli-0.5.0 → learn_cli-0.5.1}/site-astro/src/content-export/docs/subject-plugin-contract.md +0 -0
  150. {learn_cli-0.5.0 → learn_cli-0.5.1}/site-astro/src/content-export/docs/subjects.md +0 -0
  151. {learn_cli-0.5.0 → learn_cli-0.5.1}/site-astro/src/content-export/meta.json +0 -0
  152. {learn_cli-0.5.0 → learn_cli-0.5.1}/site-astro/src/content-export/stories-culture-guide.json +0 -0
  153. {learn_cli-0.5.0 → learn_cli-0.5.1}/site-astro/src/content-export/stories-french.json +0 -0
  154. {learn_cli-0.5.0 → learn_cli-0.5.1}/site-astro/src/content-export/stories-spanish.json +0 -0
  155. {learn_cli-0.5.0 → learn_cli-0.5.1}/site-astro/src/content-export/subjects.json +0 -0
  156. {learn_cli-0.5.0 → learn_cli-0.5.1}/site-astro/src/data/site.ts +0 -0
  157. {learn_cli-0.5.0 → learn_cli-0.5.1}/site-astro/src/layouts/Layout.astro +0 -0
  158. {learn_cli-0.5.0 → learn_cli-0.5.1}/site-astro/src/lib/api.ts +0 -0
  159. {learn_cli-0.5.0 → learn_cli-0.5.1}/site-astro/src/lib/content.ts +0 -0
  160. {learn_cli-0.5.0 → learn_cli-0.5.1}/site-astro/src/pages/[subject]/index.astro +0 -0
  161. {learn_cli-0.5.0 → learn_cli-0.5.1}/site-astro/src/pages/[subject]/stories/[id]/index.astro +0 -0
  162. {learn_cli-0.5.0 → learn_cli-0.5.1}/site-astro/src/pages/index.astro +0 -0
  163. {learn_cli-0.5.0 → learn_cli-0.5.1}/site-astro/src/scripts/learner.js +0 -0
  164. {learn_cli-0.5.0 → learn_cli-0.5.1}/site-astro/src/styles/global.css +0 -0
  165. {learn_cli-0.5.0 → learn_cli-0.5.1}/site-astro/tsconfig.json +0 -0
  166. {learn_cli-0.5.0 → learn_cli-0.5.1}/sonar-project.properties +0 -0
  167. {learn_cli-0.5.0 → learn_cli-0.5.1}/tests/__init__.py +0 -0
  168. {learn_cli-0.5.0 → learn_cli-0.5.1}/tests/conftest.py +0 -0
  169. {learn_cli-0.5.0 → learn_cli-0.5.1}/tests/e2e/__init__.py +0 -0
  170. {learn_cli-0.5.0 → learn_cli-0.5.1}/tests/e2e/conftest.py +0 -0
  171. {learn_cli-0.5.0 → learn_cli-0.5.1}/tests/e2e/test_cli_golden.py +0 -0
  172. {learn_cli-0.5.0 → learn_cli-0.5.1}/tests/e2e/test_mcp_harness.py +0 -0
  173. {learn_cli-0.5.0 → learn_cli-0.5.1}/tests/fixtures/motivation/completed_track_current.json +0 -0
  174. {learn_cli-0.5.0 → learn_cli-0.5.1}/tests/fixtures/motivation/completed_track_decayed.json +0 -0
  175. {learn_cli-0.5.0 → learn_cli-0.5.1}/tests/fixtures/motivation/cross_subject.json +0 -0
  176. {learn_cli-0.5.0 → learn_cli-0.5.1}/tests/fixtures/motivation/single_subject_progression.json +0 -0
  177. {learn_cli-0.5.0 → learn_cli-0.5.1}/tests/fixtures/payloads/advice.json +0 -0
  178. {learn_cli-0.5.0 → learn_cli-0.5.1}/tests/fixtures/payloads/doctor.json +0 -0
  179. {learn_cli-0.5.0 → learn_cli-0.5.1}/tests/fixtures/payloads/error.json +0 -0
  180. {learn_cli-0.5.0 → learn_cli-0.5.1}/tests/fixtures/payloads/lesson.json +0 -0
  181. {learn_cli-0.5.0 → learn_cli-0.5.1}/tests/fixtures/payloads/overview.json +0 -0
  182. {learn_cli-0.5.0 → learn_cli-0.5.1}/tests/fixtures/payloads/practice.json +0 -0
  183. {learn_cli-0.5.0 → learn_cli-0.5.1}/tests/fixtures/payloads/progress.json +0 -0
  184. {learn_cli-0.5.0 → learn_cli-0.5.1}/tests/fixtures/payloads/record.json +0 -0
  185. {learn_cli-0.5.0 → learn_cli-0.5.1}/tests/fixtures/payloads/story.json +0 -0
  186. {learn_cli-0.5.0 → learn_cli-0.5.1}/tests/fixtures/payloads/story_list.json +0 -0
  187. {learn_cli-0.5.0 → learn_cli-0.5.1}/tests/fixtures/payloads/story_read.json +0 -0
  188. {learn_cli-0.5.0 → learn_cli-0.5.1}/tests/fixtures/stories/culture-guide-scenario-overloaded-agent.json +0 -0
  189. {learn_cli-0.5.0 → learn_cli-0.5.1}/tests/fixtures/stories/french-beginner-le-marche.json +0 -0
  190. {learn_cli-0.5.0 → learn_cli-0.5.1}/tests/fixtures/stories/spanish-beginner-la-panaderia.json +0 -0
  191. {learn_cli-0.5.0 → learn_cli-0.5.1}/tests/fixtures/subjects/conformant_subject.py +0 -0
  192. {learn_cli-0.5.0 → learn_cli-0.5.1}/tests/fixtures/subjects/drifted_subject.py +0 -0
  193. {learn_cli-0.5.0 → learn_cli-0.5.1}/tests/test_auth_cli.py +0 -0
  194. {learn_cli-0.5.0 → learn_cli-0.5.1}/tests/test_cli.py +0 -0
  195. {learn_cli-0.5.0 → learn_cli-0.5.1}/tests/test_cli_faces.py +0 -0
  196. {learn_cli-0.5.0 → learn_cli-0.5.1}/tests/test_cli_introspection.py +0 -0
  197. {learn_cli-0.5.0 → learn_cli-0.5.1}/tests/test_contract_schemas.py +0 -0
  198. {learn_cli-0.5.0 → learn_cli-0.5.1}/tests/test_contract_validator.py +0 -0
  199. {learn_cli-0.5.0 → learn_cli-0.5.1}/tests/test_front_app.py +0 -0
  200. {learn_cli-0.5.0 → learn_cli-0.5.1}/tests/test_front_driver.py +0 -0
  201. {learn_cli-0.5.0 → learn_cli-0.5.1}/tests/test_motivation_decay_review.py +0 -0
  202. {learn_cli-0.5.0 → learn_cli-0.5.1}/tests/test_motivation_extras.py +0 -0
  203. {learn_cli-0.5.0 → learn_cli-0.5.1}/tests/test_motivation_golden.py +0 -0
  204. {learn_cli-0.5.0 → learn_cli-0.5.1}/tests/test_motivation_purity.py +0 -0
  205. {learn_cli-0.5.0 → learn_cli-0.5.1}/tests/test_motivation_scoring.py +0 -0
  206. {learn_cli-0.5.0 → learn_cli-0.5.1}/tests/test_motivation_streaks.py +0 -0
  207. {learn_cli-0.5.0 → learn_cli-0.5.1}/tests/test_motivation_whatnext.py +0 -0
  208. {learn_cli-0.5.0 → learn_cli-0.5.1}/tests/test_progress_next_record.py +0 -0
  209. {learn_cli-0.5.0 → learn_cli-0.5.1}/tests/test_site_export.py +0 -0
  210. {learn_cli-0.5.0 → learn_cli-0.5.1}/tests/test_story_fixtures.py +0 -0
  211. {learn_cli-0.5.0 → learn_cli-0.5.1}/tests/test_subject_doctor.py +0 -0
  212. {learn_cli-0.5.0 → learn_cli-0.5.1}/tests/test_subject_driver.py +0 -0
  213. {learn_cli-0.5.0 → learn_cli-0.5.1}/tests/test_subject_registry.py +0 -0
  214. {learn_cli-0.5.0 → learn_cli-0.5.1}/tests/test_surfaces_agree.py +0 -0
  215. {learn_cli-0.5.0 → learn_cli-0.5.1}/tools/launch-gate/.gitignore +0 -0
  216. {learn_cli-0.5.0 → learn_cli-0.5.1}/tools/launch-gate/README.md +0 -0
  217. {learn_cli-0.5.0 → learn_cli-0.5.1}/tools/launch-gate/api-server.mjs +0 -0
  218. {learn_cli-0.5.0 → learn_cli-0.5.1}/tools/launch-gate/launch_bar.py +0 -0
  219. {learn_cli-0.5.0 → learn_cli-0.5.1}/tools/launch-gate/package.json +0 -0
  220. {learn_cli-0.5.0 → learn_cli-0.5.1}/tools/launch-gate/report.py +0 -0
  221. {learn_cli-0.5.0 → learn_cli-0.5.1}/tools/launch-gate/run.sh +0 -0
  222. {learn_cli-0.5.0 → learn_cli-0.5.1}/tools/launch-gate/walk.mjs +0 -0
  223. {learn_cli-0.5.0 → learn_cli-0.5.1}/workers/learn-api/.gitignore +0 -0
  224. {learn_cli-0.5.0 → learn_cli-0.5.1}/workers/learn-api/README.md +0 -0
  225. {learn_cli-0.5.0 → learn_cli-0.5.1}/workers/learn-api/package.json +0 -0
  226. {learn_cli-0.5.0 → learn_cli-0.5.1}/workers/learn-api/schema.sql +0 -0
  227. {learn_cli-0.5.0 → learn_cli-0.5.1}/workers/learn-api/src/auth.js +0 -0
  228. {learn_cli-0.5.0 → learn_cli-0.5.1}/workers/learn-api/src/db.js +0 -0
  229. {learn_cli-0.5.0 → learn_cli-0.5.1}/workers/learn-api/src/github.js +0 -0
  230. {learn_cli-0.5.0 → learn_cli-0.5.1}/workers/learn-api/src/progress.js +0 -0
  231. {learn_cli-0.5.0 → learn_cli-0.5.1}/workers/learn-api/src/session.js +0 -0
  232. {learn_cli-0.5.0 → learn_cli-0.5.1}/workers/learn-api/src/util.js +0 -0
  233. {learn_cli-0.5.0 → learn_cli-0.5.1}/workers/learn-api/src/validate.js +0 -0
  234. {learn_cli-0.5.0 → learn_cli-0.5.1}/workers/learn-api/test/helpers.js +0 -0
  235. {learn_cli-0.5.0 → learn_cli-0.5.1}/workers/learn-api/test/session.test.js +0 -0
  236. {learn_cli-0.5.0 → learn_cli-0.5.1}/workers/learn-api/test/validate.test.js +0 -0
  237. {learn_cli-0.5.0 → learn_cli-0.5.1}/workers/learn-api/wrangler.toml +0 -0
@@ -5,6 +5,15 @@ All notable changes to this project will be documented in this file.
5
5
  Format follows [Keep a Changelog](https://keepachangelog.com/). This project
6
6
  adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.5.1] - 2026-07-11
9
+
10
+ ### Fixed
11
+
12
+ - API worker: the /learn zone-mount proxy strips Cookie/Authorization before forwarding to the Pages origin (review finding: session leak)
13
+ - API worker: withCors appends to an existing Vary header instead of overwriting it
14
+ - Profile ledger: created 0600 like auth.json (learner data is private)
15
+ - Profile ledger: a malformed ledger line is skipped instead of crashing progress/next/sync
16
+
8
17
  ## [0.5.0] - 2026-07-11
9
18
 
10
19
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: learn-cli
3
- Version: 0.5.0
3
+ Version: 0.5.1
4
4
  Summary: A CLI, MCP server, and web front for learning — a hosted site where humans and agents learn a subject step by step. Fronts the french-cli and spanish-cli language tutors, and generalizes to other learnable domains such as leading teams of agents. The learn command is the local operator surface and how agents drive it.
5
5
  Project-URL: Homepage, https://github.com/agentculture/learn-cli
6
6
  Project-URL: Issues, https://github.com/agentculture/learn-cli/issues
@@ -8,26 +8,44 @@ written to by ``learn record`` (never by a subject CLI directly) and read by
8
8
  every motivation computation. Append-only by design — nothing here ever
9
9
  rewrites or deletes a row, so the sync cursor (:mod:`learn.profile._syncstate`)
10
10
  can track "pushed so far" as a simple row count.
11
+
12
+ The ledger is learner data: the file is created ``0600`` (like ``auth.json``),
13
+ and a malformed line — a crash mid-append, a manual edit — is skipped rather
14
+ than crashing every ``progress``/``next``/sync flow. Because the ledger is
15
+ append-only and a malformed line never becomes valid, skipping is stable, so
16
+ the sync cursor's count-of-valid-rows arithmetic stays consistent across runs.
11
17
  """
12
18
 
13
19
  from __future__ import annotations
14
20
 
15
21
  import json
22
+ import os
16
23
  from typing import Any
17
24
 
18
25
  from learn.profile._paths import ensure_store_dir, ledger_path
19
26
 
27
+ #: Learner data is private to the local user, like ``auth.json``.
28
+ _LEDGER_MODE = 0o600
29
+
20
30
 
21
31
  def append_ledger(row: dict[str, Any]) -> None:
22
32
  """Append one row to the local ledger, creating the store dir if needed."""
23
33
  ensure_store_dir()
24
- with ledger_path().open("a", encoding="utf-8") as handle:
34
+ path = ledger_path()
35
+ created = not path.exists()
36
+ with path.open("a", encoding="utf-8") as handle:
25
37
  handle.write(json.dumps(row, ensure_ascii=False))
26
38
  handle.write("\n")
39
+ if created:
40
+ os.chmod(path, _LEDGER_MODE)
27
41
 
28
42
 
29
43
  def read_ledger() -> list[dict[str, Any]]:
30
- """Read every row, in append order. Empty list when the ledger doesn't exist yet."""
44
+ """Read every valid row, in append order. Empty list when no ledger exists yet.
45
+
46
+ A line that fails to parse (or parses to a non-object) is skipped — one
47
+ corrupt row must not take down ``progress``/``next``/sync.
48
+ """
31
49
  path = ledger_path()
32
50
  if not path.is_file():
33
51
  return []
@@ -37,7 +55,12 @@ def read_ledger() -> list[dict[str, Any]]:
37
55
  stripped = line.strip()
38
56
  if not stripped:
39
57
  continue
40
- rows.append(json.loads(stripped))
58
+ try:
59
+ parsed = json.loads(stripped)
60
+ except json.JSONDecodeError:
61
+ continue
62
+ if isinstance(parsed, dict):
63
+ rows.append(parsed)
41
64
  return rows
42
65
 
43
66
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "learn-cli"
3
- version = "0.5.0"
3
+ version = "0.5.1"
4
4
  description = "A CLI, MCP server, and web front for learning — a hosted site where humans and agents learn a subject step by step. Fronts the french-cli and spanish-cli language tutors, and generalizes to other learnable domains such as leading teams of agents. The learn command is the local operator surface and how agents drive it."
5
5
  readme = "README.md"
6
6
  license = "Apache-2.0"
@@ -72,6 +72,23 @@ def test_read_ledger_skips_blank_lines(profile_home) -> None:
72
72
  assert len(p.read_ledger()) == 1
73
73
 
74
74
 
75
+ def test_ledger_file_is_created_private(profile_home) -> None:
76
+ p.append_ledger({"subject": "french", "item_id": "fr.x", "result": "pass"})
77
+ mode = p.ledger_path().stat().st_mode & 0o777
78
+ assert mode == 0o600
79
+
80
+
81
+ def test_read_ledger_skips_malformed_lines(profile_home) -> None:
82
+ p.append_ledger({"subject": "french", "item_id": "fr.a", "result": "pass"})
83
+ with p.ledger_path().open("a", encoding="utf-8") as handle:
84
+ handle.write("{not json\n")
85
+ handle.write("[1, 2, 3]\n")
86
+ p.append_ledger({"subject": "french", "item_id": "fr.b", "result": "partial"})
87
+ rows = p.read_ledger()
88
+ assert [r["item_id"] for r in rows] == ["fr.a", "fr.b"]
89
+ assert p.ledger_count() == 2
90
+
91
+
75
92
  # --- auth state ------------------------------------------------------------
76
93
 
77
94
 
@@ -474,7 +474,7 @@ wheels = [
474
474
 
475
475
  [[package]]
476
476
  name = "learn-cli"
477
- version = "0.5.0"
477
+ version = "0.5.1"
478
478
  source = { editable = "." }
479
479
  dependencies = [
480
480
  { name = "agentfront", extra = ["mcp"] },
@@ -102,7 +102,13 @@ async function proxyToPages(request, env, url) {
102
102
  requireConfig(env, "PAGES_ORIGIN");
103
103
  const origin = env.PAGES_ORIGIN.replace(/\/+$/, "");
104
104
  const upstream = origin + url.pathname + url.search;
105
- return fetch(new Request(upstream, { method: request.method, headers: request.headers }));
105
+ // Never forward credentials to the static origin: the session cookie (and
106
+ // any Authorization header) is for this API only — the Pages origin serves
107
+ // public files and must not see learner sessions.
108
+ const headers = new Headers(request.headers);
109
+ headers.delete("Cookie");
110
+ headers.delete("Authorization");
111
+ return fetch(new Request(upstream, { method: request.method, headers }));
106
112
  }
107
113
 
108
114
  // --- public routes ---------------------------------------------------------
@@ -325,6 +331,6 @@ function withCors(env, request, response) {
325
331
  headers.set("Access-Control-Allow-Credentials", "true");
326
332
  headers.set("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
327
333
  headers.set("Access-Control-Allow-Headers", "Content-Type, Authorization");
328
- headers.set("Vary", "Origin");
334
+ headers.append("Vary", "Origin");
329
335
  return new Response(response.body, { status: response.status, headers });
330
336
  }
@@ -429,3 +429,47 @@ test("POST under the /learn site mount is rejected (read-only proxy)", async ()
429
429
  const res = await call(env, new Request(`${BASE}/learn/french/`, { method: "POST" }));
430
430
  assert.equal(res.status, 405);
431
431
  });
432
+
433
+ test("proxy to PAGES_ORIGIN strips Cookie and Authorization headers", async () => {
434
+ let seenHeaders = null;
435
+ const realFetch = globalThis.fetch;
436
+ globalThis.fetch = async (req) => {
437
+ seenHeaders = req.headers;
438
+ return new Response("ok", { status: 200 });
439
+ };
440
+ try {
441
+ const env = makeEnv({ PAGES_ORIGIN: "https://agentculture-learn.pages.dev" });
442
+ const req = new Request(`${BASE}/learn/french/`, {
443
+ headers: { Cookie: "session=supersecret", Authorization: "Bearer tok", Accept: "text/html" },
444
+ });
445
+ const res = await call(env, req);
446
+ assert.equal(res.status, 200);
447
+ assert.equal(seenHeaders.get("Cookie"), null);
448
+ assert.equal(seenHeaders.get("Authorization"), null);
449
+ assert.equal(seenHeaders.get("Accept"), "text/html");
450
+ } finally {
451
+ globalThis.fetch = realFetch;
452
+ }
453
+ });
454
+
455
+ test("withCors appends to an existing Vary header instead of overwriting", async () => {
456
+ let called = 0;
457
+ const realFetch = globalThis.fetch;
458
+ globalThis.fetch = async () => {
459
+ called += 1;
460
+ return new Response("ok", { status: 200, headers: { Vary: "Accept-Encoding" } });
461
+ };
462
+ try {
463
+ const env = makeEnv({
464
+ PAGES_ORIGIN: "https://agentculture-learn.pages.dev",
465
+ CORS_ORIGIN: "https://agentculture.org",
466
+ });
467
+ const res = await call(env, new Request(`${BASE}/learn/french/`, { headers: { Origin: "https://agentculture.org" } }));
468
+ assert.equal(called, 1);
469
+ const vary = res.headers.get("Vary");
470
+ assert.ok(vary.includes("Accept-Encoding"), `Vary lost upstream value: ${vary}`);
471
+ assert.ok(vary.includes("Origin"), `Vary missing Origin: ${vary}`);
472
+ } finally {
473
+ globalThis.fetch = realFetch;
474
+ }
475
+ });
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes