learningfoundry 0.83.2__tar.gz → 0.83.3__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 (123) hide show
  1. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/CHANGELOG.md +9 -0
  2. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/PKG-INFO +5 -4
  3. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/README.md +4 -3
  4. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/__init__.py +1 -1
  5. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/cli.py +7 -2
  6. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/launch.py +28 -5
  7. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/.gitignore +0 -0
  8. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/LICENSE +0 -0
  9. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/docs/specs/nbfoundry/README.md +0 -0
  10. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/docs/specs/pyve/README.md +0 -0
  11. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/docs/specs/quizazz/README.md +0 -0
  12. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/pyproject.toml +0 -0
  13. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/__main__.py +0 -0
  14. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/asset_resolver.py +0 -0
  15. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/config.py +0 -0
  16. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/directives.py +0 -0
  17. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/exceptions.py +0 -0
  18. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/generator.py +0 -0
  19. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/integrations/__init__.py +0 -0
  20. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/integrations/d3foundry_stub.py +0 -0
  21. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/integrations/nbfoundry.py +0 -0
  22. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/integrations/nbfoundry_stub.py +0 -0
  23. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/integrations/protocols.py +0 -0
  24. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/integrations/quizazz.py +0 -0
  25. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/logging_config.py +0 -0
  26. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/parser.py +0 -0
  27. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/pipeline.py +0 -0
  28. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/py.typed +0 -0
  29. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/resolver.py +0 -0
  30. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/schema_extensions.py +0 -0
  31. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/schema_v1.py +0 -0
  32. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/e2e/README.md +0 -0
  33. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/e2e/finish.spec.ts +0 -0
  34. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/e2e/fixtures/curriculum.json +0 -0
  35. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/e2e/global-teardown.ts +0 -0
  36. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/e2e/lifecycle.spec.ts +0 -0
  37. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/e2e/navigation.spec.ts +0 -0
  38. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/e2e/progress.spec.ts +0 -0
  39. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/e2e/reset.spec.ts +0 -0
  40. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/e2e/text-block-bottom.spec.ts +0 -0
  41. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/e2e/video.spec.ts +0 -0
  42. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/package.json +0 -0
  43. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/playwright.config.ts +0 -0
  44. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/pnpm-lock.yaml +0 -0
  45. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/pnpm-workspace.yaml +0 -0
  46. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/app.css +0 -0
  47. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/app.html +0 -0
  48. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/lib/components/AssessmentBlock.svelte +0 -0
  49. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/lib/components/AssessmentBlock.test.ts +0 -0
  50. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/lib/components/ContentBlock.svelte +0 -0
  51. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/lib/components/ExerciseBlock.svelte +0 -0
  52. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/lib/components/ExerciseBlock.test.ts +0 -0
  53. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/lib/components/LessonList.svelte +0 -0
  54. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/lib/components/LessonList.test.ts +0 -0
  55. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/lib/components/LessonView.svelte +0 -0
  56. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/lib/components/LessonView.test.ts +0 -0
  57. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/lib/components/LockedLessonPlaceholder.svelte +0 -0
  58. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/lib/components/ModuleList.svelte +0 -0
  59. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/lib/components/ModuleList.test.ts +0 -0
  60. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/lib/components/Navigation.svelte +0 -0
  61. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/lib/components/PlaceholderBlock.svelte +0 -0
  62. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/lib/components/ProgressBar.svelte +0 -0
  63. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/lib/components/ProgressDashboard.svelte +0 -0
  64. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/lib/components/ProgressDashboard.test.ts +0 -0
  65. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/lib/components/RecordingPausedBanner.svelte +0 -0
  66. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/lib/components/RecordingPausedBanner.test.ts +0 -0
  67. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/lib/components/ResetCourseButton.svelte +0 -0
  68. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/lib/components/ResetCourseButton.test.ts +0 -0
  69. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/lib/components/TextBlock.observer.test.ts +0 -0
  70. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/lib/components/TextBlock.svelte +0 -0
  71. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/lib/components/TextBlock.test.ts +0 -0
  72. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/lib/components/VideoBlock.svelte +0 -0
  73. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/lib/components/VideoBlock.test.ts +0 -0
  74. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/lib/components/VisualizationBlock.svelte +0 -0
  75. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/lib/components/lesson-view.helpers.ts +0 -0
  76. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/lib/components/module-list.helpers.ts +0 -0
  77. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/lib/components/module-list.test.ts +0 -0
  78. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/lib/components/mount.test.ts +0 -0
  79. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/lib/components/navigation.helpers.ts +0 -0
  80. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/lib/components/navigation.test.ts +0 -0
  81. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/lib/components/progress-dashboard.helpers.ts +0 -0
  82. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/lib/db/database.test.ts +0 -0
  83. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/lib/db/database.ts +0 -0
  84. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/lib/db/index.ts +0 -0
  85. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/lib/db/progress.test.ts +0 -0
  86. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/lib/db/progress.ts +0 -0
  87. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/lib/db/user-id.test.ts +0 -0
  88. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/lib/db/user-id.ts +0 -0
  89. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/lib/stores/curriculum.test.ts +0 -0
  90. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/lib/stores/curriculum.ts +0 -0
  91. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/lib/stores/db-init.test.ts +0 -0
  92. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/lib/stores/db-init.ts +0 -0
  93. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/lib/stores/progress.test.ts +0 -0
  94. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/lib/stores/progress.ts +0 -0
  95. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/lib/types/index.ts +0 -0
  96. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/lib/utils/assessment-passed.test.ts +0 -0
  97. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/lib/utils/assessment-passed.ts +0 -0
  98. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/lib/utils/duration.test.ts +0 -0
  99. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/lib/utils/duration.ts +0 -0
  100. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/lib/utils/locking.test.ts +0 -0
  101. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/lib/utils/locking.ts +0 -0
  102. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/lib/utils/markdown-directives.ts +0 -0
  103. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/lib/utils/markdown.test.ts +0 -0
  104. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/lib/utils/markdown.ts +0 -0
  105. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/lib/utils/progress.test.ts +0 -0
  106. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/lib/utils/progress.ts +0 -0
  107. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/lib/utils/viewport-completion.ts +0 -0
  108. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/routes/+layout.svelte +0 -0
  109. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/routes/+layout.ts +0 -0
  110. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/routes/+page.svelte +0 -0
  111. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/routes/[module]/[lesson]/+page.svelte +0 -0
  112. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/routes/[module]/[lesson]/page.test.ts +0 -0
  113. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/routes/[module]/assessment/[id]/+page.svelte +0 -0
  114. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/routes/[module]/assessment/[id]/page.test.ts +0 -0
  115. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/routes/layout.helpers.ts +0 -0
  116. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/routes/layout.scroll.test.ts +0 -0
  117. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/routes/layout.scroll.ts +0 -0
  118. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/src/routes/layout.test.ts +0 -0
  119. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/static/.gitkeep +0 -0
  120. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/svelte.config.js +0 -0
  121. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/test-results/.last-run.json +0 -0
  122. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/tsconfig.json +0 -0
  123. {learningfoundry-0.83.2 → learningfoundry-0.83.3}/src/learningfoundry/sveltekit_template/vite.config.ts +0 -0
@@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.83.3] - 2026-06-18
11
+
12
+ **`learningfoundry launch`/`stop` fixes from first real-world use** (Story K.l).
13
+
14
+ ### Fixed
15
+
16
+ - **`launch`/`stop` auto-detect the build in `dist/`.** The learner runs these from the curriculum project root, where `build` writes the app into `dist/` — so the manifest is at `dist/exercises-manifest.json` and the bare command previously failed (`No exercises-manifest.json found in .`), forcing `--dir dist` every time. The `--dir` value now resolves to `<dir>/exercises-manifest.json`, else `<dir>/dist/exercises-manifest.json`; an explicit `--dir` still overrides.
17
+ - **`stop` no longer leaves marimo children hanging.** `terminate_pid` signalled only the parent marimo pid, orphaning its multiprocessing children — which printed marimo's goodbye banner + `resource_tracker` leaked-semaphore + `parent_poller` warnings *after* the shell prompt returned. It now `SIGTERM`s the whole process group (`os.killpg(os.getpgid(pid), …)`; marimo is its own group leader via `start_new_session=True`) so children shut down cleanly; Windows uses `taskkill /T` (tree-kill).
18
+
10
19
  ## [0.83.2] - 2026-06-18
11
20
 
12
21
  **Single-source the package version** (Story K.k) — build hygiene.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: learningfoundry
3
- Version: 0.83.2
3
+ Version: 0.83.3
4
4
  Summary: A curriculum engine that turns a YAML curriculum definition into a deployable SvelteKit learning application.
5
5
  Project-URL: Homepage, https://github.com/pointmatic/learningfoundry
6
6
  Project-URL: Repository, https://github.com/pointmatic/learningfoundry
@@ -213,15 +213,16 @@ This serves the SvelteKit project from source via Vite's dev server; it does **n
213
213
 
214
214
  ### `learningfoundry launch` / `learningfoundry stop`
215
215
 
216
- Run a `ready` exercise's marimo notebook locally. These are **learner-side** commands — run from inside the generated app (where `build` writes `exercises-manifest.json`), not from the author's repo. The generated app is static and a browser page can't spawn a process, so the exercise's banner asks the learner to copy and run `learningfoundry launch <id>`.
216
+ Run a `ready` exercise's marimo notebook locally. These are **learner-side** commands — run them from the curriculum project root (they auto-detect the build in `dist/`) or from inside the generated app itself. The generated app is static and a browser page can't spawn a process, so the exercise's banner asks the learner to copy and run `learningfoundry launch <id>`.
217
217
 
218
218
  ```
219
219
  Usage: learningfoundry launch [OPTIONS] EXERCISE_ID
220
220
  learningfoundry stop [OPTIONS] [EXERCISE_ID]
221
221
 
222
222
  Options:
223
- --dir PATH Directory holding exercises-manifest.json (the
224
- generated app's root). [default: .]
223
+ --dir PATH Where to find exercises-manifest.json. Auto-detects
224
+ `<dir>/exercises-manifest.json`, else
225
+ `<dir>/dist/exercises-manifest.json`. [default: .]
225
226
  --log-level LEVEL Logging verbosity. [default: INFO]
226
227
  --help Show this message and exit.
227
228
  ```
@@ -182,15 +182,16 @@ This serves the SvelteKit project from source via Vite's dev server; it does **n
182
182
 
183
183
  ### `learningfoundry launch` / `learningfoundry stop`
184
184
 
185
- Run a `ready` exercise's marimo notebook locally. These are **learner-side** commands — run from inside the generated app (where `build` writes `exercises-manifest.json`), not from the author's repo. The generated app is static and a browser page can't spawn a process, so the exercise's banner asks the learner to copy and run `learningfoundry launch <id>`.
185
+ Run a `ready` exercise's marimo notebook locally. These are **learner-side** commands — run them from the curriculum project root (they auto-detect the build in `dist/`) or from inside the generated app itself. The generated app is static and a browser page can't spawn a process, so the exercise's banner asks the learner to copy and run `learningfoundry launch <id>`.
186
186
 
187
187
  ```
188
188
  Usage: learningfoundry launch [OPTIONS] EXERCISE_ID
189
189
  learningfoundry stop [OPTIONS] [EXERCISE_ID]
190
190
 
191
191
  Options:
192
- --dir PATH Directory holding exercises-manifest.json (the
193
- generated app's root). [default: .]
192
+ --dir PATH Where to find exercises-manifest.json. Auto-detects
193
+ `<dir>/exercises-manifest.json`, else
194
+ `<dir>/dist/exercises-manifest.json`. [default: .]
194
195
  --log-level LEVEL Logging verbosity. [default: INFO]
195
196
  --help Show this message and exit.
196
197
  ```
@@ -1,4 +1,4 @@
1
1
  # Copyright 2026 Pointmatic
2
2
  # SPDX-License-Identifier: Apache-2.0
3
3
 
4
- __version__ = "0.83.2"
4
+ __version__ = "0.83.3"
@@ -286,8 +286,9 @@ _launch_dir_option = click.option(
286
286
  default=Path("."),
287
287
  show_default=True,
288
288
  help=(
289
- "Directory holding `exercises-manifest.json` (the generated app's "
290
- "root). Defaults to the current directory."
289
+ "Where to find `exercises-manifest.json`. Auto-detects "
290
+ "`<dir>/exercises-manifest.json`, else `<dir>/dist/...`. "
291
+ "Defaults to the current directory."
291
292
  ),
292
293
  )
293
294
 
@@ -302,6 +303,8 @@ def launch(exercise_id: str, launch_dir: Path, log_level: str) -> None:
302
303
 
303
304
  from learningfoundry import launch as _launch
304
305
 
306
+ launch_dir = _launch.resolve_manifest_dir(launch_dir)
307
+
305
308
  try:
306
309
  spec = _launch.resolve_launch_spec(launch_dir, exercise_id)
307
310
  except LaunchError as exc:
@@ -362,6 +365,8 @@ def stop(exercise_id: str | None, launch_dir: Path, log_level: str) -> None:
362
365
 
363
366
  from learningfoundry import launch as _launch
364
367
 
368
+ launch_dir = _launch.resolve_manifest_dir(launch_dir)
369
+
365
370
  if exercise_id is not None:
366
371
  try:
367
372
  spec = _launch.resolve_launch_spec(launch_dir, exercise_id)
@@ -58,6 +58,24 @@ class LaunchSpec:
58
58
  port: int
59
59
 
60
60
 
61
+ def resolve_manifest_dir(start: Path) -> Path:
62
+ """Locate the directory that holds ``exercises-manifest.json``.
63
+
64
+ The learner typically runs ``launch``/``stop`` from the curriculum project
65
+ root, where ``build`` writes the app into ``dist/`` — so the manifest is at
66
+ ``dist/exercises-manifest.json``, not in the cwd. Prefer ``start`` itself
67
+ (running from inside the app), then fall back to ``start/dist``. If neither
68
+ holds the manifest, return ``start`` unchanged so the downstream
69
+ :class:`ManifestNotFoundError` names the directory the user actually gave.
70
+ """
71
+ if (start / MANIFEST_FILENAME).is_file():
72
+ return start
73
+ nested = start / "dist"
74
+ if (nested / MANIFEST_FILENAME).is_file():
75
+ return nested
76
+ return start
77
+
78
+
61
79
  def resolve_launch_spec(manifest_dir: Path, exercise_id: str) -> LaunchSpec:
62
80
  """Resolve an exercise id to its :class:`LaunchSpec` from the manifest.
63
81
 
@@ -295,20 +313,25 @@ def spawn_detached(argv: list[str], cwd: Path) -> int:
295
313
 
296
314
 
297
315
  def terminate_pid(pid: int) -> None:
298
- """Ask the process ``pid`` to terminate (idempotent if already gone).
299
-
300
- POSIX sends ``SIGTERM`` (graceful); Windows shells out to ``taskkill``.
316
+ """Ask the launch-owned marimo at ``pid`` to terminate (idempotent).
317
+
318
+ Signals the whole **process group**, not just ``pid``: ``launch`` spawns
319
+ marimo with ``start_new_session=True``, so marimo is its own group leader
320
+ and its multiprocessing children share the group. Killing only the parent
321
+ orphans those children, which then print marimo's goodbye banner and
322
+ ``resource_tracker`` leaked-semaphore warnings *after* the shell returns.
323
+ POSIX `SIGTERM`s the group (graceful); Windows uses ``taskkill /T`` (tree).
301
324
  A process that has already exited is treated as success.
302
325
  """
303
326
  if sys.platform == "win32": # pragma: no cover - Windows only
304
327
  subprocess.run(
305
- ["taskkill", "/F", "/PID", str(pid)],
328
+ ["taskkill", "/F", "/T", "/PID", str(pid)],
306
329
  check=False,
307
330
  capture_output=True,
308
331
  )
309
332
  return
310
333
  try:
311
- os.kill(pid, signal.SIGTERM)
334
+ os.killpg(os.getpgid(pid), signal.SIGTERM)
312
335
  except ProcessLookupError:
313
336
  pass # already gone — nothing to do
314
337